diff --git a/.agents/skills/conventions/SKILL.md b/.agents/skills/conventions/SKILL.md index 12a5f26edb4..05e36bcb0dc 100644 --- a/.agents/skills/conventions/SKILL.md +++ b/.agents/skills/conventions/SKILL.md @@ -46,6 +46,10 @@ throw new UnexpectedError('message', { extra: { context } }); - SQLite/PostgreSQL only (app DB) - Exception: DB nodes (MySQL Node, etc.) can use DB-specific features +**GitHub Workflows:** +- Every workflow declares a least-privilege top-level `permissions:` block + (usually `contents: read`); jobs needing more override at job level + **Commands:** ```bash pnpm build > build.log 2>&1 # Always redirect diff --git a/.agents/skills/create-instance-ai-eval/SKILL.md b/.agents/skills/create-instance-ai-eval/SKILL.md index 73ef5c27688..a4ecc4b8271 100644 --- a/.agents/skills/create-instance-ai-eval/SKILL.md +++ b/.agents/skills/create-instance-ai-eval/SKILL.md @@ -31,7 +31,7 @@ exhaustive field reference; this skill is the opinionated *how*. > > **Exception — seeded cases.** The case-write API can't hold any seeding mode, so > seeded cases are never pushed. A `seedThread` case is a local throwaway (don't -> commit it either — it dies when its trace is pruned); a `seedFile` or +> commit it either — it dies when its trace is pruned); a `conversationSeed` or > `priorConversation` case isn't transient and has no suite home, so it's the one > sanctioned exception — it lives as committed JSON. See [`case-shapes.md`](case-shapes.md). @@ -104,7 +104,7 @@ case can still assert outcome), but the primary shape drives the work. | **Build** (default) | Does the workflow the agent builds actually *work*? | `outcomeExpectations` + `executionScenarios` | | **Behaviour / process** | Does the agent *converse* correctly (ask the right clarifying question, not re-ask, honour a correction, respect plan approval)? | `processExpectations` + multi-turn director script; often **build-only** | | **Credential** | Does the build behave correctly given a specific credential view? | `credentials[]` | -| **Seeded** | Reproduce a conversation mid-thread and drive the turn under test | `seedThread` / `priorConversation` / `seedFile` | +| **Seeded** | Reproduce a conversation mid-thread and drive the turn under test | `seedThread` / `priorConversation` / `conversationSeed` | **Build** is documented in full below. The other three, the director-script vocabulary, and the seeding modes are in [`case-shapes.md`](case-shapes.md). @@ -207,7 +207,7 @@ calibration you hand the driver the thread link + login to review the real build case's home, not the repo. Leave the `data/workflows/*.json` file uncommitted (or delete it once it's in the suite). Committing new case JSONs into the repo is no longer the approach. (Exception: seeded cases can't be pushed — a - `seedFile`/`priorConversation` case stays committed JSON, a `seedThread` case is + `conversationSeed`/`priorConversation` case stays committed JSON, a `seedThread` case is a local throwaway; see [`case-shapes.md`](case-shapes.md).) For a sourced case, finish by **linking it to its source thread/finding** over the MCP — see [Link the pushed case to its source](#link-the-pushed-case-to-its-source-provenance-step--always-do-this). @@ -661,10 +661,10 @@ npx dotenvx run -f .env.eval -- pnpm eval:langtracer-push --suite baseline --cha deployment predating that change silently ignores the key; if a pushed scenario edit doesn't land, update the scenario in the lang-tracer UI. - **Seeded cases can't be pushed:** the case-write API rejects every seeding mode - (`seedThread` / `seedFile` / `priorConversation`), so the push lists them under + (`seedThread` / `conversationSeed` / `priorConversation`), so the push lists them under `skipped:` and they never reach the suite. A `seedThread` case shouldn't be committed either — it dies when its trace is pruned or deleted — so derive a - durable synthetic case as the artifact instead. A `seedFile`/`priorConversation` + durable synthetic case as the artifact instead. A `conversationSeed`/`priorConversation` case isn't transient and has no suite home, so it's the one exception to "don't commit the JSON" — it lives as a committed artifact. diff --git a/.agents/skills/create-instance-ai-eval/case-shapes.md b/.agents/skills/create-instance-ai-eval/case-shapes.md index ff10c7c27ef..a4ddbd37400 100644 --- a/.agents/skills/create-instance-ai-eval/case-shapes.md +++ b/.agents/skills/create-instance-ai-eval/case-shapes.md @@ -16,7 +16,7 @@ The schema ([`harness/schema.ts`](../../../packages/@n8n/instance-ai/evaluations/harness/schema.ts)) enforces the rules you must respect: -- `seedFile`, `priorConversation`, `seedThread` are **mutually exclusive** — pick +- `conversationSeed`, `priorConversation`, `seedThread` are **mutually exclusive** — pick one seeding mode. - A case needs a `conversation` **or** a `seedThread` (which supplies the live turn from the trace). @@ -149,7 +149,7 @@ Pick the lightest mode that fits: |---|---|---| | Reproduce a real conversation (common case) | `seedThread` — fetch + reconstruct its LangSmith trace at run time; nothing committed | supplies its own live turn (omit `conversation`) | | Prelude is just "what was discussed" (no tool calls, no workflows) | `priorConversation` — prose turns, authored inline | a normal `conversation` for the live turn | -| A synthetic/sanitised fixture you want durable in git | `seedFile` — a committed seed JSON (never real conversation data) | a normal `conversation` for the live turn | +| Prior work already exists (a workflow to repair) | `conversationSeed` — prior messages + the workflows they reference, in the case body | a normal `conversation` for the live turn | | Shallow 2–3 turn prelude where the agent's live replies matter | none — a plain multi-turn `conversation` re-drives it live | — | All three modes are implemented and wired (`harness/conversation-seed.ts` + @@ -175,7 +175,7 @@ after the correction. Asserting on the seeded prelude itself proves nothing. ### Which mode — and when to avoid seedThread Default to a **synthetic** case (an authored prompt + director script, or a -`priorConversation` / `seedFile` prelude): it's durable, carries no real user +`priorConversation` / `conversationSeed` prelude): it's durable, carries no real user data, never expires, and you control the setup exactly. Reach for **`seedThread`** only when the misbehaviour genuinely needs real prior context that's impractical to synthesize — a long accumulated thread, specific built workflows/tables — **and** @@ -236,10 +236,10 @@ which user turn goes live. (a synthetic case whose `executionScenarios` precondition builds the stand-in), or grade the live turn with `processExpectations` only. - **Can't be pushed to a lang-tracer suite either.** The case-write API rejects - every seeding mode (`seedThread` / `seedFile` / `priorConversation`), so + every seeding mode (`seedThread` / `conversationSeed` / `priorConversation`), so `eval:langtracer-push` silently lists them under `skipped:`. Combined with the don't-commit rule above, a `seedThread` case has **no durable home by design** — - the durable artifact is always the synthetic case you derive from it. (`seedFile` + the durable artifact is always the synthetic case you derive from it. (`conversationSeed` and `priorConversation` carry no thread dependency and can't be pushed either, so — unlike a normal case — they're the one exception to the skill's "push, don't commit the JSON" rule: they live as committed artifacts.) @@ -256,11 +256,20 @@ which user turn goes live. Plain text only — no tool calls, no restored workflows. Paired with a normal `conversation` for the live turn. -### `seedFile` — durable synthetic fixture +### `conversationSeed` — durable synthetic fixture -For a **synthetic, sanitised** fixture pinned in git (never a real user's -conversation): hand-author `data/workflows/seeds/.seed.json` (schema in +For a **synthetic, sanitised** seed pinned in git (never a real user's +conversation): author the prior messages, plus the workflows they reference, in +the case body (schema in [`harness/conversation-seed.ts`](../../../packages/@n8n/instance-ai/evaluations/harness/conversation-seed.ts) -— `messages` + optional `workflows` + `dataTables`) and point `seedFile` at it. -Real conversations belong in `seedThread`, which keeps their content out of the -repo. +— `messages` + optional `workflows` + `dataTables`). Real conversations belong in +`seedThread`, which keeps their content out of the repo. + +Two constraints that bite: a workflow `id` must be ≥8 characters (the id remap +refuses shorter ones), and a seeded `build-workflow` tool call's +`output.workflowId` must match the seeded workflow's `id` — otherwise the remap +separates them and the agent can't find the workflow it should act on. + +The seed sits in the case body, not a sibling file, so it travels with the case +whether it comes off disk, out of a LangTracer suite, or from a dispatched case +body. diff --git a/.agents/skills/create-instance-ai-eval/sourcing-cases.md b/.agents/skills/create-instance-ai-eval/sourcing-cases.md index 2b6a16ad6e8..aec94e5732d 100644 --- a/.agents/skills/create-instance-ai-eval/sourcing-cases.md +++ b/.agents/skills/create-instance-ai-eval/sourcing-cases.md @@ -56,7 +56,7 @@ connected.) 5. **Push it to a curated suite** (don't commit the JSON) with `eval:langtracer-push` — see [Push to a lang-tracer suite](SKILL.md#push-to-a-lang-tracer-suite). Exception: - seeded cases (`seedThread` / `seedFile` / `priorConversation`) can't be pushed — + seeded cases (`seedThread` / `conversationSeed` / `priorConversation`) can't be pushed — the case-write API rejects every seeding mode, so the push lists them under `skipped:`. And a `seedThread` case shouldn't be committed either — it dies when its trace is pruned or deleted — so it has no durable home; that's exactly why diff --git a/.agents/skills/public-api/SKILL.md b/.agents/skills/public-api/SKILL.md index 074603a2506..b53559b9a1d 100644 --- a/.agents/skills/public-api/SKILL.md +++ b/.agents/skills/public-api/SKILL.md @@ -1,23 +1,62 @@ --- name: n8n:public-api description: >- - Adds or updates n8n Public API v1 endpoints, OpenAPI specs, and handler wiring. - Use when creating public API handlers, registering paths in openapi.yml, or adding OpenAPI tags. Always use @PublicApiController for new endpoints (API-37+). + Adds, migrates, or updates n8n Public API v1 endpoints with @PublicApiController + — public DTOs, API-key and RBAC scopes, cursor pagination, OpenAPI + coverage + wiring, and tests. Use when working under packages/cli/src/public-api/v1/ or + when exposing an existing service through /api/v1. --- # Public API v1 -Public API lives under `packages/cli/src/public-api/v1/`. +Public API v1 lives in `packages/cli/src/public-api/v1/`, mounted at `/api/v1` +with API-key auth and public error formatting via `PublicApiControllerRegistry` +(`packages/cli/src/public-api/public-api-controller.registry.ts`). -**Always use `@PublicApiController` for new endpoints (API-37+).** Do not add new business logic to `express-openapi-validator` handlers. Controllers use the same decorator style as internal `@RestController`, mounted at `/api/v1` with API-key auth and public errors via `PublicApiControllerRegistry`. +Two rule tiers: **invariants** (never break) and **team defaults** (follow unless +an existing public contract forces otherwise). When this skill and the code +disagree on a detail, the code wins — so open the files below. That is a reason to +check the code, not license to drop a team default. -**Legacy (existing endpoints only):** `express-openapi-validator` handlers under `handlers/`; OpenAPI path specs are YAML under each handler's `spec/` directory. -When you touch a legacy endpoint, migrate it to `@PublicApiController` rather than extending the eov handler. Do not mix data-access styles within a new feature. +## 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 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 + `Container.get(…Repository)` (`no-repository-in-public-api-handler`). +- Input/output go through DTOs from `@n8n/api-types`; every JSON route declares + `@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`) +and can't be silenced inline (`no-public-api-guardrail-disable`). The `off` +allowlist there covers pre-existing legacy files only — it's shrink-only, don't +add to it. + +## Team defaults + +- List endpoints: cursor-based pagination (internal API uses both cursor- and + page-based — don't copy an internal endpoint's model). +- Updates: full-object `PUT`, not `PATCH`. A successful `GET` body should be + acceptable as a `PUT` body for the same resource (round-trip), aside from + server-managed/immutable fields. +- Strict input DTOs; output DTOs are an allowlist of public fields. +- Never return real secrets/tokens in responses or error details — mask with the + resource's sentinel/placeholder (or omit). Echoing that sentinel on `PUT` + means keep; any other value replaces. Detail: + [Updates and write-only secrets](reference.md#updates-and-write-only-secrets). +- "Test connection/config" endpoints validate the request body (test-before-save). ## Architecture -Convergence is at the **service layer**, not HTTP. Public and internal stay as -two sibling routes over one shared service — neither calls the other: +Public and internal are sibling routes over one shared, HTTP-agnostic service; +neither calls the other. ``` GET /rest/tags → TagsController ┐ JWT auth, internal shape @@ -25,95 +64,101 @@ GET /rest/tags → TagsController ┐ JWT auth, internal shape GET /api/v1/tags → TagsPublicController ┘ API-key auth, public DTO ``` -## Adding an endpoint (required: controller pattern) +Reuse the service behavior. Reuse a DTO only when public and internal contracts +are intentionally identical; otherwise make a public-specific DTO that doesn't +depend on a UI-oriented internal shape. -Reference: `v1/controllers/tags.public.controller.ts` (`GET /tags`). +## Before editing -1. **Public DTOs** in `@n8n/api-types` — input query/body + output resource DTO - (distinct from internal shapes when they diverge). Use `Z.class` so the - registry can validate/parse. Wrap list payloads as objects (e.g. `{ data: [...] }`), - same as `TagListPublicDto` / `WorkflowVersionHistoryListPublicDto`. -2. **Controller** — `v1/controllers/.public.controller.ts` - ```ts - @PublicApiController('/tags') - export class TagsPublicController { - constructor(private readonly tagService: TagService) {} +Open these — they are the source of truth, not this skill: - @Get('/') - @ApiKeyScope('tag:list') - @ApiResponse(TagListPublicDto) - async getTags(_req, _res, @Query q: ListTagsQueryDto) { /* call service */ } - } - ``` - - Reuse `@Get/@Post/@Body/@Query/@Param/@GlobalScope/@ProjectScope` as-is. - - For `@ProjectScope` on workflow/credential routes, name the path param - `workflowId` / `credentialId` (or `projectId` / `dataTableId`) — the - registry passes `req.params` to `userHasScopes`, which does not remap `id`. - - `@ApiKeyScope` — string, or `{ anyOf }` / `{ allOf }` (no bare arrays). - - `@ApiResponse(Dto)` — registry `.parse()`s the return value (strips - undeclared fields). Shape is provisional until API-39 (doc-gen). - - Delegate to the same service as the internal REST controller. -3. **Side-effect import** the controller from - `packages/cli/src/public-api/v1/controllers/index.ts` (re-exported via - `public-api/index.ts`) so metadata is registered before - `PublicApiControllerRegistry.activate`. Name the file - `*.public.controller.ts` — `public-api-controllers.test.ts` fails CI if a - controller file is missing from that barrel or lives outside `controllers/`. -4. **OpenAPI path spec** — still required for docs + `scope-parity.test.ts` - (`handlers//spec/paths/…`, `$ref` in `openapi.yml`). Set - `operationId`, `x-required-scope` matching `@ApiKeyScope`. **Do not** set - `x-eov-operation-id` / `x-eov-operation-handler` — those are legacy eov-only; - scope-parity and discover read `@ApiKeyScope` from the controller by matching - method + path. -5. **Coverage manifest** — add every new OpenAPI endpoint to - `packages/nodes-base/nodes/N8n/n8n-api-coverage.json`. +- `v1/controllers/` — copy structure from `tags.public.controller.ts` (list + + cursor) or `workflows.public.controller.ts` (`@Param` + `@ProjectScope`), and + `index.ts` for the barrel. +- Decorators in `packages/@n8n/decorators/src/controller/`: + `public-api-controller.ts`, `api-key-scope.ts`, `api-response.ts`, `route.ts`, + `scoped.ts`, `args.ts`, `licensed.ts`. +- Pagination helpers: `v1/shared/services/pagination.service.ts` + (`decodeCursor`, `encodeNextCursor`). +- DTOs: `packages/@n8n/api-types/src/dto/`. +- Gating tests: `v1/__tests__/public-api-controllers.test.ts`, + `v1/__tests__/scope-parity.test.ts`. +- The internal controller for this resource and its neighboring functional tests. -## Legacy eov handlers (do not use for new endpoints) +## Declaring a controller -Only for maintaining endpoints not yet migrated: +A controller is a class marked `@PublicApiController('/base')` that injects the +shared service via its constructor and delegates to it. Copy the shape from an +existing controller in `v1/controllers/` with the same operation type and auth +model; reuse only what applies. Decorators, all from `@n8n/decorators`: -1. **Handler** — `handlers//.handler.ts` - - Export middleware arrays keyed by `x-eov-operation-id`. - - For scope-protected endpoints, use `publicApiScope()` or - `apiKeyHasScopeWithGlobalScopeFallback()` (they tag the scope-enforcement - middleware with `__apiKeyScope`) and add matching `x-required-scope` in the - path YAML; use `none` for endpoints without an API-key scope (see - `scope-parity.test.ts`). - - **Delegate to the same service layer as the internal REST API.** - Parse input with `@n8n/api-types` DTOs, call `Container.get(SomeService)`, - map errors — do not reach into repositories or duplicate business logic. -2. **OpenAPI path spec** — `handlers//spec/paths/.yml` - - Set `tags: []` matching a top-level tag in `openapi.yml`. -3. **Register path** — add a `$ref` entry under `paths:` in `openapi.yml`. -4. **Request types** — add a namespace in `packages/cli/src/public-api/types.ts` - when the handler needs typed query/body params. -5. **Coverage manifest** — add every new OpenAPI endpoint to - `packages/nodes-base/nodes/N8n/n8n-api-coverage.json`. +| Decorator | Use | +|---|---| +| `@PublicApiController('/base')` | Class marker; mounts routes at `/api/v1/base`. | +| `@Get/@Post/@Put/@Patch/@Delete('/path')` | Route method. | +| `@ApiKeyScope('res:action')` | API-key grant check. | +| `@ProjectScope/@GlobalScope('res:action')` | User RBAC check. | +| `@ApiResponse(Dto)` | Output DTO; registry `.parse()`s + strips the return value. | +| `@Query` / `@Body` / `@Param('name')` | Bind + validate via a `Z.class` DTO / path param. | +| `@Licensed('feat')` | Gate an EE-only endpoint. | -## `tags` array in `openapi.yml` +## Authorization (easy to get wrong) -When adding or editing the top-level `tags:` array in -`packages/cli/src/public-api/v1/openapi.yml`, **keep entries sorted -alphabetically by `name`**. Insert the new tag in order; do not append to the end. +- `@ApiKeyScope` (what the API key is granted) and `@ProjectScope`/`@GlobalScope` + (what the user may do) are independent. Use both when the model needs both. +- `@ProjectScope` reads `req.params` as-is and does not remap `id` — name the path + param what the resolver expects (`workflowId`, `credentialId`, `projectId`, + `dataTableId`, …). A generic `id` often fails. +- `@ApiKeyScope` takes a string, `{ anyOf: [...] }`, or `{ allOf: [...] }` — never + a bare array. The scope must exist in the permissions registry + (`API_KEY_RESOURCES` in `@n8n/permissions`); `scope-parity.test.ts` fails on an + orphan scope. -```yaml -tags: - - name: Audit - description: Operations about security audit - - name: CommunityPackage - description: Operations about community packages - # … remaining tags in A→Z order by name -``` +## DTOs -`scope-parity.test.ts` asserts this ordering — CI fails if tags drift out of -sort order. +- Build the public response shape explicitly; don't return an ORM entity and lean + on `@ApiResponse` stripping to hide fields. +- Treat the output DTO as an allowlist. Re-check nested relations, ownership + fields, tokens, and encrypted values. +- Make input DTOs strict so unknown/partial fields aren't silently accepted. +- Secrets: never return a real secret; use the resource's sentinel/placeholder + (or omit). See [Updates and write-only secrets](reference.md#updates-and-write-only-secrets). -## Reference +## List endpoints (cursor pagination) -- **Controller pattern (required for new work):** `v1/controllers/tags.public.controller.ts`, `v1/controllers/workflows.public.controller.ts` -- **Registry:** `packages/cli/src/public-api/public-api-controller.registry.ts` -- Legacy eov examples (migration targets, not templates for new endpoints): - `handlers/insights/`, `handlers/variables/`, `handlers/folders/`, - `handlers/projects/`, `handlers/data-tables/`, `handlers/workflows/` -- Multipart: `handlers/n8n-packages/` (see also - `packages/cli/src/modules/n8n-packages/CLAUDE.md`) +Copy the cursor flow from `tags.public.controller.ts`. Use `publicApiPaginationSchema` +plus `decodeCursor` / `encodeNextCursor` from the shared pagination service; the +cursor is opaque; return `{ data, nextCursor }` (never a bare array) with +`nextCursor: null` on the last page; an invalid cursor is a `400`. Preserve an +existing endpoint's pagination as-is. Detail: +[List endpoints and cursor pagination](reference.md#list-endpoints-and-cursor-pagination). + +## Wiring checklist + +1. `v1/controllers/.public.controller.ts` + side-effect import in + `v1/controllers/index.ts`. +2. Public DTO in `@n8n/api-types` + export from the barrel (`src/dto/`). +3. `@ApiKeyScope` value exists in the permissions registry. +4. OpenAPI path with `x-required-scope` matching `@ApiKeyScope`; do **not** set + `x-eov-operation-*` for controller routes; keep top-level `tags:` sorted A→Z. +5. Add the route to `packages/nodes-base/nodes/N8n/n8n-api-coverage.json`. +6. Tests. + +## Testing + +Always cover: happy path, input-validation failure, missing API-key scope, RBAC +denial. Prefer covering the business path in +`packages/cli/test/integration/public-api/` (real HTTP + DB); mocked-service unit +tests don't replace that. Add the cases that apply (cursor pages, +not-found/conflict, no sensitive fields, credential keep/replace, migration +contract) — see [Testing matrix](reference.md#testing-matrix). Match the nearest +existing tests. + +## More detail (reference.md) + +- [List endpoints and cursor pagination](reference.md#list-endpoints-and-cursor-pagination) +- [Updates and write-only secrets](reference.md#updates-and-write-only-secrets) +- [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) diff --git a/.agents/skills/public-api/reference.md b/.agents/skills/public-api/reference.md new file mode 100644 index 00000000000..80aa8132901 --- /dev/null +++ b/.agents/skills/public-api/reference.md @@ -0,0 +1,115 @@ +# Public API v1 — reference + +Detail for tasks that need it. Essentials and the rule tiers are in +[SKILL.md](SKILL.md). Open the cited files; they are the source of truth. + +## List endpoints and cursor pagination + +The internal API mixes cursor- and page-based pagination. New Public API list +endpoints use **cursor-based** pagination — do not copy an internal controller's +model. + +Copy the working flow from `v1/controllers/tags.public.controller.ts` (and +`workflows.public.controller.ts` for a `@Param` list) rather than pasting a +snippet here — a copy would drift. The moving parts: + +- Input DTO composes `publicApiPaginationSchema` from `@n8n/api-types` (a `limit` + plus an opaque `cursor`). +- `decodeCursor` / `encodeNextCursor` live in + `v1/shared/services/pagination.service.ts`. Decode the incoming cursor to + `{ offset, limit }`, guard the decoded shape, and pass `offset`/`limit` to the + service. +- Treat the cursor as opaque; never hand-encode a token. +- Return an envelope `{ data, nextCursor }` — never a bare array. +- `encodeNextCursor(...)` returns `null` when there is no further page; surface + that as `nextCursor: null`. +- An invalid/undecodable cursor is a `400` via the existing bad-request error. +- For an existing list endpoint, keep its current pagination semantics unchanged. + +The output DTO wraps the list as `{ data, nextCursor }` and is declared with +`@ApiResponse(...)` so the registry strips undeclared fields. + +## Updates and write-only secrets + +- Default to `PUT`. The update DTO describes the full mutable object; the + validation layer rejects a partial payload (typically `400`). Don't implement + merge semantics behind a `PUT`. +- Don't add a new `PATCH` unless the task explicitly requires partial-update + semantics or an established resource-specific exception applies. +- Migrating an update endpoint keeps its current public HTTP semantics. + +Write-only secrets (credentials, tokens, keys) support GET→PUT round-trip via a +resource-specific sentinel/placeholder (e.g. credentials use +`CREDENTIAL_BLANKING_VALUE` / related helpers — do not invent a new format): + +- `GET` never returns the real secret; it returns that sentinel (or omits the + field). Never echo a real secret in responses or error details (including + test-connection and upstream error messages). +- On `PUT`, sending the **exact** sentinel from `GET` means **keep** the stored + secret. Sending **any other** value means **replace** it. Do not treat + "looks masked" or "field omitted" as keep unless the resource helper/tests say + so. +- Reuse the resource's redact/unredact (or equivalent) helper in the service — + don't reimplement sentinel detection or credential merge in the controller, and + never persist the sentinel as a real secret. +- Sentinel support does not make the whole `PUT` a partial update; other required + client-manageable fields stay required. Server-managed/immutable fields from + `GET` (`id`, timestamps, …) follow the resource DTO (ignored or not required on + write). + +## Test-before-save endpoints + +A connection/config-test endpoint validates the config in the **request body**, +not stored state — unless the endpoint explicitly verifies an already-saved +resource. Secret handling is the same as any other endpoint — see above. + +## Errors + +- Don't leak persistence errors, stack traces, or internal messages. Reuse + existing domain errors when the registry already maps them to the right public + errors; otherwise map at the controller boundary. +- Follow the error semantics of the nearest existing public controller; invalid + input and invalid cursors use the existing bad-request pattern. +- When migrating, preserve the documented status codes and public error behavior. + +## Testing matrix + +Always (in SKILL.md): happy path, input-validation failure, missing API-key +scope, RBAC denial. Add whichever apply, matching the nearest existing tests: + +- At least one integration test under `packages/cli/test/integration/public-api/` + that exercises the real service/DB path for the main success case (and + paging/RBAC where they matter). Controller unit tests with a mocked service are + fine for wiring/validation edges — not as the only coverage of behavior. +- Cursor paging: first page, final page with `nextCursor: null`, invalid cursor, + `limit` handling. +- Not-found and conflict semantics. +- Response carries no sensitive/internal fields. +- Credential resources: response has no real secret (sentinel or omitted); + `PUT` with the exact sentinel keeps the secret; any other value replaces it; + the sentinel is never persisted as a real secret. +- Migration: path, method, status codes, scope, and response contract are + unchanged. + +## 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. +- A route must be served by either the EOV handler or a controller, not both — + `scope-parity.test.ts` rejects a duplicated route. Remove the legacy wiring + (its `x-eov-operation-*` entry and handler middleware) only after the new + controller is registered and the spec + tests are updated. +- 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). +- For complex legacy-only, multipart, or non-standard endpoints, study the + nearest existing handler first. diff --git a/.github/CLAUDE.md b/.github/CLAUDE.md index 111d7a23aad..ac305248160 100644 --- a/.github/CLAUDE.md +++ b/.github/CLAUDE.md @@ -30,7 +30,12 @@ Reusable workflows: add `-reusable` or `-callable` suffix. ### Common Tasks -**Add workflow:** Create in `workflows/`, document in `WORKFLOWS.md` +**Add workflow:** Create in `workflows/`, document in `WORKFLOWS.md`. +Always declare a least-privilege top-level `permissions:` block (usually +`contents: read`) — without one the workflow runs with the repo's broad +default token and review flags it. Jobs needing more override at job level; +a job calling a reusable workflow must grant at least what that workflow +declares. **Add script:** Create `.mjs` in `scripts/`, document in `WORKFLOWS.md` diff --git a/.github/WORKFLOWS.md b/.github/WORKFLOWS.md index 5c5f844bb4e..0da4a4153ac 100644 --- a/.github/WORKFLOWS.md +++ b/.github/WORKFLOWS.md @@ -179,7 +179,7 @@ These only run if specific files changed: | `**/package.json`, `**/turbo.json` | `build-windows.yml` | master | | `packages/@n8n/ai-workflow-builder.ee/evaluations/programmatic/python/**` | `test-evals-python.yml` | any | | `packages/@n8n/benchmark/**` | `build-benchmark-image.yml` | master | -| `packages/cli/src/public-api/**/*.{css,yaml,yml}` | `util-sync-api-docs.yml` | master | +| `packages/cli/src/public-api/**/*.{css,yaml,yml}` | `util-publish-api-schema.yml` | master | | `packages/@n8n/instance-ai/src/**`, `packages/@n8n/instance-ai/skills/**`, `packages/@n8n/instance-ai/knowledge-base/**`, `packages/@n8n/instance-ai/evaluations/**`, `packages/cli/src/modules/instance-ai/**`, `packages/core/src/execution-engine/eval-mock-helpers.ts` | `ci-instance-ai-evals.yml` | on PR `opened` / `reopened` / `ready_for_review` | ### On PR Review diff --git a/.github/test-metrics/e2e-impact-map.json b/.github/test-metrics/e2e-impact-map.json index 76102e12795..da9313b53f0 100644 --- a/.github/test-metrics/e2e-impact-map.json +++ b/.github/test-metrics/e2e-impact-map.json @@ -1 +1 @@ -{"specs":["tests/e2e/ai/ai-2505-pdf-embed.spec.ts","tests/e2e/ai/langchain-agents.spec.ts","tests/e2e/ai/langchain-vectorstores.spec.ts","tests/e2e/chat-hub/chat-hub-attachment.spec.ts","tests/e2e/chat-hub/chat-hub-basic.spec.ts","tests/e2e/chat-hub/chat-hub-chat-user.spec.ts","tests/e2e/workflows/editor/execution/logs.spec.ts","tests/e2e/ai/langchain-chains.spec.ts","tests/e2e/credentials/crud.spec.ts","tests/e2e/dynamic-credentials/mcp-trigger-credential-gate.spec.ts","tests/e2e/mcp-registry/mcp-registry.spec.ts","tests/e2e/nodes/mcp-trigger.spec.ts","tests/e2e/regression/AI-1401-sub-nodes-input-panel.spec.ts","tests/e2e/workflows/editor/ndv/ndv-floating-nodes.spec.ts","tests/e2e/workflows/editor/ndv/ndv-parameters.spec.ts","tests/e2e/ai/assistant-basic.spec.ts","tests/e2e/ai/assistant-code-help.spec.ts","tests/e2e/ai/assistant-credential-help.spec.ts","tests/e2e/ai/assistant-support-chat.spec.ts","tests/e2e/ai/builder-setup-wizard.spec.ts","tests/e2e/ai/workflow-builder.spec.ts","tests/e2e/api/binary-data-xss.spec.ts","tests/e2e/api/wait-form-resume.spec.ts","tests/e2e/api/waiting-endpoint-security.spec.ts","tests/e2e/api/webhook-external.spec.ts","tests/e2e/api/workflow-publication-service.spec.ts","tests/e2e/app-config/security-notifications.spec.ts","tests/e2e/auth/oidc.spec.ts","tests/e2e/building-blocks/canvas-actions.spec.ts","tests/e2e/building-blocks/credentials.spec.ts","tests/e2e/building-blocks/workflow-entry-points.spec.ts","tests/e2e/capabilities/proxy-server.spec.ts","tests/e2e/capabilities/task-runner.spec.ts","tests/e2e/chat-hub/chat-hub-workflow-agent.spec.ts","tests/e2e/credentials/global.spec.ts","tests/e2e/credentials/oauth.spec.ts","tests/e2e/data-tables/details.spec.ts","tests/e2e/instance-ai/instance-ai-artifacts.spec.ts","tests/e2e/instance-ai/instance-ai-confirmations.spec.ts","tests/e2e/instance-ai/instance-ai-remediation-guard.spec.ts","tests/e2e/instance-ai/instance-ai-workflow-execution.spec.ts","tests/e2e/instance-ai/instance-ai-workflow-preview.spec.ts","tests/e2e/nodes/email-send-node.spec.ts","tests/e2e/nodes/http-request-node.spec.ts","tests/e2e/nodes/if-node.spec.ts","tests/e2e/nodes/kafka-nodes.spec.ts","tests/e2e/nodes/pdf-node.spec.ts","tests/e2e/nodes/send-and-wait.spec.ts","tests/e2e/projects/folders-advanced.spec.ts","tests/e2e/projects/folders-operations.spec.ts","tests/e2e/projects/projects-move-resources.spec.ts","tests/e2e/projects/projects.spec.ts","tests/e2e/redaction-enforcement/redaction-enforcement.spec.ts","tests/e2e/regression/ADO-1338-ndv-missing-input-panel.spec.ts","tests/e2e/regression/ADO-2372-prevent-clipping-params.spec.ts","tests/e2e/regression/CAT-726-canvas-node-connectors-not-rendered-when-nodes-inserted.spec.ts","tests/e2e/regression/SUG-38-inline-expression-preview.spec.ts","tests/e2e/scheduling/schedule-trigger-timezone.spec.ts","tests/e2e/settings/external-secrets/secret-providers-connections-ui.spec.ts","tests/e2e/settings/log-streaming/log-streaming-delivery.spec.ts","tests/e2e/settings/log-streaming/log-streaming-observability.spec.ts","tests/e2e/settings/log-streaming/log-streaming.spec.ts","tests/e2e/sharing/access-control.spec.ts","tests/e2e/sharing/credential-visibility.spec.ts","tests/e2e/workflows/editor/execution/executions-list-infinite-scroll.spec.ts","tests/e2e/workflows/editor/subworkflows/subworkflow-version-resolution.spec.ts","tests/e2e/workflows/editor/viewer-permissions.spec.ts","tests/e2e/workflows/editor/workflow-actions/archive.spec.ts","tests/e2e/workflows/templates/credentials-setup.spec.ts","tests/e2e/app-config/versions.spec.ts","tests/e2e/building-blocks/credential-mismatch.spec.ts","tests/e2e/cloud/cloud.spec.ts","tests/e2e/instance-ai/instance-ai-attachments.spec.ts","tests/e2e/instance-ai/instance-ai-background-lifecycle.spec.ts","tests/e2e/instance-ai/instance-ai-chat-basics.spec.ts","tests/e2e/instance-ai/instance-ai-sidebar.spec.ts","tests/e2e/instance-ai/thread-launcher.spec.ts","tests/e2e/node-creator/navigation.spec.ts","tests/e2e/regression/ADO-4462-template-setup-experiment.spec.ts","tests/e2e/sentry/sentry-baseline.spec.ts","tests/e2e/settings/environments/source-control.spec.ts","tests/e2e/settings/personal/two-factor-authentication.spec.ts","tests/e2e/ai/chat-session.spec.ts","tests/e2e/ai/eval-collections-compare.spec.ts","tests/e2e/app-config/demo-reimport.spec.ts","tests/e2e/app-config/demo.spec.ts","tests/e2e/auth/admin-smoke.spec.ts","tests/e2e/auth/authenticated.spec.ts","tests/e2e/auth/signin.spec.ts","tests/e2e/building-blocks/node-details-configuration.spec.ts","tests/e2e/building-blocks/user-service.spec.ts","tests/e2e/chat-hub/chat-hub-personal-agent.spec.ts","tests/e2e/chat-hub/chat-hub-settings.spec.ts","tests/e2e/chat-hub/chat-hub-tools.spec.ts","tests/e2e/data-tables/tables.spec.ts","tests/e2e/instance-ai/instance-ai-out-of-credits.spec.ts","tests/e2e/node-creator/actions.spec.ts","tests/e2e/node-creator/special-nodes.spec.ts","tests/e2e/node-creator/vector-stores.spec.ts","tests/e2e/node-creator/workflows.spec.ts","tests/e2e/nodes/community-nodes.spec.ts","tests/e2e/nodes/form-trigger-node.spec.ts","tests/e2e/nodes/schedule-trigger-node.spec.ts","tests/e2e/nodes/webhook.spec.ts","tests/e2e/projects/folders-basic.spec.ts","tests/e2e/projects/project-settings.spec.ts","tests/e2e/regression/ADO-2230-ndv-reset-data-pagination.spec.ts","tests/e2e/regression/ADO-2929-can-load-old-switch-node-workflows.spec.ts","tests/e2e/regression/AI-716-correctly-set-up-agent-model-shows-error.spec.ts","tests/e2e/regression/AI-812-partial-execs-broken-when-using-chat-trigger.spec.ts","tests/e2e/regression/CAT-2395-node-spacing.spec.ts","tests/e2e/regression/PAY-4367-node-shifting-cyclic.spec.ts","tests/e2e/regression/SUG-121-fields-reset-after-closing-ndv.spec.ts","tests/e2e/settings/community-nodes/community-nodes.spec.ts","tests/e2e/settings/environments/variables.spec.ts","tests/e2e/settings/log-streaming/log-streaming-ui-e2e.spec.ts","tests/e2e/settings/personal/personal.spec.ts","tests/e2e/settings/users/users.spec.ts","tests/e2e/settings/workers/workers.spec.ts","tests/e2e/sharing/credential-sharing.spec.ts","tests/e2e/sharing/workflow-sharing.spec.ts","tests/e2e/workflows/checklist/production-checklist.spec.ts","tests/e2e/workflows/demo-diff.spec.ts","tests/e2e/workflows/demo-executable-chat-trigger.spec.ts","tests/e2e/workflows/editor/canvas/actions.spec.ts","tests/e2e/workflows/editor/canvas/canvas-nodes.spec.ts","tests/e2e/workflows/editor/canvas/canvas-zoom.spec.ts","tests/e2e/workflows/editor/canvas/focus-panel.spec.ts","tests/e2e/workflows/editor/canvas/node-groups.spec.ts","tests/e2e/workflows/editor/canvas/stickies.spec.ts","tests/e2e/workflows/editor/canvas/undo-redo.spec.ts","tests/e2e/workflows/editor/code/code-node.spec.ts","tests/e2e/workflows/editor/code/editors.spec.ts","tests/e2e/workflows/editor/editor-after-route-changes.spec.ts","tests/e2e/workflows/editor/execution/debug.spec.ts","tests/e2e/workflows/editor/execution/execution.spec.ts","tests/e2e/workflows/editor/execution/inject-previous.spec.ts","tests/e2e/workflows/editor/execution/partial.spec.ts","tests/e2e/workflows/editor/expressions/inline.spec.ts","tests/e2e/workflows/editor/expressions/mapping.spec.ts","tests/e2e/workflows/editor/expressions/modal.spec.ts","tests/e2e/workflows/editor/expressions/transformation.spec.ts","tests/e2e/workflows/editor/ndv/io-filter.spec.ts","tests/e2e/workflows/editor/ndv/ndv-core.spec.ts","tests/e2e/workflows/editor/ndv/ndv-data-display.spec.ts","tests/e2e/workflows/editor/ndv/paired-item.spec.ts","tests/e2e/workflows/editor/ndv/pinning.spec.ts","tests/e2e/workflows/editor/ndv/resource-locator.spec.ts","tests/e2e/workflows/editor/ndv/resource-mapper.spec.ts","tests/e2e/workflows/editor/ndv/schema-preview.spec.ts","tests/e2e/workflows/editor/routing.spec.ts","tests/e2e/workflows/editor/subworkflows/debugging.spec.ts","tests/e2e/workflows/editor/subworkflows/extraction.spec.ts","tests/e2e/workflows/editor/subworkflows/workflow-selector.spec.ts","tests/e2e/workflows/editor/tags.spec.ts","tests/e2e/workflows/editor/workflow-actions/publish.spec.ts","tests/e2e/workflows/executions/filter.spec.ts","tests/e2e/workflows/executions/list.spec.ts","tests/e2e/workflows/executions/preview-isolation.spec.ts","tests/e2e/workflows/list/import.spec.ts","tests/e2e/workflows/list/workflows.spec.ts","tests/e2e/workflows/templates/templates.spec.ts","tests/e2e/api/form-endpoint-isolation.spec.ts","tests/e2e/dynamic-credentials/execution-status.spec.ts","tests/e2e/mcp/mcp-service.spec.ts","tests/e2e/scheduling/poll-trigger-durable.spec.ts","tests/e2e/scheduling/poll-trigger-legacy.spec.ts","tests/e2e/scheduling/schedule-trigger-durable.spec.ts","tests/e2e/api/webhook-isolation.spec.ts","tests/e2e/dynamic-credentials/external-user-trigger.spec.ts","tests/e2e/dynamic-credentials/resolver-deletion.spec.ts","tests/e2e/journeys/admin-views-executions-list.spec.ts","tests/e2e/mcp/mcp-oauth.spec.ts","tests/e2e/api/discovery.spec.ts","tests/e2e/app-config/env-feature-flags.spec.ts","tests/e2e/auth/password-reset.spec.ts","tests/e2e/auth/token-exchange.spec.ts","tests/e2e/instance-ai/instance-ai-multimain.spec.ts","tests/e2e/scheduling/schedule-trigger-multimain.spec.ts","tests/e2e/settings/external-secrets/aws-secrets-manager.spec.ts","tests/e2e/credentials/api-operations.spec.ts","tests/e2e/workflows/editor/execution/subworkflow-stop.spec.ts","tests/e2e/scheduling/schedule-trigger-legacy.spec.ts","tests/e2e/workflows/editor/subworkflows/wait.spec.ts","tests/e2e/nodes/n8n-trigger.spec.ts","tests/e2e/ai/rag-callout.spec.ts","tests/e2e/nodes/code-node-api.spec.ts"],"files":{"packages/@n8n/ai-utilities/src/guards.ts":{"7":[0,1,2,3,4,5,6],"9":[0,1,2,3,4,5,6],"17":[0,1,2,3,4,5,6],"21":[0,1,2,3,4,5,6],"25":[0,1,2,3,4,5,6],"31":[0,1,2,3,4,5,6]},"packages/@n8n/ai-utilities/src/index.ts":{"7":[0,1,7,2,3,4,5,8,9,10,11,12,6,13,14],"12":[0,1,7,2,3,4,5,8,9,10,11,12,6,13,14],"13":[0,1,7,2,3,4,5,8,9,10,11,12,6,13,14],"14":[0,1,7,2,3,4,5,8,9,10,11,12,6,13,14],"16":[0,1,7,2,3,4,5,8,9,10,11,12,6,13,14],"51":[0,1,7,2,3,4,5,8,9,10,11,12,6,13,14],"81":[0,1,7,2,3,4,5,8,9,10,11,12,6,13,14],"89":[0,1,7,2,3,4,5,8,9,10,11,12,6,13,14],"90":[0,1,7,2,3,4,5,8,9,10,11,12,6,13,14],"119":[0,1,7,2,3,4,5,8,9,10,11,12,6,13,14],"120":[0,1,7,2,3,4,5,8,9,10,11,12,6,13,14],"124":[0,1,7,2,3,4,5,8,9,10,11,12,6,13,14]},"packages/@n8n/ai-utilities/src/utils/embeddings-input-validation.ts":{"13":[0,2],"32":[0,2],"40":[0,2]},"packages/@n8n/ai-utilities/src/utils/helpers.ts":{"3":[0,1,2,3,4,5,6],"17":[0,1,2,3,4,5,6],"37":[0,1,2,3,4,5,6]},"packages/@n8n/ai-utilities/src/utils/log-ai-event.ts":{"4":[0,1,2,3,4,5,6]},"packages/@n8n/ai-utilities/src/utils/log-wrapper.ts":{"38":[0,2,3,4,5],"80":[0,2,3,4,5],"109":[0,2,3,4,5],"127":[0,2,3,4,5],"132":[0,2,3,4,5],"155":[0,2,3,4,5],"184":[0,2,3,4,5],"205":[0,2,3,4,5],"227":[0,2,3,4,5],"271":[0,2,3,4,5],"298":[0,2,3,4,5],"325":[0,2,3,4,5],"355":[0,2,3,4,5],"374":[0,2,3,4,5],"398":[0,2,3,4,5],"422":[0,2,3,4,5],"456":[0,2,3,4,5]},"packages/@n8n/ai-utilities/src/utils/n8n-json-loader.ts":{"15":[2],"21":[2],"35":[2],"45":[2],"81":[2]},"packages/@n8n/ai-utilities/src/utils/shared-fields.ts":{"37":[0,1,7,2,3,4,5,8,9,10,11,12,6,13,14],"46":[0,1,7,2,3,4,5,8,9,10,11,12,6,13,14],"110":[0,1,7,2,3,4,5,8,9,10,11,12,6,13,14],"115":[0,1,7,2,3,4,5,8,9,10,11,12,6,13,14],"120":[0,1,7,2,3,4,5,8,9,10,11,12,6,13,14],"125":[0,1,7,2,3,4,5,8,9,10,11,12,6,13,14],"132":[0,1,7,2,3,4,5,8,9,10,11,12,6,13,14],"158":[0,1,7,2,3,4,5,8,9,10,11,12,6,13,14],"164":[0,1,7,2,3,4,5,8,9,10,11,12,6,13,14]},"packages/@n8n/ai-utilities/src/utils/vector-store/MemoryManager/MemoryCalculator.ts":{"21":[0,2],"61":[0,2]},"packages/@n8n/ai-utilities/src/utils/vector-store/MemoryManager/MemoryVectorStoreManager.ts":{"40":[0,2],"69":[0,2],"80":[0,2],"91":[0,2],"106":[0,2],"114":[0,2],"131":[0,2],"138":[0,2],"163":[0,2],"175":[0,2],"182":[0,2],"190":[0,2],"211":[0,2],"268":[0,2]},"packages/@n8n/ai-utilities/src/utils/vector-store/MemoryManager/StoreCleanupService.ts":{"16":[0,2],"31":[0,2],"45":[0,2],"79":[0,2],"117":[0,2],"120":[0,2],"153":[0,2]},"packages/@n8n/ai-utilities/src/utils/vector-store/MemoryManager/config.ts":{"10":[0,2],"38":[0,2],"47":[0,2]},"packages/@n8n/ai-utilities/src/utils/vector-store/createVectorStoreNode/createVectorStoreNode.ts":{"50":[0,2,14],"53":[0,2,14],"417":[0,2,14],"479":[0,2,14]},"packages/@n8n/ai-utilities/src/utils/vector-store/createVectorStoreNode/operations/insertOperation.ts":{"17":[0,2]},"packages/@n8n/ai-utilities/src/utils/vector-store/createVectorStoreNode/utils.ts":{"12":[0,2,14],"16":[0,2,14],"26":[0,2,14],"36":[0,2,14],"41":[0,2,14],"47":[0,2,14]},"packages/@n8n/ai-utilities/src/utils/vector-store/processDocuments.ts":{"7":[0,2],"19":[0,2],"28":[0,2],"41":[0,2]},"packages/@n8n/api-types/src/dto/index.ts":{"1":"b:R5f///////8fAAAAAAAAAAAAAAAAAAAA","3":"b:R5f///////8fAAAAAAAAAAAAAAAAAAAA","4":"b:R5f///////8fAAAAAAAAAAAAAAAAAAAA","12":"b:R5f///////8fAAAAAAAAAAAAAAAAAAAA","16":"b:R5f///////8fAAAAAAAAAAAAAAAAAAAA","36":"b:R5f///////8fAAAAAAAAAAAAAAAAAAAA","37":"b:R5f///////8fAAAAAAAAAAAAAAAAAAAA","40":"b:R5f///////8fAAAAAAAAAAAAAAAAAAAA","44":"b:R5f///////8fAAAAAAAAAAAAAAAAAAAA","51":"b:R5f///////8fAAAAAAAAAAAAAAAAAAAA","53":"b:R5f///////8fAAAAAAAAAAAAAAAAAAAA","55":"b:R5f///////8fAAAAAAAAAAAAAAAAAAAA","59":"b:R5f///////8fAAAAAAAAAAAAAAAAAAAA","61":"b:R5f///////8fAAAAAAAAAAAAAAAAAAAA","64":"b:R5f///////8fAAAAAAAAAAAAAAAAAAAA","66":"b:R5f///////8fAAAAAAAAAAAAAAAAAAAA","72":"b:R5f///////8fAAAAAAAAAAAAAAAAAAAA","73":"b:R5f///////8fAAAAAAAAAAAAAAAAAAAA","89":"b:R5f///////8fAAAAAAAAAAAAAAAAAAAA","92":"b:R5f///////8fAAAAAAAAAAAAAAAAAAAA","98":"b:R5f///////8fAAAAAAAAAAAAAAAAAAAA","107":"b:R5f///////8fAAAAAAAAAAAAAAAAAAAA","108":"b:R5f///////8fAAAAAAAAAAAAAAAAAAAA","110":"b:R5f///////8fAAAAAAAAAAAAAAAAAAAA","118":"b:R5f///////8fAAAAAAAAAAAAAAAAAAAA","119":"b:R5f///////8fAAAAAAAAAAAAAAAAAAAA","127":"b:R5f///////8fAAAAAAAAAAAAAAAAAAAA","128":"b:R5f///////8fAAAAAAAAAAAAAAAAAAAA","129":"b:R5f///////8fAAAAAAAAAAAAAAAAAAAA","131":"b:R5f///////8fAAAAAAAAAAAAAAAAAAAA","133":"b:R5f///////8fAAAAAAAAAAAAAAAAAAAA","137":"b:R5f///////8fAAAAAAAAAAAAAAAAAAAA","138":"b:R5f///////8fAAAAAAAAAAAAAAAAAAAA","151":"b:R5f///////8fAAAAAAAAAAAAAAAAAAAA","157":"b:R5f///////8fAAAAAAAAAAAAAAAAAAAA","164":"b:R5f///////8fAAAAAAAAAAAAAAAAAAAA","165":"b:R5f///////8fAAAAAAAAAAAAAAAAAAAA","180":"b:R5f///////8fAAAAAAAAAAAAAAAAAAAA","181":"b:R5f///////8fAAAAAAAAAAAAAAAAAAAA","211":"b:R5f///////8fAAAAAAAAAAAAAAAAAAAA","216":"b:R5f///////8fAAAAAAAAAAAAAAAAAAAA","218":"b:R5f///////8fAAAAAAAAAAAAAAAAAAAA","228":"b:R5f///////8fAAAAAAAAAAAAAAAAAAAA","231":"b:R5f///////8fAAAAAAAAAAAAAAAAAAAA","232":"b:R5f///////8fAAAAAAAAAAAAAAAAAAAA","238":"b:R5f///////8fAAAAAAAAAAAAAAAAAAAA","272":"b:R5f///////8fAAAAAAAAAAAAAAAAAAAA","284":"b:R5f///////8fAAAAAAAAAAAAAAAAAAAA","285":"b:R5f///////8fAAAAAAAAAAAAAAAAAAAA","287":"b:R5f///////8fAAAAAAAAAAAAAAAAAAAA","288":"b:R5f///////8fAAAAAAAAAAAAAAAAAAAA","297":"b:R5f///////8fAAAAAAAAAAAAAAAAAAAA","298":"b:R5f///////8fAAAAAAAAAAAAAAAAAAAA","310":"b:R5f///////8fAAAAAAAAAAAAAAAAAAAA","311":"b:R5f///////8fAAAAAAAAAAAAAAAAAAAA","314":"b:R5f///////8fAAAAAAAAAAAAAAAAAAAA"},"packages/@n8n/api-types/src/dto/pagination/pagination.dto.ts":{"10":"b:R5Td3KelnIT9/wMAAAAAAAAAAAAAAAAA","11":"b:R5Td3KelnIT9/wMAAAAAAAAAAAAAAAAA","14":"b:R5Td3KelnIT9/wMAAAAAAAAAAAAAAAAA","18":"b://Xf/P///+39//////////////8DAAAA","22":"b:R5Td3KelnIT9/wMAAAAAAAAAAAAAAAAA","23":"b:R5Td3KelnIT9/wMAAAAAAAAAAAAAAAAA","27":"b:R5Td3KelnIT9/wMAAAAAAAAAAAAAAAAA","35":"b:R5Td3KelnIT9/wMAAAAAAAAAAAAAAAAA","47":"b:R5Td3KelnIT9/wMAAAAAAAAAAAAAAAAA","48":"b:R5Td3KelnIT9/wMAAAAAAAAAAAAAAAAA","51":"b:R5Td3KelnIT9/wMAAAAAAAAAAAAAAAAA","55":"b://Xf/P///+39//////////////8DAAAA","59":"b:R5Td3KelnIT9/wMAAAAAAAAAAAAAAAAA","60":"b:R5Td3KelnIT9/wMAAAAAAAAAAAAAAAAA","63":"b:R5Td3KelnIT9/wMAAAAAAAAAAAAAAAAA","66":"b:R5Td3KelnIT9/wMAAAAAAAAAAAAAAAAA"},"packages/@n8n/api-types/src/dto/user/users-list-filter.dto.ts":{"51":"b:R5Td3KelnIT9/wMAAAAAAAAAAAAAAAAA"},"packages/@n8n/api-types/src/dto/workflows/base-workflow.dto.ts":{"26":"b:R5/900e13ZsXAAAAAAAAAAAAQHD8AAAA","31":"b:R5/900e13ZsXAAAAAAAAAAAAQHD8AAAA","42":"b:R5/900e13ZsXAAAAAAAAAAAAQHD8AAAA","54":"b:R5/900e13ZsXAAAAAAAAAAAAQHD8AAAA","55":"b:R5/900e13ZsXAAAAAAAAAAAAQHD8AAAA","69":"b:R5/900e13ZsXAAAAAAAAAAAAQHD8AAAA","81":"b:R5/900e13ZsXAAAAAAAAAAAAQHD8AAAA","90":"b:R5/900e13ZsXAAAAAAAAAAAAQHD8AAAA","141":"b:R5/900e13ZsXAAAAAAAAAAAAQHD8AAAA","144":"b:R5/900e13ZsXAAAAAAAAAAAAQHD8AAAA"},"packages/@n8n/api-types/src/dto/workflows/manual-run.dto.ts":{"5":"b:R5DlwAX/sAkBAAAAAAAAAAAAAAAAAAAA","64":"b:R5DlwAX/sAkBAAAAAAAAAAAAAAAAAAAA","69":"b:R5DlwAX/sAkBAAAAAAAAAAAAAAAAAAAA","86":"b:R5DlwAX/sAkBAAAAAAAAAAAAAAAAAAAA"},"packages/@n8n/api-types/src/index.ts":{"94":"b:f5X93+f3/N///4OgAAKAAQAAAAD8HwAA","115":"b:f5X93+f3/N///4OgAAKAAQAAAAD8HwAA","123":"b:f5X93+f3/N///4OgAAKAAQAAAAD8HwAA","253":"b:f5X93+f3/N///4OgAAKAAQAAAAD8HwAA","626":"b:f5X93+f3/N///4OgAAKAAQAAAAD8HwAA","637":"b:f5X93+f3/N///4OgAAKAAQAAAAD8HwAA"},"packages/@n8n/api-types/src/schemas/boolean-from-string.ts":{"3":"b:f5Td3KWlnI79/wMAAAAIAAAAAAAAEAAA"},"packages/@n8n/api-types/src/zod-class.ts":{"32":"b://X////////////////////////v8A8A","37":"b://Xf/P///+39//////////////8DAAAA","39":"b:f4X/77+hHt6f/xMAAAAAAAAAAADs8A8A","44":"b:f4X/77+hHt6f/xMAAAAAAAAAAADs8A8A","48":"b:f4X/77+hHt6f/xMAAAAAAAAAAADs8A8A","52":"b:f4X/77+hHt6f/xMAAAAAAAAAAADs8A8A"},"packages/@n8n/backend-common/src/index.ts":{"5":"b:f5jv7yX3vo9fgAMAAAAAAyAAAAAIAQAA","6":"b:f5jv7yX3vo9fgAMAAAAAAyAAAAAIAQAA","7":"b:f5jv7yX3vo9fgAMAAAAAAyAAAAAIAQAA","9":"b:f5jv7yX3vo9fgAMAAAAAAyAAAAAIAQAA","20":"b:f5jv7yX3vo9fgAMAAAAAAyAAAAAIAQAA"},"packages/@n8n/backend-common/src/license-state.ts":{"9":"b:f539/6+33t7//4NJoAEsAyAACAHsFBEA","15":"b:f539/6+33t7//4NJoAEsAyAACAHsFBEA","18":"b:f539/6+33t7//4NJoAEsAyAACAHsFBEA","22":"b:f539/6+33t7//4NJoAEsAyAACAHsFBEA","33":"b:f539/6+33t7//4NJoAEsAyAACAHsFBEA","47":"b:f539/6+33t7//4NJoAEsAyAACAHsFBEA","57":"b:f539/6+33t7//4NJoAEsAyAACAHsFBEA","61":"b:f539/6+33t7//4NJoAEsAyAACAHsFBEA","65":"b:f539/6+33t7//4NJoAEsAyAACAHsFBEA","69":"b:f539/6+33t7//4NJoAEsAyAACAHsFBEA","73":"b:f539/6+33t7//4NJoAEsAyAACAHsFBEA","77":"b:f539/6+33t7//4NJoAEsAyAACAHsFBEA","81":"b:f539/6+33t7//4NJoAEsAyAACAHsFBEA","85":"b:f539/6+33t7//4NJoAEsAyAACAHsFBEA","89":"b:f539/6+33t7//4NJoAEsAyAACAHsFBEA","93":"b:f539/6+33t7//4NJoAEsAyAACAHsFBEA","97":"b:f539/6+33t7//4NJoAEsAyAACAHsFBEA","101":"b:f539/6+33t7//4NJoAEsAyAACAHsFBEA","105":"b:f539/6+33t7//4NJoAEsAyAACAHsFBEA","109":"b:f539/6+33t7//4NJoAEsAyAACAHsFBEA","113":"b:f539/6+33t7//4NJoAEsAyAACAHsFBEA","117":"b:f539/6+33t7//4NJoAEsAyAACAHsFBEA","121":"b:f539/6+33t7//4NJoAEsAyAACAHsFBEA","125":"b:f539/6+33t7//4NJoAEsAyAACAHsFBEA","129":"b:f539/6+33t7//4NJoAEsAyAACAHsFBEA","133":"b:f539/6+33t7//4NJoAEsAyAACAHsFBEA","137":"b:f539/6+33t7//4NJoAEsAyAACAHsFBEA","141":"b:f539/6+33t7//4NJoAEsAyAACAHsFBEA","145":"b:f539/6+33t7//4NJoAEsAyAACAHsFBEA","149":"b:f539/6+33t7//4NJoAEsAyAACAHsFBEA","153":"b:f539/6+33t7//4NJoAEsAyAACAHsFBEA","157":"b:f539/6+33t7//4NJoAEsAyAACAHsFBEA","161":"b:f539/6+33t7//4NJoAEsAyAACAHsFBEA","165":"b:f539/6+33t7//4NJoAEsAyAACAHsFBEA","169":"b:f539/6+33t7//4NJoAEsAyAACAHsFBEA","173":"b:f539/6+33t7//4NJoAEsAyAACAHsFBEA","177":"b:f539/6+33t7//4NJoAEsAyAACAHsFBEA","181":"b:f539/6+33t7//4NJoAEsAyAACAHsFBEA","185":"b:f539/6+33t7//4NJoAEsAyAACAHsFBEA","189":"b:f539/6+33t7//4NJoAEsAyAACAHsFBEA","193":"b:f539/6+33t7//4NJoAEsAyAACAHsFBEA","197":"b:f539/6+33t7//4NJoAEsAyAACAHsFBEA","201":"b:f539/6+33t7//4NJoAEsAyAACAHsFBEA","205":"b:f539/6+33t7//4NJoAEsAyAACAHsFBEA","209":"b:f539/6+33t7//4NJoAEsAyAACAHsFBEA","213":"b:f539/6+33t7//4NJoAEsAyAACAHsFBEA","221":"b:f539/6+33t7//4NJoAEsAyAACAHsFBEA","225":"b:f539/6+33t7//4NJoAEsAyAACAHsFBEA","229":"b:f539/6+33t7//4NJoAEsAyAACAHsFBEA","233":"b:f539/6+33t7//4NJoAEsAyAACAHsFBEA","237":"b:f539/6+33t7//4NJoAEsAyAACAHsFBEA","241":"b:f539/6+33t7//4NJoAEsAyAACAHsFBEA","245":"b:f539/6+33t7//4NJoAEsAyAACAHsFBEA","249":"b:f539/6+33t7//4NJoAEsAyAACAHsFBEA","253":"b:f539/6+33t7//4NJoAEsAyAACAHsFBEA","257":"b:f539/6+33t7//4NJoAEsAyAACAHsFBEA","262":"b:f539/6+33t7//4NJoAEsAyAACAHsFBEA","275":"b:f539/6+33t7//4NJoAEsAyAACAHsFBEA"},"packages/@n8n/backend-common/src/utils/parse-flatted.ts":{"15":"b:f5DlwyX3sAMDAAAAAAAAAAAAAAAAAAAA"},"packages/@n8n/backend-network/src/http/axios/config.ts":{"16":"b:B4Xd0KWl2IjdfRCAAAAAAAAAAABoAAAA","27":"b:B4Xd0KWl2IjdfRCAAAAAAAAAAABoAAAA","38":"b:B4Xd0KWl2IjdfRCAAAAAAAAAAABoAAAA","52":"b:B4Xd0KWl2IjdfRCAAAAAAAAAAABoAAAA","53":"b:B4Xd0KWl2IjdfRCAAAAAAAAAAABoAAAA"},"packages/@n8n/backend-network/src/http/axios/invoke.ts":{"6":"b:B4Xd0AWk2IjdYBAAAAAAAAAAAABoAAAA"},"packages/@n8n/backend-network/src/http/axios/redirect.ts":{"49":"b:B4Xd0AWk2IjdYBAAAAAAAAAAAABoAAAA","67":"b:B4Xd0AWk2IjdYBAAAAAAAAAAAABoAAAA","72":"b:B4Xd0AWk2IjdYBAAAAAAAAAAAABoAAAA","80":"b:B4Xd0AWk2IjdYBAAAAAAAAAAAABoAAAA","95":"b:B4Xd0AWk2IjdYBAAAAAAAAAAAABoAAAA","107":"b:B4Xd0AWk2IjdYBAAAAAAAAAAAABoAAAA","113":"b:B4Xd0AWk2IjdYBAAAAAAAAAAAABoAAAA","119":"b:B4Xd0AWk2IjdYBAAAAAAAAAAAABoAAAA","135":"b:B4Xd0AWk2IjdYBAAAAAAAAAAAABoAAAA","151":"b:B4Xd0AWk2IjdYBAAAAAAAAAAAABoAAAA","183":"b:B4Xd0AWk2IjdYBAAAAAAAAAAAABoAAAA","206":"b:B4Xd0AWk2IjdYBAAAAAAAAAAAABoAAAA","219":"b:B4Xd0AWk2IjdYBAAAAAAAAAAAABoAAAA","227":"b:B4Xd0AWk2IjdYBAAAAAAAAAAAABoAAAA","234":"b:B4Xd0AWk2IjdYBAAAAAAAAAAAABoAAAA","238":"b:B4Xd0AWk2IjdYBAAAAAAAAAAAABoAAAA"},"packages/@n8n/backend-network/src/http/axios/request.ts":{"27":"b:B4Xd0AWk2IjdYBAAAAAAAAAAAABoAAAA","77":"b:B4Xd0AWk2IjdYBAAAAAAAAAAAABoAAAA","128":"b:B4Xd0AWk2IjdYBAAAAAAAAAAAABoAAAA","132":"b:B4Xd0AWk2IjdYBAAAAAAAAAAAABoAAAA","144":"b:B4Xd0AWk2IjdYBAAAAAAAAAAAABoAAAA","158":"b:B4Xd0AWk2IjdYBAAAAAAAAAAAABoAAAA","182":"b:B4Xd0AWk2IjdYBAAAAAAAAAAAABoAAAA"},"packages/@n8n/backend-network/src/http/axios/user-agent.ts":{"11":"b:B4Xd0AWk2IjdYBAAAAAAAAAAAABoAAAA","28":"b:B4Xd0AWk2IjdYBAAAAAAAAAAAABoAAAA","40":"b:B4Xd0AWk2IjdYBAAAAAAAAAAAABoAAAA","54":"b:B4Xd0AWk2IjdYBAAAAAAAAAAAABoAAAA"},"packages/@n8n/backend-network/src/http/axios/utils.ts":{"20":"b:B4Xd0AWs2IjdYBAAAAAAAEACAABoAgAA","29":"b:B4Xd0AWs2IjdYBAAAAAAAEACAABoAgAA","38":"b:B4Xd0AWs2IjdYBAAAAAAAEACAABoAgAA","50":"b:B4Xd0AWs2IjdYBAAAAAAAEACAABoAgAA","57":"b:B4Xd0AWs2IjdYBAAAAAAAEACAABoAgAA","61":"b:B4Xd0AWs2IjdYBAAAAAAAEACAABoAgAA","81":"b:B4Xd0AWs2IjdYBAAAAAAAEACAABoAgAA","100":"b:B4Xd0AWs2IjdYBAAAAAAAEACAABoAgAA","108":"b:B4Xd0AWs2IjdYBAAAAAAAEACAABoAgAA","150":"b:B4Xd0AWs2IjdYBAAAAAAAEACAABoAgAA","157":"b:B4Xd0AWs2IjdYBAAAAAAAEACAABoAgAA","162":"b:B4Xd0AWs2IjdYBAAAAAAAEACAABoAgAA","165":"b:B4Xd0AWs2IjdYBAAAAAAAEACAABoAgAA","168":"b:B4Xd0AWs2IjdYBAAAAAAAEACAABoAgAA","201":"b:B4Xd0AWs2IjdYBAAAAAAAEACAABoAgAA","211":"b:B4Xd0AWs2IjdYBAAAAAAAEACAABoAgAA","214":"b:B4Xd0AWs2IjdYBAAAAAAAEACAABoAgAA","218":"b:B4Xd0AWs2IjdYBAAAAAAAEACAABoAgAA","234":"b:B4Xd0AWs2IjdYBAAAAAAAEACAABoAgAA","250":"b:B4Xd0AWs2IjdYBAAAAAAAEACAABoAgAA","270":"b:B4Xd0AWs2IjdYBAAAAAAAEACAABoAgAA","277":"b:B4Xd0AWs2IjdYBAAAAAAAEACAABoAgAA","287":"b:B4Xd0AWs2IjdYBAAAAAAAEACAABoAgAA","295":"b:B4Xd0AWs2IjdYBAAAAAAAEACAABoAgAA","302":"b:B4Xd0AWs2IjdYBAAAAAAAEACAABoAgAA","303":"b:B4Xd0AWs2IjdYBAAAAAAAEACAABoAgAA","318":"b:B4Xd0AWs2IjdYBAAAAAAAEACAABoAgAA","349":"b:B4Xd0AWs2IjdYBAAAAAAAEACAABoAgAA","360":"b:B4Xd0AWs2IjdYBAAAAAAAEACAABoAgAA","383":"b:B4Xd0AWs2IjdYBAAAAAAAEACAABoAgAA","398":"b:B4Xd0AWs2IjdYBAAAAAAAEACAABoAgAA"},"packages/@n8n/backend-network/src/http/client-default-headers.ts":{"18":"b:B4Xd0AWk2IjdYBAAAAAAAAAAAAAIAAAA"},"packages/@n8n/backend-network/src/http/env-proxy-https-agent.ts":{"17":"b:B4Xd0AWk2IDdYBAAAAAAAAAAAAAIAAgA","22":"b:B4Xd0AWk2IDdYBAAAAAAAAAAAAAIAAgA","26":"b:B4Xd0AWk2IDdYBAAAAAAAAAAAAAIAAgA"},"packages/@n8n/backend-network/src/http/env-proxy-router.ts":{"22":"b:B4Xd0KWl2IjdfRCAAAAAAAAAAABoAAgA","31":"b:B4Xd0KWl2IjdfRCAAAAAAAAAAABoAAgA","51":"b:B4Xd0KWl2IjdfRCAAAAAAAAAAABoAAgA","64":"b:B4Xd0KWl2IjdfRCAAAAAAAAAAABoAAgA"},"packages/@n8n/backend-network/src/http/index.ts":{"9":"b:f5Dl0yX3sBsDAAAAAAAAAACACADkAyAA","10":"b:f5Dl0yX3sBsDAAAAAAAAAACACADkAyAA","11":"b:f5Dl0yX3sBsDAAAAAAAAAACACADkAyAA","12":"b:f5Dl0yX3sBsDAAAAAAAAAACACADkAyAA"},"packages/@n8n/backend-network/src/http/node-agents.ts":{"23":"b:B4Xd0AWk2IjdYBAAAAAAAAAAAABoAAgA","67":"b:B4Xd0AWk2IjdYBAAAAAAAAAAAABoAAgA","122":"b:B4Xd0AWk2IjdYBAAAAAAAAAAAABoAAgA","137":"b:B4Xd0AWk2IjdYBAAAAAAAAAAAABoAAgA","142":"b:B4Xd0AWk2IjdYBAAAAAAAAAAAABoAAgA"},"packages/@n8n/backend-network/src/http/outbound-http.ts":{"161":"b:B4Xd2KWt2IjdfRCAAAAAAEACAAAIAggA","170":"b:B4Xd2KWt2IjdfRCAAAAAAEACAAAIAggA","174":"b:B4Xd2KWt2IjdfRCAAAAAAEACAAAIAggA","186":"b:B4Xd2KWt2IjdfRCAAAAAAEACAAAIAggA","197":"b:B4Xd2KWt2IjdfRCAAAAAAEACAAAIAggA","210":"b:B4Xd2KWt2IjdfRCAAAAAAEACAAAIAggA","220":"b:B4Xd2KWt2IjdfRCAAAAAAEACAAAIAggA","223":"b:B4Xd2KWt2IjdfRCAAAAAAEACAAAIAggA","224":"b:B4Xd2KWt2IjdfRCAAAAAAEACAAAIAggA","225":"b:B4Xd2KWt2IjdfRCAAAAAAEACAAAIAggA","231":"b:B4Xd2KWt2IjdfRCAAAAAAEACAAAIAggA","233":"b:B4Xd2KWt2IjdfRCAAAAAAEACAAAIAggA"},"packages/@n8n/config/src/index.ts":{"58":"b:f4/d0AWk2IjdYBQAACAACAAAAABoAAAA","60":"b:f4/d0AWk2IjdYBQAACAACAAAAABoAAAA","61":"b:f4/d0AWk2IjdYBQAACAACAAAAABoAAAA","63":"b:f4/d0AWk2IjdYBQAACAACAAAAABoAAAA","64":"b:f4/d0AWk2IjdYBQAACAACAAAAABoAAAA","69":"b:f4/d0AWk2IjdYBQAACAACAAAAABoAAAA","71":"b:f4/d0AWk2IjdYBQAACAACAAAAABoAAAA","81":"b:f4/d0AWk2IjdYBQAACAACAAAAABoAAAA","83":"b:f4/d0AWk2IjdYBQAACAACAAAAABoAAAA","84":"b:f4/d0AWk2IjdYBQAACAACAAAAABoAAAA","85":"b:f4/d0AWk2IjdYBQAACAACAAAAABoAAAA","89":"b:f4/d0AWk2IjdYBQAACAACAAAAABoAAAA","90":"b:f4/d0AWk2IjdYBQAACAACAAAAABoAAAA","101":"b:f4/d0AWk2IjdYBQAACAACAAAAABoAAAA"},"packages/@n8n/db/src/entities/index.ts":{"5":[82,1,2,3,4,5,109,123,6],"9":[82,1,2,3,4,5,109,123,6],"17":[82,1,2,3,4,5,109,123,6],"20":[82,1,2,3,4,5,109,123,6],"21":[82,1,2,3,4,5,109,123,6],"27":[82,1,2,3,4,5,109,123,6],"29":[82,1,2,3,4,5,109,123,6],"32":[82,1,2,3,4,5,109,123,6],"36":[82,1,2,3,4,5,109,123,6],"41":[82,1,2,3,4,5,109,123,6],"44":[82,1,2,3,4,5,109,123,6],"48":[82,1,2,3,4,5,109,123,6],"50":[82,1,2,3,4,5,109,123,6],"53":[82,1,2,3,4,5,109,123,6],"54":[82,1,2,3,4,5,109,123,6],"56":[82,1,2,3,4,5,109,123,6]},"packages/@n8n/db/src/entities/types-db.ts":{"327":"b:f5T936WlHJqX/wMAAAAAAAAAAADsMAgA","339":"b:f5T936WlHJqX/wMAAAAAAAAAAADsMAgA","477":"b:f5T936WlHJqX/wMAAAAAAAAAAADsMAgA"},"packages/@n8n/db/src/entities/user.ts":{"60":"b:R5Xd2KW32IBdAAAAAAAAAAAAAAAAAAAA","84":"b:R5Xd2KW32IBdAAAAAAAAAAAAAAAAAAAA","115":"b:R5Xd2KW32IBdAAAAAAAAAAAAAAAAAAAA","117":"b:R5Xd2KW32IBdAAAAAAAAAAAAAAAAAAAA","124":"b:R5Xd2KW32IBdAAAAAAAAAAAAAAAAAAAA","129":"b:R5Xd2KW32IBdAAAAAAAAAAAAAAAAAAAA","139":"b:R5Xd2KW32IBdAAAAAAAAAAAAAAAAAAAA"},"packages/@n8n/db/src/index.ts":{"14":"b:f5Hn/zf3vN93jLNRAABkACCCCADvAxEA","16":"b:f5Hn/zf3vN93jLNRAABkACCCCADvAxEA","19":"b:f5Hn/zf3vN93jLNRAABkACCCCADvAxEA","20":"b:f5Hn/zf3vN93jLNRAABkACCCCADvAxEA","21":"b:f5Hn/zf3vN93jLNRAABkACCCCADvAxEA","36":"b:f5Hn/zf3vN93jLNRAABkACCCCADvAxEA","46":"b:f5Hn/zf3vN93jLNRAABkACCCCADvAxEA","56":"b:f5Hn/zf3vN93jLNRAABkACCCCADvAxEA"},"packages/@n8n/db/src/repositories/credentials.repository.ts":{"19":"b:f9X//O/3/9zd4IMYsBGgAQAACAIAAhAA","26":"b:f9X//O/3/9zd4IMYsBGgAQAACAIAAhAA","33":"b:f9X//O/3/9zd4IMYsBGgAQAACAIAAhAA","40":"b:f9X//O/3/9zd4IMYsBGgAQAACAIAAhAA","48":"b:f9X//O/3/9zd4IMYsBGgAQAACAIAAhAA","58":"b:f9X//O/3/9zd4IMYsBGgAQAACAIAAhAA","65":"b:f9X//O/3/9zd4IMYsBGgAQAACAIAAhAA","78":"b:f9X//O/3/9zd4IMYsBGgAQAACAIAAhAA","117":"b:f9X//O/3/9zd4IMYsBGgAQAACAIAAhAA","135":"b:f9X//O/3/9zd4IMYsBGgAQAACAIAAhAA","153":"b:f9X//O/3/9zd4IMYsBGgAQAACAIAAhAA","160":"b:f9X//O/3/9zd4IMYsBGgAQAACAIAAhAA","231":"b:f9X//O/3/9zd4IMYsBGgAQAACAIAAhAA","271":"b:f9X//O/3/9zd4IMYsBGgAQAACAIAAhAA","290":"b:f9X//O/3/9zd4IMYsBGgAQAACAIAAhAA","320":"b:f9X//O/3/9zd4IMYsBGgAQAACAIAAhAA","346":"b:f9X//O/3/9zd4IMYsBGgAQAACAIAAhAA","362":"b:f9X//O/3/9zd4IMYsBGgAQAACAIAAhAA","376":"b:f9X//O/3/9zd4IMYsBGgAQAACAIAAhAA","389":"b:f9X//O/3/9zd4IMYsBGgAQAACAIAAhAA","397":"b:f9X//O/3/9zd4IMYsBGgAQAACAIAAhAA","409":"b:f9X//O/3/9zd4IMYsBGgAQAACAIAAhAA","449":"b:f9X//O/3/9zd4IMYsBGgAQAACAIAAhAA","528":"b:f9X//O/3/9zd4IMYsBGgAQAACAIAAhAA","530":"b:f9X//O/3/9zd4IMYsBGgAQAACAIAAhAA","534":"b:f9X//O/3/9zd4IMYsBGgAQAACAIAAhAA","552":"b:f9X//O/3/9zd4IMYsBGgAQAACAIAAhAA"},"packages/@n8n/db/src/repositories/execution-metadata.repository.ts":{"8":[82,1,2,3,4,5,109,123,6]},"packages/@n8n/db/src/repositories/execution.repository.ts":{"63":"b:f5Dlwyf3sBsDAAAAAAAAANAAADD0C+AA","98":"b:f5Dlwyf3sBsDAAAAAAAAANAAADD0C+AA","141":"b:f5Dlwyf3sBsDAAAAAAAAANAAADD0C+AA","145":"b:f5Dlwyf3sBsDAAAAAAAAANAAADD0C+AA","157":"b:f5Dlwyf3sBsDAAAAAAAAANAAADD0C+AA","189":"b:f5Dlwyf3sBsDAAAAAAAAANAAADD0C+AA","209":"b:f5Dlwyf3sBsDAAAAAAAAANAAADD0C+AA","214":"b:f5Dlwyf3sBsDAAAAAAAAANAAADD0C+AA","221":"b:f5Dlwyf3sBsDAAAAAAAAANAAADD0C+AA","228":"b:f5Dlwyf3sBsDAAAAAAAAANAAADD0C+AA","234":"b:f5Dlwyf3sBsDAAAAAAAAANAAADD0C+AA","240":"b:f5Dlwyf3sBsDAAAAAAAAANAAADD0C+AA","246":"b:f5Dlwyf3sBsDAAAAAAAAANAAADD0C+AA","253":"b:f5Dlwyf3sBsDAAAAAAAAANAAADD0C+AA","284":"b:f5Dlwyf3sBsDAAAAAAAAANAAADD0C+AA","347":"b:f5Dlwyf3sBsDAAAAAAAAANAAADD0C+AA","353":"b:f5Dlwyf3sBsDAAAAAAAAANAAADD0C+AA","376":"b:f5Dlwyf3sBsDAAAAAAAAANAAADD0C+AA","379":"b:f5Dlwyf3sBsDAAAAAAAAANAAADD0C+AA","408":"b:f5Dlwyf3sBsDAAAAAAAAANAAADD0C+AA","432":"b:f5Dlwyf3sBsDAAAAAAAAANAAADD0C+AA","459":"b:f5Dlwyf3sBsDAAAAAAAAANAAADD0C+AA","499":"b:f5Dlwyf3sBsDAAAAAAAAANAAADD0C+AA","510":"b:f5Dlwyf3sBsDAAAAAAAAANAAADD0C+AA","511":"b:f5Dlwyf3sBsDAAAAAAAAANAAADD0C+AA","518":"b:f5Dlwyf3sBsDAAAAAAAAANAAADD0C+AA","524":"b:f5Dlwyf3sBsDAAAAAAAAANAAADD0C+AA","527":"b:f5Dlwyf3sBsDAAAAAAAAANAAADD0C+AA","573":"b:f5Dlwyf3sBsDAAAAAAAAANAAADD0C+AA","582":"b:f5Dlwyf3sBsDAAAAAAAAANAAADD0C+AA","600":"b:f5Dlwyf3sBsDAAAAAAAAANAAADD0C+AA","607":"b:f5Dlwyf3sBsDAAAAAAAAANAAADD0C+AA","611":"b:f5Dlwyf3sBsDAAAAAAAAANAAADD0C+AA","635":"b:f5Dlwyf3sBsDAAAAAAAAANAAADD0C+AA","650":"b:f5Dlwyf3sBsDAAAAAAAAANAAADD0C+AA","660":"b:f5Dlwyf3sBsDAAAAAAAAANAAADD0C+AA","674":"b:f5Dlwyf3sBsDAAAAAAAAANAAADD0C+AA","692":"b:f5Dlwyf3sBsDAAAAAAAAANAAADD0C+AA","703":"b:f5Dlwyf3sBsDAAAAAAAAANAAADD0C+AA","709":"b:f5Dlwyf3sBsDAAAAAAAAANAAADD0C+AA","722":"b:f5Dlwyf3sBsDAAAAAAAAANAAADD0C+AA","760":"b:f5Dlwyf3sBsDAAAAAAAAANAAADD0C+AA","801":"b:f5Dlwyf3sBsDAAAAAAAAANAAADD0C+AA","817":"b:f5Dlwyf3sBsDAAAAAAAAANAAADD0C+AA","821":"b:f5Dlwyf3sBsDAAAAAAAAANAAADD0C+AA","848":"b:f5Dlwyf3sBsDAAAAAAAAANAAADD0C+AA","884":"b:f5Dlwyf3sBsDAAAAAAAAANAAADD0C+AA","888":"b:f5Dlwyf3sBsDAAAAAAAAANAAADD0C+AA","908":"b:f5Dlwyf3sBsDAAAAAAAAANAAADD0C+AA","928":"b:f5Dlwyf3sBsDAAAAAAAAANAAADD0C+AA","1047":"b:f5Dlwyf3sBsDAAAAAAAAANAAADD0C+AA","1049":"b:f5Dlwyf3sBsDAAAAAAAAANAAADD0C+AA","1057":"b:f5Dlwyf3sBsDAAAAAAAAANAAADD0C+AA","1095":"b:f5Dlwyf3sBsDAAAAAAAAANAAADD0C+AA","1103":"b:f5Dlwyf3sBsDAAAAAAAAANAAADD0C+AA","1106":"b:f5Dlwyf3sBsDAAAAAAAAANAAADD0C+AA","1114":"b:f5Dlwyf3sBsDAAAAAAAAANAAADD0C+AA","1117":"b:f5Dlwyf3sBsDAAAAAAAAANAAADD0C+AA","1120":"b:f5Dlwyf3sBsDAAAAAAAAANAAADD0C+AA","1126":"b:f5Dlwyf3sBsDAAAAAAAAANAAADD0C+AA","1134":"b:f5Dlwyf3sBsDAAAAAAAAANAAADD0C+AA","1141":"b:f5Dlwyf3sBsDAAAAAAAAANAAADD0C+AA","1149":"b:f5Dlwyf3sBsDAAAAAAAAANAAADD0C+AA","1166":"b:f5Dlwyf3sBsDAAAAAAAAANAAADD0C+AA"},"packages/@n8n/db/src/repositories/index.ts":{"3":"b:R5T906e33JoXAAAAAAAAAAAAAADsAAEA","4":"b:R5T906e33JoXAAAAAAAAAAAAAADsAAEA","7":"b:R5T906e33JoXAAAAAAAAAAAAAADsAAEA","10":"b:R5T906e33JoXAAAAAAAAAAAAAADsAAEA","11":"b:R5T906e33JoXAAAAAAAAAAAAAADsAAEA","12":"b:R5T906e33JoXAAAAAAAAAAAAAADsAAEA","13":"b:R5T906e33JoXAAAAAAAAAAAAAADsAAEA","26":"b:R5T906e33JoXAAAAAAAAAAAAAADsAAEA","28":"b:R5T906e33JoXAAAAAAAAAAAAAADsAAEA","35":"b:R5T906e33JoXAAAAAAAAAAAAAADsAAEA","37":"b:R5T906e33JoXAAAAAAAAAAAAAADsAAEA","38":"b:R5T906e33JoXAAAAAAAAAAAAAADsAAEA","42":"b:R5T906e33JoXAAAAAAAAAAAAAADsAAEA","59":"b:R5T906e33JoXAAAAAAAAAAAAAADsAAEA","61":"b:R5T906e33JoXAAAAAAAAAAAAAADsAAEA","64":"b:R5T906e33JoXAAAAAAAAAAAAAADsAAEA","65":"b:R5T906e33JoXAAAAAAAAAAAAAADsAAEA","67":"b:R5T906e33JoXAAAAAAAAAAAAAADsAAEA","69":"b:R5T906e33JoXAAAAAAAAAAAAAADsAAEA","88":"b:R5T906e33JoXAAAAAAAAAAAAAADsAAEA","89":"b:R5T906e33JoXAAAAAAAAAAAAAADsAAEA","95":"b:R5T906e33JoXAAAAAAAAAAAAAADsAAEA","96":"b:R5T906e33JoXAAAAAAAAAAAAAADsAAEA"},"packages/@n8n/db/src/repositories/project-relation.repository.ts":{"9":"b:fgjkywalGNIHAIQUAAKACUAABDDkC+AB","13":"b:fgjkywalGNIHAIQUAAKACUAABDDkC+AB","27":"b:fgjkywalGNIHAIQUAAKACUAABDDkC+AB","35":"b:fgjkywalGNIHAIQUAAKACUAABDDkC+AB","38":"b:fgjkywalGNIHAIQUAAKACUAABDDkC+AB","43":"b:fgjkywalGNIHAIQUAAKACUAABDDkC+AB","49":"b:fgjkywalGNIHAIQUAAKACUAABDDkC+AB","56":"b:fgjkywalGNIHAIQUAAKACUAABDDkC+AB","62":"b:fgjkywalGNIHAIQUAAKACUAABDDkC+AB","70":"b:fgjkywalGNIHAIQUAAKACUAABDDkC+AB","76":"b:fgjkywalGNIHAIQUAAKACUAABDDkC+AB","79":"b:fgjkywalGNIHAIQUAAKACUAABDDkC+AB"},"packages/@n8n/db/src/repositories/project.repository.ts":{"9":"b:f5T93+e1/t7//wNQAAKIAQAAAADtEAEA","13":"b:f5T93+e1/t7//wNQAAKIAQAAAADtEAEA","25":"b:f5T93+e1/t7//wNQAAKIAQAAAADtEAEA","36":"b:f5T93+e1/t7//wNQAAKIAQAAAADtEAEA","46":"b:f5T93+e1/t7//wNQAAKIAQAAAADtEAEA","70":"b:f5T93+e1/t7//wNQAAKIAQAAAADtEAEA","85":"b:f5T93+e1/t7//wNQAAKIAQAAAADtEAEA","102":"b:f5T93+e1/t7//wNQAAKIAQAAAADtEAEA","112":"b:f5T93+e1/t7//wNQAAKIAQAAAADtEAEA","124":"b:f5T93+e1/t7//wNQAAKIAQAAAADtEAEA","140":"b:f5T93+e1/t7//wNQAAKIAQAAAADtEAEA","149":"b:f5T93+e1/t7//wNQAAKIAQAAAADtEAEA","165":"b:f5T93+e1/t7//wNQAAKIAQAAAADtEAEA","178":"b:f5T93+e1/t7//wNQAAKIAQAAAADtEAEA","192":"b:f5T93+e1/t7//wNQAAKIAQAAAADtEAEA","202":"b:f5T93+e1/t7//wNQAAKIAQAAAADtEAEA","209":"b:f5T93+e1/t7//wNQAAKIAQAAAADtEAEA"},"packages/@n8n/db/src/repositories/role.repository.ts":{"9":"b:f5T936elnN79/wMEAAKIAQAAAADgOgEA","13":"b:f5T936elnN79/wMEAAKIAQAAAADgOgEA","18":"b:f5T936elnN79/wMEAAKIAQAAAADgOgEA","36":"b:f5T936elnN79/wMEAAKIAQAAAADgOgEA","52":"b:f5T936elnN79/wMEAAKIAQAAAADgOgEA","63":"b:f5T936elnN79/wMEAAKIAQAAAADgOgEA","72":"b:f5T936elnN79/wMEAAKIAQAAAADgOgEA","80":"b:f5T936elnN79/wMEAAKIAQAAAADgOgEA","106":"b:f5T936elnN79/wMEAAKIAQAAAADgOgEA","109":"b:f5T936elnN79/wMEAAKIAQAAAADgOgEA","112":"b:f5T936elnN79/wMEAAKIAQAAAADgOgEA","126":"b:f5T936elnN79/wMEAAKIAQAAAADgOgEA","129":"b:f5T936elnN79/wMEAAKIAQAAAADgOgEA","149":"b:f5T936elnN79/wMEAAKIAQAAAADgOgEA","178":"b:f5T936elnN79/wMEAAKIAQAAAADgOgEA","185":"b:f5T936elnN79/wMEAAKIAQAAAADgOgEA","192":"b:f5T936elnN79/wMEAAKIAQAAAADgOgEA","203":"b:f5T936elnN79/wMEAAKIAQAAAADgOgEA","204":"b:f5T936elnN79/wMEAAKIAQAAAADgOgEA","228":"b:f5T936elnN79/wMEAAKIAQAAAADgOgEA","261":"b:f5T936elnN79/wMEAAKIAQAAAADgOgEA","265":"b:f5T936elnN79/wMEAAKIAQAAAADgOgEA"},"packages/@n8n/db/src/repositories/shared-workflow.repository.ts":{"21":"b:d4T93/e339ofgJNBAAGgA2CAEDDsCuEA","25":"b:d4T93/e339ofgJNBAAGgA2CAEDDsCuEA","32":"b:d4T93/e339ofgJNBAAGgA2CAEDDsCuEA","35":"b:d4T93/e339ofgJNBAAGgA2CAEDDsCuEA","46":"b:d4T93/e339ofgJNBAAGgA2CAEDDsCuEA","52":"b:d4T93/e339ofgJNBAAGgA2CAEDDsCuEA","55":"b:d4T93/e339ofgJNBAAGgA2CAEDDsCuEA","76":"b:d4T93/e339ofgJNBAAGgA2CAEDDsCuEA","86":"b:d4T93/e339ofgJNBAAGgA2CAEDDsCuEA","92":"b:d4T93/e339ofgJNBAAGgA2CAEDDsCuEA","104":"b:d4T93/e339ofgJNBAAGgA2CAEDDsCuEA","116":"b:d4T93/e339ofgJNBAAGgA2CAEDDsCuEA","128":"b:d4T93/e339ofgJNBAAGgA2CAEDDsCuEA","131":"b:d4T93/e339ofgJNBAAGgA2CAEDDsCuEA","139":"b:d4T93/e339ofgJNBAAGgA2CAEDDsCuEA","148":"b:d4T93/e339ofgJNBAAGgA2CAEDDsCuEA","157":"b:d4T93/e339ofgJNBAAGgA2CAEDDsCuEA","166":"b:d4T93/e339ofgJNBAAGgA2CAEDDsCuEA","171":"b:d4T93/e339ofgJNBAAGgA2CAEDDsCuEA","184":"b:d4T93/e339ofgJNBAAGgA2CAEDDsCuEA","222":"b:d4T93/e339ofgJNBAAGgA2CAEDDsCuEA"},"packages/@n8n/db/src/repositories/user.repository.ts":{"16":"b:f5X93Ke33MX9/wMEAAKgAQAAAAAAqBAC","20":"b:f5X93Ke33MX9/wMEAAKgAQAAAAAAqBAC","32":"b:f5X93Ke33MX9/wMEAAKgAQAAAAAAqBAC","56":"b:f5X93Ke33MX9/wMEAAKgAQAAAAAAqBAC","60":"b:f5X93Ke33MX9/wMEAAKgAQAAAAAAqBAC","64":"b:f5X93Ke33MX9/wMEAAKgAQAAAAAAqBAC","68":"b:f5X93Ke33MX9/wMEAAKgAQAAAAAAqBAC","75":"b:f5X93Ke33MX9/wMEAAKgAQAAAAAAqBAC","79":"b:f5X93Ke33MX9/wMEAAKgAQAAAAAAqBAC","90":"b:f5X93Ke33MX9/wMEAAKgAQAAAAAAqBAC","97":"b:f5X93Ke33MX9/wMEAAKgAQAAAAAAqBAC","108":"b:f5X93Ke33MX9/wMEAAKgAQAAAAAAqBAC","115":"b:f5X93Ke33MX9/wMEAAKgAQAAAAAAqBAC","119":"b:f5X93Ke33MX9/wMEAAKgAQAAAAAAqBAC","166":"b:f5X93Ke33MX9/wMEAAKgAQAAAAAAqBAC","200":"b:f5X93Ke33MX9/wMEAAKgAQAAAAAAqBAC","221":"b:f5X93Ke33MX9/wMEAAKgAQAAAAAAqBAC","234":"b:f5X93Ke33MX9/wMEAAKgAQAAAAAAqBAC","242":"b:f5X93Ke33MX9/wMEAAKgAQAAAAAAqBAC","247":"b:f5X93Ke33MX9/wMEAAKgAQAAAAAAqBAC","306":"b:f5X93Ke33MX9/wMEAAKgAQAAAAAAqBAC","332":"b:f5X93Ke33MX9/wMEAAKgAQAAAAAAqBAC","351":"b:f5X93Ke33MX9/wMEAAKgAQAAAAAAqBAC","373":"b:f5X93Ke33MX9/wMEAAKgAQAAAAAAqBAC","384":"b:f5X93Ke33MX9/wMEAAKgAQAAAAAAqBAC"},"packages/@n8n/db/src/repositories/workflow-dependency.repository.ts":{"15":"b:R5T906e33JofAAAAAAAAAAAAAADtAAEA","21":"b:R5T906e33JofAAAAAAAAAAAAAADtAAEA","40":"b:R5T906e33JofAAAAAAAAAAAAAADtAAEA","53":"b:R5T906e33JofAAAAAAAAAAAAAADtAAEA","57":"b:R5T906e33JofAAAAAAAAAAAAAADtAAEA","62":"b:R5T906e33JofAAAAAAAAAAAAAADtAAEA","95":"b:R5T906e33JofAAAAAAAAAAAAAADtAAEA","120":"b:R5T906e33JofAAAAAAAAAAAAAADtAAEA","121":"b:R5T906e33JofAAAAAAAAAAAAAADtAAEA","132":"b:R5T906e33JofAAAAAAAAAAAAAADtAAEA","158":"b:R5T906e33JofAAAAAAAAAAAAAADtAAEA"},"packages/@n8n/db/src/repositories/workflow-statistics.repository.ts":{"43":"b:R5jlwyf3sBsDAAQAgAAACAAABADkAwAA","50":"b:R5jlwyf3sBsDAAQAgAAACAAABADkAwAA","83":"b:R5jlwyf3sBsDAAQAgAAACAAABADkAwAA","126":"b:R5jlwyf3sBsDAAQAgAAACAAABADkAwAA","131":"b:R5jlwyf3sBsDAAQAgAAACAAABADkAwAA","144":"b:R5jlwyf3sBsDAAQAgAAACAAABADkAwAA","197":"b:R5jlwyf3sBsDAAQAgAAACAAABADkAwAA","202":"b:R5jlwyf3sBsDAAQAgAAACAAABADkAwAA","212":"b:R5jlwyf3sBsDAAQAgAAACAAABADkAwAA"},"packages/@n8n/db/src/repositories/workflow.repository.ts":{"58":"b:f5/93+f/v9v/4JNBhAUCAyIAQAP3BuAD","68":"b:f5/93+f/v9v/4JNBhAUCAyIAQAP3BuAD","78":"b:f5/93+f/v9v/4JNBhAUCAyIAQAP3BuAD","84":"b:f5/93+f/v9v/4JNBhAUCAyIAQAP3BuAD","87":"b:f5/93+f/v9v/4JNBhAUCAyIAQAP3BuAD","94":"b:f5/93+f/v9v/4JNBhAUCAyIAQAP3BuAD","97":"b:f5/93+f/v9v/4JNBhAUCAyIAQAP3BuAD","103":"b:f5/93+f/v9v/4JNBhAUCAyIAQAP3BuAD","112":"b:f5/93+f/v9v/4JNBhAUCAyIAQAP3BuAD","118":"b:f5/93+f/v9v/4JNBhAUCAyIAQAP3BuAD","128":"b:f5/93+f/v9v/4JNBhAUCAyIAQAP3BuAD","143":"b:f5/93+f/v9v/4JNBhAUCAyIAQAP3BuAD","154":"b:f5/93+f/v9v/4JNBhAUCAyIAQAP3BuAD","160":"b:f5/93+f/v9v/4JNBhAUCAyIAQAP3BuAD","163":"b:f5/93+f/v9v/4JNBhAUCAyIAQAP3BuAD","179":"b:f5/93+f/v9v/4JNBhAUCAyIAQAP3BuAD","188":"b:f5/93+f/v9v/4JNBhAUCAyIAQAP3BuAD","195":"b:f5/93+f/v9v/4JNBhAUCAyIAQAP3BuAD","204":"b:f5/93+f/v9v/4JNBhAUCAyIAQAP3BuAD","211":"b:f5/93+f/v9v/4JNBhAUCAyIAQAP3BuAD","225":"b:f5/93+f/v9v/4JNBhAUCAyIAQAP3BuAD","238":"b:f5/93+f/v9v/4JNBhAUCAyIAQAP3BuAD","245":"b:f5/93+f/v9v/4JNBhAUCAyIAQAP3BuAD","251":"b:f5/93+f/v9v/4JNBhAUCAyIAQAP3BuAD","257":"b:f5/93+f/v9v/4JNBhAUCAyIAQAP3BuAD","267":"b:f5/93+f/v9v/4JNBhAUCAyIAQAP3BuAD","356":"b:f5/93+f/v9v/4JNBhAUCAyIAQAP3BuAD","380":"b:f5/93+f/v9v/4JNBhAUCAyIAQAP3BuAD","402":"b:f5/93+f/v9v/4JNBhAUCAyIAQAP3BuAD","428":"b:f5/93+f/v9v/4JNBhAUCAyIAQAP3BuAD","439":"b:f5/93+f/v9v/4JNBhAUCAyIAQAP3BuAD","440":"b:f5/93+f/v9v/4JNBhAUCAyIAQAP3BuAD","443":"b:f5/93+f/v9v/4JNBhAUCAyIAQAP3BuAD","465":"b:f5/93+f/v9v/4JNBhAUCAyIAQAP3BuAD","509":"b:f5/93+f/v9v/4JNBhAUCAyIAQAP3BuAD","567":"b:f5/93+f/v9v/4JNBhAUCAyIAQAP3BuAD","601":"b:f5/93+f/v9v/4JNBhAUCAyIAQAP3BuAD","631":"b:f5/93+f/v9v/4JNBhAUCAyIAQAP3BuAD","710":"b:f5/93+f/v9v/4JNBhAUCAyIAQAP3BuAD","721":"b:f5/93+f/v9v/4JNBhAUCAyIAQAP3BuAD","726":"b:f5/93+f/v9v/4JNBhAUCAyIAQAP3BuAD","727":"b:f5/93+f/v9v/4JNBhAUCAyIAQAP3BuAD","730":"b:f5/93+f/v9v/4JNBhAUCAyIAQAP3BuAD","732":"b:f5/93+f/v9v/4JNBhAUCAyIAQAP3BuAD","733":"b:f5/93+f/v9v/4JNBhAUCAyIAQAP3BuAD","736":"b:f5/93+f/v9v/4JNBhAUCAyIAQAP3BuAD","751":"b:f5/93+f/v9v/4JNBhAUCAyIAQAP3BuAD","758":"b:f5/93+f/v9v/4JNBhAUCAyIAQAP3BuAD","759":"b:f5/93+f/v9v/4JNBhAUCAyIAQAP3BuAD","761":"b:f5/93+f/v9v/4JNBhAUCAyIAQAP3BuAD","770":"b:f5/93+f/v9v/4JNBhAUCAyIAQAP3BuAD","784":"b:f5/93+f/v9v/4JNBhAUCAyIAQAP3BuAD","803":"b:f5/93+f/v9v/4JNBhAUCAyIAQAP3BuAD","834":"b:f5/93+f/v9v/4JNBhAUCAyIAQAP3BuAD","904":"b:f5/93+f/v9v/4JNBhAUCAyIAQAP3BuAD","927":"b:f5/93+f/v9v/4JNBhAUCAyIAQAP3BuAD","966":"b:f5/93+f/v9v/4JNBhAUCAyIAQAP3BuAD","973":"b:f5/93+f/v9v/4JNBhAUCAyIAQAP3BuAD","974":"b:f5/93+f/v9v/4JNBhAUCAyIAQAP3BuAD","981":"b:f5/93+f/v9v/4JNBhAUCAyIAQAP3BuAD","997":"b:f5/93+f/v9v/4JNBhAUCAyIAQAP3BuAD","1010":"b:f5/93+f/v9v/4JNBhAUCAyIAQAP3BuAD","1020":"b:f5/93+f/v9v/4JNBhAUCAyIAQAP3BuAD","1034":"b:f5/93+f/v9v/4JNBhAUCAyIAQAP3BuAD","1051":"b:f5/93+f/v9v/4JNBhAUCAyIAQAP3BuAD","1063":"b:f5/93+f/v9v/4JNBhAUCAyIAQAP3BuAD","1071":"b:f5/93+f/v9v/4JNBhAUCAyIAQAP3BuAD","1094":"b:f5/93+f/v9v/4JNBhAUCAyIAQAP3BuAD","1102":"b:f5/93+f/v9v/4JNBhAUCAyIAQAP3BuAD","1108":"b:f5/93+f/v9v/4JNBhAUCAyIAQAP3BuAD","1119":"b:f5/93+f/v9v/4JNBhAUCAyIAQAP3BuAD","1125":"b:f5/93+f/v9v/4JNBhAUCAyIAQAP3BuAD","1130":"b:f5/93+f/v9v/4JNBhAUCAyIAQAP3BuAD","1141":"b:f5/93+f/v9v/4JNBhAUCAyIAQAP3BuAD","1153":"b:f5/93+f/v9v/4JNBhAUCAyIAQAP3BuAD","1174":"b:f5/93+f/v9v/4JNBhAUCAyIAQAP3BuAD","1187":"b:f5/93+f/v9v/4JNBhAUCAyIAQAP3BuAD","1196":"b:f5/93+f/v9v/4JNBhAUCAyIAQAP3BuAD","1217":"b:f5/93+f/v9v/4JNBhAUCAyIAQAP3BuAD","1228":"b:f5/93+f/v9v/4JNBhAUCAyIAQAP3BuAD","1241":"b:f5/93+f/v9v/4JNBhAUCAyIAQAP3BuAD","1251":"b:f5/93+f/v9v/4JNBhAUCAyIAQAP3BuAD","1253":"b:f5/93+f/v9v/4JNBhAUCAyIAQAP3BuAD","1276":"b:f5/93+f/v9v/4JNBhAUCAyIAQAP3BuAD","1303":"b:f5/93+f/v9v/4JNBhAUCAyIAQAP3BuAD","1307":"b:f5/93+f/v9v/4JNBhAUCAyIAQAP3BuAD","1317":"b:f5/93+f/v9v/4JNBhAUCAyIAQAP3BuAD","1349":"b:f5/93+f/v9v/4JNBhAUCAyIAQAP3BuAD","1357":"b:f5/93+f/v9v/4JNBhAUCAyIAQAP3BuAD","1363":"b:f5/93+f/v9v/4JNBhAUCAyIAQAP3BuAD","1373":"b:f5/93+f/v9v/4JNBhAUCAyIAQAP3BuAD","1378":"b:f5/93+f/v9v/4JNBhAUCAyIAQAP3BuAD","1394":"b:f5/93+f/v9v/4JNBhAUCAyIAQAP3BuAD","1403":"b:f5/93+f/v9v/4JNBhAUCAyIAQAP3BuAD","1410":"b:f5/93+f/v9v/4JNBhAUCAyIAQAP3BuAD","1417":"b:f5/93+f/v9v/4JNBhAUCAyIAQAP3BuAD","1424":"b:f5/93+f/v9v/4JNBhAUCAyIAQAP3BuAD","1434":"b:f5/93+f/v9v/4JNBhAUCAyIAQAP3BuAD","1449":"b:f5/93+f/v9v/4JNBhAUCAyIAQAP3BuAD","1483":"b:f5/93+f/v9v/4JNBhAUCAyIAQAP3BuAD","1490":"b:f5/93+f/v9v/4JNBhAUCAyIAQAP3BuAD","1494":"b:f5/93+f/v9v/4JNBhAUCAyIAQAP3BuAD","1509":"b:f5/93+f/v9v/4JNBhAUCAyIAQAP3BuAD","1516":"b:f5/93+f/v9v/4JNBhAUCAyIAQAP3BuAD","1549":"b:f5/93+f/v9v/4JNBhAUCAyIAQAP3BuAD","1565":"b:f5/93+f/v9v/4JNBhAUCAyIAQAP3BuAD","1601":"b:f5/93+f/v9v/4JNBhAUCAyIAQAP3BuAD","1616":"b:f5/93+f/v9v/4JNBhAUCAyIAQAP3BuAD","1652":"b:f5/93+f/v9v/4JNBhAUCAyIAQAP3BuAD"},"packages/@n8n/db/src/utils/transformers.ts":{"7":"b:f5T906X3/JsXAAAAAAAAAAAAAADsAwEA","8":"b:f5T906X3/JsXAAAAAAAAAAAAAADsAwEA","15":"b:f5T906X3/JsXAAAAAAAAAAAAAADsAwEA","16":"b:f5T906X3/JsXAAAAAAAAAAAAAADsAwEA","23":"b:f5T906X3/JsXAAAAAAAAAAAAAADsAwEA","24":"b:f5T906X3/JsXAAAAAAAAAAAAAADsAwEA","32":"b:f5T906X3/JsXAAAAAAAAAAAAAADsAwEA","34":"b:f5T906X3/JsXAAAAAAAAAAAAAADsAwEA","44":"b:f5T906X3/JsXAAAAAAAAAAAAAADsAwEA","45":"b:f5T906X3/JsXAAAAAAAAAAAAAADsAwEA"},"packages/@n8n/decorators/src/execution-lifecycle/lifecycle-metadata.ts":{"82":"b:f5DlwyX3sBsDAAAAAAAAAAAAAADkAwAA","85":"b:f5DlwyX3sBsDAAAAAAAAAAAAAADkAwAA","89":"b:f5DlwyX3sBsDAAAAAAAAAAAAAADkAwAA"},"packages/@n8n/decorators/src/redactable.ts":{"14":"b:f4T9z6GhHJqT/xMAAAAAAAAAAADs8A8A","21":"b:f4T9z6GhHJqT/xMAAAAAAAAAAADs8A8A","46":"b:f4T9z6GhHJqT/xMAAAAAAAAAAADs8A8A","47":"b:f4T9z6GhHJqT/xMAAAAAAAAAAADs8A8A","53":"b:f4T9z6GhHJqT/xMAAAAAAAAAAADs8A8A","54":"b:f4T9z6GhHJqT/xMAAAAAAAAAAADs8A8A"},"packages/@n8n/errors/src/isolate.error.ts":{"2":"b:f5DlwwX3sBsTAAAAAAAAAAAAAADsAAAA"},"packages/@n8n/expression-runtime/src/bridge/isolated-vm-bridge.ts":{"15":"b:f5jl0yW3sBsTAAQAoCAACIDoAQHsA4AA","26":"b:f5jl0yW3sBsTAAQAoCAACIDoAQHsA4AA","42":"b:f5jl0yW3sBsTAAQAoCAACIDoAQHsA4AA","45":"b:f5jl0yW3sBsTAAQAoCAACIDoAQHsA4AA","66":"b:f5jl0yW3sBsTAAQAoCAACIDoAQHsA4AA","98":"b:f5jl0yW3sBsTAAQAoCAACIDoAQHsA4AA","121":"b:f5jl0yW3sBsTAAQAoCAACIDoAQHsA4AA","163":"b:f5jl0yW3sBsTAAQAoCAACIDoAQHsA4AA","204":"b:f5jl0yW3sBsTAAQAoCAACIDoAQHsA4AA","255":"b:f5jl0yW3sBsTAAQAoCAACIDoAQHsA4AA","294":"b:f5jl0yW3sBsTAAQAoCAACIDoAQHsA4AA","295":"b:f5jl0yW3sBsTAAQAoCAACIDoAQHsA4AA","371":"b:f5jl0yW3sBsTAAQAoCAACIDoAQHsA4AA","372":"b:f5jl0yW3sBsTAAQAoCAACIDoAQHsA4AA","468":"b:f5jl0yW3sBsTAAQAoCAACIDoAQHsA4AA","469":"b:f5jl0yW3sBsTAAQAoCAACIDoAQHsA4AA","531":"b:f5jl0yW3sBsTAAQAoCAACIDoAQHsA4AA","538":"b:f5jl0yW3sBsTAAQAoCAACIDoAQHsA4AA","545":"b:f5jl0yW3sBsTAAQAoCAACIDoAQHsA4AA","563":"b:f5jl0yW3sBsTAAQAoCAACIDoAQHsA4AA","567":"b:f5jl0yW3sBsTAAQAoCAACIDoAQHsA4AA","571":"b:f5jl0yW3sBsTAAQAoCAACIDoAQHsA4AA","584":"b:f5jl0yW3sBsTAAQAoCAACIDoAQHsA4AA","607":"b:f5jl0yW3sBsTAAQAoCAACIDoAQHsA4AA","629":"b:f5jl0yW3sBsTAAQAoCAACIDoAQHsA4AA","636":"b:f5jl0yW3sBsTAAQAoCAACIDoAQHsA4AA","643":"b:f5jl0yW3sBsTAAQAoCAACIDoAQHsA4AA","662":"b:f5jl0yW3sBsTAAQAoCAACIDoAQHsA4AA","683":"b:f5jl0yW3sBsTAAQAoCAACIDoAQHsA4AA","713":"b:f5jl0yW3sBsTAAQAoCAACIDoAQHsA4AA","812":"b:f5jl0yW3sBsTAAQAoCAACIDoAQHsA4AA","832":"b:f5jl0yW3sBsTAAQAoCAACIDoAQHsA4AA","853":"b:f5jl0yW3sBsTAAQAoCAACIDoAQHsA4AA"},"packages/@n8n/expression-runtime/src/evaluator/expression-evaluator.ts":{"19":"b:f5jl0yW3sBsTAAAAgAAACABIAQDsAgAA","52":"b:f5jl0yW3sBsTAAAAgAAACABIAQDsAgAA","54":"b:f5jl0yW3sBsTAAAAgAAACABIAQDsAgAA","58":"b:f5jl0yW3sBsTAAAAgAAACABIAQDsAgAA","64":"b:f5jl0yW3sBsTAAAAgAAACABIAQDsAgAA","82":"b:f5jl0yW3sBsTAAAAgAAACABIAQDsAgAA","92":"b:f5jl0yW3sBsTAAAAgAAACABIAQDsAgAA","107":"b:f5jl0yW3sBsTAAAAgAAACABIAQDsAgAA","135":"b:f5jl0yW3sBsTAAAAgAAACABIAQDsAgAA","150":"b:f5jl0yW3sBsTAAAAgAAACABIAQDsAgAA","157":"b:f5jl0yW3sBsTAAAAgAAACABIAQDsAgAA","170":"b:f5jl0yW3sBsTAAAAgAAACABIAQDsAgAA","184":"b:f5jl0yW3sBsTAAAAgAAACABIAQDsAgAA","200":"b:f5jl0yW3sBsTAAAAgAAACABIAQDsAgAA","207":"b:f5jl0yW3sBsTAAAAgAAACABIAQDsAgAA"},"packages/@n8n/expression-runtime/src/evaluator/lru-cache.ts":{"4":"b:f4jAQwCQMBECAAAAgAAACABIAQAEAgAA","13":"b:f4jAQwCQMBECAAAAgAAACABIAQAEAgAA","21":"b:f4jAQwCQMBECAAAAgAAACABIAQAEAgAA","31":"b:f4jAQwCQMBECAAAAgAAACABIAQAEAgAA","35":"b:f4jAQwCQMBECAAAAgAAACABIAQAEAgAA"},"packages/@n8n/expression-runtime/src/pool/isolate-pool.ts":{"6":"b:f5Dl0yX3sBsTAAAAAAAAAAAAAADsAAAA","13":"b:f5Dl0yX3sBsTAAAAAAAAAAAAAADsAAAA","36":"b:f5Dl0yX3sBsTAAAAAAAAAAAAAADsAAAA","43":"b:f5Dl0yX3sBsTAAAAAAAAAAAAAADsAAAA","45":"b:f5Dl0yX3sBsTAAAAAAAAAAAAAADsAAAA","67":"b:f5Dl0yX3sBsTAAAAAAAAAAAAAADsAAAA","75":"b:f5Dl0yX3sBsTAAAAAAAAAAAAAADsAAAA","80":"b:f5Dl0yX3sBsTAAAAAAAAAAAAAADsAAAA","83":"b:f5Dl0yX3sBsTAAAAAAAAAAAAAADsAAAA","90":"b:f5Dl0yX3sBsTAAAAAAAAAAAAAADsAAAA","96":"b:f5Dl0yX3sBsTAAAAAAAAAAAAAADsAAAA","103":"b:f5Dl0yX3sBsTAAAAAAAAAAAAAADsAAAA","112":"b:f5Dl0yX3sBsTAAAAAAAAAAAAAADsAAAA","119":"b:f5Dl0yX3sBsTAAAAAAAAAAAAAADsAAAA"},"packages/@n8n/nodes-langchain/nodes/chains/ChainRetrievalQA/ChainRetrievalQa.node.ts":{"20":[2],"185":[2],"201":[2],"207":[2]},"packages/@n8n/nodes-langchain/nodes/chains/ChainRetrievalQA/processItem.ts":{"19":[2]},"packages/@n8n/nodes-langchain/nodes/code/Code.node.ts":{"67":[0,2],"122":[0,2],"126":[0,2],"136":[0,2],"146":[0,2],"150":[0,2],"151":[0,2],"152":[0,2]},"packages/@n8n/nodes-langchain/nodes/document_loaders/DocumentDefaultDataLoader/DocumentDefaultDataLoader.node.ts":{"21":[0,2],"38":[0,2],"329":[0,2]},"packages/@n8n/nodes-langchain/nodes/retrievers/RetrieverVectorStore/RetrieverVectorStore.node.ts":{"14":[2],"73":[2]},"packages/@n8n/nodes-langchain/nodes/text_splitters/TextSplitterCharacterTextSplitter/TextSplitterCharacterTextSplitter.node.ts":{"13":[0,2],"68":[0,2]},"packages/@n8n/nodes-langchain/nodes/trigger/ChatTrigger/ChatTrigger.node.ts":{"35":[82,1,2,3,4,5,8,10,109,123,6],"229":[82,1,2,3,4,5,8,10,109,123,6],"750":[82,1,2,3,4,5,8,10,109,123,6],"813":[82,1,2,3,4,5,8,10,109,123,6],"939":[82,1,2,3,4,5,8,10,109,123,6],"940":[82,1,2,3,4,5,8,10,109,123,6]},"packages/@n8n/nodes-langchain/nodes/trigger/ChatTrigger/GenericFunctions.ts":{"7":[82,1,2,3,4,5,8,10,109,123,6],"41":[82,1,2,3,4,5,8,10,109,123,6]},"packages/@n8n/nodes-langchain/nodes/trigger/ChatTrigger/error.ts":{"4":[82,1,2,3,4,5,8,10,109,123,6]},"packages/@n8n/nodes-langchain/nodes/trigger/ChatTrigger/templates.ts":{"5":[82,1,2,3,4,5,8,10,109,123,6],"21":[82,1,2,3,4,5,8,10,109,123,6],"26":[82,1,2,3,4,5,8,10,109,123,6],"27":[82,1,2,3,4,5,8,10,109,123,6],"42":[82,1,2,3,4,5,8,10,109,123,6],"43":[82,1,2,3,4,5,8,10,109,123,6],"46":[82,1,2,3,4,5,8,10,109,123,6],"55":[82,1,2,3,4,5,8,10,109,123,6],"62":[82,1,2,3,4,5,8,10,109,123,6]},"packages/@n8n/nodes-langchain/nodes/trigger/ChatTrigger/types.ts":{"8":[82,1,2,3,4,5,8,10,109,123,6],"12":[82,1,2,3,4,5,8,10,109,123,6]},"packages/@n8n/nodes-langchain/nodes/vector_store/VectorStoreInMemory/VectorStoreInMemory.node.ts":{"37":[0,2],"132":[0,2],"143":[0,2],"152":[0,2],"161":[0,2],"186":[0,2],"192":[0,2]},"packages/@n8n/nodes-langchain/utils/N8nTool.ts":{"11":[1,2,3,4,5,8,9,10,11,12,121,6,14],"25":[1,2,3,4,5,8,9,10,11,12,121,6,14],"28":[1,2,3,4,5,8,9,10,11,12,121,6,14],"33":[1,2,3,4,5,8,9,10,11,12,121,6,14],"50":[1,2,3,4,5,8,9,10,11,12,121,6,14],"57":[1,2,3,4,5,8,9,10,11,12,121,6,14],"62":[1,2,3,4,5,8,9,10,11,12,121,6,14]},"packages/@n8n/nodes-langchain/utils/descriptions.ts":{"28":[1,2,3,4,5,8,9,10,11,12,121,6,14],"54":[1,2,3,4,5,8,9,10,11,12,121,6,14],"72":[1,2,3,4,5,8,9,10,11,12,121,6,14]},"packages/@n8n/nodes-langchain/utils/helpers.ts":{"17":[1,2,3,4,5,93,8,9,10,11,12,121,6,14],"64":[1,2,3,4,5,93,8,9,10,11,12,121,6,14],"77":[1,2,3,4,5,93,8,9,10,11,12,121,6,14],"81":[1,2,3,4,5,93,8,9,10,11,12,121,6,14],"145":[1,2,3,4,5,93,8,9,10,11,12,121,6,14],"147":[1,2,3,4,5,93,8,9,10,11,12,121,6,14],"159":[1,2,3,4,5,93,8,9,10,11,12,121,6,14],"180":[1,2,3,4,5,93,8,9,10,11,12,121,6,14],"192":[1,2,3,4,5,93,8,9,10,11,12,121,6,14],"213":[1,2,3,4,5,93,8,9,10,11,12,121,6,14],"217":[1,2,3,4,5,93,8,9,10,11,12,121,6,14],"221":[1,2,3,4,5,93,8,9,10,11,12,121,6,14],"271":[1,2,3,4,5,93,8,9,10,11,12,121,6,14],"289":[1,2,3,4,5,93,8,9,10,11,12,121,6,14],"307":[1,2,3,4,5,93,8,9,10,11,12,121,6,14]},"packages/@n8n/nodes-langchain/utils/tracing.ts":{"19":[1,2,3,4,5,8,10,12,121,6],"83":[1,2,3,4,5,8,10,12,121,6]},"packages/@n8n/permissions/src/index.ts":{"20":"b:f5T936W33J6X/xMAAAAAAAAAAADs8A8A","21":"b:f5T936W33J6X/xMAAAAAAAAAAADs8A8A","25":"b:f5T936W33J6X/xMAAAAAAAAAAADs8A8A","27":"b:f5T936W33J6X/xMAAAAAAAAAAADs8A8A"},"packages/@n8n/permissions/src/roles/all-roles.ts":{"58":"b://Xf/P///+39//////////////8DEAEA","62":"b://Xf/P///+39//////////////8DEAEA","83":"b:f5Td3KWlnIz5/wMAAAAIAAAAAAAAEAEA"},"packages/@n8n/permissions/src/utilities/combine-scopes.ee.ts":{"16":"b://X////////////////////////vMAkA","18":"b://X////////////////////////vMAkA","23":"b:f5T936W33J6X/xMAAAAAAAAAAADsMAkA","26":"b:f5T936W33J6X/xMAAAAAAAAAAADsMAkA"},"packages/@n8n/permissions/src/utilities/get-global-scopes.ee.ts":{"8":"b:f4T9z6GhHJqT/xMAAAAAAAAAAADs8A8A","9":"b:f4T9z6GhHJqT/xMAAAAAAAAAAADs8A8A"},"packages/@n8n/permissions/src/utilities/get-role-scopes.ee.ts":{"5":"b://X////////////////////////vMAkA","16":"b://X////////////////////////vMAkA","19":"b:f5T936W33J6X/xMAAAAAAAAAAADsMAkA","30":"b:f5T936W33J6X/xMAAAAAAAAAAADsMAkA","36":"b:f5T936W33J6X/xMAAAAAAAAAAADsMAkA","38":"b:f5T936W33J6X/xMAAAAAAAAAAADsMAkA"},"packages/@n8n/permissions/src/utilities/has-global-scope.ee.ts":{"10":"b:f4T936WlHJqT/xMAAAAAAAAAAADsMAkA"},"packages/@n8n/permissions/src/utilities/has-scope.ee.ts":{"11":"b://X////////////////////////vMAkA","20":"b://X//////////5//////8//////vMAkA","21":"b://X////////////////////////vMAkA"},"packages/@n8n/tournament/src/ExpressionBuilder.ts":{"25":"b:///P8+S7+NVWQCQCq/wA+vX//6sHAyAA","29":"b:///P8+S7+NVWQCQCq/wA+PX//ysHAyAA","33":"b:///IUwCQOBECACQCoTgAOoTqn6MHAyAA","37":"b:f4rAQwCQMBECAAQAoCAAKIB8hSAEAyAA","50":"b:///P8+S7+NVWQCQCq/wA+vX//6sHAyAA","54":"b:f4rAQwCQMBECAAQAoCgAKIBohSAEAyAA","58":"b:f4rAQwCQMBECAAQAoCAAKIBohSAEAyAA","62":"b:///AUwCQMBECACQCoDgAOoTqh6AHAyAA","71":"b:///P8+S7+NVWQCQCq/wA+vX//6sHAyAA","75":"b:f7rAQwCQMBECAAQAoCAAKoBohSAEAyAA","88":"b:///P8+S7+NVWQCQCq/wA+vX//6sHAyAA","103":"b:///P8+S7+NVWQCQCq/wA+vX//6sHAyAA","110":"b:f4rIQwCQcBECAAQAoCgAKIDohSAEAyAA","136":"b:///P8+S7+NVWQCQCq/wA+vX//6sHAyAA","137":"b:f7rAQwCQMBECAAQAoCAAKoBohSAEAyAA","141":"b:f7rAQwCQMBECAAQAoCAAKoBohSAEAyAA","157":"b:///P8+S7+NVWQCQCq/wA+vX//6sHAyAA","158":"b:///P8+S7+NVWQCQCq/wA+vX//6sHAyAA","171":"b:///P8+S7+NVWQCQCq/wA+vX//6sHAyAA","188":"b:///P8+S7+NVWQCQCq/wA+vX//6sHAyAA","197":"b:///P8+S7+NVWQCQCq/wA+vX//6sHAyAA","198":"b:///P8+S7+NVWQCQCq/wA+vX//6sHAyAA","263":"b:f4rIQwCQcBECAAQAoCgAKIDohSAEAyAA"},"packages/@n8n/tournament/src/ExpressionSplitter.ts":{"19":"b://XP8+S7+NVWQCQCq/wA+vX//4sHAgAA","23":"b://XP8+S7+NVWQCQCq/wA+vX//4sHAgAA","27":"b://XP8+S7+NVWQCQCq/wA+vX//4sHAgAA","101":"b:f4DAQwCQMBECAAAAgAAACABIAQAEAgAA","105":"b:f4DAQwCQMBECAAAAgAAACABIAQAEAgAA","107":"b:f4DAQwCQMBECAAAAgAAACABIAQAEAgAA"},"packages/@n8n/tournament/src/Parser.ts":{"5":"b://XP8+S7+NVWQCQCq/wA+vX//4sHAgAA"},"packages/@n8n/tournament/src/VariablePolyfill.ts":{"12":"b:/z3AQwCQMBECAAAAgBAAGABIBSAEAyAA","21":"b://3P8+S7+NVWQCQCq/wA+vX//6sHAyAA","32":"b://3P8+S7+NVWQCQCq/wA+vX//6sHAyAA","45":"b://3P8+S7+NVWQCQCq/wA+vX//6sHAyAA","72":"b://3P8+S7+NVWQCQCq/wA+vX//6sHAyAA","77":"b:fwjAQwCQMBECAAAAgAAACABIBSAEAyAA","82":"b:/33AUwCQMBECACQCgDgAOoTeB6AHAyAA","101":"b:fwjAQwCQMBECAAAAgAAACABIBSAEAyAA","106":"b:/33AUwCQMBECACACgBgAOoTKB6AHAyAA","111":"b:/33AUwCQMBECACACgBgAOoTKB6AHAyAA","122":"b://3P8+S7+NVWQCQCq/wA+vX//6sHAyAA","127":"b:fwjAQwCQMBECAAAAgAAACABIBSAEAyAA","130":"b://3P8+S7+NVWQCQCq/wA+vX//6sHAyAA"},"packages/@n8n/tournament/src/index.ts":{"16":"b://Xf//////3///////////////8HAgAA","17":"b:f4DAQwCQMBECAAAAgAAACABIAQAEAgAA","25":"b://Xf//////3///////////////8HAgAA","29":"b://XP8+S7+NVWQCQCq/wA+vX//4sHAgAA","33":"b://XP8+S7+NVWQCQCq/wA+vX//4sHAgAA"},"packages/@n8n/utils/src/promise/deferred-promise.ts":{"10":"b:f5DlwyX3sBsDAAAAAAAAAAAAAADkAwAA","12":"b:f5DlwyX3sBsDAAAAAAAAAAAAAADkAwAA"},"packages/cli/src/abstract-server.ts":{"80":"b:VwkBgLRBHYABLQAoAAAAAWAAAAIAAAAA","113":"b:VwkBgLRBHYABLQAoAAAAAWAAAAIAAAAA","117":"b:VwkBgLRBHYABLQAoAAAAAWAAAAIAAAAA","125":"b:VwkBgLRBHYABLQAoAAAAAWAAAAIAAAAA","133":"b:VwkBgLRBHYABLQAoAAAAAWAAAAIAAAAA","137":"b:VwkBgLRBHYABLQAoAAAAAWAAAAIAAAAA","140":"b:VwkBgLRBHYABLQAoAAAAAWAAAAIAAAAA","144":"b:VwkBgLRBHYABLQAoAAAAAWAAAAIAAAAA","151":"b:VwkBgLRBHYABLQAoAAAAAWAAAAIAAAAA","157":"b:VwkBgLRBHYABLQAoAAAAAWAAAAIAAAAA","166":"b:VwkBgLRBHYABLQAoAAAAAWAAAAIAAAAA","174":"b:VwkBgLRBHYABLQAoAAAAAWAAAAIAAAAA","194":"b:VwkBgLRBHYABLQAoAAAAAWAAAAIAAAAA","224":"b:VwkBgLRBHYABLQAoAAAAAWAAAAIAAAAA","233":"b:VwkBgLRBHYABLQAoAAAAAWAAAAIAAAAA","306":"b:VwkBgLRBHYABLQAoAAAAAWAAAAIAAAAA","310":"b:VwkBgLRBHYABLQAoAAAAAWAAAAIAAAAA","349":"b:VwkBgLRBHYABLQAoAAAAAWAAAAIAAAAA","358":"b:VwkBgLRBHYABLQAoAAAAAWAAAAIAAAAA"},"packages/cli/src/active-executions.ts":{"45":"b:f5jlwyX3sBsDAAQAoAAACAAABCDkA6AA","54":"b:f5jlwyX3sBsDAAQAoAAACAAABCDkA6AA","61":"b:f5jlwyX3sBsDAAQAoAAACAAABCDkA6AA","159":"b:f5jlwyX3sBsDAAQAoAAACAAABCDkA6AA","163":"b:f5jlwyX3sBsDAAQAoAAACAAABCDkA6AA","184":"b:f5jlwyX3sBsDAAQAoAAACAAABCDkA6AA","188":"b:f5jlwyX3sBsDAAQAoAAACAAABCDkA6AA","195":"b:f5jlwyX3sBsDAAQAoAAACAAABCDkA6AA","201":"b:f5jlwyX3sBsDAAQAoAAACAAABCDkA6AA","210":"b:f5jlwyX3sBsDAAQAoAAACAAABCDkA6AA","240":"b:f5jlwyX3sBsDAAQAoAAACAAABCDkA6AA","262":"b:f5jlwyX3sBsDAAQAoAAACAAABCDkA6AA","280":"b:f5jlwyX3sBsDAAQAoAAACAAABCDkA6AA","287":"b:f5jlwyX3sBsDAAQAoAAACAAABCDkA6AA","307":"b:f5jlwyX3sBsDAAQAoAAACAAABCDkA6AA","311":"b:f5jlwyX3sBsDAAQAoAAACAAABCDkA6AA","315":"b:f5jlwyX3sBsDAAQAoAAACAAABCDkA6AA","319":"b:f5jlwyX3sBsDAAQAoAAACAAABCDkA6AA","324":"b:f5jlwyX3sBsDAAQAoAAACAAABCDkA6AA","359":"b:f5jlwyX3sBsDAAQAoAAACAAABCDkA6AA"},"packages/cli/src/auth/auth-handler.registry.ts":{"7":"b:f4T9z6GhHJqT/xMAAAAAAAAAAADs8A8A","20":"b:f4T9z6GhHJqT/xMAAAAAAAAAAADs8A8A","30":"b:f4T9z6GhHJqT/xMAAAAAAAAAAADs8A8A","82":"b:f4T9z6GhHJqT/xMAAAAAAAAAAADs8A8A","94":"b:f4T9z6GhHJqT/xMAAAAAAAAAAADs8A8A","112":"b:f4T9z6GhHJqT/xMAAAAAAAAAAADs8A8A"},"packages/cli/src/auth/auth.service.ts":{"64":"b:f5T936X/vJuT/xMAAAAAAAAAAADs8A8A","106":"b:f5T936X/vJuT/xMAAAAAAAAAAADs8A8A","111":"b:f5T936X/vJuT/xMAAAAAAAAAAADs8A8A","169":"b:f5T936X/vJuT/xMAAAAAAAAAAADs8A8A","178":"b:f5T936X/vJuT/xMAAAAAAAAAAADs8A8A","186":"b:f5T936X/vJuT/xMAAAAAAAAAAADs8A8A","190":"b:f5T936X/vJuT/xMAAAAAAAAAAADs8A8A","194":"b:f5T936X/vJuT/xMAAAAAAAAAAADs8A8A","198":"b:f5T936X/vJuT/xMAAAAAAAAAAADs8A8A","214":"b:f5T936X/vJuT/xMAAAAAAAAAAADs8A8A","239":"b:f5T936X/vJuT/xMAAAAAAAAAAADs8A8A","260":"b:f5T936X/vJuT/xMAAAAAAAAAAADs8A8A","267":"b:f5T936X/vJuT/xMAAAAAAAAAAADs8A8A","293":"b:f5T936X/vJuT/xMAAAAAAAAAAADs8A8A","303":"b:f5T936X/vJuT/xMAAAAAAAAAAADs8A8A","319":"b:f5T936X/vJuT/xMAAAAAAAAAAADs8A8A","336":"b:f5T936X/vJuT/xMAAAAAAAAAAADs8A8A","367":"b:f5T936X/vJuT/xMAAAAAAAAAAADs8A8A","397":"b:f5T936X/vJuT/xMAAAAAAAAAAADs8A8A","402":"b:f5T936X/vJuT/xMAAAAAAAAAAADs8A8A","412":"b:f5T936X/vJuT/xMAAAAAAAAAAADs8A8A","446":"b:f5T936X/vJuT/xMAAAAAAAAAAADs8A8A","454":"b:f5T936X/vJuT/xMAAAAAAAAAAADs8A8A","459":"b:f5T936X/vJuT/xMAAAAAAAAAAADs8A8A","469":"b:f5T936X/vJuT/xMAAAAAAAAAAADs8A8A"},"packages/cli/src/auth/handlers/email.auth-handler.ts":{"16":"b:f4T9z6GhHJqT/xMAAAAAAAAAAADs8A8A","23":"b:f4T9z6GhHJqT/xMAAAAAAAAAAADs8A8A","35":"b:f4T9z6GhHJqT/xMAAAAAAAAAAADs8A8A"},"packages/cli/src/chat/chat-server.ts":{"21":"b:f5Xd0KWl2IDdfwAAAAAAAAAAAAAAAAAA","23":"b:f5Xd0KWl2IDdfwAAAAAAAAAAAAAAAAAA","24":"b:f5Xd0KWl2IDdfwAAAAAAAAAAAAAAAAAA","28":"b:f5Xd0KWl2IDdfwAAAAAAAAAAAAAAAAAA","34":"b:f5Xd0KWl2IDdfwAAAAAAAAAAAAAAAAAA","39":"b:f5Xd0KWl2IDdfwAAAAAAAAAAAAAAAAAA","42":"b:f5Xd0KWl2IDdfwAAAAAAAAAAAAAAAAAA","51":"b:f5Xd0KWl2IDdfwAAAAAAAAAAAAAAAAAA"},"packages/cli/src/chat/chat-service.ts":{"44":"b:X4Ed/KmDDJTZ/BuoAAAAEAAAAACAAAAA","47":"b:X4Ed/KmDDJTZ/BuoAAAAEAAAAACAAAAA","51":"b:X4Ed/KmDDJTZ/BuoAAAAEAAAAACAAAAA","63":"b:X4Ed/KmDDJTZ/BuoAAAAEAAAAACAAAAA","69":"b:X4Ed/KmDDJTZ/BuoAAAAEAAAAACAAAAA","74":"b:X4Ed/KmDDJTZ/BuoAAAAEAAAAACAAAAA","121":"b:X4Ed/KmDDJTZ/BuoAAAAEAAAAACAAAAA","123":"b:X4Ed/KmDDJTZ/BuoAAAAEAAAAACAAAAA","146":"b:X4Ed/KmDDJTZ/BuoAAAAEAAAAACAAAAA","176":"b:X4Ed/KmDDJTZ/BuoAAAAEAAAAACAAAAA","181":"b:X4Ed/KmDDJTZ/BuoAAAAEAAAAACAAAAA","190":"b:X4Ed/KmDDJTZ/BuoAAAAEAAAAACAAAAA","191":"b:X4Ed/KmDDJTZ/BuoAAAAEAAAAACAAAAA","237":"b:X4Ed/KmDDJTZ/BuoAAAAEAAAAACAAAAA","238":"b:X4Ed/KmDDJTZ/BuoAAAAEAAAAACAAAAA","266":"b:X4Ed/KmDDJTZ/BuoAAAAEAAAAACAAAAA","272":"b:X4Ed/KmDDJTZ/BuoAAAAEAAAAACAAAAA","298":"b:X4Ed/KmDDJTZ/BuoAAAAEAAAAACAAAAA","300":"b:X4Ed/KmDDJTZ/BuoAAAAEAAAAACAAAAA","307":"b:X4Ed/KmDDJTZ/BuoAAAAEAAAAACAAAAA","313":"b:X4Ed/KmDDJTZ/BuoAAAAEAAAAACAAAAA","318":"b:X4Ed/KmDDJTZ/BuoAAAAEAAAAACAAAAA","328":"b:X4Ed/KmDDJTZ/BuoAAAAEAAAAACAAAAA","335":"b:X4Ed/KmDDJTZ/BuoAAAAEAAAAACAAAAA","363":"b:X4Ed/KmDDJTZ/BuoAAAAEAAAAACAAAAA","379":"b:X4Ed/KmDDJTZ/BuoAAAAEAAAAACAAAAA"},"packages/cli/src/collaboration/collaboration.message.ts":{"71":"b:R5Xd0KW32IBdAAAAAAAAAAAAAAAAAAAA"},"packages/cli/src/collaboration/collaboration.service.ts":{"34":"b:R93d0+e32JJfAAAAIAQAY2ETQAH1AgAA","43":"b:R93d0+e32JJfAAAAIAQAY2ETQAH1AgAA","44":"b:R93d0+e32JJfAAAAIAQAY2ETQAH1AgAA","69":"b:R93d0+e32JJfAAAAIAQAY2ETQAH1AgAA","78":"b:R93d0+e32JJfAAAAIAQAY2ETQAH1AgAA","94":"b:R93d0+e32JJfAAAAIAQAY2ETQAH1AgAA","110":"b:R93d0+e32JJfAAAAIAQAY2ETQAH1AgAA","133":"b:R93d0+e32JJfAAAAIAQAY2ETQAH1AgAA","137":"b:R93d0+e32JJfAAAAIAQAY2ETQAH1AgAA","143":"b:R93d0+e32JJfAAAAIAQAY2ETQAH1AgAA","145":"b:R93d0+e32JJfAAAAIAQAY2ETQAH1AgAA","155":"b:R93d0+e32JJfAAAAIAQAY2ETQAH1AgAA","183":"b:R93d0+e32JJfAAAAIAQAY2ETQAH1AgAA","200":"b:R93d0+e32JJfAAAAIAQAY2ETQAH1AgAA","211":"b:R93d0+e32JJfAAAAIAQAY2ETQAH1AgAA","217":"b:R93d0+e32JJfAAAAIAQAY2ETQAH1AgAA","232":"b:R93d0+e32JJfAAAAIAQAY2ETQAH1AgAA","234":"b:R93d0+e32JJfAAAAIAQAY2ETQAH1AgAA","247":"b:R93d0+e32JJfAAAAIAQAY2ETQAH1AgAA","249":"b:R93d0+e32JJfAAAAIAQAY2ETQAH1AgAA","263":"b:R93d0+e32JJfAAAAIAQAY2ETQAH1AgAA","270":"b:R93d0+e32JJfAAAAIAQAY2ETQAH1AgAA","301":"b:R93d0+e32JJfAAAAIAQAY2ETQAH1AgAA","307":"b:R93d0+e32JJfAAAAIAQAY2ETQAH1AgAA","327":"b:R93d0+e32JJfAAAAIAQAY2ETQAH1AgAA","329":"b:R93d0+e32JJfAAAAIAQAY2ETQAH1AgAA","347":"b:R93d0+e32JJfAAAAIAQAY2ETQAH1AgAA","361":"b:R93d0+e32JJfAAAAIAQAY2ETQAH1AgAA","375":"b:R93d0+e32JJfAAAAIAQAY2ETQAH1AgAA"},"packages/cli/src/collaboration/collaboration.state.ts":{"35":"b:R9Xd06e32JJfAAAAIAQAY2ETQAHlAgAA","40":"b:R9Xd06e32JJfAAAAIAQAY2ETQAHlAgAA","52":"b:R9Xd06e32JJfAAAAIAQAY2ETQAHlAgAA","58":"b:R9Xd06e32JJfAAAAIAQAY2ETQAHlAgAA","86":"b:R9Xd06e32JJfAAAAIAQAY2ETQAHlAgAA","90":"b:R9Xd06e32JJfAAAAIAQAY2ETQAHlAgAA","105":"b:R9Xd06e32JJfAAAAIAQAY2ETQAHlAgAA","112":"b:R9Xd06e32JJfAAAAIAQAY2ETQAHlAgAA","117":"b:R9Xd06e32JJfAAAAIAQAY2ETQAHlAgAA","149":"b:R9Xd06e32JJfAAAAIAQAY2ETQAHlAgAA","160":"b:R9Xd06e32JJfAAAAIAQAY2ETQAHlAgAA","166":"b:R9Xd06e32JJfAAAAIAQAY2ETQAHlAgAA","177":"b:R9Xd06e32JJfAAAAIAQAY2ETQAHlAgAA","198":"b:R9Xd06e32JJfAAAAIAQAY2ETQAHlAgAA","203":"b:R9Xd06e32JJfAAAAIAQAY2ETQAHlAgAA","212":"b:R9Xd06e32JJfAAAAIAQAY2ETQAHlAgAA"},"packages/cli/src/concurrency/concurrency-capacity-reservation.ts":{"21":"b:f5DlwyX3sBsDAAAAAAAAAAAAAADkAwAA","23":"b:f5DlwyX3sBsDAAAAAAAAAAAAAADkAwAA","30":"b:f5DlwyX3sBsDAAAAAAAAAAAAAADkAwAA"},"packages/cli/src/concurrency/concurrency-control.service.ts":{"35":"b:f5DlwyX3sBsDAAAAAAAAAAAAAADkAyAA","45":"b:f5DlwyX3sBsDAAAAAAAAAAAAAADkAyAA","90":"b:f5DlwyX3sBsDAAAAAAAAAAAAAADkAyAA","100":"b:f5DlwyX3sBsDAAAAAAAAAAAAAADkAyAA","113":"b:f5DlwyX3sBsDAAAAAAAAAAAAAADkAyAA","135":"b:f5DlwyX3sBsDAAAAAAAAAAAAAADkAyAA","136":"b:f5DlwyX3sBsDAAAAAAAAAAAAAADkAyAA","145":"b:f5DlwyX3sBsDAAAAAAAAAAAAAADkAyAA","150":"b:f5DlwyX3sBsDAAAAAAAAAAAAAADkAyAA","158":"b:f5DlwyX3sBsDAAAAAAAAAAAAAADkAyAA","173":"b:f5DlwyX3sBsDAAAAAAAAAAAAAADkAyAA","183":"b:f5DlwyX3sBsDAAAAAAAAAAAAAADkAyAA","193":"b:f5DlwyX3sBsDAAAAAAAAAAAAAADkAyAA","205":"b:f5DlwyX3sBsDAAAAAAAAAAAAAADkAyAA","208":"b:f5DlwyX3sBsDAAAAAAAAAAAAAADkAyAA","225":"b:f5DlwyX3sBsDAAAAAAAAAAAAAADkAyAA","233":"b:f5DlwyX3sBsDAAAAAAAAAAAAAADkAyAA","237":"b:f5DlwyX3sBsDAAAAAAAAAAAAAADkAyAA","244":"b:f5DlwyX3sBsDAAAAAAAAAAAAAADkAyAA"},"packages/cli/src/controller.registry.ts":{"37":"b:f5X9/62lHt6X/wNIAAAEAAAAAAD8MBgA","46":"b:f5X9/62lHt6X/wNIAAAEAAAAAAD8MBgA","54":"b:f5X9/62lHt6X/wNIAAAEAAAAAAD8MBgA","66":"b:f5X9/62lHt6X/wNIAAAEAAAAAAD8MBgA","92":"b:f5X9/62lHt6X/wNIAAAEAAAAAAD8MBgA","118":"b:f5X9/62lHt6X/wNIAAAEAAAAAAD8MBgA","123":"b:f5X9/62lHt6X/wNIAAAEAAAAAAD8MBgA","146":"b:f5X9/62lHt6X/wNIAAAEAAAAAAD8MBgA","226":"b:f5X9/62lHt6X/wNIAAAEAAAAAAD8MBgA","227":"b:f5X9/62lHt6X/wNIAAAEAAAAAAD8MBgA","236":"b:f5X9/62lHt6X/wNIAAAEAAAAAAD8MBgA","237":"b:f5X9/62lHt6X/wNIAAAEAAAAAAD8MBgA"},"packages/cli/src/controllers/active-workflows.controller.ts":{"8":"b:R5Td3KWnnID94AMAAAAAAAAAAAAAAAAA","11":"b:R5Td3KWnnID94AMAAAAAAAAAAAAAAAAA","16":"b:R5Td3KWnnID94AMAAAAAAAAAAAAAAAAA"},"packages/cli/src/controllers/auth.controller.ts":{"40":"b:f5T936WlnJ7//xMAAAAIAAAAAADs8A8A","67":"b:f5T936WlnJ7//xMAAAAIAAAAAADs8A8A","109":"b:f5T936WlnJ7//xMAAAAIAAAAAADs8A8A","115":"b:f5T936WlnJ7//xMAAAAIAAAAAADs8A8A","131":"b:f5T936WlnJ7//xMAAAAIAAAAAADs8A8A","164":"b:f5T936WlnJ7//xMAAAAIAAAAAADs8A8A","192":"b:f5T936WlnJ7//xMAAAAIAAAAAADs8A8A","205":"b:f5T936WlnJ7//xMAAAAIAAAAAADs8A8A","250":"b:f5T936WlnJ7//xMAAAAIAAAAAADs8A8A","259":"b:f5T936WlnJ7//xMAAAAIAAAAAADs8A8A","278":"b:f5T936WlnJ7//xMAAAAIAAAAAADs8A8A"},"packages/cli/src/controllers/e2e.controller.ts":{"186":"b:f4T936WlnJ7//xMAAABIAAAAAADs8Q8A","200":"b:f4T936WlnJ7//xMAAABIAAAAAADs8Q8A","203":"b:f4T936WlnJ7//xMAAABIAAAAAADs8Q8A","214":"b:f4T936WlnJ7//xMAAABIAAAAAADs8Q8A","218":"b:f4T936WlnJ7//xMAAABIAAAAAADs8Q8A","229":"b:f4T936WlnJ7//xMAAABIAAAAAADs8Q8A","235":"b:f4T936WlnJ7//xMAAABIAAAAAADs8Q8A","241":"b:f4T936WlnJ7//xMAAABIAAAAAADs8Q8A","247":"b:f4T936WlnJ7//xMAAABIAAAAAADs8Q8A","258":"b:f4T936WlnJ7//xMAAABIAAAAAADs8Q8A","266":"b:f4T936WlnJ7//xMAAABIAAAAAADs8Q8A","273":"b:f4T936WlnJ7//xMAAABIAAAAAADs8Q8A","278":"b:f4T936WlnJ7//xMAAABIAAAAAADs8Q8A","317":"b:f4T936WlnJ7//xMAAABIAAAAAADs8Q8A","336":"b:f4T936WlnJ7//xMAAABIAAAAAADs8Q8A","370":"b:f4T936WlnJ7//xMAAABIAAAAAADs8Q8A","405":"b:f4T936WlnJ7//xMAAABIAAAAAADs8Q8A","418":"b:f4T936WlnJ7//xMAAABIAAAAAADs8Q8A","422":"b:f4T936WlnJ7//xMAAABIAAAAAADs8Q8A","423":"b:f4T936WlnJ7//xMAAABIAAAAAADs8Q8A","426":"b:f4T936WlnJ7//xMAAABIAAAAAADs8Q8A","437":"b:f4T936WlnJ7//xMAAABIAAAAAADs8Q8A","444":"b:f4T936WlnJ7//xMAAABIAAAAAADs8Q8A","455":"b:f4T936WlnJ7//xMAAABIAAAAAADs8Q8A","465":"b:f4T936WlnJ7//xMAAABIAAAAAADs8Q8A","477":"b:f4T936WlnJ7//xMAAABIAAAAAADs8Q8A","557":"b:f4T936WlnJ7//xMAAABIAAAAAADs8Q8A","563":"b:f4T936WlnJ7//xMAAABIAAAAAADs8Q8A","564":"b:f4T936WlnJ7//xMAAABIAAAAAADs8Q8A","572":"b:f4T936WlnJ7//xMAAABIAAAAAADs8Q8A","583":"b:f4T936WlnJ7//xMAAABIAAAAAADs8Q8A","588":"b:f4T936WlnJ7//xMAAABIAAAAAADs8Q8A","597":"b:f4T936WlnJ7//xMAAABIAAAAAADs8Q8A","618":"b:f4T936WlnJ7//xMAAABIAAAAAADs8Q8A","625":"b:f4T936WlnJ7//xMAAABIAAAAAADs8Q8A","693":"b:f4T936WlnJ7//xMAAABIAAAAAADs8Q8A"},"packages/cli/src/controllers/module-settings.controller.ts":{"7":"b:f5Td3KWlnIz9/wMAAAAIAAAAAAAAEAAA","13":"b:f5Td3KWlnIz9/wMAAAAIAAAAAAAAEAAA"},"packages/cli/src/controllers/project.controller.ts":{"43":"b:f5Xf/L+lnsz9/wNYAAKoAQAAAAABGBAA","52":"b:f5Xf/L+lnsz9/wNYAAKoAQAAAAABGBAA","73":"b:f5Xf/L+lnsz9/wNYAAKoAQAAAAABGBAA","84":"b:f5Xf/L+lnsz9/wNYAAKoAQAAAAABGBAA","101":"b:f5Xf/L+lnsz9/wNYAAKoAQAAAAABGBAA","122":"b:f5Xf/L+lnsz9/wNYAAKoAQAAAAABGBAA","135":"b:f5Xf/L+lnsz9/wNYAAKoAQAAAAABGBAA","143":"b:f5Xf/L+lnsz9/wNYAAKoAQAAAAABGBAA","159":"b:f5Xf/L+lnsz9/wNYAAKoAQAAAAABGBAA","197":"b:f5Xf/L+lnsz9/wNYAAKoAQAAAAABGBAA","210":"b:f5Xf/L+lnsz9/wNYAAKoAQAAAAABGBAA","223":"b:f5Xf/L+lnsz9/wNYAAKoAQAAAAABGBAA","234":"b:f5Xf/L+lnsz9/wNYAAKoAQAAAAABGBAA","243":"b:f5Xf/L+lnsz9/wNYAAKoAQAAAAABGBAA","253":"b:f5Xf/L+lnsz9/wNYAAKoAQAAAAABGBAA","262":"b:f5Xf/L+lnsz9/wNYAAKoAQAAAAABGBAA","280":"b:f5Xf/L+lnsz9/wNYAAKoAQAAAAABGBAA","290":"b:f5Xf/L+lnsz9/wNYAAKoAQAAAAABGBAA","313":"b:f5Xf/L+lnsz9/wNYAAKoAQAAAAABGBAA","335":"b:f5Xf/L+lnsz9/wNYAAKoAQAAAAABGBAA","350":"b:f5Xf/L+lnsz9/wNYAAKoAQAAAAABGBAA","364":"b:f5Xf/L+lnsz9/wNYAAKoAQAAAAABGBAA","376":"b:f5Xf/L+lnsz9/wNYAAKoAQAAAAABGBAA","384":"b:f5Xf/L+lnsz9/wNYAAKoAQAAAAABGBAA"},"packages/cli/src/controllers/role.controller.ts":{"39":"b:f5Td3KWlnIz9/wMAAAAIAAAAAAAAEAAA","44":"b:f5Td3KWlnIz9/wMAAAAIAAAAAAAAEAAA","51":"b:f5Td3KWlnIz9/wMAAAAIAAAAAAAAEAAA","58":"b:f5Td3KWlnIz9/wMAAAAIAAAAAAAAEAAA","59":"b:f5Td3KWlnIz9/wMAAAAIAAAAAAAAEAAA","60":"b:f5Td3KWlnIz9/wMAAAAIAAAAAAAAEAAA","61":"b:f5Td3KWlnIz9/wMAAAAIAAAAAAAAEAAA","66":"b:f5Td3KWlnIz9/wMAAAAIAAAAAAAAEAAA","79":"b:f5Td3KWlnIz9/wMAAAAIAAAAAAAAEAAA","92":"b:f5Td3KWlnIz9/wMAAAAIAAAAAAAAEAAA","102":"b:f5Td3KWlnIz9/wMAAAAIAAAAAAAAEAAA","113":"b:f5Td3KWlnIz9/wMAAAAIAAAAAAAAEAAA","132":"b:f5Td3KWlnIz9/wMAAAAIAAAAAAAAEAAA","150":"b:f5Td3KWlnIz9/wMAAAAIAAAAAAAAEAAA"},"packages/cli/src/controllers/tags.controller.ts":{"20":"b:X5Td3KWlnIz9/wMIAAAJAAAAQIQAEAAA","24":"b:X5Td3KWlnIz9/wMIAAAJAAAAQIQAEAAA","30":"b:X5Td3KWlnIz9/wMIAAAJAAAAQIQAEAAA","43":"b:X5Td3KWlnIz9/wMIAAAJAAAAQIQAEAAA","56":"b:X5Td3KWlnIz9/wMIAAAJAAAAQIQAEAAA"},"packages/cli/src/controllers/users.controller.ts":{"55":"b:R5Td3KelnIT9/wMAAAAgAAAAAAAAAAAA","82":"b:R5Td3KelnIT9/wMAAAAgAAAAAAAAAAAA","106":"b:R5Td3KelnIT9/wMAAAAgAAAAAAAAAAAA","115":"b:R5Td3KelnIT9/wMAAAAgAAAAAAAAAAAA","128":"b:R5Td3KelnIT9/wMAAAAgAAAAAAAAAAAA","135":"b:R5Td3KelnIT9/wMAAAAgAAAAAAAAAAAA","153":"b:R5Td3KelnIT9/wMAAAAgAAAAAAAAAAAA","175":"b:R5Td3KelnIT9/wMAAAAgAAAAAAAAAAAA","203":"b:R5Td3KelnIT9/wMAAAAgAAAAAAAAAAAA","224":"b:R5Td3KelnIT9/wMAAAAgAAAAAAAAAAAA","301":"b:R5Td3KelnIT9/wMAAAAgAAAAAAAAAAAA","318":"b:R5Td3KelnIT9/wMAAAAgAAAAAAAAAAAA","341":"b:R5Td3KelnIT9/wMAAAAgAAAAAAAAAAAA"},"packages/cli/src/credentials/credentials-finder.service.ts":{"13":"b:f9f90K2n3MDdYAAAAACAAAAACAAIABAA","22":"b:f9f90K2n3MDdYAAAAACAAAAACAAIABAA","34":"b:f9f90K2n3MDdYAAAAACAAAAACAAIABAA","41":"b:f9f90K2n3MDdYAAAAACAAAAACAAIABAA","55":"b:f9f90K2n3MDdYAAAAACAAAAACAAIABAA","71":"b:f9f90K2n3MDdYAAAAACAAAAACAAIABAA","76":"b:f9f90K2n3MDdYAAAAACAAAAACAAIABAA","78":"b:f9f90K2n3MDdYAAAAACAAAAACAAIABAA","80":"b:f9f90K2n3MDdYAAAAACAAAAACAAIABAA","92":"b:f9f90K2n3MDdYAAAAACAAAAACAAIABAA","132":"b:f9f90K2n3MDdYAAAAACAAAAACAAIABAA","191":"b:f9f90K2n3MDdYAAAAACAAAAACAAIABAA","223":"b:f9f90K2n3MDdYAAAAACAAAAACAAIABAA","234":"b:f9f90K2n3MDdYAAAAACAAAAACAAIABAA","236":"b:f9f90K2n3MDdYAAAAACAAAAACAAIABAA","253":"b:f9f90K2n3MDdYAAAAACAAAAACAAIABAA","287":"b:f9f90K2n3MDdYAAAAACAAAAACAAIABAA","301":"b:f9f90K2n3MDdYAAAAACAAAAACAAIABAA","324":"b:f9f90K2n3MDdYAAAAACAAAAACAAIABAA"},"packages/cli/src/credentials/credentials.controller.ts":{"54":"b:f93f/K+n3uz94IMRsBCIASAAQAAIABAA","71":"b:f93f/K+n3uz94IMRsBCIASAAQAAIABAA","86":"b:f93f/K+n3uz94IMRsBCIASAAQAAIABAA","96":"b:f93f/K+n3uz94IMRsBCIASAAQAAIABAA","104":"b:f93f/K+n3uz94IMRsBCIASAAQAAIABAA","118":"b:f93f/K+n3uz94IMRsBCIASAAQAAIABAA","147":"b:f93f/K+n3uz94IMRsBCIASAAQAAIABAA","160":"b:f93f/K+n3uz94IMRsBCIASAAQAAIABAA","204":"b:f93f/K+n3uz94IMRsBCIASAAQAAIABAA","371":"b:f93f/K+n3uz94IMRsBCIASAAQAAIABAA","405":"b:f93f/K+n3uz94IMRsBCIASAAQAAIABAA","448":"b:f93f/K+n3uz94IMRsBCIASAAQAAIABAA","454":"b:f93f/K+n3uz94IMRsBCIASAAQAAIABAA","470":"b:f93f/K+n3uz94IMRsBCIASAAQAAIABAA","471":"b:f93f/K+n3uz94IMRsBCIASAAQAAIABAA","474":"b:f93f/K+n3uz94IMRsBCIASAAQAAIABAA","475":"b:f93f/K+n3uz94IMRsBCIASAAQAAIABAA","499":"b:f93f/K+n3uz94IMRsBCIASAAQAAIABAA","539":"b:f93f/K+n3uz94IMRsBCIASAAQAAIABAA","546":"b:f93f/K+n3uz94IMRsBCIASAAQAAIABAA"},"packages/cli/src/credentials/credentials.service.ts":{"159":"b:f93//O+33v394IMZoFSoAWAAQAAIABAC","191":"b:f93//O+33v394IMZoFSoAWAAQAAIABAC","195":"b:f93//O+33v394IMZoFSoAWAAQAAIABAC","199":"b:f93//O+33v394IMZoFSoAWAAQAAIABAC","204":"b:f93//O+33v394IMZoFSoAWAAQAAIABAC","212":"b:f93//O+33v394IMZoFSoAWAAQAAIABAC","225":"b:f93//O+33v394IMZoFSoAWAAQAAIABAC","226":"b:f93//O+33v394IMZoFSoAWAAQAAIABAC","235":"b:f93//O+33v394IMZoFSoAWAAQAAIABAC","266":"b:f93//O+33v394IMZoFSoAWAAQAAIABAC","335":"b:f93//O+33v394IMZoFSoAWAAQAAIABAC","396":"b:f93//O+33v394IMZoFSoAWAAQAAIABAC","479":"b:f93//O+33v394IMZoFSoAWAAQAAIABAC","520":"b:f93//O+33v394IMZoFSoAWAAQAAIABAC","556":"b:f93//O+33v394IMZoFSoAWAAQAAIABAC","570":"b:f93//O+33v394IMZoFSoAWAAQAAIABAC","572":"b:f93//O+33v394IMZoFSoAWAAQAAIABAC","573":"b:f93//O+33v394IMZoFSoAWAAQAAIABAC","577":"b:f93//O+33v394IMZoFSoAWAAQAAIABAC","580":"b:f93//O+33v394IMZoFSoAWAAQAAIABAC","585":"b:f93//O+33v394IMZoFSoAWAAQAAIABAC","588":"b:f93//O+33v394IMZoFSoAWAAQAAIABAC","592":"b:f93//O+33v394IMZoFSoAWAAQAAIABAC","617":"b:f93//O+33v394IMZoFSoAWAAQAAIABAC","632":"b:f93//O+33v394IMZoFSoAWAAQAAIABAC","633":"b:f93//O+33v394IMZoFSoAWAAQAAIABAC","638":"b:f93//O+33v394IMZoFSoAWAAQAAIABAC","643":"b:f93//O+33v394IMZoFSoAWAAQAAIABAC","645":"b:f93//O+33v394IMZoFSoAWAAQAAIABAC","646":"b:f93//O+33v394IMZoFSoAWAAQAAIABAC","651":"b:f93//O+33v394IMZoFSoAWAAQAAIABAC","652":"b:f93//O+33v394IMZoFSoAWAAQAAIABAC","656":"b:f93//O+33v394IMZoFSoAWAAQAAIABAC","675":"b:f93//O+33v394IMZoFSoAWAAQAAIABAC","682":"b:f93//O+33v394IMZoFSoAWAAQAAIABAC","695":"b:f93//O+33v394IMZoFSoAWAAQAAIABAC","712":"b:f93//O+33v394IMZoFSoAWAAQAAIABAC","742":"b:f93//O+33v394IMZoFSoAWAAQAAIABAC","767":"b:f93//O+33v394IMZoFSoAWAAQAAIABAC","811":"b:f93//O+33v394IMZoFSoAWAAQAAIABAC","838":"b:f93//O+33v394IMZoFSoAWAAQAAIABAC","861":"b:f93//O+33v394IMZoFSoAWAAQAAIABAC","882":"b:f93//O+33v394IMZoFSoAWAAQAAIABAC","890":"b:f93//O+33v394IMZoFSoAWAAQAAIABAC","923":"b:f93//O+33v394IMZoFSoAWAAQAAIABAC","935":"b:f93//O+33v394IMZoFSoAWAAQAAIABAC","949":"b:f93//O+33v394IMZoFSoAWAAQAAIABAC","1005":"b:f93//O+33v394IMZoFSoAWAAQAAIABAC","1022":"b:f93//O+33v394IMZoFSoAWAAQAAIABAC","1036":"b:f93//O+33v394IMZoFSoAWAAQAAIABAC","1090":"b:f93//O+33v394IMZoFSoAWAAQAAIABAC","1128":"b:f93//O+33v394IMZoFSoAWAAQAAIABAC","1132":"b:f93//O+33v394IMZoFSoAWAAQAAIABAC","1144":"b:f93//O+33v394IMZoFSoAWAAQAAIABAC","1171":"b:f93//O+33v394IMZoFSoAWAAQAAIABAC","1185":"b:f93//O+33v394IMZoFSoAWAAQAAIABAC","1199":"b:f93//O+33v394IMZoFSoAWAAQAAIABAC","1214":"b:f93//O+33v394IMZoFSoAWAAQAAIABAC","1258":"b:f93//O+33v394IMZoFSoAWAAQAAIABAC","1273":"b:f93//O+33v394IMZoFSoAWAAQAAIABAC","1274":"b:f93//O+33v394IMZoFSoAWAAQAAIABAC","1277":"b:f93//O+33v394IMZoFSoAWAAQAAIABAC","1286":"b:f93//O+33v394IMZoFSoAWAAQAAIABAC","1289":"b:f93//O+33v394IMZoFSoAWAAQAAIABAC","1302":"b:f93//O+33v394IMZoFSoAWAAQAAIABAC","1311":"b:f93//O+33v394IMZoFSoAWAAQAAIABAC","1332":"b:f93//O+33v394IMZoFSoAWAAQAAIABAC","1344":"b:f93//O+33v394IMZoFSoAWAAQAAIABAC","1361":"b:f93//O+33v394IMZoFSoAWAAQAAIABAC","1386":"b:f93//O+33v394IMZoFSoAWAAQAAIABAC","1471":"b:f93//O+33v394IMZoFSoAWAAQAAIABAC","1473":"b:f93//O+33v394IMZoFSoAWAAQAAIABAC","1492":"b:f93//O+33v394IMZoFSoAWAAQAAIABAC","1501":"b:f93//O+33v394IMZoFSoAWAAQAAIABAC","1509":"b:f93//O+33v394IMZoFSoAWAAQAAIABAC","1510":"b:f93//O+33v394IMZoFSoAWAAQAAIABAC","1521":"b:f93//O+33v394IMZoFSoAWAAQAAIABAC","1522":"b:f93//O+33v394IMZoFSoAWAAQAAIABAC","1527":"b:f93//O+33v394IMZoFSoAWAAQAAIABAC","1534":"b:f93//O+33v394IMZoFSoAWAAQAAIABAC","1542":"b:f93//O+33v394IMZoFSoAWAAQAAIABAC","1563":"b:f93//O+33v394IMZoFSoAWAAQAAIABAC","1567":"b:f93//O+33v394IMZoFSoAWAAQAAIABAC","1584":"b:f93//O+33v394IMZoFSoAWAAQAAIABAC","1609":"b:f93//O+33v394IMZoFSoAWAAQAAIABAC","1619":"b:f93//O+33v394IMZoFSoAWAAQAAIABAC","1651":"b:f93//O+33v394IMZoFSoAWAAQAAIABAC","1665":"b:f93//O+33v394IMZoFSoAWAAQAAIABAC","1693":"b:f93//O+33v394IMZoFSoAWAAQAAIABAC","1705":"b:f93//O+33v394IMZoFSoAWAAQAAIABAC","1716":"b:f93//O+33v394IMZoFSoAWAAQAAIABAC","1782":"b:f93//O+33v394IMZoFSoAWAAQAAIABAC","1840":"b:f93//O+33v394IMZoFSoAWAAQAAIABAC","1872":"b:f93//O+33v394IMZoFSoAWAAQAAIABAC","1881":"b:f93//O+33v394IMZoFSoAWAAQAAIABAC","1911":"b:f93//O+33v394IMZoFSoAWAAQAAIABAC"},"packages/cli/src/environments.ee/variables/variables.service.ee.ts":{"31":"b:f5Dl0yW3uBsTAAAAAAAEAAAAAADsAAAA","39":"b:f5Dl0yW3uBsTAAAAAAAEAAAAAADsAAAA","46":"b:f5Dl0yW3uBsTAAAAAAAEAAAAAADsAAAA","69":"b:f5Dl0yW3uBsTAAAAAAAEAAAAAADsAAAA","74":"b:f5Dl0yW3uBsTAAAAAAAEAAAAAADsAAAA","78":"b:f5Dl0yW3uBsTAAAAAAAEAAAAAADsAAAA","84":"b:f5Dl0yW3uBsTAAAAAAAEAAAAAADsAAAA","86":"b:f5Dl0yW3uBsTAAAAAAAEAAAAAADsAAAA","93":"b:f5Dl0yW3uBsTAAAAAAAEAAAAAADsAAAA","99":"b:f5Dl0yW3uBsTAAAAAAAEAAAAAADsAAAA","109":"b:f5Dl0yW3uBsTAAAAAAAEAAAAAADsAAAA","116":"b:f5Dl0yW3uBsTAAAAAAAEAAAAAADsAAAA","124":"b:f5Dl0yW3uBsTAAAAAAAEAAAAAADsAAAA","128":"b:f5Dl0yW3uBsTAAAAAAAEAAAAAADsAAAA","146":"b:f5Dl0yW3uBsTAAAAAAAEAAAAAADsAAAA","174":"b:f5Dl0yW3uBsTAAAAAAAEAAAAAADsAAAA","179":"b:f5Dl0yW3uBsTAAAAAAAEAAAAAADsAAAA","184":"b:f5Dl0yW3uBsTAAAAAAAEAAAAAADsAAAA","202":"b:f5Dl0yW3uBsTAAAAAAAEAAAAAADsAAAA","204":"b:f5Dl0yW3uBsTAAAAAAAEAAAAAADsAAAA","207":"b:f5Dl0yW3uBsTAAAAAAAEAAAAAADsAAAA","214":"b:f5Dl0yW3uBsTAAAAAAAEAAAAAADsAAAA","221":"b:f5Dl0yW3uBsTAAAAAAAEAAAAAADsAAAA","263":"b:f5Dl0yW3uBsTAAAAAAAEAAAAAADsAAAA"},"packages/cli/src/eventbus/event-message-classes/abstract-event-message.ts":{"10":"b:f4T9z6GhHLqT/xMAAAAAAAAAAADs8A8A","12":"b:f4T9z6GhHLqT/xMAAAAAAAAAAADs8A8A","16":"b:f4T9z6GhHLqT/xMAAAAAAAAAAADs8A8A","28":"b:f4T9z6GhHLqT/xMAAAAAAAAAAADs8A8A","48":"b:f4T9z6GhHLqT/xMAAAAAAAAAAADs8A8A","59":"b:f4T9z6GhHLqT/xMAAAAAAAAAAADs8A8A","72":"b:f4T9z6GhHLqT/xMAAAAAAAAAAADs8A8A","101":"b:f4T9z6GhHLqT/xMAAAAAAAAAAADs8A8A","108":"b:f4T9z6GhHLqT/xMAAAAAAAAAAADs8A8A","113":"b:f4T9z6GhHLqT/xMAAAAAAAAAAADs8A8A","124":"b:f4T9z6GhHLqT/xMAAAAAAAAAAADs8A8A","135":"b:f4T9z6GhHLqT/xMAAAAAAAAAAADs8A8A","139":"b:f4T9z6GhHLqT/xMAAAAAAAAAAADs8A8A"},"packages/cli/src/eventbus/event-message-classes/event-message-ai-node.ts":{"34":[0,1,2,3,4,5,6],"42":[0,1,2,3,4,5,6],"47":[0,1,2,3,4,5,6]},"packages/cli/src/eventbus/event-message-classes/event-message-confirm.ts":{"19":"b:f4T9z6GhHJqT/xMAAAAAAAAAAADs8A8A","25":"b:f4T9z6GhHJqT/xMAAAAAAAAAAADs8A8A","36":"b:f4T9z6GhHJqT/xMAAAAAAAAAAADs8A8A"},"packages/cli/src/eventbus/event-message-classes/event-message-node.ts":{"35":"b:f5DlwyX3sBsDAAAAAAAAAAAAAADkAwAA","43":"b:f5DlwyX3sBsDAAAAAAAAAAAAAADkAwAA","48":"b:f5DlwyX3sBsDAAAAAAAAAAAAAADkAwAA"},"packages/cli/src/eventbus/event-message-classes/event-message-runner.ts":{"24":"b:RQohAAFQAAABAAAAAAAAAICAAUAAAQAA","32":"b:RQohAAFQAAABAAAAAAAAAICAAUAAAQAA","37":"b:RQohAAFQAAABAAAAAAAAAICAAUAAAQAA"},"packages/cli/src/eventbus/event-message-classes/event-message-workflow.ts":{"36":"b:f5DlwyX3sBsDAAAAAAAAAAAAAADkAwAA","44":"b:f5DlwyX3sBsDAAAAAAAAAAAAAADkAwAA","49":"b:f5DlwyX3sBsDAAAAAAAAAAAAAADkAwAA"},"packages/cli/src/eventbus/message-event-bus/message-event-bus.ts":{"58":"b:f5rlwyX3sBsDAAAAAAAAAICAAUDkAwAA","77":"b:f5rlwyX3sBsDAAAAAAAAAICAAUDkAwAA","117":"b:f5rlwyX3sBsDAAAAAAAAAICAAUDkAwAA","126":"b:f5rlwyX3sBsDAAAAAAAAAICAAUDkAwAA","137":"b:f5rlwyX3sBsDAAAAAAAAAICAAUDkAwAA","140":"b:f5rlwyX3sBsDAAAAAAAAAICAAUDkAwAA","150":"b:f5rlwyX3sBsDAAAAAAAAAICAAUDkAwAA","161":"b:f5rlwyX3sBsDAAAAAAAAAICAAUDkAwAA","168":"b:f5rlwyX3sBsDAAAAAAAAAICAAUDkAwAA","191":"b:f5rlwyX3sBsDAAAAAAAAAICAAUDkAwAA","195":"b:f5rlwyX3sBsDAAAAAAAAAICAAUDkAwAA","196":"b:f5rlwyX3sBsDAAAAAAAAAICAAUDkAwAA","201":"b:f5rlwyX3sBsDAAAAAAAAAICAAUDkAwAA","207":"b:f5rlwyX3sBsDAAAAAAAAAICAAUDkAwAA","213":"b:f5rlwyX3sBsDAAAAAAAAAICAAUDkAwAA","219":"b:f5rlwyX3sBsDAAAAAAAAAICAAUDkAwAA","224":"b:f5rlwyX3sBsDAAAAAAAAAICAAUDkAwAA","238":"b:f5rlwyX3sBsDAAAAAAAAAICAAUDkAwAA","249":"b:f5rlwyX3sBsDAAAAAAAAAICAAUDkAwAA","253":"b:f5rlwyX3sBsDAAAAAAAAAICAAUDkAwAA","257":"b:f5rlwyX3sBsDAAAAAAAAAICAAUDkAwAA","261":"b:f5rlwyX3sBsDAAAAAAAAAICAAUDkAwAA","265":"b:f5rlwyX3sBsDAAAAAAAAAICAAUDkAwAA","269":"b:f5rlwyX3sBsDAAAAAAAAAICAAUDkAwAA","273":"b:f5rlwyX3sBsDAAAAAAAAAICAAUDkAwAA","284":"b:f5rlwyX3sBsDAAAAAAAAAICAAUDkAwAA","350":"b:f5rlwyX3sBsDAAAAAAAAAICAAUDkAwAA","369":"b:f5rlwyX3sBsDAAAAAAAAAICAAUDkAwAA","388":"b:f5rlwyX3sBsDAAAAAAAAAICAAUDkAwAA"},"packages/cli/src/eventbus/message-event-bus-writer/message-event-bus-log-writer.ts":{"67":"b:f4T9z6GhHJqT/xMAAAAAAAAAAADs8A8A","72":"b:f4T9z6GhHJqT/xMAAAAAAAAAAADs8A8A","82":"b:f4T9z6GhHJqT/xMAAAAAAAAAAADs8A8A","110":"b:f4T9z6GhHJqT/xMAAAAAAAAAAADs8A8A","116":"b:f4T9z6GhHJqT/xMAAAAAAAAAAADs8A8A","122":"b:f4T9z6GhHJqT/xMAAAAAAAAAAADs8A8A","126":"b:f4T9z6GhHJqT/xMAAAAAAAAAAADs8A8A","132":"b:f4T9z6GhHJqT/xMAAAAAAAAAAADs8A8A","142":"b:f4T9z6GhHJqT/xMAAAAAAAAAAADs8A8A","153":"b:f4T9z6GhHJqT/xMAAAAAAAAAAADs8A8A","162":"b:f4T9z6GhHJqT/xMAAAAAAAAAAADs8A8A","169":"b:f4T9z6GhHJqT/xMAAAAAAAAAAADs8A8A","175":"b:f4T9z6GhHJqT/xMAAAAAAAAAAADs8A8A","184":"b:f4T9z6GhHJqT/xMAAAAAAAAAAADs8A8A","205":"b:f4T9z6GhHJqT/xMAAAAAAAAAAADs8A8A","213":"b:f4T9z6GhHJqT/xMAAAAAAAAAAADs8A8A","241":"b:f4T9z6GhHJqT/xMAAAAAAAAAAADs8A8A","291":"b:f4T9z6GhHJqT/xMAAAAAAAAAAADs8A8A","326":"b:f4T9z6GhHJqT/xMAAAAAAAAAAADs8A8A","333":"b:f4T9z6GhHJqT/xMAAAAAAAAAAADs8A8A","341":"b:f4T9z6GhHJqT/xMAAAAAAAAAAADs8A8A","345":"b:f4T9z6GhHJqT/xMAAAAAAAAAAADs8A8A","353":"b:f4T9z6GhHJqT/xMAAAAAAAAAAADs8A8A","366":"b:f4T9z6GhHJqT/xMAAAAAAAAAAADs8A8A","369":"b:f4T9z6GhHJqT/xMAAAAAAAAAAADs8A8A","380":"b:f4T9z6GhHJqT/xMAAAAAAAAAAADs8A8A","406":"b:f4T9z6GhHJqT/xMAAAAAAAAAAADs8A8A","410":"b:f4T9z6GhHJqT/xMAAAAAAAAAAADs8A8A","414":"b:f4T9z6GhHJqT/xMAAAAAAAAAAADs8A8A","418":"b:f4T9z6GhHJqT/xMAAAAAAAAAAADs8A8A","422":"b:f4T9z6GhHJqT/xMAAAAAAAAAAADs8A8A","433":"b:f4T9z6GhHJqT/xMAAAAAAAAAAADs8A8A"},"packages/cli/src/events/relays/event-relay.ts":{"8":"b:f4T9z6GhHJqT/xMAAAAAAAAAAADs8A8A","10":"b:f4T9z6GhHJqT/xMAAAAAAAAAAADs8A8A","18":"b:f4T9z6GhHJqT/xMAAAAAAAAAAADs8A8A"},"packages/cli/src/events/relays/log-streaming.event-relay.ts":{"15":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","19":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","34":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","42":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","44":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","45":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","46":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","47":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","48":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","49":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","50":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","51":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","52":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","53":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","54":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","55":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","56":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","57":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","58":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","59":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","60":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","61":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","62":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","63":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","64":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","65":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","66":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","67":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","68":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","69":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","70":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","71":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","72":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","73":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","74":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","75":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","76":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","77":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","78":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","79":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","80":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","81":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","82":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","83":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","85":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","86":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","87":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","88":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","90":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","91":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","93":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","95":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","97":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","98":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","100":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","101":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","102":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","103":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","104":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","105":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","106":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","107":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","108":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","109":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","110":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","111":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","112":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","113":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","114":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","115":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","116":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","117":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","118":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","119":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","120":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","121":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","122":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","123":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","124":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","125":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","126":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","127":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","128":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","129":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","130":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","131":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","132":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","133":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","134":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","135":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","136":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","137":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","138":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","139":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","140":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","141":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","142":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","143":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","144":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","151":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","159":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","167":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","175":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","183":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","195":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","203":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","211":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","219":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","232":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","250":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","263":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","284":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","320":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","384":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","407":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","428":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","440":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","460":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","485":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","493":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","501":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","509":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","517":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","525":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","537":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","545":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","552":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","567":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","575":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","583":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","597":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","605":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","613":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","625":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","637":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","645":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","653":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","664":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","672":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","679":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","688":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","702":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","710":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","718":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","729":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","738":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","747":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","756":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","765":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","774":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","783":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","797":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","808":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","816":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","827":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","834":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","843":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","861":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","876":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","886":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","904":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","919":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","946":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","955":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","962":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","969":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","976":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","983":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","990":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","997":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","1004":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","1011":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","1018":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","1025":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","1032":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","1039":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","1050":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","1057":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","1068":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","1075":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","1082":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","1094":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","1137":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","1152":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","1159":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","1166":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","1173":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","1180":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","1187":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","1194":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","1201":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","1212":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","1230":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","1245":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","1259":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B","1272":"b:f9/9/+///N/f/xc0oBa0C8CSBWr9+/8B"},"packages/cli/src/execution-lifecycle/execution-lifecycle-hooks.ts":{"54":"b:f5DlwyX/sBsDAAAAIAAAAMBCgCDkA6AA","62":"b:f5DlwyX/sBsDAAAAIAAAAMBCgCDkA6AA","77":"b:f5DlwyX/sBsDAAAAIAAAAMBCgCDkA6AA","91":"b:f5DlwyX/sBsDAAAAIAAAAMBCgCDkA6AA","106":"b:f5DlwyX/sBsDAAAAIAAAAMBCgCDkA6AA","120":"b:f5DlwyX/sBsDAAAAIAAAAMBCgCDkA6AA","145":"b:f5DlwyX/sBsDAAAAIAAAAMBCgCDkA6AA","154":"b:f5DlwyX/sBsDAAAAIAAAAMBCgCDkA6AA","164":"b:f5DlwyX/sBsDAAAAIAAAAMBCgCDkA6AA","206":"b:f5DlwyX/sBsDAAAAIAAAAMBCgCDkA6AA","208":"b:f5DlwyX/sBsDAAAAIAAAAMBCgCDkA6AA","210":"b:f5DlwyX/sBsDAAAAIAAAAMBCgCDkA6AA","220":"b:f5DlwyX/sBsDAAAAIAAAAMBCgCDkA6AA","222":"b:f5DlwyX/sBsDAAAAIAAAAMBCgCDkA6AA","238":"b:f5DlwyX/sBsDAAAAIAAAAMBCgCDkA6AA","261":"b:f5DlwyX/sBsDAAAAIAAAAMBCgCDkA6AA","275":"b:f5DlwyX/sBsDAAAAIAAAAMBCgCDkA6AA","292":"b:f5DlwyX/sBsDAAAAIAAAAMBCgCDkA6AA","310":"b:f5DlwyX/sBsDAAAAIAAAAMBCgCDkA6AA","388":"b:f5DlwyX/sBsDAAAAIAAAAMBCgCDkA6AA","448":"b:f5DlwyX/sBsDAAAAIAAAAMBCgCDkA6AA","469":"b:f5DlwyX/sBsDAAAAIAAAAMBCgCDkA6AA","472":"b:f5DlwyX/sBsDAAAAIAAAAMBCgCDkA6AA","475":"b:f5DlwyX/sBsDAAAAIAAAAMBCgCDkA6AA","484":"b:f5DlwyX/sBsDAAAAIAAAAMBCgCDkA6AA","489":"b:f5DlwyX/sBsDAAAAIAAAAMBCgCDkA6AA","501":"b:f5DlwyX/sBsDAAAAIAAAAMBCgCDkA6AA","502":"b:f5DlwyX/sBsDAAAAIAAAAMBCgCDkA6AA","507":"b:f5DlwyX/sBsDAAAAIAAAAMBCgCDkA6AA","512":"b:f5DlwyX/sBsDAAAAIAAAAMBCgCDkA6AA","523":"b:f5DlwyX/sBsDAAAAIAAAAMBCgCDkA6AA","542":"b:f5DlwyX/sBsDAAAAIAAAAMBCgCDkA6AA","553":"b:f5DlwyX/sBsDAAAAIAAAAMBCgCDkA6AA","671":"b:f5DlwyX/sBsDAAAAIAAAAMBCgCDkA6AA","679":"b:f5DlwyX/sBsDAAAAIAAAAMBCgCDkA6AA","758":"b:f5DlwyX/sBsDAAAAIAAAAMBCgCDkA6AA","783":"b:f5DlwyX/sBsDAAAAIAAAAMBCgCDkA6AA","815":"b:f5DlwyX/sBsDAAAAIAAAAMBCgCDkA6AA","846":"b:f5DlwyX/sBsDAAAAIAAAAMBCgCDkA6AA","903":"b:f5DlwyX/sBsDAAAAIAAAAMBCgCDkA6AA"},"packages/cli/src/execution-lifecycle/restore-binary-data-id.ts":{"30":"b:f5jlwyf3sBsDAAAAAAAAAAAAAAD0A6AA","64":"b:f5jlwyf3sBsDAAAAAAAAAAAAAAD0A6AA","78":"b:f5jlwyf3sBsDAAAAAAAAAAAAAAD0A6AA"},"packages/cli/src/execution-lifecycle/shared/shared-hook-functions.ts":{"13":"b:f5DlwyX3sBsDAAAAAAAAAAAAAADkAwAA","29":"b:f5DlwyX3sBsDAAAAAAAAAAAAAADkAwAA","78":"b:f5DlwyX3sBsDAAAAAAAAAAAAAADkAwAA","94":"b:f5DlwyX3sBsDAAAAAAAAAAAAAADkAwAA"},"packages/cli/src/execution-lifecycle/to-save-settings.ts":{"20":"b:f5DlwyX3sBsDAAAAAAAAAAAAAADkAwAA"},"packages/cli/src/executions/execution-data/db-store.ts":{"14":"b:f5DlwyX3sBsDAAAAACAAIIABAADkAwAA","16":"b:f5DlwyX3sBsDAAAAACAAIIABAADkAwAA","32":"b:f5DlwyX3sBsDAAAAACAAIIABAADkAwAA","50":"b:f5DlwyX3sBsDAAAAACAAIIABAADkAwAA","58":"b:f5DlwyX3sBsDAAAAACAAIIABAADkAwAA","71":"b:f5DlwyX3sBsDAAAAACAAIIABAADkAwAA","82":"b:f5DlwyX3sBsDAAAAACAAIIABAADkAwAA","96":"b:f5DlwyX3sBsDAAAAACAAIIABAADkAwAA","100":"b:f5DlwyX3sBsDAAAAACAAIIABAADkAwAA"},"packages/cli/src/executions/execution-persistence.ts":{"76":"b:f5Dlwyf3sBsLAAQAICAAKIABACD1A6AA","93":"b:f5Dlwyf3sBsLAAQAICAAKIABACD1A6AA","110":"b:f5Dlwyf3sBsLAAQAICAAKIABACD1A6AA","116":"b:f5Dlwyf3sBsLAAQAICAAKIABACD1A6AA","170":"b:f5Dlwyf3sBsLAAQAICAAKIABACD1A6AA","207":"b:f5Dlwyf3sBsLAAQAICAAKIABACD1A6AA","227":"b:f5Dlwyf3sBsLAAQAICAAKIABACD1A6AA","299":"b:f5Dlwyf3sBsLAAQAICAAKIABACD1A6AA","397":"b:f5Dlwyf3sBsLAAQAICAAKIABACD1A6AA","451":"b:f5Dlwyf3sBsLAAQAICAAKIABACD1A6AA","452":"b:f5Dlwyf3sBsLAAQAICAAKIABACD1A6AA","460":"b:f5Dlwyf3sBsLAAQAICAAKIABACD1A6AA","461":"b:f5Dlwyf3sBsLAAQAICAAKIABACD1A6AA","466":"b:f5Dlwyf3sBsLAAQAICAAKIABACD1A6AA","475":"b:f5Dlwyf3sBsLAAQAICAAKIABACD1A6AA","506":"b:f5Dlwyf3sBsLAAQAICAAKIABACD1A6AA","507":"b:f5Dlwyf3sBsLAAQAICAAKIABACD1A6AA","514":"b:f5Dlwyf3sBsLAAQAICAAKIABACD1A6AA","524":"b:f5Dlwyf3sBsLAAQAICAAKIABACD1A6AA","539":"b:f5Dlwyf3sBsLAAQAICAAKIABACD1A6AA","554":"b:f5Dlwyf3sBsLAAQAICAAKIABACD1A6AA","595":"b:f5Dlwyf3sBsLAAQAICAAKIABACD1A6AA","605":"b:f5Dlwyf3sBsLAAQAICAAKIABACD1A6AA","610":"b:f5Dlwyf3sBsLAAQAICAAKIABACD1A6AA","611":"b:f5Dlwyf3sBsLAAQAICAAKIABACD1A6AA","616":"b:f5Dlwyf3sBsLAAQAICAAKIABACD1A6AA","632":"b:f5Dlwyf3sBsLAAQAICAAKIABACD1A6AA","646":"b:f5Dlwyf3sBsLAAQAICAAKIABACD1A6AA","676":"b:f5Dlwyf3sBsLAAQAICAAKIABACD1A6AA","690":"b:f5Dlwyf3sBsLAAQAICAAKIABACD1A6AA","709":"b:f5Dlwyf3sBsLAAQAICAAKIABACD1A6AA","710":"b:f5Dlwyf3sBsLAAQAICAAKIABACD1A6AA","713":"b:f5Dlwyf3sBsLAAQAICAAKIABACD1A6AA","726":"b:f5Dlwyf3sBsLAAQAICAAKIABACD1A6AA","736":"b:f5Dlwyf3sBsLAAQAICAAKIABACD1A6AA","764":"b:f5Dlwyf3sBsLAAQAICAAKIABACD1A6AA","786":"b:f5Dlwyf3sBsLAAQAICAAKIABACD1A6AA","789":"b:f5Dlwyf3sBsLAAQAICAAKIABACD1A6AA","827":"b:f5Dlwyf3sBsLAAQAICAAKIABACD1A6AA","846":"b:f5Dlwyf3sBsLAAQAICAAKIABACD1A6AA","864":"b:f5Dlwyf3sBsLAAQAICAAKIABACD1A6AA","890":"b:f5Dlwyf3sBsLAAQAICAAKIABACD1A6AA","914":"b:f5Dlwyf3sBsLAAQAICAAKIABACD1A6AA","926":"b:f5Dlwyf3sBsLAAQAICAAKIABACD1A6AA","936":"b:f5Dlwyf3sBsLAAQAICAAKIABACD1A6AA","943":"b:f5Dlwyf3sBsLAAQAICAAKIABACD1A6AA","964":"b:f5Dlwyf3sBsLAAQAICAAKIABACD1A6AA","977":"b:f5Dlwyf3sBsLAAQAICAAKIABACD1A6AA","982":"b:f5Dlwyf3sBsLAAQAICAAKIABACD1A6AA","991":"b:f5Dlwyf3sBsLAAQAICAAKIABACD1A6AA","1007":"b:f5Dlwyf3sBsLAAQAICAAKIABACD1A6AA","1027":"b:f5Dlwyf3sBsLAAQAICAAKIABACD1A6AA","1040":"b:f5Dlwyf3sBsLAAQAICAAKIABACD1A6AA","1054":"b:f5Dlwyf3sBsLAAQAICAAKIABACD1A6AA","1066":"b:f5Dlwyf3sBsLAAQAICAAKIABACD1A6AA","1082":"b:f5Dlwyf3sBsLAAQAICAAKIABACD1A6AA","1098":"b:f5Dlwyf3sBsLAAQAICAAKIABACD1A6AA","1104":"b:f5Dlwyf3sBsLAAQAICAAKIABACD1A6AA","1114":"b:f5Dlwyf3sBsLAAQAICAAKIABACD1A6AA"},"packages/cli/src/executions/execution-redaction-proxy.service.ts":{"13":"b:R5DlwyX3sAMBAAAAAAAAAAAAAAAAAAAA","17":"b:R5DlwyX3sAMBAAAAAAAAAAAAAAAAAAAA","27":"b:R5DlwyX3sAMBAAAAAAAAAAAAAAAAAAAA"},"packages/cli/src/executions/execution.service.ee.ts":{"13":"b:R5DlwCX/sAEBAAAAAAAAAAAAAAAAAAAA","19":"b:R5DlwCX/sAEBAAAAAAAAAAAAAAAAAAAA"},"packages/cli/src/executions/execution.service.ts":{"115":"b:R5DlwyX3+ZNfAAAAACQAAOBKADHgCuAA","143":"b:R5DlwyX3+ZNfAAAAACQAAOBKADHgCuAA","151":"b:R5DlwyX3+ZNfAAAAACQAAOBKADHgCuAA","205":"b:R5DlwyX3+ZNfAAAAACQAAOBKADHgCuAA","237":"b:R5DlwyX3+ZNfAAAAACQAAOBKADHgCuAA","403":"b:R5DlwyX3+ZNfAAAAACQAAOBKADHgCuAA","408":"b:R5DlwyX3+ZNfAAAAACQAAOBKADHgCuAA","442":"b:R5DlwyX3+ZNfAAAAACQAAOBKADHgCuAA","479":"b:R5DlwyX3+ZNfAAAAACQAAOBKADHgCuAA","499":"b:R5DlwyX3+ZNfAAAAACQAAOBKADHgCuAA","502":"b:R5DlwyX3+ZNfAAAAACQAAOBKADHgCuAA","538":"b:R5DlwyX3+ZNfAAAAACQAAOBKADHgCuAA","546":"b:R5DlwyX3+ZNfAAAAACQAAOBKADHgCuAA","563":"b:R5DlwyX3+ZNfAAAAACQAAOBKADHgCuAA","580":"b:R5DlwyX3+ZNfAAAAACQAAOBKADHgCuAA","591":"b:R5DlwyX3+ZNfAAAAACQAAOBKADHgCuAA","624":"b:R5DlwyX3+ZNfAAAAACQAAOBKADHgCuAA","645":"b:R5DlwyX3+ZNfAAAAACQAAOBKADHgCuAA","655":"b:R5DlwyX3+ZNfAAAAACQAAOBKADHgCuAA","675":"b:R5DlwyX3+ZNfAAAAACQAAOBKADHgCuAA","697":"b:R5DlwyX3+ZNfAAAAACQAAOBKADHgCuAA","715":"b:R5DlwyX3+ZNfAAAAACQAAOBKADHgCuAA","716":"b:R5DlwyX3+ZNfAAAAACQAAOBKADHgCuAA","727":"b:R5DlwyX3+ZNfAAAAACQAAOBKADHgCuAA","765":"b:R5DlwyX3+ZNfAAAAACQAAOBKADHgCuAA","777":"b:R5DlwyX3+ZNfAAAAACQAAOBKADHgCuAA"},"packages/cli/src/executions/executions.controller.ts":{"19":"b:R5DlwyX3sBMDAAAAAAAAAMAAADDgCuAA","26":"b:R5DlwyX3sBMDAAAAAAAAAMAAADDgCuAA","31":"b:R5DlwyX3sBMDAAAAAAAAAMAAADDgCuAA","75":"b:R5DlwyX3sBMDAAAAAAAAAMAAADDgCuAA","86":"b:R5DlwyX3sBMDAAAAAAAAAMAAADDgCuAA","101":"b:R5DlwyX3sBMDAAAAAAAAAMAAADDgCuAA","117":"b:R5DlwyX3sBMDAAAAAAAAAMAAADDgCuAA","128":"b:R5DlwyX3sBMDAAAAAAAAAMAAADDgCuAA","137":"b:R5DlwyX3sBMDAAAAAAAAAMAAADDgCuAA","146":"b:R5DlwyX3sBMDAAAAAAAAAMAAADDgCuAA"},"packages/cli/src/executions/pre-execution-checks/credentials-permission-checker.ts":{"16":"b:f5Dlwyf3sBsDAAAAoAAAAAAAgADkAwAA","25":"b:f5Dlwyf3sBsDAAAAoAAAAAAAgADkAwAA","36":"b:f5Dlwyf3sBsDAAAAoAAAAAAAgADkAwAA","46":"b:f5Dlwyf3sBsDAAAAoAAAAAAAgADkAwAA","63":"b:f5Dlwyf3sBsDAAAAoAAAAAAAgADkAwAA","90":"b:f5Dlwyf3sBsDAAAAoAAAAAAAgADkAwAA","102":"b:f5Dlwyf3sBsDAAAAoAAAAAAAgADkAwAA","141":"b:f5Dlwyf3sBsDAAAAoAAAAAAAgADkAwAA","157":"b:f5Dlwyf3sBsDAAAAoAAAAAAAgADkAwAA","171":"b:f5Dlwyf3sBsDAAAAoAAAAAAAgADkAwAA","172":"b:f5Dlwyf3sBsDAAAAoAAAAAAAgADkAwAA","199":"b:f5Dlwyf3sBsDAAAAoAAAAAAAgADkAwAA"},"packages/cli/src/executions/sum-binary-data-bytes.ts":{"13":"b:f5DlwyX3sBsDAAAAAAAAAAAAAADkAwAA","37":"b:f5DlwyX3sBsDAAAAAAAAAAAAAADkAwAA","64":"b:f5DlwyX3sBsDAAAAAAAAAAAAAADkAwAA"},"packages/cli/src/expression-observability/expression-observability.provider.ts":{"46":"b:f5jl0yW3sBsTAAAAgAAACADIAQDsAgAA","49":"b:f5jl0yW3sBsTAAAAgAAACADIAQDsAgAA","70":"b:f5jl0yW3sBsTAAAAgAAACADIAQDsAgAA","71":"b:f5jl0yW3sBsTAAAAgAAACADIAQDsAgAA","72":"b:f5jl0yW3sBsTAAAAgAAACADIAQDsAgAA","76":"b:f5jl0yW3sBsTAAAAgAAACADIAQDsAgAA","80":"b:f5jl0yW3sBsTAAAAgAAACADIAQDsAgAA","81":"b:f5jl0yW3sBsTAAAAgAAACADIAQDsAgAA","82":"b:f5jl0yW3sBsTAAAAgAAACADIAQDsAgAA","83":"b:f5jl0yW3sBsTAAAAgAAACADIAQDsAgAA","87":"b:f5jl0yW3sBsTAAAAgAAACADIAQDsAgAA","119":"b:f5jl0yW3sBsTAAAAgAAACADIAQDsAgAA","126":"b:f5jl0yW3sBsTAAAAgAAACADIAQDsAgAA","134":"b:f5jl0yW3sBsTAAAAgAAACADIAQDsAgAA","142":"b:f5jl0yW3sBsTAAAAgAAACADIAQDsAgAA","152":"b:f5jl0yW3sBsTAAAAgAAACADIAQDsAgAA","183":"b:f5jl0yW3sBsTAAAAgAAACADIAQDsAgAA","191":"b:f5jl0yW3sBsTAAAAgAAACADIAQDsAgAA","200":"b:f5jl0yW3sBsTAAAAgAAACADIAQDsAgAA","204":"b:f5jl0yW3sBsTAAAAgAAACADIAQDsAgAA","208":"b:f5jl0yW3sBsTAAAAgAAACADIAQDsAgAA","209":"b:f5jl0yW3sBsTAAAAgAAACADIAQDsAgAA","213":"b:f5jl0yW3sBsTAAAAgAAACADIAQDsAgAA"},"packages/cli/src/external-hooks.ts":{"51":"b:R5T906e33JoXAAAAAAAAAAAAAADsAAEA","146":"b:R5T906e33JoXAAAAAAAAAAAAAADsAAEA","163":"b:R5T906e33JoXAAAAAAAAAAAAAADsAAEA","183":"b:R5T906e33JoXAAAAAAAAAAAAAADsAAEA","194":"b:R5T906e33JoXAAAAAAAAAAAAAADsAAEA","198":"b:R5T906e33JoXAAAAAAAAAAAAAADsAAEA"},"packages/cli/src/generic-helpers.ts":{"13":"b:R5T906e33JoXAAAQAAAAAAAAAADsAAEA","25":"b:R5T906e33JoXAAAQAAAAAAAAAADsAAEA"},"packages/cli/src/license/license.controller.ts":{"15":"b:R4Td3KWnnID94BMAAAAAAAAAAAAAAAAA","22":"b:R4Td3KWnnID94BMAAAAAAAAAAAAAAAAA","28":"b:R4Td3KWnnID94BMAAAAAAAAAAAAAAAAA","45":"b:R4Td3KWnnID94BMAAAAAAAAAAAAAAAAA","61":"b:R4Td3KWnnID94BMAAAAAAAAAAAAAAAAA","73":"b:R4Td3KWnnID94BMAAAAAAAAAAAAAAAAA","78":"b:R4Td3KWnnID94BMAAAAAAAAAAAAAAAAA"},"packages/cli/src/license/license.service.ts":{"30":"b:R4Td3KWnnID94BMAAAAAAAAAAAAAAAAA","45":"b:R4Td3KWnnID94BMAAAAAAAAAAAAAAAAA","70":"b:R4Td3KWnnID94BMAAAAAAAAAAAAAAAAA","85":"b:R4Td3KWnnID94BMAAAAAAAAAAAAAAAAA","128":"b:R4Td3KWnnID94BMAAAAAAAAAAAAAAAAA","136":"b:R4Td3KWnnID94BMAAAAAAAAAAAAAAAAA","162":"b:R4Td3KWnnID94BMAAAAAAAAAAAAAAAAA","180":"b:R4Td3KWnnID94BMAAAAAAAAAAAAAAAAA","195":"b:R4Td3KWnnID94BMAAAAAAAAAAAAAAAAA","209":"b:R4Td3KWnnID94BMAAAAAAAAAAAAAAAAA"},"packages/cli/src/license.ts":{"48":"b:f4T936WnnJr//xMAAAAAAAAAAADs8A8A","58":"b:f4T936WnnJr//xMAAAAAAAAAAADs8A8A","77":"b:f4T936WnnJr//xMAAAAAAAAAAADs8A8A","78":"b:f4T936WnnJr//xMAAAAAAAAAAADs8A8A","80":"b:f4T936WnnJr//xMAAAAAAAAAAADs8A8A","81":"b:f4T936WnnJr//xMAAAAAAAAAAADs8A8A","83":"b:f4T936WnnJr//xMAAAAAAAAAAADs8A8A","84":"b:f4T936WnnJr//xMAAAAAAAAAAADs8A8A","86":"b:f4T936WnnJr//xMAAAAAAAAAAADs8A8A","87":"b:f4T936WnnJr//xMAAAAAAAAAAADs8A8A","89":"b:f4T936WnnJr//xMAAAAAAAAAAADs8A8A","90":"b:f4T936WnnJr//xMAAAAAAAAAAADs8A8A","91":"b:f4T936WnnJr//xMAAAAAAAAAAADs8A8A","115":"b:f4T936WnnJr//xMAAAAAAAAAAADs8A8A","117":"b:f4T936WnnJr//xMAAAAAAAAAAADs8A8A","143":"b:f4T936WnnJr//xMAAAAAAAAAAADs8A8A","157":"b:f4T936WnnJr//xMAAAAAAAAAAADs8A8A","172":"b:f4T936WnnJr//xMAAAAAAAAAAADs8A8A","177":"b:f4T936WnnJr//xMAAAAAAAAAAADs8A8A","182":"b:f4T936WnnJr//xMAAAAAAAAAAADs8A8A","189":"b:f4T936WnnJr//xMAAAAAAAAAAADs8A8A","206":"b:f4T936WnnJr//xMAAAAAAAAAAADs8A8A","208":"b:f4T936WnnJr//xMAAAAAAAAAAADs8A8A","216":"b:f4T936WnnJr//xMAAAAAAAAAAADs8A8A","229":"b:f4T936WnnJr//xMAAAAAAAAAAADs8A8A","239":"b:f4T936WnnJr//xMAAAAAAAAAAADs8A8A","248":"b:f4T936WnnJr//xMAAAAAAAAAAADs8A8A","257":"b:f4T936WnnJr//xMAAAAAAAAAAADs8A8A","267":"b:f4T936WnnJr//xMAAAAAAAAAAADs8A8A","280":"b:f4T936WnnJr//xMAAAAAAAAAAADs8A8A","284":"b:f4T936WnnJr//xMAAAAAAAAAAADs8A8A","288":"b:f4T936WnnJr//xMAAAAAAAAAAADs8A8A","293":"b:f4T936WnnJr//xMAAAAAAAAAAADs8A8A","298":"b:f4T936WnnJr//xMAAAAAAAAAAADs8A8A","303":"b:f4T936WnnJr//xMAAAAAAAAAAADs8A8A","308":"b:f4T936WnnJr//xMAAAAAAAAAAADs8A8A","313":"b:f4T936WnnJr//xMAAAAAAAAAAADs8A8A","318":"b:f4T936WnnJr//xMAAAAAAAAAAADs8A8A","323":"b:f4T936WnnJr//xMAAAAAAAAAAADs8A8A","328":"b:f4T936WnnJr//xMAAAAAAAAAAADs8A8A","333":"b:f4T936WnnJr//xMAAAAAAAAAAADs8A8A","338":"b:f4T936WnnJr//xMAAAAAAAAAAADs8A8A","343":"b:f4T936WnnJr//xMAAAAAAAAAAADs8A8A","348":"b:f4T936WnnJr//xMAAAAAAAAAAADs8A8A","353":"b:f4T936WnnJr//xMAAAAAAAAAAADs8A8A","358":"b:f4T936WnnJr//xMAAAAAAAAAAADs8A8A","363":"b:f4T936WnnJr//xMAAAAAAAAAAADs8A8A","368":"b:f4T936WnnJr//xMAAAAAAAAAAADs8A8A","373":"b:f4T936WnnJr//xMAAAAAAAAAAADs8A8A","378":"b:f4T936WnnJr//xMAAAAAAAAAAADs8A8A","383":"b:f4T936WnnJr//xMAAAAAAAAAAADs8A8A","388":"b:f4T936WnnJr//xMAAAAAAAAAAADs8A8A","393":"b:f4T936WnnJr//xMAAAAAAAAAAADs8A8A","398":"b:f4T936WnnJr//xMAAAAAAAAAAADs8A8A","403":"b:f4T936WnnJr//xMAAAAAAAAAAADs8A8A","407":"b:f4T936WnnJr//xMAAAAAAAAAAADs8A8A","411":"b:f4T936WnnJr//xMAAAAAAAAAAADs8A8A","415":"b:f4T936WnnJr//xMAAAAAAAAAAADs8A8A","425":"b:f4T936WnnJr//xMAAAAAAAAAAADs8A8A","435":"b:f4T936WnnJr//xMAAAAAAAAAAADs8A8A","438":"b:f4T936WnnJr//xMAAAAAAAAAAADs8A8A","442":"b:f4T936WnnJr//xMAAAAAAAAAAADs8A8A","449":"b:f4T936WnnJr//xMAAAAAAAAAAADs8A8A","454":"b:f4T936WnnJr//xMAAAAAAAAAAADs8A8A","459":"b:f4T936WnnJr//xMAAAAAAAAAAADs8A8A","464":"b:f4T936WnnJr//xMAAAAAAAAAAADs8A8A","469":"b:f4T936WnnJr//xMAAAAAAAAAAADs8A8A","477":"b:f4T936WnnJr//xMAAAAAAAAAAADs8A8A","481":"b:f4T936WnnJr//xMAAAAAAAAAAADs8A8A","485":"b:f4T936WnnJr//xMAAAAAAAAAAADs8A8A","493":"b:f4T936WnnJr//xMAAAAAAAAAAADs8A8A","501":"b:f4T936WnnJr//xMAAAAAAAAAAADs8A8A","516":"b:f4T936WnnJr//xMAAAAAAAAAAADs8A8A","531":"b:f4T936WnnJr//xMAAAAAAAAAAADs8A8A","540":"b:f4T936WnnJr//xMAAAAAAAAAAADs8A8A","545":"b:f4T936WnnJr//xMAAAAAAAAAAADs8A8A","550":"b:f4T936WnnJr//xMAAAAAAAAAAADs8A8A","554":"b:f4T936WnnJr//xMAAAAAAAAAAADs8A8A","560":"b:f4T936WnnJr//xMAAAAAAAAAAADs8A8A","563":"b:f4T936WnnJr//xMAAAAAAAAAAADs8A8A"},"packages/cli/src/load-nodes-and-credentials.ts":{"63":"b:f53v862//J5XfQCgqZAAEACRIgDsAAEC","72":"b:f53v862//J5XfQCgqZAAEACRIgDsAAEC","133":"b:f53v862//J5XfQCgqZAAEACRIgDsAAEC","137":"b:f53v862//J5XfQCgqZAAEACRIgDsAAEC","153":"b:f53v862//J5XfQCgqZAAEACRIgDsAAEC","168":"b:f53v862//J5XfQCgqZAAEACRIgDsAAEC","172":"b:f53v862//J5XfQCgqZAAEACRIgDsAAEC","176":"b:f53v862//J5XfQCgqZAAEACRIgDsAAEC","180":"b:f53v862//J5XfQCgqZAAEACRIgDsAAEC","184":"b:f53v862//J5XfQCgqZAAEACRIgDsAAEC","188":"b:f53v862//J5XfQCgqZAAEACRIgDsAAEC","229":"b:f53v862//J5XfQCgqZAAEACRIgDsAAEC","236":"b:f53v862//J5XfQCgqZAAEACRIgDsAAEC","240":"b:f53v862//J5XfQCgqZAAEACRIgDsAAEC","258":"b:f53v862//J5XfQCgqZAAEACRIgDsAAEC","288":"b:f53v862//J5XfQCgqZAAEACRIgDsAAEC","289":"b:f53v862//J5XfQCgqZAAEACRIgDsAAEC","304":"b:f53v862//J5XfQCgqZAAEACRIgDsAAEC","315":"b:f53v862//J5XfQCgqZAAEACRIgDsAAEC","321":"b:f53v862//J5XfQCgqZAAEACRIgDsAAEC","330":"b:f53v862//J5XfQCgqZAAEACRIgDsAAEC","341":"b:f53v862//J5XfQCgqZAAEACRIgDsAAEC","344":"b:f53v862//J5XfQCgqZAAEACRIgDsAAEC","345":"b:f53v862//J5XfQCgqZAAEACRIgDsAAEC","356":"b:f53v862//J5XfQCgqZAAEACRIgDsAAEC","367":"b:f53v862//J5XfQCgqZAAEACRIgDsAAEC","368":"b:f53v862//J5XfQCgqZAAEACRIgDsAAEC","375":"b:f53v862//J5XfQCgqZAAEACRIgDsAAEC","391":"b:f53v862//J5XfQCgqZAAEACRIgDsAAEC","395":"b:f53v862//J5XfQCgqZAAEACRIgDsAAEC","404":"b:f53v862//J5XfQCgqZAAEACRIgDsAAEC","415":"b:f53v862//J5XfQCgqZAAEACRIgDsAAEC","426":"b:f53v862//J5XfQCgqZAAEACRIgDsAAEC","436":"b:f53v862//J5XfQCgqZAAEACRIgDsAAEC","513":"b:f53v862//J5XfQCgqZAAEACRIgDsAAEC","526":"b:f53v862//J5XfQCgqZAAEACRIgDsAAEC","543":"b:f53v862//J5XfQCgqZAAEACRIgDsAAEC","555":"b:f53v862//J5XfQCgqZAAEACRIgDsAAEC","561":"b:f53v862//J5XfQCgqZAAEACRIgDsAAEC","566":"b:f53v862//J5XfQCgqZAAEACRIgDsAAEC","598":"b:f53v862//J5XfQCgqZAAEACRIgDsAAEC","617":"b:f53v862//J5XfQCgqZAAEACRIgDsAAEC","624":"b:f53v862//J5XfQCgqZAAEACRIgDsAAEC","641":"b:f53v862//J5XfQCgqZAAEACRIgDsAAEC","658":"b:f53v862//J5XfQCgqZAAEACRIgDsAAEC","676":"b:f53v862//J5XfQCgqZAAEACRIgDsAAEC","701":"b:f53v862//J5XfQCgqZAAEACRIgDsAAEC","705":"b:f53v862//J5XfQCgqZAAEACRIgDsAAEC","706":"b:f53v862//J5XfQCgqZAAEACRIgDsAAEC","712":"b:f53v862//J5XfQCgqZAAEACRIgDsAAEC","722":"b:f53v862//J5XfQCgqZAAEACRIgDsAAEC","723":"b:f53v862//J5XfQCgqZAAEACRIgDsAAEC","724":"b:f53v862//J5XfQCgqZAAEACRIgDsAAEC"},"packages/cli/src/manual-execution.service.ts":{"27":"b:R5BlwAX/sAkBAAAAQAAAEACqAAAAAAAA","29":"b:R5BlwAX/sAkBAAAAQAAAEACqAAAAAAAA","49":"b:R5BlwAX/sAkBAAAAQAAAEACqAAAAAAAA","61":"b:R5BlwAX/sAkBAAAAQAAAEACqAAAAAAAA"},"packages/cli/src/metrics/prometheus/webhook-and-form-metrics.service.ts":{"18":"b:RgDAQwCAEBACAAQAoAAACAAABCAEAwAA","20":"b:RgDAQwCAEBACAAQAoAAACAAABCAEAwAA","24":"b:RgDAQwCAEBACAAQAoAAACAAABCAEAwAA","46":"b:RgDAQwCAEBACAAQAoAAACAAABCAEAwAA","67":"b:RgDAQwCAEBACAAQAoAAACAAABCAEAwAA"},"packages/cli/src/metrics/prometheus/workflow-execution-duration-metrics.service.ts":{"19":"b:f5DlwyX3sBsDAAAAAAAAAAAAAADkAwAA","24":"b:f5DlwyX3sBsDAAAAAAAAAAAAAADkAwAA","28":"b:f5DlwyX3sBsDAAAAAAAAAAAAAADkAwAA","41":"b:f5DlwyX3sBsDAAAAAAAAAAAAAADkAwAA"},"packages/cli/src/modules/chat-hub/chat-hub-execution-store.service.ts":{"58":"b:f5DlwyX3sBsDAAAAAAAAAAAAAADkAwAA","79":"b:f5DlwyX3sBsDAAAAAAAAAAAAAADkAwAA","93":"b:f5DlwyX3sBsDAAAAAAAAAAAAAADkAwAA","103":"b:f5DlwyX3sBsDAAAAAAAAAAAAAADkAwAA","127":"b:f5DlwyX3sBsDAAAAAAAAAAAAAADkAwAA","140":"b:f5DlwyX3sBsDAAAAAAAAAAAAAADkAwAA","154":"b:f5DlwyX3sBsDAAAAAAAAAAAAAADkAwAA","158":"b:f5DlwyX3sBsDAAAAAAAAAAAAAADkAwAA","171":"b:f5DlwyX3sBsDAAAAAAAAAAAAAADkAwAA","189":"b:f5DlwyX3sBsDAAAAAAAAAAAAAADkAwAA","199":"b:f5DlwyX3sBsDAAAAAAAAAAAAAADkAwAA","202":"b:f5DlwyX3sBsDAAAAAAAAAAAAAADkAwAA","211":"b:f5DlwyX3sBsDAAAAAAAAAAAAAADkAwAA"},"packages/cli/src/modules/chat-hub/chat-hub-execution-watcher.service.ts":{"32":"b:f5DlwyX3sBsDAAAAIAAAAAAAACDkA4AA","54":"b:f5DlwyX3sBsDAAAAIAAAAAAAACDkA4AA","117":"b:f5DlwyX3sBsDAAAAIAAAAAAAACDkA4AA","160":"b:f5DlwyX3sBsDAAAAIAAAAAAAACDkA4AA","203":"b:f5DlwyX3sBsDAAAAIAAAAAAAACDkA4AA","235":"b:f5DlwyX3sBsDAAAAIAAAAAAAACDkA4AA","257":"b:f5DlwyX3sBsDAAAAIAAAAAAAACDkA4AA","280":"b:f5DlwyX3sBsDAAAAIAAAAAAAACDkA4AA"},"packages/cli/src/modules/data-table/data-table-aggregate.controller.ts":{"10":"b:X5Td3KWlnIz9/4NJAAAIASAAAAACEAAA","17":"b:X5Td3KWlnIz9/4NJAAAIASAAAAACEAAA","27":"b:X5Td3KWlnIz9/4NJAAAIASAAAAACEAAA"},"packages/cli/src/modules/data-table/data-table-size-validator.service.ts":{"16":"b:X5Td3LWlnIz9/wNIAAAIAAAAAAAAEAAA","21":"b:X5Td3LWlnIz9/wNIAAAIAAAAAAAAEAAA","33":"b:X5Td3LWlnIz9/wNIAAAIAAAAAAAAEAAA","56":"b:X5Td3LWlnIz9/wNIAAAIAAAAAAAAEAAA","73":"b:X5Td3LWlnIz9/wNIAAAIAAAAAAAAEAAA","86":"b:X5Td3LWlnIz9/wNIAAAIAAAAAAAAEAAA","91":"b:X5Td3LWlnIz9/wNIAAAIAAAAAAAAEAAA"},"packages/cli/src/modules/data-table/data-table.repository.ts":{"29":"b:X5Td3LWlnIz9/4NJAAAoASAAAAACEAAA","45":"b:X5Td3LWlnIz9/4NJAAAoASAAAAACEAAA","46":"b:X5Td3LWlnIz9/4NJAAAoASAAAAACEAAA","48":"b:X5Td3LWlnIz9/4NJAAAoASAAAAACEAAA","53":"b:X5Td3LWlnIz9/4NJAAAoASAAAAACEAAA","60":"b:X5Td3LWlnIz9/4NJAAAoASAAAAACEAAA","61":"b:X5Td3LWlnIz9/4NJAAAoASAAAAACEAAA","67":"b:X5Td3LWlnIz9/4NJAAAoASAAAAACEAAA","86":"b:X5Td3LWlnIz9/4NJAAAoASAAAAACEAAA","115":"b:X5Td3LWlnIz9/4NJAAAoASAAAAACEAAA","116":"b:X5Td3LWlnIz9/4NJAAAoASAAAAACEAAA","123":"b:X5Td3LWlnIz9/4NJAAAoASAAAAACEAAA","130":"b:X5Td3LWlnIz9/4NJAAAoASAAAAACEAAA","165":"b:X5Td3LWlnIz9/4NJAAAoASAAAAACEAAA","166":"b:X5Td3LWlnIz9/4NJAAAoASAAAAACEAAA","179":"b:X5Td3LWlnIz9/4NJAAAoASAAAAACEAAA","180":"b:X5Td3LWlnIz9/4NJAAAoASAAAAACEAAA","194":"b:X5Td3LWlnIz9/4NJAAAoASAAAAACEAAA","200":"b:X5Td3LWlnIz9/4NJAAAoASAAAAACEAAA","205":"b:X5Td3LWlnIz9/4NJAAAoASAAAAACEAAA","216":"b:X5Td3LWlnIz9/4NJAAAoASAAAAACEAAA","220":"b:X5Td3LWlnIz9/4NJAAAoASAAAAACEAAA","225":"b:X5Td3LWlnIz9/4NJAAAoASAAAAACEAAA","240":"b:X5Td3LWlnIz9/4NJAAAoASAAAAACEAAA","248":"b:X5Td3LWlnIz9/4NJAAAoASAAAAACEAAA","258":"b:X5Td3LWlnIz9/4NJAAAoASAAAAACEAAA","263":"b:X5Td3LWlnIz9/4NJAAAoASAAAAACEAAA","286":"b:X5Td3LWlnIz9/4NJAAAoASAAAAACEAAA","294":"b:X5Td3LWlnIz9/4NJAAAoASAAAAACEAAA","305":"b:X5Td3LWlnIz9/4NJAAAoASAAAAACEAAA","316":"b:X5Td3LWlnIz9/4NJAAAoASAAAAACEAAA","320":"b:X5Td3LWlnIz9/4NJAAAoASAAAAACEAAA","323":"b:X5Td3LWlnIz9/4NJAAAoASAAAAACEAAA","327":"b:X5Td3LWlnIz9/4NJAAAoASAAAAACEAAA","355":"b:X5Td3LWlnIz9/4NJAAAoASAAAAACEAAA","393":"b:X5Td3LWlnIz9/4NJAAAoASAAAAACEAAA"},"packages/cli/src/modules/data-table/data-table.service.ts":{"53":"b:X5Td3LWlnIz9/wNIAACoAAAAAAAAGAAA","67":"b:X5Td3LWlnIz9/wNIAACoAAAAAAAAGAAA","68":"b:X5Td3LWlnIz9/wNIAACoAAAAAAAAGAAA","70":"b:X5Td3LWlnIz9/wNIAACoAAAAAAAAGAAA","88":"b:X5Td3LWlnIz9/wNIAACoAAAAAAAAGAAA","136":"b:X5Td3LWlnIz9/wNIAACoAAAAAAAAGAAA","146":"b:X5Td3LWlnIz9/wNIAACoAAAAAAAAGAAA","158":"b:X5Td3LWlnIz9/wNIAACoAAAAAAAAGAAA","185":"b:X5Td3LWlnIz9/wNIAACoAAAAAAAAGAAA","194":"b:X5Td3LWlnIz9/wNIAACoAAAAAAAAGAAA","206":"b:X5Td3LWlnIz9/wNIAACoAAAAAAAAGAAA","221":"b:X5Td3LWlnIz9/wNIAACoAAAAAAAAGAAA","231":"b:X5Td3LWlnIz9/wNIAACoAAAAAAAAGAAA","242":"b:X5Td3LWlnIz9/wNIAACoAAAAAAAAGAAA","252":"b:X5Td3LWlnIz9/wNIAACoAAAAAAAAGAAA","266":"b:X5Td3LWlnIz9/wNIAACoAAAAAAAAGAAA","277":"b:X5Td3LWlnIz9/wNIAACoAAAAAAAAGAAA","289":"b:X5Td3LWlnIz9/wNIAACoAAAAAAAAGAAA","293":"b:X5Td3LWlnIz9/wNIAACoAAAAAAAAGAAA","300":"b:X5Td3LWlnIz9/wNIAACoAAAAAAAAGAAA","318":"b:X5Td3LWlnIz9/wNIAACoAAAAAAAAGAAA","324":"b:X5Td3LWlnIz9/wNIAACoAAAAAAAAGAAA","344":"b:X5Td3LWlnIz9/wNIAACoAAAAAAAAGAAA","353":"b:X5Td3LWlnIz9/wNIAACoAAAAAAAAGAAA","394":"b:X5Td3LWlnIz9/wNIAACoAAAAAAAAGAAA","404":"b:X5Td3LWlnIz9/wNIAACoAAAAAAAAGAAA","451":"b:X5Td3LWlnIz9/wNIAACoAAAAAAAAGAAA","495":"b:X5Td3LWlnIz9/wNIAACoAAAAAAAAGAAA","505":"b:X5Td3LWlnIz9/wNIAACoAAAAAAAAGAAA","559":"b:X5Td3LWlnIz9/wNIAACoAAAAAAAAGAAA","568":"b:X5Td3LWlnIz9/wNIAACoAAAAAAAAGAAA","598":"b:X5Td3LWlnIz9/wNIAACoAAAAAAAAGAAA","601":"b:X5Td3LWlnIz9/wNIAACoAAAAAAAAGAAA","611":"b:X5Td3LWlnIz9/wNIAACoAAAAAAAAGAAA","620":"b:X5Td3LWlnIz9/wNIAACoAAAAAAAAGAAA","627":"b:X5Td3LWlnIz9/wNIAACoAAAAAAAAGAAA","628":"b:X5Td3LWlnIz9/wNIAACoAAAAAAAAGAAA","630":"b:X5Td3LWlnIz9/wNIAACoAAAAAAAAGAAA","648":"b:X5Td3LWlnIz9/wNIAACoAAAAAAAAGAAA","694":"b:X5Td3LWlnIz9/wNIAACoAAAAAAAAGAAA","709":"b:X5Td3LWlnIz9/wNIAACoAAAAAAAAGAAA","722":"b:X5Td3LWlnIz9/wNIAACoAAAAAAAAGAAA","733":"b:X5Td3LWlnIz9/wNIAACoAAAAAAAAGAAA","739":"b:X5Td3LWlnIz9/wNIAACoAAAAAAAAGAAA","749":"b:X5Td3LWlnIz9/wNIAACoAAAAAAAAGAAA","785":"b:X5Td3LWlnIz9/wNIAACoAAAAAAAAGAAA","787":"b:X5Td3LWlnIz9/wNIAACoAAAAAAAAGAAA","791":"b:X5Td3LWlnIz9/wNIAACoAAAAAAAAGAAA","793":"b:X5Td3LWlnIz9/wNIAACoAAAAAAAAGAAA","809":"b:X5Td3LWlnIz9/wNIAACoAAAAAAAAGAAA","823":"b:X5Td3LWlnIz9/wNIAACoAAAAAAAAGAAA","851":"b:X5Td3LWlnIz9/wNIAACoAAAAAAAAGAAA","876":"b:X5Td3LWlnIz9/wNIAACoAAAAAAAAGAAA","881":"b:X5Td3LWlnIz9/wNIAACoAAAAAAAAGAAA","883":"b:X5Td3LWlnIz9/wNIAACoAAAAAAAAGAAA","888":"b:X5Td3LWlnIz9/wNIAACoAAAAAAAAGAAA","913":"b:X5Td3LWlnIz9/wNIAACoAAAAAAAAGAAA","941":"b:X5Td3LWlnIz9/wNIAACoAAAAAAAAGAAA","950":"b:X5Td3LWlnIz9/wNIAACoAAAAAAAAGAAA"},"packages/cli/src/modules/data-table/utils/sql-utils.ts":{"25":"b:X5Td3LWlnIz9/wNIAAAIAAAAAAAAEAAA","26":"b:X5Td3LWlnIz9/wNIAAAIAAAAAAAAEAAA","44":"b:X5Td3LWlnIz9/wNIAAAIAAAAAAAAEAAA","72":"b:X5Td3LWlnIz9/wNIAAAIAAAAAAAAEAAA","79":"b:X5Td3LWlnIz9/wNIAAAIAAAAAAAAEAAA","83":"b:X5Td3LWlnIz9/wNIAAAIAAAAAAAAEAAA","87":"b:X5Td3LWlnIz9/wNIAAAIAAAAAAAAEAAA","102":"b:X5Td3LWlnIz9/wNIAAAIAAAAAAAAEAAA","111":"b:X5Td3LWlnIz9/wNIAAAIAAAAAAAAEAAA","128":"b:X5Td3LWlnIz9/wNIAAAIAAAAAAAAEAAA","137":"b:X5Td3LWlnIz9/wNIAAAIAAAAAAAAEAAA","146":"b:X5Td3LWlnIz9/wNIAAAIAAAAAAAAEAAA","149":"b:X5Td3LWlnIz9/wNIAAAIAAAAAAAAEAAA","153":"b:X5Td3LWlnIz9/wNIAAAIAAAAAAAAEAAA","157":"b:X5Td3LWlnIz9/wNIAAAIAAAAAAAAEAAA","170":"b:X5Td3LWlnIz9/wNIAAAIAAAAAAAAEAAA","174":"b:X5Td3LWlnIz9/wNIAAAIAAAAAAAAEAAA","184":"b:X5Td3LWlnIz9/wNIAAAIAAAAAAAAEAAA","192":"b:X5Td3LWlnIz9/wNIAAAIAAAAAAAAEAAA","205":"b:X5Td3LWlnIz9/wNIAAAIAAAAAAAAEAAA","212":"b:X5Td3LWlnIz9/wNIAAAIAAAAAAAAEAAA","230":"b:X5Td3LWlnIz9/wNIAAAIAAAAAAAAEAAA","251":"b:X5Td3LWlnIz9/wNIAAAIAAAAAAAAEAAA","256":"b:X5Td3LWlnIz9/wNIAAAIAAAAAAAAEAAA","260":"b:X5Td3LWlnIz9/wNIAAAIAAAAAAAAEAAA","289":"b:X5Td3LWlnIz9/wNIAAAIAAAAAAAAEAAA","322":"b:X5Td3LWlnIz9/wNIAAAIAAAAAAAAEAAA","340":"b:X5Td3LWlnIz9/wNIAAAIAAAAAAAAEAAA","363":"b:X5Td3LWlnIz9/wNIAAAIAAAAAAAAEAAA","380":"b:X5Td3LWlnIz9/wNIAAAIAAAAAAAAEAAA","386":"b:X5Td3LWlnIz9/wNIAAAIAAAAAAAAEAAA","391":"b:X5Td3LWlnIz9/wNIAAAIAAAAAAAAEAAA"},"packages/cli/src/modules/favorites/database/repositories/user-favorite.repository.ts":{"9":"b:f5Td3KWlnoz9/wNIAAAIAAAAAAABEAAA","13":"b:f5Td3KWlnoz9/wNIAAAIAAAAAAABEAAA","20":"b:f5Td3KWlnoz9/wNIAAAIAAAAAAABEAAA","24":"b:f5Td3KWlnoz9/wNIAAAIAAAAAAABEAAA"},"packages/cli/src/modules/favorites/favorites.controller.ts":{"10":"b:f5Td3KWlnIz9/wMAAAAIAAAAAAAAEAAA","16":"b:f5Td3KWlnIz9/wMAAAAIAAAAAAAAEAAA","19":"b:f5Td3KWlnIz9/wMAAAAIAAAAAAAAEAAA","24":"b:f5Td3KWlnIz9/wMAAAAIAAAAAAAAEAAA","29":"b:f5Td3KWlnIz9/wMAAAAIAAAAAAAAEAAA"},"packages/cli/src/modules/favorites/favorites.service.ts":{"26":"b:f5Td3KWlnoz9/wNIAAAIAAAAAAABEAAA","27":"b:f5Td3KWlnoz9/wNIAAAIAAAAAAABEAAA","29":"b:f5Td3KWlnoz9/wNIAAAIAAAAAAABEAAA","35":"b:f5Td3KWlnoz9/wNIAAAIAAAAAAABEAAA","46":"b:f5Td3KWlnoz9/wNIAAAIAAAAAAABEAAA","56":"b:f5Td3KWlnoz9/wNIAAAIAAAAAAABEAAA","69":"b:f5Td3KWlnoz9/wNIAAAIAAAAAAABEAAA","79":"b:f5Td3KWlnoz9/wNIAAAIAAAAAAABEAAA","97":"b:f5Td3KWlnoz9/wNIAAAIAAAAAAABEAAA","119":"b:f5Td3KWlnoz9/wNIAAAIAAAAAAABEAAA","120":"b:f5Td3KWlnoz9/wNIAAAIAAAAAAABEAAA","128":"b:f5Td3KWlnoz9/wNIAAAIAAAAAAABEAAA","133":"b:f5Td3KWlnoz9/wNIAAAIAAAAAAABEAAA","138":"b:f5Td3KWlnoz9/wNIAAAIAAAAAAABEAAA","151":"b:f5Td3KWlnoz9/wNIAAAIAAAAAAABEAAA","170":"b:f5Td3KWlnoz9/wNIAAAIAAAAAAABEAAA","193":"b:f5Td3KWlnoz9/wNIAAAIAAAAAAABEAAA","214":"b:f5Td3KWlnoz9/wNIAAAIAAAAAAABEAAA","232":"b:f5Td3KWlnoz9/wNIAAAIAAAAAAABEAAA","261":"b:f5Td3KWlnoz9/wNIAAAIAAAAAAABEAAA","273":"b:f5Td3KWlnoz9/wNIAAAIAAAAAAABEAAA","277":"b:f5Td3KWlnoz9/wNIAAAIAAAAAAABEAAA"},"packages/cli/src/modules/insights/insights-collection.service.ts":{"65":"b:f5n9/6/3vJ/ffBuohAAAEAAgAAH0A8AB","95":"b:f5n9/6/3vJ/ffBuohAAAEAAgAAH0A8AB","105":"b:f5n9/6/3vJ/ffBuohAAAEAAgAAH0A8AB","113":"b:f5n9/6/3vJ/ffBuohAAAEAAgAAH0A8AB","119":"b:f5n9/6/3vJ/ffBuohAAAEAAgAAH0A8AB","124":"b:f5n9/6/3vJ/ffBuohAAAEAAgAAH0A8AB","131":"b:f5n9/6/3vJ/ffBuohAAAEAAgAAH0A8AB","136":"b:f5n9/6/3vJ/ffBuohAAAEAAgAAH0A8AB","152":"b:f5n9/6/3vJ/ffBuohAAAEAAgAAH0A8AB","217":"b:f5n9/6/3vJ/ffBuohAAAEAAgAAH0A8AB","232":"b:f5n9/6/3vJ/ffBuohAAAEAAgAAH0A8AB","255":"b:f5n9/6/3vJ/ffBuohAAAEAAgAAH0A8AB","283":"b:f5n9/6/3vJ/ffBuohAAAEAAgAAH0A8AB","305":"b:f5n9/6/3vJ/ffBuohAAAEAAgAAH0A8AB","328":"b:f5n9/6/3vJ/ffBuohAAAEAAgAAH0A8AB","345":"b:f5n9/6/3vJ/ffBuohAAAEAAgAAH0A8AB"},"packages/cli/src/modules/instance-registry/instance-registry.service.ts":{"29":"b:X4Ed/L2DH5zdfBvqBIAAFkCYAAAAAAAA","37":"b:X4Ed/L2DH5zdfBvqBIAAFkCYAAAAAAAA","52":"b:X4Ed/L2DH5zdfBvqBIAAFkCYAAAAAAAA","72":"b:X4Ed/L2DH5zdfBvqBIAAFkCYAAAAAAAA","76":"b:X4Ed/L2DH5zdfBvqBIAAFkCYAAAAAAAA","80":"b:X4Ed/L2DH5zdfBvqBIAAFkCYAAAAAAAA","84":"b:X4Ed/L2DH5zdfBvqBIAAFkCYAAAAAAAA","88":"b:X4Ed/L2DH5zdfBvqBIAAFkCYAAAAAAAA","92":"b:X4Ed/L2DH5zdfBvqBIAAFkCYAAAAAAAA","96":"b:X4Ed/L2DH5zdfBvqBIAAFkCYAAAAAAAA","109":"b:X4Ed/L2DH5zdfBvqBIAAFkCYAAAAAAAA","122":"b:X4Ed/L2DH5zdfBvqBIAAFkCYAAAAAAAA","123":"b:X4Ed/L2DH5zdfBvqBIAAFkCYAAAAAAAA","133":"b:X4Ed/L2DH5zdfBvqBIAAFkCYAAAAAAAA"},"packages/cli/src/modules/instance-registry/storage/memory-storage.ts":{"9":"b:X4Ed/L2DH5zdfBvqBIAAFkCYAAAAAAAA","16":"b:X4Ed/L2DH5zdfBvqBIAAFkCYAAAAAAAA","20":"b:X4Ed/L2DH5zdfBvqBIAAFkCYAAAAAAAA","24":"b:X4Ed/L2DH5zdfBvqBIAAFkCYAAAAAAAA","30":"b:X4Ed/L2DH5zdfBvqBIAAFkCYAAAAAAAA","34":"b:X4Ed/L2DH5zdfBvqBIAAFkCYAAAAAAAA","41":"b:X4Ed/L2DH5zdfBvqBIAAFkCYAAAAAAAA","45":"b:X4Ed/L2DH5zdfBvqBIAAFkCYAAAAAAAA","49":"b:X4Ed/L2DH5zdfBvqBIAAFkCYAAAAAAAA","53":"b:X4Ed/L2DH5zdfBvqBIAAFkCYAAAAAAAA"},"packages/cli/src/modules/otel/execution-level-tracer.ts":{"22":"b:f5DlwyX/sBsDAAAAAAAAAEACAADkAwAA","39":"b:f5DlwyX/sBsDAAAAAAAAAEACAADkAwAA","43":"b:f5DlwyX/sBsDAAAAAAAAAEACAADkAwAA","48":"b:f5DlwyX/sBsDAAAAAAAAAEACAADkAwAA","87":"b:f5DlwyX/sBsDAAAAAAAAAEACAADkAwAA","123":"b:f5DlwyX/sBsDAAAAAAAAAEACAADkAwAA","168":"b:f5DlwyX/sBsDAAAAAAAAAEACAADkAwAA","199":"b:f5DlwyX/sBsDAAAAAAAAAEACAADkAwAA","220":"b:f5DlwyX/sBsDAAAAAAAAAEACAADkAwAA","226":"b:f5DlwyX/sBsDAAAAAAAAAEACAADkAwAA","239":"b:f5DlwyX/sBsDAAAAAAAAAEACAADkAwAA","244":"b:f5DlwyX/sBsDAAAAAAAAAEACAADkAwAA","252":"b:f5DlwyX/sBsDAAAAAAAAAEACAADkAwAA","264":"b:f5DlwyX/sBsDAAAAAAAAAEACAADkAwAA","276":"b:f5DlwyX/sBsDAAAAAAAAAEACAADkAwAA","285":"b:f5DlwyX/sBsDAAAAAAAAAEACAADkAwAA","291":"b:f5DlwyX/sBsDAAAAAAAAAEACAADkAwAA","297":"b:f5DlwyX/sBsDAAAAAAAAAEACAADkAwAA","316":"b:f5DlwyX/sBsDAAAAAAAAAEACAADkAwAA","329":"b:f5DlwyX/sBsDAAAAAAAAAEACAADkAwAA","336":"b:f5DlwyX/sBsDAAAAAAAAAEACAADkAwAA","344":"b:f5DlwyX/sBsDAAAAAAAAAEACAADkAwAA"},"packages/cli/src/modules/otel/otel-lifecycle-handler.ts":{"20":"b:f5DlwyX3sBsDAAAAIAAAAAAAACDkA4AA","29":"b:f5DlwyX3sBsDAAAAIAAAAAAAACDkA4AA","41":"b:f5DlwyX3sBsDAAAAIAAAAAAAACDkA4AA","52":"b:f5DlwyX3sBsDAAAAIAAAAAAAACDkA4AA","57":"b:f5DlwyX3sBsDAAAAIAAAAAAAACDkA4AA","61":"b:f5DlwyX3sBsDAAAAIAAAAAAAACDkA4AA","72":"b:f5DlwyX3sBsDAAAAIAAAAAAAACDkA4AA","84":"b:f5DlwyX3sBsDAAAAIAAAAAAAACDkA4AA","117":"b:f5DlwyX3sBsDAAAAIAAAAAAAACDkA4AA","124":"b:f5DlwyX3sBsDAAAAIAAAAAAAACDkA4AA","153":"b:f5DlwyX3sBsDAAAAIAAAAAAAACDkA4AA","165":"b:f5DlwyX3sBsDAAAAIAAAAAAAACDkA4AA","168":"b:f5DlwyX3sBsDAAAAIAAAAAAAACDkA4AA","178":"b:f5DlwyX3sBsDAAAAIAAAAAAAACDkA4AA","181":"b:f5DlwyX3sBsDAAAAIAAAAAAAACDkA4AA","194":"b:f5DlwyX3sBsDAAAAIAAAAAAAACDkA4AA","198":"b:f5DlwyX3sBsDAAAAIAAAAAAAACDkA4AA","219":"b:f5DlwyX3sBsDAAAAIAAAAAAAACDkA4AA","232":"b:f5DlwyX3sBsDAAAAIAAAAAAAACDkA4AA","237":"b:f5DlwyX3sBsDAAAAIAAAAAAAACDkA4AA","242":"b:f5DlwyX3sBsDAAAAIAAAAAAAACDkA4AA","244":"b:f5DlwyX3sBsDAAAAIAAAAAAAACDkA4AA","247":"b:f5DlwyX3sBsDAAAAIAAAAAAAACDkA4AA"},"packages/cli/src/modules/redaction/executions/execution-redaction.service.ts":{"42":"b:R5DlwyX3sAMBAAAAAAAAAAAAAAAAAAAA","50":"b:R5DlwyX3sAMBAAAAAAAAAAAAAAAAAAAA","62":"b:R5DlwyX3sAMBAAAAAAAAAAAAAAAAAAAA","79":"b:R5DlwyX3sAMBAAAAAAAAAAAAAAAAAAAA","94":"b:R5DlwyX3sAMBAAAAAAAAAAAAAAAAAAAA","101":"b:R5DlwyX3sAMBAAAAAAAAAAAAAAAAAAAA","104":"b:R5DlwyX3sAMBAAAAAAAAAAAAAAAAAAAA","196":"b:R5DlwyX3sAMBAAAAAAAAAAAAAAAAAAAA","244":"b:R5DlwyX3sAMBAAAAAAAAAAAAAAAAAAAA","277":"b:R5DlwyX3sAMBAAAAAAAAAAAAAAAAAAAA","294":"b:R5DlwyX3sAMBAAAAAAAAAAAAAAAAAAAA","309":"b:R5DlwyX3sAMBAAAAAAAAAAAAAAAAAAAA","328":"b:R5DlwyX3sAMBAAAAAAAAAAAAAAAAAAAA"},"packages/cli/src/modules/redaction/instance-redaction-enforcement.service.ts":{"16":"b:f5DlwyX3sBsDAAAAAAAAAAAAAADkAwAA","29":"b:f5DlwyX3sBsDAAAAAAAAAAAAAADkAwAA","33":"b:f5DlwyX3sBsDAAAAAAAAAAAAAADkAwAA","35":"b:f5DlwyX3sBsDAAAAAAAAAAAAAADkAwAA","42":"b:f5DlwyX3sBsDAAAAAAAAAAAAAADkAwAA","60":"b:f5DlwyX3sBsDAAAAAAAAAAAAAADkAwAA","77":"b:f5DlwyX3sBsDAAAAAAAAAAAAAADkAwAA","81":"b:f5DlwyX3sBsDAAAAAAAAAAAAAADkAwAA","90":"b:f5DlwyX3sBsDAAAAAAAAAAAAAADkAwAA"},"packages/cli/src/modules/redaction/redaction-context-hook.ts":{"19":"b:f5DlwyX3sBsDAAAAAAAAAAAAAADkAwAA","27":"b:f5DlwyX3sBsDAAAAAAAAAAAAAADkAwAA","45":"b:f5DlwyX3sBsDAAAAAAAAAAAAAADkAwAA"},"packages/cli/src/modules/workflow-index/workflow-index.service.ts":{"28":"b:R9390+e33JpfAAAAIAQAAkASgAL9AuEB","40":"b:R9390+e33JpfAAAAIAQAAkASgAL9AuEB","41":"b:R9390+e33JpfAAAAIAQAAkASgAL9AuEB","43":"b:R9390+e33JpfAAAAIAQAAkASgAL9AuEB","45":"b:R9390+e33JpfAAAAIAQAAkASgAL9AuEB","48":"b:R9390+e33JpfAAAAIAQAAkASgAL9AuEB","51":"b:R9390+e33JpfAAAAIAQAAkASgAL9AuEB","54":"b:R9390+e33JpfAAAAIAQAAkASgAL9AuEB","66":"b:R9390+e33JpfAAAAIAQAAkASgAL9AuEB","69":"b:R9390+e33JpfAAAAIAQAAkASgAL9AuEB","71":"b:R9390+e33JpfAAAAIAQAAkASgAL9AuEB","77":"b:R9390+e33JpfAAAAIAQAAkASgAL9AuEB","90":"b:R9390+e33JpfAAAAIAQAAkASgAL9AuEB","138":"b:R9390+e33JpfAAAAIAQAAkASgAL9AuEB","152":"b:R9390+e33JpfAAAAIAQAAkASgAL9AuEB","170":"b:R9390+e33JpfAAAAIAQAAkASgAL9AuEB","177":"b:R9390+e33JpfAAAAIAQAAkASgAL9AuEB","199":"b:R9390+e33JpfAAAAIAQAAkASgAL9AuEB","217":"b:R9390+e33JpfAAAAIAQAAkASgAL9AuEB","218":"b:R9390+e33JpfAAAAIAQAAkASgAL9AuEB","260":"b:R9390+e33JpfAAAAIAQAAkASgAL9AuEB","270":"b:R9390+e33JpfAAAAIAQAAkASgAL9AuEB","287":"b:R9390+e33JpfAAAAIAQAAkASgAL9AuEB","309":"b:R9390+e33JpfAAAAIAQAAkASgAL9AuEB","324":"b:R9390+e33JpfAAAAIAQAAkASgAL9AuEB","338":"b:R9390+e33JpfAAAAIAQAAkASgAL9AuEB","353":"b:R9390+e33JpfAAAAIAQAAkASgAL9AuEB"},"packages/cli/src/node-types.ts":{"19":"b:f5Tt06W32JoXAAAAAAAAAAAAAADsAAEA","32":"b:f5Tt06W32JoXAAAAAAAAAAAAAADsAAEA","44":"b:f5Tt06W32JoXAAAAAAAAAAAAAADsAAEA","57":"b:f5Tt06W32JoXAAAAAAAAAAAAAADsAAEA","83":"b:f5Tt06W32JoXAAAAAAAAAAAAAADsAAEA","87":"b:f5Tt06W32JoXAAAAAAAAAAAAAADsAAEA","107":"b:f5Tt06W32JoXAAAAAAAAAAAAAADsAAEA","148":"b:f5Tt06W32JoXAAAAAAAAAAAAAADsAAEA","162":"b:f5Tt06W32JoXAAAAAAAAAAAAAADsAAEA","175":"b:f5Tt06W32JoXAAAAAAAAAAAAAADsAAEA","177":"b:f5Tt06W32JoXAAAAAAAAAAAAAADsAAEA","195":"b:f5Tt06W32JoXAAAAAAAAAAAAAADsAAEA","213":"b:f5Tt06W32JoXAAAAAAAAAAAAAADsAAEA","216":"b:f5Tt06W32JoXAAAAAAAAAAAAAADsAAEA","223":"b:f5Tt06W32JoXAAAAAAAAAAAAAADsAAEA","226":"b:f5Tt06W32JoXAAAAAAAAAAAAAADsAAEA","237":"b:f5Tt06W32JoXAAAAAAAAAAAAAADsAAEA","238":"b:f5Tt06W32JoXAAAAAAAAAAAAAADsAAEA","262":"b:f5Tt06W32JoXAAAAAAAAAAAAAADsAAEA"},"packages/cli/src/permissions.ee/check-access.ts":{"35":"b:f5T936WlHNqX/wMAAAIAAYAAAADsOAgA","57":"b:f5T936WlHNqX/wMAAAIAAYAAAADsOAgA","91":"b:f5T936WlHNqX/wMAAAIAAYAAAADsOAgA","111":"b:f5T936WlHNqX/wMAAAIAAYAAAADsOAgA","146":"b:f5T936WlHNqX/wMAAAIAAYAAAADsOAgA"},"packages/cli/src/posthog/index.ts":{"33":"b:f4T9z6GhHJqT/xMAAAAAAAAAAADs8A8A","38":"b:f4T9z6GhHJqT/xMAAAAAAAAAAADs8A8A","50":"b:f4T9z6GhHJqT/xMAAAAAAAAAAADs8A8A","56":"b:f4T9z6GhHJqT/xMAAAAAAAAAAADs8A8A","71":"b:f4T9z6GhHJqT/xMAAAAAAAAAAADs8A8A","97":"b:f4T9z6GhHJqT/xMAAAAAAAAAAADs8A8A","109":"b:f4T9z6GhHJqT/xMAAAAAAAAAAADs8A8A","123":"b:f4T9z6GhHJqT/xMAAAAAAAAAAADs8A8A","151":"b:f4T9z6GhHJqT/xMAAAAAAAAAAADs8A8A","171":"b:f4T9z6GhHJqT/xMAAAAAAAAAAADs8A8A"},"packages/cli/src/push/abstract.push.ts":{"34":"b:f5Xd9rX/+IfdfyMoEAEEEABAAACgAAAC","40":"b:f5Xd9rX/+IfdfyMoEAEEEABAAACgAAAC","43":"b:f5Xd9rX/+IfdfyMoEAEEEABAAACgAAAC","58":"b:f5Xd9rX/+IfdfyMoEAEEEABAAACgAAAC","66":"b:f5Xd9rX/+IfdfyMoEAEEEABAAACgAAAC","70":"b:f5Xd9rX/+IfdfyMoEAEEEABAAACgAAAC","79":"b:f5Xd9rX/+IfdfyMoEAEEEABAAACgAAAC","96":"b:f5Xd9rX/+IfdfyMoEAEEEABAAACgAAAC","102":"b:f5Xd9rX/+IfdfyMoEAEEEABAAACgAAAC","106":"b:f5Xd9rX/+IfdfyMoEAEEEABAAACgAAAC","115":"b:f5Xd9rX/+IfdfyMoEAEEEABAAACgAAAC","117":"b:f5Xd9rX/+IfdfyMoEAEEEABAAACgAAAC","124":"b:f5Xd9rX/+IfdfyMoEAEEEABAAACgAAAC","134":"b:f5Xd9rX/+IfdfyMoEAEEEABAAACgAAAC"},"packages/cli/src/push/index.ts":{"52":"b:f5Xd0qX/+IPdfwAAAAAAAABAAACgAAAA","62":"b:f5Xd0qX/+IPdfwAAAAAAAABAAACgAAAA","65":"b:f5Xd0qX/+IPdfwAAAAAAAABAAACgAAAA","70":"b:f5Xd0qX/+IPdfwAAAAAAAABAAACgAAAA","73":"b:f5Xd0qX/+IPdfwAAAAAAAABAAACgAAAA","75":"b:f5Xd0qX/+IPdfwAAAAAAAABAAACgAAAA","79":"b:f5Xd0qX/+IPdfwAAAAAAAABAAACgAAAA","94":"b:f5Xd0qX/+IPdfwAAAAAAAABAAACgAAAA","99":"b:f5Xd0qX/+IPdfwAAAAAAAABAAACgAAAA","111":"b:f5Xd0qX/+IPdfwAAAAAAAABAAACgAAAA","166":"b:f5Xd0qX/+IPdfwAAAAAAAABAAACgAAAA","171":"b:f5Xd0qX/+IPdfwAAAAAAAABAAACgAAAA","180":"b:f5Xd0qX/+IPdfwAAAAAAAABAAACgAAAA","189":"b:f5Xd0qX/+IPdfwAAAAAAAABAAACgAAAA","194":"b:f5Xd0qX/+IPdfwAAAAAAAABAAACgAAAA","215":"b:f5Xd0qX/+IPdfwAAAAAAAABAAACgAAAA","222":"b:f5Xd0qX/+IPdfwAAAAAAAABAAACgAAAA","237":"b:f5Xd0qX/+IPdfwAAAAAAAABAAACgAAAA","244":"b:f5Xd0qX/+IPdfwAAAAAAAABAAACgAAAA"},"packages/cli/src/push/push-helpers.ts":{"5":"b:f5Xd0KWl2IDdfwAAAAAAAAAAAAAAAAAA","9":"b:f5Xd0KWl2IDdfwAAAAAAAAAAAAAAAAAA","14":"b:f5Xd0KWl2IDdfwAAAAAAAAAAAAAAAAAA","18":"b:f5Xd0KWl2IDdfwAAAAAAAAAAAAAAAAAA"},"packages/cli/src/push/websocket.push.ts":{"9":"b:f5Xd0KW32IDdfwAqsAAAEsKQAAAAAAAA","15":"b:f5Xd0KW32IDdfwAqsAAAEsKQAAAAAAAA","21":"b:f5Xd0KW32IDdfwAqsAAAEsKQAAAAAAAA","60":"b:f5Xd0KW32IDdfwAqsAAAEsKQAAAAAAAA","71":"b:f5Xd0KW32IDdfwAqsAAAEsKQAAAAAAAA","75":"b:f5Xd0KW32IDdfwAqsAAAEsKQAAAAAAAA","79":"b:f5Xd0KW32IDdfwAqsAAAEsKQAAAAAAAA","88":"b:f5Xd0KW32IDdfwAqsAAAEsKQAAAAAAAA"},"packages/cli/src/server.ts":{"89":"b:f5Xf3K29/Iz9/xMACYAIEgARIgAAEAEC","101":"b:f5Xf3K29/Iz9/xMACYAIEgARIgAAEAEC","125":"b:f5Xf3K29/Iz9/xMACYAIEgARIgAAEAEC","163":"b:f5Xf3K29/Iz9/xMACYAIEgARIgAAEAEC","204":"b:f5Xf3K29/Iz9/xMACYAIEgARIgAAEAEC","249":"b:f5Xf3K29/Iz9/xMACYAIEgARIgAAEAEC","279":"b:f5Xf3K29/Iz9/xMACYAIEgARIgAAEAEC","333":"b:f5Xf3K29/Iz9/xMACYAIEgARIgAAEAEC","337":"b:f5Xf3K29/Iz9/xMACYAIEgARIgAAEAEC","352":"b:f5Xf3K29/Iz9/xMACYAIEgARIgAAEAEC","367":"b:f5Xf3K29/Iz9/xMACYAIEgARIgAAEAEC","441":"b:f5Xf3K29/Iz9/xMACYAIEgARIgAAEAEC","443":"b:f5Xf3K29/Iz9/xMACYAIEgARIgAAEAEC","456":"b:f5Xf3K29/Iz9/xMACYAIEgARIgAAEAEC","463":"b:f5Xf3K29/Iz9/xMACYAIEgARIgAAEAEC","485":"b:f5Xf3K29/Iz9/xMACYAIEgARIgAAEAEC","494":"b:f5Xf3K29/Iz9/xMACYAIEgARIgAAEAEC","503":"b:f5Xf3K29/Iz9/xMACYAIEgARIgAAEAEC","510":"b:f5Xf3K29/Iz9/xMACYAIEgARIgAAEAEC"},"packages/cli/src/services/access.service.ts":{"13":"b:R9Xd0KW32IBdAAAAIAQAA0ACAAAAAAAA","19":"b:R9Xd0KW32IBdAAAAIAQAA0ACAAAAAAAA","31":"b:R9Xd0KW32IBdAAAAIAQAA0ACAAAAAAAA"},"packages/cli/src/services/active-workflows.service.ts":{"13":"b:R5Td3KWnnID94AMAhASAAgIAAAMAAAAA","21":"b:R5Td3KWnnID94AMAhASAAgIAAAMAAAAA","24":"b:R5Td3KWnnID94AMAhASAAgIAAAMAAAAA","27":"b:R5Td3KWnnID94AMAhASAAgIAAAMAAAAA","33":"b:R5Td3KWnnID94AMAhASAAgIAAAMAAAAA","38":"b:R5Td3KWnnID94AMAhASAAgIAAAMAAAAA","41":"b:R5Td3KWnnID94AMAhASAAgIAAAMAAAAA"},"packages/cli/src/services/cache/cache.service.ts":{"26":"b:f5z906f3/JsfAAQAgAAgCqAARCj/AkAB","32":"b:f5z906f3/JsfAAQAgAAgCqAARCj/AkAB","69":"b:f5z906f3/JsfAAQAgAAgCqAARCj/AkAB","79":"b:f5z906f3/JsfAAQAgAAgCqAARCj/AkAB","83":"b:f5z906f3/JsfAAQAgAAgCqAARCj/AkAB","87":"b:f5z906f3/JsfAAQAgAAgCqAARCj/AkAB","91":"b:f5z906f3/JsfAAQAgAAgCqAARCj/AkAB","103":"b:f5z906f3/JsfAAQAgAAgCqAARCj/AkAB","115":"b:f5z906f3/JsfAAQAgAAgCqAARCj/AkAB","121":"b:f5z906f3/JsfAAQAgAAgCqAARCj/AkAB","139":"b:f5z906f3/JsfAAQAgAAgCqAARCj/AkAB","160":"b:f5z906f3/JsfAAQAgAAgCqAARCj/AkAB","180":"b:f5z906f3/JsfAAQAgAAgCqAARCj/AkAB","218":"b:f5z906f3/JsfAAQAgAAgCqAARCj/AkAB","256":"b:f5z906f3/JsfAAQAgAAgCqAARCj/AkAB","300":"b:f5z906f3/JsfAAQAgAAgCqAARCj/AkAB","308":"b:f5z906f3/JsfAAQAgAAgCqAARCj/AkAB","320":"b:f5z906f3/JsfAAQAgAAgCqAARCj/AkAB","339":"b:f5z906f3/JsfAAQAgAAgCqAARCj/AkAB","355":"b:f5z906f3/JsfAAQAgAAgCqAARCj/AkAB"},"packages/cli/src/services/execution-metadata.service.ts":{"7":[82,1,2,3,4,5,109,123,6],"9":[82,1,2,3,4,5,109,123,6]},"packages/cli/src/services/frontend.service.ts":{"124":"b:f5Td3KWnnIz9/4MBAAAIAAAAAAAQEAAA","145":"b:f5Td3KWnnIz9/4MBAAAIAAAAAAAQEAAA","150":"b:f5Td3KWnnIz9/4MBAAAIAAAAAAAQEAAA","157":"b:f5Td3KWnnIz9/4MBAAAIAAAAAAAQEAAA","169":"b:f5Td3KWnnIz9/4MBAAAIAAAAAAAQEAAA","176":"b:f5Td3KWnnIz9/4MBAAAIAAAAAAAQEAAA","414":"b:f5Td3KWnnIz9/4MBAAAIAAAAAAAQEAAA","415":"b:f5Td3KWnnIz9/4MBAAAIAAAAAAAQEAAA","420":"b:f5Td3KWnnIz9/4MBAAAIAAAAAAAQEAAA","451":"b:f5Td3KWnnIz9/4MBAAAIAAAAAAAQEAAA","465":"b:f5Td3KWnnIz9/4MBAAAIAAAAAAAQEAAA","644":"b:f5Td3KWnnIz9/4MBAAAIAAAAAAAQEAAA","655":"b:f5Td3KWnnIz9/4MBAAAIAAAAAAAQEAAA","676":"b:f5Td3KWnnIz9/4MBAAAIAAAAAAAQEAAA","718":"b:f5Td3KWnnIz9/4MBAAAIAAAAAAAQEAAA","722":"b:f5Td3KWnnIz9/4MBAAAIAAAAAAAQEAAA","738":"b:f5Td3KWnnIz9/4MBAAAIAAAAAAAQEAAA","746":"b:f5Td3KWnnIz9/4MBAAAIAAAAAAAQEAAA","751":"b:f5Td3KWnnIz9/4MBAAAIAAAAAAAQEAAA","761":"b:f5Td3KWnnIz9/4MBAAAIAAAAAAAQEAAA","774":"b:f5Td3KWnnIz9/4MBAAAIAAAAAAAQEAAA","775":"b:f5Td3KWnnIz9/4MBAAAIAAAAAAAQEAAA","805":"b:f5Td3KWnnIz9/4MBAAAIAAAAAAAQEAAA"},"packages/cli/src/services/jwt.service.ts":{"12":"b:f4T936WlHNqT/xMEAAKAAQAAAADs+A8A","33":"b:f4T936WlHNqT/xMEAAKAAQAAAADs+A8A","60":"b:f4T936WlHNqT/xMEAAKAAQAAAADs+A8A","64":"b:f4T936WlHNqT/xMEAAKAAQAAAADs+A8A","68":"b:f4T936WlHNqT/xMEAAKAAQAAAADs+A8A"},"packages/cli/src/services/last-active-at.service.ts":{"11":"b:f4T936WlHJqT/xMAAAAAAAAAAADsMAkA","16":"b:f4T936WlHJqT/xMAAAAAAAAAAADsMAkA","18":"b:f4T936WlHJqT/xMAAAAAAAAAAADsMAkA","25":"b:f4T936WlHJqT/xMAAAAAAAAAAADsMAkA"},"packages/cli/src/services/ownership.service.ts":{"29":"b:f5z916f33J8XAARIAAIgCEAAhAD9A2AB","46":"b:f5z916f33J8XAARIAAIgCEAAhAD9A2AB","52":"b:f5z916f33J8XAARIAAIgCEAAhAD9A2AB","59":"b:f5z916f33J8XAARIAAIgCEAAhAD9A2AB","66":"b:f5z916f33J8XAARIAAIgCEAAhAD9A2AB","76":"b:f5z916f33J8XAARIAAIgCEAAhAD9A2AB","93":"b:f5z916f33J8XAARIAAIgCEAAhAD9A2AB","116":"b:f5z916f33J8XAARIAAIgCEAAhAD9A2AB","128":"b:f5z916f33J8XAARIAAIgCEAAhAD9A2AB","148":"b:f5z916f33J8XAARIAAIgCEAAhAD9A2AB","155":"b:f5z916f33J8XAARIAAIgCEAAhAD9A2AB","162":"b:f5z916f33J8XAARIAAIgCEAAhAD9A2AB","173":"b:f5z916f33J8XAARIAAIgCEAAhAD9A2AB","176":"b:f5z916f33J8XAARIAAIgCEAAhAD9A2AB","188":"b:f5z916f33J8XAARIAAIgCEAAhAD9A2AB","230":"b:f5z916f33J8XAARIAAIgCEAAhAD9A2AB","236":"b:f5z916f33J8XAARIAAIgCEAAhAD9A2AB","256":"b:f5z916f33J8XAARIAAIgCEAAhAD9A2AB"},"packages/cli/src/services/password.utility.ts":{"8":"b:f4T9z6GhHJqT/xMAAAAAAAAAAADs8A8A","12":"b:f4T9z6GhHJqT/xMAAAAAAAAAAADs8A8A"},"packages/cli/src/services/project.service.ee.ts":{"37":"b:f5X//7+l3t7//xPYAAKsAQAAAADtGBEA","45":"b:f5X//7+l3t7//xPYAAKsAQAAAADtGBEA","51":"b:f5X//7+l3t7//xPYAAKsAQAAAADtGBEA","55":"b:f5X//7+l3t7//xPYAAKsAQAAAADtGBEA","73":"b:f5X//7+l3t7//xPYAAKsAQAAAADtGBEA","85":"b:f5X//7+l3t7//xPYAAKsAQAAAADtGBEA","86":"b:f5X//7+l3t7//xPYAAKsAQAAAADtGBEA","91":"b:f5X//7+l3t7//xPYAAKsAQAAAADtGBEA","92":"b:f5X//7+l3t7//xPYAAKsAQAAAADtGBEA","97":"b:f5X//7+l3t7//xPYAAKsAQAAAADtGBEA","98":"b:f5X//7+l3t7//xPYAAKsAQAAAADtGBEA","103":"b:f5X//7+l3t7//xPYAAKsAQAAAADtGBEA","104":"b:f5X//7+l3t7//xPYAAKsAQAAAADtGBEA","109":"b:f5X//7+l3t7//xPYAAKsAQAAAADtGBEA","111":"b:f5X//7+l3t7//xPYAAKsAQAAAADtGBEA","115":"b:f5X//7+l3t7//xPYAAKsAQAAAADtGBEA","116":"b:f5X//7+l3t7//xPYAAKsAQAAAADtGBEA","121":"b:f5X//7+l3t7//xPYAAKsAQAAAADtGBEA","122":"b:f5X//7+l3t7//xPYAAKsAQAAAADtGBEA","127":"b:f5X//7+l3t7//xPYAAKsAQAAAADtGBEA","128":"b:f5X//7+l3t7//xPYAAKsAQAAAADtGBEA","133":"b:f5X//7+l3t7//xPYAAKsAQAAAADtGBEA","135":"b:f5X//7+l3t7//xPYAAKsAQAAAADtGBEA","139":"b:f5X//7+l3t7//xPYAAKsAQAAAADtGBEA","185":"b:f5X//7+l3t7//xPYAAKsAQAAAADtGBEA","202":"b:f5X//7+l3t7//xPYAAKsAQAAAADtGBEA","267":"b:f5X//7+l3t7//xPYAAKsAQAAAADtGBEA","286":"b:f5X//7+l3t7//xPYAAKsAQAAAADtGBEA","295":"b:f5X//7+l3t7//xPYAAKsAQAAAADtGBEA","304":"b:f5X//7+l3t7//xPYAAKsAQAAAADtGBEA","308":"b:f5X//7+l3t7//xPYAAKsAQAAAADtGBEA","311":"b:f5X//7+l3t7//xPYAAKsAQAAAADtGBEA","313":"b:f5X//7+l3t7//xPYAAKsAQAAAADtGBEA","328":"b:f5X//7+l3t7//xPYAAKsAQAAAADtGBEA","336":"b:f5X//7+l3t7//xPYAAKsAQAAAADtGBEA","350":"b:f5X//7+l3t7//xPYAAKsAQAAAADtGBEA","360":"b:f5X//7+l3t7//xPYAAKsAQAAAADtGBEA","364":"b:f5X//7+l3t7//xPYAAKsAQAAAADtGBEA","394":"b:f5X//7+l3t7//xPYAAKsAQAAAADtGBEA","401":"b:f5X//7+l3t7//xPYAAKsAQAAAADtGBEA","406":"b:f5X//7+l3t7//xPYAAKsAQAAAADtGBEA","411":"b:f5X//7+l3t7//xPYAAKsAQAAAADtGBEA","412":"b:f5X//7+l3t7//xPYAAKsAQAAAADtGBEA","426":"b:f5X//7+l3t7//xPYAAKsAQAAAADtGBEA","430":"b:f5X//7+l3t7//xPYAAKsAQAAAADtGBEA","437":"b:f5X//7+l3t7//xPYAAKsAQAAAADtGBEA","449":"b:f5X//7+l3t7//xPYAAKsAQAAAADtGBEA","453":"b:f5X//7+l3t7//xPYAAKsAQAAAADtGBEA","456":"b:f5X//7+l3t7//xPYAAKsAQAAAADtGBEA","457":"b:f5X//7+l3t7//xPYAAKsAQAAAADtGBEA","462":"b:f5X//7+l3t7//xPYAAKsAQAAAADtGBEA","466":"b:f5X//7+l3t7//xPYAAKsAQAAAADtGBEA","472":"b:f5X//7+l3t7//xPYAAKsAQAAAADtGBEA","482":"b:f5X//7+l3t7//xPYAAKsAQAAAADtGBEA","494":"b:f5X//7+l3t7//xPYAAKsAQAAAADtGBEA","503":"b:f5X//7+l3t7//xPYAAKsAQAAAADtGBEA","511":"b:f5X//7+l3t7//xPYAAKsAQAAAADtGBEA","516":"b:f5X//7+l3t7//xPYAAKsAQAAAADtGBEA","530":"b:f5X//7+l3t7//xPYAAKsAQAAAADtGBEA","547":"b:f5X//7+l3t7//xPYAAKsAQAAAADtGBEA","551":"b:f5X//7+l3t7//xPYAAKsAQAAAADtGBEA","569":"b:f5X//7+l3t7//xPYAAKsAQAAAADtGBEA","573":"b:f5X//7+l3t7//xPYAAKsAQAAAADtGBEA","585":"b:f5X//7+l3t7//xPYAAKsAQAAAADtGBEA","595":"b:f5X//7+l3t7//xPYAAKsAQAAAADtGBEA","600":"b:f5X//7+l3t7//xPYAAKsAQAAAADtGBEA","609":"b:f5X//7+l3t7//xPYAAKsAQAAAADtGBEA","611":"b:f5X//7+l3t7//xPYAAKsAQAAAADtGBEA","615":"b:f5X//7+l3t7//xPYAAKsAQAAAADtGBEA","625":"b:f5X//7+l3t7//xPYAAKsAQAAAADtGBEA","631":"b:f5X//7+l3t7//xPYAAKsAQAAAADtGBEA","643":"b:f5X//7+l3t7//xPYAAKsAQAAAADtGBEA","649":"b:f5X//7+l3t7//xPYAAKsAQAAAADtGBEA","657":"b:f5X//7+l3t7//xPYAAKsAQAAAADtGBEA","663":"b:f5X//7+l3t7//xPYAAKsAQAAAADtGBEA","667":"b:f5X//7+l3t7//xPYAAKsAQAAAADtGBEA","674":"b:f5X//7+l3t7//xPYAAKsAQAAAADtGBEA","684":"b:f5X//7+l3t7//xPYAAKsAQAAAADtGBEA","715":"b:f5X//7+l3t7//xPYAAKsAQAAAADtGBEA","737":"b:f5X//7+l3t7//xPYAAKsAQAAAADtGBEA","740":"b:f5X//7+l3t7//xPYAAKsAQAAAADtGBEA","746":"b:f5X//7+l3t7//xPYAAKsAQAAAADtGBEA","749":"b:f5X//7+l3t7//xPYAAKsAQAAAADtGBEA","782":"b:f5X//7+l3t7//xPYAAKsAQAAAADtGBEA","795":"b:f5X//7+l3t7//xPYAAKsAQAAAADtGBEA","804":"b:f5X//7+l3t7//xPYAAKsAQAAAADtGBEA","808":"b:f5X//7+l3t7//xPYAAKsAQAAAADtGBEA","815":"b:f5X//7+l3t7//xPYAAKsAQAAAADtGBEA","825":"b:f5X//7+l3t7//xPYAAKsAQAAAADtGBEA","836":"b:f5X//7+l3t7//xPYAAKsAQAAAADtGBEA"},"packages/cli/src/services/role.service.ts":{"52":"b:f9T9/6+33t7//4NFAAaIA2AAAAHsOiEA","61":"b:f9T9/6+33t7//4NFAAaIA2AAAAHsOiEA","64":"b:f9T9/6+33t7//4NFAAaIA2AAAAHsOiEA","71":"b:f9T9/6+33t7//4NFAAaIA2AAAAHsOiEA","79":"b:f9T9/6+33t7//4NFAAaIA2AAAAHsOiEA","83":"b:f9T9/6+33t7//4NFAAaIA2AAAAHsOiEA","91":"b:f9T9/6+33t7//4NFAAaIA2AAAAHsOiEA","98":"b:f9T9/6+33t7//4NFAAaIA2AAAAHsOiEA","116":"b:f9T9/6+33t7//4NFAAaIA2AAAAHsOiEA","129":"b:f9T9/6+33t7//4NFAAaIA2AAAAHsOiEA","142":"b:f9T9/6+33t7//4NFAAaIA2AAAAHsOiEA","176":"b:f9T9/6+33t7//4NFAAaIA2AAAAHsOiEA","192":"b:f9T9/6+33t7//4NFAAaIA2AAAAHsOiEA","206":"b:f9T9/6+33t7//4NFAAaIA2AAAAHsOiEA","210":"b:f9T9/6+33t7//4NFAAaIA2AAAAHsOiEA","212":"b:f9T9/6+33t7//4NFAAaIA2AAAAHsOiEA","214":"b:f9T9/6+33t7//4NFAAaIA2AAAAHsOiEA","224":"b:f9T9/6+33t7//4NFAAaIA2AAAAHsOiEA","257":"b:f9T9/6+33t7//4NFAAaIA2AAAAHsOiEA","286":"b:f9T9/6+33t7//4NFAAaIA2AAAAHsOiEA","291":"b:f9T9/6+33t7//4NFAAaIA2AAAAHsOiEA","300":"b:f9T9/6+33t7//4NFAAaIA2AAAAHsOiEA","327":"b:f9T9/6+33t7//4NFAAaIA2AAAAHsOiEA","375":"b:f9T9/6+33t7//4NFAAaIA2AAAAHsOiEA","385":"b:f9T9/6+33t7//4NFAAaIA2AAAAHsOiEA","389":"b:f9T9/6+33t7//4NFAAaIA2AAAAHsOiEA","399":"b:f9T9/6+33t7//4NFAAaIA2AAAAHsOiEA","408":"b:f9T9/6+33t7//4NFAAaIA2AAAAHsOiEA","420":"b:f9T9/6+33t7//4NFAAaIA2AAAAHsOiEA","444":"b:f9T9/6+33t7//4NFAAaIA2AAAAHsOiEA","455":"b:f9T9/6+33t7//4NFAAaIA2AAAAHsOiEA","456":"b:f9T9/6+33t7//4NFAAaIA2AAAAHsOiEA","466":"b:f9T9/6+33t7//4NFAAaIA2AAAAHsOiEA","468":"b:f9T9/6+33t7//4NFAAaIA2AAAAHsOiEA","473":"b:f9T9/6+33t7//4NFAAaIA2AAAAHsOiEA","479":"b:f9T9/6+33t7//4NFAAaIA2AAAAHsOiEA","483":"b:f9T9/6+33t7//4NFAAaIA2AAAAHsOiEA","493":"b:f9T9/6+33t7//4NFAAaIA2AAAAHsOiEA"},"packages/cli/src/services/tag.service.ts":{"17":"b:X5fd36W1nJ39/wMIAAAJAAAAQPQAEAAA","33":"b:X5fd36W1nJ39/wMIAAAJAAAAQPQAEAAA","43":"b:X5fd36W1nJ39/wMIAAAJAAAAQPQAEAAA","48":"b:X5fd36W1nJ39/wMIAAAJAAAAQPQAEAAA","54":"b:X5fd36W1nJ39/wMIAAAJAAAAQPQAEAAA","68":"b:X5fd36W1nJ39/wMIAAAJAAAAQPQAEAAA","78":"b:X5fd36W1nJ39/wMIAAAJAAAAQPQAEAAA","85":"b:X5fd36W1nJ39/wMIAAAJAAAAQPQAEAAA","104":"b:X5fd36W1nJ39/wMIAAAJAAAAQPQAEAAA","117":"b:X5fd36W1nJ39/wMIAAAJAAAAQPQAEAAA","127":"b:X5fd36W1nJ39/wMIAAAJAAAAQPQAEAAA","141":"b:X5fd36W1nJ39/wMIAAAJAAAAQPQAEAAA","142":"b:X5fd36W1nJ39/wMIAAAJAAAAQPQAEAAA","147":"b:X5fd36W1nJ39/wMIAAAJAAAAQPQAEAAA","154":"b:X5fd36W1nJ39/wMIAAAJAAAAQPQAEAAA","159":"b:X5fd36W1nJ39/wMIAAAJAAAAQPQAEAAA","174":"b:X5fd36W1nJ39/wMIAAAJAAAAQPQAEAAA","179":"b:X5fd36W1nJ39/wMIAAAJAAAAQPQAEAAA"},"packages/cli/src/services/user.service.ts":{"45":"b:f93936elnN7//xMEAAK4AwAAgAH8+U8B","60":"b:f93936elnN7//xMEAAK4AwAAgAH8+U8B","70":"b:f93936elnN7//xMEAAK4AwAAgAH8+U8B","74":"b:f93936elnN7//xMEAAK4AwAAgAH8+U8B","86":"b:f93936elnN7//xMEAAK4AwAAgAH8+U8B","98":"b:f93936elnN7//xMEAAK4AwAAgAH8+U8B","109":"b:f93936elnN7//xMEAAK4AwAAgAH8+U8B","115":"b:f93936elnN7//xMEAAK4AwAAgAH8+U8B","120":"b:f93936elnN7//xMEAAK4AwAAgAH8+U8B","163":"b:f93936elnN7//xMEAAK4AwAAgAH8+U8B","166":"b:f93936elnN7//xMEAAK4AwAAgAH8+U8B","167":"b:f93936elnN7//xMEAAK4AwAAgAH8+U8B","172":"b:f93936elnN7//xMEAAK4AwAAgAH8+U8B","180":"b:f93936elnN7//xMEAAK4AwAAgAH8+U8B","190":"b:f93936elnN7//xMEAAK4AwAAgAH8+U8B","262":"b:f93936elnN7//xMEAAK4AwAAgAH8+U8B","263":"b:f93936elnN7//xMEAAK4AwAAgAH8+U8B","267":"b:f93936elnN7//xMEAAK4AwAAgAH8+U8B","269":"b:f93936elnN7//xMEAAK4AwAAgAH8+U8B","271":"b:f93936elnN7//xMEAAK4AwAAgAH8+U8B","283":"b:f93936elnN7//xMEAAK4AwAAgAH8+U8B","289":"b:f93936elnN7//xMEAAK4AwAAgAH8+U8B","291":"b:f93936elnN7//xMEAAK4AwAAgAH8+U8B","311":"b:f93936elnN7//xMEAAK4AwAAgAH8+U8B","319":"b:f93936elnN7//xMEAAK4AwAAgAH8+U8B","322":"b:f93936elnN7//xMEAAK4AwAAgAH8+U8B","338":"b:f93936elnN7//xMEAAK4AwAAgAH8+U8B","341":"b:f93936elnN7//xMEAAK4AwAAgAH8+U8B","431":"b:f93936elnN7//xMEAAK4AwAAgAH8+U8B","457":"b:f93936elnN7//xMEAAK4AwAAgAH8+U8B"},"packages/cli/src/services/workflow-statistics.service.ts":{"62":"b:f5jlwyf3sBtDAAQAoAAACAAAhAH0A+AB","86":"b:f5jlwyf3sBtDAAQAoAAACAAAhAH0A+AB","101":"b:f5jlwyf3sBtDAAQAoAAACAAAhAH0A+AB","116":"b:f5jlwyf3sBtDAAQAoAAACAAAhAH0A+AB","120":"b:f5jlwyf3sBtDAAQAoAAACAAAhAH0A+AB","125":"b:f5jlwyf3sBtDAAQAoAAACAAAhAH0A+AB","185":"b:f5jlwyf3sBtDAAQAoAAACAAAhAH0A+AB","205":"b:f5jlwyf3sBtDAAQAoAAACAAAhAH0A+AB","232":"b:f5jlwyf3sBtDAAQAoAAACAAAhAH0A+AB","276":"b:f5jlwyf3sBtDAAQAoAAACAAAhAH0A+AB","307":"b:f5jlwyf3sBtDAAQAoAAACAAAhAH0A+AB"},"packages/cli/src/sso.ee/sso-helpers.ts":{"14":"b:f4T9z6GhHNqT/xMEAAKAAQAAAADs+A8A","28":"b:f4T9z6GhHNqT/xMEAAKAAQAAAADs+A8A","47":"b:f4T9z6GhHNqT/xMEAAKAAQAAAADs+A8A","51":"b:f4T9z6GhHNqT/xMEAAKAAQAAAADs+A8A","58":"b:f4T9z6GhHNqT/xMEAAKAAQAAAADs+A8A","62":"b:f4T9z6GhHNqT/xMEAAKAAQAAAADs+A8A","66":"b:f4T9z6GhHNqT/xMEAAKAAQAAAADs+A8A","70":"b:f4T9z6GhHNqT/xMEAAKAAQAAAADs+A8A","74":"b:f4T9z6GhHNqT/xMEAAKAAQAAAADs+A8A","78":"b:f4T9z6GhHNqT/xMEAAKAAQAAAADs+A8A","82":"b:f4T9z6GhHNqT/xMEAAKAAQAAAADs+A8A","90":"b:f4T9z6GhHNqT/xMEAAKAAQAAAADs+A8A","94":"b:f4T9z6GhHNqT/xMEAAKAAQAAAADs+A8A","98":"b:f4T9z6GhHNqT/xMEAAKAAQAAAADs+A8A"},"packages/cli/src/task-runners/default-task-runner-disconnect-analyzer.ts":{"18":"b:RQohAAFQAAABAAAAAAAAAICAAUAAAQAA","22":"b:RQohAAFQAAABAAAAAAAAAICAAUAAAQAA"},"packages/cli/src/task-runners/errors/task-runner-disconnected-error.ts":{"7":"b:RQohAAFQAAABAAAAAAAAAICAAUAAAQAA","27":"b:RQohAAFQAAABAAAAAAAAAICAAUAAAQAA"},"packages/cli/src/task-runners/task-broker/errors/task-deferred.error.ts":{"4":"b:RQohAAFQAAABAAAAAAAAAICAAUAAAQAA"},"packages/cli/src/task-runners/task-broker/errors/task-runner-accept-timeout.error.ts":{"4":"b:RQghAAFQAAABAAAAAAAAAICAAUAAAQAA"},"packages/cli/src/task-runners/task-broker/task-broker-ws-server.ts":{"21":"b:X4s9/K3THJzd/BuoAAAAEICAAUCAAQAA","37":"b:X4s9/K3THJzd/BuoAAAAEICAAUCAAQAA","46":"b:X4s9/K3THJzd/BuoAAAAEICAAUCAAQAA","50":"b:X4s9/K3THJzd/BuoAAAAEICAAUCAAQAA","57":"b:X4s9/K3THJzd/BuoAAAAEICAAUCAAQAA","74":"b:X4s9/K3THJzd/BuoAAAAEICAAUCAAQAA","83":"b:X4s9/K3THJzd/BuoAAAAEICAAUCAAQAA","87":"b:X4s9/K3THJzd/BuoAAAAEICAAUCAAQAA","91":"b:X4s9/K3THJzd/BuoAAAAEICAAUCAAQAA","95":"b:X4s9/K3THJzd/BuoAAAAEICAAUCAAQAA","101":"b:X4s9/K3THJzd/BuoAAAAEICAAUCAAQAA","146":"b:X4s9/K3THJzd/BuoAAAAEICAAUCAAQAA","158":"b:X4s9/K3THJzd/BuoAAAAEICAAUCAAQAA","177":"b:X4s9/K3THJzd/BuoAAAAEICAAUCAAQAA","181":"b:X4s9/K3THJzd/BuoAAAAEICAAUCAAQAA","186":"b:X4s9/K3THJzd/BuoAAAAEICAAUCAAQAA","192":"b:X4s9/K3THJzd/BuoAAAAEICAAUCAAQAA"},"packages/cli/src/task-runners/task-broker/task-broker.service.ts":{"97":"b:RQohAAFQAAAhAAAAAAAAAICAAXAABSAA","108":"b:RQohAAFQAAAhAAAAAAAAAICAAXAABSAA","109":"b:RQohAAFQAAAhAAAAAAAAAICAAXAABSAA","114":"b:RQohAAFQAAAhAAAAAAAAAICAAXAABSAA","115":"b:RQohAAFQAAAhAAAAAAAAAICAAXAABSAA","131":"b:RQohAAFQAAAhAAAAAAAAAICAAXAABSAA","142":"b:RQohAAFQAAAhAAAAAAAAAICAAXAABSAA","147":"b:RQohAAFQAAAhAAAAAAAAAICAAXAABSAA","169":"b:RQohAAFQAAAhAAAAAAAAAICAAXAABSAA","173":"b:RQohAAFQAAAhAAAAAAAAAICAAXAABSAA","177":"b:RQohAAFQAAAhAAAAAAAAAICAAXAABSAA","181":"b:RQohAAFQAAAhAAAAAAAAAICAAXAABSAA","185":"b:RQohAAFQAAAhAAAAAAAAAICAAXAABSAA","233":"b:RQohAAFQAAAhAAAAAAAAAICAAXAABSAA","252":"b:RQohAAFQAAAhAAAAAAAAAICAAXAABSAA","260":"b:RQohAAFQAAAhAAAAAAAAAICAAXAABSAA","268":"b:RQohAAFQAAAhAAAAAAAAAICAAXAABSAA","276":"b:RQohAAFQAAAhAAAAAAAAAICAAXAABSAA","293":"b:RQohAAFQAAAhAAAAAAAAAICAAXAABSAA","310":"b:RQohAAFQAAAhAAAAAAAAAICAAXAABSAA","327":"b:RQohAAFQAAAhAAAAAAAAAICAAXAABSAA","364":"b:RQohAAFQAAAhAAAAAAAAAICAAXAABSAA","381":"b:RQohAAFQAAAhAAAAAAAAAICAAXAABSAA","393":"b:RQohAAFQAAAhAAAAAAAAAICAAXAABSAA","409":"b:RQohAAFQAAAhAAAAAAAAAICAAXAABSAA","420":"b:RQohAAFQAAAhAAAAAAAAAICAAXAABSAA","428":"b:RQohAAFQAAAhAAAAAAAAAICAAXAABSAA","442":"b:RQohAAFQAAAhAAAAAAAAAICAAXAABSAA","456":"b:RQohAAFQAAAhAAAAAAAAAICAAXAABSAA","472":"b:RQohAAFQAAAhAAAAAAAAAICAAXAABSAA","479":"b:RQohAAFQAAAhAAAAAAAAAICAAXAABSAA","490":"b:RQohAAFQAAAhAAAAAAAAAICAAXAABSAA","516":"b:RQohAAFQAAAhAAAAAAAAAICAAXAABSAA","530":"b:RQohAAFQAAAhAAAAAAAAAICAAXAABSAA","544":"b:RQohAAFQAAAhAAAAAAAAAICAAXAABSAA","548":"b:RQohAAFQAAAhAAAAAAAAAICAAXAABSAA","552":"b:RQohAAFQAAAhAAAAAAAAAICAAXAABSAA","594":"b:RQohAAFQAAAhAAAAAAAAAICAAXAABSAA","606":"b:RQohAAFQAAAhAAAAAAAAAICAAXAABSAA","615":"b:RQohAAFQAAAhAAAAAAAAAICAAXAABSAA","648":"b:RQohAAFQAAAhAAAAAAAAAICAAXAABSAA","655":"b:RQohAAFQAAAhAAAAAAAAAICAAXAABSAA","670":"b:RQohAAFQAAAhAAAAAAAAAICAAXAABSAA","674":"b:RQohAAFQAAAhAAAAAAAAAICAAXAABSAA","681":"b:RQohAAFQAAAhAAAAAAAAAICAAXAABSAA","695":"b:RQohAAFQAAAhAAAAAAAAAICAAXAABSAA","700":"b:RQohAAFQAAAhAAAAAAAAAICAAXAABSAA","704":"b:RQohAAFQAAAhAAAAAAAAAICAAXAABSAA","712":"b:RQohAAFQAAAhAAAAAAAAAICAAXAABSAA","716":"b:RQohAAFQAAAhAAAAAAAAAICAAXAABSAA","720":"b:RQohAAFQAAAhAAAAAAAAAICAAXAABSAA","724":"b:RQohAAFQAAAhAAAAAAAAAICAAXAABSAA","728":"b:RQohAAFQAAAhAAAAAAAAAICAAXAABSAA","732":"b:RQohAAFQAAAhAAAAAAAAAICAAXAABSAA","736":"b:RQohAAFQAAAhAAAAAAAAAICAAXAABSAA","740":"b:RQohAAFQAAAhAAAAAAAAAICAAXAABSAA","744":"b:RQohAAFQAAAhAAAAAAAAAICAAXAABSAA","748":"b:RQohAAFQAAAhAAAAAAAAAICAAXAABSAA","752":"b:RQohAAFQAAAhAAAAAAAAAICAAXAABSAA"},"packages/cli/src/task-runners/task-managers/data-request-response-builder.ts":{"17":"b:RQohAAFQAAABAAAAAAAAAICAAUAAAQAA","37":"b:RQohAAFQAAABAAAAAAAAAICAAUAAAQAA","57":"b:RQohAAFQAAABAAAAAAAAAICAAUAAAQAA","70":"b:RQohAAFQAAABAAAAAAAAAICAAUAAAQAA"},"packages/cli/src/task-runners/task-managers/data-request-response-stripper.ts":{"18":"b:RQohAAFQAAABAAAAAAAAAIiAAUAAAQAE","32":"b:RQohAAFQAAABAAAAAAAAAIiAAUAAAQAE","43":"b:RQohAAFQAAABAAAAAAAAAIiAAUAAAQAE","64":"b:RQohAAFQAAABAAAAAAAAAIiAAUAAAQAE","68":"b:RQohAAFQAAABAAAAAAAAAIiAAUAAAQAE","72":"b:RQohAAFQAAABAAAAAAAAAIiAAUAAAQAE","86":"b:RQohAAFQAAABAAAAAAAAAIiAAUAAAQAE","94":"b:RQohAAFQAAABAAAAAAAAAIiAAUAAAQAE","118":"b:RQohAAFQAAABAAAAAAAAAIiAAUAAAQAE","138":"b:RQohAAFQAAABAAAAAAAAAIiAAUAAAQAE"},"packages/cli/src/task-runners/task-managers/local-task-requester.ts":{"19":"b:RQohAAFQAAABAAAAAAAAAICAAUAAAQAA","30":"b:RQohAAFQAAABAAAAAAAAAICAAUAAAQAA","39":"b:RQohAAFQAAABAAAAAAAAAICAAUAAAQAA","40":"b:RQohAAFQAAABAAAAAAAAAICAAUAAAQAA"},"packages/cli/src/task-runners/task-managers/task-requester.ts":{"70":"b:RQohAAFQAAABAAAAAAAAAICAAXAAASAA","78":"b:RQohAAFQAAABAAAAAAAAAICAAXAAASAA","82":"b:RQohAAFQAAABAAAAAAAAAICAAXAAASAA","87":"b:RQohAAFQAAABAAAAAAAAAICAAXAAASAA","135":"b:RQohAAFQAAABAAAAAAAAAICAAXAAASAA","171":"b:RQohAAFQAAABAAAAAAAAAICAAXAAASAA","195":"b:RQohAAFQAAABAAAAAAAAAICAAXAAASAA","217":"b:RQohAAFQAAABAAAAAAAAAICAAXAAASAA","223":"b:RQohAAFQAAABAAAAAAAAAICAAXAAASAA","227":"b:RQohAAFQAAABAAAAAAAAAICAAXAAASAA","246":"b:RQohAAFQAAABAAAAAAAAAICAAXAAASAA","258":"b:RQohAAFQAAABAAAAAAAAAICAAXAAASAA","260":"b:RQohAAFQAAABAAAAAAAAAICAAXAAASAA","286":"b:RQohAAFQAAABAAAAAAAAAICAAXAAASAA","300":"b:RQohAAFQAAABAAAAAAAAAICAAXAAASAA","324":"b:RQohAAFQAAABAAAAAAAAAICAAXAAASAA","332":"b:RQohAAFQAAABAAAAAAAAAICAAXAAASAA","340":"b:RQohAAFQAAABAAAAAAAAAICAAXAAASAA","348":"b:RQohAAFQAAABAAAAAAAAAICAAXAAASAA","374":"b:RQohAAFQAAABAAAAAAAAAICAAXAAASAA","389":"b:RQohAAFQAAABAAAAAAAAAICAAXAAASAA"},"packages/cli/src/utils/get-item-count-by-connection-type.ts":{"4":"b:R5DFwCX/oAEBAAAAAAAAAABAAAAAAAAA","15":"b:R5DFwCX/oAEBAAAAAAAAAABAAAAAAAAA"},"packages/cli/src/utils/strapi-utils.ts":{"54":"b:B4Xd0AWk2IDdYBAAAAAAAAAAAAAAAAAA","66":"b:B4Xd0AWk2IDdYBAAAAAAAAAAAAAAAAAA","76":"b:B4Xd0AWk2IDdYBAAAAAAAAAAAAAAAAAA","129":"b:B4Xd0AWk2IDdYBAAAAAAAAAAAAAAAAAA"},"packages/cli/src/utils.ts":{"10":"b:f93t/633/t9/gIdhoBGACaCAnAHuAykA","19":"b:f93t/633/t9/gIdhoBGACaCAnAHuAykA","28":"b:f93t/633/t9/gIdhoBGACaCAnAHuAykA","32":"b:f93t/633/t9/gIdhoBGACaCAnAHuAykA","33":"b:f93t/633/t9/gIdhoBGACaCAnAHuAykA","35":"b:f93t/633/t9/gIdhoBGACaCAnAHuAykA","40":"b:f93t/633/t9/gIdhoBGACaCAnAHuAykA","60":"b:f93t/633/t9/gIdhoBGACaCAnAHuAykA","64":"b:f93t/633/t9/gIdhoBGACaCAnAHuAykA","66":"b:f93t/633/t9/gIdhoBGACaCAnAHuAykA","71":"b:f93t/633/t9/gIdhoBGACaCAnAHuAykA","76":"b:f93t/633/t9/gIdhoBGACaCAnAHuAykA","82":"b:f93t/633/t9/gIdhoBGACaCAnAHuAykA","95":"b:f93t/633/t9/gIdhoBGACaCAnAHuAykA","97":"b:f93t/633/t9/gIdhoBGACaCAnAHuAykA","102":"b:f93t/633/t9/gIdhoBGACaCAnAHuAykA","122":"b:f93t/633/t9/gIdhoBGACaCAnAHuAykA","129":"b:f93t/633/t9/gIdhoBGACaCAnAHuAykA","149":"b:f93t/633/t9/gIdhoBGACaCAnAHuAykA","161":"b:f93t/633/t9/gIdhoBGACaCAnAHuAykA","165":"b:f93t/633/t9/gIdhoBGACaCAnAHuAykA","173":"b:f93t/633/t9/gIdhoBGACaCAnAHuAykA"},"packages/cli/src/webhooks/node-type-matcher.ts":{"5":"b:RgjAQwCAEBACAAQAgAAACAAABAAEAwAA"},"packages/cli/src/webhooks/test-webhook-registrations.service.ts":{"28":"b:RgCAQAAAAAAAAAQAgCAACIAABAACAAAA","40":"b:RgCAQAAAAAAAAAQAgCAACIAABAACAAAA","47":"b:RgCAQAAAAAAAAAQAgCAACIAABAACAAAA","76":"b:RgCAQAAAAAAAAAQAgCAACIAABAACAAAA","85":"b:RgCAQAAAAAAAAAQAgCAACIAABAACAAAA","90":"b:RgCAQAAAAAAAAAQAgCAACIAABAACAAAA","98":"b:RgCAQAAAAAAAAAQAgCAACIAABAACAAAA","106":"b:RgCAQAAAAAAAAAQAgCAACIAABAACAAAA","116":"b:RgCAQAAAAAAAAAQAgCAACIAABAACAAAA"},"packages/cli/src/webhooks/test-webhooks.ts":{"56":"b:R5DlwAX/sCkBAAQAgCAACIAABAACAAAA","72":"b:R5DlwAX/sCkBAAQAgCAACIAABAACAAAA","102":"b:R5DlwAX/sCkBAAQAgCAACIAABAACAAAA","150":"b:R5DlwAX/sCkBAAQAgCAACIAABAACAAAA","164":"b:R5DlwAX/sCkBAAQAgCAACIAABAACAAAA","222":"b:R5DlwAX/sCkBAAQAgCAACIAABAACAAAA","245":"b:R5DlwAX/sCkBAAQAgCAACIAABAACAAAA","251":"b:R5DlwAX/sCkBAAQAgCAACIAABAACAAAA","274":"b:R5DlwAX/sCkBAAQAgCAACIAABAACAAAA","278":"b:R5DlwAX/sCkBAAQAgCAACIAABAACAAAA","285":"b:R5DlwAX/sCkBAAQAgCAACIAABAACAAAA","288":"b:R5DlwAX/sCkBAAQAgCAACIAABAACAAAA","301":"b:R5DlwAX/sCkBAAQAgCAACIAABAACAAAA","314":"b:R5DlwAX/sCkBAAQAgCAACIAABAACAAAA","360":"b:R5DlwAX/sCkBAAQAgCAACIAABAACAAAA","363":"b:R5DlwAX/sCkBAAQAgCAACIAABAACAAAA","371":"b:R5DlwAX/sCkBAAQAgCAACIAABAACAAAA","382":"b:R5DlwAX/sCkBAAQAgCAACIAABAACAAAA","465":"b:R5DlwAX/sCkBAAQAgCAACIAABAACAAAA","493":"b:R5DlwAX/sCkBAAQAgCAACIAABAACAAAA","500":"b:R5DlwAX/sCkBAAQAgCAACIAABAACAAAA","514":"b:R5DlwAX/sCkBAAQAgCAACIAABAACAAAA","522":"b:R5DlwAX/sCkBAAQAgCAACIAABAACAAAA","523":"b:R5DlwAX/sCkBAAQAgCAACIAABAACAAAA","532":"b:R5DlwAX/sCkBAAQAgCAACIAABAACAAAA","544":"b:R5DlwAX/sCkBAAQAgCAACIAABAACAAAA","551":"b:R5DlwAX/sCkBAAQAgCAACIAABAACAAAA","587":"b:R5DlwAX/sCkBAAQAgCAACIAABAACAAAA"},"packages/cli/src/webhooks/webhook-execution-context.ts":{"17":"b:RgjAQwCAEBACAAQAoAAACAAABCAEAyAA","28":"b:RgjAQwCAEBACAAQAoAAACAAABCAEAyAA","46":"b:RgjAQwCAEBACAAQAoAAACAAABCAEAyAA"},"packages/cli/src/webhooks/webhook-helpers.ts":{"99":"b:R5rlwwf3sBsDAAQAoAAACAAABADkAwAA","116":"b:R5rlwwf3sBsDAAQAoAAACAAABADkAwAA","128":"b:R5rlwwf3sBsDAAQAoAAACAAABADkAwAA","137":"b:R5rlwwf3sBsDAAQAoAAACAAABADkAwAA","147":"b:R5rlwwf3sBsDAAQAoAAACAAABADkAwAA","187":"b:R5rlwwf3sBsDAAQAoAAACAAABADkAwAA","205":"b:R5rlwwf3sBsDAAQAoAAACAAABADkAwAA","276":"b:R5rlwwf3sBsDAAQAoAAACAAABADkAwAA","312":"b:R5rlwwf3sBsDAAQAoAAACAAABADkAwAA","321":"b:R5rlwwf3sBsDAAQAoAAACAAABADkAwAA","350":"b:R5rlwwf3sBsDAAQAoAAACAAABADkAwAA","352":"b:R5rlwwf3sBsDAAQAoAAACAAABADkAwAA","362":"b:R5rlwwf3sBsDAAQAoAAACAAABADkAwAA","430":"b:R5rlwwf3sBsDAAQAoAAACAAABADkAwAA","516":"b:R5rlwwf3sBsDAAQAoAAACAAABADkAwAA","526":"b:R5rlwwf3sBsDAAQAoAAACAAABADkAwAA","538":"b:R5rlwwf3sBsDAAQAoAAACAAABADkAwAA","559":"b:R5rlwwf3sBsDAAQAoAAACAAABADkAwAA","573":"b:R5rlwwf3sBsDAAQAoAAACAAABADkAwAA","885":"b:R5rlwwf3sBsDAAQAoAAACAAABADkAwAA","918":"b:R5rlwwf3sBsDAAQAoAAACAAABADkAwAA","1002":"b:R5rlwwf3sBsDAAQAoAAACAAABADkAwAA","1037":"b:R5rlwwf3sBsDAAQAoAAACAAABADkAwAA","1121":"b:R5rlwwf3sBsDAAQAoAAACAAABADkAwAA","1172":"b:R5rlwwf3sBsDAAQAoAAACAAABADkAwAA","1197":"b:R5rlwwf3sBsDAAQAoAAACAAABADkAwAA"},"packages/cli/src/webhooks/webhook-last-node-response-extractor.ts":{"30":[82,1,2,168,103,123,6,146],"72":[82,1,2,168,103,123,6,146],"117":[82,1,2,168,103,123,6,146],"184":[82,1,2,168,103,123,6,146]},"packages/cli/src/webhooks/webhook-request-handler.ts":{"34":"b:RgjAQwCAEBACAAQAoCAACAAABCAEAyAA","43":"b:RgjAQwCAEBACAAQAoCAACAAABCAEAyAA","96":"b:RgjAQwCAEBACAAQAoCAACAAABCAEAyAA","112":"b:RgjAQwCAEBACAAQAoCAACAAABCAEAyAA","121":"b:RgjAQwCAEBACAAQAoCAACAAABCAEAyAA","124":"b:RgjAQwCAEBACAAQAoCAACAAABCAEAyAA","137":"b:RgjAQwCAEBACAAQAoCAACAAABCAEAyAA","143":"b:RgjAQwCAEBACAAQAoCAACAAABCAEAyAA","152":"b:RgjAQwCAEBACAAQAoCAACAAABCAEAyAA","182":"b:RgjAQwCAEBACAAQAoCAACAAABCAEAyAA","236":"b:RgjAQwCAEBACAAQAoCAACAAABCAEAyAA","244":"b:RgjAQwCAEBACAAQAoCAACAAABCAEAyAA","283":"b:RgjAQwCAEBACAAQAoCAACAAABCAEAyAA","290":"b:RgjAQwCAEBACAAQAoCAACAAABCAEAyAA"},"packages/cli/src/webhooks/webhook-response-headers.ts":{"22":"b:RgjAQwCAEBACAAQAgAAACAAABCAEAwAA","26":"b:RgjAQwCAEBACAAQAgAAACAAABCAEAwAA","33":"b:RgjAQwCAEBACAAQAgAAACAAABCAEAwAA","50":"b:RgjAQwCAEBACAAQAgAAACAAABCAEAwAA","57":"b:RgjAQwCAEBACAAQAgAAACAAABCAEAwAA","66":"b:RgjAQwCAEBACAAQAgAAACAAABCAEAwAA","74":"b:RgjAQwCAEBACAAQAgAAACAAABCAEAwAA"},"packages/cli/src/webhooks/webhook-response.ts":{"37":"b:RgjAQwCAEBACAAQAgAAACAAABCAEAwAA","41":"b:RgjAQwCAEBACAAQAgAAACAAABCAEAwAA","45":"b:RgjAQwCAEBACAAQAgAAACAAABCAEAwAA","49":"b:RgjAQwCAEBACAAQAgAAACAAABCAEAwAA","53":"b:RgjAQwCAEBACAAQAgAAACAAABCAEAwAA","59":"b:RgjAQwCAEBACAAQAgAAACAAABCAEAwAA","72":"b:RgjAQwCAEBACAAQAgAAACAAABCAEAwAA"},"packages/cli/src/webhooks/webhook.service.ts":{"33":"b:R5rlwwf3sBsLAAQAgAAACoAABCr1A+AB","40":"b:R5rlwwf3sBsLAAQAgAAACoAABCr1A+AB","45":"b:R5rlwwf3sBsLAAQAgAAACoAABCr1A+AB","48":"b:R5rlwwf3sBsLAAQAgAAACoAABCr1A+AB","52":"b:R5rlwwf3sBsLAAQAgAAACoAABCr1A+AB","64":"b:R5rlwwf3sBsLAAQAgAAACoAABCr1A+AB","82":"b:R5rlwwf3sBsLAAQAgAAACoAABCr1A+AB","95":"b:R5rlwwf3sBsLAAQAgAAACoAABCr1A+AB","104":"b:R5rlwwf3sBsLAAQAgAAACoAABCr1A+AB","112":"b:R5rlwwf3sBsLAAQAgAAACoAABCr1A+AB","129":"b:R5rlwwf3sBsLAAQAgAAACoAABCr1A+AB","130":"b:R5rlwwf3sBsLAAQAgAAACoAABCr1A+AB","148":"b:R5rlwwf3sBsLAAQAgAAACoAABCr1A+AB","152":"b:R5rlwwf3sBsLAAQAgAAACoAABCr1A+AB","189":"b:R5rlwwf3sBsLAAQAgAAACoAABCr1A+AB","194":"b:R5rlwwf3sBsLAAQAgAAACoAABCr1A+AB","198":"b:R5rlwwf3sBsLAAQAgAAACoAABCr1A+AB","205":"b:R5rlwwf3sBsLAAQAgAAACoAABCr1A+AB","209":"b:R5rlwwf3sBsLAAQAgAAACoAABCr1A+AB","214":"b:R5rlwwf3sBsLAAQAgAAACoAABCr1A+AB","215":"b:R5rlwwf3sBsLAAQAgAAACoAABCr1A+AB","220":"b:R5rlwwf3sBsLAAQAgAAACoAABCr1A+AB","224":"b:R5rlwwf3sBsLAAQAgAAACoAABCr1A+AB","235":"b:R5rlwwf3sBsLAAQAgAAACoAABCr1A+AB","246":"b:R5rlwwf3sBsLAAQAgAAACoAABCr1A+AB","248":"b:R5rlwwf3sBsLAAQAgAAACoAABCr1A+AB","255":"b:R5rlwwf3sBsLAAQAgAAACoAABCr1A+AB","352":"b:R5rlwwf3sBsLAAQAgAAACoAABCr1A+AB","369":"b:R5rlwwf3sBsLAAQAgAAACoAABCr1A+AB","383":"b:R5rlwwf3sBsLAAQAgAAACoAABCr1A+AB","432":"b:R5rlwwf3sBsLAAQAgAAACoAABCr1A+AB","434":"b:R5rlwwf3sBsLAAQAgAAACoAABCr1A+AB","438":"b:R5rlwwf3sBsLAAQAgAAACoAABCr1A+AB","443":"b:R5rlwwf3sBsLAAQAgAAACoAABCr1A+AB","462":"b:R5rlwwf3sBsLAAQAgAAACoAABCr1A+AB","471":"b:R5rlwwf3sBsLAAQAgAAACoAABCr1A+AB","503":"b:R5rlwwf3sBsLAAQAgAAACoAABCr1A+AB","534":"b:R5rlwwf3sBsLAAQAgAAACoAABCr1A+AB"},"packages/cli/src/workflow-execute-additional-data.ts":{"75":"b:f5rl0yX/uBsTAAAAIAAAAMCAgWHsAaAA","120":"b:f5rl0yX/uBsTAAAAIAAAAMCAgWHsAaAA","166":"b:f5rl0yX/uBsTAAAAIAAAAMCAgWHsAaAA","184":"b:f5rl0yX/uBsTAAAAIAAAAMCAgWHsAaAA","242":"b:f5rl0yX/uBsTAAAAIAAAAMCAgWHsAaAA","275":"b:f5rl0yX/uBsTAAAAIAAAAMCAgWHsAaAA","292":"b:f5rl0yX/uBsTAAAAIAAAAMCAgWHsAaAA","363":"b:f5rl0yX/uBsTAAAAIAAAAMCAgWHsAaAA","442":"b:f5rl0yX/uBsTAAAAIAAAAMCAgWHsAaAA","448":"b:f5rl0yX/uBsTAAAAIAAAAMCAgWHsAaAA","462":"b:f5rl0yX/uBsTAAAAIAAAAMCAgWHsAaAA","463":"b:f5rl0yX/uBsTAAAAIAAAAMCAgWHsAaAA","477":"b:f5rl0yX/uBsTAAAAIAAAAMCAgWHsAaAA","496":"b:f5rl0yX/uBsTAAAAIAAAAMCAgWHsAaAA","697":"b:f5rl0yX/uBsTAAAAIAAAAMCAgWHsAaAA","707":"b:f5rl0yX/uBsTAAAAIAAAAMCAgWHsAaAA","736":"b:f5rl0yX/uBsTAAAAIAAAAMCAgWHsAaAA","784":"b:f5rl0yX/uBsTAAAAIAAAAMCAgWHsAaAA","790":"b:f5rl0yX/uBsTAAAAIAAAAMCAgWHsAaAA","800":"b:f5rl0yX/uBsTAAAAIAAAAMCAgWHsAaAA","837":"b:f5rl0yX/uBsTAAAAIAAAAMCAgWHsAaAA","840":"b:f5rl0yX/uBsTAAAAIAAAAMCAgWHsAaAA","843":"b:f5rl0yX/uBsTAAAAIAAAAMCAgWHsAaAA"},"packages/cli/src/workflow-helpers.ts":{"41":"b:f9/906e3/JtXAAQAoAQACkEShCHsA4EA","68":"b:f9/906e3/JtXAAQAoAQACkEShCHsA4EA","74":"b:f9/906e3/JtXAAQAoAQACkEShCHsA4EA","80":"b:f9/906e3/JtXAAQAoAQACkEShCHsA4EA","99":"b:f9/906e3/JtXAAQAoAQACkEShCHsA4EA","118":"b:f9/906e3/JtXAAQAoAQACkEShCHsA4EA","122":"b:f9/906e3/JtXAAQAoAQACkEShCHsA4EA","134":"b:f9/906e3/JtXAAQAoAQACkEShCHsA4EA","159":"b:f9/906e3/JtXAAQAoAQACkEShCHsA4EA","160":"b:f9/906e3/JtXAAQAoAQACkEShCHsA4EA","182":"b:f9/906e3/JtXAAQAoAQACkEShCHsA4EA","208":"b:f9/906e3/JtXAAQAoAQACkEShCHsA4EA","246":"b:f9/906e3/JtXAAQAoAQACkEShCHsA4EA","254":"b:f9/906e3/JtXAAQAoAQACkEShCHsA4EA","260":"b:f9/906e3/JtXAAQAoAQACkEShCHsA4EA","283":"b:f9/906e3/JtXAAQAoAQACkEShCHsA4EA","319":"b:f9/906e3/JtXAAQAoAQACkEShCHsA4EA","442":"b:f9/906e3/JtXAAQAoAQACkEShCHsA4EA","463":"b:f9/906e3/JtXAAQAoAQACkEShCHsA4EA","491":"b:f9/906e3/JtXAAQAoAQACkEShCHsA4EA","587":"b:f9/906e3/JtXAAQAoAQACkEShCHsA4EA","609":"b:f9/906e3/JtXAAQAoAQACkEShCHsA4EA"},"packages/cli/src/workflow-hook-context.service.ts":{"9":"b:f5DlwyX3sBsDAAAAAAAAAAAAAADkAwAA","11":"b:f5DlwyX3sBsDAAAAAAAAAAAAAADkAwAA","17":"b:f5DlwyX3sBsDAAAAAAAAAAAAAADkAwAA"},"packages/cli/src/workflow-runner.ts":{"67":"b:f5DlwyX3sBsDAAAAoAAAAAACAADkAwAA","77":"b:f5DlwyX3sBsDAAAAoAAAAAACAADkAwAA","96":"b:f5DlwyX3sBsDAAAAoAAAAAACAADkAwAA","164":"b:f5DlwyX3sBsDAAAAoAAAAAACAADkAwAA","185":"b:f5DlwyX3sBsDAAAAoAAAAAACAADkAwAA","263":"b:f5DlwyX3sBsDAAAAoAAAAAACAADkAwAA","299":"b:f5DlwyX3sBsDAAAAoAAAAAACAADkAwAA","318":"b:f5DlwyX3sBsDAAAAoAAAAAACAADkAwAA","328":"b:f5DlwyX3sBsDAAAAoAAAAAACAADkAwAA","393":"b:f5DlwyX3sBsDAAAAoAAAAAACAADkAwAA","398":"b:f5DlwyX3sBsDAAAAoAAAAAACAADkAwAA","453":"b:f5DlwyX3sBsDAAAAoAAAAAACAADkAwAA","463":"b:f5DlwyX3sBsDAAAAoAAAAAACAADkAwAA","473":"b:f5DlwyX3sBsDAAAAoAAAAAACAADkAwAA","500":"b:f5DlwyX3sBsDAAAAoAAAAAACAADkAwAA","554":"b:f5DlwyX3sBsDAAAAoAAAAAACAADkAwAA","556":"b:f5DlwyX3sBsDAAAAoAAAAAACAADkAwAA","669":"b:f5DlwyX3sBsDAAAAoAAAAAACAADkAwAA","689":"b:f5DlwyX3sBsDAAAAoAAAAAACAADkAwAA"},"packages/cli/src/workflows/utils.ts":{"19":"b:f5T900f//JsXAAAAAAAAAAACAADsAAEA","26":"b:f5T900f//JsXAAAAAAAAAAACAADsAAEA","27":"b:f5T900f//JsXAAAAAAAAAAACAADsAAEA","35":"b:f5T900f//JsXAAAAAAAAAAACAADsAAEA","41":"b:f5T900f//JsXAAAAAAAAAAACAADsAAEA","46":"b:f5T900f//JsXAAAAAAAAAAACAADsAAEA","56":"b:f5T900f//JsXAAAAAAAAAAACAADsAAEA"},"packages/cli/src/workflows/workflow-creation.service.ts":{"43":"b:R5T900e135oXAAAAAAEAAAAAAADsAAEA","64":"b:R5T900e135oXAAAAAAEAAAAAAADsAAEA","184":"b:R5T900e135oXAAAAAAEAAAAAAADsAAEA","279":"b:R5T900e135oXAAAAAAEAAAAAAADsAAEA","291":"b:R5T900e135oXAAAAAAEAAAAAAADsAAEA","296":"b:R5T900e135oXAAAAAAEAAAAAAADsAAEA"},"packages/cli/src/workflows/workflow-entity-mapper.ts":{"22":"b:R5T900e13JoXAAAAAAAAAAAAAADsAAEA","29":"b:R5T900e13JoXAAAAAAAAAAAAAADsAAEA","30":"b:R5T900e13JoXAAAAAAAAAAAAAADsAAEA"},"packages/cli/src/workflows/workflow-execution.service.ts":{"49":"b:f5DlwAX/sAsBAAAA4AAAEIi+ESDgCEAB","67":"b:f5DlwAX/sAsBAAAA4AAAEIi+ESDgCEAB","111":"b:f5DlwAX/sAsBAAAA4AAAEIi+ESDgCEAB","112":"b:f5DlwAX/sAsBAAAA4AAAEIi+ESDgCEAB","123":"b:f5DlwAX/sAsBAAAA4AAAEIi+ESDgCEAB","295":"b:f5DlwAX/sAsBAAAA4AAAEIi+ESDgCEAB","343":"b:f5DlwAX/sAsBAAAA4AAAEIi+ESDgCEAB","388":"b:f5DlwAX/sAsBAAAA4AAAEIi+ESDgCEAB","551":"b:f5DlwAX/sAsBAAAA4AAAEIi+ESDgCEAB","569":"b:f5DlwAX/sAsBAAAA4AAAEIi+ESDgCEAB","574":"b:f5DlwAX/sAsBAAAA4AAAEIi+ESDgCEAB","577":"b:f5DlwAX/sAsBAAAA4AAAEIi+ESDgCEAB","580":"b:f5DlwAX/sAsBAAAA4AAAEIi+ESDgCEAB","583":"b:f5DlwAX/sAsBAAAA4AAAEIi+ESDgCEAB","591":"b:f5DlwAX/sAsBAAAA4AAAEIi+ESDgCEAB","616":"b:f5DlwAX/sAsBAAAA4AAAEIi+ESDgCEAB","630":"b:f5DlwAX/sAsBAAAA4AAAEIi+ESDgCEAB","643":"b:f5DlwAX/sAsBAAAA4AAAEIi+ESDgCEAB","655":"b:f5DlwAX/sAsBAAAA4AAAEIi+ESDgCEAB","662":"b:f5DlwAX/sAsBAAAA4AAAEIi+ESDgCEAB"},"packages/cli/src/workflows/workflow-finder.service.ts":{"13":"b:R4b906e33pp/AJBBAAEAASAAQADuAAEA","20":"b:R4b906e33pp/AJBBAAEAASAAQADuAAEA","53":"b:R4b906e33pp/AJBBAAEAASAAQADuAAEA","72":"b:R4b906e33pp/AJBBAAEAASAAQADuAAEA","95":"b:R4b906e33pp/AJBBAAEAASAAQADuAAEA","141":"b:R4b906e33pp/AJBBAAEAASAAQADuAAEA","152":"b:R4b906e33pp/AJBBAAEAASAAQADuAAEA","155":"b:R4b906e33pp/AJBBAAEAASAAQADuAAEA","161":"b:R4b906e33pp/AJBBAAEAASAAQADuAAEA","164":"b:R4b906e33pp/AJBBAAEAASAAQADuAAEA","190":"b:R4b906e33pp/AJBBAAEAASAAQADuAAEA","217":"b:R4b906e33pp/AJBBAAEAASAAQADuAAEA","228":"b:R4b906e33pp/AJBBAAEAASAAQADuAAEA","236":"b:R4b906e33pp/AJBBAAEAASAAQADuAAEA","267":"b:R4b906e33pp/AJBBAAEAASAAQADuAAEA","270":"b:R4b906e33pp/AJBBAAEAASAAQADuAAEA","274":"b:R4b906e33pp/AJBBAAEAASAAQADuAAEA","283":"b:R4b906e33pp/AJBBAAEAASAAQADuAAEA","295":"b:R4b906e33pp/AJBBAAEAASAAQADuAAEA","298":"b:R4b906e33pp/AJBBAAEAASAAQADuAAEA","315":"b:R4b906e33pp/AJBBAAEAASAAQADuAAEA"},"packages/cli/src/workflows/workflow-history/workflow-history.controller.ts":{"20":"b:RwAYQKC3WIBdABgAACQACCBKAAAAAAAA","23":"b:RwAYQKC3WIBdABgAACQACCBKAAAAAAAA","40":"b:RwAYQKC3WIBdABgAACQACCBKAAAAAAAA","58":"b:RwAYQKC3WIBdABgAACQACCBKAAAAAAAA","79":"b:RwAYQKC3WIBdABgAACQACCBKAAAAAAAA","92":"b:RwAYQKC3WIBdABgAACQACCBKAAAAAAAA"},"packages/cli/src/workflows/workflow-history/workflow-history.service.ts":{"27":"b:R5z906e33JofABgAAAAACiBIAAjtAgEB","36":"b:R5z906e33JofABgAAAAACiBIAAjtAgEB","71":"b:R5z906e33JofABgAAAAACiBIAAjtAgEB","104":"b:R5z906e33JofABgAAAAACiBIAAjtAgEB","126":"b:R5z906e33JofABgAAAAACiBIAAjtAgEB","169":"b:R5z906e33JofABgAAAAACiBIAAjtAgEB","216":"b:R5z906e33JofABgAAAAACiBIAAjtAgEB","264":"b:R5z906e33JofABgAAAAACiBIAAjtAgEB","284":"b:R5z906e33JofABgAAAAACiBIAAjtAgEB","309":"b:R5z906e33JofABgAAAAACiBIAAjtAgEB","312":"b:R5z906e33JofABgAAAAACiBIAAjtAgEB","330":"b:R5z906e33JofABgAAAAACiBIAAjtAgEB"},"packages/cli/src/workflows/workflow-sharing.service.ts":{"21":"b:R5DlwyX3sBMDAAAAAAAAAEAAADDgCuAA","36":"b:R5DlwyX3sBMDAAAAAAAAAEAAADDgCuAA","44":"b:R5DlwyX3sBMDAAAAAAAAAEAAADDgCuAA","69":"b:R5DlwyX3sBMDAAAAAAAAAEAAADDgCuAA","72":"b:R5DlwyX3sBMDAAAAAAAAAEAAADDgCuAA","86":"b:R5DlwyX3sBMDAAAAAAAAAEAAADDgCuAA","89":"b:R5DlwyX3sBMDAAAAAAAAAEAAADDgCuAA","97":"b:R5DlwyX3sBMDAAAAAAAAAEAAADDgCuAA","100":"b:R5DlwyX3sBMDAAAAAAAAAEAAADDgCuAA","106":"b:R5DlwyX3sBMDAAAAAAAAAEAAADDgCuAA","113":"b:R5DlwyX3sBMDAAAAAAAAAEAAADDgCuAA","126":"b:R5DlwyX3sBMDAAAAAAAAAEAAADDgCuAA"},"packages/cli/src/workflows/workflow-static-data.service.ts":{"11":"b:RgjAQwKgEBIKAAQAAAAACgAABAr0A+AB","18":"b:RgjAQwKgEBIKAAQAAAAACgAABAr0A+AB","27":"b:RgjAQwKgEBIKAAQAAAAACgAABAr0A+AB","48":"b:RgjAQwKgEBIKAAQAAAAACgAABAr0A+AB","54":"b:RgjAQwKgEBIKAAQAAAAACgAABAr0A+AB"},"packages/cli/src/workflows/workflow-validation.service.ts":{"50":"b:R53986e33JpXAAAAoAAAAgAACAL8AuEB","51":"b:R53986e33JpXAAAAoAAAAgAACAL8AuEB","56":"b:R53986e33JpXAAAAoAAAAgAACAL8AuEB","67":"b:R53986e33JpXAAAAoAAAAgAACAL8AuEB","129":"b:R53986e33JpXAAAAoAAAAgAACAL8AuEB","130":"b:R53986e33JpXAAAAoAAAAgAACAL8AuEB","153":"b:R53986e33JpXAAAAoAAAAgAACAL8AuEB","185":"b:R53986e33JpXAAAAoAAAAgAACAL8AuEB","246":"b:R53986e33JpXAAAAoAAAAgAACAL8AuEB","267":"b:R53986e33JpXAAAAoAAAAgAACAL8AuEB","306":"b:R53986e33JpXAAAAoAAAAgAACAL8AuEB","343":"b:R53986e33JpXAAAAoAAAAgAACAL8AuEB","371":"b:R53986e33JpXAAAAoAAAAgAACAL8AuEB","399":"b:R53986e33JpXAAAAoAAAAgAACAL8AuEB","440":"b:R53986e33JpXAAAAoAAAAgAACAL8AuEB","445":"b:R53986e33JpXAAAAoAAAAgAACAL8AuEB","479":"b:R53986e33JpXAAAAoAAAAgAACAL8AuEB","498":"b:R53986e33JpXAAAAoAAAAgAACAL8AuEB","523":"b:R53986e33JpXAAAAoAAAAgAACAL8AuEB","531":"b:R53986e33JpXAAAAoAAAAgAACAL8AuEB","552":"b:R53986e33JpXAAAAoAAAAgAACAL8AuEB"},"packages/cli/src/workflows/workflow.service.ee.ts":{"49":"b:R9398+f3/5pfAAAAoAQAI0ASSAHsAAAA","66":"b:R9398+f3/5pfAAAAoAQAI0ASSAHsAAAA","79":"b:R9398+f3/5pfAAAAoAQAI0ASSAHsAAAA","81":"b:R9398+f3/5pfAAAAoAQAI0ASSAHsAAAA","88":"b:R9398+f3/5pfAAAAoAQAI0ASSAHsAAAA","99":"b:R9398+f3/5pfAAAAoAQAI0ASSAHsAAAA","111":"b:R9398+f3/5pfAAAAoAQAI0ASSAHsAAAA","121":"b:R9398+f3/5pfAAAAoAQAI0ASSAHsAAAA","125":"b:R9398+f3/5pfAAAAoAQAI0ASSAHsAAAA","137":"b:R9398+f3/5pfAAAAoAQAI0ASSAHsAAAA","138":"b:R9398+f3/5pfAAAAoAQAI0ASSAHsAAAA","152":"b:R9398+f3/5pfAAAAoAQAI0ASSAHsAAAA","156":"b:R9398+f3/5pfAAAAoAQAI0ASSAHsAAAA","160":"b:R9398+f3/5pfAAAAoAQAI0ASSAHsAAAA","165":"b:R9398+f3/5pfAAAAoAQAI0ASSAHsAAAA","173":"b:R9398+f3/5pfAAAAoAQAI0ASSAHsAAAA","197":"b:R9398+f3/5pfAAAAoAQAI0ASSAHsAAAA","209":"b:R9398+f3/5pfAAAAoAQAI0ASSAHsAAAA","221":"b:R9398+f3/5pfAAAAoAQAI0ASSAHsAAAA","225":"b:R9398+f3/5pfAAAAoAQAI0ASSAHsAAAA","228":"b:R9398+f3/5pfAAAAoAQAI0ASSAHsAAAA","245":"b:R9398+f3/5pfAAAAoAQAI0ASSAHsAAAA","254":"b:R9398+f3/5pfAAAAoAQAI0ASSAHsAAAA","267":"b:R9398+f3/5pfAAAAoAQAI0ASSAHsAAAA","271":"b:R9398+f3/5pfAAAAoAQAI0ASSAHsAAAA","273":"b:R9398+f3/5pfAAAAoAQAI0ASSAHsAAAA","283":"b:R9398+f3/5pfAAAAoAQAI0ASSAHsAAAA","306":"b:R9398+f3/5pfAAAAoAQAI0ASSAHsAAAA","332":"b:R9398+f3/5pfAAAAoAQAI0ASSAHsAAAA","387":"b:R9398+f3/5pfAAAAoAQAI0ASSAHsAAAA","409":"b:R9398+f3/5pfAAAAoAQAI0ASSAHsAAAA","478":"b:R9398+f3/5pfAAAAoAQAI0ASSAHsAAAA","501":"b:R9398+f3/5pfAAAAoAQAI0ASSAHsAAAA","526":"b:R9398+f3/5pfAAAAoAQAI0ASSAHsAAAA","551":"b:R9398+f3/5pfAAAAoAQAI0ASSAHsAAAA","566":"b:R9398+f3/5pfAAAAoAQAI0ASSAHsAAAA","592":"b:R9398+f3/5pfAAAAoAQAI0ASSAHsAAAA","603":"b:R9398+f3/5pfAAAAoAQAI0ASSAHsAAAA","640":"b:R9398+f3/5pfAAAAoAQAI0ASSAHsAAAA","644":"b:R9398+f3/5pfAAAAoAQAI0ASSAHsAAAA","666":"b:R9398+f3/5pfAAAAoAQAI0ASSAHsAAAA","671":"b:R9398+f3/5pfAAAAoAQAI0ASSAHsAAAA","684":"b:R9398+f3/5pfAAAAoAQAI0ASSAHsAAAA","698":"b:R9398+f3/5pfAAAAoAQAI0ASSAHsAAAA","704":"b:R9398+f3/5pfAAAAoAQAI0ASSAHsAAAA","725":"b:R9398+f3/5pfAAAAoAQAI0ASSAHsAAAA"},"packages/cli/src/workflows/workflow.service.ts":{"68":"b:f9393/e339p/gINBIAUiA2ASQAv/DuAB","99":"b:f9393/e339p/gINBIAUiA2ASQAv/DuAB","166":"b:f9393/e339p/gINBIAUiA2ASQAv/DuAB","211":"b:f9393/e339p/gINBIAUiA2ASQAv/DuAB","232":"b:f9393/e339p/gINBIAUiA2ASQAv/DuAB","239":"b:f9393/e339p/gINBIAUiA2ASQAv/DuAB","243":"b:f9393/e339p/gINBIAUiA2ASQAv/DuAB","249":"b:f9393/e339p/gINBIAUiA2ASQAv/DuAB","261":"b:f9393/e339p/gINBIAUiA2ASQAv/DuAB","264":"b:f9393/e339p/gINBIAUiA2ASQAv/DuAB","265":"b:f9393/e339p/gINBIAUiA2ASQAv/DuAB","268":"b:f9393/e339p/gINBIAUiA2ASQAv/DuAB","269":"b:f9393/e339p/gINBIAUiA2ASQAv/DuAB","273":"b:f9393/e339p/gINBIAUiA2ASQAv/DuAB","279":"b:f9393/e339p/gINBIAUiA2ASQAv/DuAB","284":"b:f9393/e339p/gINBIAUiA2ASQAv/DuAB","290":"b:f9393/e339p/gINBIAUiA2ASQAv/DuAB","298":"b:f9393/e339p/gINBIAUiA2ASQAv/DuAB","305":"b:f9393/e339p/gINBIAUiA2ASQAv/DuAB","309":"b:f9393/e339p/gINBIAUiA2ASQAv/DuAB","311":"b:f9393/e339p/gINBIAUiA2ASQAv/DuAB","326":"b:f9393/e339p/gINBIAUiA2ASQAv/DuAB","642":"b:f9393/e339p/gINBIAUiA2ASQAv/DuAB","714":"b:f9393/e339p/gINBIAUiA2ASQAv/DuAB","738":"b:f9393/e339p/gINBIAUiA2ASQAv/DuAB","748":"b:f9393/e339p/gINBIAUiA2ASQAv/DuAB","766":"b:f9393/e339p/gINBIAUiA2ASQAv/DuAB","967":"b:f9393/e339p/gINBIAUiA2ASQAv/DuAB","1055":"b:f9393/e339p/gINBIAUiA2ASQAv/DuAB","1114":"b:f9393/e339p/gINBIAUiA2ASQAv/DuAB","1184":"b:f9393/e339p/gINBIAUiA2ASQAv/DuAB","1222":"b:f9393/e339p/gINBIAUiA2ASQAv/DuAB","1226":"b:f9393/e339p/gINBIAUiA2ASQAv/DuAB","1230":"b:f9393/e339p/gINBIAUiA2ASQAv/DuAB","1234":"b:f9393/e339p/gINBIAUiA2ASQAv/DuAB","1246":"b:f9393/e339p/gINBIAUiA2ASQAv/DuAB","1254":"b:f9393/e339p/gINBIAUiA2ASQAv/DuAB","1263":"b:f9393/e339p/gINBIAUiA2ASQAv/DuAB","1264":"b:f9393/e339p/gINBIAUiA2ASQAv/DuAB","1275":"b:f9393/e339p/gINBIAUiA2ASQAv/DuAB","1276":"b:f9393/e339p/gINBIAUiA2ASQAv/DuAB","1281":"b:f9393/e339p/gINBIAUiA2ASQAv/DuAB","1287":"b:f9393/e339p/gINBIAUiA2ASQAv/DuAB","1299":"b:f9393/e339p/gINBIAUiA2ASQAv/DuAB","1306":"b:f9393/e339p/gINBIAUiA2ASQAv/DuAB","1317":"b:f9393/e339p/gINBIAUiA2ASQAv/DuAB","1319":"b:f9393/e339p/gINBIAUiA2ASQAv/DuAB","1326":"b:f9393/e339p/gINBIAUiA2ASQAv/DuAB","1336":"b:f9393/e339p/gINBIAUiA2ASQAv/DuAB","1337":"b:f9393/e339p/gINBIAUiA2ASQAv/DuAB","1357":"b:f9393/e339p/gINBIAUiA2ASQAv/DuAB","1384":"b:f9393/e339p/gINBIAUiA2ASQAv/DuAB","1423":"b:f9393/e339p/gINBIAUiA2ASQAv/DuAB","1445":"b:f9393/e339p/gINBIAUiA2ASQAv/DuAB","1452":"b:f9393/e339p/gINBIAUiA2ASQAv/DuAB","1499":"b:f9393/e339p/gINBIAUiA2ASQAv/DuAB","1505":"b:f9393/e339p/gINBIAUiA2ASQAv/DuAB"},"packages/cli/src/workflows/workflows.controller.ts":{"73":"b:R9393+f//9v/4JtBICUCA2BaAAv9BuAD","100":"b:R9393+f//9v/4JtBICUCA2BaAAv9BuAD","132":"b:R9393+f//9v/4JtBICUCA2BaAAv9BuAD","157":"b:R9393+f//9v/4JtBICUCA2BaAAv9BuAD","173":"b:R9393+f//9v/4JtBICUCA2BaAAv9BuAD","207":"b:R9393+f//9v/4JtBICUCA2BaAAv9BuAD","290":"b:R9393+f//9v/4JtBICUCA2BaAAv9BuAD","297":"b:R9393+f//9v/4JtBICUCA2BaAAv9BuAD","341":"b:R9393+f//9v/4JtBICUCA2BaAAv9BuAD","352":"b:R9393+f//9v/4JtBICUCA2BaAAv9BuAD","373":"b:R9393+f//9v/4JtBICUCA2BaAAv9BuAD","407":"b:R9393+f//9v/4JtBICUCA2BaAAv9BuAD","441":"b:R9393+f//9v/4JtBICUCA2BaAAv9BuAD","475":"b:R9393+f//9v/4JtBICUCA2BaAAv9BuAD","506":"b:R9393+f//9v/4JtBICUCA2BaAAv9BuAD","563":"b:R9393+f//9v/4JtBICUCA2BaAAv9BuAD","569":"b:R9393+f//9v/4JtBICUCA2BaAAv9BuAD","583":"b:R9393+f//9v/4JtBICUCA2BaAAv9BuAD","584":"b:R9393+f//9v/4JtBICUCA2BaAAv9BuAD","588":"b:R9393+f//9v/4JtBICUCA2BaAAv9BuAD","589":"b:R9393+f//9v/4JtBICUCA2BaAAv9BuAD","593":"b:R9393+f//9v/4JtBICUCA2BaAAv9BuAD","594":"b:R9393+f//9v/4JtBICUCA2BaAAv9BuAD","615":"b:R9393+f//9v/4JtBICUCA2BaAAv9BuAD","639":"b:R9393+f//9v/4JtBICUCA2BaAAv9BuAD","646":"b:R9393+f//9v/4JtBICUCA2BaAAv9BuAD","663":"b:R9393+f//9v/4JtBICUCA2BaAAv9BuAD","684":"b:R9393+f//9v/4JtBICUCA2BaAAv9BuAD","706":"b:R9393+f//9v/4JtBICUCA2BaAAv9BuAD","732":"b:R9393+f//9v/4JtBICUCA2BaAAv9BuAD","751":"b:R9393+f//9v/4JtBICUCA2BaAAv9BuAD"},"packages/community-node-types-utils.ts":{"34":"b:B4Xd0AWk2IDdYBAAAAAAAAAAAAAAAAAA","40":"b:B4Xd0AWk2IDdYBAAAAAAAAAAAAAAAAAA","57":"b:B4Xd0AWk2IDdYBAAAAAAAAAAAAAAAAAA"},"packages/community-node-types.controller.ts":{"9":"b:B4Xd0KWn2IDdYBAAAAAAAAAAAAAAAAAA","12":"b:B4Xd0KWn2IDdYBAAAAAAAAAAAAAAAAAA","17":"b:B4Xd0KWn2IDdYBAAAAAAAAAAAAAAAAAA"},"packages/community-node-types.service.ts":{"31":"b:B4Xd0KWn2IDdYBAAAAAAAAAAAAAAAAAA","37":"b:B4Xd0KWn2IDdYBAAAAAAAAAAAAAAAAAA","54":"b:B4Xd0KWn2IDdYBAAAAAAAAAAAAAAAAAA","85":"b:B4Xd0KWn2IDdYBAAAAAAAAAAAAAAAAAA","127":"b:B4Xd0KWn2IDdYBAAAAAAAAAAAAAAAAAA","135":"b:B4Xd0KWn2IDdYBAAAAAAAAAAAAAAAAAA","151":"b:B4Xd0KWn2IDdYBAAAAAAAAAAAAAAAAAA","153":"b:B4Xd0KWn2IDdYBAAAAAAAAAAAAAAAAAA","166":"b:B4Xd0KWn2IDdYBAAAAAAAAAAAAAAAAAA","188":"b:B4Xd0KWn2IDdYBAAAAAAAAAAAAAAAAAA","194":"b:B4Xd0KWn2IDdYBAAAAAAAAAAAAAAAAAA","199":"b:B4Xd0KWn2IDdYBAAAAAAAAAAAAAAAAAA","204":"b:B4Xd0KWn2IDdYBAAAAAAAAAAAAAAAAAA","206":"b:B4Xd0KWn2IDdYBAAAAAAAAAAAAAAAAAA","208":"b:B4Xd0KWn2IDdYBAAAAAAAAAAAAAAAAAA","211":"b:B4Xd0KWn2IDdYBAAAAAAAAAAAAAAAAAA","218":"b:B4Xd0KWn2IDdYBAAAAAAAAAAAAAAAAAA","224":"b:B4Xd0KWn2IDdYBAAAAAAAAAAAAAAAAAA","231":"b:B4Xd0KWn2IDdYBAAAAAAAAAAAAAAAAAA","236":"b:B4Xd0KWn2IDdYBAAAAAAAAAAAAAAAAAA"},"packages/core/src/execution-engine/execution-context-hook-registry.service.ts":{"18":"b:f5DlwyX3sBsDAAAAAAAAAAAAgADkAyAA","34":"b:f5DlwyX3sBsDAAAAAAAAAAAAgADkAyAA","89":"b:f5DlwyX3sBsDAAAAAAAAAAAAgADkAyAA","98":"b:f5DlwyX3sBsDAAAAAAAAAAAAgADkAyAA","111":"b:f5DlwyX3sBsDAAAAAAAAAAAAgADkAyAA","112":"b:f5DlwyX3sBsDAAAAAAAAAAAAgADkAyAA","117":"b:f5DlwyX3sBsDAAAAAAAAAAAAgADkAyAA","127":"b:f5DlwyX3sBsDAAAAAAAAAAAAgADkAyAA"},"packages/core/src/execution-engine/execution-context.service.ts":{"22":"b:f5DlwyX/sBsDAAAAAAAAAAACgADkAyAA","28":"b:f5DlwyX/sBsDAAAAAAAAAAACgADkAyAA","48":"b:f5DlwyX/sBsDAAAAAAAAAAACgADkAyAA","57":"b:f5DlwyX/sBsDAAAAAAAAAAACgADkAyAA","69":"b:f5DlwyX/sBsDAAAAAAAAAAACgADkAyAA","91":"b:f5DlwyX/sBsDAAAAAAAAAAACgADkAyAA","119":"b:f5DlwyX/sBsDAAAAAAAAAAACgADkAyAA"},"packages/core/src/execution-engine/execution-context.ts":{"104":"b:f5DlwyX3sBsDAAAAAAAAAAAAAADkAwAA"},"packages/core/src/execution-engine/execution-lifecycle-hooks.ts":{"103":"b:f5DlwyX3sBsDAAAAAAAAAAAAAADkAwAA","110":"b:f5DlwyX3sBsDAAAAAAAAAAAAAADkAwAA","118":"b:f5DlwyX3sBsDAAAAAAAAAAAAAADkAwAA"},"packages/core/src/execution-engine/node-execution-context/base-execute-context.ts":{"47":"b:f5rlwyX/sBMDAAAgIAAAAMCAgWHgA6AA","62":"b:f5rlwyX/sBMDAAAgIAAAAMCAgWHgA6AA","66":"b:f5rlwyX/sBMDAAAgIAAAAMCAgWHgA6AA","70":"b:f5rlwyX/sBMDAAAgIAAAAMCAgWHgA6AA","71":"b:f5rlwyX/sBMDAAAgIAAAAMCAgWHgA6AA","78":"b:f5rlwyX/sBMDAAAgIAAAAMCAgWHgA6AA","79":"b:f5rlwyX/sBMDAAAgIAAAAMCAgWHgA6AA","91":"b:f5rlwyX/sBMDAAAgIAAAAMCAgWHgA6AA","95":"b:f5rlwyX/sBMDAAAgIAAAAMCAgWHgA6AA","102":"b:f5rlwyX/sBMDAAAgIAAAAMCAgWHgA6AA","107":"b:f5rlwyX/sBMDAAAgIAAAAMCAgWHgA6AA","117":"b:f5rlwyX/sBMDAAAgIAAAAMCAgWHgA6AA","129":"b:f5rlwyX/sBMDAAAgIAAAAMCAgWHgA6AA","136":"b:f5rlwyX/sBMDAAAgIAAAAMCAgWHgA6AA","195":"b:f5rlwyX/sBMDAAAgIAAAAMCAgWHgA6AA","225":"b:f5rlwyX/sBMDAAAgIAAAAMCAgWHgA6AA","239":"b:f5rlwyX/sBMDAAAgIAAAAMCAgWHgA6AA","255":"b:f5rlwyX/sBMDAAAgIAAAAMCAgWHgA6AA","259":"b:f5rlwyX/sBMDAAAgIAAAAMCAgWHgA6AA","277":"b:f5rlwyX/sBMDAAAgIAAAAMCAgWHgA6AA","285":"b:f5rlwyX/sBMDAAAgIAAAAMCAgWHgA6AA","301":"b:f5rlwyX/sBMDAAAgIAAAAMCAgWHgA6AA","307":"b:f5rlwyX/sBMDAAAgIAAAAMCAgWHgA6AA","332":"b:f5rlwyX/sBMDAAAgIAAAAMCAgWHgA6AA","343":"b:f5rlwyX/sBMDAAAgIAAAAMCAgWHgA6AA","368":"b:f5rlwyX/sBMDAAAgIAAAAMCAgWHgA6AA"},"packages/core/src/execution-engine/node-execution-context/execute-context.ts":{"54":"b:f5DlwyX3sBMDAAAAoAAAAAAAAADgAwAA","101":"b:f5DlwyX3sBMDAAAAoAAAAAAAAADgAwAA","103":"b:f5DlwyX3sBMDAAAAoAAAAAAAAADgAwAA","111":"b:f5DlwyX3sBMDAAAAoAAAAAAAAADgAwAA","115":"b:f5DlwyX3sBMDAAAAoAAAAAAAAADgAwAA","125":"b:f5DlwyX3sBMDAAAAoAAAAAAAAADgAwAA","140":"b:f5DlwyX3sBMDAAAAoAAAAAAAAADgAwAA","144":"b:f5DlwyX3sBMDAAAAoAAAAAAAAADgAwAA","159":"b:f5DlwyX3sBMDAAAAoAAAAAAAAADgAwAA","184":"b:f5DlwyX3sBMDAAAAoAAAAAAAAADgAwAA","205":"b:f5DlwyX3sBMDAAAAoAAAAAAAAADgAwAA","213":"b:f5DlwyX3sBMDAAAAoAAAAAAAAADgAwAA","215":"b:f5DlwyX3sBMDAAAAoAAAAAAAAADgAwAA","227":"b:f5DlwyX3sBMDAAAAoAAAAAAAAADgAwAA","232":"b:f5DlwyX3sBMDAAAAoAAAAAAAAADgAwAA","237":"b:f5DlwyX3sBMDAAAAoAAAAAAAAADgAwAA","241":"b:f5DlwyX3sBMDAAAAoAAAAAAAAADgAwAA","245":"b:f5DlwyX3sBMDAAAAoAAAAAAAAADgAwAA","250":"b:f5DlwyX3sBMDAAAAoAAAAAAAAADgAwAA"},"packages/core/src/execution-engine/node-execution-context/node-execution-context.ts":{"52":"b:f5rl0yf3uBsLAAQA4CAAKICKjSv0A+AB","64":"b:f5rl0yf3uBsLAAQA4CAAKICKjSv0A+AB","69":"b:f5rl0yf3uBsLAAQA4CAAKICKjSv0A+AB","81":"b:f5rl0yf3uBsLAAQA4CAAKICKjSv0A+AB","85":"b:f5rl0yf3uBsLAAQA4CAAKICKjSv0A+AB","89":"b:f5rl0yf3uBsLAAQA4CAAKICKjSv0A+AB","93":"b:f5rl0yf3uBsLAAQA4CAAKICKjSv0A+AB","98":"b:f5rl0yf3uBsLAAQA4CAAKICKjSv0A+AB","102":"b:f5rl0yf3uBsLAAQA4CAAKICKjSv0A+AB","106":"b:f5rl0yf3uBsLAAQA4CAAKICKjSv0A+AB","128":"b:f5rl0yf3uBsLAAQA4CAAKICKjSv0A+AB","166":"b:f5rl0yf3uBsLAAQA4CAAKICKjSv0A+AB","177":"b:f5rl0yf3uBsLAAQA4CAAKICKjSv0A+AB","181":"b:f5rl0yf3uBsLAAQA4CAAKICKjSv0A+AB","186":"b:f5rl0yf3uBsLAAQA4CAAKICKjSv0A+AB","197":"b:f5rl0yf3uBsLAAQA4CAAKICKjSv0A+AB","207":"b:f5rl0yf3uBsLAAQA4CAAKICKjSv0A+AB","212":"b:f5rl0yf3uBsLAAQA4CAAKICKjSv0A+AB","214":"b:f5rl0yf3uBsLAAQA4CAAKICKjSv0A+AB","218":"b:f5rl0yf3uBsLAAQA4CAAKICKjSv0A+AB","223":"b:f5rl0yf3uBsLAAQA4CAAKICKjSv0A+AB","225":"b:f5rl0yf3uBsLAAQA4CAAKICKjSv0A+AB","229":"b:f5rl0yf3uBsLAAQA4CAAKICKjSv0A+AB","232":"b:f5rl0yf3uBsLAAQA4CAAKICKjSv0A+AB","233":"b:f5rl0yf3uBsLAAQA4CAAKICKjSv0A+AB","234":"b:f5rl0yf3uBsLAAQA4CAAKICKjSv0A+AB","237":"b:f5rl0yf3uBsLAAQA4CAAKICKjSv0A+AB","241":"b:f5rl0yf3uBsLAAQA4CAAKICKjSv0A+AB","245":"b:f5rl0yf3uBsLAAQA4CAAKICKjSv0A+AB","249":"b:f5rl0yf3uBsLAAQA4CAAKICKjSv0A+AB","253":"b:f5rl0yf3uBsLAAQA4CAAKICKjSv0A+AB","257":"b:f5rl0yf3uBsLAAQA4CAAKICKjSv0A+AB","265":"b:f5rl0yf3uBsLAAQA4CAAKICKjSv0A+AB","269":"b:f5rl0yf3uBsLAAQA4CAAKICKjSv0A+AB","294":"b:f5rl0yf3uBsLAAQA4CAAKICKjSv0A+AB","298":"b:f5rl0yf3uBsLAAQA4CAAKICKjSv0A+AB","307":"b:f5rl0yf3uBsLAAQA4CAAKICKjSv0A+AB","318":"b:f5rl0yf3uBsLAAQA4CAAKICKjSv0A+AB","369":"b:f5rl0yf3uBsLAAQA4CAAKICKjSv0A+AB","474":"b:f5rl0yf3uBsLAAQA4CAAKICKjSv0A+AB","479":"b:f5rl0yf3uBsLAAQA4CAAKICKjSv0A+AB","489":"b:f5rl0yf3uBsLAAQA4CAAKICKjSv0A+AB","584":"b:f5rl0yf3uBsLAAQA4CAAKICKjSv0A+AB","599":"b:f5rl0yf3uBsLAAQA4CAAKICKjSv0A+AB"},"packages/core/src/execution-engine/node-execution-context/supply-data-context.ts":{"64":[0,1,2,3,4,5,9,11,6],"109":[0,1,2,3,4,5,9,11,6],"111":[0,1,2,3,4,5,9,11,6],"119":[0,1,2,3,4,5,9,11,6],"126":[0,1,2,3,4,5,9,11,6],"141":[0,1,2,3,4,5,9,11,6],"164":[0,1,2,3,4,5,9,11,6],"185":[0,1,2,3,4,5,9,11,6],"193":[0,1,2,3,4,5,9,11,6],"199":[0,1,2,3,4,5,9,11,6],"204":[0,1,2,3,4,5,9,11,6],"220":[0,1,2,3,4,5,9,11,6],"233":[0,1,2,3,4,5,9,11,6],"249":[0,1,2,3,4,5,9,11,6],"259":[0,1,2,3,4,5,9,11,6],"395":[0,1,2,3,4,5,9,11,6],"422":[0,1,2,3,4,5,9,11,6],"424":[0,1,2,3,4,5,9,11,6],"436":[0,1,2,3,4,5,9,11,6]},"packages/core/src/execution-engine/node-execution-context/trigger-context.ts":{"21":"b:R5BlwCX/sAsDAAAAAAAAAAACAACAAAAB","25":"b:R5BlwCX/sAsDAAAAAAAAAAACAACAAAAB","29":"b:R5BlwCX/sAsDAAAAAAAAAAACAACAAAAB","36":"b:R5BlwCX/sAsDAAAAAAAAAAACAACAAAAB","63":"b:R5BlwCX/sAsDAAAAAAAAAAACAACAAAAB","67":"b:R5BlwCX/sAsDAAAAAAAAAAACAACAAAAB"},"packages/core/src/execution-engine/node-execution-context/utils/ai-tool-types.ts":{"13":[0,1,2,3,4,5,9,11,6],"17":[0,1,2,3,4,5,9,11,6],"23":[0,1,2,3,4,5,9,11,6]},"packages/core/src/execution-engine/node-execution-context/utils/binary-helper-functions.ts":{"33":"b:f5DlwyX3sBsDAAAAgAAAAAAAAADkAwAA","40":"b:f5DlwyX3sBsDAAAAgAAAAAAAAADkAwAA","47":"b:f5DlwyX3sBsDAAAAgAAAAAAAAADkAwAA","56":"b:f5DlwyX3sBsDAAAAgAAAAAAAAADkAwAA","128":"b:f5DlwyX3sBsDAAAAgAAAAAAAAADkAwAA","155":"b:f5DlwyX3sBsDAAAAgAAAAAAAAADkAwAA","167":"b:f5DlwyX3sBsDAAAAgAAAAAAAAADkAwAA","180":"b:f5DlwyX3sBsDAAAAgAAAAAAAAADkAwAA","244":"b:f5DlwyX3sBsDAAAAgAAAAAAAAADkAwAA","342":"b:f5DlwyX3sBsDAAAAgAAAAAAAAADkAwAA","351":"b:f5DlwyX3sBsDAAAAgAAAAAAAAADkAwAA","355":"b:f5DlwyX3sBsDAAAAgAAAAAAAAADkAwAA","357":"b:f5DlwyX3sBsDAAAAgAAAAAAAAADkAwAA","359":"b:f5DlwyX3sBsDAAAAgAAAAAAAAADkAwAA"},"packages/core/src/execution-engine/node-execution-context/utils/cleanup-parameter-data.ts":{"8":"b:f5Dl0yW3uBMDAAQAgAAACABIAQDkAwAB","14":"b:f5Dl0yW3uBMDAAQAgAAACABIAQDkAwAB","19":"b:f5Dl0yW3uBMDAAQAgAAACABIAQDkAwAB"},"packages/core/src/execution-engine/node-execution-context/utils/credential-check-helper-functions.ts":{"3":"b:f5DlwyX3sBMDAAAAAAAAAAAAAADgAwAA","9":"b:f5DlwyX3sBMDAAAAAAAAAAAAAADgAwAA"},"packages/core/src/execution-engine/node-execution-context/utils/custom-data.ts":{"15":"b:f5DlwyX3sBEDAAQAACAACAAAAAAAAwAA","23":"b:f5DlwyX3sBEDAAQAACAACAAAAAAAAwAA","34":"b:f5DlwyX3sBEDAAQAACAACAAAAAAAAwAA","45":"b:f5DlwyX3sBEDAAQAACAACAAAAAAAAwAA","48":"b:f5DlwyX3sBEDAAQAACAACAAAAAAAAwAA"},"packages/core/src/execution-engine/node-execution-context/utils/data-table-helper-functions.ts":{"8":"b:f5Dl0yW3sBMDAAAAAAAAAAAAAADgAwAA","16":"b:f5Dl0yW3sBMDAAAAAAAAAAAAAADgAwAA","22":"b:f5Dl0yW3sBMDAAAAAAAAAAAAAADgAwAA"},"packages/core/src/execution-engine/node-execution-context/utils/deduplication-helper-functions.ts":{"16":"b:f5DlwyX3sBMDAAAAAAAAAAAAAADgAwAA","30":"b:f5DlwyX3sBMDAAAAAAAAAAAAAADgAwAA","46":"b:f5DlwyX3sBMDAAAAAAAAAAAAAADgAwAA","60":"b:f5DlwyX3sBMDAAAAAAAAAAAAAADgAwAA","72":"b:f5DlwyX3sBMDAAAAAAAAAAAAAADgAwAA","84":"b:f5DlwyX3sBMDAAAAAAAAAAAAAADgAwAA","88":"b:f5DlwyX3sBMDAAAAAAAAAAAAAADgAwAA","95":"b:f5DlwyX3sBMDAAAAAAAAAAAAAADgAwAA","109":"b:f5DlwyX3sBMDAAAAAAAAAAAAAADgAwAA","116":"b:f5DlwyX3sBMDAAAAAAAAAAAAAADgAwAA","122":"b:f5DlwyX3sBMDAAAAAAAAAAAAAADgAwAA"},"packages/core/src/execution-engine/node-execution-context/utils/execution-metadata.ts":{"8":[82,1,2,3,4,5,109,123,6],"46":[82,1,2,3,4,5,109,123,6],"51":[82,1,2,3,4,5,109,123,6],"63":[82,1,2,3,4,5,109,123,6],"70":[82,1,2,3,4,5,109,123,6]},"packages/core/src/execution-engine/node-execution-context/utils/file-system-helper-functions.ts":{"41":"b:f5DlwyX3sBMDAAAAAAAAAAAAAADgAwAA","47":"b:f5DlwyX3sBMDAAAAAAAAAAAAAADgAwAA","48":"b:f5DlwyX3sBMDAAAAAAAAAAAAAADgAwAA","49":"b:f5DlwyX3sBMDAAAAAAAAAAAAAADgAwAA","54":"b:f5DlwyX3sBMDAAAAAAAAAAAAAADgAwAA","60":"b:f5DlwyX3sBMDAAAAAAAAAAAAAADgAwAA","77":"b:f5DlwyX3sBMDAAAAAAAAAAAAAADgAwAA","85":"b:f5DlwyX3sBMDAAAAAAAAAAAAAADgAwAA","86":"b:f5DlwyX3sBMDAAAAAAAAAAAAAADgAwAA","87":"b:f5DlwyX3sBMDAAAAAAAAAAAAAADgAwAA","96":"b:f5DlwyX3sBMDAAAAAAAAAAAAAADgAwAA","105":"b:f5DlwyX3sBMDAAAAAAAAAAAAAADgAwAA","126":"b:f5DlwyX3sBMDAAAAAAAAAAAAAADgAwAA","132":"b:f5DlwyX3sBMDAAAAAAAAAAAAAADgAwAA","142":"b:f5DlwyX3sBMDAAAAAAAAAAAAAADgAwAA","148":"b:f5DlwyX3sBMDAAAAAAAAAAAAAADgAwAA","149":"b:f5DlwyX3sBMDAAAAAAAAAAAAAADgAwAA","157":"b:f5DlwyX3sBMDAAAAAAAAAAAAAADgAwAA","169":"b:f5DlwyX3sBMDAAAAAAAAAAAAAADgAwAA","177":"b:f5DlwyX3sBMDAAAAAAAAAAAAAADgAwAA","189":"b:f5DlwyX3sBMDAAAAAAAAAAAAAADgAwAA","242":"b:f5DlwyX3sBMDAAAAAAAAAAAAAADgAwAA","245":"b:f5DlwyX3sBMDAAAAAAAAAAAAAADgAwAA","251":"b:f5DlwyX3sBMDAAAAAAAAAAAAAADgAwAA","278":"b:f5DlwyX3sBMDAAAAAAAAAAAAAADgAwAA","279":"b:f5DlwyX3sBMDAAAAAAAAAAAAAADgAwAA","329":"b:f5DlwyX3sBMDAAAAAAAAAAAAAADgAwAA","333":"b:f5DlwyX3sBMDAAAAAAAAAAAAAADgAwAA","423":"b:f5DlwyX3sBMDAAAAAAAAAAAAAADgAwAA","435":"b:f5DlwyX3sBMDAAAAAAAAAAAAAADgAwAA","438":"b:f5DlwyX3sBMDAAAAAAAAAAAAAADgAwAA","442":"b:f5DlwyX3sBMDAAAAAAAAAAAAAADgAwAA","450":"b:f5DlwyX3sBMDAAAAAAAAAAAAAADgAwAA"},"packages/core/src/execution-engine/node-execution-context/utils/get-additional-keys.ts":{"13":"b:f5Dl0yX3uBMTAAAAAAAAAAAAAADsAwAA","20":"b:f5Dl0yX3uBMTAAAAAAAAAAAAAADsAwAA","62":"b:f5Dl0yX3uBMTAAAAAAAAAAAAAADsAwAA"},"packages/core/src/execution-engine/node-execution-context/utils/get-input-connection-data.ts":{"46":[0,1,2,3,4,5,93,9,11,6],"52":[0,1,2,3,4,5,93,9,11,6],"64":[0,1,2,3,4,5,93,9,11,6],"68":[0,1,2,3,4,5,93,9,11,6],"78":[0,1,2,3,4,5,93,9,11,6],"91":[0,1,2,3,4,5,93,9,11,6],"116":[0,1,2,3,4,5,93,9,11,6],"156":[0,1,2,3,4,5,93,9,11,6],"160":[0,1,2,3,4,5,93,9,11,6],"169":[0,1,2,3,4,5,93,9,11,6],"172":[0,1,2,3,4,5,93,9,11,6],"181":[0,1,2,3,4,5,93,9,11,6],"182":[0,1,2,3,4,5,93,9,11,6],"190":[0,1,2,3,4,5,93,9,11,6],"221":[0,1,2,3,4,5,93,9,11,6],"233":[0,1,2,3,4,5,93,9,11,6],"247":[0,1,2,3,4,5,93,9,11,6],"341":[0,1,2,3,4,5,93,9,11,6],"361":[0,1,2,3,4,5,93,9,11,6],"364":[0,1,2,3,4,5,93,9,11,6],"378":[0,1,2,3,4,5,93,9,11,6],"393":[0,1,2,3,4,5,93,9,11,6],"409":[0,1,2,3,4,5,93,9,11,6],"418":[0,1,2,3,4,5,93,9,11,6],"471":[0,1,2,3,4,5,93,9,11,6],"494":[0,1,2,3,4,5,93,9,11,6],"571":[0,1,2,3,4,5,93,9,11,6]},"packages/core/src/execution-engine/node-execution-context/utils/normalize-items.ts":{"10":"b:RQAhAAFQAAABAAAAAAAAAICAA2AAAQAA","17":"b:RQAhAAFQAAABAAAAAAAAAICAA2AAAQAA","20":"b:RQAhAAFQAAABAAAAAAAAAICAA2AAAQAA","24":"b:RQAhAAFQAAABAAAAAAAAAICAA2AAAQAA","26":"b:RQAhAAFQAAABAAAAAAAAAICAA2AAAQAA","27":"b:RQAhAAFQAAABAAAAAAAAAICAA2AAAQAA","40":"b:RQAhAAFQAAABAAAAAAAAAICAA2AAAQAA","44":"b:RQAhAAFQAAABAAAAAAAAAICAA2AAAQAA"},"packages/core/src/execution-engine/node-execution-context/utils/request-helpers/factory.ts":{"27":"b:f5Dl0yW/sBsDAAAAAAAAAECCCADkAwAA","34":"b:f5Dl0yW/sBsDAAAAAAAAAECCCADkAwAA","69":"b:f5Dl0yW/sBsDAAAAAAAAAECCCADkAwAA","89":"b:f5Dl0yW/sBsDAAAAAAAAAECCCADkAwAA","90":"b:f5Dl0yW/sBsDAAAAAAAAAECCCADkAwAA","112":"b:f5Dl0yW/sBsDAAAAAAAAAECCCADkAwAA","130":"b:f5Dl0yW/sBsDAAAAAAAAAECCCADkAwAA","144":"b:f5Dl0yW/sBsDAAAAAAAAAECCCADkAwAA","169":"b:f5Dl0yW/sBsDAAAAAAAAAECCCADkAwAA","189":"b:f5Dl0yW/sBsDAAAAAAAAAECCCADkAwAA","208":"b:f5Dl0yW/sBsDAAAAAAAAAECCCADkAwAA"},"packages/core/src/execution-engine/node-execution-context/utils/resolve-source-overwrite.ts":{"7":"b:f5DlwyX3sBsDAAAAAAAAAAAAAADkAwAA"},"packages/core/src/execution-engine/node-execution-context/utils/return-json-array.ts":{"8":"b:R5BlwCX/sAkBAAAAAAAAAAACAABgAAAA","15":"b:R5BlwCX/sAkBAAAAAAAAAAACAABgAAAA"},"packages/core/src/execution-engine/node-execution-context/utils/scheduling-helper-functions.ts":{"8":"b:R5BlwCX/sAkLAAAAAAAAAgACAAhwAEAA","15":"b:R5BlwCX/sAkLAAAAAAAAAgACAAhwAEAA"},"packages/core/src/execution-engine/node-execution-context/utils/ssh-tunnel-helper-functions.ts":{"7":"b:f5Dl0yW3sBsDAAAAAAAAAAAAAADgAwAA","10":"b:f5Dl0yW3sBsDAAAAAAAAAAAAAADgAwAA","12":"b:f5Dl0yW3sBsDAAAAAAAAAAAAAADgAwAA"},"packages/core/src/execution-engine/node-execution-context/utils/validate-value-against-schema.ts":{"20":"b:/5Dl0yX3uBMLAAAAwAAAAIDIEQHkA6AA","44":"b:/5Dl0yX3uBMLAAAAwAAAAIDIEQHkA6AA","77":"b:/5Dl0yX3uBMLAAAAwAAAAIDIEQHkA6AA","89":"b:/5Dl0yX3uBMLAAAAwAAAAIDIEQHkA6AA","151":"b:/5Dl0yX3uBMLAAAAwAAAAIDIEQHkA6AA","162":"b:/5Dl0yX3uBMLAAAAwAAAAIDIEQHkA6AA"},"packages/core/src/execution-engine/node-execution-context/webhook-context.ts":{"36":"b:RgrAQwCAEBACAAQAoCAACAAABCAEAyAA","88":"b:RgrAQwCAEBACAAQAoCAACAAABCAEAyAA","99":"b:RgrAQwCAEBACAAQAoCAACAAABCAEAyAA","103":"b:RgrAQwCAEBACAAQAoCAACAAABCAEAyAA","107":"b:RgrAQwCAEBACAAQAoCAACAAABCAEAyAA","111":"b:RgrAQwCAEBACAAQAoCAACAAABCAEAyAA","115":"b:RgrAQwCAEBACAAQAoCAACAAABCAEAyAA","119":"b:RgrAQwCAEBACAAQAoCAACAAABCAEAyAA","123":"b:RgrAQwCAEBACAAQAoCAACAAABCAEAyAA","130":"b:RgrAQwCAEBACAAQAoCAACAAABCAEAyAA","138":"b:RgrAQwCAEBACAAQAoCAACAAABCAEAyAA","156":"b:RgrAQwCAEBACAAQAoCAACAAABCAEAyAA","160":"b:RgrAQwCAEBACAAQAoCAACAAABCAEAyAA","169":"b:RgrAQwCAEBACAAQAoCAACAAABCAEAyAA","176":"b:RgrAQwCAEBACAAQAoCAACAAABCAEAyAA","186":"b:RgrAQwCAEBACAAQAoCAACAAABCAEAyAA","193":"b:RgrAQwCAEBACAAQAoCAACAAABCAEAyAA","200":"b:RgrAQwCAEBACAAQAoCAACAAABCAEAyAA"},"packages/core/src/execution-engine/requests-response.ts":{"31":"b:f5DlwyX3sBsDAAAAAAAAAAAAAADkAwAA","55":"b:f5DlwyX3sBsDAAAAAAAAAAAAAADkAwAA","89":"b:f5DlwyX3sBsDAAAAAAAAAAAAAADkAwAA","187":"b:f5DlwyX3sBsDAAAAAAAAAAAAAADkAwAA","203":"b:f5DlwyX3sBsDAAAAAAAAAAAAAADkAwAA","238":"b:f5DlwyX3sBsDAAAAAAAAAAAAAADkAwAA","285":"b:f5DlwyX3sBsDAAAAAAAAAAAAAADkAwAA","291":"b:f5DlwyX3sBsDAAAAAAAAAAAAAADkAwAA"},"packages/core/src/execution-engine/ssh-clients-manager.ts":{"10":"b:f5Hn83W3sBsLACAwABEGEgCgAADgAwAC","16":"b:f5Hn83W3sBsLACAwABEGEgCgAADgAwAC","17":"b:f5Hn83W3sBsLACAwABEGEgCgAADgAwAC","67":"b:f5Hn83W3sBsLACAwABEGEgCgAADgAwAC","69":"b:f5Hn83W3sBsLACAwABEGEgCgAADgAwAC","85":"b:f5Hn83W3sBsLACAwABEGEgCgAADgAwAC","88":"b:f5Hn83W3sBsLACAwABEGEgCgAADgAwAC","108":"b:f5Hn83W3sBsLACAwABEGEgCgAADgAwAC","133":"b:f5Hn83W3sBsLACAwABEGEgCgAADgAwAC","135":"b:f5Hn83W3sBsLACAwABEGEgCgAADgAwAC","157":"b:f5Hn83W3sBsLACAwABEGEgCgAADgAwAC","158":"b:f5Hn83W3sBsLACAwABEGEgCgAADgAwAC","162":"b:f5Hn83W3sBsLACAwABEGEgCgAADgAwAC","166":"b:f5Hn83W3sBsLACAwABEGEgCgAADgAwAC","170":"b:f5Hn83W3sBsLACAwABEGEgCgAADgAwAC","180":"b:f5Hn83W3sBsLACAwABEGEgCgAADgAwAC","191":"b:f5Hn83W3sBsLACAwABEGEgCgAADgAwAC","203":"b:f5Hn83W3sBsLACAwABEGEgCgAADgAwAC"},"packages/core/src/execution-engine/triggers-and-pollers.ts":{"26":"b:R5BlwCX/sAsDAAAAAAAAAAACAADgAAAA","51":"b:R5BlwCX/sAsDAAAAAAAAAAACAADgAAAA","55":"b:R5BlwCX/sAsDAAAAAAAAAAACAADgAAAA","61":"b:R5BlwCX/sAsDAAAAAAAAAAACAADgAAAA","65":"b:R5BlwCX/sAsDAAAAAAAAAAACAADgAAAA","71":"b:R5BlwCX/sAsDAAAAAAAAAAACAADgAAAA","76":"b:R5BlwCX/sAsDAAAAAAAAAAACAADgAAAA","81":"b:R5BlwCX/sAsDAAAAAAAAAAACAADgAAAA","95":"b:R5BlwCX/sAsDAAAAAAAAAAACAADgAAAA"},"packages/core/src/execution-engine/workflow-execute.ts":{"114":"b:f5DlwyX/sBsDAAAwIAAAIMjCETHkA6AA","132":"b:f5DlwyX/sBsDAAAwIAAAIMjCETHkA6AA","199":"b:f5DlwyX/sBsDAAAwIAAAIMjCETHkA6AA","207":"b:f5DlwyX/sBsDAAAwIAAAIMjCETHkA6AA","310":"b:f5DlwyX/sBsDAAAwIAAAIMjCETHkA6AA","342":"b:f5DlwyX/sBsDAAAwIAAAIMjCETHkA6AA","383":"b:f5DlwyX/sBsDAAAwIAAAIMjCETHkA6AA","411":"b:f5DlwyX/sBsDAAAwIAAAIMjCETHkA6AA","430":"b:f5DlwyX/sBsDAAAwIAAAIMjCETHkA6AA","633":"b:f5DlwyX/sBsDAAAwIAAAIMjCETHkA6AA","850":"b:f5DlwyX/sBsDAAAwIAAAIMjCETHkA6AA","917":"b:f5DlwyX/sBsDAAAwIAAAIMjCETHkA6AA","935":"b:f5DlwyX/sBsDAAAwIAAAIMjCETHkA6AA","946":"b:f5DlwyX/sBsDAAAwIAAAIMjCETHkA6AA","988":"b:f5DlwyX/sBsDAAAwIAAAIMjCETHkA6AA","1001":"b:f5DlwyX/sBsDAAAwIAAAIMjCETHkA6AA","1012":"b:f5DlwyX/sBsDAAAwIAAAIMjCETHkA6AA","1027":"b:f5DlwyX/sBsDAAAwIAAAIMjCETHkA6AA","1032":"b:f5DlwyX/sBsDAAAwIAAAIMjCETHkA6AA","1041":"b:f5DlwyX/sBsDAAAwIAAAIMjCETHkA6AA","1092":"b:f5DlwyX/sBsDAAAwIAAAIMjCETHkA6AA","1099":"b:f5DlwyX/sBsDAAAwIAAAIMjCETHkA6AA","1101":"b:f5DlwyX/sBsDAAAwIAAAIMjCETHkA6AA","1126":"b:f5DlwyX/sBsDAAAwIAAAIMjCETHkA6AA","1194":"b:f5DlwyX/sBsDAAAwIAAAIMjCETHkA6AA","1214":"b:f5DlwyX/sBsDAAAwIAAAIMjCETHkA6AA","1267":"b:f5DlwyX/sBsDAAAwIAAAIMjCETHkA6AA","1302":"b:f5DlwyX/sBsDAAAwIAAAIMjCETHkA6AA","1322":"b:f5DlwyX/sBsDAAAwIAAAIMjCETHkA6AA","1451":"b:f5DlwyX/sBsDAAAwIAAAIMjCETHkA6AA","1479":"b:f5DlwyX/sBsDAAAwIAAAIMjCETHkA6AA","1505":"b:f5DlwyX/sBsDAAAwIAAAIMjCETHkA6AA","1531":"b:f5DlwyX/sBsDAAAwIAAAIMjCETHkA6AA","1577":"b:f5DlwyX/sBsDAAAwIAAAIMjCETHkA6AA","1593":"b:f5DlwyX/sBsDAAAwIAAAIMjCETHkA6AA","1598":"b:f5DlwyX/sBsDAAAwIAAAIMjCETHkA6AA","1607":"b:f5DlwyX/sBsDAAAwIAAAIMjCETHkA6AA","1708":"b:f5DlwyX/sBsDAAAwIAAAIMjCETHkA6AA","1713":"b:f5DlwyX/sBsDAAAwIAAAIMjCETHkA6AA","1790":"b:f5DlwyX/sBsDAAAwIAAAIMjCETHkA6AA","1791":"b:f5DlwyX/sBsDAAAwIAAAIMjCETHkA6AA","1820":"b:f5DlwyX/sBsDAAAwIAAAIMjCETHkA6AA","1821":"b:f5DlwyX/sBsDAAAwIAAAIMjCETHkA6AA","1939":"b:f5DlwyX/sBsDAAAwIAAAIMjCETHkA6AA","1943":"b:f5DlwyX/sBsDAAAwIAAAIMjCETHkA6AA","1992":"b:f5DlwyX/sBsDAAAwIAAAIMjCETHkA6AA","2282":"b:f5DlwyX/sBsDAAAwIAAAIMjCETHkA6AA","2378":"b:f5DlwyX/sBsDAAAwIAAAIMjCETHkA6AA","2395":"b:f5DlwyX/sBsDAAAwIAAAIMjCETHkA6AA","2398":"b:f5DlwyX/sBsDAAAwIAAAIMjCETHkA6AA","2425":"b:f5DlwyX/sBsDAAAwIAAAIMjCETHkA6AA","2430":"b:f5DlwyX/sBsDAAAwIAAAIMjCETHkA6AA","2467":"b:f5DlwyX/sBsDAAAwIAAAIMjCETHkA6AA","2483":"b:f5DlwyX/sBsDAAAwIAAAIMjCETHkA6AA","2501":"b:f5DlwyX/sBsDAAAwIAAAIMjCETHkA6AA","2522":"b:f5DlwyX/sBsDAAAwIAAAIMjCETHkA6AA","2541":"b:f5DlwyX/sBsDAAAwIAAAIMjCETHkA6AA","2562":"b:f5DlwyX/sBsDAAAwIAAAIMjCETHkA6AA","2618":"b:f5DlwyX/sBsDAAAwIAAAIMjCETHkA6AA","2699":"b:f5DlwyX/sBsDAAAwIAAAIMjCETHkA6AA","2710":"b:f5DlwyX/sBsDAAAwIAAAIMjCETHkA6AA","2722":"b:f5DlwyX/sBsDAAAwIAAAIMjCETHkA6AA","2828":"b:f5DlwyX/sBsDAAAwIAAAIMjCETHkA6AA","2888":"b:f5DlwyX/sBsDAAAwIAAAIMjCETHkA6AA","2889":"b:f5DlwyX/sBsDAAAwIAAAIMjCETHkA6AA","2891":"b:f5DlwyX/sBsDAAAwIAAAIMjCETHkA6AA","2899":"b:f5DlwyX/sBsDAAAwIAAAIMjCETHkA6AA"},"packages/core/src/html-sandbox.ts":{"4":"b:RgDgQwCAEBACAAQAoAAACAAABCAEAwAA","8":"b:RgDgQwCAEBACAAQAoAAACAAABCAEAwAA","15":"b:RgDgQwCAEBACAAQAoAAACAAABCAEAwAA"},"packages/core/src/instance-settings/instance-settings.ts":{"74":[82,1,2,23,30,103,123,135,6,146,161],"101":[82,1,2,23,30,103,123,135,6,146,161],"118":[82,1,2,23,30,103,123,135,6,146,161],"119":[82,1,2,23,30,103,123,135,6,146,161],"128":[82,1,2,23,30,103,123,135,6,146,161],"129":[82,1,2,23,30,103,123,135,6,146,161],"135":[82,1,2,23,30,103,123,135,6,146,161],"190":[82,1,2,23,30,103,123,135,6,146,161],"194":[82,1,2,23,30,103,123,135,6,146,161],"199":[82,1,2,23,30,103,123,135,6,146,161],"204":[82,1,2,23,30,103,123,135,6,146,161],"208":[82,1,2,23,30,103,123,135,6,146,161],"212":[82,1,2,23,30,103,123,135,6,146,161],"216":[82,1,2,23,30,103,123,135,6,146,161],"220":[82,1,2,23,30,103,123,135,6,146,161],"224":[82,1,2,23,30,103,123,135,6,146,161],"228":[82,1,2,23,30,103,123,135,6,146,161],"232":[82,1,2,23,30,103,123,135,6,146,161],"236":[82,1,2,23,30,103,123,135,6,146,161],"240":[82,1,2,23,30,103,123,135,6,146,161],"248":[82,1,2,23,30,103,123,135,6,146,161],"271":[82,1,2,23,30,103,123,135,6,146,161],"279":[82,1,2,23,30,103,123,135,6,146,161],"326":[82,1,2,23,30,103,123,135,6,146,161],"333":[82,1,2,23,30,103,123,135,6,146,161],"341":[82,1,2,23,30,103,123,135,6,146,161],"349":[82,1,2,23,30,103,123,135,6,146,161],"377":[82,1,2,23,30,103,123,135,6,146,161],"382":[82,1,2,23,30,103,123,135,6,146,161],"403":[82,1,2,23,30,103,123,135,6,146,161],"415":[82,1,2,23,30,103,123,135,6,146,161]},"packages/core/src/node-execute-functions.ts":{"17":"b:R5BlwCX/sAkBAAAAAAAAAAACAAAAAAAA","31":"b:R5BlwCX/sAkBAAAAAAAAAAACAAAAAAAA"},"packages/core/src/nodes-loader/directory-loader.ts":{"37":"b:f5Xt06W32JoXAAQgiAAASAAIBCD9AgEA","88":"b:f5Xt06W32JoXAAQgiAAASAAIBCD9AgEA","108":"b:f5Xt06W32JoXAAQgiAAASAAIBCD9AgEA","117":"b:f5Xt06W32JoXAAQgiAAASAAIBCD9AgEA","123":"b:f5Xt06W32JoXAAQgiAAASAAIBCD9AgEA","135":"b:f5Xt06W32JoXAAQgiAAASAAIBCD9AgEA","139":"b:f5Xt06W32JoXAAQgiAAASAAIBCD9AgEA","143":"b:f5Xt06W32JoXAAQgiAAASAAIBCD9AgEA","145":"b:f5Xt06W32JoXAAQgiAAASAAIBCD9AgEA","146":"b:f5Xt06W32JoXAAQgiAAASAAIBCD9AgEA","147":"b:f5Xt06W32JoXAAQgiAAASAAIBCD9AgEA","150":"b:f5Xt06W32JoXAAQgiAAASAAIBCD9AgEA","165":"b:f5Xt06W32JoXAAQgiAAASAAIBCD9AgEA","229":"b:f5Xt06W32JoXAAQgiAAASAAIBCD9AgEA","242":"b:f5Xt06W32JoXAAQgiAAASAAIBCD9AgEA","260":"b:f5Xt06W32JoXAAQgiAAASAAIBCD9AgEA","289":"b:f5Xt06W32JoXAAQgiAAASAAIBCD9AgEA","310":"b:f5Xt06W32JoXAAQgiAAASAAIBCD9AgEA","313":"b:f5Xt06W32JoXAAQgiAAASAAIBCD9AgEA","325":"b:f5Xt06W32JoXAAQgiAAASAAIBCD9AgEA","327":"b:f5Xt06W32JoXAAQgiAAASAAIBCD9AgEA","332":"b:f5Xt06W32JoXAAQgiAAASAAIBCD9AgEA","344":"b:f5Xt06W32JoXAAQgiAAASAAIBCD9AgEA","369":"b:f5Xt06W32JoXAAQgiAAASAAIBCD9AgEA","400":"b:f5Xt06W32JoXAAQgiAAASAAIBCD9AgEA","406":"b:f5Xt06W32JoXAAQgiAAASAAIBCD9AgEA","412":"b:f5Xt06W32JoXAAQgiAAASAAIBCD9AgEA","424":"b:f5Xt06W32JoXAAQgiAAASAAIBCD9AgEA","443":"b:f5Xt06W32JoXAAQgiAAASAAIBCD9AgEA","460":"b:f5Xt06W32JoXAAQgiAAASAAIBCD9AgEA","479":"b:f5Xt06W32JoXAAQgiAAASAAIBCD9AgEA","499":"b:f5Xt06W32JoXAAQgiAAASAAIBCD9AgEA","513":"b:f5Xt06W32JoXAAQgiAAASAAIBCD9AgEA","533":"b:f5Xt06W32JoXAAQgiAAASAAIBCD9AgEA","539":"b:f5Xt06W32JoXAAQgiAAASAAIBCD9AgEA","540":"b:f5Xt06W32JoXAAQgiAAASAAIBCD9AgEA","542":"b:f5Xt06W32JoXAAQgiAAASAAIBCD9AgEA","548":"b:f5Xt06W32JoXAAQgiAAASAAIBCD9AgEA","582":"b:f5Xt06W32JoXAAQgiAAASAAIBCD9AgEA","604":"b:f5Xt06W32JoXAAQgiAAASAAIBCD9AgEA"},"packages/core/src/observability/tracing/tracing.ts":{"38":"b:R5T906e33JoXAAAAAAAAAAAAAADsAAEA","62":"b:R5T906e33JoXAAAAAAAAAAAAAADsAAEA","69":"b:R5T906e33JoXAAAAAAAAAAAAAADsAAEA","77":"b:R5T906e33JoXAAAAAAAAAAAAAADsAAEA","86":"b:R5T906e33JoXAAAAAAAAAAAAAADsAAEA","94":"b:R5T906e33JoXAAAAAAAAAAAAAADsAAEA","102":"b:R5T906e33JoXAAAAAAAAAAAAAADsAAEA"},"packages/core/src/storage.config.ts":{"22":"b:f5DlwyX3sBsDAAAAAAAAAAAAAADkAwAA","34":"b:f5DlwyX3sBsDAAAAAAAAAAAAAADkAwAA","40":"b:f5DlwyX3sBsDAAAAAAAAAAAAAADkAwAA","68":"b:f5DlwyX3sBsDAAAAAAAAAAAAAADkAwAA","103":"b:f5DlwyX3sBsDAAAAAAAAAAAAAADkAwAA"},"packages/core/src/utils/assertions.ts":{"14":"b:f5DlwyX3sBsDAAAAAAAAAAAAAADkAwAA","31":"b:f5DlwyX3sBsDAAAAAAAAAAAAAADkAwAA"},"packages/core/src/utils/convert-binary-data.ts":{"13":"b:f5DlwyX3sBsDAAAAAAAAAAAAAADkAwAA"},"packages/core/src/utils/deep-merge.ts":{"4":"b:f5DlwyX3sBsDAAAAAAAAAAAAAADkAwAA","17":"b:f5DlwyX3sBsDAAAAAAAAAAAAAADkAwAA"},"packages/nodes-base/nodes/Code/Code.node.ts":{"24":"b:RYAhEAFQAAABAAAAAAABEICQAWAAAQAA","192":"b:RYAhEAFQAAABAAAAAAABEICQAWAAAQAA"},"packages/nodes-base/nodes/Code/ExecutionError.ts":{"14":[0,2,143,68],"30":[0,2,143,68],"38":[0,2,143,68],"39":[0,2,143,68],"59":[0,2,143,68],"75":[0,2,143,68]},"packages/nodes-base/nodes/Code/JavaScriptSandbox.ts":{"30":[0,2,143,68],"52":[0,2,143,68],"55":[0,2,143,68],"65":[0,2,143,68],"85":[0,2,143,68],"93":[0,2,143,68],"103":[0,2,143,68]},"packages/nodes-base/nodes/Code/JsCodeValidator.ts":{"8":"b:R4IhEAEQAAARAAAAAAABEIiQAEAAAAAE","15":"b:R4IhEAEQAAARAAAAAAABEIiQAEAAAAAE","42":"b:R4IhEAEQAAARAAAAAAABEIiQAEAAAAAE","54":"b:R4IhEAEQAAARAAAAAAABEIiQAEAAAAAE"},"packages/nodes-base/nodes/Code/JsTaskRunnerSandbox.ts":{"20":"b:R4ohEAFQAAABAAAAAAABEIiQAWAAAQAE","30":"b:R4ohEAFQAAABAAAAAAABEIiQAWAAAQAE","56":"b:R4ohEAFQAAABAAAAAAABEIiQAWAAAQAE","78":"b:R4ohEAFQAAABAAAAAAABEIiQAWAAAQAE","123":"b:R4ohEAFQAAABAAAAAAABEIiQAWAAAQAE","145":"b:R4ohEAFQAAABAAAAAAABEIiQAWAAAQAE"},"packages/nodes-base/nodes/Code/PythonTaskRunnerSandbox.ts":{"18":"b:R4IhEAEQAAABAAAAAAABEICQAEAAAAAA","26":"b:R4IhEAEQAAABAAAAAAABEICQAEAAAAAA","42":"b:R4IhEAEQAAABAAAAAAABEICQAEAAAAAA","80":"b:R4IhEAEQAAABAAAAAAABEICQAEAAAAAA","98":"b:R4IhEAEQAAABAAAAAAABEICQAEAAAAAA"},"packages/nodes-base/nodes/Code/Sandbox.ts":{"24":[0,2,143,68],"46":[0,2,143,68],"59":[0,2,143,68],"71":[0,2,143,68]},"packages/nodes-base/nodes/Code/ValidationError.ts":{"12":"b:R4IhEAEQAAARAAAAAAABEICQAEAAAAAA"},"packages/nodes-base/nodes/Code/errors/WrappedExecutionError.ts":{"13":"b:R4IhEAEQAAABAAAAAAABEICQAHAAAAAA","22":"b:R4IhEAEQAAABAAAAAAABEICQAHAAAAAA","29":"b:R4IhEAEQAAABAAAAAAABEICQAHAAAAAA"},"packages/nodes-base/nodes/Code/python-runner-unavailable.error.ts":{"11":"b:RYAhEAEQAAABAAAAAAABEICQAEAAAAAA"},"packages/nodes-base/nodes/Code/reserved-key-found-error.ts":{"4":"b:R4IhEAEQAAARAAAAAAABEICQAEAAAAAA"},"packages/nodes-base/nodes/Code/result-validation.ts":{"16":"b:R4IhEAFQAAARAAAAAAABEIiQA2AAAQAE","25":"b:R4IhEAFQAAARAAAAAAABEIiQA2AAAQAE","31":"b:R4IhEAFQAAARAAAAAAABEIiQA2AAAQAE","61":"b:R4IhEAFQAAARAAAAAAABEIiQA2AAAQAE","86":"b:R4IhEAFQAAARAAAAAAABEIiQA2AAAQAE","126":"b:R4IhEAFQAAARAAAAAAABEIiQA2AAAQAE","159":"b:R4IhEAFQAAARAAAAAAABEIiQA2AAAQAE","160":"b:R4IhEAFQAAARAAAAAAABEIiQA2AAAQAE","172":"b:R4IhEAFQAAARAAAAAAABEIiQA2AAAQAE"},"packages/nodes-base/nodes/Code/throw-execution-error.ts":{"5":"b:R4IhEAEQAAABAAAAAAABEICQAHAAACAA"},"packages/nodes-base/nodes/Code/utils.ts":{"3":"b:R4IhEAFQAAARAAAAAAABEICQAWAAAQAA","9":"b:R4IhEAFQAAARAAAAAAABEICQAWAAAQAA","16":"b:R4IhEAFQAAARAAAAAAABEICQAWAAAQAA","17":"b:R4IhEAFQAAARAAAAAAABEICQAWAAAQAA","40":"b:R4IhEAFQAAARAAAAAAABEICQAWAAAQAA","47":"b:R4IhEAFQAAARAAAAAAABEICQAWAAAQAA"},"packages/nodes-base/nodes/ManualTrigger/ManualTrigger.node.ts":{"40":"b:R5Bl0KX/uIlHAAAAAAAAAAAAAEAAAAEA","41":"b:R5Bl0KX/uIlHAAAAAAAAAAAAAEAAAAEA"},"packages/nodes-base/nodes/Set/Set.node.ts":{"8":"b:RpDBQyCj2BAGAAQAAAAAiAFoIAAAAAAA"},"packages/nodes-base/nodes/Set/v1/SetV1.node.ts":{"139":"b:RpDBQyCj2BAGAAQAAAAAiIHqIwAAAAAA","146":"b:RpDBQyCj2BAGAAQAAAAAiIHqIwAAAAAA","182":"b:RpDBQyCj2BAGAAQAAAAAiIHqIwAAAAAA","193":"b:RpDBQyCj2BAGAAQAAAAAiIHqIwAAAAAA","211":"b:RpDBQyCj2BAGAAQAAAAAiIHqIwAAAAAA"},"packages/nodes-base/nodes/Set/v2/SetV2.node.ts":{"302":"b:RpDBQyCj2BEGAAQAgAAAiAFoIAAAAQAA","309":"b:RpDBQyCj2BEGAAQAgAAAiAFoIAAAAQAA"},"packages/nodes-base/nodes/Set/v2/helpers/utils.ts":{"28":"b:RpDBQyCj2BEGAAQAgAAAiAFoISAAAQAA","31":"b:RpDBQyCj2BEGAAQAgAAAiAFoISAAAQAA","34":"b:RpDBQyCj2BEGAAQAgAAAiAFoISAAAQAA","37":"b:RpDBQyCj2BEGAAQAgAAAiAFoISAAAQAA","43":"b:RpDBQyCj2BEGAAQAgAAAiAFoISAAAQAA","46":"b:RpDBQyCj2BEGAAQAgAAAiAFoISAAAQAA","49":"b:RpDBQyCj2BEGAAQAgAAAiAFoISAAAQAA","56":"b:RpDBQyCj2BEGAAQAgAAAiAFoISAAAQAA","89":"b:RpDBQyCj2BEGAAQAgAAAiAFoISAAAQAA","90":"b:RpDBQyCj2BEGAAQAgAAAiAFoISAAAQAA","104":"b:RpDBQyCj2BEGAAQAgAAAiAFoISAAAQAA","105":"b:RpDBQyCj2BEGAAQAgAAAiAFoISAAAQAA","130":"b:RpDBQyCj2BEGAAQAgAAAiAFoISAAAQAA","161":"b:RpDBQyCj2BEGAAQAgAAAiAFoISAAAQAA","222":"b:RpDBQyCj2BEGAAQAgAAAiAFoISAAAQAA","236":"b:RpDBQyCj2BEGAAQAgAAAiAFoISAAAQAA","238":"b:RpDBQyCj2BEGAAQAgAAAiAFoISAAAQAA","245":"b:RpDBQyCj2BEGAAQAgAAAiAFoISAAAQAA","265":"b:RpDBQyCj2BEGAAQAgAAAiAFoISAAAQAA"},"packages/nodes-base/nodes/Set/v2/manual.mode.ts":{"189":"b:RpDBQyCj2BEGAAQAgAAAiAFoIAAAAQAA"},"packages/nodes-base/nodes/Set/v2/raw.mode.ts":{"36":"b:RpDBQyCj2BAGAAQAAAAAiAFoIAAAAAAA"},"packages/nodes-base/utils/utilities.ts":{"34":"b:fpHNw6Tn+BAWAAQAIAAAgIFIoAAMACAA","53":"b:fpHNw6Tn+BAWAAQAIAAAgIFIoAAMACAA","65":"b:fpHNw6Tn+BAWAAQAIAAAgIFIoAAMACAA","70":"b:fpHNw6Tn+BAWAAQAIAAAgIFIoAAMACAA","86":"b:fpHNw6Tn+BAWAAQAIAAAgIFIoAAMACAA","89":"b:fpHNw6Tn+BAWAAQAIAAAgIFIoAAMACAA","113":"b:fpHNw6Tn+BAWAAQAIAAAgIFIoAAMACAA","136":"b:fpHNw6Tn+BAWAAQAIAAAgIFIoAAMACAA","140":"b:fpHNw6Tn+BAWAAQAIAAAgIFIoAAMACAA","148":"b:fpHNw6Tn+BAWAAQAIAAAgIFIoAAMACAA","167":"b:fpHNw6Tn+BAWAAQAIAAAgIFIoAAMACAA","174":"b:fpHNw6Tn+BAWAAQAIAAAgIFIoAAMACAA","183":"b:fpHNw6Tn+BAWAAQAIAAAgIFIoAAMACAA","186":"b:fpHNw6Tn+BAWAAQAIAAAgIFIoAAMACAA","189":"b:fpHNw6Tn+BAWAAQAIAAAgIFIoAAMACAA","267":"b:fpHNw6Tn+BAWAAQAIAAAgIFIoAAMACAA","271":"b:fpHNw6Tn+BAWAAQAIAAAgIFIoAAMACAA","276":"b:fpHNw6Tn+BAWAAQAIAAAgIFIoAAMACAA","278":"b:fpHNw6Tn+BAWAAQAIAAAgIFIoAAMACAA","287":"b:fpHNw6Tn+BAWAAQAIAAAgIFIoAAMACAA","309":"b:fpHNw6Tn+BAWAAQAIAAAgIFIoAAMACAA","336":"b:fpHNw6Tn+BAWAAQAIAAAgIFIoAAMACAA","345":"b:fpHNw6Tn+BAWAAQAIAAAgIFIoAAMACAA","346":"b:fpHNw6Tn+BAWAAQAIAAAgIFIoAAMACAA","356":"b:fpHNw6Tn+BAWAAQAIAAAgIFIoAAMACAA","365":"b:fpHNw6Tn+BAWAAQAIAAAgIFIoAAMACAA","387":"b:fpHNw6Tn+BAWAAQAIAAAgIFIoAAMACAA","389":"b:fpHNw6Tn+BAWAAQAIAAAgIFIoAAMACAA","413":"b:fpHNw6Tn+BAWAAQAIAAAgIFIoAAMACAA","414":"b:fpHNw6Tn+BAWAAQAIAAAgIFIoAAMACAA","417":"b:fpHNw6Tn+BAWAAQAIAAAgIFIoAAMACAA","441":"b:fpHNw6Tn+BAWAAQAIAAAgIFIoAAMACAA","447":"b:fpHNw6Tn+BAWAAQAIAAAgIFIoAAMACAA","454":"b:fpHNw6Tn+BAWAAQAIAAAgIFIoAAMACAA"},"packages/workflow/src/common/get-child-nodes.ts":{"5":"b:fgjkwyX3sBsDAAQAAAAAAAAAAADkCwAA"},"packages/workflow/src/common/get-connected-nodes.ts":{"13":"b:f5Dlw6X/sBsDAAAAAAAAAAiqAQHkAwAA","29":"b:f5Dlw6X/sBsDAAAAAAAAAAiqAQHkAwAA","63":"b:f5Dlw6X/sBsDAAAAAAAAAAiqAQHkAwAA","77":"b:f5Dlw6X/sBsDAAAAAAAAAAiqAQHkAwAA","83":"b:f5Dlw6X/sBsDAAAAAAAAAAiqAQHkAwAA","84":"b:f5Dlw6X/sBsDAAAAAAAAAAiqAQHkAwAA"},"packages/workflow/src/common/get-parent-nodes.ts":{"11":"b:f5LBgKALAAACAAAAQAAAEACoAAEAAQAA"},"packages/workflow/src/common/map-connections-by-destination.ts":{"5":"b:f5Dl06W3uBsTAAAAAAAAAAAAAADsAAAA"},"packages/workflow/src/common/resolve-variables.ts":{"23":"b:f5Dl0yW3uBsTAAAAAAAAAAAAAADsAAAA","51":"b:f5Dl0yW3uBsTAAAAAAAAAAAAAADsAAAA"},"packages/workflow/src/credential-domain-restrictions.ts":{"55":"b:B4Xd0AWk2IjdYBAAAAAAAAAAAABoAgAA","59":"b:B4Xd0AWk2IjdYBAAAAAAAAAAAABoAgAA","66":"b:B4Xd0AWk2IjdYBAAAAAAAAAAAABoAgAA","75":"b:B4Xd0AWk2IjdYBAAAAAAAAAAAABoAgAA","87":"b:B4Xd0AWk2IjdYBAAAAAAAAAAAABoAgAA","92":"b:B4Xd0AWk2IjdYBAAAAAAAAAAAABoAgAA","99":"b:B4Xd0AWk2IjdYBAAAAAAAAAAAABoAgAA","117":"b:B4Xd0AWk2IjdYBAAAAAAAAAAAABoAgAA","134":"b:B4Xd0AWk2IjdYBAAAAAAAAAAAABoAgAA","148":"b:B4Xd0AWk2IjdYBAAAAAAAAAAAABoAgAA","182":"b:B4Xd0AWk2IjdYBAAAAAAAAAAAABoAgAA"},"packages/workflow/src/dynamic-credentials-helpers.ts":{"9":"b:f5LlwyX3sBsDAAAAAAAAAAAAgAHkAyAA","10":"b:f5LlwyX3sBsDAAAAAAAAAAAAgAHkAyAA","13":"b:f5LlwyX3sBsDAAAAAAAAAAAAgAHkAyAA","22":"b:f5LlwyX3sBsDAAAAAAAAAAAAgAHkAyAA","23":"b:f5LlwyX3sBsDAAAAAAAAAAAAgAHkAyAA","24":"b:f5LlwyX3sBsDAAAAAAAAAAAAgAHkAyAA","33":"b:f5LlwyX3sBsDAAAAAAAAAAAAgAHkAyAA","66":"b:f5LlwyX3sBsDAAAAAAAAAAAAgAHkAyAA","87":"b:f5LlwyX3sBsDAAAAAAAAAAAAgAHkAyAA","99":"b:f5LlwyX3sBsDAAAAAAAAAAAAgAHkAyAA"},"packages/workflow/src/errors/base/base.error.ts":{"32":"b:f4IhEQUREAADDDMAAAAAAECCAQAEEQEA"},"packages/workflow/src/errors/base/operational.error.ts":{"16":"b:V4AhEAEQAAABAAAwAAAAAICACUAAAQAA"},"packages/workflow/src/errors/base/unexpected.error.ts":{"16":"b:RQohAQNRAAABAAEAAAAAAACAAUAAAQAA"},"packages/workflow/src/errors/base/user.error.ts":{"19":"b:VQohAAFQAAABADAQAAAAAICCAQAAAQAA"},"packages/workflow/src/execution-context-establishment-hooks.ts":{"41":"b:f5DlwyX3sBsDAAAAAAAAAAAAAADkAwAA"},"packages/workflow/src/execution-context.ts":{"12":"b:R5BlwAX/sAkBAAAAAAAAAAACAAAAAgAA","294":"b:R5BlwAX/sAkBAAAAAAAAAAACAAAAAgAA","316":"b:R5BlwAX/sAkBAAAAAAAAAAACAAAAAgAA","329":"b:R5BlwAX/sAkBAAAAAAAAAAACAAAAAgAA","334":"b:R5BlwAX/sAkBAAAAAAAAAAACAAAAAgAA"},"packages/workflow/src/execution-status.ts":{"16":"b:R5Dlwyf3sBsDAAAAAAAAAAAAAADkAwAA","29":"b:R5Dlwyf3sBsDAAAAAAAAAAAAAADkAwAA"},"packages/workflow/src/expression-sandboxing.ts":{"22":"b:fwjAQwCQMBECAAAAgAAACADIBSAEAyAA","25":"b:fwjAQwCQMBECAAAAgAAACADIBSAEAyAA","73":"b:fwjAQwCQMBECAAAAgAAACADIBSAEAyAA","74":"b:fwjAQwCQMBECAAAAgAAACADIBSAEAyAA","93":"b:fwjAQwCQMBECAAAAgAAACADIBSAEAyAA","137":"b:fwjAQwCQMBECAAAAgAAACADIBSAEAyAA","139":"b:fwjAQwCQMBECAAAAgAAACADIBSAEAyAA","171":"b:fwjAQwCQMBECAAAAgAAACADIBSAEAyAA","195":"b:fwjAQwCQMBECAAAAgAAACADIBSAEAyAA","211":"b:fwjAQwCQMBECAAAAgAAACADIBSAEAyAA","248":"b:fwjAQwCQMBECAAAAgAAACADIBSAEAyAA","250":"b:fwjAQwCQMBECAAAAgAAACADIBSAEAyAA","326":"b:fwjAQwCQMBECAAAAgAAACADIBSAEAyAA","356":"b:fwjAQwCQMBECAAAAgAAACADIBSAEAyAA","358":"b:fwjAQwCQMBECAAAAgAAACADIBSAEAyAA","367":"b:fwjAQwCQMBECAAAAgAAACADIBSAEAyAA","384":"b:fwjAQwCQMBECAAAAgAAACADIBSAEAyAA","393":"b:fwjAQwCQMBECAAAAgAAACADIBSAEAyAA","413":"b:fwjAQwCQMBECAAAAgAAACADIBSAEAyAA","433":"b:fwjAQwCQMBECAAAAgAAACADIBSAEAyAA","442":"b:fwjAQwCQMBECAAAAgAAACADIBSAEAyAA","451":"b:fwjAQwCQMBECAAAAgAAACADIBSAEAyAA","460":"b:fwjAQwCQMBECAAAAgAAACADIBSAEAyAA","469":"b:fwjAQwCQMBECAAAAgAAACADIBSAEAyAA","508":"b:fwjAQwCQMBECAAAAgAAACADIBSAEAyAA","533":"b:fwjAQwCQMBECAAAAgAAACADIBSAEAyAA","542":"b:fwjAQwCQMBECAAAAgAAACADIBSAEAyAA","551":"b:fwjAQwCQMBECAAAAgAAACADIBSAEAyAA","557":"b:fwjAQwCQMBECAAAAgAAACADIBSAEAyAA"},"packages/workflow/src/expression.ts":{"35":"b:f5Dl0yW3uBsTAAAAgAAACADIAQDsAgAA","38":"b:f5Dl0yW3uBsTAAAAgAAACADIAQDsAgAA","41":"b:f5Dl0yW3uBsTAAAAgAAACADIAQDsAgAA","45":"b:f5Dl0yW3uBsTAAAAgAAACADIAQDsAgAA","59":"b:f5Dl0yW3uBsTAAAAgAAACADIAQDsAgAA","112":"b:f5Dl0yW3uBsTAAAAgAAACADIAQDsAgAA","113":"b:f5Dl0yW3uBsTAAAAgAAACADIAQDsAgAA","137":"b:f5Dl0yW3uBsTAAAAgAAACADIAQDsAgAA","151":"b:f5Dl0yW3uBsTAAAAgAAACADIAQDsAgAA","183":"b:f5Dl0yW3uBsTAAAAgAAACADIAQDsAgAA","185":"b:f5Dl0yW3uBsTAAAAgAAACADIAQDsAgAA","193":"b:f5Dl0yW3uBsTAAAAgAAACADIAQDsAgAA","197":"b:f5Dl0yW3uBsTAAAAgAAACADIAQDsAgAA","208":"b:f5Dl0yW3uBsTAAAAgAAACADIAQDsAgAA","210":"b:f5Dl0yW3uBsTAAAAgAAACADIAQDsAgAA","217":"b:f5Dl0yW3uBsTAAAAgAAACADIAQDsAgAA","220":"b:f5Dl0yW3uBsTAAAAgAAACADIAQDsAgAA","231":"b:f5Dl0yW3uBsTAAAAgAAACADIAQDsAgAA","237":"b:f5Dl0yW3uBsTAAAAgAAACADIAQDsAgAA","246":"b:f5Dl0yW3uBsTAAAAgAAACADIAQDsAgAA","262":"b:f5Dl0yW3uBsTAAAAgAAACADIAQDsAgAA","283":"b:f5Dl0yW3uBsTAAAAgAAACADIAQDsAgAA","288":"b:f5Dl0yW3uBsTAAAAgAAACADIAQDsAgAA","296":"b:f5Dl0yW3uBsTAAAAgAAACADIAQDsAgAA","307":"b:f5Dl0yW3uBsTAAAAgAAACADIAQDsAgAA","320":"b:f5Dl0yW3uBsTAAAAgAAACADIAQDsAgAA","324":"b:f5Dl0yW3uBsTAAAAgAAACADIAQDsAgAA","481":"b:f5Dl0yW3uBsTAAAAgAAACADIAQDsAgAA","490":"b:f5Dl0yW3uBsTAAAAgAAACADIAQDsAgAA","531":"b:f5Dl0yW3uBsTAAAAgAAACADIAQDsAgAA","628":"b:f5Dl0yW3uBsTAAAAgAAACADIAQDsAgAA"},"packages/workflow/src/extensions/expression-extension.ts":{"25":"b:f4DAQwCQMBECAAAAoAAACABIAQEEAgAA","29":"b:f4DAQwCQMBECAAAAoAAACABIAQEEAgAA","64":"b:f4DAQwCQMBECAAAAoAAACABIAQEEAgAA","66":"b:f4DAQwCQMBECAAAAoAAACABIAQEEAgAA","69":"b:f4DAQwCQMBECAAAAoAAACABIAQEEAgAA","77":"b:f4DAQwCQMBECAAAAoAAACABIAQEEAgAA","79":"b:f4DAQwCQMBECAAAAoAAACABIAQEEAgAA","95":"b:f4DAQwCQMBECAAAAoAAACABIAQEEAgAA","124":"b:f4DAQwCQMBECAAAAoAAACABIAQEEAgAA","133":"b:f4DAQwCQMBECAAAAoAAACABIAQEEAgAA","160":"b:f4DAQwCQMBECAAAAoAAACABIAQEEAgAA","174":"b:f4DAQwCQMBECAAAAoAAACABIAQEEAgAA","183":"b:f4DAQwCQMBECAAAAoAAACABIAQEEAgAA","196":"b:f4DAQwCQMBECAAAAoAAACABIAQEEAgAA","208":"b:f4DAQwCQMBECAAAAoAAACABIAQEEAgAA","387":"b:f4DAQwCQMBECAAAAoAAACABIAQEEAgAA","405":"b:f4DAQwCQMBECAAAAoAAACABIAQEEAgAA","440":"b:f4DAQwCQMBECAAAAoAAACABIAQEEAgAA","457":"b:f4DAQwCQMBECAAAAoAAACABIAQEEAgAA","519":"b:f4DAQwCQMBECAAAAoAAACABIAQEEAgAA","527":"b:f4DAQwCQMBECAAAAoAAACABIAQEEAgAA","535":"b:f4DAQwCQMBECAAAAoAAACABIAQEEAgAA","555":"b:f4DAQwCQMBECAAAAoAAACABIAQEEAgAA","571":"b:f4DAQwCQMBECAAAAoAAACABIAQEEAgAA","579":"b:f4DAQwCQMBECAAAAoAAACABIAQEEAgAA","583":"b:f4DAQwCQMBECAAAAoAAACABIAQEEAgAA","584":"b:f4DAQwCQMBECAAAAoAAACABIAQEEAgAA","598":"b:f4DAQwCQMBECAAAAoAAACABIAQEEAgAA"},"packages/workflow/src/extensions/expression-parser.ts":{"20":"b:f4DAQwCQMBECAAAAoAAACABIAQEEAgAA","24":"b:f4DAQwCQMBECAAAAoAAACABIAQEEAgAA","93":"b:f4DAQwCQMBECAAAAoAAACABIAQEEAgAA","97":"b:f4DAQwCQMBECAAAAoAAACABIAQEEAgAA","99":"b:f4DAQwCQMBECAAAAoAAACABIAQEEAgAA"},"packages/workflow/src/global-state.ts":{"9":"b:f5Dl0yW3uBsTAAAAAAAAAAAAAADsAAAA","13":"b:f5Dl0yW3uBsTAAAAAAAAAAAAAADsAAAA"},"packages/workflow/src/highlighted-data.ts":{"3":[82,1,2,3,4,5,109,123,6],"7":[82,1,2,3,4,5,109,123,6]},"packages/workflow/src/index.ts":{"93":"b:f53/8af/+JlXAAAAoAAAAoDAGQBcA6AA","102":"b:f53/8af/+JlXAAAAoAAAAoDAGQBcA6AA"},"packages/workflow/src/interfaces.ts":{"128":"b:f9nt4y33vJ9TAAAAoBAAAAAAAADsAwAA","2461":"b:f9nt4y33vJ9TAAAAoBAAAAAAAADsAwAA","2478":"b:f9nt4y33vJ9TAAAAoBAAAAAAAADsAwAA"},"packages/workflow/src/node-grouping-validation.ts":{"30":"b:R5T906e33JoXAAAAAAAAAAEAAADsAAEA","106":"b:R5T906e33JoXAAAAAAAAAAEAAADsAAEA","114":"b:R5T906e33JoXAAAAAAAAAAEAAADsAAEA","133":"b:R5T906e33JoXAAAAAAAAAAEAAADsAAEA","137":"b:R5T906e33JoXAAAAAAAAAAEAAADsAAEA","152":"b:R5T906e33JoXAAAAAAAAAAEAAADsAAEA","164":"b:R5T906e33JoXAAAAAAAAAAEAAADsAAEA","234":"b:R5T906e33JoXAAAAAAAAAAEAAADsAAEA","245":"b:R5T906e33JoXAAAAAAAAAAEAAADsAAEA","253":"b:R5T906e33JoXAAAAAAAAAAEAAADsAAEA","256":"b:R5T906e33JoXAAAAAAAAAAEAAADsAAEA","262":"b:R5T906e33JoXAAAAAAAAAAEAAADsAAEA","313":"b:R5T906e33JoXAAAAAAAAAAEAAADsAAEA","318":"b:R5T906e33JoXAAAAAAAAAAEAAADsAAEA","336":"b:R5T906e33JoXAAAAAAAAAAEAAADsAAEA","359":"b:R5T906e33JoXAAAAAAAAAAEAAADsAAEA","364":"b:R5T906e33JoXAAAAAAAAAAEAAADsAAEA","372":"b:R5T906e33JoXAAAAAAAAAAEAAADsAAEA","377":"b:R5T906e33JoXAAAAAAAAAAEAAADsAAEA","395":"b:R5T906e33JoXAAAAAAAAAAEAAADsAAEA","399":"b:R5T906e33JoXAAAAAAAAAAEAAADsAAEA","400":"b:R5T906e33JoXAAAAAAAAAAEAAADsAAEA","403":"b:R5T906e33JoXAAAAAAAAAAEAAADsAAEA","430":"b:R5T906e33JoXAAAAAAAAAAEAAADsAAEA","436":"b:R5T906e33JoXAAAAAAAAAAEAAADsAAEA","481":"b:R5T906e33JoXAAAAAAAAAAEAAADsAAEA","491":"b:R5T906e33JoXAAAAAAAAAAEAAADsAAEA"},"packages/workflow/src/node-helpers.ts":{"256":"b:f//986///P/fYBQw6CAAWMH6XSH9E6EB","264":"b:f//986///P/fYBQw6CAAWMH6XSH9E6EB","274":"b:f//986///P/fYBQw6CAAWMH6XSH9E6EB","284":"b:f//986///P/fYBQw6CAAWMH6XSH9E6EB","299":"b:f//986///P/fYBQw6CAAWMH6XSH9E6EB","321":"b:f//986///P/fYBQw6CAAWMH6XSH9E6EB","322":"b:f//986///P/fYBQw6CAAWMH6XSH9E6EB","339":"b:f//986///P/fYBQw6CAAWMH6XSH9E6EB","343":"b:f//986///P/fYBQw6CAAWMH6XSH9E6EB","358":"b:f//986///P/fYBQw6CAAWMH6XSH9E6EB","417":"b:f//986///P/fYBQw6CAAWMH6XSH9E6EB","444":"b:f//986///P/fYBQw6CAAWMH6XSH9E6EB","483":"b:f//986///P/fYBQw6CAAWMH6XSH9E6EB","519":"b:f//986///P/fYBQw6CAAWMH6XSH9E6EB","556":"b:f//986///P/fYBQw6CAAWMH6XSH9E6EB","595":"b:f//986///P/fYBQw6CAAWMH6XSH9E6EB","600":"b:f//986///P/fYBQw6CAAWMH6XSH9E6EB","676":"b:f//986///P/fYBQw6CAAWMH6XSH9E6EB","944":"b:f//986///P/fYBQw6CAAWMH6XSH9E6EB","983":"b:f//986///P/fYBQw6CAAWMH6XSH9E6EB","1015":"b:f//986///P/fYBQw6CAAWMH6XSH9E6EB","1016":"b:f//986///P/fYBQw6CAAWMH6XSH9E6EB","1064":"b:f//986///P/fYBQw6CAAWMH6XSH9E6EB","1094":"b:f//986///P/fYBQw6CAAWMH6XSH9E6EB","1124":"b:f//986///P/fYBQw6CAAWMH6XSH9E6EB","1145":"b:f//986///P/fYBQw6CAAWMH6XSH9E6EB","1154":"b:f//986///P/fYBQw6CAAWMH6XSH9E6EB","1158":"b:f//986///P/fYBQw6CAAWMH6XSH9E6EB","1164":"b:f//986///P/fYBQw6CAAWMH6XSH9E6EB","1167":"b:f//986///P/fYBQw6CAAWMH6XSH9E6EB","1190":"b:f//986///P/fYBQw6CAAWMH6XSH9E6EB","1252":"b:f//986///P/fYBQw6CAAWMH6XSH9E6EB","1288":"b:f//986///P/fYBQw6CAAWMH6XSH9E6EB","1314":"b:f//986///P/fYBQw6CAAWMH6XSH9E6EB","1344":"b:f//986///P/fYBQw6CAAWMH6XSH9E6EB","1358":"b:f//986///P/fYBQw6CAAWMH6XSH9E6EB","1383":"b:f//986///P/fYBQw6CAAWMH6XSH9E6EB","1411":"b:f//986///P/fYBQw6CAAWMH6XSH9E6EB","1448":"b:f//986///P/fYBQw6CAAWMH6XSH9E6EB","1463":"b:f//986///P/fYBQw6CAAWMH6XSH9E6EB","1474":"b:f//986///P/fYBQw6CAAWMH6XSH9E6EB","1479":"b:f//986///P/fYBQw6CAAWMH6XSH9E6EB","1505":"b:f//986///P/fYBQw6CAAWMH6XSH9E6EB","1517":"b:f//986///P/fYBQw6CAAWMH6XSH9E6EB","1562":"b:f//986///P/fYBQw6CAAWMH6XSH9E6EB","1565":"b:f//986///P/fYBQw6CAAWMH6XSH9E6EB","1730":"b:f//986///P/fYBQw6CAAWMH6XSH9E6EB","1771":"b:f//986///P/fYBQw6CAAWMH6XSH9E6EB","1779":"b:f//986///P/fYBQw6CAAWMH6XSH9E6EB","1791":"b:f//986///P/fYBQw6CAAWMH6XSH9E6EB","1801":"b:f//986///P/fYBQw6CAAWMH6XSH9E6EB","1805":"b:f//986///P/fYBQw6CAAWMH6XSH9E6EB","1822":"b:f//986///P/fYBQw6CAAWMH6XSH9E6EB","1835":"b:f//986///P/fYBQw6CAAWMH6XSH9E6EB","1841":"b:f//986///P/fYBQw6CAAWMH6XSH9E6EB","1843":"b:f//986///P/fYBQw6CAAWMH6XSH9E6EB","1851":"b:f//986///P/fYBQw6CAAWMH6XSH9E6EB","1855":"b:f//986///P/fYBQw6CAAWMH6XSH9E6EB","1879":"b:f//986///P/fYBQw6CAAWMH6XSH9E6EB","1914":"b:f//986///P/fYBQw6CAAWMH6XSH9E6EB","1921":"b:f//986///P/fYBQw6CAAWMH6XSH9E6EB","1937":"b:f//986///P/fYBQw6CAAWMH6XSH9E6EB","1942":"b:f//986///P/fYBQw6CAAWMH6XSH9E6EB","1976":"b:f//986///P/fYBQw6CAAWMH6XSH9E6EB","2009":"b:f//986///P/fYBQw6CAAWMH6XSH9E6EB","2024":"b:f//986///P/fYBQw6CAAWMH6XSH9E6EB","2056":"b:f//986///P/fYBQw6CAAWMH6XSH9E6EB","2083":"b:f//986///P/fYBQw6CAAWMH6XSH9E6EB","2096":"b:f//986///P/fYBQw6CAAWMH6XSH9E6EB","2110":"b:f//986///P/fYBQw6CAAWMH6XSH9E6EB","2124":"b:f//986///P/fYBQw6CAAWMH6XSH9E6EB","2135":"b:f//986///P/fYBQw6CAAWMH6XSH9E6EB"},"packages/workflow/src/node-parameters/parameter-type-validation.ts":{"15":[82,1,2,3,4,5,109,123,6],"30":[82,1,2,3,4,5,109,123,6],"39":[82,1,2,3,4,5,109,123,6],"47":[82,1,2,3,4,5,109,123,6],"55":[82,1,2,3,4,5,109,123,6],"69":[82,1,2,3,4,5,109,123,6],"75":[82,1,2,3,4,5,109,123,6],"82":[82,1,2,3,4,5,109,123,6],"103":[82,1,2,3,4,5,109,123,6],"110":[82,1,2,3,4,5,109,123,6],"121":[82,1,2,3,4,5,109,123,6],"133":[82,1,2,3,4,5,109,123,6],"141":[82,1,2,3,4,5,109,123,6],"142":[82,1,2,3,4,5,109,123,6],"148":[82,1,2,3,4,5,109,123,6],"162":[82,1,2,3,4,5,109,123,6],"175":[82,1,2,3,4,5,109,123,6],"197":[82,1,2,3,4,5,109,123,6],"246":[82,1,2,3,4,5,109,123,6],"259":[82,1,2,3,4,5,109,123,6]},"packages/workflow/src/observable-object.ts":{"9":"b:f5rl0yX3uBsbAAAAQAAAAIKKBUj8A0AA","38":"b:f5rl0yX3uBsbAAAAQAAAAIKKBUj8A0AA","48":"b:f5rl0yX3uBsbAAAAQAAAAIKKBUj8A0AA","51":"b:f5rl0yX3uBsbAAAAQAAAAIKKBUj8A0AA","54":"b:f5rl0yX3uBsbAAAAQAAAAIKKBUj8A0AA"},"packages/workflow/src/redaction-channels.ts":{"23":"b:f5DlwyX3sBsDAAAAAAAAAAAAAADkAwAA","37":"b:f5DlwyX3sBsDAAAAAAAAAAAAAADkAwAA","52":"b:f5DlwyX3sBsDAAAAAAAAAAAAAADkAwAA"},"packages/workflow/src/run-execution-data/run-execution-data.ts":{"29":"b:f5DlwyX3sAMDAAAAAAAAAAAAAAAAAAAA"},"packages/workflow/src/run-execution-data-factory.ts":{"54":"b:f5jlwyX3sBsTAAAAgAAACAAABADtAwAA","67":"b:f5jlwyX3sBsTAAAAgAAACAAABADtAwAA","86":"b:f5jlwyX3sBsTAAAAgAAACAAABADtAwAA","108":"b:f5jlwyX3sBsTAAAAgAAACAAABADtAwAA","125":"b:f5jlwyX3sBsTAAAAgAAACAAABADtAwAA"},"packages/workflow/src/type-guards.ts":{"15":"b:f5jFw6f/sBEDAAAggAAACIDAFQAFAqAA","21":"b:f5jFw6f/sBEDAAAggAAACIDAFQAFAqAA","25":"b:f5jFw6f/sBEDAAAggAAACIDAFQAFAqAA","29":"b:f5jFw6f/sBEDAAAggAAACIDAFQAFAqAA","33":"b:f5jFw6f/sBEDAAAggAAACIDAFQAFAqAA","37":"b:f5jFw6f/sBEDAAAggAAACIDAFQAFAqAA","41":"b:f5jFw6f/sBEDAAAggAAACIDAFQAFAqAA","47":"b:f5jFw6f/sBEDAAAggAAACIDAFQAFAqAA","60":"b:f5jFw6f/sBEDAAAggAAACIDAFQAFAqAA","70":"b:f5jFw6f/sBEDAAAggAAACIDAFQAFAqAA","83":"b:f5jFw6f/sBEDAAAggAAACIDAFQAFAqAA","93":"b:f5jFw6f/sBEDAAAggAAACIDAFQAFAqAA","99":"b:f5jFw6f/sBEDAAAggAAACIDAFQAFAqAA","103":"b:f5jFw6f/sBEDAAAggAAACIDAFQAFAqAA"},"packages/workflow/src/type-validation.ts":{"15":"b:f5rAQ7C3kBECAAQAgAAAKIDoAQAAAQAA","24":"b:f5rAQ7C3kBECAAQAgAAAKIDoAQAAAQAA","38":"b:f5rAQ7C3kBECAAQAgAAAKIDoAQAAAQAA","48":"b:f5rAQ7C3kBECAAQAgAAAKIDoAQAAAQAA","72":"b:f5rAQ7C3kBECAAQAgAAAKIDoAQAAAQAA","114":"b:f5rAQ7C3kBECAAQAgAAAKIDoAQAAAQAA","124":"b:f5rAQ7C3kBECAAQAgAAAKIDoAQAAAQAA","146":"b:f5rAQ7C3kBECAAQAgAAAKIDoAQAAAQAA","162":"b:f5rAQ7C3kBECAAQAgAAAKIDoAQAAAQAA","206":"b:f5rAQ7C3kBECAAQAgAAAKIDoAQAAAQAA","272":"b:f5rAQ7C3kBECAAQAgAAAKIDoAQAAAQAA","284":"b:f5rAQ7C3kBECAAQAgAAAKIDoAQAAAQAA","305":"b:f5rAQ7C3kBECAAQAgAAAKIDoAQAAAQAA","333":"b:f5rAQ7C3kBECAAQAgAAAKIDoAQAAAQAA","437":"b:f5rAQ7C3kBECAAQAgAAAKIDoAQAAAQAA","438":"b:f5rAQ7C3kBECAAQAgAAAKIDoAQAAAQAA"},"packages/workflow/src/utils.ts":{"18":"b:f53/06f3/JsXAAAgoAAACsCIAQDsAwAA","29":"b:f53/06f3/JsXAAAgoAAACsCIAQDsAwAA","37":"b:f53/06f3/JsXAAAgoAAACsCIAQDsAwAA","58":"b:f53/06f3/JsXAAAgoAAACsCIAQDsAwAA","97":"b:f53/06f3/JsXAAAgoAAACsCIAQDsAwAA","102":"b:f53/06f3/JsXAAAgoAAACsCIAQDsAwAA","103":"b:f53/06f3/JsXAAAgoAAACsCIAQDsAwAA","110":"b:f53/06f3/JsXAAAgoAAACsCIAQDsAwAA","128":"b:f53/06f3/JsXAAAgoAAACsCIAQDsAwAA","130":"b:f53/06f3/JsXAAAgoAAACsCIAQDsAwAA","157":"b:f53/06f3/JsXAAAgoAAACsCIAQDsAwAA","200":"b:f53/06f3/JsXAAAgoAAACsCIAQDsAwAA","206":"b:f53/06f3/JsXAAAgoAAACsCIAQDsAwAA","216":"b:f53/06f3/JsXAAAgoAAACsCIAQDsAwAA","241":"b:f53/06f3/JsXAAAgoAAACsCIAQDsAwAA","245":"b:f53/06f3/JsXAAAgoAAACsCIAQDsAwAA","246":"b:f53/06f3/JsXAAAgoAAACsCIAQDsAwAA","250":"b:f53/06f3/JsXAAAgoAAACsCIAQDsAwAA","251":"b:f53/06f3/JsXAAAgoAAACsCIAQDsAwAA","259":"b:f53/06f3/JsXAAAgoAAACsCIAQDsAwAA","267":"b:f53/06f3/JsXAAAgoAAACsCIAQDsAwAA","278":"b:f53/06f3/JsXAAAgoAAACsCIAQDsAwAA","296":"b:f53/06f3/JsXAAAgoAAACsCIAQDsAwAA","300":"b:f53/06f3/JsXAAAgoAAACsCIAQDsAwAA","302":"b:f53/06f3/JsXAAAgoAAACsCIAQDsAwAA","309":"b:f53/06f3/JsXAAAgoAAACsCIAQDsAwAA","322":"b:f53/06f3/JsXAAAgoAAACsCIAQDsAwAA","326":"b:f53/06f3/JsXAAAgoAAACsCIAQDsAwAA","343":"b:f53/06f3/JsXAAAgoAAACsCIAQDsAwAA","360":"b:f53/06f3/JsXAAAgoAAACsCIAQDsAwAA","363":"b:f53/06f3/JsXAAAgoAAACsCIAQDsAwAA","370":"b:f53/06f3/JsXAAAgoAAACsCIAQDsAwAA","408":"b:f53/06f3/JsXAAAgoAAACsCIAQDsAwAA","414":"b:f53/06f3/JsXAAAgoAAACsCIAQDsAwAA","418":"b:f53/06f3/JsXAAAgoAAACsCIAQDsAwAA","427":"b:f53/06f3/JsXAAAgoAAACsCIAQDsAwAA","439":"b:f53/06f3/JsXAAAgoAAACsCIAQDsAwAA","447":"b:f53/06f3/JsXAAAgoAAACsCIAQDsAwAA","455":"b:f53/06f3/JsXAAAgoAAACsCIAQDsAwAA","474":"b:f53/06f3/JsXAAAgoAAACsCIAQDsAwAA","493":"b:f53/06f3/JsXAAAgoAAACsCIAQDsAwAA","496":"b:f53/06f3/JsXAAAgoAAACsCIAQDsAwAA"},"packages/workflow/src/versioned-node-type.ts":{"10":"b:fpXNw6S32JAWAAQAAAAACAAIAAAMAAAA","19":"b:fpXNw6S32JAWAAQAAAAACAAIAAAMAAAA","23":"b:fpXNw6S32JAWAAQAAAAACAAIAAAMAAAA"},"packages/workflow/src/workflow-checksum.ts":{"46":"b:R5T906e33JoXAAAAAAAAAAAAAADsAAAA","50":"b:R5T906e33JoXAAAAAAAAAAAAAADsAAAA","74":"b:R5T906e33JoXAAAAAAAAAAAAAADsAAAA","99":"b:R5T906e33JoXAAAAAAAAAAAAAADsAAAA"},"packages/workflow/src/workflow-data-proxy-env-provider.ts":{"13":"b:f4DhQwHQMBEDAAAAgAAACABIAQAEAgAA","41":"b:f4DhQwHQMBEDAAAAgAAACABIAQAEAgAA","49":"b:f4DhQwHQMBEDAAAAgAAACABIAQAEAgAA","53":"b:f4DhQwHQMBEDAAAAgAAACABIAQAEAgAA"},"packages/workflow/src/workflow-data-proxy.ts":{"41":"b:f4jAQwKQMBECAAQgoCAACIDIjSEFAqAA","63":"b:f4jAQwKQMBECAAQgoCAACIDIjSEFAqAA","84":"b:f4jAQwKQMBECAAQgoCAACIDIjSEFAqAA","123":"b:f4jAQwKQMBECAAQgoCAACIDIjSEFAqAA","128":"b:f4jAQwKQMBECAAQgoCAACIDIjSEFAqAA","140":"b:f4jAQwKQMBECAAQgoCAACIDIjSEFAqAA","162":"b:f4jAQwKQMBECAAQgoCAACIDIjSEFAqAA","163":"b:f4jAQwKQMBECAAQgoCAACIDIjSEFAqAA","171":"b:f4jAQwKQMBECAAQgoCAACIDIjSEFAqAA","177":"b:f4jAQwKQMBECAAQgoCAACIDIjSEFAqAA","189":"b:f4jAQwKQMBECAAQgoCAACIDIjSEFAqAA","195":"b:f4jAQwKQMBECAAQgoCAACIDIjSEFAqAA","196":"b:f4jAQwKQMBECAAQgoCAACIDIjSEFAqAA","200":"b:f4jAQwKQMBECAAQgoCAACIDIjSEFAqAA","234":"b:f4jAQwKQMBECAAQgoCAACIDIjSEFAqAA","259":"b:f4jAQwKQMBECAAQgoCAACIDIjSEFAqAA","260":"b:f4jAQwKQMBECAAQgoCAACIDIjSEFAqAA","266":"b:f4jAQwKQMBECAAQgoCAACIDIjSEFAqAA","268":"b:f4jAQwKQMBECAAQgoCAACIDIjSEFAqAA","270":"b:f4jAQwKQMBECAAQgoCAACIDIjSEFAqAA","279":"b:f4jAQwKQMBECAAQgoCAACIDIjSEFAqAA","280":"b:f4jAQwKQMBECAAQgoCAACIDIjSEFAqAA","282":"b:f4jAQwKQMBECAAQgoCAACIDIjSEFAqAA","283":"b:f4jAQwKQMBECAAQgoCAACIDIjSEFAqAA","297":"b:f4jAQwKQMBECAAQgoCAACIDIjSEFAqAA","302":"b:f4jAQwKQMBECAAQgoCAACIDIjSEFAqAA","303":"b:f4jAQwKQMBECAAQgoCAACIDIjSEFAqAA","306":"b:f4jAQwKQMBECAAQgoCAACIDIjSEFAqAA","312":"b:f4jAQwKQMBECAAQgoCAACIDIjSEFAqAA","315":"b:f4jAQwKQMBECAAQgoCAACIDIjSEFAqAA","320":"b:f4jAQwKQMBECAAQgoCAACIDIjSEFAqAA","321":"b:f4jAQwKQMBECAAQgoCAACIDIjSEFAqAA","333":"b:f4jAQwKQMBECAAQgoCAACIDIjSEFAqAA","340":"b:f4jAQwKQMBECAAQgoCAACIDIjSEFAqAA","341":"b:f4jAQwKQMBECAAQgoCAACIDIjSEFAqAA","344":"b:f4jAQwKQMBECAAQgoCAACIDIjSEFAqAA","350":"b:f4jAQwKQMBECAAQgoCAACIDIjSEFAqAA","352":"b:f4jAQwKQMBECAAQgoCAACIDIjSEFAqAA","414":"b:f4jAQwKQMBECAAQgoCAACIDIjSEFAqAA","446":"b:f4jAQwKQMBECAAQgoCAACIDIjSEFAqAA","563":"b:f4jAQwKQMBECAAQgoCAACIDIjSEFAqAA","574":"b:f4jAQwKQMBECAAQgoCAACIDIjSEFAqAA","575":"b:f4jAQwKQMBECAAQgoCAACIDIjSEFAqAA","682":"b:f4jAQwKQMBECAAQgoCAACIDIjSEFAqAA","689":"b:f4jAQwKQMBECAAQgoCAACIDIjSEFAqAA","690":"b:f4jAQwKQMBECAAQgoCAACIDIjSEFAqAA","693":"b:f4jAQwKQMBECAAQgoCAACIDIjSEFAqAA","699":"b:f4jAQwKQMBECAAQgoCAACIDIjSEFAqAA","730":"b:f4jAQwKQMBECAAQgoCAACIDIjSEFAqAA","737":"b:f4jAQwKQMBECAAQgoCAACIDIjSEFAqAA","738":"b:f4jAQwKQMBECAAQgoCAACIDIjSEFAqAA","741":"b:f4jAQwKQMBECAAQgoCAACIDIjSEFAqAA","747":"b:f4jAQwKQMBECAAQgoCAACIDIjSEFAqAA","775":"b:f4jAQwKQMBECAAQgoCAACIDIjSEFAqAA","780":"b:f4jAQwKQMBECAAQgoCAACIDIjSEFAqAA","781":"b:f4jAQwKQMBECAAQgoCAACIDIjSEFAqAA","805":"b:f4jAQwKQMBECAAQgoCAACIDIjSEFAqAA","809":"b:f4jAQwKQMBECAAQgoCAACIDIjSEFAqAA","836":"b:f4jAQwKQMBECAAQgoCAACIDIjSEFAqAA","886":"b:f4jAQwKQMBECAAQgoCAACIDIjSEFAqAA","908":"b:f4jAQwKQMBECAAQgoCAACIDIjSEFAqAA","921":"b:f4jAQwKQMBECAAQgoCAACIDIjSEFAqAA","932":"b:f4jAQwKQMBECAAQgoCAACIDIjSEFAqAA","944":"b:f4jAQwKQMBECAAQgoCAACIDIjSEFAqAA","957":"b:f4jAQwKQMBECAAQgoCAACIDIjSEFAqAA","968":"b:f4jAQwKQMBECAAQgoCAACIDIjSEFAqAA","979":"b:f4jAQwKQMBECAAQgoCAACIDIjSEFAqAA","988":"b:f4jAQwKQMBECAAQgoCAACIDIjSEFAqAA","1007":"b:f4jAQwKQMBECAAQgoCAACIDIjSEFAqAA","1016":"b:f4jAQwKQMBECAAQgoCAACIDIjSEFAqAA","1019":"b:f4jAQwKQMBECAAQgoCAACIDIjSEFAqAA","1055":"b:f4jAQwKQMBECAAQgoCAACIDIjSEFAqAA","1077":"b:f4jAQwKQMBECAAQgoCAACIDIjSEFAqAA","1081":"b:f4jAQwKQMBECAAQgoCAACIDIjSEFAqAA","1089":"b:f4jAQwKQMBECAAQgoCAACIDIjSEFAqAA","1096":"b:f4jAQwKQMBECAAQgoCAACIDIjSEFAqAA","1148":"b:f4jAQwKQMBECAAQgoCAACIDIjSEFAqAA","1173":"b:f4jAQwKQMBECAAQgoCAACIDIjSEFAqAA","1191":"b:f4jAQwKQMBECAAQgoCAACIDIjSEFAqAA","1214":"b:f4jAQwKQMBECAAQgoCAACIDIjSEFAqAA","1215":"b:f4jAQwKQMBECAAQgoCAACIDIjSEFAqAA","1228":"b:f4jAQwKQMBECAAQgoCAACIDIjSEFAqAA","1261":"b:f4jAQwKQMBECAAQgoCAACIDIjSEFAqAA","1271":"b:f4jAQwKQMBECAAQgoCAACIDIjSEFAqAA","1369":"b:f4jAQwKQMBECAAQgoCAACIDIjSEFAqAA","1388":"b:f4jAQwKQMBECAAQgoCAACIDIjSEFAqAA","1412":"b:f4jAQwKQMBECAAQgoCAACIDIjSEFAqAA","1443":"b:f4jAQwKQMBECAAQgoCAACIDIjSEFAqAA","1444":"b:f4jAQwKQMBECAAQgoCAACIDIjSEFAqAA","1447":"b:f4jAQwKQMBECAAQgoCAACIDIjSEFAqAA","1453":"b:f4jAQwKQMBECAAQgoCAACIDIjSEFAqAA","1468":"b:f4jAQwKQMBECAAQgoCAACIDIjSEFAqAA","1483":"b:f4jAQwKQMBECAAQgoCAACIDIjSEFAqAA","1498":"b:f4jAQwKQMBECAAQgoCAACIDIjSEFAqAA","1544":"b:f4jAQwKQMBECAAQgoCAACIDIjSEFAqAA","1571":"b:f4jAQwKQMBECAAQgoCAACIDIjSEFAqAA","1595":"b:f4jAQwKQMBECAAQgoCAACIDIjSEFAqAA","1651":"b:f4jAQwKQMBECAAQgoCAACIDIjSEFAqAA","1652":"b:f4jAQwKQMBECAAQgoCAACIDIjSEFAqAA"},"packages/workflow/src/workflow-expression.ts":{"22":"b:f5jl0yW3uBsTAAQAgAAACABIBQDtAwAB","30":"b:f5jl0yW3uBsTAAQAgAAACABIBQDtAwAB","74":"b:f5jl0yW3uBsTAAQAgAAACABIBQDtAwAB","89":"b:f5jl0yW3uBsTAAQAgAAACABIBQDtAwAB","94":"b:f5jl0yW3uBsTAAQAgAAACABIBQDtAwAB","154":"b:f5jl0yW3uBsTAAQAgAAACABIBQDtAwAB","189":"b:f5jl0yW3uBsTAAQAgAAACABIBQDtAwAB","222":"b:f5jl0yW3uBsTAAQAgAAACABIBQDtAwAB","269":"b:f5jl0yW3uBsTAAQAgAAACABIBQDtAwAB","274":"b:f5jl0yW3uBsTAAQAgAAACABIBQDtAwAB","278":"b:f5jl0yW3uBsTAAQAgAAACABIBQDtAwAB"},"packages/workflow/src/workflow-structure-validation.ts":{"94":"b:R5T906e33JoXAAAAAAAAAAAAAADsAAEA","97":"b:R5T906e33JoXAAAAAAAAAAAAAADsAAEA","103":"b:R5T906e33JoXAAAAAAAAAAAAAADsAAEA","105":"b:R5T906e33JoXAAAAAAAAAAAAAADsAAEA","113":"b:R5T906e33JoXAAAAAAAAAAAAAADsAAEA","119":"b:R5T906e33JoXAAAAAAAAAAAAAADsAAEA","190":"b:R5T906e33JoXAAAAAAAAAAAAAADsAAEA"},"packages/workflow/src/workflow.ts":{"87":"b:f5rl0yf/uBsbAAQA4CAAGoCqBAv8C+AB","137":"b:f5rl0yf/uBsbAAQA4CAAGoCqBAv8C+AB","144":"b:f5rl0yf/uBsbAAQA4CAAGoCqBAv8C+AB","149":"b:f5rl0yf/uBsbAAQA4CAAGoCqBAv8C+AB","153":"b:f5rl0yf/uBsbAAQA4CAAGoCqBAv8C+AB","157":"b:f5rl0yf/uBsbAAQA4CAAGoCqBAv8C+AB","164":"b:f5rl0yf/uBsbAAQA4CAAGoCqBAv8C+AB","217":"b:f5rl0yf/uBsbAAQA4CAAGoCqBAv8C+AB","245":"b:f5rl0yf/uBsbAAQA4CAAGoCqBAv8C+AB","253":"b:f5rl0yf/uBsbAAQA4CAAGoCqBAv8C+AB","254":"b:f5rl0yf/uBsbAAQA4CAAGoCqBAv8C+AB","261":"b:f5rl0yf/uBsbAAQA4CAAGoCqBAv8C+AB","262":"b:f5rl0yf/uBsbAAQA4CAAGoCqBAv8C+AB","271":"b:f5rl0yf/uBsbAAQA4CAAGoCqBAv8C+AB","300":"b:f5rl0yf/uBsbAAQA4CAAGoCqBAv8C+AB","309":"b:f5rl0yf/uBsbAAQA4CAAGoCqBAv8C+AB","330":"b:f5rl0yf/uBsbAAQA4CAAGoCqBAv8C+AB","334":"b:f5rl0yf/uBsbAAQA4CAAGoCqBAv8C+AB","390":"b:f5rl0yf/uBsbAAQA4CAAGoCqBAv8C+AB","409":"b:f5rl0yf/uBsbAAQA4CAAGoCqBAv8C+AB","447":"b:f5rl0yf/uBsbAAQA4CAAGoCqBAv8C+AB","491":"b:f5rl0yf/uBsbAAQA4CAAGoCqBAv8C+AB","544":"b:f5rl0yf/uBsbAAQA4CAAGoCqBAv8C+AB","563":"b:f5rl0yf/uBsbAAQA4CAAGoCqBAv8C+AB","581":"b:f5rl0yf/uBsbAAQA4CAAGoCqBAv8C+AB","595":"b:f5rl0yf/uBsbAAQA4CAAGoCqBAv8C+AB","610":"b:f5rl0yf/uBsbAAQA4CAAGoCqBAv8C+AB","624":"b:f5rl0yf/uBsbAAQA4CAAGoCqBAv8C+AB","635":"b:f5rl0yf/uBsbAAQA4CAAGoCqBAv8C+AB","658":"b:f5rl0yf/uBsbAAQA4CAAGoCqBAv8C+AB","676":"b:f5rl0yf/uBsbAAQA4CAAGoCqBAv8C+AB","677":"b:f5rl0yf/uBsbAAQA4CAAGoCqBAv8C+AB","691":"b:f5rl0yf/uBsbAAQA4CAAGoCqBAv8C+AB","750":"b:f5rl0yf/uBsbAAQA4CAAGoCqBAv8C+AB","821":"b:f5rl0yf/uBsbAAQA4CAAGoCqBAv8C+AB","851":"b:f5rl0yf/uBsbAAQA4CAAGoCqBAv8C+AB","852":"b:f5rl0yf/uBsbAAQA4CAAGoCqBAv8C+AB","871":"b:f5rl0yf/uBsbAAQA4CAAGoCqBAv8C+AB","896":"b:f5rl0yf/uBsbAAQA4CAAGoCqBAv8C+AB"},"localhost-33597/assets/_plugin-vue_export-helper-Da88TEg1.js":{"2":[2]},"localhost-33597/assets/chunk-CC9Q-vWm.js":{"8":[2],"9":[2],"10":[2],"19":[2],"23":[2],"29":[2],"33":[2],"34":[2]},"packages/@n8n/api-types/src/agent-builder-tool-node-types.ts":{"8":"b://Xf/P///+39//////////////8DAAAA"},"packages/@n8n/api-types/src/agents/agent-integration.schema.ts":{"3":"b://Xf/P///+39//////////////8DAAAA","16":"b://Xf/P///+39//////////////8DAAAA","22":"b://Xf/P///+39//////////////8DAAAA","35":"b://Xf/P///+39//////////////8DAAAA"},"packages/@n8n/api-types/src/agents/dto.ts":{"95":"b://Xf/P///+39//////////////8DAAAA","111":"b://Xf/P///+39//////////////8DAAAA"},"packages/@n8n/api-types/src/dto/data-table/list-data-table-query.dto.ts":{"32":"b://Xf/P///+39//////////////8DAAAA","36":"b:AAAATAAADABkgINBAAAAASAAAAACAAAA"},"packages/@n8n/api-types/src/dto/evaluations/llm-judge-providers.ts":{"120":"b://Xf/P///+39//////////////8DAAAA"},"packages/@n8n/api-types/src/schemas/password.schema.ts":{"13":"b://Xf/P///+39//////////////8DKAAA","18":[173,90,171,105,52,62,119,63,120],"21":[173,90,171,105,52,62,119,63,120]},"packages/@n8n/chat-hub/src/constants.ts":{"50":"b://Xf/P///+39//////////////8DAAAA"},"packages/@n8n/codemirror-lang-sql/src/sql.ts":{"19":"b://Xf/P///+39//////////////8DAAAA","26":[132],"61":"b://Xf/P///+39//////////////8DAAAA","76":[132],"80":[132],"140":"b://Xf/P///+39//////////////8DAAAA","157":"b://Xf/P///+39//////////////8DAAAA","195":[132]},"packages/@n8n/codemirror-lang-sql/src/tokens.ts":{"33":"b://Xf/P///+39//////////////8DAAAA","80":[132],"88":[132],"113":[132],"122":[132],"141":[132],"164":[132],"171":"b://Xf/P///+39//////////////8DAAAA","221":"b://Xf/P///+39//////////////8DAAAA","240":"b://Xf/P///+39//////////////8DAAAA","241":[132]},"packages/@n8n/permissions/src/roles/custom-role-scopes.ee.ts":{"154":"b://Xf/P///+39//////////////8DAAAA","169":"b://Xf/P///+39//////////////8DAAAA","170":"b://Xf/P///+39//////////////8DAAAA","182":"b://Xf/P///+39//////////////8DAAAA","183":"b://Xf/P///+39//////////////8DAAAA"},"packages/@n8n/permissions/src/scope-information.ts":{"4":"b://Xf/P///+39//////////////8DAAAA","5":"b://Xf/P///+39//////////////8DAAAA","6":"b://Xf/P///+39//////////////8DAAAA","12":"b://Xf/P///+39//////////////8DAAAA","13":"b://Xf/P///+39//////////////8DAAAA","14":"b://Xf/P///+39//////////////8DAAAA"},"packages/@n8n/permissions/src/utilities/get-resource-permissions.ee.ts":{"14":"b://Xf/P///+39/8//////+/////8DAAAA","15":"b://Xf/P///+39/8//////+/////8DAAAA","18":"b://Xf/P///+39/8//////+/////8DAAAA"},"packages/@n8n/telemetry/src/define.ts":{"19":"b://Xf/P///+39//////////////8DAAAA"},"packages/@n8n/tournament/src/FunctionEvaluator.ts":{"8":"b:x/WP8OS7+MVUQCQCq/wA+vX//4sDAAAA","18":"b:x/WP8OS7+MVUQCQCq/wA+vX//4sDAAAA"},"packages/@n8n/utils/src/create-event-queue.ts":{"7":"b:x/Xf8O3/+cHdYDwC//1D//////8DAAAA","17":"b:x/Hf0OX/+cFdAAwA4CUA+/3/338DAAAA","44":"b:x/Hf0OX/+cFdAAwA4CUA+/3/338DAAAA"},"packages/@n8n/utils/src/event-bus.ts":{"50":"b://Xf/P///+39//////////////8DAAAA","60":[81,160],"61":[81,160],"68":"b:9/Xf9P3//+3dYDZz//+v+/////8DAAAA","75":"b:9/Xf8O3//+3dYD4T//2///////8DAAAA","78":"b:x/Xf8O3//+3dYDYT//27//////8DAAAA"},"packages/@n8n/utils/src/placeholder.ts":{"13":[37,72,74,95,39,75,40,41,76],"22":[37,72,74,95,39,75,40,41,76],"32":"b:1vXPsK2b+O1UHSCy+/SJ8tz//0sBAAAA","43":[19,37,72,74,95,39,75,40,41,76],"63":"b:1vXPsK2b+O1UHSCy+/SJ8tz//0sBAAAA","84":[19,37,72,74,95,39,75,40,41,76],"87":[19,37,72,74,95,39,75,40,41,76],"91":[19,37,72,74,95,39,75,40,41,76],"95":[19,37,72,74,95,39,75,40,41,76],"107":[19,37,72,74,95,39,75,40,41,76],"111":[19,37,72,74,95,39,75,40,41,76]},"packages/@n8n/utils/src/sort/sort-by-property.ts":{"1":"b:x/Xf/P///8X9f4/D//+F8/////8DAAAA","6":"b:ACEIIBgIP8AEAIBCgquBwIZmKuIDAAAA"},"packages/frontend/@n8n/chat/src/api/generic.ts":{"1":[82,1,7,2,48,109,123,6],"5":[82,1,7,2,48,109,123,6],"38":[48],"49":[82,1,7,2,48,109,123,6],"56":[48]},"packages/frontend/@n8n/chat/src/api/message.ts":{"9":[48],"24":[82,1,7,2,48,109,123,6],"68":[48],"73":[48],"82":[48],"119":[48],"151":[48],"211":[48],"245":[48]},"packages/frontend/@n8n/chat/src/components/Chat.vue":{"14":[82,1,7,2,48,109,123,6],"20":[82,1,7,2,48,109,123,6],"25":[48],"27":[48],"28":[48],"31":[82,1,7,2,48,109,123,6],"36":[82,1,7,2,48,109,123,6],"41":[82,1,7,2,48,109,123,6],"46":[82,1,7,2,48,109,123,6],"51":[48],"55":[48],"106":[82,1,7,2,48,109,123,6],"108":[1,48,6],"116":[82,1,7,2,48,109,123,6],"122":[48],"127":[82,1,7,2,48,109,123,6],"131":[82,1,7,2,48,109,123,6],"149":[82,1,7,2,48,109,123,6]},"packages/frontend/@n8n/chat/src/components/GetStartedFooter.vue":{"5":[82,1,7,2,48,109,123,6]},"packages/frontend/@n8n/chat/src/components/Input.vue":{"23":[82,1,7,2,48,109,123,6],"48":[82,1,7,2,48,109,123,6],"54":[82,1,7,2,48,109,123,6],"55":[48],"58":[82,1,7,2,48,109,123,6],"59":[48],"61":[82,1,7,2,48,109,123,6],"77":[48],"93":[48],"97":[82,1,7,2,48,109,123,6],"104":[82,1,7,2,48,109,123,6],"117":[48],"129":[48],"135":[82,1,7,2,48,109,123],"141":[48],"146":[82,1,7,2,48,109,123,6],"157":[48],"178":[48],"222":[48],"235":[48],"238":[48],"241":[48],"244":[48],"251":[48],"261":[48],"282":[82,1,7,2,48,109,123,6],"312":[48,123],"321":[48],"334":[48,123],"345":[48],"350":[82,1,7,2,48,109,123,6],"358":[82,1,7,2,48,109,123,6],"369":[82,1,7,2,48,109,123,6],"397":[48]},"packages/frontend/@n8n/chat/src/components/Layout.vue":{"6":[82,1,7,2,48,109,123,6],"8":[82,1,7,2,48,109,123,6],"15":[82,1,7,2,48,109,123,6],"20":[48],"24":[82,1,7,2,48,109,123,6]},"packages/frontend/@n8n/chat/src/components/MarkdownRenderer.vue":{"16":[82,1,7,2,109,123,6],"22":[82,1,7,2,109,123,6],"42":[82,1,7,2,109,123,6]},"packages/frontend/@n8n/chat/src/components/Message.vue":{"20":[82,1,7,2,48,109,123,6],"25":[82,1,7,2,48,109,123,6],"29":[82,1,7,2,48,109,123,6],"37":[82,1,7,2,48,109,123,6],"49":[48],"50":[48],"52":[48],"57":[82,1,7,2,48,109,123,6],"69":[82,1,7,2,48,109,123,6]},"packages/frontend/@n8n/chat/src/components/MessageActions.vue":{"8":[82,1,7,2,48,109,123,6],"16":[48],"30":[48],"35":[82,1,7,2,48,109,123,6],"43":[48]},"packages/frontend/@n8n/chat/src/components/MessageTyping.vue":{"8":[82,1,7,2,48,109,123,6],"23":[82,1,7,2,48,109,123,6],"31":[82,1,7,2,48,109,123,6],"34":[82,1,7,2,48,109,123,6]},"packages/frontend/@n8n/chat/src/components/MessagesList.vue":{"19":[82,1,7,2,48,109,123,6],"23":[82,1,7,2,48,109,123,6],"24":[82,1,7,2,48,109,123,6],"32":[82,1,7,2,48,109,123,6],"41":[48],"48":[48],"53":[82,1,7,2,48,109,123,6],"55":[82,1,7,2,48,109,123,6]},"packages/frontend/@n8n/chat/src/components/PoweredBy.vue":{"2":[82,1,7,2,48,109,123,6]},"packages/frontend/@n8n/chat/src/composables/useChat.ts":{"6":"b://Xf/P///+39//////////////8DAAAA"},"packages/frontend/@n8n/chat/src/composables/useI18n.ts":{"5":[82,1,7,2,109,123,6],"9":[82,1,7,2,109,123,6],"17":[82,1,7,2,109,123,6]},"packages/frontend/@n8n/chat/src/composables/useOptions.ts":{"6":[82,1,7,2,109,123,6]},"packages/frontend/@n8n/chat/src/plugins/chat.ts":{"26":[82,1,7,2,48,109,123,6],"41":[82,1,7,2,48,109,123,6],"69":[48],"77":[48],"100":[48],"130":[48],"143":[48],"146":[48],"149":[48],"193":[82,1,7,2,48,109,123,6],"210":[82,1,7,2,48,109,123,6],"218":[82,1,7,2,48,109,123,6],"219":[48],"226":[82,1,7,2,48,109,123,6],"235":[82,1,7,2,48,109,123,6],"295":[82,1,7,2,48,109,123,6],"302":[82,1,7,2,48,109,123,6],"318":[48],"334":[82,1,7,2,48,109,123,6]},"packages/frontend/@n8n/chat/src/utils/event-bus.ts":{"11":"b://Xf/P///+39//////////////8DAAAA","22":[82,1,7,2,109,123,6],"36":[82,1,7,2,109,123,6],"40":[82,1,7,2,109,123,6]},"packages/frontend/@n8n/chat/src/utils/streaming.ts":{"22":[82,1,7,2,48,109,123,6],"24":[48],"31":[48],"46":[48],"51":[48],"57":[48],"66":[48],"82":[48],"88":[48],"89":[48],"92":[48],"96":[48],"100":[48],"102":[48],"103":[48],"106":[48],"113":[82,1,7,2,48,109,123,6],"122":[48],"127":[48]},"packages/frontend/@n8n/composables/src/structuralComputed.ts":{"21":"b:x/XP8OX/+OldYDQi//wJ//////sDAAAC","27":"b:x/XP8OX/+MFdYDQC//wB//////sDAAAC"},"packages/frontend/@n8n/composables/src/useClipboard.ts":{"25":[81,135],"48":[126,130,132,66,67],"59":[126,130,132,66],"77":"b://Xf8P//+cHdfzfq//0l+/////8DAAAA","90":"b:1vXHECUTyMBdCAJI6/EF8vz/f3kDAAAA"},"packages/frontend/@n8n/composables/src/useDebounce.ts":{"8":"b://Xf/P///+39//////////////8DAAAC","27":"b://Xf/P///+39//////////////8DAAAC","39":"b:1vXPsB3Z/e11ICBC//eJc9z//3sDAAAC","53":"b:guXCMBUYjMBlIABC/9MEc9y/fSoDAAAC"},"packages/frontend/@n8n/composables/src/useDeviceSupport.ts":{"3":"b://Xf/P///+39//////////////8DAAAA","26":"b:RgAAAGADAAAAAAQAACAB4AcCBEEAAAAA","28":"b:QuENsBBAooUcCAAACZuFeBf1RfkDAAAA"},"packages/frontend/@n8n/composables/src/useDocumentTitle.ts":{"28":"b://Xf/P///+39//////////////8DAAAA","37":"b://Xf/P///+39f7////////////8DAAAA","42":[67],"47":"b:x/Xf8O3/+cHdYDwC//0B//////8DAAAA"},"packages/frontend/@n8n/composables/src/useExternalHooks.ts":{"48":"b://Xf/P///+39//////////////8DAAAA"},"packages/frontend/@n8n/composables/src/useShortKeyPress.ts":{"6":"b:x/Xf8O3/+cHdYDQC//0B//////8DAAAC","22":[185],"33":"b:AMAMAAAAAAAAAAAAAAAAQBAkHAAAAAAC"},"packages/frontend/@n8n/composables/src/useStyles.ts":{"3":"b://Xf/P///+39//////////////8DAAAA","4":"b://Xf/P///+39//////////////8DAAAA","11":"b://Xf/P///+39//////////////8DAAAA"},"packages/frontend/@n8n/composables/src/useTelemetry.ts":{"91":"b://Xf/P///+39//////////////8DAAAA"},"packages/frontend/@n8n/composables/src/useThrottleWithReactiveDelay.ts":{"7":"b:x/Xf8O3/+cHdYAQC//0B+/////8DAAAA","12":"b:x/Xf8O3/+cHdYAQC//0B+/////8DAAAA"},"packages/frontend/@n8n/composables/src/useToast.ts":{"39":"b://Xf/P///+39//////////////8DAAAA","48":"b://Xf/P///+39//////////////8DAAAA","52":"b:woEBAAYAAAAUAABgAAAQQMiCBDAAAAAA","61":"b:woEBAAYAAAAUAABgAAAQQMiCBDAAAAAA","67":"b:19FF5A/8v4UdAAd44De2edn/33UBAAAA","138":"b:AAAABAAAHwAIAAIAAAEwAEEAQAABAAAA","152":[26],"194":[93,33,8,94,116,137,146,68],"231":"b:x5DFwCX/qMEdAAYB4CUAevj/13EDAAAA","232":[15,16,134,156,157,68],"242":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAA","244":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAA"},"packages/frontend/@n8n/design-system/src/components/AskAssistantChat/AskAssistantChat.vue":{"25":"b://Xf/P///+39f7////////////8DAAAA","62":[15],"77":"b://Xf/P///+39f7////////////8DAAAA","78":[15,16,17,18,19,20],"86":"b://Xf/P///+39f7////////////8DAAAA","87":[15,16,17,18,19,20],"99":"b://Xf/P///+39f7////////////8DAAAA","133":"b://Xf/P///+39f7////////////8DAAAA","272":"b://Xf/P///+39f7////////////8DAAAA","275":"b://Xf/P///+39f7////////////8DAAAA","287":[15,16,17,18,19,20],"303":"b:x/Xf8AX8+OldYDQC//wJ8/////sDAAAA","307":"b://Xf/P///+39f7////////////8DAAAA","311":"b://Xf/P///+39f7////////////8DAAAA","315":"b://Xf/P///+39f7////////////8DAAAA","321":[15,16,17,18,19,20],"327":"b://Xf/P///+39f7////////////8DAAAA","332":"b://Xf/P///+39f7////////////8DAAAA","345":[19],"360":"b:x/Xf8AX8+OldYDQC//wJ8/////sDAAAA","364":[20],"378":[15],"382":[15,18,19,20],"391":[19,20],"395":[19,20],"399":[15,18,19,20],"403":"b://Xf/P///+39f7////////////8DAAAA","405":"b://Xf/P///+39f7////////////8DAAAA","422":"b://Xf/P///+39f7////////////8DAAAA","439":[15,16,17,18],"443":[15,16,17,18,19,20],"456":[15,16,17,18,19,20],"464":[15,16,17,18,19,20],"469":"b://Xf/P///+39f7////////////8DAAAA","500":[15,16,17,18,19,20],"507":[15,16,17,18,19,20],"534":[16],"535":[16],"538":[19],"554":[15,16,18],"562":[15],"563":[15,16,18],"597":"b://Xf/P///+39f7////////////8DAAAA","602":[19,20],"614":"b://Xf/P///+39f7////////////8DAAAA"},"packages/frontend/@n8n/design-system/src/components/AskAssistantIcon/AssistantIcon.vue":{"4":"b://Xf/P///+39f7////////////8DAAAA","22":"b://Xf/P///+39f7////////////8DAAAA","30":"b://Xf/P///+39f7////////////8DAAAA"},"packages/frontend/@n8n/design-system/src/components/AskAssistantText/AssistantText.vue":{"9":"b://Xf/P///+39f7////////////8DAAAA"},"packages/frontend/@n8n/design-system/src/components/ConditionalRouterLink/CondtionalRouterLink.vue":{"16":"b:x/Xf/P///8X9f4/D//+F8/////8DAAAA","27":"b:x/Xb/P/+/8X9f4/D//+F8/////8DAAAA","32":"b:x/Xf/P///8X9f4/D//+F8/////8DAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nActionDropdown/ActionDropdown.vue":{"14":"b://Xf8P//+cHdaAxq//0B+/////8DAAAA","50":"b://Xf8P//+cHdaAxq//0B+/////8DAAAA","55":"b://Xf8P//+cHdaAxq//0B+/////8DAAAA","60":"b://Xf8P//+cHdaAxq//0B+/////8DAAAA","68":"b://Xf8P//+cHdaAxq//0B+/////8DAAAA","69":"b://Xf8P//+cHdaAxq//0B+/////8DAAAA","87":"b:QMAFgBBAMUEICAAIAIgBAQSARYAAAAAA","88":"b:QMAFgBBAMUEICAAIAIgBAQSARYAAAAAA","98":"b://Xf8P//+cHdaAxq//0B+/////8DAAAA","105":"b://Xf8P//+cHdaAxq//0B+/////8DAAAA","130":"b://Xf8P//+cHdaAxq//0B+/////8DAAAA","143":"b:QMAFgBBAMUEICAAIAIgBAQSARYAAAAAA","151":"b:QMAFgBBAMUEICAAIAIgBAQSARYAAAAAA","153":"b:QMABgBBAMUEICAAIAIgBAQSARYAAAAAA","158":"b:QMAFgBBAMUEICAAIAIgBAQSARYAAAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nActionToggle/ActionToggle.vue":{"31":"b:1vXPtJ+Z7+18AABy+/ev8vz//wsDAAAA","52":"b:1vXPtJ+Z7+18AABy+/ev8vz//wsDAAAA","53":"b:1vXPtJ+Z7+18AABy+/ev8vz//wsDAAAA","62":"b:AAAAAAAADyQAAABQAAEiAAAAEAABAAAA","63":"b:AAAAAAAADyQAAABQAAEiAAAAEAABAAAA","92":"b:1vXPtJ+Z7+18AABy+/ev8vz//wsDAAAA","99":"b:AAAAAAAADyQAAABQAAEiAAAAEAABAAAA","117":"b:1vXPtJ+Z7+18AABy+/ev8vz//wsDAAAA","130":[104],"132":[104],"139":"b:AAAAAAAADyQAAABQAAEiAAAAEAABAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nBlockUi/BlockUi.vue":{"12":"b:xvXHsGWb+MFEACQC+/QB8tz//0sBAAAA","13":"b:xvXDsGWa+MFEACQC+/QB8tz//0sBAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nButton/Button.vue":{"9":"b://Xf/P///+39f7////////////8DAAAA","25":"b://Xf/P///+39f7////////////8DAAAA","31":"b://Xf/P///+39f7////////////8DAAAA","37":"b://Xf/P///+39f7////////////8DAAAA","43":"b://Xf/P///+39f7////////////8DAAAA","48":"b://Xf/P///+39f7////////////8DAAAA","53":"b://Xf/P///+39f7////////////8DAAAA","55":"b://Xf/P///+39f7////////////8DAAAA","81":"b://Xf9P///+39LQf79/+/+/////8DAAAA","84":"b://Xf/P///+39f7////////////8DAAAA","103":"b:9/Xb/P/+/+39f7////////////8DAAAA","105":"b://Xf/P///+39f7////////////8DAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nChatInput/ChatInput.vue":{"41":"b://Xf/P///+39f7////////////8DAAAA","77":"b://Xf/P///+39f7////////////8DAAAA","82":"b://Xf/P///+39f7////////////8DAAAA","96":"b://Xf/P///+39f7////////////8DAAAA","97":"b://Xf/P///+39f7////////////8DAAAA","107":"b://Xf/P///+39f7////////////8DAAAA","111":"b://Xf/P///+39f7////////////8DAAAA","120":"b://Xf/P///+39f7////////////8DAAAA","122":"b://Xf/P///+39f7////////////8DAAAA","131":"b://Xf/P///+39f7////////////8DAAAA","133":"b:OIAcAOIDAAAADQCoAAAAAAAAAAAAAAAA","144":"b://Xf/P///+39f7////////////8DAAAA","153":"b:OIAcAOIDAAAADQCoAAAAAAAAAAAAAAAA","160":"b:OIAcAOIDAAAADQCoAAAAAAAAAAAAAAAA","164":[15,18,19,20],"170":"b:OIAcAOIDAAAADQCoAAAAAAAAAAAAAAAA","177":[20],"184":[15,18,19,20],"229":"b:OIAfAOIDAAAAHQCoAAAAAAAAAAAAAAAA","234":"b:OIAdAOIDAAAADQCoAAAAAAAAAAAAAAAA","239":[19],"244":[19],"249":"b://Xf/P///+39f7////////////8DAAAA","253":"b://Xf/P///+39f7////////////8DAAAA","255":"b://Xf/P///+39f7////////////8DAAAA","265":"b://Xf/P///+39f7////////////8DAAAA","269":"b:9/Xb/P/+/+39f7////////////8DAAAA","290":"b:OIAMAOIDAAAADQCoAAAAAAAAAAAAAAAA","304":"b:OIAMAOIDAAAADQCoAAAAAAAAAAAAAAAA","314":[93],"326":"b:OIAcAOIDAAAADQCoAAAAAAAAAAAAAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nCommandBar/CommandBar.vue":{"34":"b://Xf/P///+39/8//////8/////8DAAAA","42":[157,160],"43":[157,160],"46":[157,160],"50":[157,160],"57":[157,160],"62":[160],"85":[157,160],"90":[157,160],"103":[157,160],"110":[157,160],"115":[157,160],"122":[160],"126":[157,160],"127":[157,160],"155":[157,160],"163":[160],"189":[160],"202":"b:QuEdsBBAroUcKAIACZ+NcBf9Xf0DAAAA","252":"b://Xf9H///+39KA96//+/8/////8DAAAA","260":[160],"265":"b://Xf/P///+39/8//////8/////8DAAAA","270":[51,81],"274":"b://Xf/P///+39/8//////8/////8DAAAA","279":"b:9/Xb/P/+/+39/8//////8/////8DAAAA","280":"b:9/Xb/P/+/+39/8//////8/////8DAAAA","288":[157],"293":[160],"313":[157,160],"325":[157,160],"327":[157,160],"340":[160]},"packages/frontend/@n8n/design-system/src/components/N8nDropdownMenu/DropdownMenu.typeguards.ts":{"6":"b://Xf9P///+39aAx6//+v+/////8DAAAA","8":"b://Xf9P///+39aAx6//+v+/////8DAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nDropdownMenu/DropdownMenu.vue":{"25":"b://Xf9P///+39aAx6//+v+/////8DAAAA","54":[4,5,91,48,104,49],"67":"b://Xf9P///+39aAx6//+v+/////8DAAAA","75":"b://Xf9P///+39aAx6//+v+/////8DAAAA","89":"b:cMEHgBBAP2UICABagIkjAQSAVYABAAAA","98":[48,104,49],"117":"b:cMEHgBBAP2UICABagIkjAQSAVYABAAAA","131":"b:9vXHsHXT/+18CAR66/uj8/z/f/sDAAAA","138":"b:8vXHkBVQ/2VMCABa4skjc/z+d8kBAAAA","145":"b:8vXHgBVQ/2VMCABaoMkjc/z+d8gBAAAA","159":"b:cMEHgBBAP2UICABagIkjAQSAVYABAAAA","165":"b://Xf9P///+39aAx6//+v+/////8DAAAA","166":"b://Xf9P///+39aAx6//+v+/////8DAAAA","178":"b:1vXGMHWTz+18CAR66/Oj8vz/f3sDAAAA","187":"b:cMEHgBBAP2UICABagIkjAQSAVYABAAAA","217":"b://Xf9P///+39aAx6//+v+/////8DAAAA","223":"b:9/Xb9P/+/+39aAx6//+v+/////8DAAAA","224":"b://Xf9P///+39aAx6//+v+/////8DAAAA","253":"b://Xf9P///+39aAx6//+v+/////8DAAAA","267":"b:cMEDgBBAP2UICABagIkjAQSAVYABAAAA","279":[4,5,91],"287":[4,5,91],"312":[4,5,91],"315":[4,5,91],"318":[4,5,91],"338":[104],"346":"b:QMAFgBBAMUEICAAIAIgBAQSARYAAAAAA","349":"b:QMAFgBBAM0EICAAIAIkBAQSARYAAAAAA","352":"b:QMAFgBBAP2UICABYAIkjAQSAVYABAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nFormInput/validators.ts":{"7":"b:AAAABAIACIQEAINFAAIUAAAAAAABAAAA","36":[114],"67":[88,90,51,116,81],"97":[80,114],"109":"b://Xf/P///+39//////////////8DAAAA","139":"b:AAAABAIACIQEAINFAAIUAAAAAAABAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nHeading/Heading.vue":{"27":"b://Xf/P///+39//////////////8DAAAA","34":"b://Xf/P///+39//////////////8DAAAA","50":"b://Xf/P///+39//////////////8DAAAA","52":"b://Xf/P///+39//////////////8DAAAA","56":"b:9/Xb/P/+/+39//////////////8DAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nIcon/Icon.vue":{"25":"b://Xf/P///+39//////////////8DAAAA","32":"b://Xf/P///+39//////////////8DAAAA","42":"b://Xf/P///+39//////////////8DAAAA","54":"b://Xf/P///+39//////////////8DAAAA","66":"b://Xf/P///+39//////////////8DAAAA","83":"b://Xf/P///+39//////////////8DAAAA","91":"b://Xf/P///+39//////////////8DAAAA","93":"b://Xf/P///+39//////////////8DAAAA","95":"b:x/XP8OX/+MVdIDQi//yB//3///sDAAAA","107":"b://Xf/P///+39//////////////8DAAAA","109":"b://Xf/P///+39//////////////8DAAAA","129":"b://Xf/P///+39//////////////8DAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nIcon/custom/bolt-filled.svg":{"9":"b:x/XP8OX/+MFdYDQC//wB//3///sDAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nIcon/custom/node-ellipsis.svg":{"11":"b:x/XP8OX/+MFdYDQC//wB+/////sDAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nIcon/custom/node-play.svg":{"11":"b:x/XP8GX/+MFdYAQC//wB+/3///sDAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nIcon/custom/node-power.svg":{"11":"b:x/XP8GX/+MFdYAQC//wB8/3///sDAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nIcon/custom/node-success.svg":{"11":"b:x5DFwGX/sAEBAAQA4CQAeMj/l3EAAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nIcon/custom/node-trash.svg":{"11":"b:x/XP8GX/+MFdYAQC//wB8/////sDAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nIcon/custom/pop-out.svg":{"8":"b:x/Xf8O3/+cHdYAQC//0B+/////8DAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nIcon/custom/schema.svg":{"8":[82,1,7,2,37,38,40,41,109,6,158]},"packages/frontend/@n8n/design-system/src/components/N8nIcon/custom/spinner.svg":{"9":"b://Xf/P///+39//////////////8DAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nIcon/iconColor.ts":{"25":"b://Xf/P///+39//////////////8DAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nIcon/icons.ts":{"790":"b:x/XP8OX/+MFdYDQC//wB//3///sDAAAA","794":"b:x/XP8OX/+MVdIDQi//yB//3///sDAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nIcon/nodes/character-text-splitter.svg":{"11":[0,2]},"packages/frontend/@n8n/design-system/src/components/N8nIcon/nodes/chat-trigger.svg":{"11":"b:xuXCMAUYiMAEIAQC//AAe9y8fCoBAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nIcon/nodes/code.svg":{"11":"b:RYABEAFQAAABAAAAAIABdKyAQ3AAAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nIcon/nodes/default-data-loader.svg":{"11":[0,2,14]},"packages/frontend/@n8n/design-system/src/components/N8nIcon/nodes/edit-fields.svg":{"11":"b:RvABQGCz2AEEADQCgOgAvKXr54EAAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nIcon/nodes/manual-trigger.svg":{"11":"b:x/XH8OX/uMFFIDAC/9wBd/39/3sBAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nIcon/nodes/question-and-answer-chain.svg":{"11":[2,14]},"packages/frontend/@n8n/design-system/src/components/N8nIcon/nodes/simple-vector-store.svg":{"11":[0,2,98,139,14]},"packages/frontend/@n8n/design-system/src/components/N8nIcon/nodes/vector-store-retriever.svg":{"11":[2,14]},"packages/frontend/@n8n/design-system/src/components/N8nIconButton/IconButton.vue":{"6":"b://Xf/P///+39f7////////////8DAAAA","7":"b:9/Xb/P/+/+39f7////////////8DAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nIconPicker/iconCategories.ts":{"118":"b://Xf/P///+39//////////////8DAAAA","129":[51]},"packages/frontend/@n8n/design-system/src/components/N8nInfoTip/InfoTip.vue":{"14":"b:1sFDoDybH+10AAJQ8BeMAPj/30EDAAAA","57":"b:1sFDoDybH+10AAJQ8BeMAPj/30EDAAAA","63":"b:1sFDoDybH+10AAJQ8BeMAPj/30EDAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nInlineTextEdit/InlineTextEdit.vue":{"16":"b:1/Xf8P3/++3dYCxy//2J8/////8DAAAA","35":"b:1/Xf8P3/++3dYCxy//2J8/////8DAAAA","38":"b:1/Xf8P3/++3dYCxy//2J8/////8DAAAA","44":"b:1/Xf8P3/++3dYCxy//2J8/////8DAAAA","47":"b:1/Xf8P3/++3dYCxy//2J8/////8DAAAA","57":"b:1/Xf8P3/++3dYCxy//2J8/////8DAAAA","68":"b:QMEFoBRAIq0QAAAAAImJAQGARYABAAAA","74":"b:QMAFgARAKAEIAAAAAIkBAQCARYQBAAAA","81":"b:QMEFoBRAIq0QAAAAAImJAQGARYABAAAA","92":"b:QMEFoBRAIq0QAAAAAImJAQGARYABAAAA","96":"b:QMEFoBRAKq0YAAAAAImJAQGARYQBAAAA","103":"b:1/Xf8P3/++3dYCxy//2J8/////8DAAAA","122":"b:1/Xb8P3+++3dYCxy//2J8/////8DAAAA","127":"b:1/Xb8P3+++3dYCxy//2J8/////8DAAAA","145":"b:QMEFoBRAIq0QAAAAAImJAQGARYABAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nInput/Input.vue":{"11":"b:1vXPtP+b/+18AKd/+/e/8v///98DAAAA","40":"b:1vXPtP+b/+18AKd/+/e/8v///9sDAAAA","49":"b:1vXPtP+b/+18AKd/+/e/8v///98DAAAA","59":"b:1vXPtP+b/+18AKd/+/e/8v///98DAAAA","78":"b:1vXPtP+b/+18AKd/+/e/8v///98DAAAA","81":"b:1vXPtP+b/+18AKd/+/e/8v///98DAAAA","83":"b:1vXPtP+b/+18AKd/+/e/8v///98DAAAA","97":"b:1vXPtP+b/+18AKd/+/e/8v///98DAAAA","111":"b:kkHEpBwYCKwUAANLoBO+AEjIGYYDAAAA","118":"b:kkHEpByYCawcAANLoBK+gkzIGY8BAAAA","124":"b:kkHEpByYCawcAINLoBO+gkzIGY8DAAAA","130":"b:AEEEABAAAAAAAAIAAAMMAATIAAQBAAAA","135":"b:AEDAgAAICAAAAAACIAEgAAQIGAIBAAAA","146":"b:1vXPtP+b/+18AKd/+/e/8v///98DAAAA","150":"b:1vXPtP+b/+18AKd/+/e/8v///98DAAAA","156":"b:1vXGNBeSz6RcAAd76/en8v3/f88DAAAA","157":"b:1vXPtP+b/+18AKd/+/e/8v///98DAAAA","162":"b:1vXPtP+b/+18AKd/+/e/8v///98DAAAA","163":"b:0uHEtByZWa1UACNfsTe+wMjLH4cDAAAA","172":"b:1vXPtP+b/+18AKd/+/e/8v///98DAAAA","182":"b:kkHEpBwYCawUAIJLoBOsgETIGYMBAAAA","183":[143,147],"184":"b:AAAABBCACIAMAABAAAIAAgBAAQkBAAAA","187":"b:1vXPtP+b/+18AKd/+/e/8v///98DAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nInputLabel/InputLabel.vue":{"25":"b:1vXPtJ+Z+O1cAKN/+/a/8tz//0sBAAAA","38":"b:1vXPtJ+Z+O1cAKN/+/a/8tz//0sBAAAA","72":"b:1vXPsJ2Z+O1cAKJ/+/S/8tz//0sBAAAA","76":"b:kkECABiAAAQYAAJcABAUAgAACAgBAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nInputNumber/InputNumber.vue":{"15":"b:RuBCAAAA0AQQACASwwQIIpS/fwgAAAAA","31":"b:RuBCAAAA0AQQACASwwQIIpS/fwgAAAAA","32":"b:RuBCAAAA0AQQACASwwQIIpS/fwgAAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nLink/Link.vue":{"31":"b:1/Xf8P3//+39YK9X////8/////8DAAAA","32":"b:1/Xb8P3+/+39YK9X////8/////8DAAAA","34":"b:1/Xb8P3+/+39YK9X////8/////8DAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nMenuItem/MenuItem.vue":{"15":"b://Xf/P///+39f4//////8/////8DAAAA","29":"b://Xf/P///+39f4//////8/////8DAAAA","45":"b:AAAAABQADIQkAAJYAACAACQAQAAAAAAA","52":"b://Xf/P///+39f4//////8/////8DAAAA","64":"b://Xf/P///+39f4//////8/////8DAAAA","73":"b://Xf/P///+39f4//////8/////8DAAAA","89":"b://Xf/P///+39f4//////8/////8DAAAA","92":"b://Xf/P///+39f4//////8/////8DAAAA","99":"b://Xf/P///+39f4//////8/////8DAAAA","117":"b:9/Xb/P/+/+39f4//////8/////8DAAAA","125":[51],"134":"b:ukECKA4AHKy0AAN8EBD6AAQACAAAAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nNavigationDropdown/NavigationDropdown.vue":{"42":"b:x/Xf/P///8X9f4/D//+F8/////8DAAAA","56":"b:x/Xf/P///8X9f4/D//+F8/////8DAAAA","58":"b:x/Xf/P///8X9f4/D//+F8/////8DAAAA","59":"b:x/Xf/P///8X9f4/D//+F8/////8DAAAA","60":"b:x/Xf/P///8X9f4/D//+F8/////8DAAAA","84":"b:x/Xf9P///8X9LQ7C//+F8/////8DAAAA","89":[26,94,105,51,63,150,66,160],"94":[26,94,105,51,63,66,160],"103":"b:x/Xf/P///8X9f4/D//+F8/////8DAAAA","113":[26,94,105,51,63,150,66,160],"117":"b:x/Xf/P///8X9f4/D//+F8/////8DAAAA","126":"b:x/Xf/P///8X9f4/D//+F8/////8DAAAA","128":"b:x/Xf/P///8X9f4/D//+F8/////8DAAAA","130":"b:x/Xb/P/+/8X9f4/D//+F8/////8DAAAA","141":"b:AGEIZBoIf8UEAIBCg7uFwcb3auYDAAAA","143":"b:AGEIZBoIf8UEAIBCg7uFwcb3auYDAAAA","161":"b:AGEIZBoIf8UEAIBCg7uFwcb3auYDAAAA","163":"b:AGEIZBoIf8UEAIBCg7uFwcb3auYDAAAA","166":"b:AGEIZBoIf8UEAIBCg7uFwcb3auYDAAAA","170":[51,150,160],"171":"b:AGEIZBoIf8UEAIBCg7uFwcb3auYDAAAA","172":"b:AGEIZBoIf8UEAIBCg7uFwcb3auYDAAAA","184":[51],"208":"b:x/Xf/P///8X9f4/D//+F8/////8DAAAA","212":"b:x/Xb/P/+/8X9f4/D//+F8/////8DAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nNodeIcon/IconContent.vue":{"21":"b://XP8O///MVdYDQ6//yB//3///sDAAAA","35":"b:x/XP8OX/+MVdIDQi//yB//3///sDAAAA","54":"b:x/XP8OX/+MVdIDQi//yB//3///sDAAAA","57":"b://XP8O///MVdYDQ6//yB//3///sDAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nNodeIcon/NodeIcon.vue":{"24":"b://XP8O///MVdYDQ6//yB//3///sDAAAA","28":"b://XP8O///MVdYDQ6//yB//3///sDAAAA","43":"b://XP8O///MVdYDQ6//yB//3///sDAAAA","59":"b:xvXHsAWZ+MFUACAC+/QBctz//0sDAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nNotice/Notice.vue":{"16":"b:xuHHsAUIyMRQQCEC49ABctz+zEsBAAAA","30":"b:xuHHsAUIyMRQQCEC49ABctz+zEsBAAAA","34":"b:xuHHsAUIyMRQACAC49ABctz+zEgBAAAA","50":[14],"66":"b:xuHHsAUIyMRQQCEC49ABctz+zEsBAAAA","75":"b:xuHDsAUIyMRQQCEC49ABctz+zEsBAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nOption/Option.vue":{"12":"b:9vXPsJ+Z/+10ACN2+/e98/z//18DAAAA","13":"b:9vXLsJ+Y/+10ACN2+/e98/z//18DAAAA","16":"b:9vXLsJ+Y/+10ACN2+/e98/z//18DAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nPopover/Popover.vue":{"84":"b://Xf/P///8X9f4/r//+F8/////8DAAAA","105":"b:AEAAABAACAQoAAIAAAAAgha8HhIBAAAA","111":"b:AEAAABAACAQIAAIAAAAAghS8GAIBAAAA","125":"b:AEAAABAACAQAAAAAAAAAghQ8HBIBAAAA","133":"b://Xf/P///8X9f4/r//+F8/////8DAAAA","134":"b:AEAAABAACAQIAAAAAAAAghK8HgIAAAAA","143":"b://Xf/P///8X9f4/r//+F8/////8DAAAA","146":"b:AEAAABAACAQoAAIAAAAAghQ8HBIBAAAA","147":"b://Xf/P///8X9f4/r//+F8/////8DAAAA","148":"b://Xf/P///8X9f4/r//+F8/////8DAAAA","151":"b://Xf/P///8X9f4/r//+F8/////8DAAAA","167":"b:AEAAABAACAQoAAIAAAAAgha8HhIBAAAA","174":[69,36,51,81,121,127,130,67,156,160]},"packages/frontend/@n8n/design-system/src/components/N8nPromptInputSuggestions/N8nPromptInputSuggestions.vue":{"10":"b://Xf/P///+39f7////////////8DAAAA","20":[20],"23":"b://Xf/P///+39f7////////////8DAAAA","41":"b://Xf/P///+39f7////////////8DAAAA","46":[20]},"packages/frontend/@n8n/design-system/src/components/N8nRadioButtons/RadioButton.vue":{"22":"b:1/Xf8G3/+c3dYCwS//2J//////8DAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nRadioButtons/RadioButtons.vue":{"20":"b:1/Xf8G3/+c3dYCwS//2J//////8DAAAA","33":"b:QlAAAAAAAAQBAAAAAAAAQMD8H3IAAAAA","41":"b:1/Xf8G3/+c3dYCwS//2J//////8DAAAA","50":"b:1/Xb8G3++c3dYCwS//2J//////8DAAAA","58":"b:QlAAAAAAAAQBAAAAAAAAQMD8H3IAAAAA","59":"b:1/Xf8G3/+c3dYCwS//2J//////8DAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nResizeWrapper/ResizeWrapper.vue":{"6":"b://Xf/P///+39f7////////////8DAAAA","72":"b://Xf/P///+39f7////////////8DAAAA","91":"b://Xf/P///+39f7////////////8DAAAA","141":"b:xvXGECUTyMBdCAII6/EBcvz/d3kDAAAA","167":"b:xvXGECUTyMBdCAII6/EBcvz/d3kDAAAA","195":"b://Xf/P///+39f7////////////8DAAAA","201":"b://Xf/P///+39f7////////////8DAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nRoute/Route.vue":{"13":"b://Xf/P///+39f6//////8/////8DAAAA","15":"b://Xf/P///+39f6//////8/////8DAAAA","28":"b://Xf/P///+39f6//////8/////8DAAAA","29":"b://Xf/P///+39f6//////8/////8DAAAA","38":"b://Xf/P///+39f4//////8/////8DAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nScrollArea/N8nScrollArea.vue":{"50":"b://Xf/P///8X9f4/r//+F8/////8DAAAA","68":"b://Xf/P///8X9f4/r//+F8/////8DAAAA","82":"b:x/Xf/P///8X9f4/D//+F8/////8DAAAA","104":[19,20],"144":"b:x/Xf/P///8X9f4/D//+F8/////8DAAAA","163":"b://Xf/P///8X9f4/r//+F8/////8DAAAA","172":"b:9/Xb/P/+/8X9f4/r//+F8/////8DAAAA","174":"b://Xf/P///8X9f4/r//+F8/////8DAAAA","181":[3,4]},"packages/frontend/@n8n/design-system/src/components/N8nSelect/Select.vue":{"11":"b:9vXPtJ+Z/+10ACN2+/e98/z//18DAAAA","63":"b:9vXPtJ+Z/+10ACN2+/e98/z//18DAAAA","64":"b:9vXPtJ+Z/+10ACN2+/e98/z//18DAAAA","72":"b:9vXPtJ+Z/+10ACN2+/e98/z//18DAAAA","85":"b:9vXPtJ+Z/+10ACN2+/e98/z//18DAAAA","89":[17,29,8,34,35,51,58,119,63],"93":[1,7,92],"97":"b:AEFAAAQAAAgAAAAisgAAAAgAAAQAAAAA","113":"b:9vXPtJ+Z/+10ACN2+/e98/z//18DAAAA","134":"b:9vXPtJ+Z/+10ACN2+/e98/z//18DAAAA","136":"b:wFECJA4ADoQUAABQABKkAQDICxABAAAA","142":"b:4mEKMIQIaMBQAAAyuxAAQhCALggAAAAA","145":"b:4mEKMIQIaMBQAAAyuxAAQhCALggAAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nSendStopButton/N8nSendStopButton.vue":{"5":"b://Xf/P///+39f7////////////8DAAAA","30":"b:OIAMAOIDAAAADQCoAAAAAAAAAAAAAAAA","34":[20],"36":"b://Xf/P///+39f7////////////8DAAAA","62":"b:9/Xb/P/+/+39f7////////////8DAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nSpinner/Spinner.vue":{"20":"b://Xf/P///+39//////////////8DAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nSuggestedActions/SuggestedActions.vue":{"39":"b:x/Hf0OH/+cFdAAwA4CUA8/3/338DAAAA","50":"b:x/Hf0OH/+cFdAAwA4CUA8/3/338DAAAA","52":[121],"58":[121],"60":[121],"64":"b:x/Hf0OH/+cFdAAwA4CUA8/3/338DAAAA","74":[121,67],"76":"b:x/Hf0OH/+cFdAAwA4CUA8/3/338DAAAA","84":[121,67],"86":[121,67],"89":[121,67],"100":[121],"108":[121,67],"112":[121,67],"121":[121,67],"131":[121],"132":[121,67],"142":[121],"143":[121,67]},"packages/frontend/@n8n/design-system/src/components/N8nTabs/Tabs.vue":{"25":"b:xvXHtB+Z/8VsAChi+/eF8vz//0sDAAAA","30":"b:xvXHtB+Z/8VsAChi+/eF8vz//0sDAAAA","40":"b:xvXHtB+Z/8VsAChi+/eF8vz//0sDAAAA","53":"b:xvXGEBUQzMBMAABg6/MB8vz/d0kBAAAA","63":[51,143,145],"73":"b:xvXHtB+Z/8VsAChi+/eF8vz//0sDAAAA","92":"b:xvXDtB+Y/8VsAChi+/eF8vz//0sDAAAA","101":"b:xvXHtB+Z/8VsAChi+/eF8vz//0sDAAAA","125":"b:AAECJB4AD8RsAABAAAOEACAAQAADAAAA","145":[51,143,145]},"packages/frontend/@n8n/design-system/src/components/N8nTag/Tag.vue":{"15":"b:x/Hf0OH/+e3dAA8U4CV78/3/3/8DAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nText/Text.vue":{"18":"b://Xf/P///+39f6////////////8DAAAA","26":"b://Xf/P///+39f6////////////8DAAAA","46":"b://Xf/P///+39f6////////////8DAAAA","48":"b://Xf/P///+39f6////////////8DAAAA","52":"b:9/Xb/P/+/+39f6////////////8DAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nTooltip/Tooltip.vue":{"21":"b://Xf/P///+39f7////////////8DAAAA","37":"b://Xf/P///+39f7////////////8DAAAA","45":"b://Xf/P///+39f7////////////8DAAAA","46":"b://Xf/P///+39f7////////////8DAAAA","48":"b://Xf/P///+39f7////////////8DAAAA","57":"b://Xf/P///+39f7////////////8DAAAA","62":"b://Xf/P///+39f7////////////8DAAAA","63":"b://Xf/P///+39f7////////////8DAAAA","73":"b://Xf/P///+39f7////////////8DAAAA","75":"b:1/XG8Bf//MHlKAZi//gj+/////sBAAAA","77":"b://Xf/P///+39f7////////////8DAAAA","88":"b:9/Xb/P/+/+39f7////////////8DAAAA","94":"b:xgAAAGIDAAAAAAQAACIgCAAAAEAAAAAA","98":"b://Xf/P///+39f7////////////8DAAAA","101":"b://Xf/P///+39f7////////////8DAAAA","109":"b:xgCAQAAAEAAAAAQAoCAACIxSBAACAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nTree/Tree.vue":{"20":"b:RAAAAAAQAAAAACAAgAAAAEBoAwAAAAAA","28":"b:RAAAAAAQAAAAACAAgAAAAEBoAwAAAAAA","32":"b:RAAAAAAQAAAAACAAgAAAAEBoAwAAAAAA","36":"b:RAAAAAAQAAAAACAAgAAAAEBoAwAAAAAA","52":"b:RAAAAAAQAAAAACAAgAAAAEBoAwAAAAAA","61":"b:RAAAAAAQAAAAACAAgAAAAEBoAwAAAAAA","68":"b:RAAAAAAQAAAAACAAgAAAAEBoAwAAAAAA","87":[6,139,142,145],"95":[6,139,142,145]},"packages/frontend/@n8n/design-system/src/composables/useAutosizeTextarea.ts":{"41":"b://Xf/P///+39f7////////////8DAAAA","57":"b://Xf/P///+39f7////////////8DAAAA","70":"b://Xf/P///+39f7////////////8DAAAA","116":"b://Xf/P///+39f7////////////8DAAAA","127":"b://Xf/P///+39f7////////////8DAAAA","141":"b://Xf/P///+39f7////////////8DAAAA","149":"b:1/Xf8Pf//+1dbTf7//+v8/////8DAAAA","159":"b://Xf/P///+39f7////////////8DAAAA","165":"b://Xf/P///+39f7////////////8DAAAA"},"packages/frontend/@n8n/design-system/src/composables/useCharacterLimit.ts":{"15":"b://Xf/P///+39f7////////////8DAAAA","19":"b://Xf/P///+39f7////////////8DAAAA","23":[15,18,19,20],"25":"b://Xf/P///+39f7////////////8DAAAA"},"packages/frontend/@n8n/design-system/src/composables/useI18n.ts":{"4":"b://Xf/P///+39f7////////////8DAAAA","6":"b://Xf/P///+39f7////////////8DAAAA"},"packages/frontend/@n8n/design-system/src/composables/useIconBodyLoader.ts":{"20":"b://Xf/P///+39//////////////8DAAAA"},"packages/frontend/@n8n/design-system/src/composables/useMessage.ts":{"14":"b://Xf/P///+39//////////////8DAAAA","15":[61,134,147,150,68],"49":"b:AAEAABAAACAYAAFIAAAEAsAASAABAAAA","64":"b:AAEAABAAACAYAAFIAAAEAsAASAABAAAA","111":[104,49,126]},"packages/frontend/@n8n/design-system/src/composables/useTooltipAppendTo.ts":{"7":"b:x/Xf8O3/+cHdYAQC//0B+/////8DAAAA","11":"b://Xf/P///+39f7////////////8DAAAA","14":"b://Xf/P///+39f7////////////8DAAAA"},"packages/frontend/@n8n/design-system/src/directives/n8n-html.ts":{"19":"b:1vXftH2b/u3UQCRy+/WP8tz//0sDAAAA","31":"b:1vXftH2b/u3UQCRy+/WP8tz//0sDAAAA"},"packages/frontend/@n8n/design-system/src/icons/lucide/bucket.ts":{"16":"b:x/XP8OX/+MVdIDQi//yB//3///sDAAAA"},"packages/frontend/@n8n/design-system/src/icons/lucide/index.ts":{"9":"b:x/XP8OX/+MVdIDQi//yB//3///sDAAAA","15":"b:x/XP8OX/+MVdIDQi//yB//3///sDAAAA"},"packages/frontend/@n8n/design-system/src/locale/format.ts":{"8":"b://Xf/P///+39//////////////8DAAAA","12":"b://Xf/P///+39f7////////////8DAAAA"},"packages/frontend/@n8n/design-system/src/locale/index.ts":{"16":"b://Xf/P///+39f7////////////8DAAAA","36":"b://Xf/P///+39//////////////8DAAAA","38":"b://Xf/P///+39//////////////8DAAAA"},"packages/frontend/@n8n/design-system/src/plugin.ts":{"8":"b://Xf/P///+39//////////////8DAAAA"},"packages/frontend/@n8n/design-system/src/types/binary.ts":{"24":"b:RNBJgACZsAEAACAAgAQAoNj/t0EAAAAA"},"packages/frontend/@n8n/design-system/src/utils/cn.ts":{"4":"b://Xf/P///+39f7////////////8DAAAA"},"packages/frontend/@n8n/design-system/src/utils/typeguards.ts":{"1":"b:9vXPtJ+Z/+10ACN2+/e98/z//18DAAAA"},"packages/frontend/@n8n/design-system/src/utils/uid.ts":{"6":"b://Xf/P///+39//////////////8DAAAA"},"packages/frontend/@n8n/design-system/src/v2/components/Select/Select.vue":{"45":"b://Xf/P///+39f7////////////8DAAAA","63":"b://XY/P///+39f7////////////8DAAAA","69":"b://Xf/P///+39f7////////////8DAAAA","84":"b://Xf/P///+39f7////////////8DAAAA","91":"b://Xf/P///+39f7////////////8DAAAA","99":"b://Xf/P///+39f7////////////8DAAAA","106":[33,105,117],"108":"b://XY/P///+39f7////////////8DAAAA","110":"b://XY/P///+39f7////////////8DAAAA","126":"b://Xf/P///+39f7////////////8DAAAA","137":"b:9/Xb/P/+/+39f7////////////8DAAAA","144":"b://Xf/P///+39f7////////////8DAAAA","147":"b://Xf/P///+39f7////////////8DAAAA","158":"b://XY/P///+39f7////////////8DAAAA","166":"b://XY/P///+39f7////////////8DAAAA","167":"b:9/XY/P/+/+39f7////////////8DAAAA","169":[33,105,117],"181":"b://XY/P///+39f7////////////8DAAAA","182":"b://XY/P///+39f7////////////8DAAAA","185":"b://XY/P///+39f7////////////8DAAAA","188":"b://XY/P///+39f7////////////8DAAAA"},"packages/frontend/@n8n/design-system/src/v2/components/Select/SelectItem.vue":{"10":"b://XY/P///+39f7////////////8DAAAA","13":"b://XY/P///+39f7////////////8DAAAA","17":"b://XY/P///+39f7////////////8DAAAA","21":"b://XY/P///+39f7////////////8DAAAA","25":"b://XY/P///+39f7////////////8DAAAA","32":[33,52,117],"33":"b://XY/P///+39f7////////////8DAAAA","35":"b://XY/P///+39f7////////////8DAAAA","39":"b://XY/P///+39f7////////////8DAAAA","46":"b://XY/P///+39f7////////////8DAAAA"},"packages/frontend/@n8n/design-system/src/v2/utils/index.ts":{"6":"b://XY/P///+39f7////////////8DAAAA","12":"b://XY/P///+39f7////////////8DAAAA"},"packages/frontend/@n8n/frontend-module-sdk/src/registries/modalRegistry.ts":{"6":"b://Xf/P///+39//////////////8DAAAA","10":"b://Xf/P///+39/4//////8/////8DAAAA","11":"b://Xf/P///+39/4//////8/////8DAAAA","14":"b://Xf/P///+39/4//////8/////8DAAAA","42":"b://Xf/P///+39//////////////8DAAAA"},"packages/frontend/@n8n/frontend-module-sdk/src/registries/resourceRegistry.ts":{"25":"b://Xf/P///+39/4//////8/////8DAAAA"},"packages/frontend/@n8n/i18n/src/index.ts":{"36":"b://Xf/P///+39//////////////8DAAAA","48":"b:x/XP8OX/+OlFICQi//QJ+9z//3sBAAAA","63":"b://Xf/P///+39//////////////8DAAAA","93":"b://Xf/P///+39//////////////8DAAAA","101":"b:1/XP8O3/+O1VICQy//SJ+9z//3sBAAAA","105":"b:xgAAAGADEAABAAQAACAASMAAAHAAAAAA","138":"b:guXCMAUYiMAEIAAC/9AAc9y9fSoBAAAA","145":"b:kkECIAwACIQQAAAQEBCAAAAACAAAAAAA","153":"b:kkECIAwACIQQAAAQEBCAAAAACAAAAAAA","170":"b:kkECIAwACIQQAAAQEBCAAAAACAAAAAAA","180":"b:kkECIAwACIQQAAAQEBCAAAAACAAAAAAA","190":"b:kkECIAwACIQQAAAQEBCAAAAACAAAAAAA","203":"b:kkECIAwACIQQAAAQEBCAAAAACAAAAAAA","216":"b:kkECIAwACIQQAAAQEBCAAAAACAAAAAAA","229":"b:1/XP8O3/+O1VACQy+/SJ+tz//3sBAAAA","238":"b:1vXPsI2Z+O1UACAy+/SJ8tz//0sBAAAA","250":"b:xvXPsIWZ+OlEACAi+/QJ8tz//0sBAAAA","262":"b:xvXPsIWZ+OlEACAi+/QJ8tz//0sBAAAA","277":"b:xvXPsAWZ+OlEACAi+/QJ8tz//0sBAAAA","295":"b:xvXPsAWZ+OlEACAi+/QJ8tz//0sBAAAA","317":"b:xvXHsAWZ+MFEACAi+/QB8tz//0sBAAAA","340":[137,139,143,144,145],"373":[23,101],"380":"b:x5DFwGX/oAEBAAQA4CQAeMiLl3AAAAAA","389":"b:wuXPsAVYqMEUYAAC/9gBc969fasDAAAA","407":"b://Xf/P///+39//////////////8DAAAA","490":"b://Xf/P///+39//////////////8DAAAA"},"packages/frontend/@n8n/i18n/src/utils.ts":{"9":"b:1vXPsI2Z+O1UACAy+/SJ8tz//0sBAAAA","29":"b:1vXPsI2Z+O1UACAy+/SJ8tz//0sBAAAA","31":"b:1vXPsI2Z+O1UACAy+/SJ8tz//0sBAAAA","34":"b:1vXPsI2Z+O1UACAy+/SJ8tz//0sBAAAA","41":"b:1vXPsI2Z+O1UACAy+/SJ8tz//0sBAAAA","49":"b:QsDGgACRUAFAAAAC4SAAooS+9wgAAAAA","57":"b:QsDGgACRUAFAAAAC4SAAooS+9wgAAAAA","58":"b:QsDGgACRUAFAAAAC4SAAooS+9wgAAAAA"},"packages/frontend/@n8n/stores/src/cloudPlan.store.ts":{"31":"b://Xf/P///+39//////////////8DAAAA","45":"b://Xf/P///+39/4//////8/////8DAAAA","49":[51,81],"55":[71],"57":[71],"60":[71],"63":[71],"72":[71],"79":[71],"85":[71],"87":[71],"97":[71],"99":[71],"101":"b:x/Xf/P///8X9f4/D//+F8/////8DAAAA","103":[71],"105":"b:AAAADBAAD8BsAINBAAMAACAAQAADAAAA","125":[71],"135":[90,71,116,81],"141":[71],"156":[71],"172":[71],"197":[71],"218":[71],"233":[71],"244":[71],"252":[71]},"packages/frontend/@n8n/stores/src/metaTagConfig.ts":{"1":"b://Xf/P///+39//////////////8DAAAA","8":"b://Xf/P///+39//////////////8DAAAA","34":"b://Xf/P///+39//////////////8DAAAA"},"packages/frontend/@n8n/stores/src/notifications.store.ts":{"56":"b://Xf/P///+39//////////////8DAAAA","65":[84,85,123]},"packages/frontend/@n8n/stores/src/rbac.store.ts":{"9":"b://Xf/P///+39//////////////8DAAAA","80":"b://Xf/P///+39/4//////8/////8DAAAA","116":"b://Xf/P///+39//////////////8DAAAA"},"packages/frontend/@n8n/stores/src/roles.store.ts":{"23":[26,33,94,105,51,58,117,63,66,160],"24":[26,33,94,105,51,58,117,63,66,160],"35":"b://Xf/P///+39/4//////8/////8DAAAA","51":[26,33,94,105,51,58,63,66,160],"52":[26,33,94,105,51,58,63,66,160],"56":[117],"57":[117],"60":[117],"62":[117],"70":[117],"71":[117],"74":[26,33,94,105,51,58,63,66,160],"76":[26,33,94,105,51,58,63,66,160],"84":[92,34,119,63],"85":[92,34,119,63],"88":[62,120,160],"89":[62,120,160],"92":"b://Xf/P///+39/4//////8/////8DAAAA"},"packages/frontend/@n8n/stores/src/settings.store.ts":{"44":"b://Xf/P///+39//////////////8DAAAA","73":"b:x/Xf/P///8X9f4/D//+F8/////8DAAAA","75":"b:x/Xf/P///8X9f4/D//+F8/////8DAAAA","77":"b:x/Xf/P///8X9f4/D//+F8/////8DAAAA","81":"b:x/Xf/P///8X9f4/D//+F8/////8DAAAA","83":"b:x/Xf/P///8X9f4/D//+F8/////8DAAAA","85":"b:x/Xf/P///8X9f4/D//+F8/////8DAAAA","90":"b://Xf/P///+39//////////////8DAAAA","92":"b:x/Xf/P///8X9f4/D//+F8/////8DAAAA","94":"b:x/Xf/P///8X9f4/D//+F8/////8DAAAA","96":"b://Xf/P///+39//////////////8DAAAA","98":"b://Xf/P///+39//////////////8DAAAA","100":"b://Xf/P///+39f4//////8/////8DAAAA","102":[71],"104":"b://Xf/P///+39//////////////8DAAAA","106":"b:x/Xf8O3/+MHdYAwC//0B8/////8DAAAA","112":[71],"114":[71],"116":"b://Xf/P///+39//////////////8DAAAA","120":"b:guXDMAUYiMAEIAAC/9ABc9y9fUoAAAAA","122":"b://Xf/P///+39f7////////////8DAAAA","126":[15,16,17,18,19,20],"130":"b://Xf/P///+39/7////////////8DAAAA","132":"b:x/Xf/P///8X9f4/D//+F8/////8DAAAA","134":"b://Xf/P///+39/7////////////8DAAAA","136":"b://Xf/P///+39//////////////8DAAAA","138":"b://Xf/P///+39//////////////8DAAAA","156":"b:/vXHvJ+Z/8V8ACN6+/eF8vz//0sDAAAA","160":[3,4,5,91,93,33],"166":"b://Xf/P///+39f4//////8/////8DAAAA","172":"b:x/Xf/P3//8H9YLdD//8B+/////8DAAAA","176":"b://Xf/P///+39//////////////8DAAAA","180":"b:wmGAABgIEGBIAAAApueoYp9mwvsDAAAA","186":[90,116,81],"188":"b:AAECLBwAD8RsAINBAAOEACAAQAADAAAA","190":"b://Xf/P///+39/4//////8/////8DAAAA","192":"b://Xf/P///+39////////+/////8DAAAA","196":"b:xvXHtAeZ+MVEACBi+/YB8tz//0sBAAAA","212":[7],"216":[33,105,117],"220":"b:x/Xf8P3//8H9YAxC//8B8/////8DAAAA","224":"b:x/Xf8O3/+cHdYAwC//0B8/////8DAAAA","230":"b://Xf/P///+39//////////////8DAAAA","232":"b:x/Xf/P///8X9f4/D//+F8/////8DAAAA","234":[161],"236":"b:x/Xf/P///8X9f4/D//+F8/////8DAAAA","238":"b://Xf/P///+39//////////////8DAAAA","240":"b:3/Xf9P///+39f7////////////8DAAAA","242":[113],"248":"b://Xf/P///+39f4//////8/////8DAAAA","249":"b:x/Xf8G3/+MHdYAQC//0B8/////8DAAAA","251":[118,139,148],"253":[52,121],"257":[71],"259":"b:x/Xf/P///8X9f4/D//+F8/////8DAAAA","263":"b://Xf/P///+39//////////////8DAAAA","265":"b:x/Xf8O3/+cHdYAwC//0B8/////8DAAAA","269":"b://Xf/P///+39//////////////8DAAAA","301":"b://Xf/P///+39//////////////8DAAAA","309":"b://Xf/P///+39//////////////8DAAAA","313":"b://Xf/P///+39//////////////8DAAAA","317":"b://Xf/P///+39//////////////8DAAAA","321":"b://Xf/P///+39//////////////8DAAAA","325":"b://Xf/P///+39//////////////8DAAAA","376":"b://Xf/P///+39//////////////8DAAAA","400":[161],"401":[161],"402":[161],"408":[52,121],"417":"b://Xf/P///+39/4//////8/////8DAAAA"},"packages/frontend/@n8n/stores/src/useAgentRequestStore.ts":{"18":"b://Xf/P///+39//////////////8DAAAA","64":[0,2,40,12,109,124,135,137,138,140],"78":"b:xZABgAAJAAAAAAAAwCQAEIi+FwAAAAAA"},"packages/frontend/@n8n/stores/src/useRootStore.ts":{"39":"b://Xf/P///+39//////////////8DAAAA","71":"b://Xf/P///+39//////////////8DAAAA","73":"b:AAGAEAAAAAAAACACoQAAAICCDAABAAAA","75":"b:AAGAEAAAAAAAACACoQAAAICCDAABAAAA","79":[6],"83":"b:AAGAEAAAAAAAACACoQAAAICCDAABAAAA","85":"b:xgGAEAAAAAAAACQCoSAACICCDAABAAAA","93":"b:AAGAEAAAAAAAACACoQAAAICCDAABAAAA","95":"b:AAGAEAAAAAAAACACoQAAAICCDAABAAAA","99":"b://Xf/P///+39//////////////8DAAAA","101":"b:x5DFwAX/oAEBAAQA4CQAeMj/l3ECAAAA","103":"b://Xf/P///+39//////////////8DAAAA","105":[15,16,1,7,35,134,6,143,147],"109":"b://Xf/P///+39/4//////8/////8DAAAA","111":"b://Xf/P///+39/7////////////8DAAAA","113":[17,8,35,147,68],"117":"b://Xf/P///+39//////////////8DAAAA","119":[52,121],"121":[52,121],"123":[52,121],"125":"b://Xf/P///+39//////////////8DAAAA","136":"b://Xf/P///+39//////////////8DAAAA","141":"b://Xf/P///+39//////////////8DAAAA","146":"b://Xf/P///+39//////////////8DAAAA","151":"b://Xf/P///+39//////////////8DAAAA","155":"b://Xf/P///+39//////////////8DAAAA","159":"b://Xf/P///+39//////////////8DAAAA","163":"b://Xf/P///+39//////////////8DAAAA","167":"b://Xf/P///+39//////////////8DAAAA","171":"b://Xf/P///+39//////////////8DAAAA","175":"b://Xf/P///+39//////////////8DAAAA","179":"b://Xf/P///+39//////////////8DAAAA","183":"b://Xf/P///+39//////////////8DAAAA","188":"b://Xf/P///+39//////////////8DAAAA","192":"b://Xf/P///+39//////////////8DAAAA","196":"b://Xf/P///+39//////////////8DAAAA","200":"b://Xf/P///+39//////////////8DAAAA","204":"b://Xf/P///+39//////////////8DAAAA","212":"b://Xf/P///+39//////////////8DAAAA","216":"b://Xf/P///+39//////////////8DAAAA","220":"b://Xf/P///+39//////////////8DAAAA"},"packages/frontend/@n8n/stores/src/users.store.ts":{"41":"b://Xf/P///+39/8//////8/////8DAAAA","42":"b://Xf/P///+39//////////////8DAAAA","43":"b://Xf/P///+39/8//////8/////8DAAAA","45":[34,51,66],"50":"b://Xf/P///+39//////////////8DAAAA","75":"b://Xf/P///+39/4//////8/////8DAAAA","85":"b://Xf/P///+39/4//////8/////8DAAAA","96":"b:x/Xf/P///8X9f4/D//+F8/////8DAAAA","98":"b://Xf/P///+39//////////////8DAAAA","102":"b:AEGCMAEACMAAAAAArQAAchyUPQoAAAAA","106":"b://Xf/P///+39f7////////////8DAAAA","108":[34,51,66],"110":"b:guXCMAUYiMAEIAAi/9ACc9y9fSoBAAAA","112":[90,116,81],"114":[90,116,81],"118":[1,7,8,109,121,6,13,14],"122":[69],"126":[8,109,121,6,14],"132":[1,7,8,10,108,124,135,13,14],"143":[117],"149":"b://Xf/P///+39/8//////8/////8DAAAA","150":"b://Xf/P///+39/8//////8/////8DAAAA","172":"b://Xf/P///+39/8//////8/////8DAAAA","185":"b://Xf/P///+39/8//////8/////8DAAAA","194":"b://Xf/P///+39/8//////8/////8DAAAA","208":[51,81],"212":[117],"231":[88,51,81],"240":"b://Xf/P///+39//////////////8DAAAA","244":"b://Xf/P///+39//////////////8DAAAA","248":[51,81],"305":[116,81],"341":[117],"346":"b:x/Xf/P///8X9f4/D//+l8/////8DAAAA","398":"b:x/Xf/P3//8H9YLdD//8B+/////8DAAAA","405":[81],"409":[81],"417":[81],"424":[81],"441":[117],"459":[117],"468":"b://Xf/P///+39/4//////8/////8DAAAA"},"packages/frontend/editor-ui/src/app/App.vue":{"35":"b://Xf/P///+39//////////////8DAAAA","49":"b://Xf/P///+39//////////////8DAAAA","69":"b://Xf/P///+39//////////////8DAAAA","74":"b:xuGDAAQQSAAAAAACoBAAYlz+HwIAAAAA","81":"b://Xf/P///+39//////////////8DAAAA","87":"b://Xf/P///+39//////////////8DAAAA","93":"b://Xf/P///+39f7////////////8DAAAA","94":"b://Xf/P///+39f7////////////8DAAAA","101":"b://Xf/P///+39//////////////8DAAAA","113":"b://Xf/P///+39f7////////////8DAAAA","120":"b://Xf/P///+39//////////////8DAAAA","125":"b://Xf/P///+39//////////////8DAAAA","133":"b://Xf/P///+39//////////////8DAAAA","136":"b://Xf/P///+39//////////////8DAAAA"},"packages/frontend/editor-ui/src/app/api/favorites.ts":{"16":"b://Xf/P///+39/4//////8/////8DAAAA"},"packages/frontend/editor-ui/src/app/api/workflows.ts":{"26":"b:wuXf8A1YqMGcYDAC/9kBf968fa4DAAAA","39":"b:wuXf8A1YqMGcYDAC/9kBf968fa4DAAAA","65":"b:R3AAQOC3XsBdAAwAICUA4qHLw3MDAAAA","69":"b:wuXf8A1YqMGMIAAC/9kBc968fa4BAAAA","73":[52,121,160],"87":[113],"98":"b:AAAADBAAD8BsAINBAAMAACAAQAMDAAAA","114":"b:x/Xf/P3//8H9YI9D//8B8/////8DAAAA","136":"b:x5BFwGX/oAEBAAQA4CQAeMj/l3EAAAAA","149":"b:R3AAQAC0WMBdAAQAICQA4KHLw3EDAAAA","160":"b:x/Hf0OX/+MFdAAwA4CUA8/3/338DAAAA"},"packages/frontend/editor-ui/src/app/components/BottomMenu.vue":{"23":"b://Xf/P///8X9f4/r//+F8/////8DAAAA","33":[69,130],"78":[69,51,81,130],"82":[51,81],"84":"b://Xf/P///8X9f4/r//+F8/////8DAAAA","104":[69,130],"106":[69,130],"118":[69],"129":"b:x/Xf/P///8X9f4/D//+F8/////8DAAAA","134":[69,130],"146":[51,81],"148":[51,81],"168":"b://Xf/P///8X9f4/r//+F8/////8DAAAA","173":[51,81]},"packages/frontend/editor-ui/src/app/components/BreakpointsObserver.vue":{"30":"b:x/Xf8O3/+cHdYAwC//0B8/////8DAAAA","37":"b:x/Xf8O3/+cHdYAwC//0B8/////8DAAAA","53":"b:x/Xf8O3/+cHdYAwC//0B8/////8DAAAA","72":[48,64],"76":[48,64],"84":"b:x/Xf8O3/+cHdYAwC//0B8/////8DAAAA","88":[37,40,41,48,51,133,150,67],"91":"b:x/Xf8O3/+cHdYAwC//0B8/////8DAAAA"},"packages/frontend/editor-ui/src/app/components/ConnectionTracker.vue":{"26":"b:x/Xf8O3/+cHdYAwC//1B8/////8DAAAA"},"packages/frontend/editor-ui/src/app/components/Draggable.vue":{"40":"b:xvXHsFWZ/8FkACRC+/cB8vz//0sDAAAA","44":"b:AAAAAAAADwAEAAAAAAEAACBIAwABAAAA","78":[48,139],"105":[48,139],"115":[48,104,49,50,51,133,139,160],"127":[48,104,49,50,51,133,139,160],"135":"b:xvXEkBUYzwBkAARA6/cB8vz/d0kDAAAA","149":"b:xvXHsFWZ/8FkACRC+/cB8vz//0sDAAAA"},"packages/frontend/editor-ui/src/app/components/DraggableTarget.vue":{"15":"b:xvXPsIWZ+O1EACAi+/QJ8tz//0sBAAAA","31":"b:xvXPsIWZ6M1EAAAi+/QJ8tz//wsBAAAA","32":[139],"34":"b:xvXPsIWZ+O1EACAi+/QJ8tz//0sBAAAA","37":"b:xvXPsIWZ+O1EACAi+/QJ8tz//0sBAAAA","39":[139],"49":"b:wsHIkAWJyAxEACAissAAcJy/3wIAAAAA","53":"b:wsHAkAWJyAxEACAissAAcJy/3wIAAAAA","57":"b:AkHAgAQICAxAAAAisgAAABi+HgIAAAAA","64":[139],"79":"b:xvXPsIWZ+O1EACAi+/QJ8tz//0sBAAAA"},"packages/frontend/editor-ui/src/app/components/DynamicModalLoader.vue":{"9":"b://Xf/P///+39//////////////8DAAAA","17":"b://Xf/P///+39/4//////8/////8DAAAA","23":"b://Xf/P///+39//////////////8DAAAA","29":"b://Xf/P///+39/4//////8/////8DAAAA","44":"b://Xf/P///+39//////////////8DAAAA","46":"b://Xf/P///+39/4//////8/////8DAAAA","54":"b://Xf/P///+39//////////////8DAAAA","58":"b:9/Xb/P/+/+39/4//////8/////8DAAAA","60":[5,91,92,93,36,94]},"packages/frontend/editor-ui/src/app/components/FocusSidebar.vue":{"32":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAA","51":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAA","52":[112,127],"53":"b:x/Xf8OX/+cHdYCQC//0B+/////8DAAAA","55":[112,127],"60":[112,127],"65":[112,127],"72":[112,127],"83":[112,127],"87":"b:x/Xf8OX/+cHdYCQC//0B+/////8DAAAA","95":[112,127],"111":[112,127],"115":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAA","119":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAA","120":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAA","131":"b:x/Xf8OX/+cHdYCQC//0B+/////8DAAAA","159":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAA","163":"b:QgAAACADCAAJAAAAAAAAQuAAQHACAAAA","166":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAA","188":[112,127]},"packages/frontend/editor-ui/src/app/components/FreeAiCreditsCallout.vue":{"21":"b:1vXHsI2Z+MVUACAS+/SB8tz//0sBAAAA","55":"b:1vXHsI2Z+MVUACAS+/SB8tz//0sBAAAA","57":"b:1vXHsI2Z+MVUACAS+/SB8tz//0sBAAAA","63":"b:1vXHsI2Z+MVUACAS+/SB8tz//0sBAAAA"},"packages/frontend/editor-ui/src/app/components/KeyboardShortcutTooltip.vue":{"21":"b://Xf/P///8X9f7/r//+F//////8DAAAA"},"packages/frontend/editor-ui/src/app/components/MainHeader/ActionsDropdownMenu.vue":{"44":"b:x/Xf8O3/+cHdYAwC//0B8/////8DAAAA","74":"b:x/Xf8O3/+cHdYAwC//0B8/////8DAAAA","82":"b:x/Xf8O3/+cHdYAwC//0B8/////8DAAAA","84":"b:x/Xf8O3/+cHdYAwC//0B8/////8DAAAA","88":"b:QMAFgABAIQEAAAAAAIgBAACARYAAAAAA","92":"b:QMAFgABAIQEAAAAAAIgBAACARYAAAAAA","114":"b:x/Xf8O3/+cHdYAwC//0B8/////8DAAAA","239":"b:QMAFgABAMUEIAAAAAIgBAQSARYAAAAAA","413":"b:x/Xf8O3/+cHdYAwC//0B8/////8DAAAA","421":"b:QMAFgABAIQEAAAAAAIgBAACARYAAAAAA"},"packages/frontend/editor-ui/src/app/components/MainHeader/MainHeader.vue":{"62":"b:x/Xf8A38+cHdYAwC//0B8/////8DAAAA","70":"b:x/Xf8O3/+cHdYAwC//0B8/////8DAAAA","71":"b:x/Xf8O3/+cHdYAwC//0B8/////8DAAAA","76":"b:x/Xf8O3/+cHdYAwC//0B8/////8DAAAA","77":"b:x/Xf8O3/+cHdYAwC//0B8/////8DAAAA","78":"b:x/Xf8O3/+cHdYAwC//0B8/////8DAAAA","79":"b:x/Xf8O3/+cHdYAwC//0B8/////8DAAAA","80":"b:x/Xf8O3/+cHdYAwC//0B8/////8DAAAA","82":"b:x/Xf8O3/+cHdYAwC//0B8/////8DAAAA","85":[48],"88":"b:x/Xf8O3/+cHdYAwC//0B8/////8DAAAA","96":"b:x/Xf8O3/+cHdYAwC//0B8/////8DAAAA","106":"b:xvXfsAXY6cFdQAAC+/UBc/z//38DAAAA","110":"b:x/Xf8O3/+cHdYAwC//0B8/////8DAAAA","114":[37,40,41,48,133,150,67],"118":"b:x/Xf8O3/+cHdYAwC//0B8/////8DAAAA","123":"b:x/Xf8O3/+cHdYAwC//0B8/////8DAAAA","130":"b:x/Xf8O3/+cHdYAwC//0B8/////8DAAAA","140":"b:x/Xf8A38+cHdYAQC//0B8/////8DAAAA","160":[1,48,126,134,135,64,6,156,157,158],"181":[1,48,126,157,158],"212":[1,48,126,134,135,64,6,156,157,158],"238":[48],"256":[48],"260":[48]},"packages/frontend/editor-ui/src/app/components/MainHeader/TabBar.vue":{"22":[1,48,126,134,135,64,6,156,157,158],"24":"b:x/Xf8A38+cHdYAwC//0B8/////8DAAAA"},"packages/frontend/editor-ui/src/app/components/MainHeader/WorkflowDetails.vue":{"46":"b:x/Xf8O3/+cHdYAwC//0B8/////8DAAAA","92":[48,154],"98":[48],"101":"b:x/Xf8O3/+cHdYAwC//0B8/////8DAAAA","105":"b:x/Xf8O3/+cHdYAwC//0B8/////8DAAAA","109":"b:x/Xf8O3/+cHdYAwC//0B8/////8DAAAA","114":"b:x/Xf8O3/+cHdYAwC//0B8/////8DAAAA","119":[48,112,150,154,66,67,159,161],"121":"b:x/Xf8O3/+cHdYAwC//0B8/////8DAAAA","133":"b:x/Xf8O3/+cHdYAwC//0B8/////8DAAAA","134":[48,51,150,67],"141":[48,154],"149":[48,154],"156":[48,154],"185":[48],"190":[48],"196":"b:QMAFgARAIQEAAAAAAIkBAQCARYABAAAA","222":[48,67],"266":[48],"287":[48],"323":[48,67],"333":[48,67],"384":[48],"386":[48],"392":"b:QMAFgABAIQEAAAAAAIgBAACARYAAAAAA","398":"b:x/Xf8O3/+cHdYAwC//0B8/////8DAAAA","407":[37,40,41,48,51,133,150,67],"415":"b:x/Xf8O3/+cHdYAwC//0B8/////8DAAAA","426":"b:x/Xf8O3/+cHdYAwC//0B8/////8DAAAA","432":"b:x/Xb8O3++cHdYAwC//0B8/////8DAAAA","491":[67],"498":"b:x/Xf8O3/+cHdYAwC//0B8/////8DAAAA"},"packages/frontend/editor-ui/src/app/components/MainHeader/WorkflowHeaderDraftPublishActions.vue":{"77":"b:x/Xf8O3/+cHdYAwC//0B8/////8DAAAA","83":"b:x/Xf8O3/+cHdYAwC//0B8/////8DAAAA","84":"b:x/Xf8O3/+cHdYAwC//0B8/////8DAAAA","101":[48],"105":"b:x/Xf8O3/+cHdYAwC//0B8/////8DAAAA","109":"b:x/Xf8O3/+cHdYAwC//0B8/////8DAAAA","120":"b:x/Xf8O3/+cHdYAwC//0B8/////8DAAAA","121":[48,51,150,67],"131":"b:x/Xf8O3/+cHdYAwC//0B8/////8DAAAA","133":[48],"142":"b:QMAFgABAIQEAAAAAAIgBAACARYAAAAAA","144":"b:x/Xf8O3/+cHdYAwC//0B8/////8DAAAA","148":"b:x/Xf8O3/+cHdYAwC//0B8/////8DAAAA","152":"b:x/Xf0OX/+cFdAAwA4TUA8/3///8DAAAA","156":"b:xuWKUACAUQAYQAQC4TAAooS+bIgDAAAA","169":[48],"170":[48],"171":"b:x/Xf8O3/+cHdYAwC//0B8/////8DAAAA","173":"b:x/Xf8O3/+cHdYAwC//0B8/////8DAAAA","204":"b:x/Hf0OX/+cFdAAwA4CUA8/3/338DAAAA","205":"b:x/Hf0OX/+cFdAAwA4CUA8/3/338DAAAA","206":"b:x/Xf8O3/+cHdYAwC//0B8/////8DAAAA","207":[30,47,48,52,121,67,155,160],"209":"b:wuXf8A1YqcGcYAAC/9kBc968fa4DAAAA","215":[48,121,160],"240":[47,48,121,67,155,160],"245":[47,48,121,67,155,160],"252":[48],"258":[48],"269":[48],"277":[48],"281":[47,48,121,67,155,160],"309":"b:x/Xf8O3/+cHdYAwC//0B8/////8DAAAA","455":"b:x/Xf8O3/+cHdYAwC//0B8/////8DAAAA","460":"b:x/Xf8O3/+cHdYAwC//0B8/////8DAAAA","469":"b:x/Xf8O3/+cHdYAwC//0B8/////8DAAAA","471":[48],"478":[48],"483":"b:x/Xf8O3/+cHdfwyC//0B8/////8DAAAA","488":"b:x/Xf8O3/+cHdYAwC//0B8/////8DAAAA","490":"b:x/Xf8O3/+cHdYAwC//0B8/////8DAAAA","526":"b:x/Xf8O3/+cHdYAwC//0B8/////8DAAAA","531":"b:x/Xf8O3/+cHdYAwC//0B8/////8DAAAA","534":[48],"553":[48],"588":[48],"598":[48],"618":[48],"643":[48],"644":[48],"649":[48],"653":[48],"658":[48],"664":"b:x/Xf8O3/+cHdYAwC//0B8/////8DAAAA","669":[37,40,41,48,51,133,150,67],"677":"b:x/Xf8O3/+cHdYAwC//0B8/////8DAAAA","721":"b:x/Xf8O3/+cHdYAwC//0B8/////8DAAAA","732":"b:x/Xb8O3++cHdYAwC//0B8/////8DAAAA","768":"b:x/Xf8O3/+cHdYAwC//0B8/////8DAAAA"},"packages/frontend/editor-ui/src/app/components/MainSidebar.vue":{"36":"b:x/Xf/P///8X9f4/D//+F8/////8DAAAA","83":"b:x/Xf/P///8X9f4/D//+F8/////8DAAAA","91":"b:x/Xf/P///8X9f4/D//+F8/////8DAAAA","93":"b:x/Xf/P///8X9f4/D//+F8/////8DAAAA","216":"b:x/Xf/P///8X9f4/D//+F8/////8DAAAA","217":"b:x/Xf/P///8X9f4/D//+F8/////8DAAAA","220":"b:x/Xf/P///8X9f4/D//+F8/////8DAAAA","232":[69,71,94,50,51,81,66],"233":[69,71,94,50,51,81,66],"238":"b:x/Xf/P///8X9f4/D//+F8/////8DAAAA","242":"b:x/Xf/P///8X9f4/D//+F8/////8DAAAA","247":"b:x/Xf/P///8X9f4/D//+F8/////8DAAAA","258":"b:AAAAAAAACMAcAAIAAAEAACAAQAADAAAA","267":[130],"274":[160],"277":[160],"289":[69,51,81,130],"332":[51,81],"340":"b:x/Xf/P///8X9f4/D//+F8/////8DAAAA","373":"b:x/Xf/P///8X9f4/D//+F8/////8DAAAA"},"packages/frontend/editor-ui/src/app/components/MainSidebarHeader.vue":{"37":"b:x/Xf9P///8X9LQ7C//+F8/////8DAAAA","41":[69,71,94,50,51,81,66],"45":[160],"69":"b:OAAACAIADACkAAJoAAAAAAAAAAAAAAAA","114":"b:x/Xf/P///8X9f4/D//+F8/////8DAAAA","123":"b:x/Xf/P///8X9f4/D//+F8/////8DAAAA","132":"b:x/Xf/P///8X9f4/D//+F8/////8DAAAA","137":[80],"144":[27,34,51,66],"168":"b://Xf/P///8X9f4/r//+F8/////8DAAAA"},"packages/frontend/editor-ui/src/app/components/MainSidebarSourceControl.vue":{"27":[80],"37":[80],"41":"b:x/Xf/P///8X9f4/D//+F8/////8DAAAA","47":[80],"53":[80],"62":[80],"84":"b:x/Xf/P///8X9f4/D//+F8/////8DAAAA","111":[80],"144":[80],"170":[80]},"packages/frontend/editor-ui/src/app/components/ModalDrawer.vue":{"7":"b://Xf/P///+39//////////////8DAAAA","40":"b:QOEFsBBAIoUcCAAAAIuFeAfQRaEDAAAA","61":"b://Xf/P///+39//////////////8DAAAA","75":"b://Xf/P///+39//////////////8DAAAA","89":"b://Xf/P///+39//////////////8DAAAA","91":[26]},"packages/frontend/editor-ui/src/app/components/ModalRoot.vue":{"20":"b://Xf/P///+39//////////////8DAAAA","21":"b:9/Xb/P/+/+39//////////////8DAAAA"},"packages/frontend/editor-ui/src/app/components/Modals.vue":{"151":"b://Xf/P///+39//////////////8DAAAA","153":"b:kkECIAwACIQQAAAQEBCAAAAACAAAAAAA","182":[130],"190":[17,29,8,34,35,51,58,119,63],"194":[48],"200":[130,159],"208":[154],"216":"b://Xf/P///+39//////////////8DAAAA","220":"b://Xf/P///+39//////////////8DAAAA","226":[52,121],"244":[117],"250":[47,121,67,155,160],"254":[81],"258":[81],"262":[62,120,160],"268":[113],"276":[113],"287":[59,115,61],"341":[58],"347":[58],"373":[160],"402":[78,68],"411":[50],"420":[15],"432":[49],"438":[49,50,51],"468":[152],"493":[114],"516":[47,121,67,155,160]},"packages/frontend/editor-ui/src/app/components/NodeExecuteButton.vue":{"15":"b:xvXHsAWZ6MFEAAAC+/QB8tz//wsBAAAA","59":"b:xvXHsAWZ6MFEAAAC+/QB8tz//wsBAAAA","74":"b:hIABgAAIAAAAAAAAwCQAAAiqFwAAAAAA","81":"b:xvXHsAWZ6MFEAAAC+/QB8tz//wsBAAAA","93":"b:xvXHsAWZ6MFEAAAC+/QB8tz//wsBAAAA","99":"b:xvXHsAWZ6MFEAAAC+/QB8tz//wsBAAAA","105":"b:xvXHsAWZ6MFEAAAC+/QB8tz//wsBAAAA","116":"b:xuXHsAUYyMBEAAAC+/QB8ly+fwoAAAAA","120":"b:hMCBgAAIAAAAAAAA4CQAAAiqFwAAAAAA","132":"b:hIABgAAIAAAAAAAAwCQAAAiqFwAAAAAA","136":"b:xvXHsAWZ6MFEAAAC+/QB8tz//wsBAAAA","148":"b:xvXHsAWZ6MFEAAAC+/QB8tz//wsBAAAA"},"packages/frontend/editor-ui/src/app/components/NodeIcon.vue":{"43":"b:xvXPsOWb+MFUYCQi//QB+9z//2sDAAAA","44":"b:xvXPsOWb+MFUYCQi//QB+9z//2sDAAAA","48":"b:x/XP8OX/+MFdYDQi//wB//3///sDAAAA","50":"b:x/XP8OX/+MFdYDQi//wB//3///sDAAAA","51":"b:x/XP8OX/+MFdYDQi//wB//3///sDAAAA","56":"b:x/XP8OX/+MFdYDQi//wB//3///sDAAAA","61":"b:x/XP8OX/+MFdYDQi//wB//3///sDAAAA","66":"b:x/XP8OX/+MFdYDQi//wB//3///sDAAAA","68":"b:x/XP8OX/+MFdYDQi//wB//3///sDAAAA"},"packages/frontend/editor-ui/src/app/components/NpsSurvey.vue":{"14":"b://Xf/P///+39//////////////8DAAAA","40":"b://Xf/P///+39//////////////8DAAAA","117":"b://Xf/P///+39//////////////8DAAAA","127":"b://Xf/P///+39//////////////8DAAAA","144":"b://Xf/P///+39//////////////8DAAAA","146":"b:9/Xb/P/+/+39//////////////8DAAAA"},"packages/frontend/editor-ui/src/app/components/TidyUpIcon.vue":{"2":"b:x/Xf8G3/+cHdYAQC//0B8/////8DAAAC"},"packages/frontend/editor-ui/src/app/components/UpdatesPanel.vue":{"11":"b://Xf/P///+39//////////////8DAAAA","15":"b:9/Xb/P/+/+39//////////////8DAAAA","24":"b://Xf/P///+39//////////////8DAAAA","29":[26],"61":[26],"70":[26]},"packages/frontend/editor-ui/src/app/components/WorkflowProductionChecklist.vue":{"32":"b:x/Hf0OX/+cFdAAwA4CUA8/3/338DAAAA","49":[30,47,48,52,121,67,155,160],"50":[30,47,48,52,121,67,155,160],"51":[30,47,48,52,121,67,155,160],"57":[48,121],"61":[30,47,48,52,121,67,155,160],"66":[30,47,48,52,121,67,155,160],"67":[30,47,48,52,121,67,155,160],"72":[30,47,48,52,121,67,155,160],"73":[30,47,48,52,121,67,155,160],"76":[30,47,48,52,121,67,155,160],"81":[48,121,67],"85":[47,48,121,67,155,160],"87":"b:x/Hf0OH/+cFdAAwA4CUA8/3/338DAAAA","91":"b:x/Hf0OX/+cFdAAwAwCUA0/3/338DAAAA","92":[34,48,51,66],"94":"b:x/Hf0OX/+cFdAAwA4CUA8/3/338DAAAA","98":"b:x/Hf0OX/+cFdAAwA4CUA8/3/338DAAAA","102":"b:x/Hf0OX/+cFdAAwAwCUA0/3/338DAAAA","104":"b:x/Hf0OX/+cFdAAwA4CUA8/3/338DAAAA","169":"b:x/Hb0OX++cFdAAwA4CUA8/3/338DAAAA","215":"b:x/Hf0OX/+cFdAAwA4CUA8/3/338DAAAA","224":[48,121],"249":[48,121],"259":[48,121],"275":[48,121],"286":[48,121],"294":[48,121,67],"298":[48,121],"302":[48,121,67],"312":"b:x/Hf0OX/+cFdAAwA4CUA8/3/338DAAAA","314":[47,48,121,67,155,160],"321":[48,121,67],"328":[48],"340":"b:x/Hf0OX/+cFdAAwA4CUA8/3/338DAAAA","343":"b:x/Hf0OX/+cFdAAwA4CUA8/3/338DAAAA"},"packages/frontend/editor-ui/src/app/components/app/AppBanners.vue":{"7":"b://Xf/P///+39//////////////8DAAAA","9":"b://Xf/P///+39//////////////8DAAAA","10":"b://Xf/P///+39//////////////8DAAAA"},"packages/frontend/editor-ui/src/app/components/app/AppChatPanel.vue":{"9":"b://Xf/P///+39f7////////////8DAAAA","24":"b://Xf/P///+39f7////////////8DAAAA","26":"b://Xf/P///+39f7////////////8DAAAA","34":"b://Xf/P///+39f7////////////8DAAAA","49":"b://Xf/P///+39f7////////////8DAAAA"},"packages/frontend/editor-ui/src/app/components/app/AppCommandBar.vue":{"11":"b://Xf/P///+39//////////////8DAAAA","24":"b://Xf/P///+39/8//////8/////8DAAAA","26":"b://Xf/P///+39//////////////8DAAAA","28":"b://Xf/P///+39/8//////8/////8DAAAA","34":[157,160],"37":"b://Xf/P///+39//////////////8DAAAA"},"packages/frontend/editor-ui/src/app/components/app/AppHeader.vue":{"6":"b:x/Xf8A38+cHdYAwC//0B8/////8DAAAA"},"packages/frontend/editor-ui/src/app/components/app/AppLayout.vue":{"5":"b://Xf/P///+39//////////////8DAAAA","6":"b:AAECbB4AD8R8AINBAAOkACAAQAADAAAA","9":"b:AAAAAAAAECyAAAMUAAB6AAAAAAAAAAAA","12":"b:x/Xf8A38+cHdYAwC//0B8/////8DAAAA","14":[87,88,51,81],"15":[84,85,122,123],"16":[3,4,5,91,93,33],"18":[37,72,73,74,38,95,39,75,40,41,76],"30":"b://Xf/P///+39f7////////////8DAAAA","35":"b://Xf/P///+39f7////////////8DAAAA","37":"b://Xf/P///+39//////////////8DAAAA"},"packages/frontend/editor-ui/src/app/components/app/AppModals.vue":{"7":"b://Xf/P///+39//////////////8DAAAA"},"packages/frontend/editor-ui/src/app/components/transitions/SlideTransition.vue":{"3":"b:x/Xf8G3/+cHdYAQC//0B8/////8DAAAA"},"packages/frontend/editor-ui/src/app/composables/useActivityDetection.ts":{"7":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAA","10":"b:AmEPEAEACIBMAAAAoCQAck0fVnkBAAAA","16":"b:QmEPEAGAiIBMAAAAoCQAcl0fVnkBAAAA","17":"b:QmEPEAGAiIBMAAAAoCQAcl0fVnkBAAAA","22":"b:QgAAACADCMAdAAAAAAEAQuEAQHADAAAA","23":"b:QgAAACADCMAdAAAAAAEAQuEAQHADAAAA","29":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAA","30":"b:QmEPEAGAiIBMAAAAoCQAcl0fVnkBAAAA","40":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAA","47":"b:QgAAACADCMAdAAAAAAEAQuAAQHADAAAA"},"packages/frontend/editor-ui/src/app/composables/useAiGateway.ts":{"14":"b://Xf/P///+39f4//////8/////8DAAAA","18":"b:x/Xf/P///+39f4/3////8/////8DAAAA","37":"b:AFGCMAAAaMAAAAAAuwAAQhCUPwgAAAAA","43":"b:xvXPsIWZ+OlEACAi+/QJ8tz//0sBAAAA","46":"b:x/Xf8O3/+cHdYAQi//0B8/////8DAAAA"},"packages/frontend/editor-ui/src/app/composables/useBackendStatus.ts":{"10":"b://Xf/P///+39//////////////8DAAAA","19":"b://Xf/P///+39//////////////8DAAAA","49":"b://Xf/P///+39//////////////8DAAAA","67":"b:RyABAPYDCYAAAAB4oAAQYGAUACIAAAAA","72":"b://Xf/P///+39//////////////8DAAAA"},"packages/frontend/editor-ui/src/app/composables/useBasePageRedirectionHelper.ts":{"21":"b://Xf/P///+39//////////////8DAAAA"},"packages/frontend/editor-ui/src/app/composables/useBeforeUnload.ts":{"15":"b://Xf/P///+39//////////////8DAAAA","22":"b:AGAAAAgQGABEAAAAIAEAYAGFRQEBAAAA","27":"b:AGAAAAgQGABEAAAAIAEAYAGFRQEBAAAA","32":"b:AAAAAAAAGABEAAAAAAAAYAEBQAEBAAAA","43":"b://Xf/P///+39//////////////8DAAAA","47":"b:x/Xf8O3/+MHdYDwC//0B+/////8DAAAA","51":"b:QgAAACADCMAdAAAAAAEAQuAAQHADAAAA"},"packages/frontend/editor-ui/src/app/composables/useBrowserNotifications.ts":{"57":"b://Xf/P///+39f7////////////8DAAAA","66":[19,20],"80":[19,20]},"packages/frontend/editor-ui/src/app/composables/useBugReporting.ts":{"30":"b:x/Xf/P///8X9f4/D//+F8/////8DAAAA"},"packages/frontend/editor-ui/src/app/composables/useCanvasOperations.ts":{"203":"b:wuXPsAVYqMEUYAAC/9gBc969fasDAAAA","213":"b:QsHPsAVYqMEUQAACu8gBcFy9fYsCAAAA","245":"b:xvXHsAWZ+MFEACAC+/QBctz//0sBAAAA","281":"b:guXCMAUYiMAEIAAC/1AAc9y8fCoBAAAA","285":"b:wuXPsAVYqMEUYAAC/9gBc969fasDAAAA","295":[19,110],"308":[19,110],"317":[110],"325":[19,109,110,124,125,128,130,66],"333":[19,109,110,124,125,128,130,66],"342":"b:AAAIAAAAgAAEAAAAAOAAMAUAAAAAAAAA","364":[130],"373":[111,152],"401":[152],"413":[126,14,153],"474":[107,124,125,128,130,6,152],"553":[107,124,125,128,130,6,152],"617":[124,125,128,130,152],"622":[124,125,128,130,152],"629":[130,152],"634":[107,124,125,128,130,6,152],"653":[152],"674":[152],"850":"b:xvXHsAWZ+MFEACAC+/QBctz//0sBAAAA","860":"b:xvXHsAWZ+MFEACAC+/QBctz//0sBAAAA","883":"b:x/XHsAXZ+MFcICQC//wB89////sBAAAA","899":"b:QgAAAAAAAAAAAAAAAAAAUgQCAHgAAAAA","912":[130],"955":"b:wuXPsAVYqMEUYAAC/9gBc969fasDAAAA","963":"b:wuXPsAVYqMEUYAAC/9gBc969fasDAAAA","1047":"b:wuXPsAVYqMEUYAAC/9gBc969fasDAAAA","1066":"b:wuXPsAVYqMEUYAAC/9gBc969fasDAAAA","1080":[126,66],"1091":"b:wuXPsAVYqMEUYAAC/9gBc969fasDAAAA","1121":"b:wuXPsAVYqMEUYAAC/9gBc969fasDAAAA","1170":[130,152],"1179":[55,111,124],"1190":"b:wuXPsAVYqMEUYAAC/9gBc969fasDAAAA","1263":"b:guXCMAUYiMAEIAAC/9AAc969fCoBAAAA","1273":[129],"1279":"b:guXCMAUYiMAEIAAC/9AAc9y9fCoBAAAA","1309":"b:wuXPsAVYqMEUYAAC/9gBc969fasDAAAA","1352":"b:wuXPsAVYqMEUYAAC/9gBc969fasDAAAA","1358":"b:wuXPsAVYqMEUYAAC/9gBc969fasDAAAA","1363":"b:wuXPsAVYqMEUYAAC/9gBc969fasDAAAA","1383":"b:guXCMAUYiMAEIAAC/9AAc969fCoBAAAA","1395":"b:wuXPsAVYqMEUYAAC/9gBc969fasDAAAA","1521":"b:giUAAAAAAAAAAAAAgNAAcBwVdAAAAAAA","1534":"b:gmXCMAUIiMAAAAAAstAAcly9fAoAAAAA","1555":[55,111,124],"1611":"b:gmXCMAUIiMAAAAAAstAAcly9fAoAAAAA","1616":[1,7,8,10,108,13],"1643":"b:AgAAAAAAAAAAAAAAgMAAcBwVdAAAAAAA","1694":"b:wuXPsAVYqMEUYAAC/9gBc969fasDAAAA","1703":[55,111,124],"1725":[55,111,124],"1762":[55,111,124],"1777":[111],"1782":[55,111,124],"1784":[55,111,124],"1790":[111],"1804":[111],"1816":[111],"1830":[111],"1837":[111],"1844":[55,111,124],"1851":[55,111,124],"1859":[111,124],"1868":[111,124],"1872":[55,111,124],"1884":[55,111,124],"1887":[55,111,124],"1890":[55,111,124],"1991":[111],"2038":[55,111,124],"2062":[55,111,124],"2109":"b:wuXPsAVYqMEEAAACu9gBcl29fYsAAAAA","2147":"b:wuXPsAVYqMEEAAACu9gBcl+9fYsAAAAA","2199":"b:wuXPsAVYqMEEAAACu9gBcl29fYsAAAAA","2209":[130,152],"2224":[107,124,125,128,130,6,152],"2290":"b:QAAIAAAAgAAAAAAAAIgAMAUAAAEAAAAA","2325":"b:QAAIAAAAgAAAAAAAAIgAMAUAAAEAAAAA","2339":[130,152],"2353":[1,89,103,125,152,161],"2370":[1,125,152,161],"2397":[1,152,161],"2401":[89,103,125,152,161],"2405":"b:wuXPsAVYqMEEAAACu9gBcl+9fYsCAAAA","2413":"b:wuXPsAVYqMEEAAACu9gBcl+9fYsCAAAA","2419":"b:wuXPsAVYqMEEAAACu9gBcl29fYsCAAAA","2426":"b:wuXPsAVYqMEEAAACu9gBcl29fYsCAAAA","2430":"b:wuXPsAVYqMEEAAACu9gBcl29fYsCAAAA","2435":"b:wuXPsAVYqMEEAAACu9gBcl29fYsCAAAA","2514":"b:wuXPsAVYqMEEAAACu9gBcl29fYsCAAAA","2545":"b:wuXPsAVYqMEEIAAC/9gBc969faoBAAAA","2572":"b:x/Xf8O3/+cHdYDwC//0B//////8DAAAA","2623":"b:R3AAQOC3WMBdADwAICQA6KHLw3EDAAAA","2660":"b:QMAFgABAIAEEAAAAAIgBQASARYAAAAAA","2677":"b:QMAFgABAIAEEAAAAAIgBQASARYAAAAAA","2695":"b:QMAFgABAIAEEAAAAAIgBQASARYAAAAAA","2712":"b:QMAFgABAIAEEAAAAAIgBQASARYAAAAAA","2715":"b:QMAFgABAIAEEAAAAAIgBQASARYAAAAAA","2758":"b:QMAFgABAIAEEAAAAAIgBQASARYAAAAAA","2880":"b:QMAFgABAIAEEAAAAAIgBQASARYAAAAAA","2914":"b:QMAFgABAIAEEAAAAAIgBQASARYAAAAAA","2929":"b:QMAFgABAIAEEAAAAAIgBQASARYAAAAAA","2936":"b:QMAFgABAIAEEAAAAAIgBQASARYAAAAAA","2946":"b:QMAFgABAIAEEAAAAAIgBQASARYAAAAAA","2947":"b:QMAFgABAIAEEAAAAAIgBQASARYAAAAAA","3071":"b:QMAFgABAIAEEAAAAAIgBQASARYAAAAAA","3101":[46,111,112,144,14,146,150,159],"3103":[159],"3106":[112,150,159],"3112":[112,150,159],"3121":[112,150,159],"3129":[112,150,159],"3130":[112,150,159],"3141":[159],"3164":[124,126,130,146,66,68],"3178":"b:QMAFgABAIAEEAAAAAIgBQASARYAAAAAA","3215":[124,126,130,146,66,68],"3227":[124,126,130,146,66,68],"3269":[68],"3274":[68],"3283":[124,126,130,146,66,68],"3304":[124,126,66,68],"3311":[124,126,66,68],"3312":[124,126,66,68],"3323":[126,130,146],"3330":[126,130,146],"3333":[124,66,68],"3345":[124,66,68],"3405":[1,7,2,109,123],"3420":[1,7,2,109,123],"3425":[78,68,161],"3522":"b:guXCMAUYiMAEIAAC/9AAc969fCoBAAAA","3548":"b:gmUAAAAQgAAEAAACG1AAAACAGAAAAAAA","3598":"b:x/Xf8O3/+MHdYDwC//0B//////8DAAAA","3599":"b:x/Xf8O3/+MHdYDwC//0B//////8DAAAA","3602":[78,68,161],"3610":[78,68,161],"3617":[78,68,161]},"packages/frontend/editor-ui/src/app/composables/useCollectionOverhaul.ts":{"5":"b:1vXPsI2Z+O1UACAy+/SJ8tz//0sBAAAA","8":"b:1vXHsAWZ+OlUACAy+/QJ8tz//0sBAAAA"},"packages/frontend/editor-ui/src/app/composables/useDataSchema.ts":{"31":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAC","35":"b:RNBJgACZsAEAAAAAAAQAoNj/t0EAAAAC","58":[185],"65":[185,44,53,127,6,139,141,142,14,145],"75":"b:RNBBgACZsAEAAAAAAAQAoNj/t0EAAAAC","100":"b:RNBJgACZsAEAAAAAAAQAoNj/t0EAAAAC","110":[185,137,149],"130":[185,137,149],"151":[185,137,149],"163":"b:RJDBgACZkAEAAAAA4AQAIMj+l0EAAAAC","179":"b:xvXPsAWZ+MFEACAC+/QB8t3//0sBAAAC","207":"b:xvXPsAWZ+MFEACAC+/QB8t3//0sBAAAC","216":"b:AEAAAAAAAAAAACAAAAAAgFB+BwEAAAAC","221":"b:AEAAAAAAAAAAACAAAAAAgFB+BwEAAAAC","228":[185,144],"236":"b:RNBBgACZsAEAAAAAAAQAoNj/t0EAAAAC","241":[185,144],"365":"b:RNBBgACZsAEAAAAAAAQAoNj/t0EAAAAC","367":"b:QJBBgACZMAEAAAAAAAQAIMiTt0AAAAAC","378":[185,137,149],"385":"b:RNBBgACZsAEAAAAAAAQAoNj/t0EAAAAC","394":"b:RNBBgACZsAEAAAAAAAQAoNj/t0EAAAAC","396":"b:RNBBgACZsAEAAAAAAAQAoNj/t0EAAAAC","399":[185,139,144],"407":"b:RNBBgACZsAEAAAAAAAQAoNj/t0EAAAAC","468":"b:RNBBgACZsAEAAAAAAAQAoNj/t0EAAAAC","509":"b:QNBBgACZsAEAAAAAAAQAoNj/t0EAAAAC","514":"b:QNBBgACZMAEAAAAAAAQAoNj/t0EAAAAC"},"packages/frontend/editor-ui/src/app/composables/useDebugInfo.ts":{"72":"b://Xf/P///+39//////////////8DAAAA","78":"b:x/Xf/P///8X9f4/D//+F8/////8DAAAA","115":"b:x/Xf/P///8X9f4/D//+F8/////8DAAAA","126":"b:x/Xf/P///8X9f4/D//+F8/////8DAAAA","136":"b:x/Xf/P///8X9f4/D//+F8/////8DAAAA","147":"b:x/Xf/P///8X9f4/D//+F8/////8DAAAA","154":"b:x/Xf/P///8X9f4/D//+F8/////8DAAAA","158":[130],"166":[130],"169":[130],"174":[130],"184":"b:x/Xf/P///8X9f4/D//+F8/////8DAAAA","201":"b:x/Xf/P///8X9f4/D//+F8/////8DAAAA","219":[130],"233":"b:x/Xf/P///8X9f4/D//+F8/////8DAAAA","237":"b:x/Xf/P///8X9f4/D//+F8/////8DAAAA"},"packages/frontend/editor-ui/src/app/composables/useDocumentVisibility.ts":{"17":"b:GGAABAoQH6xMFgNEIAMAYAGFRQEDAAAA","28":"b://Xf/P///+39f7////////////8DAAAA","36":"b://Xf/P///+39f7////////////8DAAAA","40":"b:AIAHACADCAAIAAAAAAAAACAAWAMAAAAA"},"packages/frontend/editor-ui/src/app/composables/useEditorContext.ts":{"27":"b:AIAfEAEBAAAAAAAAAAABcAyAAEAAAAAA","30":"b:x/Xf8A39+cHdYAQC//0B8/////8DAAAA","51":"b://Xf/P///+39//////////////8DAAAA","52":"b:x/Xf8A39+cHdYAQC//0B8/////8DAAAA","59":"b:x/Xf8O3/+cHdYAQC//0B8/////8DAAAA","60":"b:x/Xf8O3/+cHdYAQC//0B8/////8DAAAA","61":"b:R/HP8GX/+MFdYAAC/8wB8/3///sDAAAA","62":"b:x/Hf0OX/+cFdAAwA4CUA+/3/338DAAAA","65":"b:x/Hf0OX/+cFdAAwA4CUA+/3/338DAAAA"},"packages/frontend/editor-ui/src/app/composables/useErrorHandler.ts":{"26":"b://Xf/P///+39f7////////////8DAAAA"},"packages/frontend/editor-ui/src/app/composables/useExecutingNode.ts":{"19":"b://Xf/P///+39//////////////8DAAAC","25":"b:x5DFwGX/oAEBAAQA4CQAeMj/l3EAAAAC","37":"b:x5DFwGX/oAEBAAQA4CQAeMj/l3EAAAAC","45":"b:x/Xf8O3/+cHdYDwC//0B//////8DAAAC","51":"b:x/XP8OX/+MFdYAQC//wB//////sDAAAC"},"packages/frontend/editor-ui/src/app/composables/useExposeCssVar.ts":{"7":"b://Xf/P///+39//////////////8DAAAA","11":"b://Xf/P///+39//////////////8DAAAA","12":"b://Xf/P///+39//////////////8DAAAA"},"packages/frontend/editor-ui/src/app/composables/useExternalHooks.ts":{"16":"b://Xf/P///+39/7////////////8DAAAA","38":"b://Xf/P///+39/7////////////8DAAAA"},"packages/frontend/editor-ui/src/app/composables/useFloatingUiOffsets.ts":{"16":"b://Xf/P///+39//////////////8DAAAA","21":"b://Xf/P///+39//////////////8DAAAA","25":"b:xvXHsAWY6MFEACAC+/QBctz//wsBAAAA","26":"b://Xf/P///+39//////////////8DAAAA","29":"b://Xf/P///+39//////////////8DAAAA","30":"b://Xf/P///+39//////////////8DAAAA","39":"b://Xf/P///+39//////////////8DAAAA"},"packages/frontend/editor-ui/src/app/composables/useFreeAiCredits.ts":{"35":"b:/vXHsI+Z+MVUACA6+/SB8tz//0sBAAAA","37":[3,4,5,91,93,33],"53":"b:/vXHsI+Z+MVUACA6+/SB8tz//0sBAAAA"},"packages/frontend/editor-ui/src/app/composables/useGlobalEntityCreation.ts":{"28":"b:AGEIZBoIf8UEAIBCg7uBwMbnauIDAAAA","31":"b:AGEIZBoIf8UEAIBCg7uBwMbnauIDAAAA","76":"b:x/Xf/P///8X9f4/D//+F8/////8DAAAA","79":"b:x/Xf/P///8X9f4/D//+F8/////8DAAAA","83":"b:x/Xf/P///8X9f4/D//+F8/////8DAAAA","87":"b:x/Xf/P///8X9f4/D//+F8/////8DAAAA","94":"b:x/Xf/P///8X9f4/D//+F8/////8DAAAA","96":"b:x/Xf/P///8X9f4/D//+F8/////8DAAAA","103":"b:x/Xf/P///8X9f4/D//+F8/////8DAAAA","113":"b:x/Xf/P///8X9f4/D//+F8/////8DAAAA","117":[114,120,140,154],"146":"b:x/Xf/P///8X9f4/D//+F8/////8DAAAA","157":"b:AGEIZBoIf8UEAIBCg7uBwMbnauIDAAAA","169":"b:AGEIZBoIf8UEAIBCg7uBwMbnauIDAAAA","181":"b:x/Xf/P///8X9f4/D//+F8/////8DAAAA","203":"b:x/Xf/P///8X9f4/D//+F8/////8DAAAA","327":"b:AGEIZBoIf8UEAIBCg7uBwMbnauIDAAAA","361":"b:AGEIZBoIf8UEAIBCg7uBwMbnauIDAAAA","417":[26,94,105,51,63,66,160],"440":[26,94,105,51,63,150,66,160],"468":[27,34,51,66],"493":"b:x/Xf/P///8X9f4/D//+F8/////8DAAAA","494":"b:x/Xf/P///8X9f4/D//+F8/////8DAAAA","495":"b:x/Xf/P///8X9f4/D//+F8/////8DAAAA"},"packages/frontend/editor-ui/src/app/composables/useGlobalLinkActions.ts":{"14":"b:x/Xf8O3/+MHdYDQC//0B+/////8DAAAA","15":"b:x/Xf8O3/+MHdYDQC//0B+/////8DAAAA","18":"b:QgAAACADGMAdAAAAAAEAQuAAQHADAAAA","22":[14],"34":"b:x/Xf8AX/+cHdICQC//0B+/////8BAAAA","55":[14],"60":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAA","69":"b:QgAAACADGMAdAAAAAAEAQuAAQHADAAAA"},"packages/frontend/editor-ui/src/app/composables/useHistoryHelper.ts":{"18":"b://Xf/P///+39//////////////8DAAAA","24":"b:QuENsBBAooUcCAAACZuFeAfRRfkDAAAA","32":[130,152],"60":[130],"88":[130,152],"94":[130,152],"99":[130],"110":"b:QuENkBBAogEcCAAACZsFeAfRRfkDAAAA","117":"b:QuENsBBAooUcCAAACZuFeAfRRfkDAAAA","147":"b://Xf/P///+39//////////////8DAAAA"},"packages/frontend/editor-ui/src/app/composables/useInvalidNodeGroupCleanup.ts":{"24":"b://Xf/P///+39//////////////8DAAAA","31":[128],"50":[128],"51":[128],"77":"b:wuHfkAVIqAFEAAAA4CUAc92+X38DAAAA","81":[128]},"packages/frontend/editor-ui/src/app/composables/useKeybindings.ts":{"60":"b://Xf/P///8X9f7/r//+F//////8DAAAA","81":"b:AiAIEBAAgAAcAAAACZAAcAcBQHkDAAAA","83":"b:QuENsBBAooUcCAAACZuFeAfRRfkDAAAA","87":"b:AiAIEBAAgAAcAAAACZAAcAcBQHkDAAAA","89":"b:AiAIEBAAgAAcAAAACZAAcAcBQHkDAAAA","91":"b:AiAIEBAAgAAcAAAACZAAcAcBQHkDAAAA","96":"b:AiAIEBAAgAAcAAAACZAAcAcBQHkDAAAA","98":"b:AiAIEBAAgAAcAAAACZAAcAcBQHkDAAAA","99":"b:AiAIEBAAgAAcAAAACZAAcAcBQHkDAAAA","103":"b:AiAIEBAAgAAcAAAACZAAcAcBQHkDAAAA","109":"b:AiAIEBAAgAAcAAAACZAAcAcBQHkDAAAA","127":"b:AiAIEBAAgAAcAAAACZAAcAcBQHkDAAAA","145":"b:AiAIEBAAgAAcAAAACZAAcAcBQHkDAAAA","199":"b:QuENsBBAooUcCAAACZuFeAfRRfkDAAAA"},"packages/frontend/editor-ui/src/app/composables/useLayoutProps.ts":{"4":"b:x/Xf8A38+cHdYAwC//0B8/////8DAAAA","7":"b:x/Xf8A38+cHdYAwC//0B8/////8DAAAA"},"packages/frontend/editor-ui/src/app/composables/useNodeConnections.ts":{"12":"b:x/Xf8O3/+cHdYDQC//0B//////8DAAAC","25":"b:x/Xf8O3/+cHdYDQC//0B//////8DAAAC","26":"b:x/XP8OX/+MFVQDQCu/wB/v3///sCAAAC","29":"b:x/Xf8O3/+cHdYDQC//0B//////8DAAAC","30":"b:x/XP8OX/+MFVQDQCu/wB/v3///sCAAAC","33":"b:x/Xf8O3/+cHdYDQC//0B//////8DAAAC","34":"b:x3UAAAAAAAAAAAAAABAAEgAAAAAAAAAC","37":"b:x/XP8OX/+MFVQDQCu/wB/v3///sCAAAC","45":"b:x/Xf8O3/+cHdYDQC//0B//////8DAAAC","46":"b:x/XP8OX/+MFdYDQC//wB//3///sDAAAC","49":"b:x/Xf8O3/+cHdYDQC//0B//////8DAAAC","50":"b:x/XP8OX/+MFdYDQC//wB//3///sDAAAC","53":"b:x/XP8OX/+MFdYDQC//wB//3///sDAAAC","61":[185,125,128,152]},"packages/frontend/editor-ui/src/app/composables/useNodeDirtiness.ts":{"27":[55,125,126,6,137,152,156,157,158],"35":[55,125,126,6,137,152,156,157,158],"58":[6,152],"66":[55,125,126,6,137,152,156,157,158],"67":[55,125,126,6,137,152,156,157,158],"77":[156,157,158],"89":"b:QAABAAAAgAAAAAAAgAQAAEgCFAEAAAAA","138":"b://Xf/P///+39//////////////8DAAAA","141":"b:x5DFwGX/sAEBAAQA4CQAeMj/l3EAAAAA","144":"b:x/XP8OX/+MFdYDQC//wB//3///sDAAAA","148":"b:x5DFwGX/sAEBAAQA4CQAeMj/l3EAAAAA","152":[55,156,157,158],"156":"b:x5DFwGX/sAEBAAQA4CQAeMj/l3EAAAAA","158":"b:x5DFgGX/sAEBAAQAoCQAaMj/l3EAAAAA","159":[0,1,7,2,6],"162":"b:x5DFwGX/sAEBAAQA4CQAeMj/l3EAAAAA","179":"b:x5DFwGX/sAEBAAQA4CQAeMj/l3EAAAAA","215":[55],"218":[55],"258":"b:x/XP8OX/+MFdYDQC//wB//3///sDAAAA","262":"b:QAABAAAAgAAAAAAAgAQAAEgCFAEAAAAA","271":[55],"308":"b:x5DFgGX/sAEBAAQAoCQAaMj/l3EAAAAA","309":"b:x5DFgGX/sAEBAAQAoCQAaMj/l3EAAAAA"},"packages/frontend/editor-ui/src/app/composables/useNodeDocsUrl.ts":{"5":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAA","8":[100],"10":[8,100,144,13],"18":"b:xvXHsAWZ+MFEACAC+/QB8tz//0sBAAAA"},"packages/frontend/editor-ui/src/app/composables/useNodeExecution.ts":{"81":"b:xvXPsAWZ6MFEAAAC+/QB8tz//wsBAAAA","104":"b:hIABgAAIAAAAAAAAwCQAAAiqFwAAAAAA","112":"b:xvXPsAWZ6MFEAAAC+/QB8tz//wsBAAAA","116":"b:xvXPsAWZ6MFEAAAC+/QBctz//wsBAAAA","122":"b:xvXPsAWZ6MFEAAAC+/QB8tz//wsBAAAA","126":[23,101,103],"128":"b:xvXPsAWZ6MFEAAAC+/QBctz//wsBAAAA","130":"b:hIABgAAIAAAAAAAAwCQAAAiqFwAAAAAA","136":"b:xvXPsAWZ6MFEAAAC+/QBctz//wsAAAAA","138":"b:xvXPsAWZ6MFEAAAC+/QBctz//wsAAAAA","140":[15,23,101,102,103,146],"142":"b:xvXPsAWZ6MFEAAAC+/QBctz//wsBAAAA","144":"b:xvXPsAWZ6MFEAAAC+/QB8tz//wsBAAAA","154":"b:xvXPsAWZ6MFEAAAC+/QB8tz//wsBAAAA","167":"b:xvXPsAWZ6MFEAAAC+/QBctz//wsBAAAA","175":"b:xvXPsAWZ6MFEAAAC+/QB8tz//wsBAAAA","181":"b:gqGKEAAAQAAAAAAC4QAAIpT+bAoBAAAA","188":"b:xvXHsAWZ6MFEAAAC+/QB8tz//wsBAAAA","212":"b:xvXPsAWZ6MFEAAAC+/QBctz//wsBAAAA","240":"b:xvXPsAWZ6MFEAAAC+/QBctz//wsBAAAA","246":"b:xvXPsAWZ6MFEAAAC+/QB8tz//wsBAAAA","346":[7,2,109],"355":"b:hIABgAAIAAAAAAAAwCQAAAiqFwAAAAAA"},"packages/frontend/editor-ui/src/app/composables/useNodeHelpers.ts":{"82":"b:xvXPsIWZ+OlEACAi+/QJ8tz//0sBAAAA","112":"b:xvXPsAWZ+MFEACAC+/QB8tz//0sBAAAA","145":"b:xvXHsAWZ+MFEACAC+/QB8tz//0sBAAAA","158":"b:wkEAIAQACMBAAAAAoBAAAAAACAAAAAAA","159":"b:wkEAIAQACMBAAAAAoBAAAAAACAAAAAAA","160":"b:wkEAIAQACMBAAAAAoBAAAAAACAAAAAAA","164":"b:1vXP8I2/+O1UQDQy+/SJ+tz//8sDAAAA","184":[7,93,13],"245":"b:x7DFwGX/oAEBAAQg4CQAeMj/l3ECAAAA","261":[70,8,34,43],"275":"b:x/XP8OX/+MFdYAQC//wB8/////sDAAAA","295":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAA","303":"b:x/Xf8G3/+cHdYAQC//0B+/////8DAAAA","315":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAA","323":"b:AmHFsAUYCMAQAAACsAQBcFy+HAMAAAAA","331":"b:wuXPsAVYqMFUYAAC/9wBc96/ffsDAAAA","346":"b:AmHNsAUYCMAAAAACsAQBcFy+HAMAAAAA","354":"b:x/XP8OX/+MFdYCQC//wB8/////sDAAAA","381":"b:x/XP8OX/+MFdYAQi//wB8/////sDAAAA","394":"b:x/XP8OX/+MFVQAQCu/wB8v3///sCAAAA","430":"b:xnXO8ISueMBQQDQiu/QAWtSDrogDAAAA","431":"b:xnXO8ISueMBQQDQCu/QAWtSDrogDAAAA","440":"b:xnXO8ISueMBQQDQCu/QAWtSDrogDAAAA","458":"b:xnXO8ISueMBQQDQiu/QAWtSDrogDAAAA","492":"b:x/XP8OX/+MFdYDQi//wB+/////sDAAAA","600":"b:wkEIIACkCMAQAAAgoBAAAAAACAAAAAAA","655":[70,8,34,43],"663":[70,8],"674":[70,8],"677":"b:x/Xf8O3/+cHdYDQS//2B+/////8DAAAA","692":"b:xvXHsGWb+MFEACQC+/QB8tz//0sBAAAA","696":"b:xvXHsGWb+MFEACQC+/QB8tz//0sBAAAA","703":"b:xvXHsAWZ+MFEAAACu/QBctz//0sAAAAA","705":"b:QJBBgACZsAEAAAAAAAQAIMj+l0EAAAAA","711":"b:QNBBgACZsAEAAAAAAAQAoNj/t0EAAAAA","719":"b:QJBBgACZsAEAAAAAAAQAIMj/l0EAAAAA","725":"b:xvXHsGWb+MFEACQC+/QB8tz//0sBAAAA","751":"b:xpDBgGCbsAEAAAQA4CQAIMj/l0EAAAAA","759":"b:xvXHsGWb+MFEACQC+/QB8tz//0sBAAAA","790":"b:QgAAAAAAAAAAAAAAAAAAUgQCAHgAAAAA","797":"b:QgAAAAAAAAAAAAAAAAAAUgQCAHgAAAAA","839":"b:x/XP8OX/+MFdYDQC//wB+/////sDAAAA","843":[70,42,101,45,47,51,6,68],"859":[70,42,101,45,47,6,68],"894":"b:QMAFgABAIAEEAAAAAIgBQASARYAAAAAA","898":"b:QMAFgABAIAEEAAAAAIgBQASARYAAAAAA","901":"b:QMAFgABAIAEEAAAAAIgBQASARYAAAAAA","919":[130],"953":[2,44,139,143,145,146],"969":"b:xvXHsGWb+MFEACQC+/QB8tz//0sBAAAA","1038":"b:guXCMAUYiMAEIAAC/9AAc969fCoBAAAA"},"packages/frontend/editor-ui/src/app/composables/useNodeIconSource.ts":{"6":"b:x/Xf8O3/+MHdYDQi//0B//////8DAAAA","12":"b:xvXPsOWb+MFUYCQi//QB+9z//2sDAAAA"},"packages/frontend/editor-ui/src/app/composables/useNodeType.ts":{"8":"b:x/Xf8O3/+MHdYDQC//0B+/////8DAAAA","16":"b:xvXHsAWZ+MFEICAC//QB89z//2sBAAAA","29":"b:xvXHsAWZ+MFEICAC//QB89z//2sBAAAA","31":"b:xuXHsAWZ+MFEACAC+/QBctz//0sBAAAA"},"packages/frontend/editor-ui/src/app/composables/usePinnedData.ts":{"70":"b:xvXHsGWb+MFEACQC+/QB8tz//8sBAAAA","78":"b:xvXHsGWb+MFEACQC+/QB8tz//0sBAAAA","83":"b:xvXHsAWZ+MFEACAC+/QBctz//0sBAAAA","93":"b:xvXHsAWZ+MFEACAC+/QB8t3//0sBAAAA","104":"b:xvXHsAWZ+MFEACAC+/QB8tz//0sBAAAA","109":"b:xvXHsAWZ+MFEACAC+/QB8tz//0sBAAAA","171":"b:AEAAAAAAIAAAAAAAAAAAAFS8B4AAAAAA","175":"b:AEAAAAAAIAAAAAAAAAAAAFS8B4AAAAAA","225":"b:AEAAAAAAIAAAAAAAAAAAAFS8B4AAAAAA","244":[146],"266":"b:AEAAAAAAIAAAAAAAAAAAAFS8B4AAAAAA"},"packages/frontend/editor-ui/src/app/composables/usePostMessageHandler.ts":{"40":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAA","44":"b:x/Xf8A38+cHdYDwC//0B//////8DAAAA","66":"b:x/Xf8A38+cHdYDwC//0B//////8DAAAA","79":[48],"85":[84,85,48,123],"132":[48],"161":[48],"184":[48],"233":"b:x/Xf8A38+cHdYDwC//0B//////8DAAAA","296":"b:x/Xf8A38+cHdYDwC//0B//////8DAAAA","302":[48,133,150,67]},"packages/frontend/editor-ui/src/app/composables/usePushConnection/handlers/executionFinished.ts":{"62":"b:x5BFwGX/oQEBAAQA4CQAeMj/l3EAAAAA","205":"b:x5BFwGX/oQEBAAQA4CQAcMj/l3EAAAAA","211":"b:RgBEwGH3oQEBAAQAICAAYMDDh3EAAAAA","232":[48],"246":"b:x5BFwGX/oQEBAAQA4CQAeMj/l3EAAAAA","276":"b:x5BFwGX/oQEBAAQA4CQAcMj/l3EAAAAA","289":"b:x5BFwGX/oQEBAAQA4CQAcMj/l3EAAAAA","315":[48],"333":[48],"350":"b:woABAAQAAQAAAAAAAAAAAMCCADAAAAAA","433":"b:RxBEwGH/oQEBAAQA4CQAeMj/l3EAAAAA","454":"b:RxBEwGH/oQEBAAQA4CQAeMj/l3EAAAAA","524":"b:x5BFwGX/oQEBAAQA4CQAcMj/l3EAAAAA"},"packages/frontend/editor-ui/src/app/composables/usePushConnection/handlers/executionStarted.ts":{"13":"b:x5DFwGX/oQEBAAQA4CQAeMj/l3EAAAAA"},"packages/frontend/editor-ui/src/app/composables/usePushConnection/handlers/nodeExecuteAfter.ts":{"16":"b:x5DFwGX/oQEBAAQA4CQAeMj/l3EAAAAA","49":"b:x5DFwGX/oQEBAAQA4CQAeMj/l3EAAAAA","50":"b:x5DFwGX/oQEBAAQA4CQAeMj/l3EAAAAA"},"packages/frontend/editor-ui/src/app/composables/usePushConnection/handlers/nodeExecuteAfterData.ts":{"11":"b:x5DFwGX/oQEBAAQA4CQAeMj/l3EAAAAA"},"packages/frontend/editor-ui/src/app/composables/usePushConnection/handlers/nodeExecuteBefore.ts":{"9":"b:x5DFwGX/oQEBAAQA4CQAeMj/l3EAAAAA"},"packages/frontend/editor-ui/src/app/composables/usePushConnection/handlers/testWebhookReceived.ts":{"8":"b:xgCAQAAAAQAAAAQAoCAACIAABAAAAAAA"},"packages/frontend/editor-ui/src/app/composables/usePushConnection/handlers/trackNodeExecution.ts":{"11":"b:x5DFwGX/oQEBAAQA4CQAeMj/l3EAAAAA"},"packages/frontend/editor-ui/src/app/composables/usePushConnection/usePushConnection.ts":{"45":"b:x/Xf8O3/+cHdYDwC//1D//////8DAAAA","46":"b:x/Hf0OX/+cFdAAwA4CUA+/3/338DAAAA","51":[37,40,41,48,133,150,67],"60":"b:x/Hf0OX/+cFdAAwA4CUA+/3/338DAAAA"},"packages/frontend/editor-ui/src/app/composables/useResizablePanel.ts":{"51":[48],"61":[48],"62":[48],"63":"b://Xf8O//+cHdYAQq//0B+/////8DAAAA","92":[48],"96":"b://Xf8O//+cHdYAQq//0B+/////8DAAAA","100":[48],"104":"b://Xf8O//+cHdYAQq//0B+/////8DAAAA","108":[48],"125":[48],"138":"b://Xf8O//+cHdYAQq//0B+/////8DAAAA","139":"b://Xf8O//+cHdYAQq//0B+/////8DAAAA","145":"b://Xf8O//+cHdYAQq//0B+/////8DAAAA","153":"b:RgAAACADCAAJAAQIACAAQuAAQHAAAAAA","159":"b://Xf8O//+cHdYAQq//0B+/////8DAAAA","160":"b:xgAAAGADAQAAAAQAACAACAAAAEAAAAAA","161":[82,1,7,2,37,38,40,41,48,109,6,158]},"packages/frontend/editor-ui/src/app/composables/useResolvedExpression.ts":{"26":"b:1vXPsI2Z+O1UACAy+/SJ8tz//0sBAAAA","41":"b:1vXPsI2Z+O1UACAy+/SJ8tz//0sBAAAA","47":"b:1vXHsA2Z+O1UACAS+/SJ8tz//0sBAAAA","53":"b:1vXPsI2Z+O1UACAy+/SJ8tz//0sBAAAA","54":"b:QIAAAACAIAEAAAAAAAAAAICcBgAAAAAA","55":"b:wuUAAACAYAUAAAAAADAAEIC8HgAAAAAA","60":"b:1vXPsI2Z+O1UACAy+/SJ8tz//0sBAAAA","62":"b:wuUAAACAYAUAAAAAADAAEIC8HgAAAAAA","101":"b:1vXPsI2Z+O1UACAy+/SJ8tz//0sBAAAA","127":"b:1vXPsI2Z+O1UACAy+/SJ8tz//0sBAAAA","128":"b:1vXPsI2Z+O1UACAy+/SJ8tz//0sBAAAA","129":"b:1vXPsI2Z+O1UACAy+/SJ8tz//0sBAAAA"},"packages/frontend/editor-ui/src/app/composables/useRunWorkflow.ts":{"95":"b:x5DFwAX/oAEBAAQA4CQAeMj/l3ECAAAA","109":"b:QIABAAAAAAAAAAAAgAQAAIgCEwAAAAAA","113":"b:x5DFwAX/oAEBAAQA4CQAeMj/l3ECAAAA","128":"b:x5DFwAX/oAEBAAQA4CQAeMj/l3ECAAAA","160":"b:x5DFwAX/oAEBAAQA4CQAeMj/l3ECAAAA","306":"b:x5DFwAX/oAEBAAQA4CQAeMj/l3ECAAAA","309":"b:x5DFwAX/oAEBAAQAYCQAeMj/l3ECAAAA","341":[82,1,2,109,123,6],"352":[15,102,103,124,135,146],"376":[15,102,103,124,135,146],"419":[16,103,106,131,137,148],"472":[23,101],"512":"b:x5DFwAX/oAEBAAQA4CQAeMj/l3ECAAAA","563":[6],"610":[6],"638":"b:QgDEwAX3oAEBAAAAoAAAYMDDh3ECAAAA"},"packages/frontend/editor-ui/src/app/composables/useSelectionValidation.ts":{"26":"b://Xf/P///+39//////////////8DAAAA","38":"b:xvXHsAXZ6MFUIAQC//wB89////sBAAAA","55":"b:x/Xf8G3/+MHdYAQC//0B8/////8DAAAA","62":"b:wuQFgABAIAEUAAAAAIgBcAWARYEAAAAA","69":[128],"96":"b:x/Xf8G3/+cHdYAQC//0B8/////8DAAAA","98":"b:xvXHsAXZ6MFUIAQC//wB89////sBAAAA","102":"b:xvXHsAXZ6MFUIAQC//wB89////sBAAAA","110":"b:x/Xf8G3/+cHdYAQC//0B8/////8DAAAA","116":"b:xvXHsAXZ6MFUIAQC//wB89////sBAAAA","118":"b:xvXHsAXZ6MFUIAQC//wB89////sBAAAA","125":"b:xuXHsAWZ6MFUAAACu/QB8t3//3sAAAAA","127":"b:BmAAAAAB4AAAAAAAguAA8B1et3EAAAAA"},"packages/frontend/editor-ui/src/app/composables/useSettingsItems.ts":{"23":"b://Xf/P///+39f4//////8/////8DAAAA","203":"b://Xf/P///+39f4//////8/////8DAAAA","206":"b://Xf/P///+39f4//////8/////8DAAAA"},"packages/frontend/editor-ui/src/app/composables/useSidebarLayout.ts":{"11":"b://Xf/P///8X9f4/r//+F8/////8DAAAA","17":"b://Xf/P///8X9f4/r//+F8/////8DAAAA","27":[69,71,94,50,51,81,66]},"packages/frontend/editor-ui/src/app/composables/useStorage.ts":{"4":"b://Xf/P///+39//////////////8DAAAA"},"packages/frontend/editor-ui/src/app/composables/useTelemetry.ts":{"14":"b://Xf/P///+39//////////////8DAAAA"},"packages/frontend/editor-ui/src/app/composables/useTelemetryInitializer.ts":{"13":"b://Xf/P///+39//////////////8DAAAA","27":"b://Xf/P///+39//////////////8DAAAA","32":"b://Xf/P///+39//////////////8DAAAA","36":"b://Xf/P///+39//////////////8DAAAA","40":"b://Xf/P///+39//////////////8DAAAA","59":"b://Xf/P///+39//////////////8DAAAA","69":"b://Xf/P///+39//////////////8DAAAA"},"packages/frontend/editor-ui/src/app/composables/useUniqueNodeName.ts":{"4":"b://Xf/P///+39//////////////8DAAAA","11":"b:AgAAAAAAAAAEAAAAAAAAYB4ANAAAAAAA","12":"b:AgAAAAAAAAAEAAAAAAAAYB4ANAAAAAAA","27":"b:wuXPsAVYqMEUYAAC/9gBc969fasDAAAA","34":"b:AgAAAAAAAAAEAAAAAAAAYB4ANAAAAAAA"},"packages/frontend/editor-ui/src/app/composables/useUserHelpers.ts":{"5":"b://Xf/P///+39f4//////8/////8DAAAA","6":"b://Xf/P///+39f4//////8/////8DAAAA","12":"b://Xf/P///+39f4//////8/////8DAAAA"},"packages/frontend/editor-ui/src/app/composables/useWorkflowDiffRouting.ts":{"18":"b://Xf/P///+39//////////////8DAAAA","27":"b://Xf/P///+39//////////////8DAAAA","50":"b://Xf/P///+39//////////////8DAAAA","73":"b://Xf/P///+39//////////////8DAAAA","111":"b://Xf/P///+39//////////////8DAAAA","133":"b://Xf/P///+39//////////////8DAAAA"},"packages/frontend/editor-ui/src/app/composables/useWorkflowExtraction.ts":{"45":"b:x/Xf8O3/+MHdYDQC//0B+/////8DAAAA","94":[152],"112":[152],"114":[152],"145":[152],"146":[152],"172":[152],"183":[152],"191":[152],"192":[152],"198":[152],"199":[152],"253":[152],"278":[152],"306":[152],"311":[152],"312":[152],"319":[152],"337":[152],"341":[152],"343":[152],"350":[152],"379":[152],"382":[152],"416":[152],"435":[152],"454":[152],"459":[152],"475":[152],"478":[152],"479":[152],"513":[152],"567":[152],"574":[152],"577":[152],"582":[152],"591":[152],"592":[152],"598":[152],"599":[152],"614":[152],"643":[152],"644":[152],"652":[152],"659":[152],"671":[152],"691":[152]},"packages/frontend/editor-ui/src/app/composables/useWorkflowHelpers.ts":{"76":"b:wvWAEACQaAUAACAioTAAMJC+HgMBAAAC","177":"b:QJAAAACAIAEAAAAAAAAAAICcFgEAAAAC","280":[185,147,148],"287":[185],"294":[185],"311":"b:gkAAEAAACAAAAAAAAAAAAACAGAMAAAAC","321":"b:gkAAEAAACAAAAAAAAAAAAACAGAMAAAAC","347":"b:wsDFwAX3oAEFAAAAoIgBYMTDx/ECAAAC","353":"b:wvWAEACQaAEAACACoTAAMJC+HgMBAAAC","384":"b:QNAAAACAAAEAAAAAAAAAAJC8FgEAAAAC","400":[15,185,102,103,124,135,146],"426":"b:wvWAEACQaAEAACAC4TAAMJC+HgMBAAAC","515":"b://Xf/P///+39//////////////8DAAAC","529":"b:QMAFgABAIAEEAAAAAIgBQASARYAAAAAC","555":"b:wuVHsAVYqMEEIAACH9gBcdyIXSoAAAAC","569":"b:gAGAEAAAAAAAACACoQAAAICCDAABAAAC","590":"b:AAGAEAAAAAAAACACoQAAAICCDAABAAAC","631":[15,16,185],"634":[15,16,185],"665":"b:wvWAEACAYAUAACAioTAAEJC+HgABAAAC","695":[185],"742":"b:QMAFgABAIAEEAAAAAIgBQASARYAAAAAC","773":[185,48,124,126,130,146,66,68],"777":[185,124,126,130,146,66,68],"782":[185,68],"784":[185,68],"798":[185,48],"806":[185],"816":"b:R3AAQOC3WcBdADwAICQA6KHLw3EDAAAC","854":"b:R3AAQOC3WcBdADwAICQA6KHLw3EDAAAC","885":[185],"895":[185],"908":[185],"919":[185]},"packages/frontend/editor-ui/src/app/composables/useWorkflowId.ts":{"17":"b://Xf/P///+39//////////////8DAAAC","20":"b://Xf/P///+39//////////////8DAAAC","40":"b://Xf/P///+39//////////////8DAAAC"},"packages/frontend/editor-ui/src/app/composables/useWorkflowImport.ts":{"11":"b:x/Xf8A38+cHdYDwC//0B//////8DAAAA","17":[84,85,48,123],"19":[84,85,48,123]},"packages/frontend/editor-ui/src/app/composables/useWorkflowInitialization.ts":{"81":"b:x/Xf8O3/+cHdYDwC//0B//////8DAAAA","92":"b:x/Xf8O3/+cHdIAwC//0B8/////8DAAAA","93":"b:x/Xf8O3/+cHdIDwC//0B//////8DAAAA","94":"b:x/Xf8O3/+cHdYDwC//0B//////8DAAAA","95":[48,78,68,161],"96":"b:x/Xf8O3/+cHdYAwC//0B8/////8DAAAA","98":"b:x/Xf8O3/+cHdYAwC//0B8/////8DAAAA","123":"b:x/Xf8O3/+cHdYDwC//0B//////8DAAAA","188":"b:R3AAQOC3WcBdAAwAICQA4OHLw3EDAAAA","205":"b:x/Xf8O3/+cHdYDwC//0B//////8DAAAA","207":"b:x/Xf8O3/+cHdYDwC//0B//////8DAAAA","243":"b:R3AAQOC3WcBdAAwAICQA4KHLw3EDAAAA","298":"b:wuXf8A1YqcGMIDAC/9kBf968fa4BAAAA","313":"b:wuXf8A1YqcGMIDAC/9kBf968fa4BAAAA","344":"b:R3AAQOC3WcBdAAwAICQA4KHLw3EDAAAA","399":"b:x/Xf8O3/+cHdYDwC//0B//////8DAAAA","469":[37,41,48,133,150,67]},"packages/frontend/editor-ui/src/app/composables/useWorkflowNormalization.ts":{"22":"b://Xf/P///+39//////////////8DAAAA","26":"b:x/XP8OX/+MFdYDQC//wB+/////sDAAAA","46":"b:x/XP8OX/+MFdYDQC//wB+/////sDAAAA","58":"b:x/XP8OX/+MFdYDQC//wB+/////sDAAAA","76":"b:R3AAQOC3WMBdADwAICQA6OHLw3EDAAAA","81":"b:R3AAQOC3WIBdADQAICQA6OHLw3EDAAAA","82":"b:R3AAQOC3WIBdADQAICQA6OHLw3EDAAAA","83":"b:R3AAQOC3WIBdADQAICQA6OHLw3EDAAAA","85":"b:R3AAQOC3WIBdADQAICQA6OHLw3EDAAAA"},"packages/frontend/editor-ui/src/app/composables/useWorkflowPublicationStatusSync.ts":{"26":"b:x/Xf8O3/+cHdYAwC//0B8/////8DAAAA","35":[48],"38":[48],"41":"b:x/Xf8O3/+cHdYAwC//0B8/////8DAAAA","81":"b:x/Xf8O3/+cHdYAwC//0B8/////8DAAAA","82":[48,51,150,67],"88":"b:x/Xf8O3/+cHdYAwC//0B8/////8DAAAA","89":[48],"95":"b:x/Xf8O3/+cHdYAwC//0B8/////8DAAAA","96":[48],"97":[37,40,41,48,51,133,150,67]},"packages/frontend/editor-ui/src/app/composables/useWorkflowSaving.ts":{"45":"b://Xf/P///+39//////////////8DAAAA","73":[51,133,134,150,67,157,158,161],"77":[150],"149":"b:wuHfkAVIqAFEAAAA4CUAct2+X38DAAAA","156":"b:wuHfkAVIqAFEAAAA4CUAct2+X38DAAAA","186":"b:wuHfkAVIqAFEAAAA4CUAct2+X38DAAAA","373":"b:wuHfkAVIqQEEAAAA4AEAc9y0XS4DAAAA","577":"b:wuEPkAVAqAFEAAAAoCEAc92cT38BAAAA","591":"b:AmEOEAEACABEAAAAoAEAcl0UTG4BAAAA","610":"b:wuXPsAVYqMFUIAAC//0Bc9+/f/8BAAAA","636":"b:woDdgAVIoAEAAAAA4CQAc8i+FzEDAAAA","644":"b://Xf/P///+39//////////////8DAAAA"},"packages/frontend/editor-ui/src/app/composables/useWorkflowUpdate.ts":{"39":"b://Xf/P///+39f7////////////8DAAAA","58":[19],"60":[19],"63":[19],"70":[19],"93":[19],"106":[19],"183":[19],"192":[19],"196":[19],"211":[19],"236":[19],"248":[19],"249":[19],"253":[19],"255":[19],"279":[19],"313":[19],"325":[19],"340":[19],"348":[19],"367":[19]},"packages/frontend/editor-ui/src/app/composables/useWorkflowsCache.ts":{"27":"b:x/Hf0OX/+cFdAAwA4CUA8/3/338DAAAA","30":"b:x/Hf0OX/+cFdAAwA4CUA8/3/338DAAAA","35":"b:x/Hf0OX/+cFdAAwA4CUA8/3/338DAAAA","39":"b:x/Hf0OX/+cFdAAwA4CUA8/3/338DAAAA","46":"b:x/Hf0OX/+cFdAAwA4CUA8/3/338DAAAA","62":[47,48,121,67,155,160],"85":[47,48,121,67,155,160],"96":[48,121],"104":[48],"113":[48],"120":[48,121],"123":[48,121]},"packages/frontend/editor-ui/src/app/constants/actions.ts":{"1":"b://Xf/P///+39//////////////8DAAAA"},"packages/frontend/editor-ui/src/app/constants/experiments.ts":{"6":"b://Xf/P///+39//////////////8DAAAA"},"packages/frontend/editor-ui/src/app/constants/localStorage.ts":{"22":[3,4,5,91,93,33],"24":[3,4,5,91,93,33],"30":[3,4,5,91,93,33],"36":"b:x/Xf8O3/+MHdYAwC//0B8/////8DAAAA","38":"b:x/Xf8O3/+MHdYAwC//0B8/////8DAAAA","40":"b:x/Xf8O3/+MHdYAwC//0B8/////8DAAAA"},"packages/frontend/editor-ui/src/app/constants/navigation.ts":{"9":"b://Xf/P///+39//////////////8DAAAA"},"packages/frontend/editor-ui/src/app/constants/nodeTypes.ts":{"136":"b://Xf/P///+39//////////////8DAAAA"},"packages/frontend/editor-ui/src/app/constants/workflows.ts":{"16":"b://Xf/P///+39//////////////8DAAAA"},"packages/frontend/editor-ui/src/app/dev/render-tracker.ts":{"38":"b://Xf/P///+39//////////////8DAAAA","67":"b://Xf/P///+39//////////////8DAAAA"},"packages/frontend/editor-ui/src/app/init.ts":{"46":"b://Xf/P///+39//////////////8DAAAA","99":"b://Xf/P///+39/7////////////8DAAAA","119":[90,71,116,81],"140":"b:x/Xf/P///8X9f4/D//+l8/////8DAAAA","180":[71],"202":"b:3/Xf/P///+39/4//////8/////8DAAAA","247":"b://Xf/P///+39//////////////8DAAAA","260":"b://Xf/P///+39/8//////8/////8DAAAA","296":[51,81]},"packages/frontend/editor-ui/src/app/layouts/BaseLayout.vue":{"10":"b://Xf/P///+39//////////////8DAAAA"},"packages/frontend/editor-ui/src/app/layouts/WorkflowLayout.vue":{"48":"b:x/Xf8A38+cHdYAwC//0B8/////8DAAAA","57":[48,51,78,150,67,68,161],"70":[48,134],"78":[48,133,150,67],"87":"b:x/Xf8A38+cHdYAwC//0B8/////8DAAAA","89":"b:x/Xf8A38+cHdYAwC//0B8/////8DAAAA","91":"b:x/Xf8A38+cHdYAwC//0B8/////8DAAAA","101":"b:x/Xf8A38+cHdYAQC//0B8/////8DAAAA","104":"b:x/Xf8A38+cHdYAwC//0B8/////8DAAAA"},"packages/frontend/editor-ui/src/app/models/history.ts":{"8":"b://Xf/P///+39//////////////8DAAAA","38":"b:wuXPsAVYqMEEIAAC//gBc9+/ffsBAAAA","55":"b:wuXfsAVYqMEEIAAC//gBc9+/ffsBAAAA","60":"b:woDFgAVIoAEAAAAA4CAAcMi2FXEAAAAA","61":"b:woDFgAVIoAEAAAAA4CAAcMi2FXEAAAAA","72":"b:AAAIAAAAgAAEAAAAAOAAMAUAAAAAAAAA","84":[130],"88":[19,110,55,111,124,128],"99":[130],"100":[130],"113":"b:wuXPsAVYqMEEIAAC/9gBc969fasBAAAA","118":[130,152],"122":"b:wuXPsAVYqMEEAAACu9gBcly9fYsAAAAA","126":[130,152],"127":[130,152],"137":[107,124,125,128,130,6,152],"142":[130,152],"146":[125,130,152],"150":[130,152],"151":[130,152],"161":"b:wuXPsAVYqMEEAAACu9gBcl29fYsAAAAA","166":[130,152],"170":"b:wMAJAABAoAEAAAAAAogAMAWABYEAAAAA","180":[130,152],"181":[130,152],"191":"b:QAAIAAAAgAAAAAAAAIgAMAUAAAEAAAAA","196":[130,152],"200":"b:QAAIAAAAgAAAAAAAAIgAMAUAAAEAAAAA","210":[130,152],"211":[130,152],"212":[130,152],"227":"b:QgAAAAAAAAAAAAAAAAAAUgQCAHgAAAAA","234":[130],"238":[126,130],"244":[130],"245":[130],"260":[126],"290":[111,152],"299":[152],"308":[111],"317":[152],"318":[152],"329":[128],"346":[128],"372":[128],"381":[128],"400":[128],"410":[128]},"packages/frontend/editor-ui/src/app/moduleInitializer/moduleInitializer.ts":{"15":"b://Xf/P///+39/4//////8/////8DAAAA","16":"b://Xf/P///+39/4//////8/////8DAAAA","17":"b://Xf/P///+39/4//////8/////8DAAAA","26":"b://Xf/P///+39/4//////8/////8DAAAA","28":"b://Xf/P///+39/4//////8/////8DAAAA","46":"b://Xf/P///+39/4//////8/////8DAAAA","48":"b://Xf/P///+39/4//////8/////8DAAAA","59":"b:AAAAAPADAAAAHwDAAAAAAAAAAAAAAAAA","86":"b://Xf/P///+39/4//////8/////8DAAAA","87":"b://Xf/P///+39/4//////8/////8DAAAA","88":"b://Xf/P///+39/4//////8/////8DAAAA","99":"b://Xf/P///+39//////////////8DAAAA","100":"b://Xf/P///+39//////////////8DAAAA","101":"b://Xf/P///+39//////////////8DAAAA"},"packages/frontend/editor-ui/src/app/plugins/cache.ts":{"3":"b:x/Hf0OX/+cFdAAwA4CUA8/3/338DAAAA","8":"b:x/Hf0OX/+cFdAAwA4CUA8/3/338DAAAA","9":"b:x/Hf0OX/+cFdAAwA4CUA8/3/338DAAAA","10":"b:x/Hf0OX/+cFdAAwA4CUA8/3/338DAAAA","13":"b:x/Hf0OX/+cFdAAwA4CUA8/3/338DAAAA","25":[48],"33":"b:x/Hf0OX/+cFdAAwA4CUA8/3/338DAAAA","34":"b:x/Hf0OX/+cFdAAwA4CUA8/3/338DAAAA","36":"b:x/Hf0OX/+cFdAAwA4CUA8/3/338DAAAA","39":"b:x/Hf0OX/+cFdAAwA4CUA8/3/338DAAAA","40":[48],"44":[47,48,121,67,155,160],"49":"b:x/Hf0OX/+cFdAAwA4CUA8/3/338DAAAA","53":[48],"58":[48],"63":[48],"67":[48],"68":[48],"71":[48],"82":[48],"89":"b:x/Hf0OX/+cFdAAwA4CUA8/3/338DAAAA","99":"b:x/Hf0OX/+cFdAAwA4CUA8/3/338DAAAA","100":"b:x/Hf0OX/+cFdAAwA4CUA8/3/338DAAAA","104":[48],"111":[47,48,121,67,155,160],"112":[47,48,121,67,155,160],"117":[48],"118":[48],"123":[48],"124":[48]},"packages/frontend/editor-ui/src/app/plugins/chartjs.ts":{"15":"b://Xf/P///+39//////////////8DAAAA"},"packages/frontend/editor-ui/src/app/plugins/components.ts":{"10":"b://Xf/P///+39//////////////8DAAAA"},"packages/frontend/editor-ui/src/app/plugins/directives.ts":{"5":"b://Xf/P///+39//////////////8DAAAA"},"packages/frontend/editor-ui/src/app/plugins/sentry.ts":{"33":[79],"36":[79],"60":"b://Xf/P///+39//////////////8DAAAA"},"packages/frontend/editor-ui/src/app/plugins/telemetry/index.ts":{"28":"b://Xf/P///+39//////////////8DAAAA","40":"b://Xf/P///+39/7////////////8DAAAA","44":"b://Xf/P///+39//////////////8DAAAA","79":"b://Xf/P///+39/4//////8/////8DAAAA","113":"b://Xf8P///+39fw77///f+/////8DAAAA","143":"b://Xf/P///+39f7////////////8DAAAA","173":[51,81],"185":[131],"215":"b:AmHFsAUYCMAAAAACsAQBcFy+HAMAAAAA","303":"b://Xf/P///+39//////////////8DAAAA"},"packages/frontend/editor-ui/src/app/plugins/vitePreloadError.ts":{"4":"b://Xf/P///+39//////////////8DAAAA"},"packages/frontend/editor-ui/src/app/push-connection/useEventSourceClient.ts":{"13":"b://Xf/P///+39//////////////8DAAAA"},"packages/frontend/editor-ui/src/app/push-connection/useHeartbeat.ts":{"12":"b://Xf/P///+39//////////////8DAAAA","17":"b://Xf/P///+39//////////////8DAAAA","21":[91,133]},"packages/frontend/editor-ui/src/app/push-connection/useReconnectTimer.ts":{"14":"b://Xf/P///+39//////////////8DAAAA","33":[91,133],"40":"b://Xf8O//+MHdfwyq//1D+/////8DAAAA"},"packages/frontend/editor-ui/src/app/push-connection/useWebSocketClient.ts":{"24":"b://Xf/P///+39//////////////8DAAAA","35":[0,93,39,40,51],"40":"b://Xf8O//+MHdfwyq//1D+/////8DAAAA","53":"b://Hf0Of/+MFdAAwo4CUA+/3/338DAAAA","61":"b://Xf8O//+cHdfwyq//1D+/////8DAAAA","75":"b://Xf8O//+MHdfwyq//1D+/////8DAAAA","94":"b:x/Hf0OX/+cFdAAwA4CUA8/3/338DAAAA"},"packages/frontend/editor-ui/src/app/router.ts":{"40":"b:x/Xf8A38+cHdYDQC//0B+/////8DAAAA","41":[1,52,126,134,135,64,6,156,157,158],"42":[1,52,126,134,135,64,6,156,157,158],"43":[1,126,134,135,64,6,156,157,158],"44":[1,126,134,135,64,6,156,157,158],"47":[52,126,134,135,64,6,156,157,158],"48":[52,126,134,135,64,6,156,157,158],"51":[90,116,81],"52":[90,116,81],"53":[117],"54":[117],"56":[113],"57":[113],"58":[71],"59":[71],"60":[59,115,61],"61":[59,115,61],"63":[87,88,51,81],"65":[68],"66":[68],"67":[161],"68":[161],"69":[68],"70":[68],"71":[161],"72":[161],"78":[51,81],"80":[80],"81":[80],"82":[58],"87":[58],"96":[118],"97":[118],"98":[133],"99":[133],"100":[161],"101":[121],"102":[121],"105":[83],"106":[83],"107":[83,121],"108":[83,121],"115":[52],"116":[52],"176":"b:IAAATPADDABgHYDAAAIAAAAAQAABAAAA","255":[78,68],"284":[161],"289":[161],"454":"b:wuXf8A1YqMGcYAAC/9kBc968fa4DAAAA","474":[84,85,123],"485":[122],"491":[122],"601":[86],"620":"b://Xf/P///+39f4//////8/////8DAAAA","742":[15,16,17,18,19,20],"796":"b:3/Xf9P///+39f4//////8/////8DAAAA","1090":"b:3/Xf9P///+39f4//////8/////8DAAAA","1166":"b://Xf/P///+39//////////////8DAAAA","1181":"b://Xf/P///+39f7////////////8DAAAA","1191":"b://Xf/P///+39//////////////8DAAAA","1223":[87,88,117],"1255":"b://Xf/P///+39f7////////////8DAAAA"},"packages/frontend/editor-ui/src/app/stores/aiGateway.store.ts":{"16":[185],"22":[185],"25":"b://Xf/P///+39f6//////8/////8DAAAC","35":[185],"45":[185],"56":[185],"67":[185],"78":[185],"82":[185],"100":[185],"115":[185],"123":"b:xvXPsIWZ+OlEACAi+/QJ8tz//0sBAAAC","125":"b:wkEIIAQACMBAAAAgoBAAAAAACAAAAAAC","128":"b:xvXPsIWZ+OlEACAi+/QJ8tz//0sBAAAC","145":"b:AFGCMAAAaMAAAAAAuwAAQhCUPwgAAAAC","159":[185]},"packages/frontend/editor-ui/src/app/stores/backendConnection.store.ts":{"7":"b://Xf/P///+39//////////////8DAAAA","10":"b://Xf/P///+39//////////////8DAAAA"},"packages/frontend/editor-ui/src/app/stores/canvas.store.ts":{"6":"b://Xf/P///+39//////////////8DAAAA","13":"b:RQAAECACAAAAAAQAgCAA8AOAAQEAAAAA","20":"b:x/Xf8O3/+MHdYDQC//0B+/////8DAAAA","27":"b:x/Xf8O3/+MHdYAQC//0B+/////8DAAAA","28":"b:x/Xf8O3/+MHdYAQC//0B+/////8DAAAA"},"packages/frontend/editor-ui/src/app/stores/executionData.store.ts":{"59":"b:x/Xf8O3/+cHdYDwC//0B//////8DAAAC","66":"b:x/Xf8O3/+cHdYDwC//0B//////8DAAAC","75":[185,157,158],"85":"b:x/Xf8O3/+cHdYDwC//0B//////8DAAAC","86":"b:x/Xf8O3/+cHdYDwC//0B//////8DAAAC","95":"b:x/Xf8O3/+cHdYDwC//0B//////8DAAAC","99":"b:QJDBgACZoAEAAAAA4AQAIMj+lwEAAAAC","101":"b:x5BFwGX/sAEBAAQA4CQAeMj/l3EAAAAC","116":"b:x5DFwGX/sAEBAAQA4CQAeMj/l3EAAAAC","133":"b:x5DFwGX/sAEBAAQA4CQAeMj/l3EAAAAC","136":"b:x5DFwGX/sAEBAAQA4CQAeMj/l3EAAAAC","139":"b:x5DFwGX/sAEBAAQA4CQAeMj/l3EAAAAC","142":"b:QIABAAAAAAAAAAAAgAQAQIgCE2AAAAAC","145":"b:QIABAAAAAAAAAAAAgAQAQIgCE2AAAAAC","151":"b:x/Xf8O3/+cHdYDwC//0B//////8DAAAC","159":"b:x/Xf8O3/+cHdYDwC//0B//////8DAAAC","190":"b:x/Xf8O3/+cHdYDwC//0B//////8DAAAC","197":"b:x5DFwGX/sAEBAAQA4CQAeMj/l3ECAAAC","201":"b:x/Xf8O3/+cHdYDwC//0B//////8DAAAC","216":"b:x5DFwGX/sAEBAAQA4CQAeMj/l3ECAAAC","229":"b:x5DFwGX/sAEBAAQA4CQAeMj/l3ECAAAC","236":"b:x5DFwGX/sAEBAAQA4CQAeMj/l3ECAAAC","272":"b:x5DFwGX/sAEBAAQA4CQAeMj/l3ECAAAC","275":"b:x5DFwGX/sAEBAAQA4CQAeMj/l3ECAAAC","278":"b:x5DFwGX/sAEBAAQA4CQAeMj/l3ECAAAC","287":"b:x5DFwGX/sAEBAAQA4CQAeMj/l3ECAAAC","291":"b:x5DFwGX/sAEBAAQA4CQAeMj/l3ECAAAC","294":[185,152],"297":[185,152],"305":"b:x/Xf8O3/+cHdYDwC//0B//////8DAAAC","313":"b:x/Xf8O3/+cHdYDwC//0B//////8DAAAC","329":"b:x/Xf8O3/+cHdYDwC//0B//////8DAAAC","339":"b:x5DFwGX3sAEBAAQAoCQAYMjfl3EAAAAC","430":"b:x5DFwGX/sAEBAAQA4CQAeMj/l3ECAAAC","450":"b:x5DFwGX/oAEBAAQA4CQAeMj/l3EAAAAC","458":[185],"472":"b:x5BFwGX/oAEBAAQA4CQAcMj/l3EAAAAC","476":"b:x5DFwGX/sAEBAAQA4CQAeMj/l3ECAAAC","488":"b:x5DFwGX/oAEBAAQA4CQAeMj/l3EAAAAC","496":"b:x5DFwGX/oAEBAAQA4CQAeMj/l3EAAAAC","511":[185,6],"516":"b:x5DFwGX/oAEBAAQA4CQAeMj/l3EAAAAC","535":[2,185,22,101,47,6,144,145,151,152],"540":[2,185,22,101,47,6,144,145,151,152],"559":"b:x5DFwGX/oAEBAAQA4CQAeMj/l3EAAAAC","563":[185],"577":"b:x5DFwGX/oAEBAAQA4CQAeMj/l3EAAAAC","580":"b:x5DFwGX/oAEBAAQA4CQAeMj/l3EAAAAC","588":[185,158],"595":[185,126],"615":[185],"616":[185],"620":[185],"632":[185,126],"633":[185,126],"641":[185,126],"685":[185,6],"693":[185,6],"705":[185],"754":"b:x/Xf8O3/+cHdYDwC//0B//////8DAAAC"},"packages/frontend/editor-ui/src/app/stores/favorites.store.ts":{"9":"b://Xf/P///+39//////////////8DAAAA","36":"b://Xf/P///+39/4//////8/////8DAAAA","46":"b:x/Xf9P///8X9YAxC//8B8/////8DAAAA","52":[26,36,94,49,105,51,63,66,160],"86":[67,160],"92":[51,81]},"packages/frontend/editor-ui/src/app/stores/focusPanel.store.ts":{"47":"b://Xf/P///+39//////////////8DAAAA","54":"b://Xf/P///+39//////////////8DAAAA","64":"b://Xf/P///+39//////////////8DAAAA","71":"b:x/Xf8O3/+MHdYDQC//0B+/////8DAAAA","72":[112,127],"73":"b://Xf/P///+39//////////////8DAAAA","76":"b://Xf/P///+39//////////////8DAAAA","98":"b://Xf/P///+39//////////////8DAAAA","104":[48,112,127],"139":"b:wuHfkAVIqQEEAAAA4AEAc9y0XS4DAAAA","175":[112,127],"193":[112,127],"204":"b://Xf/P///+39//////////////8DAAAA","205":[112,127],"217":"b://Xf/P///+39//////////////8DAAAA"},"packages/frontend/editor-ui/src/app/stores/history.store.ts":{"10":"b://Xf/P///+39//////////////8DAAAA","17":[130,152],"24":"b:wuXPsAVYqMEEIAAC//gBc9+/ffsBAAAA","28":"b:wuXPsAVYqMEEAAACu9gBcl29fYsAAAAA","41":[130],"48":"b:wuXPsAVYqMEEIAAC//gBc9+/ffsBAAAA","53":[130,152],"58":"b:x/Xf8O3/+MHdIDwC//0B//////8DAAAA","61":"b:x/Xf8O3/+MHdIDwC//0B//////8DAAAA","64":"b:x/Xf8O3/+MHdIDwC//0B//////8DAAAA","68":[130],"74":[130,152],"78":"b:wuXfsAVYqMEEIAAC//gBc9+/ffsBAAAA","81":"b:wuXfsAVYqMEEIAAC//gBc9+/ffsBAAAA"},"packages/frontend/editor-ui/src/app/stores/logs.store.ts":{"19":"b://Xf/P///+39//////////////8DAAAA","22":"b:x/Xf8O3/+cHdYAQC//0B+/////8DAAAA","50":"b:x/Xf8O3/+MHdYAQC//0B+/////8DAAAA","54":"b://Xf/P///+39//////////////8DAAAA","58":[82,1,2,51,123],"62":[82,1,2,51,123],"66":[82,1,7,2,37,38,40,41,109,123,6,158],"74":[82,1,7,2,37,38,40,41,109,6,158],"78":[6],"124":[82,1,7,2,109,123,6],"130":"b:x/Xf8O3/+MHdYAQC//0B+/////8DAAAA","131":[82,1,7,2,37,38,40,41,109,6,158],"134":"b://Xf/P///+39//////////////8DAAAA","135":"b:x/Xf8O3/+MHdYAQC//0B+/////8DAAAA","136":[82,1,7,2,8,10,108,109,123,6,13],"137":[82,1,7,2,109,123,6]},"packages/frontend/editor-ui/src/app/stores/nodeTypes.store.ts":{"43":"b://Xf/P///+39//////////////8DAAAC","60":"b:guXCMAUYiMAEIAAC/9AAc9y9fSoDAAAC","61":"b:guXCMAUYiMAEIAAC/9AAc9y9fSoDAAAC","79":"b:x/Xf8G3/+cHdYAQi//0D8/////8DAAAC","81":"b:h/Xf8A3Y+cHdYAQC//0D8/////8DAAAC","82":"b:h/Xf8A3Y+cHdYAQC//0D8/////8DAAAC","85":"b:guXCMAUYiMAEIAAC/9AAc9y9fSoBAAAC","87":"b:guGCMAEYiMAEIAAC/9AAc9y9fSoBAAAC","88":"b:guGCMAEYiMAEIAAC/9AAc9y9fSoBAAAC","91":"b:guXCMAUYiMAEIAAC/9AAc9y9fSoBAAAC","95":"b:x/Xf8O3/+cHdYDwC//0B//////8DAAAC","96":"b:x/Xf8O37+cHdYAwC//0B8/////8DAAAC","97":"b:x/Xf8O37+cHdYAwC//0B8/////8DAAAC","101":"b:x/Xf8G3/+cHdYAQi//0D8/////8DAAAC","103":"b:x/Xf8G3/+cHdYAQi//0D8/////8DAAAC","111":"b:x/Xf8G3/+cHdYAQi//0D8/////8DAAAC","112":"b:x/Xf8G3/+cHdYAQi//0D8/////8DAAAC","128":"b:1/Xf8O3/+e3dYDQy//2J//////8DAAAC","129":"b:1/XL8OX++O1dYDQy//yJ//////sDAAAC","144":"b:xvXHsAWZ+MFEACAC+/QB8tz//0sBAAAC","145":"b:xvXDsAWY+MFEACAC+/QB8tz//0sBAAAC","150":[185,43],"151":[185,43],"162":"b:x/XP8OX/+MFdYDQC//wB//3///sDAAAC","163":"b:x/XL8OX++MFdYDQC//wB//3///sDAAAC","175":"b:x/XP8OX/+MFdYDQC//wB//3///sDAAAC","180":"b:x/XP8OX/+MFdYDQC//wB//////sDAAAC","181":"b:x/XL8OX++MFdYDQC//wB//////sDAAAC","187":"b:x/XPsAWZ+MFEACAC+/QB8tz//0sBAAAC","188":"b:x/XLsAWY+MFEACAC+/QB8tz//0sBAAAC","191":"b:x/XPsAWZ+MFEAAAC+/QB8tz//0sAAAAC","203":"b:xuXHsAWZ+MFEACAC+/QB8tz//0sBAAAC","204":"b:xuXDsAWY+MFEACAC+/QB8tz//0sBAAAC","207":"b:xuXHsAWZ+MFEAAAC+/QB8tz//0sAAAAC","219":[185],"220":[185],"225":"b:x/Xf8G3/+cHdYAQi//0D8/////8DAAAC","229":"b:x/Xf8G3/+cHdYAQi//0D8/////8DAAAC","232":[185],"233":[185],"239":[1,7,185,93,10,13,14],"240":[1,7,185,93,10,13,14],"244":[1,7,185,93,10,13,14],"265":[1,7,185,93,10,13,14],"281":[185],"282":[185],"286":[185],"305":"b:x/XP8OX/+MFdYDQC//wB//3///sDAAAC","306":"b:x/XL8OX++MFdYDQC//wB//3///sDAAAC","320":"b:x/XP8OX/+MFVQDQCu/wB/v3///sCAAAC","331":"b://Xf/P///+39f7////////////8DAAAC","339":[185],"340":[185],"348":[185],"352":[185],"375":[185],"386":[185],"398":[185],"412":[185],"414":[185],"422":"b://Xf/P///+39//////////////8DAAAC","433":"b://Xf/P///+39/8//////9/////8DAAAC","439":[185],"447":[185,28,143,14],"451":[1,7,185,147],"455":[185,147,148],"463":[185,51,152,153],"471":[185],"475":"b:x/Xf8O3/+cHdYDwi//0D//////8DAAAC","485":"b:h/Xf8A3Y+cHdYDwC//0D//////8DAAAC","492":[185,100,113],"507":"b:x/XP8OX/+MFdYDQC//wB+/////sDAAAC","508":"b:x/XL8OX++MFdYDQC//wB+/////sDAAAC","517":"b://Xf/P///+39//////////////8DAAAC","520":[185],"521":"b:x/XN0GX/+AEFACQC4bwBfPz/3/MDAAAC"},"packages/frontend/editor-ui/src/app/stores/npsSurvey.store.ts":{"18":"b://Xf/P///+39//////////////8DAAAA","27":"b://Xf/P///+39/4//////8/////8DAAAA","35":"b:wmGAABgIEGBIAAAApueoYp9mwvsDAAAA","75":[51,81],"79":"b:AmEOEAEACABEAAAAoAEAcl0UTA4BAAAA"},"packages/frontend/editor-ui/src/app/stores/posthog.store.ts":{"18":"b://Xf/P///+39//////////////8DAAAA","33":[51,81],"48":[51,81],"56":"b://Xf/P///+39f7////////////8DAAAA","60":"b:x/Xf/P///8X9f4/b//+F8/////8DAAAA","67":"b://Xf/P///+39f7////////////8DAAAA","73":[83],"159":"b://Xf/P///+39/4//////8/////8DAAAA"},"packages/frontend/editor-ui/src/app/stores/pushConnection.store.ts":{"18":"b://Xf/P///+39//////////////8DAAAA","36":"b://Xf8O//+MHdfzyq//1D//////8DAAAA","39":[91,37,41,51,133,150,67],"47":"b://Xf/P///+39//////////////8DAAAA","49":"b://Xf/P///+39//////////////8DAAAA","67":"b://Hf0Of/+cFdAAwo4CUA+/3/338DAAAA","81":"b://Hf0Of/+cFdAAwo4CUA+/3/338DAAAA","86":"b://Xf/P///+39//////////////8DAAAA","88":"b://Xf/P///+39//////////////8DAAAA","94":"b:x/Hf0OX/+cFdAAwA4CUA8/3/338DAAAA","106":"b://Xf8O//+MHdfwyq//1D+/////8DAAAA","126":"b://Xf8O//+cHdfwyq//1D+/////8DAAAA","133":"b://Xf8O//+cHdfQyq//0D+/////8DAAAA","149":[91,133,150,67],"161":[91],"174":"b://Xf/P///+39//////////////8DAAAA","175":"b://Xf8O//+cHdfwyq//1D+/////8DAAAA","196":[37,40,41,51,133,150,67],"200":"b://Xf8O//+MHdfwyq//1B+/////8DAAAA"},"packages/frontend/editor-ui/src/app/stores/ui.store.ts":{"121":"b://Xf/P///+39//////////////8DAAAA","182":"b://Xf/P///+39//////////////8DAAAA","294":"b:AGAADAgQH4RMFgNAIAMAYAGFRQEDAAAA","295":"b://Xf/P///+39//////////////8DAAAA","356":"b://XP8O///MVdYDQ6//yB//3///sDAAAA","360":"b://XP8O///MVdYDQ6//yB//3///sDAAAA","364":[114,120,160],"422":"b://Xf/P///+39//////////////8DAAAA","423":"b://Xf/P///+39//////////////8DAAAA","429":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAA","431":"b://Xf/P///+39f4//////8/////8DAAAA","433":"b://Xf/P///+39f4//////8/////8DAAAA","435":"b://Xf/P///+39f4//////8/////8DAAAA","441":"b:R3AAQOC3WcBdAAwAICQB6KfLx3EDAAAA","449":"b:x/Xf8O3/+cHdYCwC//0B8/////8DAAAA","450":"b:wuHfkAVIqQFEAAAA4CUAc92+X38DAAAA","456":"b:guXCMAUYiMAEIAAC/9AAc9y9fSoBAAAA","461":"b:QuENsBDAooUcCAAACZuFegfRRfkDAAAA","478":"b:kkECIAwACIQQAAAQEBCCAAAACAAAAAAA","485":"b:kkECIAwACoQQAAAQEBCCAAAACAAAAAAA","492":"b:kkECIAwACIQQAAAAEBCAAAAACAAAAAAA","499":"b:IIECIAyAD+wIAAA4AACsAwAAAAkBAAAA","506":"b:ssECJByAH+wYQAJ4EBCuAwQACI0BAAAA","514":"b:IIECIAyAD+wIAAA4AACsAwAAAAkBAAAA","519":"b:8sEaIDyDH+QdAAJ4ABGmQ+QASP0DAAAA","524":"b:ssECIByAH6QYAAJ4ABCmAwQACI0BAAAA","532":[92,8,34],"554":"b:kkECIAwACIQQAAAAEBCAAAAACAAAAAAA","589":[113],"595":[113],"605":[49],"614":[49,50,51],"631":"b:wuHfkAVIqQFEAAAA4CUAc92+X38DAAAA","637":"b:wuHfkAVIqQFEAAAA4CUAc92+X38DAAAA","644":[69,71,94,50,51,81,66],"651":"b:x/Xf8O3/+cHdYDwC//0B//////8DAAAA","658":"b://Xf/P///+39/4//////8/////8DAAAA","669":"b://Xf/P///+39/4//////8/////8DAAAA","677":"b:x5BFwGX/oAEBAAQA4CQAeMj/l3EAAAAA","681":"b:wuXPsAVYqOlUYAAC//0Jc9+/f/8DAAAA","689":"b:x/Xf8O3/+endYDwC//0J//////8DAAAA","697":"b://Xf/P///+39/4//////8/////8DAAAA","719":"b://Xf/P///+39/4//////8/////8DAAAA","720":"b://Xf/P///+39/4//////8/////8DAAAA","726":"b://Xf/P///+39/4//////8/////8DAAAA","728":"b://Xf/P///+39/4//////8/////8DAAAA","752":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAA","753":"b://Xf/P///+39//////////////8DAAAA","754":[30,47,52,121,67,155,160],"768":[90,116,81],"806":"b:IAECIAwADIQQQAAQAACAAAAAAAAAAAAA","814":"b:IAECIAwADIQQAAAQAACAAAAAAAAAAAAA","816":"b:IAECIAwADIQQAAAQAACAAAAAAAAAAAAA"},"packages/frontend/editor-ui/src/app/stores/ui.utils.ts":{"4":[85,122],"12":"b://Xf/P///+39//////////////8DAAAA","17":"b://Xf/P///+39//////////////8DAAAA","23":"b://Xf/P///+39//////////////8DAAAA"},"packages/frontend/editor-ui/src/app/stores/versions.store.ts":{"32":"b://Xf/P///+39//////////////8DAAAA","71":"b://Xf/P///+39/4//////8/////8DAAAA","81":"b:x/Xf/P///8X9f4/D//+F8/////8DAAAA","112":"b://Xf/P///+39/4//////8/////8DAAAA","116":[26],"132":"b:x/Xf/P///8X9f4/D//+F8/////8DAAAA","142":"b://Xf/P///+39/4//////8/////8DAAAA","157":"b://Xf/P///+39/4//////8/////8DAAAA","158":"b://Xf/P///+39/4//////8/////8DAAAA","159":"b://Xf/P///+39/4//////8/////8DAAAA","209":"b://Xf/P///+39/4//////8/////8DAAAA","251":"b://Xf/P///+39/4//////8/////8DAAAA","255":"b://Xf/P///+39/4//////8/////8DAAAA","278":[26]},"packages/frontend/editor-ui/src/app/stores/workflowDocument/useWorkflowDocumentActive.ts":{"14":"b://Xf/P///+39//////////////8DAAAC","17":"b:x/Hf0GX/uAFNAAQC4CUA+93///8DAAAC","21":"b:x/Hf0OX/+cFdADwA4CUA//3/338DAAAC","30":"b:x/Hf0OX/+cFdADwA4CUA//3/338DAAAC","34":"b:R3AAQOC3WcBdAAwAICQA6KHLw3EDAAAC"},"packages/frontend/editor-ui/src/app/stores/workflowDocument/useWorkflowDocumentChecksum.ts":{"12":"b://Xf/P///+39//////////////8DAAAC","17":"b:x/Hf0OX/+cFdADwA4CUA//3/338DAAAC","22":"b:x/Hf0OX/+cFdADwA4CUA//3/338DAAAC"},"packages/frontend/editor-ui/src/app/stores/workflowDocument/useWorkflowDocumentConnections.ts":{"33":"b://Xf/P///+39//////////////8DAAAC","44":"b:R3AIQOC3WcBdQDwAICQA7OHLw3MDAAAC","49":"b:wuXPsAVYqMEEAAACu9gBcl29fYsAAAAC","110":"b:QAAIAAAAgAAAAAAAAIgAMAUAAAEAAAAC","140":[185,107,124,125,128,130,6,152],"182":[185,61],"191":"b:x/Xf8O3/+cXdYDQi//0B//////8DAAAC","193":"b:x/XP0OX/+IFdQAQC4TwA+/3///sDAAAC","197":"b:x/XP8OX/+MFdQCQC+/wB8/3///sDAAAC","204":"b:x/XP0OX/+IFdQAQC4TwA+/3///sDAAAC","215":"b:R3AIQOC3WcBdQDwAICQA7OHLw3MDAAAC","219":"b:wuXPsAVYqMEEAAACu9gBcl29fYsAAAAC","223":"b:QAAIAAAAgAAAAAAAAIgAMAUAAAEAAAAC","227":[185,107,124,125,128,130,6,152],"234":[185],"240":[185,61]},"packages/frontend/editor-ui/src/app/stores/workflowDocument/useWorkflowDocumentDescription.ts":{"12":"b://Xf/P///+39//////////////8DAAAC","17":"b:R3AAQOC3WcBdADwAICQA7OHLw3EDAAAC","25":"b:R3AAQOC3WcBdADwAICQA7OHLw3EDAAAC"},"packages/frontend/editor-ui/src/app/stores/workflowDocument/useWorkflowDocumentExpression.ts":{"6":"b://Xf/P///+39//////////////8DAAAC","11":"b:x/Xf8O3/+cXdYDQi//0B//////8DAAAC"},"packages/frontend/editor-ui/src/app/stores/workflowDocument/useWorkflowDocumentGraph.ts":{"16":"b://Xf/P///+39//////////////8DAAAC","21":"b:x/XPsIXZ+OlUICQi//wJ89////sBAAAC","29":"b:x/XHsAWZ+MFEACAC+/QB8tz//0sBAAAC","37":"b:xvXPsAXZ+MFUYCAC//wB897//+sDAAAC","41":[185,152],"48":[185,55,111,124],"74":[185],"78":[7,2,185,109],"82":"b:wvWAEACQaAEAACACoTAAMJC+HgMBAAAC","86":"b:wvWAEACQaAEAACACoTAAMJC+HgMBAAAC","94":"b:xvXHsAUY6MBEAAACu/QB8ly//woAAAAC","109":"b:xZABgAAJAAAAAAAAwCQAEIi+FwAAAAAC","112":"b:xZABgAAJAAAAAAAAACQAAIi+FwAAAAAC","120":"b:xvXPsIWZ+OlEACAi+/QJ8tz//0sBAAAC","123":"b:xuXPsAWZ+MFEAAACu/QB8tz//0sAAAAC","132":"b:RZABgAAJAAAAAAAAwAQAEIi+FwAAAAAC","134":[185]},"packages/frontend/editor-ui/src/app/stores/workflowDocument/useWorkflowDocumentHomeProject.ts":{"9":"b://Xf/P///+39//////////////8DAAAC","14":"b:x/Xf8O3/+cHdIDwC//0B//////8DAAAC","22":"b:x/Xf8O3/+cHdIDwC//0B//////8DAAAC"},"packages/frontend/editor-ui/src/app/stores/workflowDocument/useWorkflowDocumentIsArchived.ts":{"12":"b://Xf/P///+39//////////////8DAAAC","17":"b:R3AAQOC3WcBdADwAICQA7OHLw3EDAAAC","22":"b:R3AAQOC3WcBdADwAICQA7OHLw3EDAAAC"},"packages/frontend/editor-ui/src/app/stores/workflowDocument/useWorkflowDocumentMeta.ts":{"13":"b://Xf/P///+39//////////////8DAAAC","18":"b:x3EAYOC3WcBdQDwAoDQA7uHLy3kDAAAC","26":"b:R3AAQOC3WcBdADwAICQA7OHLw3EDAAAC","30":"b:wkEAIAAACMAQQAAAgBAAAgAACAgCAAAC"},"packages/frontend/editor-ui/src/app/stores/workflowDocument/useWorkflowDocumentName.ts":{"16":"b://Xf/P///+39//////////////8DAAAC","21":"b:x/Xf8O3/+cHdYDwC//0B//////8DAAAC","27":"b:x/Xf8O3/+cHdYDwC//0B//////8DAAAC"},"packages/frontend/editor-ui/src/app/stores/workflowDocument/useWorkflowDocumentNodeGroups.ts":{"41":"b://Xf/P///+39//////////////8DAAAC","48":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAC","50":"b:xvXPsAXZ+MFcICQC//wB89////sBAAAC","60":"b:R3AAQOC3WcBdADwAICQA7OHLw3EDAAAC","61":[185,128],"68":[185,128],"84":[185,128],"93":"b:R3AAQOC3WcBdADwAICQA7OHLw3EDAAAC","97":[185,128],"113":[185,128],"114":[185],"132":[185,128],"136":[185,128],"142":[185],"150":[185,128],"156":[185],"161":[185,128],"166":[185,128],"172":[185],"181":[185],"188":[185,128],"192":"b:xvXPsAXZ+MFcICQC//wB89////sBAAAC","197":"b:QAAAAAAAACgAAAAAAAgIMAUAAAEAAAAC","200":[185,128],"209":"b:AAAAACADCCAIADAAAAAACCAAQAAAAAAC"},"packages/frontend/editor-ui/src/app/stores/workflowDocument/useWorkflowDocumentNodeMetadata.ts":{"20":"b://Xf/P///+39//////////////8DAAAC","29":"b:R3AIQOC3WeBdADwgICQA7OHLw3MDAAAC","34":"b:x/XP8OX/+OldYDQi//wJ//////sDAAAC","42":"b:xvXPsAXZ+MFUYCAC//wB897//+sDAAAC","52":[185,134],"65":"b:QAAAAAAAACgAAAAAAAgIMAUAAAEAAAAC","75":[185,126,14,153],"89":"b:R3AIQOC3WeBdADwgICQA7OHLw3MDAAAC","94":"b:wuXPsAVYqOkUYAAC/9gJc969fasDAAAC","100":"b:R3AIQOC3WIBdADQgICQA7OHLw3MDAAAC","105":"b:QAAAAAAAACgAAAAAAAgIMAUAAAEAAAAC","109":[185,126,14,153],"113":"b:xvXPsAXZ+MFUYCAC//wB897//+sDAAAC","117":"b:AkHNkAUYCAAAAAACs4QBcFy+HAMAAAAC","124":[185],"131":[185],"138":[185,134],"142":"b:x5DFwGX/sAEBAAQA4CQAeMj/l3EAAAAC","146":"b:x5DFgGX/sAEBAAQAoCQAaMj/l3EAAAAC","150":"b:x5DFwGX/sAEBAAQA4CQAeMj/l3EAAAAC","154":"b:xvXHsAWZ+MFEACAC+/QB8tz//0sBAAAC"},"packages/frontend/editor-ui/src/app/stores/workflowDocument/useWorkflowDocumentNodes.ts":{"76":"b://Xf/P///+39//////////////8DAAAC","92":"b:wvXPsIUY6OhUQAACu/QJct2+PvsCAAAC","116":"b:R3AIQOC3WcBdADwgICQA7OHLw3MDAAAC","143":"b:R3AIQOC3WIBdADQgICQA7OHLw3MDAAAC","147":"b:wuXPsAVYqOkUYAAC/9gJc969fasDAAAC","167":[185,59,115,61],"168":[185,59,115,61],"182":[185,107,124,125,128,130,6,152],"183":[185,107,124,125,128,130,6,152],"184":[185,107,124,125,128,130,6,152],"203":"b://Xf/P///+39//////////////8DAAAC","227":"b:x/XP8OX/+MFdYDQC//wB//////sDAAAC","242":"b:x/XP8OX/+MFdYDQC//wB//////sDAAAC","256":"b:x/XP8OX/+MFdYDQC//wB//////sDAAAC","270":"b:x/XP8OX/+OldYDQi//wJ//////sDAAAC","273":"b:x/XP8OX/+OldYDQi//wJ//////sDAAAC","276":"b:x/XP8OX/+MFdYDQC//wB//////sDAAAC","280":"b:x/XP8OX/+MFdYDQC//wB//////sDAAAC","283":"b:QAAAAAAAACgAABAAAAgIMAUAAAEAAAAC","286":"b:QAAAAAAAACgAABAAAAgIMAUAAAEAAAAC","293":"b://Xf/P///+39//////////////8DAAAC","301":"b://Xf/P///+39//////////////8DAAAC","302":"b:R3AIQOC3WIBdADQgICQA7OHLw3MDAAAC","303":"b:R3AIQOC3WIBdADQgICQA7OHLw3MDAAAC","309":"b:x/XP8OX/+eldYDwi//wJ//////sDAAAC","343":"b:x/XP8OX/+eldYDwi//wJ//////sDAAAC","369":[185],"371":"b:wuXPsAVYqMEUYAAC/9gBc969fasDAAAC","373":"b:x/Xf8O3/+cHdYDwi//0B//////8DAAAC","374":"b:x/XP8OX/+MFdYDQC//wB+/////sDAAAC","380":"b:guXCMAUYiMAEIAAC/9AAc969fSoBAAAC","381":"b:guXCMAUIiMAAIAAAttAAcly9fQoAAAAC","388":"b:x/Xf8O3/+cHdYDQC//0B//////8DAAAC","392":"b://Xf/P///+39//////////////8DAAAC","396":"b:xvXHsAWY6MFEAAAC+/QBctz//wsBAAAC","397":"b:xvXHsAWY6MFEAAAC+/QBctz//wsBAAAC","400":"b:QgAAAAAAAAAUAAAAAAAAUgQCBHgAAAAC","408":"b:R3AIQOC3WcBdADwgICQA7OHLw3MDAAAC","412":"b:wuXPsAVYqOkUYAAC/9gJc969fasDAAAC","416":[185,59,115,61],"420":[185,107,124,125,128,130,6,152],"424":"b:gmHHsAUYCMAAAAACu4QBcly+PAsAAAAC","425":"b:gmHHsAUYCMAAAAACu4QBcly+PAsAAAAC","450":"b:gEGCMAEACMAAAAAAqwAAchyUPAoAAAAC","451":"b:gEGCMAEACMAAAAAAqwAAchyUPAoAAAAC","469":"b:AAAIAAAAgAAEAAAAAOAAMAUAAAAAAAAC","470":"b:AAAIAAAAgAAEAAAAAOAAMAUAAAAAAAAC","493":"b:AAAIAAAAgAAEAAAAAOAAMAUAAAAAAAAC","494":"b:AAAIAAAAgAAEAAAAAOAAMAUAAAAAAAAC","500":[185],"501":[185],"506":"b:wkEIIAQACOhQAAAAgBAIUgQCCHgAAAAC","507":"b:wkEIIAQACOhQAAAAgBAIUgQCCHgAAAAC","523":"b:x/XP8OX/+MFdYDQC//wB+/////sDAAAC","524":"b:x/XP8OX/+MFdYDQC//wB+/////sDAAAC","551":[185,61],"561":[185,134],"562":[185,134],"569":[185],"574":[185],"604":"b:gkEAIAQACABAAAAgABAAAAAACAAAAAAC","611":"b:gkEAIAQACABAAAAgABAAAAAACAAAAAAC","630":[1,7,185,8,108]},"packages/frontend/editor-ui/src/app/stores/workflowDocument/useWorkflowDocumentNodesIssues.ts":{"23":"b://Xf/P///+39//////////////8DAAAC","24":"b:x/Xf0OX/+cFdAAwA4TUA8/3///8DAAAC","25":"b:x/XP0OX/+IFdAAQA4TQA8/3///sDAAAC","35":[185],"37":[185],"41":"b:xuWKUACAUAAYQAQC4TAAooS+bIgDAAAC","42":"b:xuWKUACAUAAYQAQC4TAAooS+bIgDAAAC","54":[19,185,20],"57":[185],"60":[19,185],"69":[19,185],"73":[19,185],"95":[185],"108":"b:x/XP8OX/+MFdYDQC//wB//////sDAAAC","114":"b:x/XP8OX/+OldYDQi//wJ//////sDAAAC","117":"b:x/XP8OX/+OldYDQi//wJ//////sDAAAC","120":"b:x/XP8OX/+MFdYDQC//wB//////sDAAAC","123":"b:QAAAAAAAACgAABAAAAgIMAUAAAEAAAAC","126":"b:QAAAAAAAACgAABAAAAgIMAUAAAEAAAAC","132":"b://Xf/P///+39//////////////8DAAAC","140":"b:x/XP8OX/+eldYDwi//wJ//////sDAAAC"},"packages/frontend/editor-ui/src/app/stores/workflowDocument/useWorkflowDocumentParentFolder.ts":{"9":"b://Xf/P///+39//////////////8DAAAC","14":"b:x/Xf8O3/+cHdIDwC//0B//////8DAAAC","22":"b:x/Xf8O3/+cHdIDwC//0B//////8DAAAC"},"packages/frontend/editor-ui/src/app/stores/workflowDocument/useWorkflowDocumentPinData.ts":{"38":"b:AEAAAAAAIAAAAAAAAAAAAFS8B4AAAAAC","43":"b:AEAAAAAAIAAAAAAAAAAAAFS8B4AAAAAC","56":"b:R3AAQOC3WeBdADwAICQA7OHLw3EDAAAC","57":"b:AEAAAAAAAAAAACAAAAAAgIDKAwECAAAC","58":"b:AEAAAAAAAAAAACAAAAAAgIDKAwECAAAC","68":"b:xvXHsGWb+MFUACQC+/QB8tz//8sBAAAC","72":"b:AEAAAAAAIAAAACAAAAAAgFT+B4EAAAAC","76":"b:AEAAAAAAIAAAAAAAAAAAAFS8B4AAAAAC","79":"b:AEAAAAAAIAAAAAAAAAAAAFS8B4AAAAAC","89":"b://Xf/P///+39//////////////8DAAAC","94":"b:R3AAQOC3WeBdADwAICQA7OHLw3EDAAAC","99":"b:AEAAAAAAIAAAAAAAAAAAAFS8B4AAAAAC","107":[185,130,134],"116":[185,126,14,153],"122":[185],"123":[185],"129":[185],"140":"b:R3AAQOC3WeBdADwAICQA7OHLw3EDAAAC","144":"b:AEAAAAAAIAAAAAAAAAAAAFS8B4AAAAAC","148":[185,134],"152":[185,126,14,153],"156":"b:x/Xf8OX/+MVdYDQi//0B//////8DAAAC","160":"b:xvXPsAWZ+MFEACAC+/QB8t3//0sBAAAC","179":"b:x/XP8OX/+MFdYDQC//wB//////sDAAAC","185":"b:x/XP8OX/+OldYDQi//wJ//////sDAAAC","188":"b:x/XP8OX/+OldYDQi//wJ//////sDAAAC","191":"b:x/XP8OX/+MFdYDQC//wB//////sDAAAC","194":"b:QAAAAAAAACgAABAAAAgIMAUAAAEAAAAC","197":"b:QAAAAAAAACgAABAAAAgIMAUAAAEAAAAC","203":"b://Xf/P///+39//////////////8DAAAC","211":"b:x/XP8OX/+eldYDwi//wJ//////sDAAAC"},"packages/frontend/editor-ui/src/app/stores/workflowDocument/useWorkflowDocumentPublicationStatus.ts":{"6":"b://Xf/P///+39//////////////8DAAAC","10":"b:AAAAACADCAAIADAAAAAACCAAQAAAAAAC"},"packages/frontend/editor-ui/src/app/stores/workflowDocument/useWorkflowDocumentRenderData.ts":{"106":"b:x/XP8OX/+MFdYDQC//wB//////sDAAAC","112":"b:x/XP8OX/+MFdYDQC//wB//////sDAAAC","122":"b:x/XP8OX/+MFdYDQC//wB//3///sDAAAC","128":"b:x/XP8OX/+MFdYDQC//wB//////sDAAAC","149":"b:x/XP8OX/+MFdYDQC//wB//3///sDAAAC","170":"b:x/XP8OX/+MFdYDQC//wB//3///sDAAAC","221":"b:x/XP8OX/+MFdYDQC//wB//////sDAAAC","243":"b:x/XP8OX/+MFdYDQC//wB//////sDAAAC","255":[185,53,111,128,129,135,151,159,161],"267":[185],"283":[185],"287":[185],"291":"b:x/XP8OX/+MFdYDQC//wB//3///sDAAAC","325":"b:x/XP8OX/+MFdYDQC//wB//////sDAAAC","346":"b:x/XP8OX/+MFdYDQC//wB//////sDAAAC","349":"b:x/XP8OX/+MFdYDQC//wB//////sDAAAC","352":"b:x/XP8OX/+MFdYDQC//wB//3///sDAAAC","356":"b:x/XP8OX/+MFdYDQC//wB//3///sDAAAC","360":"b:x/XP8OX/+MFdYDQC//wB//////sDAAAC","364":"b:x/XP8OX/+MFdYDQC//wB//3///sDAAAC","368":"b:x/XP8OX/+MFdYDQC//wB//3///sDAAAC","372":"b:x/XP8OX/+MFdYDQC//wB//////sDAAAC","383":"b:x/XP8OX/+MFdYDQC//wB//3///sDAAAC","390":"b:x/XP8OX/+MFdYDQC//wB//////sDAAAC","393":"b:QgAAACADCAAJABAAAAgAcuUAQHECAAAC","396":[185,84,107,124,125,128,130,6,152],"409":"b:x/Xf8O3/+cHdYDQC//0B//////8DAAAC","419":"b:wvXPsIVY+MFcIDAC//wBe9+/f/sDAAAC","452":"b:QgAAACADCAAJAAAAAAAASuAAQHACAAAC","464":"b:x/Xf8O3/+cHdYDQC//0B//////8DAAAC","485":[185,111,128,129,151],"486":[185,53,111,128,129,135,151,159,161],"520":"b:x/Xf8O3/+cHdYDQC//0B//////8DAAAC","523":"b:x/Xf8O3/+cHdYDQC//0B//////8DAAAC","526":"b:x/XP8OX/+MFdYDQC//wB//3///sDAAAC","529":"b:x/XP8OX/6MFVQDQCu/wB/v3///sCAAAC","532":"b:x/Xf8O3/+cHdYDQC//0B//////8DAAAC","551":"b:x/Xf8O3/+cHdYDQC//0B//////8DAAAC","554":"b:x5BFwGX/sAEBAAQA4CQAeMj/l3EAAAAC","557":"b:x/XP8OX/+MFdYDQC//wB//3///sDAAAC","560":"b:x/Xf8O3/+cHdYDQC//0B//////8DAAAC","563":"b:x/Xf8O3/+cHdYDQC//0B//////8DAAAC","566":"b:x/Xf8O3/+cHdYDQC//0B//////8DAAAC","569":"b:x/Xf8O3/+cHdYDQC//0B//////8DAAAC","572":"b:x/Xf8O3/+cHdYDQC//0B//////8DAAAC","575":"b:x/Xf8O3/+cHdYDQC//0B//////8DAAAC"},"packages/frontend/editor-ui/src/app/stores/workflowDocument/useWorkflowDocumentScopes.ts":{"13":"b://Xf/P///+39//////////////8DAAAC","18":"b:x/Xf8O3/+cHdIDwC//0B//////8DAAAC","23":"b:x/Xf8O3/+cHdIDwC//0B//////8DAAAC"},"packages/frontend/editor-ui/src/app/stores/workflowDocument/useWorkflowDocumentSettings.ts":{"19":"b://Xf/P///+39//////////////8DAAAC","24":"b:R3AAQOC3WcBdADwAICQA7OHLw3EDAAAC","33":"b:R3AAQOC3WcBdADwAICQA7OHLw3EDAAAC","37":[185],"41":"b:x/Hf0GX/uAFFAAQA4CUA+93///8DAAAC"},"packages/frontend/editor-ui/src/app/stores/workflowDocument/useWorkflowDocumentSharedWithProjects.ts":{"13":"b://Xf/P///+39//////////////8DAAAC","18":"b:R3AAQOC3WcBdADwAICQA7eHLw3EDAAAC","29":"b:R3AAQOC3WcBdADwAICQA7eHLw3EDAAAC"},"packages/frontend/editor-ui/src/app/stores/workflowDocument/useWorkflowDocumentTags.ts":{"15":"b:R3AAQOD3WcBdADwAIKQB7OHLx/UDAAAC","16":[185,112,150,154,159],"19":"b://Xf/P///+39//////////////8DAAAC","24":"b:R3AAQOD3WcBdADwAIKQB7OHLx/UDAAAC","29":[185,154],"30":[185],"37":"b:R3AAQOC3WcBdADwAICQA7OHLw3UDAAAC","41":[185,46,111,112,144,14,146,150,159],"46":[185,154]},"packages/frontend/editor-ui/src/app/stores/workflowDocument/useWorkflowDocumentTimestamps.ts":{"19":"b://Xf/P///+39//////////////8DAAAC","26":"b:R3AAQOC3WcBdADwAICQA7OHLw3EDAAAC","31":"b:x/Hf0OX/+cFdADwA4CUA//3/338DAAAC","36":"b:R3AAQOC3WcBdADwAICQA7OHLw3EDAAAC","40":"b:x/Hf0OX/+cFdADwA4CUA//3/338DAAAC"},"packages/frontend/editor-ui/src/app/stores/workflowDocument/useWorkflowDocumentUsedCredentials.ts":{"13":"b://Xf/P///+39//////////////8DAAAC","18":"b:R3AAQOC3WcBdADwAICQA7OHLw3EDAAAC","26":"b:R3AAQOC3WcBdADwAICQA7OHLw3EDAAAC","27":[185,70,42,101,45,47,51,6,68]},"packages/frontend/editor-ui/src/app/stores/workflowDocument/useWorkflowDocumentVersionData.ts":{"14":"b://Xf/P///+39//////////////8DAAAC","20":"b:x/Hf0OX/+cFdADwA4CUA//3/338DAAAC","32":"b:x/Hf0OX/+cFdADwA4CUA//3/338DAAAC"},"packages/frontend/editor-ui/src/app/stores/workflowDocument/useWorkflowDocumentViewport.ts":{"13":"b://Xf/P///+39//////////////8DAAAC","18":"b:QgAAACADCMAdADAAAAEASuAAQHADAAAC","26":"b:QgAAACADCMAdADAAAAEASuAAQHADAAAC"},"packages/frontend/editor-ui/src/app/stores/workflowDocument/useWorkflowDocumentWorkflowObject.ts":{"12":"b://Xf/P///+39//////////////8DAAAC","26":"b:QAAAACADGAAJADAAAAAADOAAQHACAAAC","46":"b:x/XP8OX/+eldYDwi//wJ//////sDAAAC","50":"b:x/XP8OX/+eFdQDwCu/wB/v3///sDAAAC","54":"b:x/Xf8O3/+cHdYDwC//0B//////8DAAAC","58":"b:R3AAQOC3WcBdADwAICQA7OHLw3EDAAAC","62":"b:QMANgABAIAEEAAAAAIgBQASARYIAAAAC","81":[19,185,126,14,153],"87":[19,185,126,14,153]},"packages/frontend/editor-ui/src/app/stores/workflowDocument.store.ts":{"115":"b://Xf/P///+39//////////////8DAAAC","140":[185,52,134,135,64,6,156,157,158],"149":[185,52,134,135,64,6,156,157,158],"162":"b://Xf/P///+39//////////////8DAAAC","176":"b://Xf/P///+39//////////////8DAAAC","177":"b://Xf/P///+39//////////////8DAAAC","186":"b:x/Xf8O3/+cHdYDwC//0B//////8DAAAC","200":"b:R3AAQOC3WcBdADwAICQA7OHLw3EDAAAC","209":"b:gEGCMAEACMAAAAAAqwAAchyUPAoAAAAC","212":"b:x/XP8OX/+eldYDwi//wJ//////sDAAAC","223":[185],"224":"b:x/XP8OX/+eFdQDwCu/wB/v3///sDAAAC","250":"b:wuXPsAVYqOlUYAAC//wJc9+/ffsDAAAC","251":"b:wuXPsAVYqMEEAAACu9gBcl29fYsAAAAC","252":[185,128],"254":"b:x/XP8OX/+eldYDwi//wJ//////sDAAAC","260":[185,61],"267":"b:x/Hf0AX/qAFFAAQA4CUAe93/338DAAAC","268":"b:x/HP0AX/qAFFAAQA4CQAe93/33sDAAAC","288":[185,128],"301":"b:QAAAAAAAEAABAAAAAAAABOAAAHACAAAC","353":"b:AAAAACADCAAIADAAAAAACCAAQAAAAAAC","394":"b:x/XP8OX/+MVdYDQi//wB//////sDAAAC","409":"b:x9BdwGX/oAEBAAQA4CQAcNz/l/EAAAAC","489":"b:AAAAACADCAAIAAAgAAAAAOAAQGACAAAC","505":"b://Xf/P///+39//////////////8DAAAC","507":"b://Xf/P///+39//////////////8DAAAC","516":"b://Xf/P///+39//////////////8DAAAC","533":"b://Xf/P///+39f7////////////8DAAAC","537":"b://Xf/P///+39f7////////////8DAAAC"},"packages/frontend/editor-ui/src/app/stores/workflowExecutionState.store.ts":{"86":"b://Xf/P///+39//////////////8DAAAC","102":"b://Xf/P///+39//////////////8DAAAC","103":"b://Xf/P///+39//////////////8DAAAC","163":"b:x/Xf8O3/+cHdYDwC//0B//////8DAAAC","176":"b:x5DFwGX/sAEBADQA4CQAfMj/l3ECAAAC","200":"b:1/Xf8O3/+e3dYDQy//2J//////8DAAAC","227":"b://Xf/P///+39//////////////8DAAAC","234":"b:x/XP8OX/+MFdYDQC//wB//////sDAAAC","241":"b:1/XP8O3/+O1dYDQy//yJ//3///sDAAAC","251":"b:xvXPsAWZ6MFEAAAC+/QB8tz//wsBAAAC","257":"b:x/Xf8O3/+cHdYAQC//0B+/////8DAAAC","267":[185,139,142,144,145],"274":"b://Xf/P///+39//////////////8DAAAC","281":[185],"295":"b:x/Xf8O3/+cHdYDQC//0B//////8DAAAC","307":"b:x5BFwGX/sAEBAAQA4CQAeMj/l3EAAAAC","320":"b:x/Xf8O3/+cHdYDQC//0B//////8DAAAC","326":"b:x/Xf8O3/+cHdYDQC//0B//////8DAAAC","332":"b:x/Xf8O3/+cHdYDQC//0B//////8DAAAC","339":"b:x/Xf8O3/+cHdYDQC//0B//////8DAAAC","345":"b:xvXHsGWb+MFEACQC+/QB8tz//0sBAAAC","354":"b:x/Xf8O3/+cHdYDQC//0B//////8DAAAC","384":"b:x/XP8OX/+MFdYAQC//wB//////sDAAAC","392":"b:x/XP8OX/+MFdYAQC//wB//////sDAAAC","402":"b:x/XP8OX/+OldYDQi//wJ//////sDAAAC","405":"b:x/XP8OX/+OldYDQi//wJ//////sDAAAC","408":"b:x/XP8OX/+MFdYAQC//wB//////sDAAAC","412":"b:x/XP8OX/+MFdYAQC//wB//////sDAAAC","415":"b:QAAAACADCCgIABAAAAgIMOUAQGECAAAC","418":"b:QAAAAAAAACgAAAAAAAgIMAUAAAEAAAAC","425":"b://Xf/P///+39//////////////8DAAAC","439":"b:x/XP8OX/+eldYDwi//wJ+/////sDAAAC","475":"b:x/Xf8O3/+cHdYDwC//0B//////8DAAAC","486":[185],"489":[185],"494":[185],"495":[185],"500":[185],"504":"b:x5DFwGX/sAEBADQA4CQAfMj/l3ECAAAC","532":"b:x5BFwGX/sAEBAAQA4CQAcMj/l3EAAAAC","541":"b:x/Xf8E3/+cHdYAQC//0B+/////8DAAAC","547":[185],"558":[185],"577":"b:x5DFwGX/oAEBAAQA4CQAeMj/l3EAAAAC","594":"b:x/Xf8E3/+cHdYAQC//0B+/////8DAAAC","617":"b:QgAAAAAAAAAAAAAAAAgAcgUCAHkAAAAC","624":"b:xgCAQAAAAAAAAAQAoCAACIAABAAAAAAC","629":[185,134],"634":"b:x/Xf8A38+cHdYAQC//0B+/////8DAAAC","647":"b:R3AAQAC0WcBdAAQAICQA4KHLw3EDAAAC","669":[185],"678":"b:x/Xf8A38+cHdYAQC//0B+/////8DAAAC","687":[185],"691":[185],"696":[185,78,68,161],"701":[185,78,68,161],"705":[185],"707":[185],"713":[185],"715":[185],"716":[185],"725":[185],"730":[185],"735":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAC","748":[185,126,14,153],"761":[185],"767":[185],"773":[185],"786":[185,126,14,153],"811":"b:x/Xf8O3/+cHdYDwC//0B//////8DAAAC","845":[185,6],"969":"b:x/Xf8O3/+cHdYDwC//0B//////8DAAAC","992":"b://Xf/P///+39//////////////8DAAAC","996":"b:1/Xf8O3/+e3dYDQy//2J//////8DAAAC"},"packages/frontend/editor-ui/src/app/stores/workflowSave.store.ts":{"15":"b://Xf/P///+39//////////////8DAAAA","26":"b:wuXfsAVYqMFUIAAC//0Bc9+/f/8DAAAA","30":"b:wuHfkAVIqAFEAAAA4CUAct2+X38DAAAA","55":"b:AiANACECCABAAAAAgCQAYlkeVnkBAAAA","63":[37,41]},"packages/frontend/editor-ui/src/app/stores/workflows.store.ts":{"43":"b://Xf/P///+39//////////////8DAAAC","60":"b:x/Xf8O3/+cHdYAwC//0B+/////8DAAAC","70":"b:x/Xf8O3/+cHdYAwC//0B+/////8DAAAC","71":"b:x/Hf0OX/+cFdAAwA4CUA+/3/338DAAAC","80":"b://Xf/P///+39/8//////8/////8DAAAC","85":"b:xvXHsAWY6MFEAAAC+/QBctz//wsBAAAC","89":"b:xvXHsAWY6MFEAAAC+/QBctz//wsBAAAC","99":[185,78,68,161],"100":[185],"120":[185,159],"127":"b:x/Xf8O3/+cHdYAwC//0B+/////8DAAAC","157":[185],"165":"b:x/Xf8O3/+cHdYDwC//0B//////8DAAAC","169":"b:x/Xf8O3/+cHdYDwC//0B//////8DAAAC","180":"b:R3AAQOC3WcBdAAwAICQA6KHLw3EDAAAC","187":[185,67,160],"203":[185,67,160],"218":[185,47,121,67,155,160],"230":[185,67,160],"234":[185,48],"253":[185],"271":"b:QAAAAAAAEAABAAAAAAAAQMAAAHAAAAAC","285":"b:wuHfkAVIuwFUAAAA4CUAc92+X38DAAAC","286":"b:wuHPkAVIuAFUAAAA4CQAc92+X3sDAAAC","294":"b:wuHfkAVIqQEUAAAA4AEAc9y0XS8DAAAC","328":"b:AiANAAEAGwBAAAAAgCQAYlkeVnkBAAAC","371":[185,47,121,152,67,155,160],"385":[185],"393":[185,160],"420":[185],"472":"b:x5DFwAX/oAEBAAQA4CQAeMj/l3ECAAAC","491":[185],"499":"b:x5BFwGX/oAEBAAQA4CQAeMj/l3EAAAAC","511":[185]},"packages/frontend/editor-ui/src/app/stores/workflowsList.store.ts":{"27":"b://Xf/P///+39//////////////8DAAAA","39":[152,153],"40":[152,153],"44":"b:x/Xf8O3/+MHdYAwC//0B+/////8DAAAA","58":"b:x/Hf0PX//8F9ADxA4CcA+/3/338DAAAA","68":[67,160],"73":"b:x/Xf8O3/+MHdIDwC//0B//////8DAAAA","98":[47,121,67,155,160],"111":[67,160],"125":"b:AAAADBAAD8BsAINBAAMAACAAQAMDAAAA","153":"b:AAAAABAADwBkAABAAAMAACAAQAMDAAAA","154":"b:AAAAABAADwBkAABAAAMAACAAQAMDAAAA","165":"b:AAAADBAAD8BsAINBAAMAACAAQAMDAAAA","186":[52,121,160],"213":[52,121,160],"234":"b:R3AAQOC3XsBdAAwAICUA4qHLw3MDAAAA","240":[113],"244":"b:x/Xf/P3//8H9YI9D//8B8/////8DAAAA","250":"b:wuXf8A1YqMGMIAAC/9kBc968fa4BAAAA","256":[67,160],"262":[67,160],"283":[67,160]},"packages/frontend/editor-ui/src/app/utils/aiUtils.ts":{"57":[1,6],"82":[6],"87":[6],"236":[1,6],"248":[1,6],"263":[1,6],"264":[1,6],"274":[82,1,7,2,37,38,40,41,109,123,6,158],"286":[6],"300":"b:x/XP8OX/+MFdYDQC//wB+/////sDAAAA"},"packages/frontend/editor-ui/src/app/utils/credentialOnlyNodes.ts":{"5":"b:1/XP8OX/+O1dYDQy//yJ//////sDAAAC","9":[185,43],"13":"b:x/Xf8G3/+cHdYAQC//0B8/////8DAAAC","17":"b:x/Xf8G3/+cHdYAQC//0B8/////8DAAAC","52":"b:x/Xf8G3/+cHdYAQC//0B8/////8DAAAC"},"packages/frontend/editor-ui/src/app/utils/formatters/dateFormatter.ts":{"4":"b:RIBBgACZEAEAAAAA4AQAAMjql0EAAAAA","15":[52,126,134,135,64,6,156,157,158],"27":[82,1,7,2,37,38,40,41,109,123,6],"29":[82,1,7,2,37,38,40,41,109,123,6]},"packages/frontend/editor-ui/src/app/utils/injectStrict.ts":{"4":"b://Xf/P///+39f7////////////8DAAAC"},"packages/frontend/editor-ui/src/app/utils/mappingUtils.ts":{"11":"b:RtDBgECZsAEAACQA4CQAoNj/t0EAAAAC","15":"b:RtDBgECZsAEAACQA4CQAoNj/t0EAAAAC","16":"b:RtDBgECZsAEAACQA4CQAoNj/t0EAAAAC","29":"b:RlDAgECZMAEAACQA4CQAoNj+l0EAAAAC","33":"b:RhDAgECZEAEAACQA4CQAANj+l0EAAAAC","53":"b:RlDAgECZMAEAACQA4CQAoNj/t0EAAAAC","70":[185],"82":[185,139],"96":[185,139]},"packages/frontend/editor-ui/src/app/utils/nodeIcon.ts":{"81":"b:x/XP8OX/+MVdIDQi//yB//3///sDAAAA","95":"b:x/XP8OX/+MVdYDQi//yB//3///sDAAAA","105":"b:x/XP8OX/+MVdIDQi//yB//3///sDAAAA","113":"b:wvVKMIG4aMAQYAAgT5QAcpy/74gCAAAA","118":"b:x/XP8OX/+MVdYDQi//yB//3///sDAAAA","125":"b:wvVKMIG4aMAQYAAgT5QAcpy/74gCAAAA","131":"b:x/XP8OX/+MVdIDQi//yB//3///sDAAAA","138":[161],"152":"b:x/XP8OX/+MVdYDQi//yB//3///sDAAAA"},"packages/frontend/editor-ui/src/app/utils/nodeTypes/nodeTypeTransforms.ts":{"8":"b://Xf/P///+39f7////////////8DAAAC","29":"b://Xf/P///+39f7////////////8DAAAC","32":"b://Xf/P///+39f7////////////8DAAAC"},"packages/frontend/editor-ui/src/app/utils/nodeTypesUtils.ts":{"43":"b:kkUCIAwACIQQAAAQEBCAAAAACAAAAAAC","46":"b:kkUCIAwACIQQAAAQEBCAAAAACAAAAAAC","54":"b:xnXO8ISueMBQQDQiu/QAWtSDrogDAAAC","58":[185,78,14,147,68],"61":[185,78,14,147,68],"65":[185,28,89,103,137,143,146,160,161],"77":"b:x/XP8OX/+MFdYDQC//wB//////sDAAAC","111":[185,23,96,42,101,46,103,143,161],"122":[185,23,96,42,101,46,103,143,161],"134":"b:x/Xf8O3/+cHdYAwC//0B8/////8DAAAC","135":"b:x/XP8OX/+MFdYAQC//wB8/3///sDAAAC","140":[185,68,161],"141":[185,68,161],"142":[185,68,161],"146":[185,68,161],"149":"b:AEAAAAAAAAQAAAAAAAAAAAA8HAIAAAAC","150":"b:AEAAAAAAAAQAAAAAAAAAAAA8HAAAAAAC","153":"b:1vXPsI2Z+O1UACAy+/SJ8tz//0sBAAAC","173":"b:xvXPsGWb+MFEACQC+/QB8tz//0sBAAAC","174":"b:RtDBgGCbsAEAACQA4CQAoNj/l0EAAAAC","182":"b:wmECIIAACMAAAAAAAxAAAgCACAsAAAAC","194":"b:wmUCMAAIaMAAAAAiuxAAQhCALggAAAAC","214":"b:xvWLMIGZ+OlEAAAi+/QJ8pz//0sAAAAC","221":"b:AEEKMIAASMBAAAACowAAAACALggAAAAC","224":"b:AEEKMIAASMAAAAACogAAAACALggAAAAC","241":"b:xvWLEAGZ+ClEAAAi+3QJ8pz//0sAAAAC","246":"b:wmUIEAAIIABAAAAiuRAAQhAAAAAAAAAC","250":[185,28,70,89,8,96,101,103],"255":[185,28,70,89,8,96,101,103],"265":[185,28,89,96,101,103],"282":"b:ksEDIAwACMQQAAAQEBCAAAAACAAAAAAC","296":[17,185,29,8,51,62,63,14,147],"300":[17,185,29,8,51,62,63,14,147],"303":[17,185,29,8,51,62,63,14,147],"330":[17,185,29,8,51,62,63,14,147],"339":"b:AEECMAAASMAAAAAAAwAAAACALggAAAAC","345":"b:AEECMAAASMAAAAAAAwAAAACALggAAAAC","354":[17,185,29,8,51,62,63,14,147],"361":[17,185,29,8,51,62,63,14,147],"367":"b:gkECIAAACMAAAAAAEBAAAAAACAAAAAAC","376":[17,185,29,8,51,62,63,14,147],"384":"b:AEEKMIAASMAAAAAAAwAAAACALggAAAAC","389":"b:AEEKMIAASMAAAAAAAwAAAACALggAAAAC","401":"b:xvWLMIGZ+OlEAAAi+3QJ8pz//0sAAAAC","407":"b:wmUKMIAIaMBAAAAiuxAAQhCALggAAAAC","409":"b:AEEKMIAASMBAAAACowAAAACALggAAAAC","410":"b:AEEKMIAASMBAAAACowAAAACALggAAAAC","412":"b:AEEKMIAASMBAAAACowAAAACALggAAAAC","428":"b:AEEKMIAASMBAAAACowAAAACALggAAAAC","451":[185,29,8,51,62,63,14,147],"477":"b:AEEKMIAASMAAAAAAAwAAAACALggAAAAC","486":[185],"487":[185],"499":"b:1vXPsI2Z+O1UACAy+/SJ8tz//0sBAAAC","506":[185,148],"519":[185,148],"528":[185,148],"562":[185],"563":[185],"566":[185,148],"578":"b://XP8O///MVdYDQ6//yB//3///sDAAAC"},"packages/frontend/editor-ui/src/app/utils/nodeViewUtils.ts":{"71":[185,84,85,78,123,68,161],"72":[185,84,85,78,123,68,161],"81":"b:QMAFgABAIAEEAAAAAIgBQASARYAAAAAC","82":"b:QMAFgABAIAEEAAAAAIgBQASARYAAAAAC","91":"b:QMAFgABAIAEEAAAAAIgBQASARYAAAAAC","92":"b:QMAFgABAIAEEAAAAAIgBQASARYAAAAAC","101":"b:QMAFgABAIAEEAAAAAIgBQASARYAAAAAC","102":"b:QMAFgABAIAEEAAAAAIgBQASARYAAAAAC","111":"b:QMAFgABAIAEEAAAAAIgBQASARYAAAAAC","112":"b:QMAFgABAIAEEAAAAAIgBQASARYAAAAAC","121":"b:QMAFgABAIAEEAAAAAIgBQASARYAAAAAC","138":"b:gmXCMAUIiMAEAAAAstAAcly9fAoAAAAC","155":[185,55,111,124],"170":"b:x/XP8OX/+MFdYDQi//wB//////sDAAAC","189":"b:x/XP8OX/+MFdYDQi//wB//////sDAAAC","196":[19,185,110,122],"210":"b:wuXPsAVYqMEUYAAC/9gBc969fasDAAAC","275":"b:x/TFkACReAFcICQApmQB8N/fl3kAAAAC","285":[185,129],"297":[185,129],"316":[185,129],"325":[185,84,85,78,123,68,161],"336":[185,84,85,78,123,68,161],"406":"b:xvXHsGWb+MFEACQC+/QB8tz//0sBAAAC","548":[185,125],"574":"b:QuENsBBAqoUdCAAACZuFeufRRfkDAAAC","591":"b:wuXHsAVYqMEEIAQC/9gBc969faoBAAAC","603":[82,185,6],"612":[82,185,6],"618":[82,185,6],"654":"b:x/XP8OX/+MFdYDQC//wB//3///sDAAAC"},"packages/frontend/editor-ui/src/app/utils/nodes/nodeTransforms.ts":{"33":"b:wuXHsAVYqMEUYAAC/9gBc969fasDAAAC","42":"b:w/XP8GX/+MFdYAAC/9gB8//+//sDAAAC","67":"b:wnXO8ASueMBQQAACu9AAUtSCLogDAAAC","77":[185,68,161],"91":"b:wnUKMACsaMAQQAACu5AAQhCALogCAAAC","105":"b:w/XP8GX/+MFdYAAC/9gB8//+//sDAAAC","111":"b:wnUKMACsaMAQQAACu5AAQhCALogCAAAC","122":"b:BZABgAAJAAAAAAAAwCQAEIi+FwAAAAAC","136":"b:1vXPsA2Z+O1UACAy+/SJ8tz//0sBAAAC","156":"b:xvXHsAWZ+OlEACAC+/QJctz//0sBAAAC","175":"b:x/HP0AX/qAFVAAQA4CQAe93/33sDAAAC","241":[1,7,185,42,101,45,47,103,51,6,68],"244":[1,7,185,42,101,45,47,103,51,6,68]},"packages/frontend/editor-ui/src/app/utils/objectUtils.ts":{"3":"b:x/Xf8O3/+MHdYDwi//0B+/////8DAAAA","7":"b:x/Xf8O3/+OndYDwi//0J+/////8DAAAA","11":"b:x/Xf8O3/+OndYDwi//0J+/////8DAAAA","15":[128],"16":[128],"19":"b:x/Xf8O3/+MHdYDQC//0B+/////8DAAAA","23":[142,144],"24":[142,144],"36":[15,16,17,18]},"packages/frontend/editor-ui/src/app/utils/pairedItemUtils.ts":{"19":"b:x5DFwGX/sAEBACQA4CQAcNj/l3EAAAAC","23":[185,139,145],"48":[185,139,145],"58":[185,139,145],"61":"b:x5BFwGX/sAEBAAQA4CQAcMj/l3EAAAAC","89":"b:RxAEgGF/sAEBAAQAACQAYMj/l3EAAAAC","95":"b:x5BFwGX/sAEBAAQA4CQAcMj/l3EAAAAC","124":"b:RxBEgGH/sAEBAAQAICQAYMj/l3EAAAAC","138":"b:x5BFwGX/sAEBAAQA4CQAcMj/l3EAAAAC","143":"b:x5BFwGX/sAEBAAQA4CQAcMj/l3EAAAAC","152":[185,45],"168":"b:x5DFwGX/sAEBAAQA4CQAeMj/l3ECAAAC","171":"b:x5BFwGX/sAEBAAQA4CQAcMj/l3EAAAAC","172":"b:x5BFwGX/sAEBAAQA4CQAcMj/l3EAAAAC","173":"b:x5BFwGX/sAEBAAQA4CQAcMj/l3EAAAAC","188":"b:x5DFwGX/sAEBAAQA4CQAeMj/l3ECAAAC","197":"b:x5BFwGX/sAEBAAQA4CQAcMj/l3EAAAAC","209":"b:x5DFwGX/sAEBAAQA4CQAeMj/l3ECAAAC","229":"b:x5BFwGX/sAEBAAQA4CQAcMj/l3EAAAAC","230":"b:x5BFwGX/sAEBAAQA4CQAcMj/l3EAAAAC"},"packages/frontend/editor-ui/src/app/utils/rbac/checks/hasScope.ts":{"4":"b://Xf/P///+39//////////////8DAAAA"},"packages/frontend/editor-ui/src/app/utils/rbac/checks/isAuthenticated.ts":{"5":"b://Xf/P///+39//////////////8DAAAA","14":"b://Xf/P///+39/7////////////8DAAAA"},"packages/frontend/editor-ui/src/app/utils/rbac/checks/isValid.ts":{"3":"b://Xf/P///+39f4//////8/////8DAAAA"},"packages/frontend/editor-ui/src/app/utils/rbac/middleware/authenticated.ts":{"6":"b://Xf/P///+39/7////////////8DAAAA"},"packages/frontend/editor-ui/src/app/utils/typeGuards.ts":{"31":[185],"35":[185,139,145],"39":[185,35,147,68],"43":"b:RNBJgACZsAEAAAAAAAQAoNj/t0EAAAAC","46":[185],"49":"b:QFGKMIAAaMAAAAAguwAAQpCcvwsAAAAC","57":"b:kkECIAwACIQQAAAQEBCAAAAACAAAAAAC","61":[185,148],"65":[185,58],"71":"b:x/XP8OX/+MFdYDQC//wB//////sDAAAC","77":"b:x/XP8OX/+MFVYDQC//wB//////sDAAAC","81":"b:gmQAEAAAgAAAIAAABsAAMAABEAAAAAAC","90":"b:AACAEAAAAAAAAAACoAAAAICCBAABAAAC","96":[185,134,148],"102":[185],"109":"b:QAAAAAAAEAQBAAAAAAAAQMAAAHAAAAAC","113":[185,8,35,50,114,119,63],"117":[185,8,35,50,114,119,63],"121":[185,114],"125":[185,29,8,34,35,50,51,114,58,119,63],"129":[185],"135":[185,8,35,50,114,119,63],"142":"b:AAEAIBwAD4RkAABAAAOEACAAQAADAAAC","155":[185,26,33,94,105,51,58,63,66,160],"164":[185],"167":"b:woDdgAVIoAEAAAAA4CQAc8i+FzEDAAAC"},"packages/frontend/editor-ui/src/app/utils/typesUtils.ts":{"14":"b:RtDBgGCbsAEAACQA4CQAoNz/l8ECAAAA","23":"b:1vXf/P3b/u39YLdz//+J//7///8DAAAA","40":[68,161],"63":"b:AAEAJBwAD4R0AABQAAOAACAAQAADAAAA","67":"b:AEAAAAAAIAAAAAAAAAAAAFS8B4AAAAAA","73":[146],"78":[52,126,134,135,64,6,156,157,158],"84":"b:RtDBgECZsAEAACQA4CQAoNj/t0EAAAAA","132":[132,138,139,140,141,145,146],"137":[132,138,139,140,141,145,146],"179":[78,68,161],"183":"b:1vXPsA3Z/+1UICQy//2J9/////sBAAAA","187":"b:kkHEoBwYCKwQAAACoBSIgEC8HgAAAAAA"},"packages/frontend/editor-ui/src/app/utils/workflowUtils.ts":{"11":"b:R3AAQOC3WcBdADwAICQA6KHLw3EDAAAC","15":[185],"26":[185,122],"36":[185,122],"49":"b:R/AFwOD3eIFdADQAIKwB6OXLx/EDAAAC","60":"b:R/AFwOD3eAFVADQAIKwB6OXLx/ECAAAC","80":"b:R/AFwOD3ecFdADwAIKwB6OXLx/EDAAAC","100":"b:R/AFwOD3eAFVADQAIKwB6OXLx/ECAAAC","103":"b:R/AFwOD3eAFVADQAIKwB6OXLx/ECAAAC","138":[19,185],"145":[19,185],"146":[19,185],"158":[19,185],"167":[19,185]},"packages/frontend/editor-ui/src/app/views/NodeView.vue":{"154":"b:x/Xf8G3/+cHdYAQC//0B8/////8DAAAA","161":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAA","165":"b:x/Xf8G3/+cHdYAQC//0B8/////8DAAAA","166":"b:x/Xb8G3++cHdYAQC//0B8/////8DAAAA","181":"b:AmEOEAEACABEAAAAoAEAcl0UTG4BAAAA","192":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAA","279":"b:x/Xf8OX/+cHdYDQC//0B+/////8DAAAA","290":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAA","294":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAA","296":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAA","297":"b:wuXCMAUYiMAEIAAC/9AAc96/fHoBAAAA","298":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAA","301":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAA","311":"b:R/HP8GX/+MFdYAAC/8wB8/3///sDAAAA","315":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAA","327":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAA","331":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAA","345":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAA","347":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAA","351":[82,1,7,2,8,10,108,109,123,6,13],"357":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAA","402":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAA","407":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAA","417":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAA","421":"b:x/XP8OX/+MFdYDQC//wB+/3///sDAAAA","422":"b:x/XP8OX/+MFdYDQC//wB+/3///sDAAAA","425":"b:wuXPsAVYqMEEIDAC/9gBe9y+faoDAAAA","463":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAA","465":"b:x/XP8OX/+MFdYDQC//wB+/////sDAAAA","471":"b:R/HP8GX/+MFdYAAC/8wB8/3///sDAAAA","473":"b:R/HP8GX/+MFdYAAC/8wB8/3///sDAAAA","474":"b:R/HP8GX/+MFdYAAC/8wB8/3///sDAAAA","478":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAA","479":"b:x/XP8OX/+MFdYDQC//wB+/3///sDAAAA","481":"b:x5DFwCX/oAEBAAQA4CQAeMj/l3ECAAAA","488":[19,110],"499":[152],"503":[109,124,125,128,130,66],"511":[130],"515":[107,124,6],"524":[124,125,128,130],"528":[130,152],"544":[121,124,6],"552":[130],"556":[1,126,130,137,155,156,157,158],"564":"b:xPDFgACReAFcACAAoCQB8N3fh3kAAAAA","569":"b:xPDFgACReAFAACAAICQBQNjfh0EAAAAA","591":"b:x/XHsAXZ+MFcICQC//wB89////sBAAAA","596":[124,66,68],"602":[126,130,66,67],"650":[126,130,66],"663":[126,130,146],"687":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAA","691":"b:QgAAACADCMAdAAAAAAEAQuAAQHADAAAA","710":[126],"732":[126],"751":[126],"778":[152],"794":[1,161],"798":[89,103,125,161],"802":"b:ggQAEAAAAAAAIAAABkAAMAABEAAAAAAA","842":[125,128,152],"846":[130,152],"850":"b:ggQAEAAAAAAAIAAABkAAMAABEAAAAAAA","878":[124,128,130],"882":[130,152],"890":"b:QMAFgABAIAEAAAAAAIgBAACARYAAAAAA","905":"b:QMAFgABAIAEAAAAAAIgBAACARYAAAAAA","921":[159],"931":[159],"934":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAA","940":"b:QgAAACADCMAdAAAAAAEAQuAAQHADAAAA","951":"b:guXCMAUYiMAEIAAC/9AAc9y9fSoBAAAA","955":"b:guXCMAUYiMAEIAAC/9AAc969fCoBAAAA","987":[130,152],"991":[131,132,13,148],"999":[13],"1012":"b:guXCMAUYiMAEIAAC/9AAc9y9fSoBAAAA","1025":[157],"1038":"b:x/XHsAXZ+MFcICQC//wB89////sBAAAA","1044":[129],"1048":[55,111,124],"1113":"b:x/Xf8O3/+cHdYAQC//0B8/////8DAAAA","1119":"b:wuXf8A1YqMGcYAAC/9kBc968fa4DAAAA","1121":"b:AAEIYAgACMAEAAAAAAEAAAAASAIBAAAA","1132":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAA","1133":"b:x/Xf8G3/+cHdYAQC//0B+/////8DAAAA","1137":"b:R/HP8GX/+MFdYAAC/8wB8/3///sDAAAA","1149":"b:x/Xf8G3/+cHdYAQC//0B+/////8DAAAA","1152":"b:x/Xf8G3/+cHdYAQC//0B+/////8DAAAA","1155":"b:x/Xf8G3/+cHdYAQC//0B+/////8DAAAA","1159":[0,2,40,12,109,124,135,137,138,140],"1229":[0,2,40,12,109,124,135,137,138,140],"1285":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAA","1290":"b:QgAAACADCMAdAAAAAAEAQuAAQHADAAAA","1295":[6],"1305":"b:QgDEwAX3oAEBAAAAoAAAYMHDh3EAAAAA","1309":"b:QADEwAX3oAEBAAAAoAAAYMHDh3EAAAAA","1317":"b:xuXf8A1YqcGMIAQC//kBe96+fa4BAAAA","1318":"b:xiUAAAAAAAAAAAQAALAAWAaCAAAAAAAA","1323":"b:x/Xf8G3/+cHdYAQC//0B+/////8DAAAA","1326":"b:x/XP8GX/+MFdYAQC//wB+/////sDAAAA","1334":"b:x/Xf8G3/+cHdYAQC//0B+/////8DAAAA","1335":"b:x/XP8GX/+MFdYAQC//wB+/3///sDAAAA","1338":"b:xuXf8A1YqcGMIAQC//kBe96+fa4BAAAA","1344":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAA","1360":[1,123],"1368":[1],"1447":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAA","1461":"b:QgAAACADCMAdAAAAAAEAQuAAQHADAAAA","1499":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAA","1503":"b:QgAAACADCMAdAAAAAAEAQuAAQHADAAAA","1510":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAA","1514":"b:QgAAACADCMAdAAAAAAEAQuAAQHADAAAA","1522":"b:wuXCMAUYiMAEIAAC/9AAc96/fHoBAAAA","1565":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAA","1583":[134],"1599":"b:wuXHsAVYqMEEIAAC/9gBc969faoBAAAA","1603":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAA","1609":"b:wuXHsAVYqMEEIAAC/9gBc969faoBAAAA","1610":"b:wuXHsAVYqMEEIAAC/9gBc969faoBAAAA","1617":"b:RQAAEAAAAAAAAAQAgCAA8AOAAQEAAAAA","1622":"b:RQAAEAAAAAAAAAQAgCAA8AOAAQEAAAAA","1630":[124],"1652":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAA","1662":[14],"1692":"b:QgAAACADCMAdAAAAAAEAQuAAQHADAAAA","1698":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAA","1712":"b:xvXHsAWY6MFEAAAC+/QBctz//wsBAAAA","1730":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAA","1735":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAA","1741":"b:wuXf8A1YqcGMIAAC/9kBc968fa4BAAAA","1773":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAA","1775":"b:xvXHsAWY6MFEAAAC+/QBctz//ysBAAAA","1784":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAA","1786":"b:xvXHsAWZ+MFEACAC+/QBctz//0sBAAAA","1804":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAA","1805":"b:wuXPsAVYqMFUIAAC//0Bc9+/f/8BAAAA","1823":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAA","1824":[19,20],"1836":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAA","1848":"b:RZABgGALAAAAAAAAwAQAEIi+FwAAAAAA","1849":"b:RZABgGALAAAAAAAAwAQAEIi+FwAAAAAA","1854":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAA","1863":"b:R9HP8GX/uMENAAAA8swAct3//3sAAAAA","1873":"b:R9HP8GX/uMENAAAA8swAct3//3sAAAAA","1874":"b:R9HP8GX/uMENAAAA8swAct3//3sAAAAA","1885":"b:QgAAAAAACAAJAAAAAAAAQuAAQHACAAAA","1934":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAA","1936":[52,67],"1957":"b:x/Xf8OX/+cHdYDQC//0B+/////8DAAAA","1978":"b:QgAAACADCMAdAAAAAAEAQuAAQHADAAAA","2049":[161],"2059":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAA","2079":"b:QgDEwAX3oAEBAAAAoAAAYMDDh3EAAAAA","2094":[82,1,7,2,8,10,108,109,123,6,13]},"packages/frontend/editor-ui/src/app/workers/isDataWorkerEnabled.ts":{"10":"b://Xf/P///+39//////////////8DAAAC"},"packages/frontend/editor-ui/src/experiments/aiTemplatesStarterCollection/stores/aiTemplatesStarterCollection.store.ts":{"20":"b:x/Xf/P3//8H9YL9D//8B//////8DAAAA","33":"b:AAAABBAAD8BkAABAAAMAACAAQAADAAAA"},"packages/frontend/editor-ui/src/experiments/enhancedHitlSlack/useEnhancedHitlSlackExperiment.ts":{"6":"b:xvXPsIWZ+OlEACAi+/QJ8tz//0sBAAAA","9":"b:xvXPsIWZ+OlEACAi+/QJ8tz//0sBAAAA"},"packages/frontend/editor-ui/src/experiments/enhancedHitlTelegram/useEnhancedHitlTelegramExperiment.ts":{"6":"b:xvXPsIWZ+OlEACAi+/QJ8tz//0sBAAAA","9":"b:xvXPsIWZ+OlEACAi+/QJ8tz//0sBAAAA"},"packages/frontend/editor-ui/src/experiments/evaluationsWizardSidepanel/useEvaluationsWizardSidepanelExperiment.ts":{"7":"b:x/Xf8O3/+MHdYDwC//0B+/////8DAAAA","13":"b:x/Xf8G3/+MHdYAQC//0B8/////8DAAAA"},"packages/frontend/editor-ui/src/experiments/n8nCreditsCredentialSelection/useN8nCreditsCredentialSelectionExperiment.ts":{"6":"b:xvXPsIWZ+MFEAAAi+/QB8tz//0sBAAAA"},"packages/frontend/editor-ui/src/experiments/readyToRunWorkflows/stores/readyToRunWorkflows.store.ts":{"21":"b:x/Xf/P3//8H9YL9D//8B//////8DAAAA","30":"b:AAAABBAAD8BsAABAAAMAACAAQAADAAAA","42":"b:AAAABBAAD8BsAABAAAMAACAAQAADAAAA"},"packages/frontend/editor-ui/src/experiments/readyToRunWorkflowsV2/stores/readyToRunWorkflowsV2.store.ts":{"12":"b:x/Xf/P///8X9f4/D//+F+/////8DAAAA"},"packages/frontend/editor-ui/src/experiments/resourceCenter/stores/resourceCenter.store.ts":{"19":"b:x/Xf/P///8X9f4/D//+F8/////8DAAAA","32":"b:x/Xf/P///8X9f4/D//+F8/////8DAAAA","114":"b:x/Xf/P///8X9f4/D//+F8/////8DAAAA"},"packages/frontend/editor-ui/src/experiments/sidebarExpanded/useSidebarExpandedExperiment.ts":{"6":"b:x/Xf/P///8X9f4/D//+F8/////8DAAAA","10":"b:x/Xf/P///8X9f4/D//+F8/////8DAAAA"},"packages/frontend/editor-ui/src/experiments/utils.ts":{"32":"b:wuXf8A1YqcGMIAAC/9kBc968fa4BAAAA","39":"b://Xf/P///+39//////////////8DAAAA","46":"b:x/Xf/P///8X9f4/D//+F8/////8DAAAA"},"packages/frontend/editor-ui/src/features/agents/agentNodeCanvasGeometry.store.ts":{"3":"b://Xf/P///+39//////////////8DAAAA","37":"b:QgAAACADCAAJAAAAAAAAQuAAQHACAAAA"},"packages/frontend/editor-ui/src/features/agents/agentReturnContext.store.ts":{"20":"b:x/Xf8O3/+MHdYDQC//0B+/////8DAAAA"},"packages/frontend/editor-ui/src/features/agents/composables/useAgentCapabilitiesActions.ts":{"93":"b:x/Xf8O3/+MHdYDQC//0B+/////8DAAAA"},"packages/frontend/editor-ui/src/features/agents/composables/useAgentConfig.ts":{"18":"b:x/Xf8O3/+MHdYDQC//0B+/////8DAAAA","30":"b:x/Xf8O3/+MHdYDQC//0B+/////8DAAAA"},"packages/frontend/editor-ui/src/features/agents/composables/useAgentNavigation.ts":{"11":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAA"},"packages/frontend/editor-ui/src/features/agents/composables/useAgentScopeProjectId.ts":{"19":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAA"},"packages/frontend/editor-ui/src/features/agents/composables/useMessageAgentSessionLink.ts":{"20":[48],"33":[82,1,7,2,37,38,40,41,48,109,6,158],"61":[82,1,7,2,37,38,40,41,48,109,6,158],"66":[82,1,7,2,37,38,40,41,48,109,6,158],"92":[48]},"packages/frontend/editor-ui/src/features/agents/utils/agentNode.ts":{"9":"b:x/Xf8O3/+cHdYDQi//0B//////8DAAAC"},"packages/frontend/editor-ui/src/features/ai/assistant/assistant.store.ts":{"37":"b://Xf/P///+39//////////////8DAAAA","45":"b:x/Xf8A38+MHdYAQC//0B8/////8DAAAA","58":[16],"59":[16],"79":[15,16,17,18,19,20],"80":[15,16,18],"82":[15,16,17,18],"84":[15,16,17,18,19,20],"94":"b://Xf/P///+39//////////////8DAAAA","96":"b://Xf/P///+39//////////////8DAAAA","100":[15,16,17],"102":[15,17],"107":"b://Xf/P///+39//////////////8DAAAA","115":"b://Xf/P///+39f7////////////8DAAAA","119":[15,16,17,18],"123":[15,16,17,18],"136":[15,16,17,18],"138":[15,16,17,18],"142":[15,16,17,18],"199":[15,16],"210":[17],"216":[15,16,17,18],"220":[15,16,17,18],"247":[15,16,17,18],"251":[15,17,18],"277":[15,16,17,18],"297":[15,16,17,18],"301":[15,16,17,18],"305":[15,16,17,18],"317":[17],"336":[15,18],"420":[15,17,18],"480":[15,17,18],"481":[15,17,18],"493":[15,16],"554":[15,16],"555":[15,16],"562":[16],"590":[16],"591":[16],"598":"b:x5DFwGX/oAEBAAQA4CQAeMj/l3EAAAAA","629":[15,18],"679":[15,18],"680":[15,18],"692":[15,18],"706":[15,16,17,18,19,20],"746":"b://Xf/P///+39//////////////8DAAAA","747":"b:wuXPsAVYqOlUYAAC//0Jc9+/f/8DAAAA","753":"b://Xf/P///+39//////////////8DAAAA","756":"b://Xf/P///+39//////////////8DAAAA"},"packages/frontend/editor-ui/src/features/ai/assistant/builder.store.ts":{"166":"b://Xf/P///+39//////////////8DAAAA","188":"b://Xf/P///+39f7////////////8DAAAA","230":"b://Xf9P///8HdbT7r//+B//////8DAAAA","259":"b://Xf/P///+39//////////////8DAAAA","262":[19,20],"265":[19,20],"304":[19,20],"307":"b://Xf/P///+39f7////////////8DAAAA","323":"b://Xf/P///+39f7////////////8DAAAA","327":[19,20],"329":"b://Xf/P///+39f7////////////8DAAAA","335":"b://Xf/P///+39f7////////////8DAAAA","355":"b://Xf/P///+39f7////////////8DAAAA","367":"b://Xf/P///+39f7////////////8DAAAA","371":"b://Xf/P///+39f7////////////8DAAAA","374":"b://Xf/P///+39f7////////////8DAAAA","376":"b://Xf/P///+39f7////////////8DAAAA","388":"b://Xf/P///+39f7////////////8DAAAA","410":[19,20],"420":"b://Xf/P///+39f7////////////8DAAAA","432":"b://Xf9P///8HdbT7r//+B//////8DAAAA","462":"b:x5BFwGX/oAEBAAQA4CQAeMj/l3EAAAAA","469":"b:x/Xf8O3/+cHdYDwC//0B//////8DAAAA","477":[19,20],"481":[19,20],"503":[19,20],"530":[19,20],"531":[19,20],"580":[19,20],"644":[20],"655":[20],"664":[20],"695":[19,20],"726":[19,20],"739":[19,20],"816":[19,20],"832":[19,20],"864":[19],"889":[19,20],"1025":[19],"1028":[19],"1050":[19],"1057":[20],"1128":[19,20],"1397":[19],"1402":[19],"1429":[19,20],"1440":"b:AiANAAEACABAAAAAgCQAYlkeVnkBAAAA","1448":"b:AiANAAEACABAAAAAgCQAYlkeVnkBAAAA","1456":[19],"1460":[19,20],"1465":[19,20],"1479":"b://Xf/P///+39//////////////8DAAAA","1480":"b:x/Xf8A38+cHdYDwC//0B//////8DAAAA","1513":"b://Xf/P///+39//////////////8DAAAA","1525":[19],"1546":[19],"1677":"b://Xf/P///+39f7////////////8DAAAA"},"packages/frontend/editor-ui/src/features/ai/assistant/chatPanel.store.ts":{"32":[15,16,17,18,19,20],"39":"b://Xf/P///+39//////////////8DAAAA","50":[15,16,17,18],"58":[15,16,17,18,19,20],"64":"b://Xf/P///+39f7////////////8DAAAA","66":"b:x/Xf8G3/+MHdYAQC//0B8/////8DAAAA","69":[15,16,17,18,19,20],"103":[15,17],"110":[15,16,17,18,19,20],"119":[15],"127":[15],"142":[15,18,19,20],"150":[15,16,17,18,19,20],"182":[17],"200":[15,16],"216":"b://Xf/P///+39//////////////8DAAAA","217":"b://Xf/P///+39f7////////////8DAAAA","254":"b://Xf/P///+39//////////////8DAAAA","255":"b://Xf/P///+39f7////////////8DAAAA"},"packages/frontend/editor-ui/src/features/ai/assistant/chatPanelState.store.ts":{"14":"b://Xf/P///+39//////////////8DAAAA"},"packages/frontend/editor-ui/src/features/ai/assistant/components/Agent/AskAssistantBuild.vue":{"75":"b://Xf/P///+39f7////////////8DAAAA","88":"b://Xf/P///+39f7////////////8DAAAA","118":[19],"125":"b://Xf/P///+39f7////////////8DAAAA","132":"b://Xf/P///+39f7////////////8DAAAA","136":"b://Xf/P///+39f7////////////8DAAAA","141":"b://Xf/P///+39f7////////////8DAAAA","151":"b://Xf/P///+39f7////////////8DAAAA","156":"b://Xf/P///+39f7////////////8DAAAA","167":"b://Xf/P///+39f7////////////8DAAAA","168":[19,20],"170":[19,20],"178":[19,20],"179":[19,20],"182":[19,20],"198":"b://Xf/P///+39f7////////////8DAAAA","199":"b://Xf/P///+39f7////////////8DAAAA","200":"b://Xf/P///+39f7////////////8DAAAA","203":"b://Xf/P///+39f7////////////8DAAAA","209":"b://Xf/P///+39f7////////////8DAAAA","216":"b://Xf/P///+39f7////////////8DAAAA","223":"b://Xf/P///+39f7////////////8DAAAA","227":"b://Xf/P///+39f7////////////8DAAAA","245":[19],"279":"b://Xf/P///+39f7////////////8DAAAA","311":"b://Xf/P///+39f7////////////8DAAAA","343":"b:x3XY8AX8+OldYDQC//wJ8/////sDAAAA","345":[19,20],"392":[19,20],"408":[19],"417":"b:egAAJPYDD8AdTQLpAAOAQuAAQHADAAAA","543":[19],"557":"b://Xf/P///+39f7////////////8DAAAA","559":"b:egAYJPYDD8AdTQLpAAOAQuAAQHADAAAA","594":"b://Xf/P///+39f7////////////8DAAAA","595":[19,20],"607":"b://Xf/P///+39f7////////////8DAAAA","609":[19,20],"684":"b:egAAJPYDD8AdTQLpAAOAQuAAQHADAAAA","691":"b://Xf/P///+39f7////////////8DAAAA","703":[19,20],"709":"b://Xf/P///+39f7////////////8DAAAA","712":[19,20],"743":[19,20],"749":[19],"756":"b://Xf/P///+39f7////////////8DAAAA","773":"b://Xf/P///+39f7////////////8DAAAA","778":[19,20],"785":"b:x3XA8AX8+OldYDQC//wJ8/////sDAAAA","788":[19],"840":"b://Xf/P///+39f7////////////8DAAAA","870":"b://Xf/P///+39f7////////////8DAAAA","872":"b://Xf/P///+39f7////////////8DAAAA","880":"b:x3XY8AX8+OldYDQC//wJ8/////sDAAAA","910":"b:x3XY8AX8+OldYDQC//wJ8/////sDAAAA"},"packages/frontend/editor-ui/src/features/ai/assistant/components/Agent/BuildModeEmptyState.vue":{"5":"b:x3XA8AX8+OldYDQC//wJ8/////sDAAAA","6":"b:x3XA8AX8+OldYDQC//wJ8/////sDAAAA"},"packages/frontend/editor-ui/src/features/ai/assistant/components/Agent/PlanModeSelector.vue":{"17":"b://Xf/P///+39f7////////////8DAAAA","27":"b://Xf/P///+39f7////////////8DAAAA","46":"b://Xf/P///+39f7////////////8DAAAA","47":"b://Xf/P///+39f7////////////8DAAAA","53":"b://Xf/P///+39f7////////////8DAAAA","71":"b://Xf/P///+39f7////////////8DAAAA","74":"b://XY/P///+39f7////////////8DAAAA"},"packages/frontend/editor-ui/src/features/ai/assistant/components/AssistantsHub.vue":{"19":"b://Xf/P///+39f7////////////8DAAAA","39":"b://Xf/P///+39f7////////////8DAAAA","40":[15,16,17,18,19,20],"51":"b://Xf/P///+39f7////////////8DAAAA","85":[15],"89":[15,16,17,18,19,20],"98":"b:x5DfwGX/oAEBAAQA4CQAeMj/l3EAAAAA","106":"b://Xf/P///+39f7////////////8DAAAA","112":"b:/vXftP/b78VdTQLr+/eFc/z//38DAAAA","124":"b://Xf/P///+39f7////////////8DAAAA","141":"b://Xf/P///+39f7////////////8DAAAA","150":"b:9/Xb/P/+/+39f7////////////8DAAAA","164":"b://Xf/P///+39f7////////////8DAAAA","169":[19,20]},"packages/frontend/editor-ui/src/features/ai/assistant/components/FocusedNodes/ChatInputWithMention.vue":{"24":"b://Xf/P///+39f7////////////8DAAAA","45":"b://Xf/P///+39f7////////////8DAAAA","66":"b://Xf/P///+39f7////////////8DAAAA","67":[19,20],"73":[19,20],"103":[19],"114":[19,20],"121":[19,20],"127":[20],"159":[19,20],"174":"b://Xf/P///+39f7////////////8DAAAA","283":"b://Xf/P///+39f7////////////8DAAAA"},"packages/frontend/editor-ui/src/features/ai/assistant/composables/useAIAssistantHelpers.ts":{"45":"b://Xf/P///+39//////////////8DAAAC","47":[15,16,17,185],"62":[15,16,17,185],"74":[185],"81":[15,16,17,185],"100":[185],"101":[185],"113":[185],"122":[185],"131":[185],"137":[185],"163":[185],"180":[185],"207":[15,16,185],"214":[15,16,185],"227":[15,16,17,18,185],"251":[17,185],"276":[15,16,185],"298":[15,16,17,185],"301":[15,16,17,185],"309":[15,16,17,19,185],"335":[15,18,185],"357":[18,185],"379":[18,185],"420":[15,18,19,185,20],"428":[185],"447":[185],"465":[185],"509":[185],"582":[185],"596":[185],"632":[185]},"packages/frontend/editor-ui/src/features/ai/assistant/composables/useAskModeCoachmark.ts":{"14":[19,20],"17":"b://Xf/P///+39f7////////////8DAAAA","25":"b://Xf/P///+39f7////////////8DAAAA","27":"b://Xf/P///+39f7////////////8DAAAA","35":"b://Xf/P///+39f7////////////8DAAAA"},"packages/frontend/editor-ui/src/features/ai/assistant/composables/useBuilderMessages.ts":{"82":"b://Xf/P///+39//////////////8DAAAA","88":[19,20],"89":[19,20],"208":[19],"326":"b://Xf/P///+39f7////////////8DAAAA","327":[19,20],"330":"b://Xf/P///+39f7////////////8DAAAA","342":[19],"395":[19],"419":[19],"436":[19],"460":[19],"462":[19],"471":[19,20],"517":[20],"556":"b://Xf9P///8HdbT7r//+B//////8DAAAA"},"packages/frontend/editor-ui/src/features/ai/assistant/composables/useBuilderTodos.ts":{"37":"b://Xf/P///+39//////////////8DAAAA","47":[19],"89":[19],"129":[19,20],"138":[19],"151":[19,20],"206":[19,20],"212":[19],"246":[19,20],"247":[19],"254":[19]},"packages/frontend/editor-ui/src/features/ai/assistant/composables/useFocusedNodesChipUI.ts":{"14":"b://Xf/P///+39f7////////////8DAAAA"},"packages/frontend/editor-ui/src/features/ai/assistant/composables/useNodeMention.ts":{"32":"b://Xf/P///+39f7////////////8DAAAA","61":"b://Xf/P///+39f7////////////8DAAAA","62":"b:wmXIMOULiMAMAAAAstgAen+9fAsAAAAA","155":[19,20]},"packages/frontend/editor-ui/src/features/ai/assistant/composables/useReviewChanges.ts":{"34":"b://Xf/P///+39f7////////////8DAAAA","71":"b://Xf/P///+39f7////////////8DAAAA","100":[19],"107":[19],"128":[19],"145":[19],"252":[15,16,17,18],"278":[19,20],"288":[19],"299":[19,20],"309":[15,16,17,18],"311":"b://Xf/P///+39f7////////////8DAAAA"},"packages/frontend/editor-ui/src/features/ai/assistant/focusedNodes.store.ts":{"27":"b://Xf/P///+39//////////////8DAAAA","29":"b://Xf/P///+39//////////////8DAAAA","36":"b://Xf/P///+39//////////////8DAAAA","42":"b://Xf/P///+39f7////////////8DAAAA","52":"b:x/Xf8O3/+cHdYDwC//0B//////8DAAAA","247":[19,20],"273":"b://Xf/P///+39//////////////8DAAAA","274":"b:x/Xf8O3/+cHdYDwC//0B//////8DAAAA","289":"b://Xf/P///+39//////////////8DAAAA","290":"b:x/Xf8O3/+endYDwC//0J//////8DAAAA","292":"b:x/XP8OX/+OldYDQC//wJ+/////sDAAAA","318":"b://Xf/P///+39//////////////8DAAAA","319":"b:x/Xf8O3/+endYDwC//0J//////8DAAAA","331":"b://Xf/P///+39//////////////8DAAAA","332":"b:xvXHsAWZ6OlEACAC+/QJctz//wsBAAAA","341":[19,20]},"packages/frontend/editor-ui/src/features/ai/chatHub/chat.store.ts":{"108":"b://Xf/P///+39//////////////8DAAAA","137":[3,4,5,91,93,33],"140":[3,4,5,91,93,33],"147":[3,4,5,91,93,33],"163":[3,4,5,91,93,33],"173":[3,4,5,91,93,33],"177":[3,4,5,91,93,33],"214":[3,4,5,91,93,33],"237":[4],"279":[3,4,5,91,93,33],"305":[3,4,5,91],"315":[3,4,5,91,93,33],"334":[3,4,5,91,93,33],"341":[93],"368":[3,4,5,91,93,33],"371":[3,4,5,91,93,33],"377":[3,4,5,91,93,33],"382":[3,4,5,91,93,33],"411":[3,4,5,91,93,33],"441":[3,4],"447":[3,4],"458":[3,4,5,91,93,33],"467":[3,4,5,91,93,33],"484":[93,33],"595":[3,4,5,91,93,33],"610":[3],"657":[93],"694":[4],"797":[4],"949":[4],"961":[91],"985":[91],"996":[91],"1031":[91],"1056":[91],"1076":[91],"1081":[91],"1091":[3,4,5,91,93,33],"1095":[3,4,5,91,93,33],"1106":[92],"1117":[92],"1133":[92],"1150":[3,4,5,91,93,33],"1196":[3,4,5,91],"1214":[3,4,5,91,93,33],"1230":[93,33],"1252":[3,4,5,91,93,33],"1294":[3,4,5,91,93,33],"1335":[3,4,5,91,93,33],"1362":[3,4,5,91,93,33],"1392":[3],"1413":[4],"1531":[3,4,5,91,93,33],"1532":[3,4,5,91,93,33],"1557":[3,4,5,91,93,33],"1558":[3,4,5,91,93,33]},"packages/frontend/editor-ui/src/features/ai/chatHub/chatHubPanel.store.ts":{"19":"b://Xf/P///+39f7////////////8DAAAA","29":"b:x/Xf8O3/+cHdYDwC//0B+/////8DAAAA","66":"b://Xf/P///+39f7////////////8DAAAA","67":"b:egACJPYDD8AdTQLpAAOAQuAAQHADAAAA"},"packages/frontend/editor-ui/src/features/ai/chatHub/module.descriptor.ts":{"18":[3,4,5,91,93,33],"19":[33],"20":[33],"21":[91],"22":[91],"23":[92],"24":[92],"46":[93],"57":[91],"69":[5],"93":[92],"203":"b://Xf/P///+39f4//////8/////8DAAAA"},"packages/frontend/editor-ui/src/features/ai/evaluation.ee/components/EvaluationsCanvasInfoCard/EvaluationsCanvasInfoCard.vue":{"25":"b:x/Xf8G3/+cHdYAQC//0B8/////8DAAAA","74":"b:x/Xf8G3/+cHdYAQC//0B8/////8DAAAA","85":"b:x/Xf8G3/+cHdYAQC//0B8/////8DAAAA","99":"b:x/Xf8G3/+cHdYAQC//0B8/////8DAAAA","103":"b:x/Xf8G3/+cHdYAQC//0B8/////8DAAAA","134":"b:x/Xf8G3/+cHdYAQC//0B8/////8DAAAA","159":"b:x/Xf8G3/+cHdYAQC//0B8/////8DAAAA","182":"b:x/Xf8G3/+cHdYAQC//0B8/////8DAAAA"},"packages/frontend/editor-ui/src/features/ai/evaluation.ee/composables/useAiRootNodes.ts":{"15":"b:x/Xf8G3/+cHdYAQC//0B8/////8DAAAA"},"packages/frontend/editor-ui/src/features/ai/evaluation.ee/composables/useEvaluationsLicense.ts":{"9":[83,48],"10":[48],"12":"b:x/Xf8O3/+cHdYDwC//0B+/////8DAAAA"},"packages/frontend/editor-ui/src/features/ai/evaluation.ee/evaluation.store.ts":{"11":"b:x/Xf8O3/+MHdYAwC//0B8/////8DAAAA","26":"b:x/Xf8G3/+MHdYAQC//0B8/////8DAAAA","45":[83],"52":[83],"65":[83,121],"69":[83],"138":[83]},"packages/frontend/editor-ui/src/features/ai/evaluation.ee/wizardSidepanel.store.ts":{"29":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAA","32":"b:x/Xf8G3/+cHdYAQC//0B8/////8DAAAA"},"packages/frontend/editor-ui/src/features/ai/instanceAi/composables/useInstanceAiAvailability.ts":{"15":"b:x/Xf/P///8X9f4/D//+F8/////8DAAAA"},"packages/frontend/editor-ui/src/features/ai/instanceAi/composables/useInstanceAiHandoffCapability.ts":{"33":"b:x/Xf8A38+cHdYAwC//0B8/////8DAAAA","47":[48],"66":[48],"80":[48],"130":[48],"147":[48],"168":[48]},"packages/frontend/editor-ui/src/features/ai/instanceAi/instanceAi.store.ts":{"25":"b://Xf/P///+39//////////////8DAAAA","49":[37,72,74,38,95,75,40,41],"52":[37,72,73,74,38,95,39,75,40,41,76],"55":[37,72,73,74,38,95,39,75,40,41,76],"64":[73,74,75,76],"68":[75],"82":[37,72,73,74,38,95,39,75,40,41,76],"93":[37,72,73,74,38,95,39,75,40,41,76],"105":[37,72,73,74,38,95,39,75,40,41,76],"113":[37,72,73,74,38,95,39,75,40,41,76],"116":[37,38,39,40,41],"139":[37,72,73,74,38,95,39,75,40,41,76],"151":[37,72,73,74,38,95,39,75,40,41,76],"159":[37,72,73,74,38,95,75,40,41,76],"160":[37,72,73,74,38,95,75,40,41,76],"161":[37,72,73,74,38,95,75,40,41,76],"176":[37,72,74,38,95,39,75,40,41,76],"197":[95,75],"215":[75],"228":[75],"234":[75],"235":[75],"246":[37,72,73,74,38,95,39,75,40,41,76],"247":[37,72,73,74,38,95,39,75,40,41,76],"305":[37,72,73,74,38,95,39,75,40,41,76]},"packages/frontend/editor-ui/src/features/ai/instanceAi/instanceAiSettings.store.ts":{"42":"b://Xf/P///+39//////////////8DAAAA","101":[37,72,73,74,38,95,39,75,40,41,76],"105":[37,72,73,74,38,95,39,75,40,41,76],"108":[37,72,73,74,38,95,39,75,40,41,76],"111":[37,72,73,74,38,95,39,75,40,41,76],"114":[37,72,73,74,38,95,39,75,40,41,76],"120":[37,72,73,74,38,95,39,75,40,41,76],"149":[37,72,73,74,38,95,39,75,40,41,76],"159":[37,72,73,74,38,95,39,75,40,41,76],"259":[37,72,73,74,38,95,39,75,40,41,76],"353":[37,72,73,74,38,95,39,75,40,41,76],"372":[74,75],"388":[37,72,73,74,38,95,39,75,40,41,76],"498":[37,72,73,74,38,95,39,75,40,41,76],"501":[37,38,39,40,41],"519":[37,72,73,74,38,95,39,75,40,41,76],"520":[74,75],"587":[37,72,73,74,38,95,39,75,40,41,76],"591":[37,72,73,74,38,95,39,75,40,41,76]},"packages/frontend/editor-ui/src/features/ai/mcpAccess/module.descriptor.ts":{"71":"b://Xf/P///+39f4//////8/////8DAAAA"},"packages/frontend/editor-ui/src/features/collaboration/collaboration/collaboration.store.ts":{"32":"b://Xf/P///+39//////////////8DAAAA","70":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAA","78":"b:x/Xf8O3/+MHdYDQC//0B+/////8DAAAA","79":"b:x/Hf0OX/+cFdAAQA4CUA8/3/338DAAAA","82":"b://Xf/P///+39f7////////////8DAAAA","86":"b://Xf/P///+39f7////////////8DAAAA","90":"b:x/Hf0OX/+cFdAAwA4CUA8/3/338DAAAA","108":"b:x/Hf0OX/+cFdAAwA4CUA8/3/338DAAAA","113":"b:AAAAACADGABMAAAAAAAAYCEBQAEBAAAA","117":"b:AAAAACADGABIAAAAAAAAYCEBQAEBAAAA","122":"b:x/Hf0OX/+cFdAAwA4CUA8/3/338DAAAA","129":"b:x/Hf0OX/+MFdAAwA4CUA8/3/338DAAAA","134":"b:QmEPECGDiIBMAAAAoCQAcn0fVnkBAAAA","153":"b:QmEPEAGAiIBMAAAAoCQAcl0fVnkBAAAA","161":"b:QmEPECGDiIBMAAAAoCQAcn0fVnkBAAAA","195":"b:AAAAAAAAGABEAAAAAAAAYAEBQAEBAAAA","204":"b:QmEPEAGAiIBMAAAAoCQAcl0fVnkBAAAA","208":"b:wuXfsAXYqMFcIAAC//0Bc9+/f/8BAAAA","247":[51,150,67],"266":"b:R2ABAIBBCQABAAAAgAAAQMBEA2EAAAAA","281":"b:x/Hf0OX/+cFdAAwA4CUA8/3/338DAAAA","288":"b:x/Hf0OX/+MFdAAwA4CUA8/3/338DAAAA","293":"b:x/Xf8O3/+MHdYDQC//0B+/////8DAAAA","297":"b:wuHPkCVLqClMAAAA4CUIc/2+V38DAAAA","302":[126,131,132],"312":[126,131,132],"318":"b:AiANAAGAGABIAAAAgCQAYlkeVnkBAAAA","345":"b://Xf/P///+39//////////////8DAAAA","346":"b:wuXPsAVYqOlUYAAC//0Jc9+/f/8DAAAA","355":"b:x/Hf0OX/+cFdAAwA4CUA8/3/338DAAAA","367":"b:x/Hf0OX/+cFdAAwA4CUA8/3/338DAAAA","389":"b:x/Hf0OX/+cFdAAwA4CUA8/3/338DAAAA","445":[37,40,41,51,133,150,67]},"packages/frontend/editor-ui/src/features/collaboration/collaboration/components/CollaborationPane.vue":{"11":"b:x/Hf0OX/+cFdAAwA4CUA8/3/338DAAAA","15":"b:AAAAAAAAGQBEAAAAAAEAYAEBQAEBAAAA","24":"b:x/Hf0OX/+cFdAAwA4CUA8/3/338DAAAA","26":[48],"27":[48],"29":[48],"35":[48],"37":"b:x/Hf0OX/+cFdAAwA4CUA8/3/338DAAAA","41":[37,40,41,48,51,133,150,67],"44":"b:x/Hf0OX/+cFdAAwA4CUA8/3/338DAAAA"},"packages/frontend/editor-ui/src/features/collaboration/projects/components/ProjectCardBadge.vue":{"23":"b://Xf/P///+39//////////////8DAAAA","37":"b:AAEAIBwADYRkAABAAAOAACAAQAADAAAA","46":"b:AAEAIBwADYRkAABAAAOAACAAQAADAAAA","50":"b:AAEAIBwADYRkAABAAAOAACAAQAADAAAA","73":"b:AAEAIBwADYRkAABAAAOAACAAQAADAAAA","77":"b:AAEAIBwADYRkAABAAAOAACAAQAADAAAA","89":"b:AAEAIBwADYRkAABAAAOAACAAQAADAAAA","105":"b:AAEAIBwADYRkAABAAAOAACAAQAADAAAA","148":"b:AAEAIBwADYRkAABAAAOAACAAQAADAAAA","175":"b:AAEAIBwADYRkAABAAAOAACAAQAADAAAA","178":"b:AAAAABAADQBkAABAAAMAACAAQAADAAAA"},"packages/frontend/editor-ui/src/features/collaboration/projects/components/ProjectNavigation.vue":{"24":"b:x/Xf/P///8X9f4/D//+F8/////8DAAAA","48":"b:x/Xf/P///8X9f4/D//+F8/////8DAAAA","50":"b:x/Xf/P///8X9f4/D//+F8/////8DAAAA","55":"b:x/Xf/P///8X9f4/D//+F8/////8DAAAA","60":"b:x/Xf/P///8X9f4/D//+F8/////8DAAAA","61":"b:x/Xf/P///8X9f4/D//+F8/////8DAAAA","64":"b:x/Xf/P///8X9f4/D//+F8/////8DAAAA","76":"b:x/Xf/P///8X9f4/D//+F8/////8DAAAA","85":"b:x/Xf/P///8X9fw/C//+F8/////8DAAAA","94":"b:AGEIZBoIf8UEAIBCg7uBwMbnauIDAAAA","106":"b:x/Xf/P///8X9f4/D//+F8/////8DAAAA","118":"b:x/Xf/P///8X9f4/D//+F8/////8DAAAA","120":[37,72,73,74,38,95,39,75,40,41,76],"137":[33],"151":"b:x/Xf/P///8X9f4/D//+F8/////8DAAAA","156":"b:AAAAAAAACMAcAAIAAAEAACAAQAADAAAA","159":"b:x/Xf/P///8X9f4/D//+F8/////8DAAAA","264":[94,50,51,66],"281":"b:AGEIZBoIf8UEAIBCg7uBwMbnauIDAAAA"},"packages/frontend/editor-ui/src/features/collaboration/projects/composables/useFavoriteNavItems.ts":{"23":"b:x/Xf/P///8X9f4/D//+F8/////8DAAAA","155":"b:x/Xf/P///8X9f4/D//+F8/////8DAAAA"},"packages/frontend/editor-ui/src/features/collaboration/projects/composables/useProjectPages.ts":{"9":"b:x/Xf/P///8X9f4/D//+F+/////8DAAAA","13":"b:AAECJA4AD8QEAABAAAOAAAAAAAABAAAA","16":"b:AAECLB4AD8RsAINBAAOEACAAQAADAAAA","19":"b:AAECLB4AD8RsAINBAAOEACAAQAADAAAA"},"packages/frontend/editor-ui/src/features/collaboration/projects/projects.api.ts":{"7":[185,26,33,94,105,51,58,63,66,160],"11":[185,49,50,51,160],"27":[185,92,34,117,62,119,63,120,160],"45":"b://Xf/P///+39/4//////8/////8DAAAC","49":"b://Xf/P///+39/4//////8/////8DAAAC","53":"b:AAEKZB4AD8QMAABAAAOAAAAASAIBAAAC","57":[185,26,94,105,51,63,66,160],"64":[185,26,94,105,51,63,66,160],"72":[185],"85":"b://Xf/P///+39/4//////8/////8DAAAC","89":[185,33,105],"97":[185,33],"106":[185]},"packages/frontend/editor-ui/src/features/collaboration/projects/projects.routes.ts":{"10":"b:AAAATBAAD8BsgINBAAMgACAAQAADAAAA","11":[17,29,8,34,35,50,51,58,119,63],"12":[17,29,8,34,35,50,51,58,119,63],"13":[26,33,94,105,51,58,63,66,160],"16":[114],"18":"b:IAECbBwAD8RsgINBAAOkACAAQAADAAAA","22":"b:AAECJBwAD8QMAABAAAGAAAAAAAABAAAA","33":"b:IAAATBAAD8BsgINBAAMgACAAQAADAAAA","44":[17,29,8,34,35,50,51,58,119,63],"64":[48,104,49],"74":[114],"134":"b://Xf/P///+39//////////////8DAAAA","146":[26,33,94,105,51,58,63,66,160],"147":[26,33,94,105,51,58,63,66,160],"169":"b:IAACbBQADIRkgINBAAKkACAAQAADAAAA","184":"b://Xf/P///+39//////////////8DAAAA"},"packages/frontend/editor-ui/src/features/collaboration/projects/projects.store.ts":{"32":"b://Xf/P///+39//////////////8DAAAC","51":"b://Xf/P///+39/8//////8/////8DAAAC","54":"b://Xf/P///+39/8//////8/////8DAAAC","58":"b://Xf/P///+39//////////////8DAAAC","64":[185,160],"65":[185],"66":[185],"68":[185,92,34,58,119,63],"69":"b:3/Xf/P///+39/4//////8/////8DAAAC","70":"b:x/Xf/P///8X9f4/D//+F8/////8DAAAC","71":"b:3/Xf/P///+39/4//////8/////8DAAAC","72":[185],"75":"b:3/Xf/P///+39/4//////8/////8DAAAC","81":"b://Xf/P///+39//////////////8DAAAC","84":"b://Xf/P///+39/8//////8/////8DAAAC","89":"b:x/Xf/P///8X9f4/D//+F8/////8DAAAC","90":"b://Xf/P///+39//////////////8DAAAC","95":"b:OAEKbP4DDIRkH4PpAAKkACAASAIDAAAC","99":[185,26,33,94,105,51,58,63,66,160],"103":"b://Xf/P///+39/4//////8/////8DAAAC","107":"b://Xf/P///+39/4//////8/////8DAAAC","111":[185],"119":[185,49,50,51,160],"129":[185,92,34,117,62,119,63,120,160],"139":"b:AAEKZB4AD8QMAABAAAOAAAAASAIBAAAC","142":"b:AAECJB4AD8QMAABAAAOAAAAAAAABAAAC","146":"b:AAEIYAgACAAAAAAAAAAAAAAASAIBAAAC","153":"b://Xf/P///+39/7////////////8DAAAC","159":[185,26,94,105,51,63,66,160],"166":[185,26,94,105,51,63,66,160],"174":[185,26,94,105,51,63,66,160],"192":[185,33,105],"200":[185,33],"209":[185],"214":[185],"217":[185],"220":"b://Xf/P///+39/4//////8/////8DAAAC","224":"b:R3AAQOC3WcBdAAwAICQA4KHLw3EDAAAC","232":[185,63],"264":[185,50,51],"287":[185],"301":[185,26,33,94,105,51,58,63,66,160],"312":"b://Xf/P///+39//////////////8DAAAC"},"packages/frontend/editor-ui/src/features/collaboration/projects/projects.utils.ts":{"16":[185,92,34,117,62,119,63,120,160],"18":[185,92,34,117,62,119,63,120,160],"31":"b:AAEAIBwAD4RkAABAAAOEACAAQAADAAAC","33":[185,49,50,51,160],"42":[185],"50":"b:kkECIAwAH8QUAABQEBOgAwAACAADAAAC","64":[185,50,51],"69":"b://Xf/P///+39//////////////8DAAAC"},"packages/frontend/editor-ui/src/features/core/dataTable/components/NodeStorageLimitCallout.vue":{"9":"b:xvXHsAWZ+MFEACAC+/QB8tz//0sBAAAA","13":"b:xvXHsAWZ+MFEACAC+/QB8tz//0sBAAAA","28":"b:xvXHsAWZ+MFEACAC+/QB8tz//0sBAAAA"},"packages/frontend/editor-ui/src/features/core/dataTable/dataTable.api.ts":{"13":"b:AAAADBAADABkAINBAAIAACAAQAADAAAC","40":[185,36,94],"61":[185,94],"77":[185,36],"93":[185,36],"109":[185,36],"122":[185,36],"139":[185],"156":[185,36,94],"176":[185,36],"193":[185,36],"214":[185,36],"220":[185,36],"238":"b:3/Xf/P///+39/4//////8/////8DAAAC","246":[185],"269":[185],"283":[185]},"packages/frontend/editor-ui/src/features/core/dataTable/dataTable.store.ts":{"37":"b://Xf/P///+39//////////////8DAAAA","50":[36,94],"56":"b:3/Xf/P///+39/4//////8/////8DAAAA","64":[36,94],"72":"b://Xf/P///+39/8//////8/////8DAAAA","76":"b:AAAADBAADABkAINBAAIAACAAQAADAAAA","101":[36,94],"176":[94],"179":[94],"185":[36],"197":[36],"199":[36],"207":[36],"215":[36],"246":[36,94],"247":[36,94],"254":[36],"266":[36],"274":[36],"288":[36],"289":[36],"323":[36,94],"341":[36],"351":[36],"366":[36],"370":"b:3/Xf/P///+39/4//////8/////8DAAAA"},"packages/frontend/editor-ui/src/features/core/folders/components/FolderBreadcrumbs.vue":{"59":"b:x/Xf8P3//8HdYAwC//0B8/////8DAAAA","65":"b:x/Xf8P3//8HdYAwC//0B8/////8DAAAA","67":"b:x/Xf8P3//8HdYAwC//0B8/////8DAAAA","76":[104],"88":"b:x/Xf8P3//8HdYAwC//0B8/////8DAAAA","92":[104,49],"96":[36,48,104,49,50,51,66,160],"107":"b:x/Xf8P3//8HdYAwC//0B8/////8DAAAA","111":"b:x/Xf8P3//8HdYAwC//0B8/////8DAAAA","147":[104],"162":[104],"174":[104,49],"178":[48],"185":[48,104,49],"196":[104],"208":"b:x/Xf8P3//8HdYAwC//0B8/////8DAAAA","209":"b:x/Xf8P3//8HdYAwC//0B8/////8DAAAA","212":"b:x/Xf8P3//8HdYAwC//0B8/////8DAAAA","219":"b:AAAAADADDgAMAAAAAAEAACAAQAAAAAAA","242":[48,104,49],"249":[48,104,49],"275":[36,48,104,49,50,51,66,160],"305":[104,49]},"packages/frontend/editor-ui/src/features/core/folders/components/ProjectBreadcrumb.vue":{"30":"b:x/Xf8P3//8HdYAxC//0B8/////8DAAAA","52":"b:x/Xf8P3//8HdYAxC//0B8/////8DAAAA","63":"b:x/Xf8P3//8HdYAxC//0B8/////8DAAAA","75":[48,104,49],"79":[48],"91":[48,104,49],"100":"b:x/Xf8P3//8HdYAxC//0B8/////8DAAAA"},"packages/frontend/editor-ui/src/features/core/folders/composables/useParentFolder.ts":{"5":"b:x/Xf8O3/+cHdYDwC//0B//////8DAAAA","11":"b:wuXf8A1YqcGMIDAC/9kBf968fa4BAAAA"},"packages/frontend/editor-ui/src/features/core/folders/folders.constants.ts":{"16":"b://Xf/P///+39//////////////8DAAAA"},"packages/frontend/editor-ui/src/features/core/folders/folders.store.ts":{"23":"b://Xf/P///+39//////////////8DAAAA","40":"b:AAAADBAAD8BkAANAAAMAACAAQAADAAAA","41":[48,104,49],"52":[104,49],"56":[104,49],"64":[48,104,49],"75":[48,104,49],"81":[48,104,49],"99":"b:AAAADBAAD8BsAINBAAMAACAAQAADAAAA","115":[49],"116":[49],"121":[49],"125":[49],"134":[49,50,51],"174":[49],"183":[49],"190":[49],"206":[49],"219":[48,49],"249":[49],"262":[104],"273":[104],"342":[104]},"packages/frontend/editor-ui/src/features/credentials/components/NodeCredentials.vue":{"90":"b:xvXPsIWZ+MFEAAAi+/QB8tz//0sBAAAA","117":[17,1,7,29,8,100,51,108,14,147],"146":[17,1,7,29,8,100,51,108,14,147],"148":"b:wmEKMIQIaMBAAAAiuxAAQhCALggAAAAA","163":"b:wmXOsIQIaMBAAAAiu9AAUtCCLggBAAAA","165":"b:xvXPsIWZ+MFEAAAi+/QB8tz//0sBAAAA","178":"b:xvXPsIWZ+MFEAAAi+/QB8tz//0sBAAAA","182":"b:wmUCMAAIaMAAAAAiuxAAQhCALggAAAAA","193":"b:wmUKMIQIaMBAAAAiuxAAQhCALggAAAAA","197":"b:wkEIIAQACMBAAAAgoBAAAAAACAAAAAAA","205":"b:wmUKMIQIaMBAAAAiuxAAQhCALggAAAAA","272":"b:xvXPsIWZ+MFEAAAi+/QB8tz//0sBAAAA","273":"b:xvXPsIWZ+MFEAAAi+/QB8tz//0sBAAAA","300":"b:xvXPsIWZ+MFEAAAi+/QB8tz//0sBAAAA","321":"b:wmUKMIQIaMAAAAAimxAAQhCALggAAAAA","341":"b:wkEIIAQACMAAAAAggBAAAAAACAAAAAAA","357":"b:xvXPsIWZ+MFEAAAC+/QB8tz//0sBAAAA","369":"b:xvXPsIWZ+MFEAAAi+/QB8tz//0sBAAAA","370":"b:xvXPsIWZ+MFEAAAi+/QB8tz//0sBAAAA","377":[17,1,7,29,8,100,51,108,14,147],"461":"b:xvXPsAWZ+MFEAAAi+/QB8tz//0sBAAAA","466":"b:wkEIIAQACMBAAAAgoBAAAAAACAAAAAAA","473":[70],"477":"b:wkEIIAQACMBAAAAgoBAAAAAACAAAAAAA","485":[8],"504":[17,1,7,29,8,100,51,108,14,147],"539":[147],"549":"b:wkEIIAQACMBAAAAggBAAAAAACAAAAAAA","641":[29,8,51,62,63,14,147],"676":"b:wkUKIAQACMBAAAAgoBAAAAAACAAAAAAA","680":"b:wmUKMIQIaMBAAAAiuxAAQhCALggAAAAA","743":"b:wkEIIAQACMBAAAAgoBAAAAAACAAAAAAA","756":[8],"774":"b:wmUKMIQIaMBAAAAiuxAAQhCALggAAAAA","793":[1,7,70,93,34],"797":"b:wkEIIAQACMBAAAAgoBAAAAAACAAAAAAA","801":[1,7],"802":[1,7],"807":[17,10],"812":"b:AmUKMIAIaABAAAAiGxAAQhCALggAAAAA","820":"b:AEECMAAASAAAAAAAAwAAAACALggAAAAA","825":"b:AmUKMIAIaABAAAAiGxAAQhCALggAAAAA","832":"b:AmUKMIAIaABAAAAiGxAAQhCALggAAAAA","836":"b:AmUKMIAIaAAAAAACGxAAQhCALggAAAAA","840":"b:wmEKMIQIaMBAAAAiuxAAQhCALggAAAAA","844":[17,10],"874":"b:xvXPsIWZ+MFEAAAi+/QB8tz//0sBAAAA","890":"b:wmUKMIQIaMBAAAAiuxAAQhCALggAAAAA","892":[19],"950":[17],"958":[17],"959":[17],"978":"b:AmEKMIAIaAAAAAACGxAAQhCALggAAAAA","979":"b:AmEKMIAIaAAAAAACGxAAQhCALggAAAAA","997":[17,1,29,8,100,51,108,14,147],"998":"b:AmEKMIAIaAAAAAACGxAAQhCALggAAAAA","1029":[70,93,34],"1036":"b:wkEIIAQACMBAAAAgoBAAAAAACAAAAAAA","1041":"b:wkEIIAQACMBAAAAgoBAAAAAACAAAAAAA","1062":"b:wkEIIAQACMBAAAAgoBAAAAAACAAAAAAA","1063":"b:wkEIIAQACMBAAAAgoBAAAAAACAAAAAAA","1069":[1,7],"1094":[70],"1113":[8]},"packages/frontend/editor-ui/src/features/credentials/composables/useCredentialOAuth.ts":{"42":"b:kmUKMIwIaIRQAAAyGxCAQhCALggAAAAA","60":"b:kmUKMIwIaIRQAAAyGxCAQhCALggAAAAA","73":"b:kkUCIAQACIQQAAAQEBCAAAAAAAAAAAAA","83":"b:AmUKMIAIaABAAAAiGxAAQhCALggAAAAA","100":"b:AEECMAAASAAAAAAAAgAAAACALggAAAAA","110":[17],"122":"b:AEUCMAAASAAAAAAAAgAAAACALggAAAAA","129":"b:AEUCMAAASAAAAAAAAgAAAACALggAAAAA","135":"b:AEUCMAAASAAAAAAAAgAAAACALggAAAAA","154":[17,10],"387":"b:xvXPsAWZ+MFEAAAi+/QB8tz//0sBAAAA"},"packages/frontend/editor-ui/src/features/credentials/composables/useNodeCredentialOptions.ts":{"30":"b:xvXPsIWZ+MFEACAi+/QB8tz//0sBAAAA","38":"b:wmUKMIAIaMBAAAAiuxAAQhCALggAAAAA","39":"b:wmUKMIQIaMBAAAAiuxAAQhCALggAAAAA","44":"b:xvXPsIWZ+MFEAAAi+/QB8tz//0sBAAAA","48":"b:xvXPsIWZ+MFEAAAi+/QB8tz//0sBAAAA","49":"b:wmUKMIQIaMBAAAAiuxAAQhCALggAAAAA","59":"b:wmUKMIQIaMBAAAAiuxAAQhCALggAAAAA","61":"b:wmUKMIQIaMBAAAAiuxAAQhCALggAAAAA","63":"b:wkEIIAQACMBAAAAgoBAAAAAACAAAAAAA","72":"b:wkEIIAQACMBAAAAgoBAAAAAACAAAAAAA","75":[70,8,34],"81":"b:wmXOsIQIaMBAAAAiu9AAUtCCLggBAAAA","98":"b:wmUCMAQIaMBAAAAiuxAAQhCALggAAAAA","108":[89,8,101,103,14],"116":"b:wmUCMAAIaMAAAAAiuxAAQhCALggAAAAA","129":"b:wmUKMIQIaMBAAAAiuxAAQhCALggAAAAA","141":"b:AEECMAAASMAAAAAAAwAAAACALggAAAAA","144":"b:wmUKMIQIaMBAAAAiuxAAQhCALggAAAAA","150":"b:wkEIIAQACMBAAAAgoBAAAAAACAAAAAAA"},"packages/frontend/editor-ui/src/features/credentials/credentials.api.ts":{"15":"b://Xf/P///+39f4//////8/////8DAAAC","33":"b://Xf/P///+39f4//////8/////8DAAAC","37":"b:kkECIAwACIQQAAAAEBCAAAAACAAAAAAC","60":"b:AAECLBwADqx0AINRAAKIACAAQAADAAAC","76":"b://Xf8O//+cHdYAwq//0B8/////8DAAAC","85":"b:kkEAIAwACIQQAAAAABCAAAAACAAAAAAC","92":[185,8],"96":[185],"100":[185],"104":[185,92,8,34,119,63],"117":[19,185,5,92,8,34,68],"127":[185,68],"138":[185,35,147],"148":"b:kkEIIAAACIAQAAAQABCAAAAAAAAAAAAC"},"packages/frontend/editor-ui/src/features/credentials/credentials.store.ts":{"41":"b://Xf/P///+39//////////////8DAAAC","49":[19,185],"53":[19,185],"63":[185],"67":"b://Xf/P///+39f4//////8/////8DAAAC","68":"b://Xf8O///e3dYAw6//2L8/////8DAAAC","73":"b:+mUKPJ6sb8R8QIN7uxOAUjSAbggDAAAC","74":"b:kkEAABgIBIBQAAAgoAKAQAQAQgACAAAC","77":"b:+kEIIAKkCMBQQAA4oBAAAAAACAAAAAAC","80":"b:+kEIIAKkCMBQQAA4oBAAAAAACAAAAAAC","82":"b:+kEIIAKkCMBQAAA4oBAAAAAACAAAAAAC","91":"b:wmUKMIQIaMBAAAAiuxAAQhCALggAAAAC","95":"b:wmUKMIQIaMBAAAAiuxAAQhCALggAAAAC","97":"b:wkEIIAQICMBAAAAioBAAQAAACgAAAAAC","107":[1,7,185,29,8,51,108,14,147],"108":[1,7,185,29,8,51,108,14,147],"112":[1,7,185,29,8,51,108,14,147],"116":[1,7,185],"124":"b:/vXPsI+9/MVUQCA6+/SB8tz//8sDAAAC","125":"b:/vXPsI+9/MVUQCA6+/SB8tz//8sDAAAC","128":"b:+kEKIA6kCMRQAAA4sBCAQAAACIAAAAAC","129":"b:+kEKIA6kCMRQAAA4sBCAQAAACIAAAAAC","132":[1,7,185,29,93,8,34,51,108,14,147],"133":[1,7,185,29,93,8,34,51,108,14,147],"139":"b:+kEIIAKkCMBQQAA4oBAAAAAACAAAAAAC","140":"b:8kEIIAKkCMBQQAA4oBAAAAAACAAAAAAC","145":[185],"146":[185],"151":"b:kkEAIAQACIAQAAAQABCAAAAAAAAAAAAC","152":"b:kkEAIAQACIAQAAAQABCAAAAAAAAAAAAC","160":"b:kkEAIAQACIAQAAAQABCAAAAAAAAAAAAC","165":[185,70,8],"166":[185,70,8],"172":[185,70,8],"196":"b:kkECIAwACIQQAAAQEBCAAAAACAAAAAAC","197":"b:kkECIAwACIQQAAAQEBCAAAAACAAAAAAC","214":"b:kkECIAwACIQQAAAQEBCAAAAACAAAAAAC","215":"b:kkECIAwACIQQAAAQEBCAAAAACAAAAAAC","222":"b:x/Xf8G3/+cHdYAQC//0B8/////8DAAAC","223":"b:x/Xf8G3/+cHdYAQC//0B8/////8DAAAC","234":"b://Xf/P///+39f4////+/8/////8DAAAC","235":"b://Xf/P///+39f4////+/8/////8DAAAC","245":[185],"246":[185],"253":"b://Xf/P///+39YA96//+J8/////8DAAAC","254":"b:+kEIIB6sDOxQAAA6oGaA4MQFQqADAAAC","265":"b:kkEAIAwACIQQAAAQABCAAAAACAAAAAAC","278":"b://Xf/P///+39f4//////8/////8DAAAC","286":"b:AAECLBwADqx0AINRAAKIACAAQAADAAAC","318":"b://Xf8O//+cHdYAwq//0B8/////8DAAAC","329":[19,185,5,92,8,34,68],"337":"b:xvXPsIWZ+MFEACAi+/QB8tz//0sBAAAC","350":"b:kkEAIAwACIQQAAAAABCAAAAACAAAAAAC","388":[185,92,8,34,119,63],"401":[185,8],"410":[185],"415":[185],"419":[185],"429":[185,35,147],"433":[185,68],"437":"b:kkEIIAAACIAQAAAQABCAAAAAAAAAAAAC","461":"b:kkECIAwACIQQAAAAEBCAAAAACAAAAAAC","481":[185,119,63],"491":[185,119,63],"503":[185],"509":[185],"568":[19,185,5,92,78,68],"577":[19,185,5,92,68],"579":[19,185,5,92,68]},"packages/frontend/editor-ui/src/features/credentials/quickConnect/composables/useQuickConnect.ts":{"19":"b:1vXPsI2Z+MVUACAy//SD8tz//0sBAAAA","33":"b:xvXPsIWZ+MFEACAi+/QB8tz//0sBAAAA","41":"b:gEGCMAEACMAAAAAAvQACchyUPQoAAAAA","51":"b:gmUKMIAIaABAAAAiGxAAQhCALggAAAAA","64":"b:gEGCMAEACMAAAAAAvQACchyUPQoAAAAA","71":"b:xvXHsAWZ+MFEACAC+/QB8tz//0sBAAAA","117":"b:1vXPsAWZ+MVUAAAy//SD8tz//0sBAAAA"},"packages/frontend/editor-ui/src/features/execution/executions/components/ViewSubExecution.vue":{"9":[2,22,6,151,152],"23":[2,22,6,151,152],"27":[151,152],"41":[2,22,6,151,152]},"packages/frontend/editor-ui/src/features/execution/executions/composables/useExecutionData.ts":{"8":"b:RIBBgACZEAEAAAAA4AQAAMjql0EAAAAA","10":"b:xvXHsAWZ+MFEACAC+/QBctz//0sBAAAA","12":"b:xvXHsAWZ+MFEACAC+/QBctz//0sBAAAA"},"packages/frontend/editor-ui/src/features/execution/executions/composables/useExecutionRedaction.ts":{"13":"b:xvXHsGWb+MFEACQC+/QB8tz//0sBAAAA","22":[82,1,7,2,37,38,40,41,52,109,6,158],"26":[82,1,7,2,37,38,40,41,109,6,158],"28":[52],"30":[52]},"packages/frontend/editor-ui/src/features/execution/executions/executions.store.ts":{"31":"b://Xf/P///+39//////////////8DAAAA","43":[1,52,126,134,135,64,6,156,157,158],"64":[1,52,126,134,135,64,6,156,157,158],"67":[134,64,156,157,158],"74":[1,52,126,134,135,64,6,156,157,158],"75":[52,126,134,135,64,6,156,157,158],"104":[1,52,126,134,135,64,6,156,157,158],"112":[1,52,126,134,135,64,6,156,157,158],"113":[158],"124":[1,52,126,134,135,64,6,156,157,158],"134":[158],"141":[135],"146":[156],"150":[1,52,126,134,135,64,6,156,157,158],"158":[1,52,126,134,135,64,6,156,157,158],"178":[1,52,126,134,135,64,6,156,157,158],"203":[52,126,134,135,64,6,156,157,158],"217":[1,52,126,134,135,64,6,156,157,158],"223":[158],"231":[1,52,126,134,135,64,6,156,157,158],"236":[1,52,126,134,135,64,6,156,157,158],"295":[6],"317":[135],"339":[1,126,134,156,157,158],"347":[1,126,134,156,157,158]},"packages/frontend/editor-ui/src/features/execution/executions/executions.utils.ts":{"50":"b:QgAAAAAAEAABAAAAAAAAQMAAAHAAAAAC","53":"b:QAAAAAAAEAABAAAAAAAAQMAAAHAAAAAC","56":"b://Xf/P///+39//////////////8DAAAC","70":"b:QgAAAAAAEAABAAAAAAAAQMAAAHAAAAAC","132":[185,22,23,101],"145":"b:x5DFwGX/oAEBAAQA4CQAeMj/l3EAAAAC","147":"b:x5DFwAX/oAEBAAQA4CQAeMj/l3EAAAAC","198":[185,6],"255":"b:xpDBgGCbsAEAAAQA4CQAIMj+l0EAAAAC","256":"b:xpDBgGCbsAEAAAQA4CQAIMj+l0EAAAAC","264":"b:xpDBgGCbsAEAAAQA4CQAIMj+l0EAAAAC","273":"b:xpDBgGCbsAEAAAQA4CQAIMj+l0EAAAAC","274":"b:xpDBgGCbsAEAAAQA4CQAIMj+l0EAAAAC","277":[185],"332":"b:x5BFwGX/oAEBAAQA4CQAcMj/l3EAAAAC","370":"b:woABAAQAAAAAAAAAAAAAAMCCADAAAAAC","419":"b:x5BFwGX/sAEBAAQA4CQAcMj/l3EAAAAC","436":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAC","450":[19,185],"457":[19,185,47,135,137,146],"463":"b:R/HP8OX/+MFdYDAC/8wB8/3///sDAAAC","469":[185],"505":[185],"527":[185]},"packages/frontend/editor-ui/src/features/execution/logs/components/ChatMessagesPanel.vue":{"30":[82,1,7,2,8,10,48,108,109,123,6,13],"57":[82,1,7,2,48,109,123,6],"61":[82,1,7,2,8,10,48,108,109,123,6,13],"67":[82,48],"76":[82,1,7,2,48,109,123,6],"96":[82,1,7,2,48,109,123,6],"105":[82,1,7,2,8,10,48,108,109,123,6,13],"107":[82,1,7,2,48,109,123,6],"117":[82,1,7,2,8,10,48,108,109,123,6,13],"119":[48],"135":[82,1,7,2,8,10,48,108,109,123,6,13],"137":[82,1,7,2,8,10,48,108,109,123,6,13],"145":[48],"154":[82,1,7,2,8,10,48,108,109,123,6,13],"156":[48],"166":[82,1,7,2,8,10,48,108,109,123,6,13],"168":[48],"182":[82,1,7,2,8,10,48,108,109,123,6,13],"190":[82,1,2,48,6],"193":[82,1,7,2,8,10,48,108,109,123,6,13],"208":[82,1,7,2,8,10,48,108,109,123,6,13],"210":[48],"214":[82,1,7,2,8,10,48,108,109,123,6,13],"221":[82,1,7,2,8,10,48,108,109,123,6,13],"227":[82,1,7,2,8,10,48,108,109,123,6,13]},"packages/frontend/editor-ui/src/features/execution/logs/components/LogDetailsPanel.vue":{"41":[82,1,7,2,37,38,40,41,48,109,6,158],"79":[82,1,7,2,37,38,40,41,48,109,6,158],"80":[82,1,7,2,37,38,40,41,48,109,6,158],"81":[82,1,7,2,37,38,40,41,48,109,6,158],"86":[82,1,7,2,37,38,40,41,48,109,6,158],"93":[82,1,7,2,37,38,40,41,48,109,6,158],"94":[82,1,40,48,6],"100":[48],"101":[48],"102":[82,1,7,2,37,38,40,41,48,109,6,158],"103":[82,1,7,2,37,38,40,41,48,109,6,158],"104":[82,1,7,2,37,38,40,41,48,109,6,158],"108":[1,48,6],"110":[48],"114":[1,48,6],"115":[82,1,7,2,37,38,40,41,48,109,6,158],"123":[48],"133":[82,1,7,2,37,38,40,41,48,109,6,158],"144":[82,1,7,2,37,38,40,41,48,109,6,158],"158":[82,1,7,2,37,38,40,41,48,109,6,158],"166":[48],"173":[82,1,7,2,37,38,40,41,48,109,6,158],"179":[82,1,7,2,37,38,40,41,48,109,6,158],"186":[82,1,7,2,37,38,40,41,48,109,6,158],"192":[82,1,7,2,37,38,40,41,48,109,6,158],"203":[48],"221":[1,48,6],"230":[48],"243":[48],"254":[48],"274":[48],"282":[48],"313":[48],"342":[48]},"packages/frontend/editor-ui/src/features/execution/logs/components/LogsOverviewPanel.vue":{"39":"b:x/Xf8O3/+cHdYAQC//0B+/////8DAAAA","51":"b:xgAAAGADAQAAAAQAACAACAAAAEAAAAAA","52":"b:xgAAAGADAQAAAAQAACAACAAAAEAAAAAA","57":"b:xgAAAGADAQAAAAQAACAACAAAAEAAAAAA","59":"b:xgAAAGADAQAAAAQAACAACAAAAEAAAAAA","67":"b:xgAAAGADAQAAAAQAACAACAAAAEAAAAAA","72":"b:xgAAAGADAQAAAAQAACAACAAAAEAAAAAA","75":"b:xgAAAGADAQAAAAQAACAACAAAAEAAAAAA","78":[48],"80":"b:x/Xf8O3/+cHdYAQC//0B+/////8DAAAA","94":"b:x/Xf8O3/+cHdYAQC//0B+/////8DAAAA","98":"b:x5BFwGX/oAEBAAQA4CQAcMj/l3EAAAAA","107":"b:x5BFwGX/oAEBAAQA4CQAcMj/l3EAAAAA","125":[82,1,7,2,37,38,40,41,109,123,6,158],"153":[6],"154":[82,6]},"packages/frontend/editor-ui/src/features/execution/logs/components/LogsOverviewRow.vue":{"49":"b:xgAAAGADAQAAAAQAACAACAAAAEAAAAAA","50":"b:xgAAAGADAQAAAAQAACAACAAAAEAAAAAA","51":"b:xgAAAGADAQAAAAQAACAACAAAAEAAAAAA","52":"b:xgAAAGADAQAAAAQAACAACAAAAEAAAAAA","55":"b:xgAAAGADAQAAAAQAACAACAAAAEAAAAAA","61":[48],"64":"b:xgAAAGADAQAAAAQAACAACAAAAEAAAAAA","67":"b:xgAAAGADAQAAAAQAACAACAAAAEAAAAAA","70":[82,1,7,2,37,38,40,41,48,109,123,6],"71":"b:xgAAAGADAQAAAAQAACAACAAAAEAAAAAA","74":[82,1,7,2,37,38,40,41,48,109,123,6],"79":[48],"84":[82,1,7,2,37,38,40,41,48,109,123,6],"87":[82,1,7,2,37,38,40,41,48,109,123,6],"100":[82,1,7,2,37,38,40,41,48,109,123,6],"103":[82,1,7,2,37,38,40,41,48,109,123,6],"127":[82,1,7,2,37,38,40,41,48,109,123,6],"131":"b:xgAAAGADAQAAAAQAACAACAAAAEAAAAAA","133":"b:xgAAAGADAQAAAAQAACAACAAAAEAAAAAA","150":"b:xgAAAGADAQAAAAQAACAACAAAAEAAAAAA","151":"b:xgAAAGADAQAAAAQAACAACAAAAEAAAAAA","153":"b:xgAAAGADAQAAAAQAACAACAAAAEAAAAAA","187":[1,7,48,6],"204":[82,1,7,2,37,38,40,41,48,109,123,6],"205":[82,1,7,2,37,38,40,41,48,109,123,6],"207":[82,1,7,2,37,38,40,41,48,109,123,6],"213":[82,1,7,2,37,38,40,41,48,109,123,6],"223":[82,1,7,2,37,38,40,41,48,109,123,6],"231":[48],"251":"b:xgAAAGADAQAAAAQAACAACAAAAEAAAAAA","274":"b:xgAAAGADAQAAAAQAACAACAAAAEAAAAAA"},"packages/frontend/editor-ui/src/features/execution/logs/components/LogsOverviewRows.vue":{"44":"b:xgAAAGADAQAAAAQAACAACAAAAEAAAAAA","45":"b:xgAAAGADAQAAAAQAACAACAAAAEAAAAAA","51":"b:xgAAAGADAQAAAAQAACAACAAAAEAAAAAA","55":"b:xgAAAGADAQAAAAQAACAACAAAAEAAAAAA","59":[48,6],"79":"b:xgAAAGADAQAAAAQAACAACAAAAEAAAAAA","80":"b:xgAAAGADAQAAAAQAACAACAAAAEAAAAAA","81":"b:xgAAAGADAQAAAAQAACAACAAAAEAAAAAA","91":"b:xgAAAGADAQAAAAQAACAACAAAAEAAAAAA","93":"b:xgAAAGADAQAAAAQAACAACAAAAEAAAAAA","94":"b:xgAAAGADAQAAAAQAACAACAAAAEAAAAAA","99":[82,1,7,2,37,38,40,41,48,109,6,158],"101":[48],"110":"b:xgAAAGADAQAAAAQAACAACAAAAEAAAAAA","126":[48],"127":[48,6],"128":[48,6],"129":[82,48,6]},"packages/frontend/editor-ui/src/features/execution/logs/components/LogsPanel.vue":{"21":"b:x/Xf8O3/+cHdYAQC//0B+/////8DAAAA","31":"b:x/Xf8O3/+cHdYAQC//0B+/////8DAAAA","70":[82,1,7,2,37,38,40,41,48,109,6,158],"73":"b:x/Xf8O3/+cHdYAQC//0B+/////8DAAAA","74":"b:x/Xf8O3/+cHdYAQC//0B+/////8DAAAA","77":"b:x/Xf8O3/+cHdYAQC//0B+/////8DAAAA","86":[82,1,7,2,37,38,40,41,48,109,6,158],"91":[82,1,7,2,37,38,40,41,48,109,6,158],"97":"b:x/Xf8O3/+cHdYAQC//0B+/////8DAAAA","115":[48],"123":[48,6],"130":[48,6],"140":[48],"147":[48],"152":"b:x/Xf8O3/+cHdYAQC//0B+/////8DAAAA","184":[82,1,7,2,8,10,108,109,123,6,13],"210":"b:x/Xf8O3/+cHdYAQC//0B+/////8DAAAA","253":[82,1,7,2,37,38,40,41,109,6,158]},"packages/frontend/editor-ui/src/features/execution/logs/components/LogsPanelActions.vue":{"20":"b:x/Xf8O3/+cHdYAQC//0B+/////8DAAAA","27":"b:xgAAAGADAQAAAAQAACAACAAAAEAAAAAA","29":"b:x/Xf8O3/+cHdYAQC//0B+/////8DAAAA","30":"b:x/Xf8O3/+cHdYAQC//0B+/////8DAAAA","33":"b:xgAAAGADAQAAAAQAACAACAAAAEAAAAAA","42":[48],"51":"b:x/Xf8O3/+cHdYAQC//0B+/////8DAAAA","58":"b:x/Xf8O3/+cHdYAQC//0B+/////8DAAAA","77":[6],"84":"b:x/Xf8O3/+cHdYAQC//0B+/////8DAAAA"},"packages/frontend/editor-ui/src/features/execution/logs/components/LogsPanelHeader.vue":{"8":"b:x/Xf8O3/+cHdYAQC//0B+/////8DAAAA","10":[82,1,48,6,158],"13":"b:x/Xf8O3/+cHdYAQC//0B+/////8DAAAA","20":"b:x/Xf8O3/+cHdYAQC//0B+/////8DAAAA"},"packages/frontend/editor-ui/src/features/execution/logs/components/LogsViewExecutionSummary.vue":{"18":"b:xgAAAGADAQAAAAQAACAACAAAAEAAAAAA","20":"b:xgAAAGADAQAAAAQAACAACAAAAEAAAAAA","37":"b:xgAAAGADAQAAAAQAACAACAAAAEAAAAAA"},"packages/frontend/editor-ui/src/features/execution/logs/components/LogsViewKeyboardEventListener.vue":{"8":"b:x/Xf8O3/+cHdYAQC//0B+/////8DAAAA","11":"b:AiAIEAAAgQAcAAAACZAAcAcBQHkDAAAA","24":[48],"27":"b:x/Xf8O3/+cHdYAQC//0B+/////8DAAAA"},"packages/frontend/editor-ui/src/features/execution/logs/components/LogsViewNodeName.vue":{"12":"b:xgAAAGADAQAAAAQAACAACAAAAEAAAAAA","18":"b:xgAAAGADAQAAAAQAACAACAAAAEAAAAAA"},"packages/frontend/editor-ui/src/features/execution/logs/components/LogsViewRunData.vue":{"43":[82,1,7,2,37,38,40,41,48,109,6,158],"55":[82,1,7,2,37,38,40,41,48,109,6,158],"58":[82,1,7,2,37,38,40,41,48,109,6,158],"81":[82,1,7,2,37,38,40,41,48,109,6,158],"88":[48],"92":[1,48,6],"94":[82,1,7,2,37,38,40,41,48,109,6,158],"122":[48],"124":[82,1,7,2,37,38,40,41,48,109,6,158],"126":[82,1,7,2,37,38,40,41,48,109,6,158],"130":[82,1,7,2,37,38,40,41,48,109,6,158],"137":[1,48,6],"141":[48,6],"143":[48,6],"152":[48],"160":[48],"166":[48],"170":[48],"173":[48]},"packages/frontend/editor-ui/src/features/execution/logs/composables/useChatState.ts":{"41":"b:x/Xf8O3/+cHdYAQC//0B+/////8DAAAA","59":[82,1,7,2,48,109,123,6],"60":[48,6],"63":[82,1,7,2,48,109,123,6],"65":[48],"66":[82,1,7,2,8,10,48,108,109,123,6,13],"74":[82,1,7,2,8,10,48,108,109,123,6,13],"101":[82,1,7,2,8,10,48,108,109,123,6,13],"120":[82,1,7,2,8,10,48,108,109,123,6,13],"125":[82,1,7,2,8,10,48,108,109,123,6,13],"130":[82,1,7,2,8,10,48,108,109,123,6,13],"135":[82,1,7,2,48,109,123,6],"149":[82,1,7,2,48,109,123,6],"165":[82,1,7,2,48,109,123,6],"177":[48],"178":[48],"179":[48],"183":[48],"184":[48],"230":[82,1,7,2,48,109,123,6],"276":[82,1,7,2,48,109,123,6],"290":[82,1,7,2,48,109,123,6],"321":[48,6],"329":[82,1,2,48,51,123],"342":[48],"350":"b:x/Xf8O3/+cHdYAQC//0B+/////8DAAAA","351":"b:x/Xf8A38+cHdYAQC//0B+/////8DAAAA","362":[82,1,7,2,8,10,48,108,109,123,6,13]},"packages/frontend/editor-ui/src/features/execution/logs/composables/useClearExecutionButtonVisible.ts":{"8":"b:x/Xf8O3/+cHdYAQC//0B+/////8DAAAA","13":"b:x/XP8OX/+cFdYAQC//wB8/3///sDAAAA","14":"b:x/Xf8O3/+cHdYAQC//0B8/////8DAAAA","15":"b:x/Xf8O3/+cHdYAQC//0B+/////8DAAAA","17":"b:x/Xf8O3/+cHdYAQC//0B8/////8DAAAA","18":"b:x/Xf8O3/+cHdYAQC//0B8/////8DAAAA","20":"b:x/XP8OX/+cFdYAQC//wB8/////sDAAAA","21":"b:x/XP8OX/+cFdYAQC//wB8/3///sDAAAA","24":"b:x/Xf8O3/+cHdYAQC//0B+/////8DAAAA"},"packages/frontend/editor-ui/src/features/execution/logs/composables/useLogsExecutionData.ts":{"36":"b:x/Xf8O3/+cHdYAQC//0B+/////8DAAAA","42":"b:x/Xf8O3/+cHdYAQC//0B+/////8DAAAA","49":"b:x/Xf8O3/+cHdYAQC//0B+/////8DAAAA","56":"b:x/Xf8O3/+cHdYAQC//0B+/////8DAAAA","63":"b:x/Xf8O3/+cHdYAQC//0B+/////8DAAAA","64":"b:x5DFwGX/sAEBAAQA4CQAeMj/l3ECAAAA","79":"b:x/Xf8O3/+cHdYAQC//0B+/////8DAAAA","89":"b:x/Xf8O3/+cHdYAQC//0B+/////8DAAAA","94":"b:x/Xf8O3/+cHdYAQC//0B+/////8DAAAA","118":"b:x/Xf8A38+cHdYAQC//0B+/////8DAAAA","127":[48],"157":"b:x/Xf8O3/+cHdYAQC//0B+/////8DAAAA","158":"b:x/Xf8O3/+cHdYAQC//0B+/////8DAAAA","159":"b:x/Xf8O3/+cHdYAQC//0B+/////8DAAAA","160":"b:x/Xf8O3/+cHdYAQC//0B+/////8DAAAA","161":"b:x/Xf8O3/+cHdYAQC//0B+/////8DAAAA","163":"b:x/Xf8O3/+cHdYAQC//0B+/////8DAAAA","187":"b:x/Xf8O3/+cHdYAQC//0B+/////8DAAAA","188":"b:x/Xf8A38+cHdYAQC//0B+/////8DAAAA","197":"b:x/Xf8O3/+cHdYAQC//0B+/////8DAAAA","207":"b:x/Xf8O3/+cHdYAQC//0B+/////8DAAAA"},"packages/frontend/editor-ui/src/features/execution/logs/composables/useLogsPanelLayout.ts":{"16":"b:x/Xf8O3/+cHdYAQC//0B+/////8DAAAA","30":"b:x/Xf8O3/+cHdYAQC//0B+/////8DAAAA","32":[48],"38":[82,1,7,2,8,10,48,108,109,123,6,13],"40":[48],"45":"b:x/Xf8O3/+cHdYAQC//0B+/////8DAAAA","51":"b:x/Xf8O3/+cHdYAQC//0B+/////8DAAAA","56":[82,1,7,2,37,38,40,41,48,109,6,158],"57":"b:x/Xf8O3/+cHdYAQC//0B+/////8DAAAA","58":"b:x/Xf8O3/+cHdYAQC//0B+/////8DAAAA","67":[48],"77":[82,1,48,6,158],"91":[48],"97":[48],"110":"b:x/Xf8O3/+cHdYAQC//0B+/////8DAAAA","133":"b:QgAAACADCQAJAAAAAAAAQuAAQHAAAAAA"},"packages/frontend/editor-ui/src/features/execution/logs/composables/useLogsSelection.ts":{"25":"b:x/Xf8O3/+cHdYAQC//0B+/////8DAAAA","34":"b:x/Xf8O3/+cHdYAQC//0B+/////8DAAAA","35":"b:x/Xf8O3/+cHdYAQC//0B+/////8DAAAA","43":[82,48,6],"58":[82,48,6],"85":[48],"100":[48],"116":"b:x/Xf8O3/+cHdYAQC//0B+/////8DAAAA","127":"b:x/Xf8O3/+cHdYAQC//0B+/////8DAAAA","156":"b:x/Xf8O3/+cHdYAQC//0B+/////8DAAAA","162":[1,7,2,40,109,6,158],"183":"b:x/Xf8O3/+cHdYAQC//0B+/////8DAAAA","222":"b:x/Xf8O3/+cHdYAQC//0B+/////8DAAAA","224":[82,1,7,2,37,38,40,41,109,6,158],"229":"b:x/Xf8O3/+cHdYAQC//0B+/////8DAAAA","230":[1,7,48,6]},"packages/frontend/editor-ui/src/features/execution/logs/composables/useLogsTreeExpand.ts":{"15":"b:x/Xf8O3/+cHdYAQC//0B+/////8DAAAA","19":"b:x/Xf8O3/+cHdYAQC//0B+/////8DAAAA","23":[1,48,6]},"packages/frontend/editor-ui/src/features/execution/logs/composables/usePopOutWindow.ts":{"32":[48],"39":[48],"61":[48],"77":"b:x/Xf8O3/+cHdYAQC//0B+/////8DAAAA","88":"b:x/Xf8O3/+cHdYAQC//0B+/////8DAAAA","89":"b:x/Xf8O3/+cHdYAQC//0B+/////8DAAAA","90":"b:x/Xf8O3/+cHdYAQC//0B+/////8DAAAA","93":"b:x/Xf8O3/+cHdYAQC//0B+/////8DAAAA","104":[48],"121":[48],"141":[48],"168":[48],"171":"b:x/Xf8O3/+cHdYAQC//0B+/////8DAAAA","180":"b:x/Xf8O3/+cHdYAQC//0B+/////8DAAAA","185":"b:x/Xf8O3/+cHdYAQC//0B+/////8DAAAA","194":"b:QgAAACADCQAJAAAAAAAAQuAAQHAAAAAA","199":"b:QgAAACADCQAJAAAAAAAAQuAAQHAAAAAA"},"packages/frontend/editor-ui/src/features/execution/logs/logs.types.ts":{"52":"b:xgAAAGADAQAAAAQAACAACAAAAEAAAAAA","56":"b:xgAAAGADAQAAAAQAACAACAAAAEAAAAAA"},"packages/frontend/editor-ui/src/features/execution/logs/logs.utils.ts":{"44":"b:xgAAAGADAQAAAAQAACAACAAAAEAAAAAA","45":"b:xgAAAGADAQAAAAQAACAACAAAAEAAAAAA","59":"b:xgAAAGADAQAAAAQAACAACAAAAEAAAAAA","67":"b:xgAAAGADAQAAAAQAACAACAAAAEAAAAAA","92":"b:xgAAAGADAQAAAAQAACAACAAAAEAAAAAA","123":[1,7,48,6],"131":[1,7,48,6],"132":[1,7,48,6],"136":[1,7,48,6],"157":"b:xgAAAGADAQAAAAQAACAACAAAAEAAAAAA","175":"b:xgAAAGADAQAAAAQAACAACAAAAEAAAAAA","179":"b:xgAAAGADAQAAAAQAACAACAAAAEAAAAAA","185":"b:xgAAAGADAQAAAAQAACAACAAAAEAAAAAA","204":[48],"234":[48],"254":[48],"268":[48],"273":"b:x5BFwGX/sQEBAAQA4CQAcMj/l3EAAAAA","274":[82,1,7,37,38,40,41,48,109,6,158],"280":[82,1,37,38,40,41,48,109,6,158],"297":"b:xgAAAGADAQAAAAQAACAACAAAAEAAAAAA","303":"b:xgAAAGADAQAAAAQAACAACAAAAEAAAAAA","319":"b:xgAAAGADAQAAAAQAACAACAAAAEAAAAAA","327":[48],"332":[1,7,48,6],"350":"b:xgAAAGADAQAAAAQAACAACAAAAEAAAAAA","366":"b:xgAAAGADAQAAAAQAACAACAAAAEAAAAAA","375":[48],"384":[48],"389":[48],"403":[48],"409":[48],"443":[48],"464":[48],"470":[48],"471":[48],"502":[48],"512":"b:xgAAAGADAQAAAAQAACAACAAAAEAAAAAA","574":"b:xgAAAGADAQAAAAQAACAACAAAAEAAAAAA","597":[48],"598":[48],"601":"b:x/XH8GX/+cFdIAQC//wB89////sBAAAA","620":"b:x/Xf8O3/+cHdYAQC//0B+/////8DAAAA","631":[82,1,7,2,40,109,6,158],"656":"b:x/Xf8O3/+cHdYAQC//0B+/////8DAAAA","672":[48],"677":[48],"682":"b:xgAAAGADAQAAAAQAACAACAAAAEAAAAAA","704":"b:xgAAAGADAQAAAAQAACAACAAAAEAAAAAA","718":"b:xgAAAGADAQAAAAQAACAACAAAAEAAAAAA","723":[82,1,7,2,37,38,40,41,48,109,123,6],"728":[82,1,7,2,37,38,40,41,48,109,123,6],"738":[48,6],"740":[82,1,7,2,37,38,40,41,48,109,123,6],"763":"b:xgAAAGADAQAAAAQAACAACAAAAEAAAAAA","767":"b:xgAAAGADAQAAAAQAACAACAAAAEAAAAAA","778":"b:x/Xf8O3/+cHdYAQC//0B+/////8DAAAA","781":"b:x/Xf8O3/+cHdYAQC//0B+/////8DAAAA","801":[82,48,6],"813":[48,6],"824":[48,6],"849":[48,6],"901":[48,6],"908":[48,6],"920":[48,6],"983":[82,1,7,2,37,38,40,41,48,109,6,158],"992":[82,1,7,2,37,38,40,41,48,109,6,158],"1002":"b:x5DFwGX/sQEBAAQA4CQAeMj/l3ECAAAA","1010":"b:x5DFwGX/sQEBAAQA4CQAeMj/l3EAAAAA"},"packages/frontend/editor-ui/src/features/instanceRegistry/stores/instanceRegistry.store.ts":{"7":"b://Xf/P///+39//////////////8DAAAA","14":[130]},"packages/frontend/editor-ui/src/features/integrations/externalSecrets.ee/externalSecrets.ee.store.ts":{"21":[58],"24":[58],"28":[58],"61":"b:1vXPsI2Z/O1UACAy+/SJ8tz//0sBAAAA","67":"b:kkECIAwACIQQAAAQEBCAAAAACAAAAAAA","79":"b:1vXPsI2Z+O1UACAy+/SJ8tz//0sBAAAA","93":[58],"94":[58],"98":[58],"101":[58],"102":[58],"142":"b:kkECIAwACIQQAAAQEBCAAAAACAAAAAAA","156":"b:kkECIAwACIQQAAAQEBCAAAAACAAAAAAA","182":"b:kkECIAwACIQQAAAQEBCAAAAACAAAAAAA","217":"b:kkECIAwACIQQAAAQEBCAAAAACAAAAAAA"},"packages/frontend/editor-ui/src/features/integrations/sourceControl.ee/sourceControl.api.ts":{"22":"b://Xf/P///+39//////////////8DAAAC","24":[185,80],"30":[185],"37":[185],"44":[185,80],"53":[185,80],"61":[185],"65":[185],"76":[185],"87":[185,80],"96":[185]},"packages/frontend/editor-ui/src/features/integrations/sourceControl.ee/sourceControl.store.ts":{"14":"b://Xf/P///+39//////////////8DAAAC","18":"b://Xf/P///+39/4//////8/////8DAAAC","24":"b://Xf/P///+39//////////////8DAAAC","45":[185],"58":[185],"62":[185,80],"66":"b://Xf/P///+39//////////////8DAAAC","68":[185,80],"73":[185,80],"78":[185,80],"87":[185,80],"102":[185],"112":[185],"116":[185],"124":[185]},"packages/frontend/editor-ui/src/features/ndv/agents/composables/useInlineAgentConfig.ts":{"85":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAA"},"packages/frontend/editor-ui/src/features/ndv/agents/composables/useNdvAgentConfig.ts":{"28":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAA"},"packages/frontend/editor-ui/src/features/ndv/agents/composables/useReferencedAgentSummary.ts":{"22":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAA","27":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAA","31":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAA","107":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAA","133":"b:QgAAACADCAAJAAAAAAAAQuAAQHACAAAA"},"packages/frontend/editor-ui/src/features/ndv/panel/components/InputPanel.vue":{"60":"b:xvXHsAWZ+MFEAAACu/QB8tz//0sAAAAA","110":"b:xvXHsAWZ+MFEAAACu/QB8tz//0sAAAAA","116":"b:xvXHsAWZ+MFEAAACu/QB8tz//0sAAAAA","124":"b:xvXHsAWZ+MFEAAACu/QB8tz//0sAAAAA","128":"b:xvXHsAUY6MBEAAACu/QB8ly//woAAAAA","145":"b:xvXHsAWZ+MFEAAACu/QB8tz//0sAAAAA","146":"b:xvXHsAWZ+MFEAAACu/QB8tz//0sAAAAA","155":"b:xvXHsAWZ+MFEAAACu/QB8tz//0sAAAAA","179":"b:xuXHsAWZ+MFEAAACu/QB8tz//0sAAAAA","180":[1,14],"184":"b:xvXHsAWZ+MFEAAACu/QB8tz//0sAAAAA","192":"b:xvXHsAWZ+MFEAAACu/QB8tz//0sAAAAA","208":"b:AIABgAAYAAAAAAAAAAQAAAi+lwAAAAAA","217":"b:xvXHsAUY6MBEAAACu/QB8ly//woAAAAA","222":"b:xvXHsAWZ+MFEAAACu/QB8tz//0sAAAAA","237":"b:xvXHsAWZ+MFEAAACu/QB8tz//0sAAAAA","238":"b:xuXHsAWZ+MFEAAACu/QB8tz//0sAAAAA","241":"b:xvXHsAWZ+MFEAAACu/QB8tz//0sAAAAA","248":"b:xuXHsAWZ+MFEAAACu/QB8tz//0sAAAAA","252":"b:xvXHsAWZ+MFEAAACu/QB8tz//0sAAAAA","253":"b:xuXHsAWZ+MFEAAACu/QB8tz//0sAAAAA","259":"b:xvXHsAWZ+MFEAAACu/QB8tz//0sAAAAA","278":"b:xvXHsAWZ+MFEAAACu/QB8tz//0sAAAAA","280":"b:xuXHsAUYyMBEAAACu/QBclye/woAAAAA","285":"b:xvXHsAWZ+MFEAAACu/QB8tz//0sAAAAA","298":"b:AECAAAAAAAAAAAAAoAAAgAA8FgAAAAAA","300":"b:AECAAAAAAAAAAAAAoAAAAAA8FAAAAAAA","318":"b:xuXHsAWZ+MFEAAACu/QB8tz//0sAAAAA","338":[131,139],"351":"b:xvXHsAWZ+MFEAAACu/QB8tz//0sAAAAA","355":[2,144],"359":"b:xvXHsAUY6MBEAAACu/QB8ly//woAAAAA","363":[142],"367":[139,144,145],"376":[6,139,142,143,144,145],"380":[139,145],"381":[139,145],"397":"b:RHAAAAAAAAAAAAAAAAAAEAjICwAAAAAA","403":"b:xvXHsAWZ+MFEAAACu/QB8tz//0sAAAAA","439":[12,6,139,142,143,144,145],"442":"b:xvXHsAWZ+MFEAAACu/QB8tz//0sAAAAA","449":"b:xvXHsAWZ+MFEAAACu/QB8tz//0sAAAAA","461":[12,6,139,142,143,144,145],"470":[7,8,12,108,14],"483":"b:xuXHsAUYyMBEAAACu/QBclye/woAAAAA","517":"b:xuXHsAUYyMBEAAACu/QBclye/woAAAAA","625":"b:gEEAEAAACIAAAAAAgBAAIACARAAAAAAA","670":[2],"680":[52]},"packages/frontend/editor-ui/src/features/ndv/panel/components/NDVFloatingNodes.vue":{"13":"b:x/Xf8O3/+MHdYDQC//0B+/////8DAAAA","18":"b:xvXHsAWZ+MFEACAC+/QBctz//0sBAAAA","30":"b:xvXHsAWZ+MFEACAC+/QBctz//0sBAAAA","32":[13],"39":"b:AmEEMAAAgAAAAAAACZQAcBT9XQABAAAA","53":"b:xvXHsAWZ+MFEACAC+/QBctz//0sBAAAA","55":"b:xvXHsAWZ+MFEACACu/QBctz//0sAAAAA","65":"b:xvXHsAWZ+MFEACACu/QBctz//0sAAAAA","68":"b:xvXHsAWZ+MFEACAC+/QBctz//0sBAAAA","97":"b:xvXHsAWZ+MFEACAC+/QBctz//0sBAAAA","101":"b:xvXGEAUQyABEAAAA6/ABctz/d0kBAAAA","107":"b:xvXHsAWZ+MFEACAC+/QBctz//0sBAAAA","125":"b:xvXHsAWZ+MFEACACu/QBctz//0sAAAAA","131":[131,132,13,148]},"packages/frontend/editor-ui/src/features/ndv/panel/components/NDVHeader.vue":{"27":"b:xvXHsAWZ+cFEACAC+/QBctz//0sBAAAA","32":[48],"34":"b:xvXHsAWZ+cFEACAC+/QBctz//0sBAAAA","62":"b:xvXHsAWZ+cFEACAC+/QBctz//0sBAAAA","64":"b:xvXHsAWZ+cFEACAC+/QBctz//0sBAAAA","70":[103,48,130,140],"72":"b:xvXHsAWZ+cFEACAC+/QBctz//0sBAAAA"},"packages/frontend/editor-ui/src/features/ndv/panel/components/NDVSubConnections.vue":{"25":"b:xvXHsAWZ+MFEACAC+/QBctz//0sBAAAA","57":"b:xvXHsAWZ+MFEACAC+/QBctz//0sBAAAA","61":"b:xvXHsAWZ+MFEACAC+/QBctz//0sBAAAA","66":[7,13],"84":[1,7,8,10,108,121,124,13,14],"87":[1,7,8,10,108,121,124,13,14],"99":[7,13],"110":[1,7,8,10,108,121,124,13,14],"127":[1,7,8,10,108,121,124,13,14],"131":[1,7,8,10,108,121,124,13,14],"133":[1,7,8,10,108,121,124,13,14],"137":[1,7,8,10,108,121,124,13,14],"145":[1,7,8,10,108,121,124,13,14],"152":[1,7,8,10,121,13,14],"167":[1,7,8,10,108,121,124,13,14],"169":[7,13],"183":[7,13],"186":[1,7,8,10,108,121,124,13,14],"193":"b:xuXHsAWZ+MFEAAACu/QBctz//0sAAAAA","201":"b:xvXHsAWZ+MFEACAC+/QBctz//0sBAAAA","210":"b:xuXHsAWZ+MFEAAACu/QBctz//0sAAAAA","231":[13],"250":"b:AIABgAAIAAAAAAAAQAQAAAiqFwAAAAAA","253":"b:AIABgAAIAAAAAAAAQAQAAAiqFwAAAAAA","259":"b:xvXHsAWZ+MFEACAC+/QBctz//0sBAAAA","260":"b:xvXHsAWZ+MFEACAC+/QBctz//0sBAAAA","262":"b:xvXHsAWZ+MFEACAC+/QBctz//0sBAAAA","274":"b:xvXHsAWZ+MFEACAC+/QBctz//0sBAAAA","296":[1,7,8,10,121,13,14],"297":[1,7,8,10,108,121,124,13,14],"305":[13],"317":[13],"335":[1,7,8,10,108,121,124,13,14],"355":[7,13],"376":[7,13]},"packages/frontend/editor-ui/src/features/ndv/panel/components/OutputPanel.vue":{"34":"b:xvXHsAWZ+MFEACAC+/QBctz//0sBAAAA","85":"b:xvXHsAWZ+MFEACAC+/QBctz//0sBAAAA","86":"b:RIBBgACZEAEAAAAA4AQAAMjql0EAAAAA","111":"b:xvXHsAWZ+MFEACAC+/QBctz//0sBAAAA","115":"b:xvXHsAWZ+MFEACAC+/QBctz//0sBAAAA","121":"b:xvXHsAUY6MBEAAAC+/QBcty//woBAAAA","125":"b:xvXHsAWZ+MFEACAC+/QBctz//0sBAAAA","144":"b:xvXHsAWZ+MFEACAC+/QBctz//0sBAAAA","153":"b:xvXHsAWZ+MFEACAC+/QBctz//0sBAAAA","157":"b:xvXHsAWZ+MFEACAC+/QBctz//0sBAAAA","165":"b:xvXHsAWZ+MFEACAC+/QBctz//0sBAAAA","167":"b:RIBBgACZEAEAAAAA4AQAAMjql0EAAAAA","185":"b:xvXHsAWZ+MFEACAC+/QBctz//0sBAAAA","203":"b:RIBBgACZEAEAAAAA4AQAAMjql0EAAAAA","211":"b:xvXHsAWZ+MFEACAC+/QBctz//0sBAAAA","215":"b:xvXHsAUY6MBEAAAC+/QBcty//woBAAAA","219":"b:xvXHsAWZ+MFEACAC+/QBctz//0sBAAAA","242":"b:xvXHsAWZ+MFEACAC+/QBctz//0sBAAAA","263":[144],"267":[144],"286":[2,144,145,151],"291":"b:xvXHsAWZ+MFEACAC+/QBctz//0sBAAAA","304":"b:xvXHsAWZ+MFEACAC+/QBctz//0sBAAAA","310":"b:xvXHsAWZ+MFEACAC+/QBctz//0sBAAAA","341":"b:RADAgACZEAEAACAA4AQAANj+l0EAAAAA","342":[139,142,144,145],"343":[142,144],"344":[132,135,144,145],"347":"b:xvXHsAWZ+MFEACAC+/QBctz//0sBAAAA","373":"b:xvXHsAUY6MBEAAAC+/QBcty//woBAAAA","396":"b:gqGCEAAAQAAAAAAC4QAAIpS+bAoBAAAA","404":"b:xvXHsAUY6MBEAAACu/QBcly//woAAAAA","406":"b:xvXHsAUY6MBEAAAC+/QBcty//woBAAAA","417":"b:wnUAAAAQgAAAAAAAAhAAAAAAAgAAAAAA","455":"b:huXHsAUY6MBEAAAC+/QBcty//woBAAAA","489":[6],"491":[6],"495":[2],"513":[52],"534":[2,6,144,145,151]},"packages/frontend/editor-ui/src/features/ndv/panel/components/PanelDragButtonV2.vue":{"11":"b:xvXHsAWZ+cFEAAACu/QBctz//0sBAAAA","17":[48],"21":[48],"25":[48],"27":"b:xvXHsAWZ+cFEAAACu/QBctz//0sBAAAA"},"packages/frontend/editor-ui/src/features/ndv/panel/composables/useNdvLayout.ts":{"21":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAA","29":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAA","35":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAA","37":"b:xvXHsAWZ+cFEACAC+/QBctz//0sBAAAA","41":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAA","45":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAA","46":"b:xvXHsAWZ+cFEACAC+/QBctz//0sBAAAA","51":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAA","53":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAA","75":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAA","102":[48],"106":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAA","119":[48],"123":[48],"157":[48],"175":"b:xvXHsAWZ+cFEACAC+/QBctz//0sBAAAA","182":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAA"},"packages/frontend/editor-ui/src/features/ndv/parameters/components/Collection/CollectionParameter.vue":{"9":"b:xvXGkASZ2ClEACAiq/AI8tT/7wMBAAAA","25":"b:xvXCkASY2ClEACAiq/AI8tT/7wMBAAAA","34":[101,103]},"packages/frontend/editor-ui/src/features/ndv/parameters/components/Collection/CollectionParameterLegacy.vue":{"25":"b:xvXGkASZ2ClEACAiq/AI8tT/7wMBAAAA","46":"b:xvXGkASZ2ClEACAiq/AI8tT/7wMBAAAA","50":"b:xvXGkASZyClEAAAiq/AI8tT/bwMBAAAA","57":"b:xvXGkASZyClEAAAiq/AI8tT/bwABAAAA","63":"b:xvXGkASZyClEAAAiq/AI8tT/bwABAAAA","75":"b:xvXGkASZ2ClEACAiq/AI8tT/7wMBAAAA","91":[18,31,101,103,54,144,151],"95":[18,31,101,103,54,144,151],"112":"b:xvXGkASZ2ClEACAiq/AI8tT/7wMBAAAA","114":"b:xvXGkASZ2ClEACAiq/AI8tT/7wMBAAAA","115":[18,31,101,103,54,144,151],"118":[18,31,101,103,54,144,151],"120":[18,31,101,103,54,144,151],"131":"b:xvXGkASZ2ClEACAiq/AI8tT/7wMBAAAA","133":"b:xvXGkASZ2ClEACAiq/AI8tT/7wMBAAAA","148":"b:xvXGkASZ2ClEACAiq/AI8tT/7wMBAAAA","149":"b:xvXGkASZ2ClEACAiq/AI8tT/7wMBAAAA","152":[101,103],"206":[101,103],"208":"b:xvXGkASZ2ClEACAiq/AI8tT/7wMBAAAA","215":"b:xvXCEASZ2ClEACAiq/AI8tT/bwMBAAAA","248":"b:xvXGkASZyClEAAAiq/AI8tT/bwABAAAA","251":"b:xvXCkASYyClEAAAiq/AI8tT/bwABAAAA"},"packages/frontend/editor-ui/src/features/ndv/parameters/components/ExpressionEditModal.vue":{"38":"b:1vXPsI2Z+O1UACAy+/SJ8tz//0sBAAAA","64":[140],"82":[140],"84":[140],"89":[140],"103":"b:1vXPsI2Z+O1UACAy+/SJ8tz//0sBAAAA","104":[140],"140":[140],"145":[140],"160":"b:1vXPsI2Z+O1UACAy+/SJ8tz//0sBAAAA","181":[140],"189":[140],"210":[140],"220":[140],"243":[140]},"packages/frontend/editor-ui/src/features/ndv/parameters/components/ExpressionEditorModal/theme.ts":{"4":"b:1vXPsI2Z+O1UACAy+/SJ8tz//0sBAAAA","33":[140],"39":"b:1vXPsI2Z+O1UACAy+/SJ8tz//0sBAAAA"},"packages/frontend/editor-ui/src/features/ndv/parameters/components/FilterConditions/constants.ts":{"299":"b://Xf/P///+39//////////////8DAAAA","305":"b://Xf/P///+39//////////////8DAAAA","311":"b://Xf/P///+39//////////////8DAAAA","317":"b://Xf/P///+39//////////////8DAAAA","323":"b://Xf/P///+39//////////////8DAAAA","329":"b://Xf/P///+39//////////////8DAAAA"},"packages/frontend/editor-ui/src/features/ndv/parameters/components/ParameterInput.vue":{"126":"b:1vXPsI2Z+O1UACAy+/SJ8tz//0sBAAAA","245":"b:1vXPsI2Z+O1UACAy+/SJ8tz//0sBAAAA","251":[28,143,14],"255":"b:1vXPsI2Z+O1UACAy+/SJ8tz//0sBAAAA","261":"b:1vXPsI2Z+O1UACAy+/SJ8tz//0sBAAAA","265":"b:1vXPsI2Z+O1UACAy+/SJ8tz//0sBAAAA","267":"b:1vXPsI2Z+O1UACAy+/SJ8tz//0sBAAAA","271":"b:1vXPsI2Z6O1UAAAy+/SJ8tz//wsBAAAA","275":"b:kkHEoAwYCKwQAAACoBCIAECIGAAAAAAA","289":[2,70,99,59,126,14,151],"305":[2,70,99,59,126,14,151],"309":[2,70,99,59,126,14,151],"317":"b:1vXPsI2Z+O1UACAy+/SJ8tz//0sBAAAA","321":"b:1vXPsA2Z+O1UACAy+/SJ8tz//0sBAAAA","340":"b:AFGCMAAAaMAAAAAAuwAAQhCUPwgAAAAA","347":"b:BkABEAEAAAhAAAAACAABcByAgEAAAAAA","356":"b:wmECIIAACMAAAAAAAxAAAgCACAsAAAAA","360":"b:1vXPsI2Z+O1UACAy+/SJ8tz//0sBAAAA","384":"b:1vXPsI2Z+O1UACAy+/SJ8tz//0sBAAAA","400":"b:1vXPsI2Z+O1UACAy+/SJ8tz//0sBAAAA","403":"b:AgABEAEAAAAAAAAAAAABcAyAAEAAAAAA","411":"b:1vXPsI2Z+O1UACAy+/SJ8tz//0sBAAAA","415":"b:AAABEAEAAAAAAAAAAAABcAyAAEAAAAAA","423":"b:AAABEAEAAAAAAAAAAAABcAyAAEAAAAAA","427":"b:1vXPsI2Z+O1UACAy+/SJ8tz//0sBAAAA","501":"b:wuUCIICAaMUAAAAAAzAAEoC8HgsAAAAA","516":"b:1vXPsI2Z+O1UACAy+/SJ8tz//0sBAAAA","549":"b:1vXPsI2Z+O1UACAy+/SJ8tz//0sBAAAA","566":"b:1vXPsI2Z+O1UACAy+/SJ8tz//0sBAAAA","606":"b:xvXHsAWZ+OlEACAi+/QJ8tz//0sBAAAA","637":[51,152,153],"656":"b:1vXPsI2Z+O1UACAy+/SJ8tz//0sBAAAA","670":"b:1vXPsI2Z+O1UACAy+/SJ8tz//0sBAAAA","677":"b:1vXPsI2Z+O1UACAy+/SJ8tz//0sBAAAA","685":[93,8,12,13],"689":"b:1vXPsI2Z+O1UACAy+/SJ8tz//0sBAAAA","716":"b:1vXPsI2Z+O1UACAy+/SJ8tz//0sBAAAA","735":"b:AAABEAEAAAAAAAAAAAABcAyAAEAAAAAA","739":"b:1vXPsA2Z+O1UACAy+/SJ8tz//0sBAAAA","743":"b:1vXPsI2Z+O1UACAy+/SJ8tz//0sBAAAA","747":"b:AAABEAEAAAAAAAAAAAABcAyAAEAAAAAA","751":[132],"753":"b:kkHEoAwYCKwQAAAishSIgEi8HgAAAAAA","754":"b:AEFAAAQAAAgAAAAisgQIAAgcEgAAAAAA","756":[106,134,139],"757":"b:kkHEoAwYCKwQAAACoBSIgECcGAAAAAAA","764":"b:1vXPsI2Z+O1UACAy+/SJ8tz//0sBAAAA","776":[1,143],"778":"b:1vXPsI2Z+O1UACAy+/SJ8tz//0sBAAAA","787":"b:1vXPsA2Z+O1UACAy+/SJ8tz//0sBAAAA","791":"b:1vXPsA2Z+O1UACAy+/SJ8tz//0sBAAAA","795":[70,8,34],"809":"b:1vXPsI2Z+O1UACAy+/SJ8tz//0sBAAAA","823":"b:1vXPsA2Z+O1UACAy+/SJ8tz//0sBAAAA","831":"b:1vXHsA2Z+MVUACAy+/SB8tz//0sBAAAA","839":[28,143,14,147],"891":[143],"896":[143],"901":[140],"905":[140],"935":[143],"947":[140],"962":"b:kkHEoAwYCKwQAAAishSIgEi8HgIAAAAA","970":"b:kkHEoAwYCKwQAAAishSIgEi8HgIAAAAA","1031":"b:kkHEoAwYCKwQAAACoBSIAECIGAAAAAAA","1054":"b:kkHFsA0YCKwQAAAishSJcFy8HAMAAAAA","1141":[58,138,139,140,141,14,146,147,148],"1148":"b:kkHEoAwYCKwQAAAiohSIgEi8GAIAAAAA","1215":"b:kkHEoAwYCKwQAAACoBSIAECIGAAAAAAA","1222":"b:AEAAAAAAAAQAAAAAAAAAAAA8HAIAAAAA","1302":"b:1vXPsI2Z+O1UACAy+/SJ8tz//0sBAAAA","1336":[93,8,12,13],"1365":"b:1vXGMAUQyKRUAAAw6/CB8tz/f0sBAAAA","1371":"b:1vXPsI2Z+O1UACAy+/SJ8tz//0sBAAAA","1373":"b:1vXPsI2Z+O1UACAy+/SJ8tz//0sBAAAA","1401":[143,14,147],"1407":"b:1vXPsI2Z+O1UACAy+/SJ8tz//0sBAAAA","1408":"b:kkHHsA0YCKwQAAASsBSIcEy8HAMAAAAA","1418":[28,143,14],"1423":"b:AEAAAAAAAAQAAAAAAAAAAAA8HAIAAAAA","1433":"b:1vXPsI2Z+O1UACAy+/SJ8tz//0sBAAAA","1436":[89,8],"1452":"b:1vXPsI2Z+O1UACAy+/SJ8tz//0sBAAAA","1465":"b:1vXPsI2Z+O1UACAy+/SJ8tz//0sBAAAA","1471":"b:AEAAAAAAAAAAAAAAAAQIABCsHAAAAAAA","1610":[143],"1696":"b:AAABEAEAAAAAAAAAAAABcAyAAEAAAAAA","1721":[132],"1766":[132],"1789":[1,143],"1812":[2,70,99,59,126,14,151],"1842":"b:kkHEoAwYCKwQAAACoBCIAECIGAAAAAAA","1860":[18,115,139,143,14],"1865":"b:1vXPsI2Z+O1UACAy+/SJ8tz//0sBAAAA","1976":[70,8,34],"1997":"b:1vXPsA2Z+O1UACAy+/SJ8tz//0sBAAAA","2005":"b:1vXPsA2Z+O1UACAy+/SJ8tz//0sBAAAA","2037":[28,96,147],"2044":[28,96,147]},"packages/frontend/editor-ui/src/features/ndv/parameters/components/ParameterInputFull.vue":{"63":"b:xvXPsIWZ+OlEACAi+/QJ8tz//0sBAAAA","97":"b:xvXPsIWZ+OlEACAi+/QJ8tz//0sBAAAA","108":"b:xvXPsIWZ+OlEACAi+/QJ8tz//0sBAAAA","115":"b:xvXPsIWZ+OlEACAi+/QJ8tz//0sBAAAA","119":"b:xvXPsIWZ+OlEACAi+/QJ8tz//0sBAAAA","123":"b:xvXPsIWZ+OlEACAi+/QJ8tz//0sBAAAA","129":"b:xvXPsIWZ+OlEACAi+/QJ8tz//0sBAAAA","136":"b:xvXOsISZ6MlEAAAi+/QI8sT//wsBAAAA","138":"b:xvXPsIWZ+OlEACAi+/QJ8tz//0sBAAAA","147":"b:xvXPsIWZ+OlEACAi+/QJ8tz//0sBAAAA","165":"b:AEHEgAQYCCgAAAAisgQIgEi8HgIAAAAA","173":"b:AEHEgAQYCCgAAAAiogQIgEi8GAIAAAAA","189":"b:wuXIsAUZSAhAACAistAIcNy+nwIAAAAA","193":"b:wuXAsAUZSAhAACAistAIcNy+nwIAAAAA","197":[138,140,141,14,146,147,148,153],"204":"b:AkHFkAUYCCgAAAAisAQJcFy8HAMAAAAA","208":"b:AEDEgAQYACgAAAACoAQIAECIEAAAAAAA","214":[139],"222":[139],"293":"b:xvXPsIWZ+OlEACAi+/QJ8tz//0sBAAAA","298":"b:xvXPsIWZ+OlEACAi+/QJ8tz//0sBAAAA","299":[148],"308":"b:wuXIsAUZSAhAACAistAIcNy+nwIAAAAA","313":[93,8,12,13],"317":[93],"354":"b:xvXPsIWZ+OlEACAi+/QJ8tz//0sBAAAA","439":"b:xvXPsIWZ+OlEACAi+/QJ8tz//0sBAAAA","443":[93,8,12,13],"456":"b:xvXPsIWZ+OlEACAi+/QJ8tz//0sBAAAA","477":"b:xvXPsIWZ+OlEACAi+/QJ8tz//0sBAAAA","510":[93,8,12,13]},"packages/frontend/editor-ui/src/features/ndv/parameters/components/ParameterInputList.vue":{"78":"b:guHGoAAASMBAAAAC4wAAIgT+ZwgAAAAA","81":"b:xvXGkASZ2ClEACAiq/AI8tT/7wMBAAAA","139":"b:xvXHsAWZ2OlEACAi6/AJ8tz/70sBAAAA","160":"b:xvXPsIWZ+OlEACAi+/QJ8tz//0sBAAAA","165":"b:xvXPsIWZ+OlEACAi+/QJ8tz//0sBAAAA","172":"b:xvXPsIWZ+OlEACAi+/QJ8tz//0sBAAAA","202":"b:xvXPsIWZ+OlEACAi+/QJ8tz//0sBAAAA","203":"b:xvXPsIWZ+OlEACAi+/QJ8tz//0sBAAAA","209":"b:xvXPsIWZ+OlEACAi+/QJ8tz//0sBAAAA","222":"b:xvXPsIWZ+OlEACAi+/QJ8tz//0sBAAAA","228":"b:xvXPsIWZ+OlEACAi+/QJ8tz//0sBAAAA","229":"b:xvXPsIWZ+OlEACAi+/QJ8tz//0sBAAAA","270":"b:xvXPsIWZ+OlEACAi+/QJ8tz//0sBAAAA","315":"b:xvXPsIWZ+OlEACAi+/QJ8tz//0sBAAAA","342":"b:BGAAAAAAACAAAAAgACABABgAEAIAAAAA","343":"b:BGAAAAAAACAAAAAgACABABgAEAIAAAAA","349":"b:xvXPsIWZ+OlEACAi+/QJ8tz//0sBAAAA","350":"b:xvXPsIWZ+OlEACAi+/QJ8tz//0sBAAAA","353":"b:xvXPsIWZ+OlEACAi+/QJ8tz//0sBAAAA","354":"b:xvXPsIWZ+OlEACAi+/QJ8tz//0sBAAAA","357":"b:xvXPsIWZ+OlEACAi+/QJ8tz//0sBAAAA","374":"b:xvXPsIWZ+OlEACAi+/QJ8tz//0sBAAAA","383":[23,101],"386":[23,101],"409":[23,101],"427":[22],"430":[22],"468":[1,7,8,10,108,124,13],"475":[1,124],"479":[1,124],"496":[23,101],"499":[23,101],"503":[23,101],"508":"b:AEHEgAQIACgAAAAiogQIAEicGAIAAAAA","512":"b:xvXPsIWZ+OlEACAi+/QJ8tz//0sBAAAA","519":"b:wGHOsIQISMBAACACo8AAEMCCLggBAAAA","526":"b:xvXPsIWZ+OlEACAi+/QJ8tz//0sBAAAA","530":"b:xvXPsIWZ+OlEACAi+/QJ8tz//0sBAAAA","545":"b:xvXPsIWZ+OlEACAi+/QJ8tz//0sBAAAA","573":"b:xvXPsIWZ+OlEACAi+/QJ8tz//0sBAAAA","590":"b:AkHFkAUYCCgAAAAisAQJcFy+HAMAAAAA","600":"b:xvXPsIWZ+OlEACAi+/QJ8tz//0sBAAAA","615":"b:xvXPsIWZ+OlEACAi+/QJ8tz//0sBAAAA","619":"b:xvXPsIWZ+OlEACAi+/QJ8tz//0sBAAAA","647":"b:xvXPsIWZ+OlEACAi+/QJ8tz//0sBAAAA","653":[1,7,8,10,108,124,13,14],"657":[1,7,8,10,108,124,13,14],"667":"b:xvXPsIWZ+OlEACAi+/QJ8tz//0sBAAAA","672":"b:wkEIIAQACMBAAAAgoBAAAAAACAAAAAAA","688":"b:xvXPsIWZ+OlEACAi+/QJ8tz//0sBAAAA","766":"b:xvXPsIWZ+OlEACAi+/QJ8tz//0sBAAAA","789":"b:xvXPsIWZ+OlEACAi+/QJ8tz//0sBAAAA","856":[1,7,8,10,108,124,13,14],"871":[14],"877":[1,7,8,10,108,124,13,14],"919":[153],"923":[153],"929":"b:xvXGsASZ2OlEACAi6/AI8tT/7wsBAAAA","952":"b:xvXGsASZ2OlEACAi6/AI8tT/7wsBAAAA","954":"b:xvXCsASY2OlEACAi6/AI8tT/7wsBAAAA","1077":"b:AEHEgAQICCgAAAAiogQIAEicGAIAAAAA"},"packages/frontend/editor-ui/src/features/ndv/parameters/components/ParameterInputWrapper.vue":{"70":"b:1vXPsI2Z+O1UACAy+/SJ8tz//0sBAAAA","74":"b:1vXPsI2Z+O1UACAy+/SJ8tz//0sBAAAA","85":"b:wmECIIAACMAAAAAAAxAAAgCACAgAAAAA","91":"b:1vXPsI2Z+O1UACAy+/SJ8tz//0sBAAAA","102":[54,58,138,139,140,143,145,147,148],"104":[145],"108":"b:1vXPsI2Z+O1UACAy+/SJ8tz//0sBAAAA","113":"b:1vXPsI2Z+O1UACAy+/SJ8tz//0sBAAAA","130":"b:1vXPsI2Z+O1UACAy+/SJ8tz//0sBAAAA","134":"b:1vXPsI2Z+O1UACAy+/SJ8tz//0sBAAAA","141":"b:kkHEoAwYCKwQAAAishSIgEi8HgIAAAAA","145":"b:kkHEoAwYCKwQAAAiohSIgEi8GAIAAAAA","153":"b:kkHFsA0YCKwQAAAisBSJcFy8HAMAAAAA","157":"b:kkHEoAwYCKwQAAACoBSIAECIGAAAAAAA","162":[93,8,12,13],"163":[58],"177":"b:1vXPsI2Z+O1UACAy+/SJ8tz//0sBAAAA","208":"b:1vXPsI2Z+O1UACAy+/SJ8tz//0sBAAAA"},"packages/frontend/editor-ui/src/features/ndv/parameters/components/ParameterOptions.vue":{"40":"b:1vXPsI2Z+O1UACAy+/SJ8tz//0sBAAAA","61":"b:1vXPsI2Z6O1UAAAy+/SJ8tz//wsBAAAA","62":"b:1vXPsI2Z6O1UAAAy+/SJ8tz//wsBAAAA","63":"b:1vXGsAyZ6M1UAAAS+/SI8tT//wsBAAAA","64":"b:1vXPsI2Z6O1UAAAy+/SJ8tz//wsBAAAA","67":"b:1vXPsI2Z+O1UACAy+/SJ8tz//0sBAAAA","77":"b:1vXPsI2Z+O1UACAy+/SJ8tz//0sBAAAA","96":"b:1vXPsI2Z+O1UACAy+/SJ8tz//0sBAAAA","115":"b:1vXGsAyZ6M1UAAAS+/SI8sT//wsBAAAA","116":"b:1vXPsI2Z6O1UAAAy+/SJ8tz//wsBAAAA","120":"b:1vXPsI2Z6O1UAAAy+/SJ8tz//wsBAAAA","128":"b:1vXPsI2Z6O1UAAAy+/SJ8tz//wsBAAAA","173":[148],"174":[58,138,140,141,14,146,147,148,153],"181":"b:1vXPsI2Z+O1UACAy+/SJ8tz//0sBAAAA","189":[148],"215":[148]},"packages/frontend/editor-ui/src/features/ndv/parameters/components/TextEdit.vue":{"10":"b:1vXPsI2Z+O1UACAy+/SJ8tz//0sBAAAA","31":"b:1vXPsI2Z+O1UACAy+/SJ8tz//0sBAAAA","41":"b:1vXPsI2Z+O1UACAy+/SJ8tz//0sBAAAA","42":"b:kkHHsA0YCKwQAAASsBCIcEyIGAAAAAAA","48":"b:1vXPsI2Z+O1UACAy+/SJ8tz//0sBAAAA","66":"b:1vXPsI2Z+O1UACAy+/SJ8tz//0sBAAAA"},"packages/frontend/editor-ui/src/features/ndv/parameters/utils/fromAIOverride.utils.ts":{"42":[93],"83":[12],"90":[93,8,12,13],"106":[93],"117":"b:xvXPsIWZ+OlEACAi+/QJ8tz//0sBAAAA","121":[93],"132":[93],"138":[93],"153":[93],"157":[93],"210":[8],"246":[93,8,12,13],"268":"b:xvXPsIWZ+OlEACAi+/QJ8tz//0sBAAAA","285":"b:xvXPsIWZ+OlEACAi+/QJ8tz//0sBAAAA","286":"b:xvXPsIWZ+OlEACAi+/QJ8tz//0sBAAAA","302":"b:xvXPsIWZ+OlEACAi+/QJ8tz//0sBAAAA","309":"b:xvXPsIWZ+OlEACAi+/QJ8tz//0sBAAAA","321":"b:xvXPsIWZ+OlEACAi+/QJ8tz//0sBAAAA"},"packages/frontend/editor-ui/src/features/ndv/runData/components/RunData.vue":{"103":"b:RgDAgGCbEAEAACQA4CQAANj+l0EAAAAA","104":[12,135,6,139,144],"106":"b:RNBBgACZsAEAAAAAAIQAoNj/t0EAAAAA","109":[1,6],"111":"b:RtDBgGCbsAEAACQA4CQAoNj+l0EAAAAA","236":"b:xvXHsAWZ+MFEACQC+/QB8tz//0sBAAAA","237":"b:xvXHsGWb+MFEACQC+/QB8tz//0sBAAAA","266":"b:QIDBgACZAAEAAAQA4AQAANj+lwEAAAAA","267":"b:xuXHsAWZ+MFEACQC+/QBctz//0sBAAAA","268":"b:xvXHsGWb+MFEACQC+/QB8tz//0sBAAAA","276":"b:QhAAAAAAAAAAAAAAAAQAAND9BwAAAAAA","277":"b:xvXHsGWb+MFEACQC+/QB8tz//0sBAAAA","283":"b:xvXHsGWb+MFEACQC+/QB8tz//0sBAAAA","284":"b:RtBBgACZsAEAAAAAAAQAoNj+l0EAAAAA","286":"b:xvXHsGWb+MFEACQC+/QB8tz//0sBAAAA","287":"b:RNDBgACZsAEAACAA4AQAINj+l0EAAAAA","288":"b:xvXHsGWb+MFEACQC+/QB8tz//0sBAAAA","289":"b:xvXHsGWb+MFEACQC+/QB8tz//0sBAAAA","290":"b:xvXHsGWb+MFEACQC+/QBctz//0sBAAAA","293":"b:xvXHsAWZ2MFEAAACu/QBctz//wsAAAAA","297":"b:xvXHsGWb+MFEACQC+/QBctz//0sBAAAA","305":"b:RtBBgACZsAEAAAAAAIQAoNj/t0EAAAAA","306":"b:xvXHsAWZ+MFEACAC+/QB8tz//0sBAAAA","307":"b:xvXHsAWZ+MFEACAC+/QB8tz//0sBAAAA","308":"b:QADAgACZEAEAACAA4AQAANj+l0EAAAAA","316":"b:RNBBgACZsAEAAAAAAIQAoNj/t0EAAAAA","326":"b:xvXHsGWb+MFEACQC+/QB8tz//0sBAAAA","332":"b:xvXHsGWb+MFEACQC+/QB8tz//0sBAAAA","336":"b:xtDBgGCbsAEAACQA4KQAoNj/t0EAAAAA","340":"b:RtDBgGCbsAEAACQA4KQAoNj/t0EAAAAA","349":"b:RtDBgGCbsAEAACQA4KQAoNj/t0EAAAAA","353":"b:RtDBgGCbsAEAACQA4KQAoNj/t0EAAAAA","363":"b:xvXHsGWb+MFEACQC+/QBctz//0sBAAAA","365":"b:xvXHsAWZ+MFEACAC+/QB8tz//0sBAAAA","374":"b:xvXHsGWb+MFEACQC+/QB8tz//0sBAAAA","383":"b:xtDBgGCbsAEAACQA4CQAoNj+l0EAAAAA","387":"b:xvXHsGWb+MFEACQC+/QB8tz//0sBAAAA","392":"b:xvXHsGWb+MFEACQC+/QB8tz//0sBAAAA","403":"b:xvXHsGWb+MFEACQC+/QBctz//0sBAAAA","409":"b:xvXHsGWb+MFEACQC+/QB8tz//0sBAAAA","416":"b:xvXHsGWb+MFEACQC+/QB8tz//0sBAAAA","426":"b:xvXHsGWb+MFEACQC+/QB8tz//0sBAAAA","429":"b:xvXHsGWb+MFEACQC+/QB8tz//0sBAAAA","439":"b:xvXHsGWb+MFEACQC+/QB8tz//0sBAAAA","443":"b:xvXHsGWb+MFEACQC+/QB8tz//0sBAAAA","447":"b:AICBgAAYAAAAAAQA4AQAAAj+lwAAAAAA","451":"b:xvXHsGWb+MFEACQC+/QB8tz//0sBAAAA","459":"b:xvXHsGWb+MFEACQC+/QB8tz//0sBAAAA","462":"b:QtBAgGCTsAEAACQAACAAoND+h0EAAAAA","463":"b:xvXHsGWb+MFEACQC+/QB8tz//0sBAAAA","471":"b:RgDAgGCbEAEAACQA4CQAANj+l0EAAAAA","472":"b:xvXHsGWb+MFEACQC+/QB8tz//0sBAAAA","476":"b:xvXHsGWb+MFEACQC+/QB8tz//0sBAAAA","481":[2,6,144,145,151],"491":[2,6,144,145,151],"497":"b:xvXHsGWb+MFEACQC+/QB8tz//0sBAAAA","501":"b:xvXHsGWb+MFEACQC+/QB8tz//0sBAAAA","502":"b:xvXHsGWb+MFEACQC+/QB8tz//0sBAAAA","503":"b:xvXHsGWb+MFEACQC+/QB8tz//0sBAAAA","507":"b:xvXHsGWb+MFEACQC+/QB8tz//0sBAAAA","508":"b:xvXHsGWb+MFEACQC+/QB8tz//0sBAAAA","518":"b:xvXHsGWb+MFEACQC+/QB8tz//0sBAAAA","528":[1,2,44,6,142,143,144,145],"529":[1,2,44,6,142,143,144,145],"569":"b:xvXHsGWb+MFEACQC+/QB8tz//0sBAAAA","573":"b:xuXHsAWZ6MFEAAAC+/QBctz//wsBAAAA","574":"b:xvXHsGWb+MFEACQC+/QB8tz//0sBAAAA","580":"b:xvXHsGWb+MFEACQC+/QB8tz//0sBAAAA","591":"b:RtDBgGCbsAEAACQA4KQAoNj/t0EAAAAA","595":"b:xvXHsGWb+MFEACQC+/QBctz//0sBAAAA","607":"b:xvXHsGWb+MFEACQC+/QB8tz//0sBAAAA","624":"b:xvXHsGWb+MFEACQC+/QB8tz//0sBAAAA","629":"b:xvXHsGWb+MFEACQC+/QB8tz//0sBAAAA","638":"b:QADAgACZEAEAACAA4AQAANj+l0EAAAAA","642":"b:xvXHsGWb+MFEACQC+/QB8tz//0sBAAAA","660":[2],"666":"b:xvXHsGWb+MFEACQC+/QB8tz//0sBAAAA","669":"b:xvXHsAWZ+MFEAAACu/QB8tz//0sAAAAA","672":"b:xvXHsGWb+MFEACQC+/QB8tz//0sBAAAA","674":"b:xvXHsGWb+MFEACQC+/QB8tz//0sBAAAA","677":"b:AAAAAACAQAAAAAAAAIAAABADpQAAAAAA","678":"b:AAAAAACAQAAAAAAAAIAAABADpQAAAAAA","695":"b:xvXHsGWb+MFEACQC+/QB8tz//0sBAAAA","702":"b:xvXHsGWb+MFEACQC+/QB8tz//0sBAAAA","706":"b:xvXHsGWb+MFEACQC+/QB8tz//0sBAAAA","707":[1,6],"710":"b:xvXHsGWb+MFEACQC+/QB8tz//0sBAAAA","714":[6],"720":"b:QmAAAEABAAAAAAQAAAAAABgIEgIAAAAA","725":"b:xoCBgAAYAAAAAAQA4CQAABj+lwAAAAAA","735":"b:xvXHsGWb+MFEACQC+/QB8tz//0sBAAAA","740":"b:RtDBgGCbkAEAACQA4CQAoNj+l0EAAAAA","747":"b:xvWBgEAZAAAAAAQA4DQAAFj+lwIAAAAA","756":"b:xuCBgEAZAAAAAAQA4CQAABj+lwIAAAAA","764":[15,16,6,143,145],"771":[142,144],"777":"b:xvXHsGWb+MFEACQC+/QB8tz//0sBAAAA","786":"b:xvXHsGWb+MFEACQC+/QB8tz//0sBAAAA","807":[134,6],"818":"b:xvXGEAUSyABEAAQA6/AB8tz/d0kBAAAA","823":"b:xvXHsGWb+MFEACQC+/QB8tz//0sBAAAA","839":[97,53,54,55,135,144,149,153],"869":"b:xvXHsGWb+MFEACQC+/QB8tz//0sBAAAA","908":[139,142,144,145],"930":"b:xvXHsGWb+MFEACQC+/QBctz//0sBAAAA","946":"b:AIABgAAAAAAAAAQAoAQAABj+FAAAAAAA","954":"b:xvXGEAUSyABEAAQA6/AB8tz/d0kBAAAA","962":"b:QICBgAAIAAAAAAQA4AQAAFj+FAAAAAAA","967":[132,136,138,139,140,141,145,146],"1003":[132,136,138,139,140,141,145,146],"1027":[132,138,139,140,141,145,146],"1054":[132,138,139,140,141,145,146],"1064":[134,146],"1118":[145],"1142":[2,144],"1150":[2,144],"1154":[2,144],"1158":[106],"1171":"b:RoCBgAAYAAAAAAQA4CQAABj8lwAAAAAA","1194":[1,12,135,6,139,142,143,144,145],"1225":[2,6,144,145,151],"1253":"b:xvXHsGWb+MFEACQC+/QB8tz//0sBAAAA","1278":"b:xvXHsGWb+MFEACQC+/QB8tz//0sBAAAA","1281":"b:AEAAAAAAAAAAACAAAAAAgFB+BwEAAAAA","1293":"b:xvXHsGWb+MFEACQC+/QB8tz//0sBAAAA","1299":[142,144],"1302":"b:xvXHsGWb+MFEACQC+/QB8tz//0sBAAAA","1320":"b:xvXHsGWb+MFEACQC+/QB8tz//0sBAAAA","1330":"b:xvXHsGWb+MFEACQC+/QB8tz//0sBAAAA","1357":"b:xvXHsGWb+MFEACQC+/QB8tz//0sBAAAA","1384":[1,2,44,6,142,143,144,145],"1402":"b:xvXHsGWb+MFEACQC+/QB8tz//0sBAAAA","1405":"b:RtDBgGCbkAEAACQA4CQAoNj+l0EAAAAA","1411":"b:xvXHsGWb+MFEACQC+/QB8tz//0sBAAAA","1418":[2,144,145,151],"1435":"b:xvXHsGWb+MFEACQC+/QBctz//0sBAAAA","1441":"b:xvXHsGWb+MFEACQC+/QBctz//0sBAAAA","1447":"b:xvXHsGWb+MFEACQC+/QBctz//0sBAAAA","1500":"b:AAAAAAAAAAAAAAAAAAAAAFB8BwAAAAAA","1504":"b:AAAAAAAAAAAAACAAAAAAAFB8BwAAAAAA","1512":"b:AAAAAAAAAAAAACAAAAAAAFB8BwAAAAAA","1527":"b:AAAAAAAAAAAAAAAAAAAAAFA9BgAAAAAA","1578":[132,136,138,139,140,141,145,146],"1591":[134,146],"1637":[2,144,145,151],"1639":[2,6,144,145,151],"1640":[2,6,144,145,151],"1641":[2,6,144,145,151],"1652":[2,144,145],"1686":[97,54,55,149,153],"1691":[97,54,55,149,153],"1763":"b:xoCBgAAYAAAAAAQA4CQAAAi+lwAAAAAA","1801":[136],"1807":[132,138,139,140,141,145,146],"1812":[132,136,138,139,140,141,145,146],"1814":[132,136,138,139,140,141,145,146],"1931":[52],"2073":"b:QtBAgGCTsAEAACQAACAAoND+h0EAAAAA","2116":[142],"2118":[142],"2141":"b:RgDAgECZEAEAACQA4CQAANj+l0EAAAAA"},"packages/frontend/editor-ui/src/features/ndv/runData/components/RunDataDisplayModeSelect.vue":{"18":"b:xvXHsGWb+MFEACQC+/QBctz//0sBAAAA","21":"b:xvXHsGWb+MFEACQC+/QBctz//0sBAAAA","45":"b:xvXHsGWb+MFEACQC+/QBctz//0sBAAAA","47":"b:xvXHsGWb+MFEACQC+/QBctz//0sBAAAA","53":"b:xvXHsGWb+MFEACQC+/QBctz//0sBAAAA","62":[12,135,6,139,142,143,144,145],"64":[82,1,7,2,37,38,40,41,109,6,158]},"packages/frontend/editor-ui/src/features/ndv/runData/components/RunDataItemCount.vue":{"16":"b:xpDBgGCbEAEAACQA4CQAANj+l0EAAAAA","17":"b:xpDBgGCaEAEAACQA4CQAANj+l0EAAAAA","21":[142,144],"29":"b:xpDBgGCbEAEAACQA4CQAANj+l0EAAAAA"},"packages/frontend/editor-ui/src/features/ndv/runData/components/RunDataSearch.vue":{"44":"b:RtDBgGCbsAEAACQA4CQAoNj+l0EAAAAA","50":"b:RtDBgGCbsAEAACQA4CQAoNj+l0EAAAAA","68":"b:RtDBgGCbsAEAACQA4CQAoNj+l0EAAAAA","72":"b:AEAAAAAAgAAAAAAAAAQAAAB8BQAAAAAA","99":[142,144],"104":[142,144],"109":[142,144],"115":[142,144],"121":[142,144],"134":"b:AEAAAAAAgAAAAAAAAAQAAAB8BQAAAAAA","152":"b:RtDBgGCbsAEAACQA4CQAoNj+l0EAAAAA","153":[142,144]},"packages/frontend/editor-ui/src/features/ndv/runData/components/RunDataTable.vue":{"87":"b:RADAgACZEAEAACAA4AQAANj+l0EAAAAA","88":"b:RgDAgGCbEAEAACQA4CQAANj+l0EAAAAA","89":"b:RgDAgGCbEAEAACQA4CQAANj+l0EAAAAA","91":"b:RgDAgECZEAEAACQA4CQAANj+l0EAAAAA","92":"b:RgDAgECZEAEAACQA4CQAANj+l0EAAAAA","93":[139,142,144,145],"96":"b:RgDAgGCbEAEAACQA4CQAANj+l0EAAAAA","97":"b:RgDAgGCbEAEAACQA4CQAANj+l0EAAAAA","105":"b:RgDAgGCbEAEAACQA4CQAANj+l0EAAAAA","116":"b:RgDAgGCbEAEAACQA4CQAANj+l0EAAAAA","153":[151],"165":[139,142,144,145],"187":[139,144,145],"194":[139],"198":[139],"202":[139,142],"208":"b:RgDAgECZEAEAACQA4CQAANj+l0EAAAAA","221":"b:RgDAgECZEAEAACQA4CQAANj+l0EAAAAA","228":"b:RAAAAAAQAAAAACAAgAAAAEBoAwAAAAAA","241":"b:RAAAAAAQAAAAACAAgAAAAEBoAwAAAAAA","254":"b:RgDAgECZEAEAACQA4CQAANj+l0EAAAAA","264":"b:RgDAgECZEAEAACQA4CQAANj+l0EAAAAA","286":[139],"296":[139],"304":[139],"310":"b:RAAAAAAQAAAAACAAgAAAAEBoAwAAAAAA","318":[139],"320":[139],"342":"b:RgDAgECZEAEAACQA4CQAANj+l0EAAAAA","351":"b:RAAAAAAQAAAAACAAgAAAAEBoAwAAAAAA","355":"b:RgDAgECZEAEAACQA4CQAANj+l0EAAAAA","359":"b:RgDAgGCbEAEAACQA4CQAANj+l0EAAAAA","371":"b:RgDAgGCbEAEAACQA4CQAANj+l0EAAAAA","396":"b:QAAAAAAQAAAAAAAAAAQAAIDIhwAAAAAA","414":"b:RgDAgECZEAEAACQA4CQAANj+l0EAAAAA","430":"b:RgDAgGCbEAEAACQA4CQAANj+l0EAAAAA","458":[103,106,139,145,146],"459":[103,106,139,145,146],"468":"b:RgDAgGCbEAEAACQA4CQAANj+l0EAAAAA","513":[37,41,6,143,144,146],"570":"b:RgDAgECZEAEAACQA4CQAANj+l0EAAAAA","582":"b:RgDAgECZEAEAACQA4CQAANj+l0EAAAAA","591":[139],"593":"b:RgDAgECZEAEAACQA4CQAANj+l0EAAAAA","596":"b:RgDAgECZEAEAACQA4CQAANj+l0EAAAAA","613":"b:RgDAgECZEAEAACQA4CQAANj+l0EAAAAA","669":"b:RgDAgECZEAEAACQA4CQAANj+l0EAAAAA","699":[151],"713":"b:RgDAgECZEAEAACQA4CQAANj+l0EAAAAA","731":"b:RAAAAAAQAAAAACAAgAAAAEBoAwAAAAAA","745":[139],"754":"b:RAAAAAAQAAAAACAAgAAAAEBoAwAAAAAA"},"packages/frontend/editor-ui/src/features/ndv/runData/components/RunInfo.vue":{"7":"b:RIBBgACZEAEAAAAA4AQAAMjql0EAAAAA","15":"b:RIBBgACZEAEAAAAA4AQAAMjql0EAAAAA","19":"b:RIBBgACZEAEAAAAA4AQAAMjql0EAAAAA","23":"b:RIBBgACZEAEAAAAA4AQAAMjql0EAAAAA","33":"b:RIBBgACZEAEAAAAA4AQAAMjql0EAAAAA"},"packages/frontend/editor-ui/src/features/ndv/runData/components/TextWithHighlights.vue":{"11":"b:RtDBgECZsAEAACQA4CQAoNj/t0EAAAAA","20":[142,144],"29":"b:RtDBgECZsAEAACQA4CQAoNj/t0EAAAAA"},"packages/frontend/editor-ui/src/features/ndv/runData/components/VirtualSchema.vue":{"107":"b:RNBBgACZsAEAAAAAAAQAoNj/t0EAAAAA","108":"b:RNBBgACZsAEAAAAAAAQAoNj/t0EAAAAA","114":[139],"117":[139],"123":"b:QNBBgACZsAEAAAAAAAQAoNj/t0EAAAAA","134":"b:QNBBgACZsAEAAAAAAAQAoNj/t0EAAAAA","143":"b:QNBBgACZsAEAAAAAAAQAoNj/t0EAAAAA","159":[6,143,145],"165":"b:QJBBgACZsAEAAAAAAAQAIMj/l0EAAAAA","176":"b:QJBBgACZsAEAAAAAAAQAIMj/l0EAAAAA","205":"b:QNBBgACZsAEAAAAAAAQAoNj/t0EAAAAA","209":"b:QNBBgACZsAEAAAAAAAQAoNj/t0EAAAAA","231":"b:QNBBgACZsAEAAAAAAAQAoNj/t0EAAAAA","254":"b:QNBBgACZsAEAAAAAAAQAoNj/t0EAAAAA","286":"b:RNBBgACZsAEAAAAAAAQAoNj/t0EAAAAA","298":"b:QJBBgACZsAEAAAAAAAQAIMiTt0AAAAAA","320":"b:RNBBgACZsAEAAAAAAAQAoNj/t0EAAAAA","366":"b:QNBBgACZMAEAAAAAAAQAoNj/t0EAAAAA","384":"b:QNBBgACZsAEAAAAAAAQAoNj/t0EAAAAA","388":[2,6,144],"403":"b:RNBBgACZsAEAAAAAAAQAoNj/t0EAAAAA","405":"b:RNBBgACZsAEAAAAAAAQAoNj/t0EAAAAA","431":"b:RNBBgACZsAEAAAAAAAQAoNj/t0EAAAAA","439":"b:RNBBgACZsAEAAAAAAAQAoNj/t0EAAAAA","443":"b:RNBBgACZsAEAAAAAAAQAoNj/t0EAAAAA","444":[144],"456":"b:RNBBgACZMAEAAAAAAAQAoNj/t0EAAAAA","461":"b:QNBBgACZMAEAAAAAAAQAoNj/t0EAAAAA","463":"b:QNBBgACZMAEAAAAAAAQAoNj/t0EAAAAA","466":"b:RNBBgACZMAEAAAAAAAQAoNj/t0EAAAAA","473":[139],"482":[139],"484":[139],"486":[139],"523":[144],"524":[144],"543":"b:RNBBgACZsAEAAAAAAAQAoNj/t0EAAAAA","564":[139],"574":[144],"579":[137,149],"602":[135],"628":[135,136,140]},"packages/frontend/editor-ui/src/features/ndv/runData/components/VirtualSchemaItem.vue":{"36":"b:RNBBgACZsAEAAAAAAAQAoNj/t0EAAAAA","72":"b:RNBBgACZsAEAAAAAAAQAoNj/t0EAAAAA","109":"b:QNBBgACZsAEAAAAAAAQAINivt0EAAAAA"},"packages/frontend/editor-ui/src/features/ndv/runData/schemaPreview.store.ts":{"12":"b:x/XH8GX/+MFFACQC+/QB+tz//3sBAAAA","22":"b:x5DFwGX/4AEBAAQA4KQAeNj/t3EAAAAA","31":"b:AAAAAACQ4AAAAAAAAIAAABATp0AAAAAA","50":"b:x5DFwGX/oAEBAAQA4CQAeMj/l3EAAAAA"},"packages/frontend/editor-ui/src/features/ndv/settings/components/NodeSettings.vue":{"139":"b:xvXHsAWZ+MFEAAAC+/QB8tz//0sBAAAA","169":"b:xvXHsAWZ+MFEACAC+/QB8tz//0sBAAAA","172":"b:xvXHsAWZ+MFEACAC+/QB8tz//0sBAAAA","173":[51],"176":"b:xvXHsAWZ+MFEACAC+/QB8tz//0sBAAAA","177":[51],"180":"b:xvXHsAWZ+MFEACAC+/QB8tz//0sBAAAA","183":"b:xvXHsAWZ+MFEACAC+/QB8tz//0sBAAAA","185":"b:xvXHsAWZ+MFEACAC+/QB8tz//0sBAAAA","191":"b:xvXHsAWZ+MFEACAC+/QB8tz//0sBAAAA","194":[132,136,138,139,140,141,145,146],"196":"b:xvXHsAWZ+MFEACAC+/QB8tz//0sBAAAA","198":"b:xuXHsAWZ+MFEACAC+/QB8tz//0sBAAAA","200":"b:xvXHsAWZ+MFEACAC+/QBctz//0sBAAAA","204":"b:xvXHsAWZ+MFEACAC+/QB8tz//0sBAAAA","209":"b:xvXHsAWZ+MFEACAC+/QB8tz//0sBAAAA","211":"b:xvXHsAWZ+MFEACAC+/QB8tz//0sBAAAA","215":"b:xvXHsAWZ+MFEACAC+/QB8tz//0sBAAAA","229":"b:xvXHsAWZ+MFEACAC+/QB8tz//0sBAAAA","243":"b:xvXHsAWZ+MFEACAC+/QB8tz//0sBAAAA","251":"b:xvXHsAWZ+MFEACAC+/QB8tz//0sBAAAA","255":"b:xvXHsAWZ+MFEACAC+/QB8tz//0sBAAAA","259":"b:wmXGsAQIaMBAACACu9AAUtCCLggBAAAA","263":"b:xvXHsAWZ+MFEACAC+/QB8tz//0sBAAAA","266":"b:wmXGsAQIaMBAACACu9AAUtCCLggBAAAA","267":"b:wmUCMAAIaMAAAAACuxAAQhCALggAAAAA","271":"b:xvXHsAWZ+MFEACAC+/QB8tz//0sBAAAA","275":"b:xvXHsAWZ+MFEACAC+/QB8tz//0sBAAAA","284":"b:xvXHsAWZ+MFEACAC+/QB8tz//0sBAAAA","287":"b:xvXHkAWZ2AFEACAC8/QB8tz//0sBAAAA","290":"b:xvXHsAWZ+MFEACAC+/QB8tz//0sBAAAA","292":[143],"313":"b:xvXHsAWZ+MFEACAC+/QB8tz//0sBAAAA","320":"b:xvXHsAWZ+MFEACAC+/QB8tz//0sBAAAA","329":"b:AmHFsAUYCMAAAAACsAQBcFy+HAMAAAAA","496":[13],"504":"b:xvXHsAWZ+MFEACAC+/QB8tz//0sBAAAA","507":"b:wuXCMAUYiMAEAAAC+9AB8ly9fAoBAAAA","513":"b:xvXHsAWZ+MFEACAC+/QB8tz//0sBAAAA","528":"b:xvXHsAWZ+MFEACAC+/QB8tz//0sBAAAA","529":"b:xvXHsAWZ+MFEACAC+/QB8tz//0sBAAAA","534":"b:gkHEgAQICAAAAAACogQAAEiUGAIAAAAA","535":"b:gkHAAAQICAAAAAACogAAAEiUGAIAAAAA","543":"b:AIABgAAIAAAAAAAAQAQAAAiqFwAAAAAA","549":[1,7,29,8,103,51,108,62,63,6,14,147],"573":"b:xvXHsAWZ+MFEACAC+/QB8tz//0sBAAAA","598":[143],"615":[2,109,131,132,13,14,148,153],"619":"b:xvXHsAWZ+MFEACAC+/QB8tz//0sBAAAA","630":"b:xvXGEAUQyABEAAAA6/AB8tz/d0kBAAAA","636":"b:wmXGsAQIaMBAACACu9AAUtCCLggBAAAA","802":"b:xvXHsAWZ+MFEACAC+/QB8tz//0sBAAAA"},"packages/frontend/editor-ui/src/features/ndv/settings/components/NodeSettingsHeader.vue":{"22":"b:xvXHsAWZ+MFEACAC+/QB8tz//0sBAAAA","40":[143]},"packages/frontend/editor-ui/src/features/ndv/settings/components/NodeSettingsHint.vue":{"13":"b:xvXHsAWZ+MFEACAC+/QBctz//0sBAAAA","16":"b:xvXHsAWZ+MFEACAC+/QBctz//0sBAAAA","68":"b:xvXHsAWZ+MFEACAC+/QBctz//0sBAAAA","72":[143],"77":[143]},"packages/frontend/editor-ui/src/features/ndv/settings/components/NodeSettingsTabs.vue":{"29":"b:xvXHsAWZ+MFEACAC+/QB8tz//0sBAAAA","45":[112,127],"49":"b:xvXHsAWZ+MFEACAC+/QB8tz//0sBAAAA","53":[100],"55":"b:xvXHsAWZ+MFEACAC+/QB8tz//0sBAAAA","63":"b:xvXHsAWZ+MFEACAC+/QB8tz//0sBAAAA","130":[143],"161":"b:xvXHsAWZ+MFEACAC+/QB8tz//0sBAAAA"},"packages/frontend/editor-ui/src/features/ndv/settings/components/NodeWebhooks.vue":{"21":"b:xvXHsAWZ+MFEACAC+/QB8tz//0sBAAAA","38":"b:AAGAEAAAAAAAACACoQAAAICCDAABAAAA","45":"b:AACAEAAAAAAAACACoAAAAICCBAABAAAA","53":"b:xvXHsAWZ+MFEACAC+/QB8tz//0sBAAAA","58":"b:wEHCEAAAQAAAACACowAAAICCDgABAAAA","70":"b:xvXHsAWZ+MFEACAC+/QB8tz//0sBAAAA","124":"b:AAGAEAAAAAAAACACoQAAAICCDAABAAAA","192":"b:xvXHsAWZ+MFEACAC+/QB8tz//0sBAAAA","193":[131,132,13,14,148,153],"200":"b:xvXHsAWZ+MFEACAC+/QB8tz//0sBAAAA","218":"b:AACAEAAAAAAAACACoAAAAICCBAABAAAA","224":"b:AACAEAAAAAAAACACoAAAAICCBAABAAAA"},"packages/frontend/editor-ui/src/features/ndv/settings/composables/useNodeSettingsParameters.ts":{"59":"b:1vXPsI2Z+O1UACAy+/SJ8tz//0sBAAAA","71":"b:AmHFsAUYCMAAAAACsAQBcFy+HAMAAAAA","139":[14],"194":"b:xvXPsIWZ+OlEACAi+/QJ8tz//0sBAAAA"},"packages/frontend/editor-ui/src/features/ndv/shared/composables/useBinaryDataAccessTooltip.ts":{"9":"b:1vXPsI2Z+O1UACAy+/SJ8tz//0sBAAAA"},"packages/frontend/editor-ui/src/features/ndv/shared/ndv.store.ts":{"41":"b://Xf/P///+39//////////////8DAAAC","45":"b://Xf/P///+39//////////////8DAAAC","46":"b://Xf/P///+39//////////////8DAAAC","109":"b://Xf/P///+39//////////////8DAAAC","113":"b:1vXHsA2Z+MVUACAC+/SB8tz//0sBAAAC","138":"b:1vXPsI2Z+O1UACAy+/SJ8tz//0sBAAAC","142":"b:1vXHsA2Z+MVUACAC+/SB8tz//0sBAAAC","149":"b:kkHEoAwYCIQQAAACoBSAgFC8HgAAAAAC","153":"b:xvXPsIWZ6M1EAAAi+/QJ8tz//wsBAAAC","155":[185,139],"157":[185,139],"159":"b:RtDBgECZsAEAACQA4CQAoNj/t0EAAAAC","161":"b:xvXPsGWb+MFMACQC+/QB8tz//0sBAAAC","163":"b:RtDBgECZsAEAACQA4CQAoNj/t0EAAAAC","165":"b:QNBBgACZMAEAAAAAAAQAoNj/t0EAAAAC","185":"b:QuEBAACAYAEAAAAAAAAAAJi8HgAAAAAC","187":"b:QuEAAACAYAEAAAAAAAAAAJC8HgAAAAAC","189":"b:AEAAAAAAAAQAAAAAAAQAAFA8HgAAAAAC","191":[185],"193":"b:1vXPsI2Z+O1UACAy+/SJ8tz//0sBAAAC","206":"b:1vXPsI2Z+O1UACAy+/SJ8tz//0sBAAAC","214":"b:1vXPsI2Z+O1UACAy+/SJ8tz//0sBAAAC","231":"b:x/Xf8G3/+cHdYDwC//0B//////8DAAAC","233":"b:xvXeEAUQyCBMIAAA+/ABctz/d2kBAAAC","238":"b:xvXHsAWZ+OlEACAC+/QJctz//0sBAAAC","247":"b:xvXHsAWZ+MFEAAACu/QBctz//0sAAAAC","251":[2,185,144,145],"255":"b:xvXHsAWZ+MFEACAC+/QBctz//0sBAAAC","259":[185],"269":"b:xvXHsAWZ+MFEACAC+/QBctz//0sBAAAC","273":"b:wrXOEAUQyABMAAAA69ABctz/d0kBAAAC","277":"b:QBAAAAAAAAAAAAAAAAAAAJDIAwAAAAAC","288":"b:AAAAAAAAAAAAAAAAAAAAABA9BgAAAAAC","292":"b:AAAAAAAAAAAAAAAAAAAAABA9BgAAAAAC","296":"b:AEHEgAQYCAgAAAACoAQAgEC8HgIAAAAC","300":[185,139],"318":[185,139],"328":[185,139],"332":[185,139],"336":[185,139],"340":[185,139,142,144,145],"345":"b:xvXHsGWb+MFEACQC+/QB8tz//0sBAAAC","353":"b:xvXHsGWb+MFEACQC+/QB8tz//0sBAAAC","361":[185,139],"366":[185,139,142,144,145],"371":[185],"376":"b:AEDEgAQYAAQAAAACoAAAAFA8HgAAAAAC","380":[185],"393":"b:AEHEgAQYCCgAAAAisgQIgEi8HgIAAAAC","470":"b://Xf/P///+39//////////////8DAAAC","474":"b:AAAAACADCAAIAAAgAAAAAOAAQGACAAAC","498":"b:1/Xf8O3/+e3dYDQy//2J+/////8DAAAC","501":"b:1/Xf8O3/+e3dYDQy//2J+/////8DAAAC","525":"b:1/Xf8O3/+e3dYCwy//2J8/////8DAAAC","528":"b:1/Xf8O3/+e3dYCwy//2J8/////8DAAAC"},"packages/frontend/editor-ui/src/features/ndv/shared/ndv.utils.ts":{"41":"b:xvXHsAWZ+MFEACAC+/QB8tz//0sBAAAA","57":"b:AmHFsAUYCMAAAAACsAQBcFy+HAMAAAAA","132":"b:AmHFsAUYCMAAAAACsAQBcFy+HAMAAAAA","261":"b:AkHFsAUYCMAAAAACsAQBcFy+HAMAAAAA","267":"b:AkHFsAUYCMAAAAACsAQBcFy+HAMAAAAA","289":[14,147],"290":[14,147],"293":[14],"307":"b:AmHFsAUYCMAAAAACsAQBcFy+HAMAAAAA","328":"b:1vXPsI2Z+O1UACAy+/SJ8tz//0sBAAAA","335":"b:1vXPsI2Z+O1UACAy+/SJ8tz//0sBAAAA","339":"b:1vXPsA2Z+O1UACAy+/SJ8tz//0sBAAAA","361":"b:AmHFsAUYCMAAAAACsAQBcFy+HAMAAAAA","367":"b:AEAAAAAAAAQAAAAAAAAAAAA8HAIAAAAA","446":"b:xvXHsAWZ+OlEACAi+/QJ8tz//0sBAAAA","457":"b:xvXHsAWZ+MFEACAi+/QB8tz//0sBAAAA","621":"b:xvXHsAWZ+MFEACAC+/QB8tz//0sBAAAA","733":"b:xvXHsAWZ+MFEACAi+/QB8tz//0sBAAAA"},"packages/frontend/editor-ui/src/features/ndv/shared/views/NodeDetailsViewV2.vue":{"73":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAA","93":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAA","110":"b:xvXPsAWZ+cFMACAC+/QBctz//0sBAAAA","112":"b:xvXHsAWZ+cFEACAC+/QBctz//0sBAAAA","116":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAA","125":[15,16,48,106,131,139,143,144,146],"127":"b:xvXHsAWZ+cFEACAC+/QBctz//0sBAAAA","141":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAA","146":"b:xuXHsAWZ+MFEAAACu/QBctz//0sAAAAA","153":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAA","166":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAA","172":"b:xvXHsAWZ+MFEACAC+/QBctz//0sBAAAA","193":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAA","200":"b:xvXHsAWZ+cFEACAC+/QBctz//0sBAAAA","202":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAA","206":"b:xvXHsAWZ+cFEACAC+/QBctz//0sBAAAA","220":[23,101,103,48,135],"226":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAA","231":"b:xvXHsAWZ+cFEACAC+/QBctz//0sBAAAA","233":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAA","251":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAA","257":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAA","278":"b:xvXHsAWZ+MFEAAACu/QBctz//0sAAAAA","293":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAA","304":"b:xvXHsAWZ+cFEACAC+/QBctz//0sBAAAA","308":"b:xvXHsAWZ+cFEACAC+/QBctz//0sBAAAA","310":"b:wrXOEAUQyQBMAAAA69ABctz/d0kBAAAA","312":"b:xvXHsAWZ+cFEACAC+/QBctz//0sBAAAA","314":"b:xvXHsAWZ+cFEACAC+/QBctz//0sBAAAA","318":"b:xvXHsAWZ+cFEACAC+/QBctz//0sBAAAA","320":"b:xvXHsAWZ+cFEACAC+/QBctz//0sBAAAA","324":"b:xvXHsAWZ6cFEAAAC+/QBctz//wsBAAAA","326":"b:xvXHsAWZ+cFEAAACu/QBctz//0sAAAAA","328":"b:xvXHsAWZ+cFEACAC+/QBctz//0sBAAAA","330":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAA","332":"b:xvXHsAWZ+cFEACAC+/QBctz//0sBAAAA","336":"b:xvXHsAWZ+cFEACAC+/QBctz//0sBAAAA","348":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAA","359":"b:AIABgAAAAQAAAAQAoAQAABj+FAAAAAAA","363":[48,6],"367":"b:AmEEMAAAgQAAAAAACZQAcBT9XQABAAAA","373":[48,139,144,145],"388":[48,6,139,142,143,144,145],"392":[48],"397":[48,139,142,144,145],"412":[48],"427":[48],"431":[48,144],"436":"b:xvXHsAUY6cBEAAACu/QBcly//woAAAAA","442":"b:AIABgAAIAQAAAAAAQAQAAAiqFwAAAAAA","443":[15,16,48,106,131,139,143,144,146],"451":[48],"455":"b:xvXHsAUY6cBEAAACu/QBcly//woAAAAA","464":[2,48,144],"470":[48,131,132,13,148],"474":[48,13],"478":"b:wrXOEAUQyQBMAAAA69ABctz/d0kBAAAA","518":"b:xvXHsAWZ+cFEAAACu/QBctz//0sAAAAA","527":[2,48,144,145,151],"532":"b:xvXHsAWZ+cFEAAACu/QBctz//0sAAAAA","540":"b:RADAgACZEQEAACAA4AQAANj+l0EAAAAA","544":[48,139,145],"558":[48],"562":"b:RHAAAAAAAQAAAAAAAAAAEAjICwAAAAAA","567":"b:xvXHsAWZ+cFEACAC+/QBctz//0sBAAAA","572":[48,142,144],"576":"b:xvXHsAWZ+cFEACAC+/QBctz//0sBAAAA","580":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAA","584":[48],"588":"b:QBAAAAAAAQAAAAAAAAAAAJDIAwAAAAAA","594":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAA","612":"b:xvXHsAWZ+MFEACAC+/QBctz//0sBAAAA","622":"b:xuHDAAUQyABAAAACoBAActz+PwoAAAAA","660":[2,48,6,144,145,151],"664":[2,48,144,145],"668":"b:xvXHsAWZ+cFEAAACu/QBctz//0sAAAAA","669":"b:xvXHsAWZ+cFEAAACu/QBctz//0sAAAAA","674":[2,48,144,145],"675":[2,48,144,145],"680":"b:xvXHsAWZ+cFEACAC+/QBctz//0sBAAAA","685":"b:xvXHsAWZ+cFEACAC+/QBctz//0sBAAAA","688":"b:xvXHsAWZ+cFEACAC+/QBctz//0sBAAAA","709":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAA","715":"b:QgAAACADCQAJAAAAAAAAQuAAQHACAAAA","789":"b:xvXHsAUY6MBEAAACu/QBcly//woAAAAA","797":[12,6,139,142,143,144,145],"815":"b:xvXHsAWZ+MFEACAC+/QBctz//0sBAAAA","856":[144],"863":[132,135,144,145]},"packages/frontend/editor-ui/src/features/resolvers/composables/useDynamicCredentials.ts":{"13":"b:3/Xf9P///+39f4//////8/////8DAAAA","17":"b:3/Xf9P///+39f4//////8/////8DAAAA"},"packages/frontend/editor-ui/src/features/resolvers/composables/useNodePrivateCredential.ts":{"14":"b:x/XP8OX/+MFdYDQC//wB//3///sDAAAC","20":"b:x/XP8OX/+MFdYDQC//wB//3///sDAAAC","22":"b:x/XP8OX/+MFdYDQC//wB//3///sDAAAC","26":"b:wkEIIASkCMBQAAAAoBAAAAAACAAAAAAC","32":"b:x/XP8OX/+MFdYDQC//wB//3///sDAAAC","41":"b:x/XP8OX/+MFdYDQC//wB//3///sDAAAC","47":[185]},"packages/frontend/editor-ui/src/features/resolvers/composables/usePrivateCredentials.ts":{"15":"b:1/XP8P///8V9YDRy//+B//3///sDAAAA"},"packages/frontend/editor-ui/src/features/settings/communityNodes/communityNodes.store.ts":{"12":"b:xvXHsAWZ+MFEACAi+/QD8tz//0sBAAAA","22":[100,113],"30":[113],"36":[100,113],"37":[113],"46":[100,113],"55":[100,113],"60":[113],"74":[113],"79":[113],"84":[113],"88":[113],"103":[100]},"packages/frontend/editor-ui/src/features/settings/communityNodes/composables/useInstalledCommunityPackage.ts":{"10":"b:xvXHsAWZ+MFEACAC+/QB8tz//0sBAAAA","16":"b:xvXHsAWZ+MFEACAC+/QB8tz//0sBAAAA","17":"b:xvXHsAWZ+MFEACAC+/QB8tz//0sBAAAA","25":[100],"32":"b:xvXHsAWZ+MFEACAC+/QB8tz//0sBAAAA","34":[2,100,109,13],"41":"b:xvXHsAWZ+MFEACAC+/QB8tz//0sBAAAA","51":"b:xvXHsAWZ+MFEACAC+/QB8tz//0sBAAAA"},"packages/frontend/editor-ui/src/features/settings/environments.ee/environments.store.ts":{"13":"b:1/Xf/P///+39f7/z//+N//////8DAAAC","18":"b:1vXPsI2Z+O1UACAy+/SJ8tz//0sBAAAC","21":"b:AAAADBAAD8BsAINBAAMEACAAQAADAAAC","22":[185,114],"29":"b:1vXPsI2Z+O1UACAy+/SJ8tz//0sBAAAC","30":[185,140],"35":[185,114,120,140,154],"43":[185,114],"46":[185],"56":[185,114],"59":[185],"64":[185,114],"69":[185,114],"74":[185,114],"79":"b:1vXPsI2Z+O1UACAy+/SJ8tz//0sBAAAC","83":[185]},"packages/frontend/editor-ui/src/features/settings/sso/sso.store.ts":{"18":"b://Xf/P///+39//////////////8DAAAA","25":[87,88,51,81],"38":"b://Xf/P///+39//////////////8DAAAA","90":[87,88,90,51,116,81,117],"153":[87,88,51,81,117],"174":[87,88,51,81]},"packages/frontend/editor-ui/src/features/settings/usage/usage.store.ts":{"38":"b:x/Xf/P3//8H9YL9D//8B+/////8DAAAA","49":[83],"74":"b:x/Xf/P3//8H9YD9C//8B+/////8DAAAA","79":"b:x/Xf/P3//8H9YL9D//8B+/////8DAAAA"},"packages/frontend/editor-ui/src/features/setupPanel/setupPanel.store.ts":{"10":"b://Xf/P///+39//////////////8DAAAA","14":"b:x/Xf8G3/+MHdYAQC//0B8/////8DAAAA","20":"b:x/Xf8O3/+MHdYDQC//0B//////8DAAAA","22":[19],"29":[19],"34":[19]},"packages/frontend/editor-ui/src/features/shared/banners/banners.store.ts":{"14":"b://Xf/P///+39//////////////8DAAAA","32":"b://Xf/P///+39/4//////8/////8DAAAA","96":"b://Xf/P///+39//////////////8DAAAA","100":[71],"105":[51,81],"113":"b://Xf/P///+39/4//////8/////8DAAAA"},"packages/frontend/editor-ui/src/features/shared/banners/components/BannerStack.vue":{"38":"b://Xf/P///+39//////////////8DAAAA","46":"b://Xf/P///+39//////////////8DAAAA","51":[71],"55":[71],"56":[71],"59":"b://Xf/P///+39//////////////8DAAAA","65":[71],"86":"b://Xf/P///+39//////////////8DAAAA","89":"b://Xf/P///+39//////////////8DAAAA"},"packages/frontend/editor-ui/src/features/shared/commandBar/composables/useChatHubCommands.ts":{"28":"b://Xf/P///+39//////////////8DAAAA","40":[3,4,5,91,93,33],"47":[3,4,5,91,93,33],"81":[3,4,5,91,93,33],"87":[3,4,5,91,93,33],"98":[3,4,5,91,93,33],"101":[3,4,5,91,93,33],"128":[3,4,5,91,93,33],"150":[3,4,5,91,93,33],"151":[3,4,5,91,93,33],"154":[3,4,5,91,93,33],"175":[3,4,5,91,93,33],"176":[3,4,5,91,93,33],"179":[3,4,5,91,93,33],"199":[3,4,5,91,93,33],"200":[3,4,5,91,93,33],"203":[3,4,5,91,93,33]},"packages/frontend/editor-ui/src/features/shared/commandBar/composables/useCommandBar.ts":{"31":"b://Xf/P///+39//////////////8DAAAA","45":"b://Xf/P///+39/8//////8/////8DAAAA","52":"b://Xf/P///+39/4//////8/////8DAAAA","188":"b://Xf/P///+39/8//////8/////8DAAAA","222":"b://Xf/P///+39/8//////8/////8DAAAA","244":[160],"253":"b://Xf/P///+39/8//////8/////8DAAAA","259":[160],"266":"b://Xf8O//+cHdfwSq//0B8/////8DAAAA","284":"b://Xf/P///+39/8//////8/////8DAAAA","285":"b://Xf/P///+39/8//////8/////8DAAAA","289":"b://Xf/P///+39/8//////8/////8DAAAA","290":"b://Xf/P///+39/8//////8/////8DAAAA","293":[160],"311":"b://Xf/P///+39/8//////8/////8DAAAA","314":"b://Xf/P///+39f4//////8/////8DAAAA"},"packages/frontend/editor-ui/src/features/shared/commandBar/composables/useCredentialNavigationCommands.ts":{"22":"b://Xf/P///+39//////////////8DAAAA","40":[160],"41":[160],"44":"b://Xf/P///+39/8//////8/////8DAAAA","46":[160],"56":[160],"62":[160],"119":"b://Xf/P///+39/8//////8/////8DAAAA","123":"b://Xf/P///+39/8//////8/////8DAAAA","130":"b://Xf/P///+39/8//////8/////8DAAAA","192":[160],"215":"b:OAECLP4DH+z0X4v9AAP+AAAAQAADAAAA"},"packages/frontend/editor-ui/src/features/shared/commandBar/composables/useDataTableNavigationCommands.ts":{"20":"b://Xf/P///+39//////////////8DAAAA","44":"b://Xf/P///+39/8//////8/////8DAAAA","50":[160],"58":[160],"124":"b:3/Xf/P///+39/4//////8/////8DAAAA","128":"b://Xf/P///+39/8//////8/////8DAAAA","135":"b://Xf/P///+39/8//////8/////8DAAAA","186":[160]},"packages/frontend/editor-ui/src/features/shared/commandBar/composables/useExecutionCommands.ts":{"27":"b://Xf/P///+39//////////////8DAAAA","38":[52,126,134,135,64,6,156,157,158],"40":[52,126,134,135,64,6,156,157,158],"46":[52,126,134,135,64,6,156,157,158],"51":[52,126,134,135,64,6,156,157,158],"73":[52,126,134,135,64,6,156,157,158],"79":[52,126,134,135,64,6,156,157,158],"209":[52,126,134,135,64,6,156,157,158]},"packages/frontend/editor-ui/src/features/shared/commandBar/composables/useExecutionNavigationCommands.ts":{"10":"b://Xf/P///+39//////////////8DAAAA","28":"b://Xf/P///+39/8//////8/////8DAAAA"},"packages/frontend/editor-ui/src/features/shared/commandBar/composables/useGenericCommands.ts":{"31":"b://Xf/P///+39//////////////8DAAAA","40":"b://Xf/P///+39/8//////8/////8DAAAA"},"packages/frontend/editor-ui/src/features/shared/commandBar/composables/useInstanceAiCommands.ts":{"20":"b://Xf/P///+39//////////////8DAAAA","27":"b://Xf/P///+39/8//////8/////8DAAAA","33":[37,72,73,74,38,95,39,75,40,41,76],"42":[37,72,73,74,38,95,39,75,40,41,76],"43":[37,72,73,74,38,95,39,75,40,41,76],"54":"b://Xf/P///+39/8//////8/////8DAAAA","101":"b://Xf/P///+39f4//////8/////8DAAAA"},"packages/frontend/editor-ui/src/features/shared/commandBar/composables/useNodeCommands.ts":{"26":"b://Xf/P///+39//////////////8DAAAA","43":"b:x/Xf8A38+cHdIAQC//0B8/////8DAAAA","46":"b:x/Xf8A38+cHdIAQC//0B8/////8DAAAA","48":"b:x/Xf8A38+cHdYAQC//0B8/////8DAAAA","52":"b:x/Xf8A38+cHdYAQC//0B8/////8DAAAA","56":"b:x/Xf8A38+cHdYAQC//0B8/////8DAAAA","62":"b:x/Xf8A38+cHdYAQC//0B8/////8DAAAA","91":"b:x/Xf8A38+cHdYAQC//0B8/////8DAAAA","96":"b:x/Xf8A38+cHdYAQC//0B8/////8DAAAA","99":"b:x/Xf8A38+cHdYAQC//0B8/////8DAAAA","107":"b:x/XP8AX8+MFdYAQC//wB8/////sDAAAA","136":"b:x/Xf8A38+cHdYAQC//0B8/////8DAAAA","137":"b:x/XP8AX8+MFdYAQC//wB8/////sDAAAA","140":"b:x/Xf8A38+cHdYAQC//0B8/////8DAAAA","148":"b:x/Xf8A38+cHdYAQC//0B8/////8DAAAA"},"packages/frontend/editor-ui/src/features/shared/commandBar/composables/useProjectNavigationCommands.ts":{"16":"b://Xf/P///+39//////////////8DAAAA","27":[160],"72":"b:EAAADAYACIQEAIBAAAIgAAAAAAABAAAA","78":"b://Xf/P///+39/8//////8/////8DAAAA","84":"b://Xf/P///+39/8//////8/////8DAAAA","125":[160]},"packages/frontend/editor-ui/src/features/shared/commandBar/composables/useRecentResources.ts":{"32":"b://Xf/P///+39//////////////8DAAAA","43":"b://Xf/P///+39f7////////////8DAAAA","58":"b:x/Hf0AX8+cFdAAQA4CUA8/3/338DAAAA","59":"b:RvHGkAGQ6IFMAAAA4CQAcv3fx2sBAAAA","70":"b:RvHHkAGY6IFAAAAA4CQActz/3wsBAAAA","73":"b:QrEAAAAQSABAAAAAgAAAYNzfRwIAAAAA","89":"b://Xf/P///+39/8//////8/////8DAAAA","177":"b://Xf/P///+39f4//////8/////8DAAAA","181":"b:R3AAQAC0WcBNAAQAICUA4KHLw3EBAAAA"},"packages/frontend/editor-ui/src/features/shared/commandBar/composables/useWorkflowCommands.ts":{"51":"b://Xf/P///+39//////////////8DAAAA","68":"b:x/Xf8A38+cHdIAQC//0B8/////8DAAAA","71":"b:x/Xf8A38+cHdYAQC//0B8/////8DAAAA","73":"b:x/Xf8A38+cHdYAQC//0B8/////8DAAAA","77":"b:x/Xf8A38+cHdYAQC//0B8/////8DAAAA","81":"b:x/Xf8A38+cHdYAQC//0B8/////8DAAAA","84":"b:x/XP8AX8+MFdYAQC//wB8/////sDAAAA","85":"b:wkEIIASkCMBQAAAAoBAAAAAACAAAAAAA","97":"b:wkEIIASkCMBQAAAAoBAAAAAACAAAAAAA","117":"b:x/Xf8A38+cHdYAQC//0B8/////8DAAAA","302":"b:x/Xf8A38+cHdYAQC//0B8/////8DAAAA","304":"b:x/XP8AX8+MFdYAQC//wB8/////sDAAAA","305":[51,151,152,153],"306":[51,151,152,153],"312":[51,152,153],"322":[51,152,153],"344":"b:x/Xf8A38+cHdYAQC//0B8/////8DAAAA","385":"b:x/Xf8A38+cHdYAQC//0B8/////8DAAAA","422":"b:x/Xf8A38+cHdYAQC//0B8/////8DAAAA","476":"b:x/Xf8A38+cHdYAQC//0B8/////8DAAAA","486":"b:x/Xf8A38+cHdYAQC//0B8/////8DAAAA"},"packages/frontend/editor-ui/src/features/shared/commandBar/composables/useWorkflowNavigationCommands.ts":{"29":"b://Xf/P///+39//////////////8DAAAA","55":"b://Xf/P///+39/8//////8/////8DAAAA","57":[160],"65":[160],"74":[160],"80":[160],"161":[160],"162":[160],"167":[160],"168":[160],"206":[160],"220":[160],"242":[160],"302":[160],"312":"b://Xf/P///+39/8//////8/////8DAAAA","313":[160],"316":"b://Xf/P///+39/8//////8/////8DAAAA","320":[160],"323":"b://Xf/P///+39/8//////8/////8DAAAA","377":[160],"402":"b://Xf/P///+39f4//////8/////8DAAAA"},"packages/frontend/editor-ui/src/features/shared/contextMenu/components/ContextMenu.vue":{"17":"b:x/Xf8O3/+cHdYDQC//0B//////8DAAAC","26":"b:AgAAAAAAAAAAAAAAACAAYAcCBAEAAAAC","30":"b:AgAAAAAAAAAAAAAAACAAYAcCBAEAAAAC","51":"b:AgAAAAAAAAAAAAAAACAAYAcCBAEAAAAC","59":"b:AgAAAAAAAAAAAAAAACAAYAcCBAEAAAAC","66":"b:AgAAAAAAAAAAAAAAACAAYAcCBAEAAAAC","69":"b:x/Xf8O3/+cHdYDQC//0B//////8DAAAC","77":"b:AgAAAAAAAAAAAAAAACAAYAcCBAEAAAAC","93":"b:AgAAAAAAAAAAAAAAACAAYAcCBAEAAAAC","100":"b:AgAAAAAAAAAAAAAAACAAYAcCBAEAAAAC","101":"b:AgAAAAAAAAAAAAAAACAAYAcCBAEAAAAC"},"packages/frontend/editor-ui/src/features/shared/contextMenu/composables/contextMenuGroupView.ts":{"20":"b:x/Xf8O3/+cHdYDQC//0B//////8DAAAC"},"packages/frontend/editor-ui/src/features/shared/contextMenu/composables/useContextMenu.ts":{"25":[185],"29":"b:x/Xf8O3/+cHdYDQC//0B//////8DAAAC","31":"b:x/Xf8O3/+cHdYDQC//0B//////8DAAAC","33":"b:AgAAAAAAAAAAAAAAACAAYAcCBAEAAAAC","42":"b:AgAAAAAAAAAAAAAAACAAYAcCBAEAAAAC","46":"b:AgAAAAAAAAAAAAAAACAAYAcCBAEAAAAC","48":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAC","53":"b:AgAAAAAAAAAAAAAAACAAYAcCBAEAAAAC","74":"b:x/Xf8O3/+cHdYDQC//0B//////8DAAAC","80":"b:AgAAAAAAAAAAAAAAACAAYAcCBAEAAAAC"},"packages/frontend/editor-ui/src/features/shared/contextMenu/composables/useContextMenuItems.ts":{"71":"b:AgAAAAAAAAAAAAAAACAAYAcCBAEAAAAC","77":"b:x/Xf8O3/+cHdYDQC//0B//////8DAAAC","97":"b:AgAAAAAAAAAAAAAAACABYAcCBAEAAAAC","101":"b:AgAAAAAAAAAAAAAAACABYAcCBAEAAAAC","111":[185],"121":"b:AgAAAAAAAAAAAAAAACABYAcCBAEAAAAC","123":"b:AgAAAAAAAAAAAAAAACABYAcCBAEAAAAC","129":"b:AgAAAAAAAAAAAAAAACABYAcCBAEAAAAC","131":"b:AgAAAAAAAAAAAAAAACABYAcCBAEAAAAC","132":"b:AgAAAAAAAAAAAAAAACABYAcCBAEAAAAC","138":"b:AgAAAAAAAAAAAAAAACABYAcCBAEAAAAC","146":"b:AgAAAAAAAAAAAAAAACABYAUCBAEAAAAC","150":"b:AgAAAAAAAAAAAAAAACABYAcCBAEAAAAC","165":"b:AgAAAAAAAAAAAAAAACAAYAcCBAEAAAAC","174":"b:AgAAAAAAAAAAAAAAACABYAUCBAEAAAAC","179":"b:AgAAAAAAAAAAAAAAACABYAcCBAEAAAAC","186":"b:AgAAAAAAAAAAAAAAACABYAcCBAEAAAAC","189":[185,128],"194":[185],"197":[185],"219":"b:AgAAAAAAAAAAAAAAACABYAcCBAEAAAAC","223":[185],"264":"b:AgAAAAAAAAAAAAAAACABYAcCBAEAAAAC","352":"b:AgAAAAAAAAAAAAAAACABYAcCBAEAAAAC","375":[185,128],"383":[185,128],"401":[185,128],"410":[185,128],"431":[185],"438":"b:AgAAAAAAAAAAAAAAACABYAUCBAEAAAAC","446":"b:AgAAAAAAAAAAAAAAACABYAUCBAEAAAAC","450":"b:AgAAAAAAAAAAAAAAACABYAUCBAEAAAAC"},"packages/frontend/editor-ui/src/features/shared/editors/components/CodeNodeEditor/completions/js.snippets.ts":{"8":"b://Xf/P///+39//////////////8DAAAA"},"packages/frontend/editor-ui/src/features/shared/editors/components/CodeNodeEditor/theme.ts":{"91":"b:BkABEAEAAAhAAAAACAABcBy9hkAAAAAA"},"packages/frontend/editor-ui/src/features/shared/editors/components/JsonEditor/JsonEditor.vue":{"33":"b:BEAAAAAAAAhAAAAACAAAQBA9hgAAAAAA","43":"b:BEAAAAAAAAhAAAAACAAAQBA9hgAAAAAA","71":"b:BEAAAAAAAAhAAAAACAAAQBA9hgAAAAAA","80":"b:BEAAAAAAAAhAAAAACAAAQBA9hgAAAAAA","84":"b:BEAAAAAAAAAAAAAACAAAABA8BgAAAAAA","89":"b:BEAAAAAAAAhAAAAACAAAQBA9hgAAAAAA","90":[59,132,138,139,140,141,145,146],"102":"b:BEAAAAAAAAhAAAAACAAAQBA9hgAAAAAA","124":"b:BEAAAAAAAAhAAAAACAAAQBA9hgAAAAAA"},"packages/frontend/editor-ui/src/features/shared/editors/plugins/codemirror/completions/constants.ts":{"467":"b://Xf/P///+39//////////////8DAAAA","471":"b://Xf/P///+39//////////////8DAAAA"},"packages/frontend/editor-ui/src/features/shared/editors/plugins/codemirror/completions/datatype.completions.ts":{"75":"b:AEAAAAAAAAQAAAAAAAAAABA8HgAAAAAA","163":[138,139,14,146],"179":[138,139,141,14,146],"181":[138,139,141,14,146],"187":[138,139,141,14],"188":[138,139,141,14],"205":[138,139,141,14,146],"208":[138,139,141,14,146],"210":[138,139,141,14,146],"211":[138,139,141,14,146],"215":[141],"240":[138,139,141,14,146],"261":[138,139,141,14,146],"285":[138,139,141,14],"292":[138,139,141,14],"315":[138,139,141,14],"324":[138,139,141,14],"330":[138,139,141,14],"342":[138,139,14],"348":[138,139,14],"353":[138,139,14],"362":[138,139,14],"368":[138,139,141,14],"380":[138,139,141,14],"381":[138,139,141,14],"382":[138,139,141,14],"392":[138,139,141,14,146],"414":[138,139,141,14],"422":[138,139,14,146],"453":[138,139,14,146],"472":[146],"476":[138,139,14],"477":[138,139,14],"527":[138,139,141,14,146],"545":[138,139,141,14,146],"553":[138,139,141,14,146],"561":[138,139,141,14,146],"562":[138,139,141,14,146],"573":[141],"574":[141],"578":[141],"583":[141],"633":[141],"700":[141],"731":[141],"763":[141],"776":[141],"784":[138,139,14],"985":[146],"1077":[146],"1078":[146],"1560":"b://Xf/P///+39//////////////8DAAAA"},"packages/frontend/editor-ui/src/features/shared/editors/plugins/codemirror/completions/infoBoxRenderer.ts":{"6":[141],"18":[141],"31":[141],"64":[146],"76":[141,146],"117":[141],"166":[141],"170":[141],"177":[141],"189":[141],"217":[141],"238":"b://Xf/P///+39//////////////8DAAAA","239":[141,146]},"packages/frontend/editor-ui/src/features/shared/editors/plugins/codemirror/completions/utils.ts":{"27":[132,138,139,140,141,14,146],"83":[138,139,141,14,146],"119":[138,139,14,146],"122":[138,139,14,146],"131":[58,132,138,139,140,141,14,146,148],"134":[138,139,14],"143":[138,139,14],"169":[132,138,139,140,141,14,146,148],"183":[146],"207":[58,132,138,139,140,141,14,146,148],"232":"b:wuUAAACAYAUAAAAAADAAEJC8HgAAAAAA","234":[58,132,138,139,140,141,14,146,148],"252":[132,138,139,140,141,14,146,148],"269":[138,139,14,146],"271":[138,139,14,146],"274":"b:AEABEAEAAAAAAAAAAAABcBy8FEAAAAAA","298":"b:AEABEAEAAAAAAAAAAAABcBy8FEAAAAAA","302":"b:AEABEAEAAAAAAAAAAAABcBy8FEAAAAAA","303":"b:AEABEAEAAAAAAAAAAAABcBy8FEAAAAAA","310":[58,132,138,139,140,141,14,146,148],"314":[58,132,138,139,140,141,14,146,148],"315":[58,132,138,139,140,141,14,146,148],"319":[58,132,138,139,140,141,14,146,148],"328":[58,132,138,139,140,141,14,146,148],"353":[138,139,141,14,146],"356":[138,139,141,14,146],"357":[138,139,141,14,146],"358":[138,139,141,14,146],"376":[132,138,139,140,141,14,146,148],"432":[138,139,141,14,146],"434":[138,139,141,14,146],"438":[138,139,141,14],"439":[138,139,141,14],"442":[138,139,14,146],"453":"b://Xf/P///+39//////////////8DAAAA","464":[138,139,14],"481":[138,139,141,14,146]},"packages/frontend/editor-ui/src/features/shared/editors/plugins/codemirror/dragAndDrop.ts":{"15":"b:xuUBEAGAYA1AAAAACDABcJy9nkIAAAAA","18":"b:wuUBAAGAYA0AAAAAADABUJi8ngIAAAAA","20":"b:wuUBAAGAYAUAAAAAADABUJi8ngIAAAAA","35":"b:xuUBEAGAYA1AAAAACDABcJy9nkIAAAAA","42":"b:xuUBEAGAYA1AAAAACDABcJy9nkIAAAAA","46":"b:xuUBEAGAYA1AAAAACDABcJy9nkIAAAAA","93":"b:xuUAEAEAQAQAAAAACDABcJy8BkAAAAAA","97":"b:gEAAAAEAAAwAAAAAAAAAABC8ngAAAAAA","104":"b:AEAAAAAAAAwAAAAAAAAAABi8HgAAAAAA","112":"b:gEAAAAEAAAwAAAAAAAAAABA8ngAAAAAA","115":"b:AEAAAAAAAAwAAAAAAAAAABC8HgAAAAAA","123":[139],"127":[139],"139":[139],"143":[139],"163":"b:xuUBEAGAYA1AAAAACDABcJy9nkIAAAAA"},"packages/frontend/editor-ui/src/features/shared/editors/plugins/codemirror/format.ts":{"10":"b://Xf/P///+39//////////////8DAAAA"},"packages/frontend/editor-ui/src/features/shared/editors/plugins/codemirror/keymap.ts":{"83":"b://Xf/P///+39//////////////8DAAAA","100":[139],"261":"b:AEAAAAAAAAAAAAAAAAAAABC8HAAAAAAA"},"packages/frontend/editor-ui/src/features/shared/editors/plugins/codemirror/multiCursor.ts":{"4":"b://Xf/P///+39//////////////8DAAAA"},"packages/frontend/editor-ui/src/features/shared/editors/plugins/codemirror/n8nLang.ts":{"11":[54,139,140,141],"14":"b:wuUAAACAYAUAAAAAADAAEIC8HgIAAAAA","25":"b:wuUAAACAYAUAAAAAADAAEIC8HgIAAAAA","28":"b:wuUAAACAYAUAAAAAADAAEIC8HgIAAAAA","32":"b:xuUAAACAYA1AAAAACDAAUJC9ngIAAAAA","37":[138,139,14,146]},"packages/frontend/editor-ui/src/features/shared/editors/plugins/codemirror/resolvableHighlighter.ts":{"61":"b://Xf/P///+39//////////////8DAAAA","62":"b:QuEAAACAYAUAAAAAAAAAAJC8HgAAAAAA","65":"b:QuEAAACAYAUAAAAAAAAAAJC8HgAAAAAA","75":[132,139,141],"81":[15,138,139,140,143,14,145,148],"101":"b:QuEAAACAYAUAAAAAAAAAAJC8HgAAAAAA","102":"b:QuEAAACAYAUAAAAAAAAAAJC8HgAAAAAA","115":"b:QuEAAACAYAUAAAAAAAAAAJC8HgAAAAAA","116":[54,132,135,139,140,141,14]},"packages/frontend/editor-ui/src/features/shared/editors/plugins/codemirror/tooltips/InfoBoxTooltip.ts":{"27":"b://Xf/P///+39//////////////8DAAAA","28":"b:wuUAAACAYAEAAAAAADAAEIC8HgAAAAAA","44":[138,141,146,148],"55":[141],"74":[138,141,146,148],"96":[138,140,141,146,148],"116":[138,140,141,146,148],"119":"b:wuUAAACAYAUAAAAAADAAEIC8HgAAAAAA","123":"b:wuUAAACAYAEAAAAAADAAEIC8HgAAAAAA","127":"b:wuUAAACAYAUAAAAAADAAEIC8HgIAAAAA","142":[138,141,146,148],"143":[138,140,141,146,148],"166":[138,141,146,148],"188":[141],"202":"b:wuUAAACAYAUAAAAAADAAEIC8HgIAAAAA","256":[138,141,146,148],"262":[141],"274":"b:wuUAAACAYAUAAAAAADAAEIC8HgIAAAAA","282":"b:wuUAAACAYAUAAAAAADAAEIC8HgIAAAAA","291":"b:wuUAAACAYAUAAAAAADAAEIC8HgIAAAAA","315":"b://Xf/P///+39//////////////8DAAAA","319":"b:wuUAAACAYAUAAAAAADAAEIC8HgIAAAAA","322":"b:wuUAAACAYAUAAAAAADAAEIC8HgIAAAAA","352":"b:wuUAAACAYAUAAAAAADAAEIC8HgIAAAAA","360":[141,146],"378":[141],"381":[141],"423":"b:guUBEAEAAAQAAAAAABABcBy8GgAAAAAA","435":"b:wuUAAACAYAUAAAAAADAAEIC8HgIAAAAA","443":"b:wqUAAAAAQAQAAAAAADAAEIAUAgAAAAAA","449":"b:wuUAAACAYAUAAAAAADAAEIC8HgIAAAAA"},"packages/frontend/editor-ui/src/features/shared/editors/plugins/codemirror/typescript/client/facet.ts":{"9":"b://Xf/P///+39//////////////8DAAAA"},"packages/frontend/editor-ui/src/features/shared/envFeatureFlag/useEnvFeatureFlag.ts":{"8":"b://Xf/P///+39f4//////8/////8DAAAA"},"packages/frontend/editor-ui/src/features/shared/nodeCreator/components/NodeCreator.vue":{"54":"b:x/Xf8G3/+cHdYAQC//0B8/////8DAAAA","56":"b:guXCMAUYicAEIAAC/9AAc9y9fSoBAAAA","64":"b:guXCMAUYicAEIAAC/9AAc9y9fSoBAAAA","72":[48,124],"83":"b:wuXaMCUZicANIAAC/9AAc/y9fXoBAAAA","87":"b:guXCMAUYicAEIAAC/9AAc9y9fSoBAAAA","91":"b:guXCMAUYicAEIAAC/9AAc9y9fSoBAAAA","96":[48],"99":[48],"119":"b:x/Xf8G3/+cHdYAQC//0B8/////8DAAAA","123":"b:x/Xf8G3/+cHdYAQC//0B8/////8DAAAA","124":"b:guXCMAUYicAEIAAC/9AAc9y9fSoBAAAA","134":"b:guXCMAUYicAEIAAC/9AAc9y9fSoBAAAA","142":[28,77,48,14],"145":"b:x/Xf8G3/+cHdYAQC//0B8/////8DAAAA","149":"b:x/Xf8G3/+cHdYAQC//0B8/////8DAAAA","160":"b:QgAYACABCQAJAAAAAAAAQuAAQHAAAAAA","165":[48,144]},"packages/frontend/editor-ui/src/features/shared/nodeCreator/components/NodeCreatorShortcutCoachmark.vue":{"14":"b:x/Xf8G3/+cHdYAQC//0B8/////8DAAAA","28":"b:x/Xf8G3/+cHdYAQC//0B8/////8DAAAA","31":[48],"45":[48]},"packages/frontend/editor-ui/src/features/shared/nodeCreator/composables/useActionsGeneration.ts":{"32":"b:x/Xf8G3/+cHdYAQC//0B8/////8DAAAC","38":"b:x/Xf8G3/+cHdYAQC//0B8/////8DAAAC","46":"b:x/Xf8G3/+cHdYAQC//0B8/////8DAAAC","49":"b:x/Xf8G3/+cHdYAQC//0B8/////8DAAAC","62":"b:x/Xf8G3/+cHdYAQC//0B8/////8DAAAC","68":"b:x/Xf8G3/+cHdYAQC//0B8/////8DAAAC","83":"b:x/Xf8G3/+cHdYAQC//0B8/////8DAAAC","104":"b:x/Xf8G3/+cHdYAQC//0B8/////8DAAAC","114":"b:x/Xf8G3/+cHdYAQC//0B8/////8DAAAC","120":"b:x/Xf8G3/+cHdYAQC//0B8/////8DAAAC","126":"b:x/Xf8G3/+cHdYAQC//0B8/////8DAAAC","133":"b:x/Xf8G3/+cHdYAQC//0B8/////8DAAAC","148":"b:x/Xf8G3/+cHdYAQC//0B8/////8DAAAC","153":"b:x/Xf8G3/+cHdYAQC//0B8/////8DAAAC","157":"b:x/Xf8G3/+cHdYAQC//0B8/////8DAAAC","161":"b:x/Xf8G3/+cHdYAQC//0B8/////8DAAAC","179":"b:x/Xf8G3/+cHdYAQC//0B8/////8DAAAC","184":"b:x/Xf8G3/+cHdYAQC//0B8/////8DAAAC","192":"b:x/Xf8G3/+cHdYAQC//0B8/////8DAAAC","210":"b:x/Xf8G3/+cHdYAQC//0B8/////8DAAAC","213":"b:x/Xf8G3/+cHdYAQC//0B8/////8DAAAC","234":"b:x/Xf8G3/+cHdYAQC//0B8/////8DAAAC","245":"b:x/Xf8G3/+cHdYAQC//0B8/////8DAAAC","264":"b:x/Xf8G3/+cHdYAQC//0B8/////8DAAAC","267":"b:x/Xf8G3/+cHdYAQC//0B8/////8DAAAC","271":"b:x/Xf8G3/+cHdYAQC//0B8/////8DAAAC","273":"b:x/Xf8G3/+cHdYAQC//0B8/////8DAAAC","274":"b:x/Xf8G3/+cHdYAQC//0B8/////8DAAAC","278":"b:x/Xf8G3/+cHdYAQC//0B8/////8DAAAC","291":"b:x/Xf8G3/+cHdYAQC//0B8/////8DAAAC","329":"b://Xf/P///+39//////////////8DAAAC","330":"b:x/Xf8G3/+cHdYAQC//0B8/////8DAAAC","348":"b:x/Xf8G3/+cHdYAQC//0B8/////8DAAAC","351":"b:x/Xf8G3/+cHdYAQC//0B8/////8DAAAC","362":"b:x/Xf8G3/+cHdYAQC//0B8/////8DAAAC","392":"b:x/Xf8G3/+cHdYAQC//0B8/////8DAAAC","399":"b:x/Xf8G3/+cHdYAQC//0B8/////8DAAAC","421":"b:x/Xf8G3/+cHdYAQC//0B8/////8DAAAC","422":"b:x/Xf8G3/+cHdYAQC//0B8/////8DAAAC","427":"b:x/Xf8G3/+cHdYAQC//0B8/////8DAAAC","433":"b:x/Xf8G3/+cHdYAQC//0B8/////8DAAAC","444":"b:x/Xf8G3/+cHdYAQC//0B8/////8DAAAC","445":"b:x/Xf8G3/+cHdYAQC//0B8/////8DAAAC","449":"b:x/Xf8G3/+cHdYAQC//0B8/////8DAAAC"},"packages/frontend/editor-ui/src/features/shared/nodeCreator/composables/useGetNodeCreatorFilter.ts":{"9":"b://Xf/P///+39//////////////8DAAAA","13":[1,7,10,13,14],"30":[1,7,10,13,14],"49":[1,10,13]},"packages/frontend/editor-ui/src/features/shared/nodeCreator/composables/useKeyboardNavigation.ts":{"14":"b:x/Xf8G3/+MHdYAQC//0B8/////8DAAAA","20":[28,77,124,14],"43":[28,124],"46":"b:guXCMAUYiMAEIAAC/9AAc9y9fSoBAAAA","49":"b:guXCMAUYiMAEIAAC/9AAc9y9fSoBAAAA","50":"b:guXCMAUYiMAEIAAC/9AAc9y9fSoBAAAA","53":"b:guXCMAUYiMAEIAAC/9AAc9y9fSoBAAAA","56":"b:guXCMAUYiMAEIAAC/9AAc9y9fSoBAAAA","62":[28,77,124,14],"64":[28,77,124,14],"66":[28,77,124,14],"79":[15,28,96,77,124,126,130,14],"102":[28,77,124,14],"127":"b:guXCMAUYiMAEIAAC/9AAc9y9fSoBAAAA","131":"b:guXCMAUYiMAEIAAC/9AAc9y9fSoBAAAA","141":"b:guXCMAUYiMAEIAAC/9AAc9y9fSoBAAAA","147":"b:guXCMAUYiMAEIAAC/9AAc9y9fSoBAAAA","151":"b:guXCMAUYiMAEIAAC/9AAc9y9fSoBAAAA","156":"b:x/Xf8G3/+MHdYAQC//0B8/////8DAAAA","157":"b:x/Xf8G3/+cHdYAQC//0B8/////8DAAAA","166":"b:guXCMAUYiMAEIAAC/9AAc9y9fSoBAAAA","171":"b:gkWCMAEACMAAIAAAvwAAchyUPQoAAAAA","172":"b:gkWCMAEACMAAIAAAvwAAchyUPQoAAAAA"},"packages/frontend/editor-ui/src/features/shared/nodeCreator/composables/useNodeCreatorShortcutCoachmark.ts":{"12":"b:x/Xf8G3/+cHdYAQC//0B8/////8DAAAA","22":"b:QgAYACABCAAJAAAAAAAAQuAAQHAAAAAA"},"packages/frontend/editor-ui/src/features/shared/nodeCreator/composables/useViewStacks.ts":{"108":"b://Xf/P///+39//////////////8DAAAA","115":"b:x/Xf8G3/+MHdYAQC//0B8/////8DAAAA","124":"b:guXCMAUYiMAEIAAC/9AAc9y9fSoBAAAA","156":"b:guXCMAUYiMAEIAAC/9AAc9y9fQoAAAAA","172":"b:guXCMAUYiMAEIAAC/9AAc9y9fSoBAAAA","185":"b:guXCMAUYiMAEIAAC/9AAc9y9fSoBAAAA","189":"b:guXCMAUYiMAEIAAC/9AAc9y9fQoAAAAA","193":"b:guXCMAUYiMAEIAAC/9AAc9y9fQoAAAAA","196":"b:guXCMAUYiMAEIAAC/9AAc9y9fSoBAAAA","200":"b:guXCMAUYiMAEIAAC/9AAc9y9fSoBAAAA","204":"b:guXCMAUYiMAEIAAC/9AAc9y9fSoBAAAA","208":"b:guXCMAUYiMAEIAAC/9AAc9y9fQoAAAAA","209":"b:guXCMAUYiMAEIAAC/9AAc9y9fQoAAAAA","215":"b:guXCMAUYiMAEIAAC/9AAc9y9fSoBAAAA","233":"b:guXCMAUYiMAEIAAC/9AAc9y9fQoAAAAA","234":"b:guXCMAUYiMAEIAAC/9AAc9y9fQoAAAAA","247":[32,77],"262":[77,97],"264":"b:guXCMAUYiMAEIAAC/9AAc9y9fQoAAAAA","268":[1,7,8,10,108,13,14],"269":[1,7,8,10,108,13,14],"280":[1,7,10,13,14],"281":[1,7,10,13],"295":[1,10,13],"303":[1,10,13],"304":[1,10,13],"307":[1,10,13],"321":"b:guXCMAUYiMAEIAAC/9AAc9y9fSoBAAAA","326":"b:guXCMAUYiMAEIAAC/9AAc9y9fSoBAAAA","338":[1,7,8,10,13,14],"379":[1,10,13],"411":[1,10,13],"426":"b:guXCMAUYiMAEIAAC/9AAc9y9fQoAAAAA","427":"b:guXCMAUYiMAEIAAC/9AAc9y9fQoAAAAA","443":[1,7,10,13,14],"462":[1,7,10,13,14],"492":[1,7,10,13,14],"508":[1,10,13],"515":[1,7,10,13,14],"529":"b:guXCMAUYiMAEIAAC/9AAc9y9fSoBAAAA","558":[77],"559":[77],"580":"b:guXCMAUYiMAEIAAC/9AAc9y9fSoBAAAA","602":[77,98],"609":"b:guXCMAUYiMAEIAAC/9AAc9y9fSoBAAAA","611":"b:guXCMAUYiMAEIAAC/9AAc9y9fSoBAAAA","615":"b:guXCMAUYiMAEIAAC/9AAc9y9fSoBAAAA","624":"b:guXCMAUYiMAEIAAC/9AAc9y9fSoBAAAA"},"packages/frontend/editor-ui/src/features/shared/nodeCreator/nodeCreator.store.ts":{"51":"b://Xf/P///+39//////////////8DAAAA","53":"b:AOACEAAACAAAIAAAGAAAQJQQACABAAAA","73":[1,7,10,13,14],"74":[1,7,10,13,14],"77":"b:x/Xf8G3/+MHdYAQC//0B8/////8DAAAA","85":"b:x/Xf8G3/+MHdYAQC//0B8/////8DAAAA","89":"b:x/Xf8O3/+MHdYDwC//0B//////8DAAAA","93":"b:guXCMAUYiMAEIAAC/9AAc9y9fSoBAAAA","97":[13,14],"118":[13,14],"143":"b:x/Xf8O3/+cHdYDwC//0B//////8DAAAA","182":"b:gmQAEAAAgAAAIAAABsAAMAABEAAAAAAA","271":"b:AIACEAAACAAAIAAAGAAAQJQQACABAAAA","331":"b:guXCMAUYiMAEIAAC/9AAc9y9fSoBAAAA","337":"b:guXCMAUYiMAEIAAC/9AAc9y9fSoBAAAA","341":"b:guXCMAUYiMAEIAAC/9AAc9y9fSoBAAAA","348":"b:guXCMAUYiMAEIAAC/9AAc9y9fSoBAAAA","375":"b:gmHCMAUIiMAEIAACslAAc1y8fAoAAAAA","391":"b:gmHCMAUIiMAEAAACslAAc1y8fAoAAAAA","431":[99],"435":"b:gEGCMAEACMAAAAAArQAAchyUPQoAAAAA","448":[1,10,77,97],"459":"b:guXCMAUYiMAEIAAC/9AAc9y9fCoBAAAA","475":"b:x/Xf8O3/+cHdYDwC//0B//////8DAAAA"},"packages/frontend/editor-ui/src/features/shared/nodeCreator/nodeCreator.utils.ts":{"55":"b:guXCMAUYiMAEIAAC/9AAc9y9fQoAAAAC","76":[185,77,97],"82":[185,77,97],"86":[185,77,97],"98":[185,77,97],"109":"b:gmWCMAEACMAAIAAArxAAchyUPQoAAAAC","110":"b:gmWCMAEACMAAIAAArwAAchyUPQgAAAAC","122":"b:guXCMAUYiMAEIAAC/9AAc9y9fQoAAAAC","127":"b:AsECMAUICMAAIAAAVgAAc9y8TAoAAAAC","147":[185],"150":[185],"159":[185],"166":"b:guXCMAUYiMAEIAAC/9AAc9y9fSoBAAAC","179":[185],"194":[185],"204":"b:guXCMAUYiMAEIAAC/9AAc9y9fQoAAAAC","223":"b:guXCMAUYiMAEIAAC/9AAc9y9fQoAAAAC","230":[185],"243":"b:guXCMAUYiMAEIAAC/9AAc9y9fQoAAAAC","252":"b:guXCMAUYiMAEIAAC/9AAc9y9fQoAAAAC","255":"b:guXCMAUYiMAEIAAC/9AAc9y9fSoBAAAC","256":"b:guXCMAUYiMAEIAAC/9AAc9y9fSoBAAAC","258":"b:guXCMAUYiMAEIAAC/9AAc9y9fSoBAAAC","272":[1,185,10,13],"277":[1,185,10,13],"291":[1,185,10,13],"306":[1,7,185,8,10,97,108,13,14],"311":[1,7,185,8,10,97,13,14],"315":[1,7,185,8,10,97,108,13,14],"316":[1,7,185,8,10,97,13,14],"317":[1,7,185,8,10,97,13,14],"326":[1,7,185,8,10,97,108,13,14],"330":[1,7,185,8,10,97,13,14],"341":[1,7,185,8,10,97,13,14],"342":[1,7,185,8,10,97,13,14],"358":[1,7,185,8,10,97,108,13,14],"360":[1,7,185,13,14],"374":[1,7,185,8,10,97,108,13,14],"382":"b:x/Xf8G3/+cHdYAQC//0B8/////8DAAAC","390":"b:x/XP8OX/+MFdYDQC//wD+/////sDAAAC","393":"b:x/XP8OX/+MVdYDQi//yB//3///sDAAAC","399":"b:guXCMAUYiMAEIAAC/9AAc9y9fSoBAAAC","416":[1,7,185,13],"440":"b:guXCMAUYiMAEIAAC/9AAc9y9fSoBAAAC","478":"b:guXCMAUYiMAEIAAC/9AAc9y9fSoBAAAC","480":"b:guXCMAUYiMAEIAAC/9AAc9y9fSoBAAAC","487":[185],"493":[185],"498":"b:guXCMAUYiMAEIAAC/9AAc9y9fSoBAAAC","523":[185],"524":[185],"528":"b:guGCMAEYiMAEIAAC/9AAc9y9fQoAAAAC","533":[185,100],"556":[185],"582":[185,98],"600":[185,14],"621":"b:gsHCMAUIiMAAIAAAttAAcly9fQoAAAAC","623":"b:gsHCMAUIiMAAIAAAttAAcly9fQoAAAAC","624":"b:gsHCMAUIiMAAIAAAttAAcly9fQoAAAAC","630":[185],"648":"b:guXCMAUYiMAEIAAC/9AAc9y9fSoBAAAC","657":"b:guXCMAUYiMAEIAAC/9AAc9y9fSoBAAAC","663":"b:guXCMAUYiMAEIAAC/9AAc9y9fSoBAAAC","670":"b:guXCMAUYiMAEIAAC/9AAc9y9fSoBAAAC","678":[185,97],"679":[185,97]},"packages/frontend/editor-ui/src/features/shared/nodeCreator/views/NodeCreation.vue":{"41":"b:x/Xf8G3/+cHdYAQC//0B8/////8DAAAA","66":"b:x/Xf8G3/+cHdYAQC//0B8/////8DAAAA","73":"b:guXCMAUYicAEIAAC91AAc1y8fQoAAAAA","80":[48,129],"94":"b:guXCMAUYicAEIAAC/9AAc9y9fSoBAAAA","101":"b:guXCMAUYicAEIAAC/9AAc9y9fCoBAAAA","106":[48,112,127],"124":[48],"128":[15,18,48],"145":[48],"148":[48],"164":"b:x/Xf8G3/+cHdYAQC//0B8/////8DAAAA","168":"b:x/Xb8G3++cHdYAQC//0B8/////8DAAAA","183":"b:x/Xf8G3/+cHdYAQC//0B8/////8DAAAA","197":"b:x/Xf8G3/+cHdYAQC//0B8/////8DAAAA","211":"b:x/Xf8G3/+cHdYAQC//0B8/////8DAAAA","249":[15,16,17,18,19,20],"258":[15,16,17,19,20]},"packages/frontend/editor-ui/src/features/shared/tags/tags.api.ts":{"8":"b://Xf/P///+39//////////////8DAAAC","10":"b:3/Xf/P///+39f4//////8/////8DAAAC","13":[185,112,150,154,159],"19":[185],"26":[185,154]},"packages/frontend/editor-ui/src/features/shared/tags/tags.store.ts":{"34":"b://Xf/P///+39//////////////8DAAAC","39":"b://Xf/P///+39//////////////8DAAAC","51":[185,48,154,160],"52":[185,154,160],"55":[185,48,154,160],"57":[185],"61":"b:3/Xf/P///+39f4////+/8/////8DAAAC","62":[185,129,141,150,153,154,159,160],"70":"b:R3AAQOC3WcBdADwAICQB6KHLw/UDAAAC","71":[185,112,150,154,159],"92":[185,154],"97":"b://Xf/P///+39f4//////8/////8DAAAC","116":[185,112,150,154,159],"128":[185],"139":[185],"149":[185,154]},"packages/frontend/editor-ui/src/features/workflow-reviews/composables/useWorkflowReviewDialogPreferences.ts":{"10":"b:x/Xf8O3/+cHdYAwC//0B8/////8DAAAA"},"packages/frontend/editor-ui/src/features/workflow-reviews/composables/useWorkflowReviewStatusSync.ts":{"14":"b:x/Xf8O3/+cHdYAwC//0B8/////8DAAAA","20":"b:x/Xf8O3/+cHdYAwC//0B8/////8DAAAA","30":"b:x/Hf0OX/+cFdAAwA4CUA8/3/338DAAAA","42":"b:x/Xf8O3/+cHdYAwC//0B8/////8DAAAA","43":"b:wuHfkAVIqQEMAAAA4AEAc9y0XS4DAAAA","48":"b:x/Xf8O3/+cHdYAwC//0B8/////8DAAAA","49":[48,133],"55":"b:x/Xf8O3/+cHdYAwC//0B8/////8DAAAA","56":[48],"57":[37,40,41,48,51,133,150,67]},"packages/frontend/editor-ui/src/features/workflow-reviews/composables/useWorkflowReviewsFeature.ts":{"11":"b:x/Xf/P///8X9f4/D//+F8/////8DAAAA","19":"b:x/Xf/P///8X9f4/D//+F8/////8DAAAA"},"packages/frontend/editor-ui/src/features/workflow-reviews/reviewRequired.store.ts":{"7":"b:x/Xf8O3/+cHdYAwC//0B8/////8DAAAA"},"packages/frontend/editor-ui/src/features/workflow-reviews/reviewStatus.store.ts":{"13":"b:x/Xf8O3/+cHdYAwC//0B8/////8DAAAA","20":[48],"25":[48],"30":[48],"33":[48],"61":[48]},"packages/frontend/editor-ui/src/features/workflows/canvas/canvas.types.ts":{"27":"b://Xf/P///+39//////////////8DAAAC","54":"b://Xf/P///+39//////////////8DAAAC","169":[185,128],"173":"b:xvXHsAXZ+MFcICQC//wB89////sBAAAC","218":"b:x/Xf8O3/+cHdYDQC//0B//////8DAAAC"},"packages/frontend/editor-ui/src/features/workflows/canvas/canvas.utils.ts":{"37":"b:x/Xf8O3/+cHdYDQC//0B//////8DAAAC","51":"b:x/XP8OX/+MFdYDQC//wB+/3///sDAAAC","60":"b:x/XP8OX/+MFVQDQCu/wB+v3///sCAAAC","61":"b:x/XP8OX/+MFdYDQC//wB+/3///sDAAAC","63":"b:x/XP8OX/+MFVQDQCu/wB+v3///sCAAAC","64":"b:x/XP8OX/+MFdYDQC//wB+/3///sDAAAC","80":"b:x/Xf8O3/+cHdYDQC//0B//////8DAAAC","97":"b:x/Xf8O3/+cHdYDQC//0B//////8DAAAC","122":[185],"130":"b:x/Xf8O3/+cHdYDQC//0B//////8DAAAC","135":"b:x/Xf8O3/+cHdYDQC//0B//////8DAAAC","137":"b:x/XP8OX/+MFVQDQCu/wB/v3///sCAAAC","143":"b:x/XP8OX/+MFVQDQCu/wB/v3///sCAAAC","145":"b:x/XP8OX/+MFVQDQCu/wB/v3///sCAAAC","146":"b:x/XP8OX/+MFVQDQCu/wB/v3///sCAAAC","203":[185,130,152],"235":"b:x/XP8OX/+MFVYDQC//wB//////sDAAAC","255":"b:x/XP8OX/+MFdYDQC//wB//////sDAAAC","270":"b:x/XP8OX/+MFVQDQCu/wB/v3///sCAAAC","281":[185,55,111,124,128,130],"298":"b:wuXPsAVYqMEEAAACu9gBcl+9fYsAAAAC","332":"b:x/XP8OX/+MFdYDQC//wB//////sDAAAC","341":"b:x/XP8OX/+MFdYDQC//wB//3///sDAAAC","349":"b:x/XP8OX/+MFdYDQC//wB//3///sDAAAC","366":[185,111,128,129,151],"384":"b:x/Xf8O3/+cHdYDQC//0B//////8DAAAC","401":"b:x/XP8GX/+MFdYAQC//wB9/3///sDAAAC","411":"b:x/XP8GX/+MFdYAQC//wB9/3///sDAAAC","415":"b:x/XP8GX/+MFdYAQC//wB9/3///sDAAAC","429":"b:QuENsBBAooUcCAAACZuFeAfRRfkDAAAC"},"packages/frontend/editor-ui/src/features/workflows/canvas/components/Canvas.vue":{"221":"b:x/Xf8O3/+cHdYDQC//0B//////8DAAAC","223":"b:x/Xf8O3/+cHdYDQC//0B//////8DAAAC","224":[185,122],"261":"b:x/Xf8O3/+cHdYDQC//0B//////8DAAAC","262":[185],"275":"b:x/Xf8O3/+cHdYDQC//0B//////8DAAAC","276":"b:xvXHsAXZ+MFcICQC//wB89////sBAAAC","278":[185,124,125,126,130,66,68],"279":[185,124,125,126,130,66,68],"287":"b:x/Xf8O3/+cHdYDQC//0B//////8DAAAC","304":"b:AAAAAAAAAAAcAAAAAAAAcAeAACECAAAC","310":"b:AMAMAAAAAAAcAAAAAAAAcBe0HCECAAAC","330":[185],"348":"b:AiAIEAAAgAAcAAAACZAAcAcBQHkDAAAC","350":[185,124],"358":[185,124],"366":[185],"368":[185],"377":[185],"379":[185],"388":[185,152],"390":[185,152],"393":[185,124],"395":[185,124],"398":[185],"410":[185,128],"417":[185,128],"433":[185,128],"439":[185,128],"449":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAC","459":[185,128],"464":[185,128],"469":[185],"476":"b:AAAIAAAAAAAcAAAAAAAAcAcAAHkCAAAC","478":[185],"480":[185,124,66,68],"481":[185,124,66,68],"483":[185],"486":[185],"487":[185],"488":[185],"489":[185],"496":[185],"497":[185],"498":[185],"504":[185],"507":[185],"511":[185],"515":[185],"521":[185],"523":[185,126],"524":[185,126,130,155,156,157,158],"525":[185],"526":[185,126],"527":[185],"529":[185],"530":[185],"534":[185,129],"535":[185],"536":[185],"537":[185,161],"540":[185],"541":[185],"542":[185],"543":[185],"544":[185],"545":[185],"546":[185],"547":[185],"551":[185],"555":[185],"556":[185],"574":"b:x/Xf8O3/+cHdYDQC//0B//////8DAAAC","580":"b:AAAAAAAAAAAUAAAAAAAAcAYAAHkAAAAC","592":"b:x/TfkACR+AFcICQAvuwB8v/fl/kBAAAC","593":"b:x/TfkACR+AFcICQAvuwB8v/fl/kBAAAC","602":"b:x/TFkACR+AFcICQApuwB8v3fl/kAAAAC","609":"b:x/Xf8O3/+cHdYDQC//0B//////8DAAAC","610":"b:xvXHsAVY+MFUAAQC8vwB8t/+/3sAAAAC","615":"b:x/Xf8O3/+cHdYDQC//0B//////8DAAAC","622":"b:x/Xf8O3/+cHdYDQC//0B//////8DAAAC","623":"b:xvXHsAXZ+MFcICQC//wB89////sBAAAC","625":"b:x/Xf8O3/+cHdYDQC//0B//////8DAAAC","628":"b:x/Xf8O3/+cHdYDQC//0B//////8DAAAC","629":[15,16,17,18,19,185,20],"637":"b:ggQAEAAAAAAAIAAABkAAMAABEAAAAAAC","641":[185],"645":[185],"649":[185,109,124,125,128,130,66],"659":[185,128],"663":[185],"674":[185,109,124,125,128,130,66],"675":[185,128],"676":[185,128],"677":[185,109,124,125,128,130,66],"678":[185,109,124,125,128,130,66],"679":[185,128],"688":"b:x/Xf8O3/+cHdYDQC//0B//////8DAAAC","689":[185,128],"690":"b:xvXHsAXZ+MFcICQC//wB89////sBAAAC","691":[185,128],"697":"b:x/Xf8O3/+cHdYDQC//0B//////8DAAAC","708":[185,109,124,125,128,130,66],"712":[185,109,124,125,128,130,66],"716":[185,109,124,125,128,130,66],"725":[185],"729":[185],"733":[185,128],"757":[185,128],"758":[185,128],"773":[185,128],"774":[185,128],"779":[185,128],"795":[185],"807":[185],"820":[185],"838":[185],"862":[185],"879":[185],"883":[185,128],"908":[185],"917":[185],"932":[185],"934":[185],"942":[185],"945":[185],"946":[185],"951":[185],"959":[185],"971":[185],"985":[185],"998":[185,124,125,130],"1007":"b:xPDFgACReAFcACAAoCQB8N3fh3kAAAAC","1041":[185],"1046":"b:RQAAEAAAAAAAAAQAgCAA8AOAAQEAAAAC","1056":"b:xPDFgACReAFAACAAICQBQNjfh0EAAAAC","1061":[185],"1065":"b:wuXHsAVYqMEEIAQC//gBc9+/fasBAAAC","1069":[185,124,125,126,130,66,68],"1073":"b:xvXHsAXZ+MFcICQC//wB89////sBAAAC","1077":"b:wuXHsAVYqMEEIAQC//gBc9+/fasBAAAC","1082":"b:wuXHsAVYqMEEIAQC//gBc9+/fasBAAAC","1107":[185,121,124,6],"1111":[185,107,124,6],"1115":[185],"1119":[1,185,161],"1123":[1,185,161],"1126":[185,89,103,125,161],"1130":[185,89,103,125,161],"1133":[185],"1146":[185],"1150":[185],"1155":[185],"1171":"b:ggQAEAAAAAAAIAAABkAAMAEBEAEAAAAC","1178":[185,125,128,152],"1185":"b:ggQAEAAAAAAAIAAABkAAMAEBEAEAAAAC","1196":[185,124,128,130],"1200":[185,55,111,124],"1213":"b:QAAAAAACiAAAAAAAAIABUAUAAAEAAAAC","1219":[185,55,111,112,124,128,130],"1237":"b:QAAAAAACiAAAAAAAAIABUAUAAAAAAAAC","1242":[185,51,55,111,112,124,128,130,6],"1249":"b:x/TfkACReAFcICAAvuwB8t/fl/kBAAAC","1253":"b:xvTfkACReAFMICAAvuQB8t/fl/kBAAAC","1261":[0,2,185,40,12,124,135,137,138,140],"1269":"b:AAAIAAAAAAAcAAAAAAAAcAcAAHkCAAAC","1277":"b:AAAIAAAAAAAcAAAAAAAAcAcAAHkCAAAC","1294":"b:x/TFkACReAFcICQApmQB8N/fl3kAAAAC","1304":"b:RQAAEAAAAAAAAAQAgCAA8AOAAQEAAAAC","1308":[185],"1312":"b:x/Xf8O3/+cHdYDQC//0B//////8DAAAC","1320":[185,126],"1324":[185,126,133],"1328":[185,126],"1332":[185,126],"1336":"b:x/Xf8O3/+cHdYDQC//0B//////8DAAAC","1345":[185],"1349":"b:x/Xf8O3/+cHdYDQC//0B//////8DAAAC","1355":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAC","1356":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAC","1364":[185],"1365":[185],"1382":"b:AgAAAAAAAAAAAAAAACAAYAcCBAEAAAAC","1384":[185,125,126,129,152],"1398":[185],"1402":"b:AgAAAAAAAAAAAAAAACAAYAcCBAEAAAAC","1417":[185],"1447":"b:AgAAAAAAAAAAAAAAACAAYAcCBAEAAAAC","1531":[19,185,110,122],"1562":[185,124],"1566":[185,124],"1576":"b:x/Xf8O3/+cHdYDQC//0B//////8DAAAC","1580":"b:x/Xf8O3/+cHdYDQC//0B//////8DAAAC","1585":[185],"1593":[185],"1602":[185],"1603":[185],"1608":[185],"1612":[185],"1620":[185],"1639":"b:AGAAAAgQGABEAAAAIAEAYAGFRQEBAAAC","1646":[185,84,85,123],"1654":[185,84,85,123],"1662":"b:x/Xf8O3/+cHdYDQC//0B//////8DAAAC","1673":"b:QgAAACADCAAJAAAAAAAAQuAAQHACAAAC","1684":"b:x/Xf8O3/+cHdYDQC//0B//////8DAAAC","1693":"b:x/Xf8O3/+cHdYDQC//0B//////8DAAAC","1709":"b:x/Xf8O3/+cHdYDQC//0B//////8DAAAC","1719":"b:RQAAEAAAAAAAAAQAgCAA8AOAAQEAAAAC","1723":"b:x/Xf8O3/+cHdYDQC//0B//////8DAAAC","1726":[185],"1733":[185],"1760":"b:x/Xf8O3/+cHdYDQC//0B//////8DAAAC","1804":[185,128],"1821":"b:x/Xf8O3/+cHdYDQC//0B//////8DAAAC","1849":[185],"1856":"b:x/XP8OX/+MFVQDQCu/wB/v3///sCAAAC","1866":[185,51,55,111,112,124,128,130,6],"1871":"b:ggQAEAAAAAAAIAAABkAAMAEBEAEAAAAC","1873":"b:x/Xf8O3/+cHdYDQC//0B//////8DAAAC","1877":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAC","1889":[185],"1893":"b:x/Xf8O3/+cHdYDQC//0B//////8DAAAC"},"packages/frontend/editor-ui/src/features/workflows/canvas/components/WorkflowCanvas.vue":{"82":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAA","83":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAA","87":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAA","93":"b:QgAAACADCAAJAAAAAAAAQuAAQHACAAAA","97":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAA","102":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAA","105":"b:x/Xf8O3/+cHdYAQC//0B8/////8DAAAA","106":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAA","107":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAA","108":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAA","112":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAA","113":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAA","114":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAA","118":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAA","119":[123],"127":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAA","128":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAA","129":"b:x/Xf8G3/+cHdYAQC//0B8/////8DAAAA","132":"b:x/XP8OX/+MFdYDQC//wB+/3///sDAAAA","148":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAA","163":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAA","171":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAA","178":[128],"179":[128],"180":[128],"182":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAA","188":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAA","191":[128],"192":[128],"193":[128],"194":[128],"200":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAA","210":[128],"215":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAA","231":[126,130,146,66,159],"239":[126,130,146,66,159],"245":[126,130,146,66,159],"296":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAA"},"packages/frontend/editor-ui/src/features/workflows/canvas/components/elements/background/CanvasBackground.vue":{"13":"b:x/Xf8O3/+cHdYDQC//0B//////8DAAAC"},"packages/frontend/editor-ui/src/features/workflows/canvas/components/elements/buttons/CanvasChatButton.vue":{"9":[82,1,7,2,8,10,108,109,123,6,13]},"packages/frontend/editor-ui/src/features/workflows/canvas/components/elements/buttons/CanvasControlButtons.vue":{"10":"b:x/Xf8O3/+cHdYDQC//0B//////8DAAAC","36":"b:x/Xf8O3/+cHdYDQC//0B//////8DAAAC","38":"b:x/Xf8O3/+cHdYDQC//0B//////8DAAAC","40":"b:x/Xf8O3/+cHdYDQC//0B//////8DAAAC","42":[185,126],"46":[185,126,133],"50":[185,126],"54":"b:RwAAAAAAAAEAAAQAACAB8IWDg4EAAAAC","58":[185,110],"62":[185],"64":"b:x/Xf8O3/+cHdYDQC//0B//////8DAAAC","71":"b:x/Xf8O3/+cHdYDQC//0B//////8DAAAC","82":"b:x/Xf8O3/+cHdYDQC//0B//////8DAAAC","92":"b:x/Xf8O3/+cHdYDQC//0B//////8DAAAC","107":[185],"126":"b:RCAAAAABAAAAAAAAAAAARKAAgwAAAAAC","140":"b:x/Xf8G3/+cHdYAQC//0B8/////8DAAAC","149":"b:x/Xf8G3/+cHdYAQC//0B8/////8DAAAC","157":[185],"170":[185]},"packages/frontend/editor-ui/src/features/workflows/canvas/components/elements/buttons/CanvasRunWorkflowButton.vue":{"40":"b:R/HP8GX/+MFdYAAC/8wB8/3///sDAAAA","44":"b:R/HP8GX/+MFdYAAC/8wB8/3///sDAAAA","45":"b:R/HP8GX/+MFdYAAC/8wB8/3///sDAAAA","49":"b:R/HP8GX/+MFdYAAC/8wB8/3///sDAAAA","60":[19,47,130,135,137,146],"62":[19,47,130,135,137,146],"63":[19,47,130,135,137,146],"69":[19,47,130,135,137,146],"76":"b:R/HP8GX/+MFdYAAC/8wB8/3///sDAAAA","97":"b:R/HP8GX/+MFdYAAC/8wB8/3///sDAAAA","108":"b:QgDEwAX3oAEBAAAAoAAAYMHDh3EAAAAA","109":"b:QADEwAX3oAEBAAAAoAAAYMHDh3EAAAAA","111":"b:R/HP8GX/+MFdYAAC/8wB8/3///sDAAAA","118":[19,47,130,135,137,146],"136":[19,47,130,135,137,146]},"packages/frontend/editor-ui/src/features/workflows/canvas/components/elements/buttons/CanvasStopCurrentExecutionButton.vue":{"6":"b:x5DFwCX/oAEBAAQA4CQAeMj/l3ECAAAA","13":"b:x5DFwCX/oAEBAAQA4CQAeMj/l3ECAAAA","18":"b:x5DFwCX/oAEBAAQA4CQAeMj/l3ECAAAA"},"packages/frontend/editor-ui/src/features/workflows/canvas/components/elements/buttons/CanvasStopWaitingForWebhookButton.vue":{"5":"b:xgCAQAAAAAAAAAQAoCAACIAABAAAAAAA","9":"b:xgCAQAAAAAAAAAQAoCAACIAABAAAAAAA"},"packages/frontend/editor-ui/src/features/workflows/canvas/components/elements/edges/CanvasArrowHeadMarker.vue":{"6":"b:x/Xf8O3/+cHdYDQC//0B//////8DAAAC"},"packages/frontend/editor-ui/src/features/workflows/canvas/components/elements/edges/CanvasEdge.vue":{"36":"b:x/XP8OX/+MFVQDQCu/wB/v3///sCAAAC","44":"b:x/XP8OX/+MFVQDQCu/wB/v3///sCAAAC","46":"b:x/XP8OX/+MFVQDQCu/wB/v3///sCAAAC","47":"b:x/XP8OX/+MFVQDQCu/wB/v3///sCAAAC","53":"b:x/XP8OX/+IFVQCQCsvQB8v3//3sCAAAC","61":"b:x/XP8OX/+MFVQDQCu/wB/v3///sCAAAC","63":"b:x/XP8OX/+MFVQDQCu/wB/v3///sCAAAC","65":"b:x/XP8OX/+MFVQDQCu/wB/v3///sCAAAC","67":"b:x/XP8OX/+MFVQDQCu/wB/v3///sCAAAC","72":"b:x/XP8OX/+MFVQDQCu/wB/v3///sCAAAC","78":"b:x/XP8OX/+MFVQDQCu/wB/v3///sCAAAC","83":"b:x/XP8OX/+MFVQDQCu/wB/v3///sCAAAC","90":"b:x/XP8OX/+MFVQDQCu/wB/v3///sCAAAC","96":"b:x/XP8OX/+MFVQDQCu/wB/v3///sCAAAC","98":"b:x/XP8OX/+MFVQDQCu/wB/v3///sCAAAC","100":[185,55,111,124,128,130],"110":"b:x/XP8OX/+MFVQDQCu/wB/v3///sCAAAC","120":"b:x/XP8OX/+MFVQDQCu/wB/v3///sCAAAC","121":"b:x/XP8OX/+MFVQDQCu/wB/v3///sCAAAC","125":"b:x/XP8OX/+MFVQDQCu/wB/v3///sCAAAC","136":[185,55,111,124],"140":[185,124,128,130],"144":[185,51,55,111,112,124,128,130,6],"148":[185,51,55,111,112,124],"150":"b:x/XP8OX/+MFVQDQCu/wB/v3///sCAAAC","164":"b:x/XP8OX/+MFVQDQCu/wB/v3///sCAAAC"},"packages/frontend/editor-ui/src/features/workflows/canvas/components/elements/edges/utils/getEdgeRenderData.ts":{"11":"b:x/XP8OX/+MFVYDQCv/wB/v3///sCAAAC","13":"b:x/XP8OX/+MFVYDQCv/wB/v3///sCAAAC"},"packages/frontend/editor-ui/src/features/workflows/canvas/components/elements/handles/CanvasHandleRenderer.vue":{"15":"b:x/XP8OX/+MFdYDQC//wB//3///sDAAAC","43":"b:x/XP8OX/+MFdYDQC//wB//3///sDAAAC","47":"b:x/XP8OX/+MFdYDQC//wB//3///sDAAAC","53":"b:x/XP8OX/+MFdYDQC//wB//3///sDAAAC","57":"b:x/XP8OX/+MFdYDQC//wB//3///sDAAAC","63":"b:x/XP8OX/+MFdYDQC//wB//3///sDAAAC","69":"b:x/XP8OX/+MFdYDQC//wB//3///sDAAAC","77":"b:x/XP8OX/+MFdYDQC//wB//3///sDAAAC","87":"b:x/XP8OX/+MFdYDQC//wB//3///sDAAAC","89":"b:x/XP8OX/+MFdYDQC//wB//3///sDAAAC","113":"b:ggQAEAAAAAAAIAAABkAAMAABEAAAAAAC","142":"b:x/XP8OX/+MFdYDQC//wB//3///sDAAAC"},"packages/frontend/editor-ui/src/features/workflows/canvas/components/elements/handles/render-types/CanvasHandleMainInput.vue":{"14":"b:x/XP8OX/+MFVQDQCu/wB/v3///sCAAAC","22":"b:x/XP8OX/+MFVQDQCu/wB/v3///sCAAAC","26":"b:x/XP8OX/+MFVQDQCu/wB/v3///sCAAAC"},"packages/frontend/editor-ui/src/features/workflows/canvas/components/elements/handles/render-types/CanvasHandleMainOutput.vue":{"22":"b:x/XP8GX/+MFdYAQC//wB9/3///sDAAAC","27":"b:x/XP8OX/+MFdYDQC//wB//3///sDAAAC","29":"b:x/XP8OX/+MFdYDQC//wB//3///sDAAAC","38":"b:x/XP8GX/+MFdYAQC//wB9/3///sDAAAC","40":"b:x5BBwGX3sAEBAAAAoAQAYMjfl3EAAAAC","49":"b:x/XP8GX/+MFdYAQC//wB9/3///sDAAAC","51":"b:x/XP8GX/+MFdYAQC//wB9/3///sDAAAC","53":"b:x/XP8GX/+MFdYAQC//wB9/3///sDAAAC","58":"b:QAAIUACQsAAAACACgogAIKXKB4ADAAAC","63":"b:x5BBwGX3sAEBAAAAoAQAYMjfl3EAAAAC","70":"b:x/XP8OX/+MFdYDQC//wB//3///sDAAAC","75":"b:AAAAEAAAAAAAIAAABkAAcAABEAAAAAAC","79":[185,28,97,98,110,124,125,136,148],"83":"b:AAAAEAAAAAAAIAAABkAAMAABEAAAAAAC","85":"b:x/XP8OX/+MFdYDQC//wB//3///sDAAAC"},"packages/frontend/editor-ui/src/features/workflows/canvas/components/elements/handles/render-types/CanvasHandleNonMainInput.vue":{"19":"b:x3UAAAAAAAAAAAAAABAAEgAAAAAAAAAC","21":"b:x3UAAAAAAAAAAAAAABAAEgAAAAAAAAAC","27":"b:x3UAAAAAAAAAAAAAABAAEgAAAAAAAAAC","33":"b:wnUAAAAAAAAAAAAAABAAEgAAAAAAAAAC","39":"b:x3UAAAAAAAAAAAAAABAAEgAAAAAAAAAC","44":[1,7,185,10],"48":[1,7,185,10],"52":[1,7,185,10],"54":"b:x3UAAAAAAAAAAAAAABAAEgAAAAAAAAAC"},"packages/frontend/editor-ui/src/features/workflows/canvas/components/elements/handles/render-types/CanvasHandleNonMainOutput.vue":{"13":[0,1,7,2,185,8,10,12,108,6,13,14],"15":[0,1,7,2,185,8,10,12,108,6,13,14],"23":[0,1,7,2,185,8,10,12,108,6,13,14],"27":[0,1,7,2,185,8,10,12,108,6,13,14]},"packages/frontend/editor-ui/src/features/workflows/canvas/components/elements/handles/render-types/parts/CanvasHandleDiamond.vue":{"13":"b:x3UAAAAAAAAAAAAAABAAEgAAAAAAAAAC"},"packages/frontend/editor-ui/src/features/workflows/canvas/components/elements/handles/render-types/parts/CanvasHandleDot.vue":{"4":"b:x/XP8OX/+MFdYDQC//wB//3///sDAAAC","13":"b:x/XP8OX/+MFdYDQC//wB//3///sDAAAC","14":"b:x/XP8OX/+MFdYDQC//wB//3///sDAAAC"},"packages/frontend/editor-ui/src/features/workflows/canvas/components/elements/handles/render-types/parts/CanvasHandlePlus.vue":{"6":"b:x/XP8GX/+MFdYAQC//wB9/3///sDAAAC","34":"b:x/XP8GX/+MFdYAQC//wB9/3///sDAAAC","41":"b:x/XP8GX/+MFdYAQC//wB9/3///sDAAAC","57":"b:x/XP8GX/+MFdYAQC//wB9/3///sDAAAC","64":"b:x/XP8GX/+MFdYAQC//wB9/3///sDAAAC","89":"b:x/XP8GX/+MFdYAQC//wB9/3///sDAAAC","102":"b:ggQAEAAAAAAAIAAABkAAMAABEAAAAAAC","104":"b:x/XP8GX/+MFdYAQC//wB9/3///sDAAAC"},"packages/frontend/editor-ui/src/features/workflows/canvas/components/elements/nodes/CanvasNode.vue":{"49":"b:x/Xf8O3/+cHdYDQC//0B//////8DAAAC","89":"b:x/Xf8O3/+cHdYDQC//0B//////8DAAAC","90":"b:x/Xf8O3/+cHdYDQC//0B//////8DAAAC","91":"b:x/XP8OX/+MFdYDQC//wB//3///sDAAAC","105":"b:x/XP8OX/+MFdYDQC//wB//3///sDAAAC","107":"b:x/Xf8O3/+cHdYDQC//0B//////8DAAAC","115":[185],"118":"b:x/Xf8O3/+cHdYDQC//0B//////8DAAAC","120":"b:x/Xf8O3/+cHdYDQC//0B//////8DAAAC","136":"b:xPDFgACReAFAACAAICQBQNrfh0EAAAAC","146":"b:x/Xf8O3/+cHdYDQC//0B//////8DAAAC","150":"b:x/Xf8O3/+cHdYDQC//0B//////8DAAAC","154":"b:x/Xf8O3/+cHdYDQC//0B//////8DAAAC","161":"b:x/Xf8O3/+cHdYDQC//0B//////8DAAAC","165":"b:x/XP8OX/+MFdYDQC//wB//3///sDAAAC","172":"b:x/Xf8O3/+cHdYDQC//0B//////8DAAAC","249":"b:ggQAEAAAAAAAIAAABkAAMAABEAAAAAAC","253":[185,107,124,6],"257":[0,2,185,40,12,124,135,137,138,140],"261":[185,121,124,6],"265":"b:xPDFgACReAFAACAAICQBANjfh0EAAAAC","269":[185],"273":[185,126],"277":"b:AgAAAAAAAAAAAAAAACAAYAcCBAEAAAAC","281":[185],"285":[185],"289":[185],"293":[185],"297":[185],"301":[185],"304":[185],"326":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAC","331":"b:x/Xf8O3/+cHdYDQC//0B//////8DAAAC","340":"b:x/Xf8O3/+cHdYDQC//0B//////8DAAAC","341":"b:xvXHsAXZ+MFcICQC//wB89////sBAAAC","347":[1,185,161],"353":[185,89,103,125,161],"359":"b:x/Xf8O3/+cHdYDQC//0B//////8DAAAC","364":"b:wuXfsCVbqMENIBAC/9gBc/+8ffsDAAAC","368":"b:x/Xf8O3/+cHdYDQC//0B//////8DAAAC","378":"b:x/XP8OX/+MFdYDQC//wB//3///sDAAAC","395":"b:x/XP8OX/+MFVQDQCu/wB/v3///sCAAAC"},"packages/frontend/editor-ui/src/features/workflows/canvas/components/elements/nodes/CanvasNodeRenderer.vue":{"11":"b:x/Xf8O3/+cHdYDQC//0B//////8DAAAC","13":"b:x/Xf8O3/+cHdYDQC//0B//////8DAAAC","37":"b:x/Xf8O3/+cHdYDQC//0B//////8DAAAC"},"packages/frontend/editor-ui/src/features/workflows/canvas/components/elements/nodes/CanvasNodeToolbar.vue":{"17":"b:x/XP8OX/+MFdYDQC//wB+/////sDAAAC","54":[0,1,7,2,185,8,10,12,108,6,13,14],"57":[0,1,7,2,185,8,10,12,108,6,13,14],"59":"b:x/XP8GX/+MFdYAQC//wB8/3///sDAAAC","66":"b:x/XP8OX/+MFdYDQC//wB+/////sDAAAC","73":"b:x/XP8OX/+MFdYDQC//wB+/////sDAAAC","91":"b:x/XP8OX/+MFdYDQC//wB+/////sDAAAC","98":"b:x/XP8OX/+MFdYDQC//wB+/////sDAAAC","100":"b:x/XP8OX/+MFdYDQC//wB+/////sDAAAC","105":"b:x/XP8OX/+MFdYDQC//wB+/////sDAAAC","113":"b:x/XP8OX/+MFdYDQC//wB+/////sDAAAC","117":[0,2,185,40,12,124,135,137,138,140],"121":[185,121,124,6],"125":[185,107,124,6],"129":[185],"135":[185,126],"139":"b:RxAAAAAAAAAAAAAAAAgAUoEWAAEAAAAC","143":"b:RhAAAAAAAAAAAAAAAAAAQoEWAAEAAAAC","147":[185],"153":[185],"156":"b:x/XP8OX/+MFdYDQC//wB+/////sDAAAC","166":"b:RRAAAAAAAAAAAAAAAAgAUoAWAAAAAAAC","167":[185],"174":"b:x/XP8GX/+MFdYAQC//wB+/3///sDAAAC","188":"b:x/XP8GX/+MFdYAQC//wB8/3///sDAAAC","201":"b:x/XP8GX/+MFdYAQC//wB8/////sDAAAC","225":[185],"236":"b:x/XP8OX/+MFdYDQC//wB+/////sDAAAC"},"packages/frontend/editor-ui/src/features/workflows/canvas/components/elements/nodes/render-types/CanvasNodeDefault.vue":{"23":"b:x/XP8OX/+MFdYDQC//wB//3///sDAAAC","55":"b:x/XP8OX/+MFdYDQC//wB//3///sDAAAC","56":"b:x/XP8OX/+MFdYDQC//wB//3///sDAAAC","57":"b:x/XP8OX/+MFdYDQC//wB//3///sDAAAC","60":"b:x/XP8OX/+MFdYDQC//wB//3///sDAAAC","65":"b:x/XP8OX/+MFdYDQC//wB//3///sDAAAC","70":"b:x/XP8OX/+MFdYDQC//wB//3///sDAAAC","79":"b:x/XP8OX/+MFdYDQC//wB//3///sDAAAC","80":[185],"82":"b:x/XP8OX/+MFdYDQC//wB//3///sDAAAC","107":"b:x/XP8OX/+MFdYDQC//wB//3///sDAAAC","113":"b:x/XP8OX/+MFdYDQC//wB//3///sDAAAC","126":"b:x/XP8OX/+MFdYDQC//wB//3///sDAAAC","133":"b:x/XP8OX/+MFdYDQC//wB//3///sDAAAC","146":"b:x/XP8OX/+MFdYDQC//wB//3///sDAAAC","155":"b:x/XP8OX/+MFdYDQC//wB//3///sDAAAC","180":"b:R3AAQOC3WIBdQDQAICQA7OHLw3EDAAAC","186":"b:x/XP8OX/+MFdQDQC+/wB/v3///sDAAAC","188":"b:x/XP8OX/+MFdQDQC+/wB/v3///sDAAAC","193":"b:AgAAAAAAAAAAAAAAACAAYAUCBAEAAAAC","197":"b:xPDFgACReAFAACAAICQBANjfh0EAAAAC","204":"b:x/XP8OX/+MFdYDQC//wB//3///sDAAAC"},"packages/frontend/editor-ui/src/features/workflows/canvas/components/elements/nodes/render-types/parts/CanvasNodeSettingsIcons.vue":{"12":"b:x/XP8OX/+MFdYDQC//wB//3///sDAAAC","13":"b:x/XP8OX/+MFdYDQC//wB//3///sDAAAC","19":[185],"28":[185,143],"36":[185],"45":[185],"53":[185],"62":[185],"72":[185],"81":[185]},"packages/frontend/editor-ui/src/features/workflows/canvas/components/elements/nodes/render-types/parts/CanvasNodeStatusIcons.vue":{"24":"b:x/XP8OX/+MFdYDQC//wB//3///sDAAAC","40":"b:x/XP8OX/+MFdYDQC//wB//3///sDAAAC","43":"b:x/XP8OX/+MFdYDQC//wB//3///sDAAAC","44":"b:x/XP8OX/+MFdIDQC//wB//3///sDAAAC","49":"b:x/XP8OX/+MFdIDQC//wB//3///sDAAAC","54":"b:x/XP8OX/+MFdIDQC//wB//3///sDAAAC","57":"b:wvXPMIQIaMBQQAACu9AAUtCILroCAAAC","58":[185],"59":"b:x/XP8OX/+MFdIDQC//wB//3///sDAAAC","63":"b:x/XP8OX/+MFRQCQC+/QA+tz/v/sCAAAC","69":[185],"70":[185],"78":[185],"82":"b:x/XP8OX/+MFdYDQC//wB//3///sDAAAC","91":[185],"92":[185],"104":[185],"106":"b:woABAAQAAAAAAAAAAAAAAMCAADAAAAAC","116":[185],"118":"b:wvXPMIQIaMBQQAACu9AAUtCILroCAAAC","136":[185],"139":[185,103,106,55,131,137,146,148,152]},"packages/frontend/editor-ui/src/features/workflows/canvas/components/elements/nodes/render-types/parts/CanvasNodeStatusMark.vue":{"5":"b:x5DFwGX/sAEBAAQA4CQAeMj/l3EAAAAC"},"packages/frontend/editor-ui/src/features/workflows/canvas/components/elements/nodes/render-types/parts/CanvasNodeTooltip.vue":{"11":"b:xgCAQAAAAAAAAAQAoCAACIAABAACAAAC","13":"b:xgCAQAAAAAAAAAQAoCAACIAABAACAAAC","14":"b:xgCAQAAAAAAAAAQAoCAACIAABAACAAAC","26":"b:xgCAQAAAAAAAAAQAoCAACIAABAACAAAC","28":"b:xgCAQAAAAAAAAAQAoCAACIAABAACAAAC"},"packages/frontend/editor-ui/src/features/workflows/canvas/components/elements/nodes/render-types/parts/CanvasNodeTrigger.vue":{"33":"b:x/XP8OX/+MFdYDQC//wB//3///sDAAAC","34":"b:x/XP8OX/+MFdYDQC//wB//3///sDAAAC","45":"b:x/XP8GX/+MFdYAQC//wB9/3///sDAAAC","53":[185],"54":[185],"58":[82,1,7,2,185,8,10,108,109,6,13],"63":[185],"64":[82,1,7,2,185,8,10,108,109,6,13],"67":"b:x/XP8GX/+MFdYAQC//wB9/3///sDAAAC","68":"b:x/XP8GX/+MFdYAQC//wB9/3///sDAAAC","70":[185],"78":[185],"86":[185,47,135],"89":"b:x/XP8OX/+MFdYDQC//wB//3///sDAAAC","94":[185,47,135],"116":[82,1,7,2,185,8,10,108,109,6,13]},"packages/frontend/editor-ui/src/features/workflows/canvas/components/elements/selection/CanvasSelectionToolbar.vue":{"15":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAC","38":"b:x/Xf8G3/+cHdYAQC//0B8/////8DAAAC","39":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAC","47":"b:x/Xf8G3/+cHdYAQC//0B8/////8DAAAC","49":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAC","53":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAC","57":[185,128,152],"69":[185,111,128,152],"77":[185,128],"82":[185],"84":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAC","96":[185,128],"103":[185,111,128,152],"119":[185,128,152]},"packages/frontend/editor-ui/src/features/workflows/canvas/composables/useCanvas.ts":{"4":"b:x/Xf8O3/+cHdYDQC//0B//////8DAAAC"},"packages/frontend/editor-ui/src/features/workflows/canvas/composables/useCanvasAgentNodeGeometry.ts":{"18":"b:x/Xf8O3/+cHdYDQC//0B//////8DAAAC","21":"b:x/Xf8O3/+cHdYDQC//0B//////8DAAAC","45":"b:QgAAACADCAAJAAAAAAAAQuAAQHACAAAC","50":[185,109,124,125,128,130,66],"55":[185,109,124,125,128,130,66],"58":[185,109,124,125,128,130,66],"66":[185],"77":[185]},"packages/frontend/editor-ui/src/features/workflows/canvas/composables/useCanvasGroupIdStorage.ts":{"10":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAC","11":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAC","22":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAC","26":[185,128]},"packages/frontend/editor-ui/src/features/workflows/canvas/composables/useCanvasLayout.ts":{"65":"b:x/Xf8O3/+cHdYDQC//0B//////8DAAAC","78":[19,185,110,122],"82":[19,185,110,122],"87":[185],"91":[19,185,110,122],"101":[19,185,110,122],"106":[19,185,110,122],"107":[19,185,110,122],"108":[19,185,110,122],"115":[19,185,110,122],"119":[19,185,110,122],"123":[19,185,110,122],"163":[19,185,110,122],"165":[19,185,110,122],"168":[19,185,110,122],"172":[19,185,110,122],"174":[19,185,110,122],"181":[19,185,110,122],"183":[19,185,110,122],"185":[19,185,110,122],"190":[19,185,110,122],"204":[185],"209":[19,185,110,122],"210":[19,185,110,122],"216":[19,185,110,122],"217":[19,185,110,122],"222":[19,185,110,122],"231":[19,185],"233":[19,185,110,122],"237":[19,185,110,122],"245":[185],"259":[185],"264":[185],"265":[185],"271":[185],"272":[185],"278":[19,185,110,122],"279":[19,185,110,122],"300":[19,185,110,122],"310":[19,185,110,122],"319":[19,185,110,122],"321":[19,185,110,122],"325":[19,185,110,122],"330":[185],"344":[185],"350":[185],"368":[19,185,110,122],"376":[185],"380":[185],"390":[185],"391":[185],"392":[185],"398":[19,185,110,122],"404":[19,185,110,122],"405":[19,185,110,122],"410":[19,185,110,122],"417":[19,185,110,122],"421":[19,185,110,122],"426":[185],"437":[185],"440":[185],"448":[185],"459":[19,185,110,122],"468":[19,185,110,122],"471":[185],"477":[19,185,110,122],"490":[185],"501":[185],"520":[19,185,110,122],"532":[19,185,110,122],"533":[185],"536":[185],"542":[185],"543":[185],"583":[19,185,110,122],"594":[19,185,110,122],"595":[185],"599":[185],"601":[185],"609":[185],"610":[185],"628":[185],"637":[19,185,110,122],"651":[185]},"packages/frontend/editor-ui/src/features/workflows/canvas/composables/useCanvasMapping.groups.ts":{"45":[128],"53":[128],"70":[128],"92":[128],"99":[128],"117":[128],"123":[128],"171":[128],"186":[128],"216":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAA","229":[128],"236":[128],"241":[128],"248":[128],"277":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAA"},"packages/frontend/editor-ui/src/features/workflows/canvas/composables/useCanvasMapping.ts":{"48":"b:x/Xf8O3/+cHdYDQC//0B//////8DAAAC","66":"b:x/Xf8O3/+cHdYDQC//0B//////8DAAAC","72":"b:x/Xf8O3/+cHdYDQC//0B//////8DAAAC","82":"b:x/Xf8O3/+cHdYDQC//0B//////8DAAAC","111":"b:x/Xf8O3/+cHdYDQC//0B//////8DAAAC","113":[185,128],"119":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAC","138":"b:x/XP8OX/+MFVQDQCu/wB/v3///sCAAAC","147":"b:x/Xf8O3/+cHdYDQC//0B//////8DAAAC","154":"b:x/Xf8O3/+cHdYDQC//0B//////8DAAAC","206":"b:x/Xf8O3/+cHdYDQC//0B//////8DAAAC","209":"b:x/XP8OX/+MFVQDQCu/wB/v3///sCAAAC","218":"b:x/XP8OX/+MFVQDQCu/wB/v3///sCAAAC","246":"b:x/XP8OX/+MFVQDQCu/wB/v3///sCAAAC","249":"b:x/XP8OX/+MFVQDQCu/wB/v3///sCAAAC","261":[185],"270":"b:x/XP8OX/+MFVQDQCu/wB/v3///sCAAAC","271":"b:x/XP8OX/+MFVQDQCu/wB/v3///sCAAAC","283":"b:x/XP8OX/+MFVQDQCu/wB/v3///sCAAAC"},"packages/frontend/editor-ui/src/features/workflows/canvas/composables/useCanvasNode.ts":{"8":"b:x/XP8OX/+MFdYDQC//wB//////sDAAAC","10":"b:x/XP8OX/+MFdYDQC//wB//////sDAAAC","33":"b:x/XP8OX/+MFdYDQC//wB//////sDAAAC","34":"b:x/XP8OX/+MFdYDQC//wB//3///sDAAAC","36":"b:x/XP8OX/+MFdYDQC//wB//3///sDAAAC","37":"b:x/XP8OX/+MFdYDQC//wB//3///sDAAAC","38":"b:x/XP8OX/+MFdYDQC//wB//3///sDAAAC","40":"b:x/XP8OX/+MFdYDQC//wB//3///sDAAAC","41":[185,53,111,128,129,135,151,159,161],"42":"b:x/XP8OX/+MFdYDQC//wB//////sDAAAC","44":[185],"45":[185],"46":"b:x/XP8OX/+MFdYDQC//wB//3///sDAAAC","48":"b:x/XP8OX/+MFdYDQC//wB//3///sDAAAC","49":"b:x/XP8OX/+MFdYDQC//wB//3///sDAAAC","50":"b:x/XP8OX/+MFdYDQC//wB//3///sDAAAC","51":"b:x/XP8OX/+MFdYDQC//wB//3///sDAAAC","53":"b:x/XP8OX/+MFdYDQC//wB//3///sDAAAC","54":"b:x5DFwGX/sAEBAAQA4CQAeMj/l3EAAAAC","55":"b:x/XP8OX/+MFdYDQC//wB//3///sDAAAC","57":"b:x/XP8OX/+MFdYDQC//wB//////sDAAAC","59":[185,53,111,128,129,135,151,159,161],"63":"b:x/XP8OX/+MFdYDQC//wB//3///sDAAAC"},"packages/frontend/editor-ui/src/features/workflows/canvas/composables/useCanvasNodeGroupActions.ts":{"20":"b:x/Xf8O3/+cHdYDQC//0B//////8DAAAC","29":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAC","31":"b:x/Xf8G3/+cHdYAQC//0B8/////8DAAAC","33":"b:xvXHsAXZ6MFUIAQC//wB89////sBAAAC","34":"b:xvXHsAXZ6MFUIAQC//wB89////sBAAAC","37":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAC","42":[185],"61":[185],"70":[185,128],"82":[185,128],"86":[185,128],"99":[185],"112":[185,128]},"packages/frontend/editor-ui/src/features/workflows/canvas/composables/useCanvasNodeGroupDescriptionVisibility.ts":{"16":[185,128],"33":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAC","39":[185],"43":[185,128],"47":[185,128],"59":[185],"65":[185],"82":[185,128],"88":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAC","89":[185],"91":[185],"94":[185,84,85,52,128,67,157,158],"110":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAC","114":[185]},"packages/frontend/editor-ui/src/features/workflows/canvas/composables/useCanvasNodeGroupDrag.ts":{"38":"b:x/Xf8O3/+cHdYDQC//0B//////8DAAAC","46":[185,128],"52":[185,128],"71":[185,128],"82":[185,128],"108":[185,128],"113":[185,109,124,125,128,130,66],"118":[185,109,124,125,128,130,66],"130":[185,109,124,125,128,130,66],"131":[185,109,124,125,128,130,66],"142":[185,109,124,125,128,130,66],"190":[185,128],"198":[185,109,124,125,128,130,66],"205":[185,109,124,125,128,130,66],"223":[185,109,124,125,128,130,66],"235":[185,109,124,125,128,130,66],"246":[185,128],"255":[185,109,124,125,128,130,66],"274":[185],"284":[185],"288":[185]},"packages/frontend/editor-ui/src/features/workflows/canvas/composables/useCanvasNodeGroupLayout.ts":{"68":[185,128],"80":[185,128],"109":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAC","121":[185,128],"128":[185,128],"132":[185,128],"136":[185,128],"140":[185,128],"144":[185,128],"155":[185,128],"159":[185,128],"239":[185],"255":[185,128],"265":[185,128],"271":[185],"274":[185,128],"279":[185,128],"282":[185,128],"286":[185],"297":[185,128],"304":[185],"336":[185,128],"354":[185],"360":[185],"368":[185,128],"376":[185],"396":[185],"397":[185],"416":[185,128],"478":[185,128],"500":[185,128],"512":[185,128],"514":[185,128]},"packages/frontend/editor-ui/src/features/workflows/canvas/composables/useCanvasNodeGroupOperationGuards.ts":{"57":"b://Xf/P///+39//////////////8DAAAA","59":"b:wuXPsAVYqMEEAAACu9gBcl29fYsAAAAA","69":[128],"89":[128],"98":[128],"106":[128],"128":"b:wuXPsAVYqMEEAAACu9gBcl29fYsAAAAA","140":[128],"143":[128],"227":[128],"237":[128],"253":[128],"272":[128],"277":"b:wuXPsAVYqMEEAAACu9gBcl29fYsAAAAA","323":[19,124,128,130]},"packages/frontend/editor-ui/src/features/workflows/canvas/composables/useCanvasNodeGroupSelection.ts":{"36":"b:x/Xf8O3/+cHdYDQC//0B//////8DAAAC","51":"b:x/Xf8O3/+cHdYDQC//0B//////8DAAAC","53":"b:x/Xf8O3/+cHdYDQC//0B//////8DAAAC","56":[185,128],"61":"b:xvXHsAXZ+MFcICQC//wB89////sBAAAC","68":"b:xvXHsAXZ+MFcICQC//wB89////sBAAAC","75":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAC","76":"b:xvXHsAXZ+MFcICQC//wB89////sBAAAC","77":"b:xvXHsAVY+MFUAAQC8vwB8t/+/3sAAAAC","91":[185,128],"106":[185,128],"116":[185,128],"117":[185,128],"126":[185,128],"127":[185],"129":[185],"137":"b:x/XHsAXZ+MFcICQC//wB89////sBAAAC","143":[185,128],"154":"b:RQAAEAAAAAAAAAQAgCAA8AOAAQEAAAAC","163":"b:x/Xf8O3/+cHdYDQC//0B//////8DAAAC","175":"b:xvXHsAXZ+MFcICQC//wB89////sBAAAC","180":"b:xvXHsAXZ+MFcICQC//wB89////sBAAAC","195":"b:x/Xf8O3/+cHdYDQC//0B//////8DAAAC"},"packages/frontend/editor-ui/src/features/workflows/canvas/composables/useCanvasNodeGroupTelemetry.ts":{"19":"b://Xf/P///+39//////////////8DAAAA","24":[128],"37":[128],"40":[128],"43":[128],"46":[128]},"packages/frontend/editor-ui/src/features/workflows/canvas/composables/useCanvasNodeGroupView.ts":{"37":[185,128],"62":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAC","66":[185,109,124,125,128,130,66],"69":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAC","74":[185,128],"77":[185,128],"81":[185,128],"89":[185,128],"92":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAC","94":[185,128],"99":[185,128],"106":[185,128],"107":[185,128],"109":[185,128],"115":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAC","126":[185,128],"132":[185,128],"139":[185,128],"143":[185,128],"148":[185,128],"150":[185,128],"154":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAC","158":[185,128],"162":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAC","167":[185,128],"177":[185,128],"181":[185],"182":[185],"196":[185,128],"208":[185,128],"223":[185,109,124,125,128,130,66],"230":[185,128],"259":[185],"263":[185,128],"268":[185],"282":[185],"294":[185,84,85,52,128,67,157,158],"296":[185],"314":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAC"},"packages/frontend/editor-ui/src/features/workflows/canvas/composables/useCanvasNodeHandle.ts":{"11":"b:x/XP8OX/+MFdYDQC//wB//3///sDAAAC","14":"b:x/XP8OX/+MFdYDQC//wB//3///sDAAAC","15":"b:x/XP8OX/+MFdYDQC//wB//3///sDAAAC","16":"b:x/XP8GX/+MFdYAQC//wB9/3///sDAAAC","17":"b:x/XP8OX/+MFdYDQC//wB//3///sDAAAC","18":"b:x/XP8OX/+MFdYDQC//wB//3///sDAAAC","19":[0,1,7,2,185,10,12,6,13],"20":[185],"21":[185],"22":[185],"23":"b:x/XP8OX/+MFdYDQC//wB//3///sDAAAC"},"packages/frontend/editor-ui/src/features/workflows/canvas/composables/useCanvasNodeHover.ts":{"10":"b:x/Xf8O3/+cHdYDQC//0B//////8DAAAC","17":"b:x/TfkACR+AFcICQAvuwB8v/fl/kBAAAC","31":"b:x/TFkACR+AFcICQApuwB8v3fl/kAAAAC","64":[185],"73":"b:x/Xf8O3/+cHdYDQC//0B//////8DAAAC","77":"b:QgAAACADCAAJAAAAAAAAQuAAQHACAAAC"},"packages/frontend/editor-ui/src/features/workflows/canvas/composables/useCanvasTraversal.ts":{"4":"b:x/Xf8O3/+cHdYDQC//0B//////8DAAAC","5":[185,124],"6":[185],"9":[185,124],"13":[185,124],"17":[185],"18":[185],"21":[185],"26":[185],"27":[185],"32":[185,152],"42":[185,152],"43":[185,152],"46":[185,124],"56":[185,124],"57":[185,124]},"packages/frontend/editor-ui/src/features/workflows/canvas/composables/useNodeGroupsSubscription.ts":{"18":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAC","21":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAC","30":"b:QgAAACADCAAJAAAAAAAAQuAAQHACAAAC"},"packages/frontend/editor-ui/src/features/workflows/canvas/composables/useViewportAutoAdjust.ts":{"7":"b:x/Xf8O3/+cHdYDQC//0B//////8DAAAC","21":"b:x/Xf8O3/+cHdYDQC//0B//////8DAAAC","27":"b:x/Xf8O3/+cHdYDQC//0B//////8DAAAC","46":"b:QgAAACADCAAJAAAAAAAAQuAAQHACAAAC","51":"b:x/Xf8O3/+cHdYDQC//0B//////8DAAAC"},"packages/frontend/editor-ui/src/features/workflows/canvas/composables/useVueFlowTransformPaneTeleport.ts":{"9":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAC","14":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAC","16":"b:x/Xf8O3/+cHdYDQC//0B+/////8DAAAC"},"packages/frontend/editor-ui/src/features/workflows/canvas/composables/useZoomAdjustedValues.ts":{"8":"b:x/XP8OX/+MFdYDQC//wB//3///sDAAAC","17":"b:x/XP8OX/+MFdYDQC//wB//3///sDAAAC","39":"b:x/XP8OX/+MFdYDQC//wB//3///sDAAAC","40":"b:x/XP8OX/+MFdYDQC//wB//3///sDAAAC","60":"b:x/XP8OX/+MFdYDQC//wB//3///sDAAAC","61":"b:x/XP8OX/+MFdYDQC//wB//3///sDAAAC","76":"b:x/XP8OX/+MFdYDQC//wB//3///sDAAAC","77":"b:x/XP8OX/+MFdYDQC//wB//3///sDAAAC","92":"b:x/XP8OX/+MFdYDQC//wB//3///sDAAAC","94":"b:x/XP8OX/+MFdYDQC//wB//3///sDAAAC"},"packages/frontend/editor-ui/src/features/workflows/canvas/experimental/composables/useIsInExperimentalNdv.ts":{"4":"b:1vXPsI2Z+O1UACAy+/SJ8tz//0sBAAAA","8":"b:AEAAAAAAACQAAAAAAAABgAA8HgAAAAAA"},"packages/frontend/editor-ui/src/features/workflows/canvas/experimental/experimentalNdv.store.ts":{"17":"b://Xf/P///+39//////////////8DAAAA","19":"b:x/Xf8O3/+MHdYDQC//0B//////8DAAAA","24":"b:x/Xf8O3/+OndYDQC//0J+/////8DAAAA","54":"b:x/Xf8O3/+cHdYDQC//0B//////8DAAAA","62":[19,112,127],"140":"b:x/Xf8O3/+MHdYDQC//0B//////8DAAAA","141":"b:x/Xf8O3/+MHdYDQC//0B+/////8DAAAA"},"packages/frontend/editor-ui/src/features/workflows/readyToRun/composables/useEmptyStateDetection.ts":{"13":"b:x/Xf/P///8X9f4/D//+F+/////8DAAAA","26":"b:AAAADBAADABkAINBAAIAACAAQAADAAAA","45":"b:AAAADBAAD8BsAANAAAMAACAAQAADAAAA"},"packages/frontend/editor-ui/src/features/workflows/readyToRun/stores/readyToRun.store.ts":{"22":"b:x/Xf/P///8X9f4/D//+F+/////8DAAAA","59":"b:x/Xf/P///8X9f4/D//+F+/////8DAAAA","66":"b:x/Xf/P///8X9f4/D//+F+/////8DAAAA","74":"b:x/Xf/P///8X9f4/D//+F+/////8DAAAA","90":"b:x/Xf/P///8X9f4/D//+F+/////8DAAAA","91":"b:x/Xf/P///8X9f4/D//+F+/////8DAAAA","110":"b:AAECLBwAD8RsAANAAAOEACAAQAADAAAA","208":"b:AAECJBwAD8RsAABAAAOEACAAQAADAAAA","220":"b:AAAADBAAD8BsAANAAAMAACAAQAADAAAA","224":[78]},"packages/frontend/editor-ui/src/features/workflows/templates/components/SetupWorkflowCredentialsButton.vue":{"28":[48,78],"32":"b:x/Xf8G3/+cHdYAQC//0B8/////8DAAAA","36":"b:x/Xf8G3/+cHdYAQC//0B8/////8DAAAA","43":"b:w/XP8GX/+MFdYAAC/9gB8//+//sDAAAA","48":"b:w/XP8GX/+MFdYAAC/9gB8//+//sDAAAA","51":"b:x/Xf8G3/+cHdYAQC//0B8/////8DAAAA","57":[48,78,68,161],"61":"b:x/Xf8G3/+cHdYAQC//0B8/////8DAAAA","78":[48,78,68,161],"86":"b:wmUCMAAIKcAQAAACm5AAQhCALIgAAAAA","96":[48],"101":[48,78,68],"105":[48,78,68],"113":"b:QgAYACABCQAJAAAAAAAAQuAAQHAAAAAA","117":"b:x/Xf8G3/+cHdYAQC//0B8/////8DAAAA","133":"b:x/Xf8G3/+cHdYAQC//0B8/////8DAAAA"},"packages/frontend/editor-ui/src/features/workflows/templates/templates.store.ts":{"49":[161],"55":"b://Xf/P///+39//////////////8DAAAA","90":[161],"91":[161],"96":[68],"97":[68],"100":[68,161],"101":[68,161],"107":[68],"109":[161],"110":[161],"113":[161],"114":[161],"121":[161],"125":[161],"126":[161],"133":[161],"137":[161],"138":[161],"166":"b:x/Xf/P///8X9f4/D//+F8/////8DAAAA","170":"b:x/Xf/P///8X9f4/D//+F8/////8DAAAA","183":"b:x/Xf/P///8X9f4/D//+F8/////8DAAAA","191":"b:x/Xf/P///8X9f4/D//+F8/////8DAAAA","209":"b:x/Xf/P///8X9f4/D//+F8/////8DAAAA","214":[14],"221":[161],"225":[68,161],"228":[68,161],"229":[68,161],"240":[68,161],"241":[68,161],"247":[161],"251":[161],"259":[161],"264":[161],"309":"b://Xf/P///+39f7////////////8DAAAA","314":[78,68,161],"320":[68,161],"336":[68],"354":[161],"370":[161],"385":[161],"390":[161],"436":[78,68,161],"444":[78,68,161],"452":[78,68,161]},"packages/frontend/editor-ui/src/features/workflows/templates/utils/typeGuards.ts":{"8":[185,68],"14":[185,68],"20":"b://Xf/P///+39//////////////8DAAAC"},"packages/frontend/editor-ui/src/features/workflows/templates/utils/workflowSamples.ts":{"14":"b://Xf/P///+39//////////////8DAAAC","22":"b://Xf/P///+39//////////////8DAAAC","26":"b://Xf/P///+39//////////////8DAAAC","43":[185],"54":[185],"94":[185],"100":[185],"103":[185]},"packages/frontend/editor-ui/src/features/workflows/workflowHistory/components/WorkflowHistoryButton.vue":{"12":"b:x/Xf8O3/+cHdYAwC//0B8/////8DAAAA","24":"b:wqFPgAVAqQEAAAAAoCAAY9ycB3sBAAAA","31":"b:x/Xf8O3/+cHdYAwC//0B8/////8DAAAA","32":"b:wuHfkAVIqQFEAAAA4CUAc92+X38DAAAA","42":"b:x/BBwOX3+cFdAAwAoCQA4/nfw3MDAAAA","49":"b:x/Hf0OX/+cFdAAwA4CUA8/3/338DAAAA","52":"b:x/Xf8O3/+cHdYAwC//0B8/////8DAAAA","55":"b:x/Xf8O3/+cHdYAwC//0B8/////8DAAAA","62":"b:x/Xb8O3++cHdYAwC//0B8/////8DAAAA"},"packages/frontend/editor-ui/src/features/workflows/workflowHistory/workflowHistory.store.ts":{"22":"b://Xf/P///+39//////////////8DAAAA","28":[133],"30":[133],"34":[133],"38":[133],"44":"b:QAAIAAAAEAABAAAAAAAAQOAAAHAAAAAA"},"packages/vite/source-map-js-shim.ts":{"1":"b://Xf/P///+39//////////////8DAAAA"},"localhost-42547/assets/_plugin-vue_export-helper-Da88TEg1.js":{"2":[74,40]},"localhost-42547/assets/chunk-CC9Q-vWm.js":{"8":[74,40],"9":[74,40],"10":[74,40],"19":[74,40],"23":[74,40],"29":[74,40],"33":[74,40],"34":[74,40]},"packages/@n8n/api-types/src/schemas/agent-run-reducer.ts":{"32":[37,72,73,74,39,75,40,76],"69":[37,72,73,74,38,95,39,75,40,41,76],"82":[37,72,73,74,38,95,39,75,40,41,76],"99":[37,72,73,74,38,95,39,75,40,41,76],"108":[37,72,73,74,38,39,75,40,41,76],"116":[37,72,73,74,38,39,75,40,41,76],"121":[37,72,73,74,38,39,75,40,41,76],"139":[37,72,73,74,38,39,75,40,41,76],"157":[37,72,73,74,38,95,39,75,40,41,76],"158":[37,72,73,74,39,75,40,76],"163":[37,72,73,74,39,75,40,76],"178":[37,72,73,74,38,95,39,75,40,41,76],"202":[37,72,73,74,38,95,39,75,40,41,76],"579":[37,72,73,74,38,95,39,75,40,41,76],"590":[37,72,73,74,38,95,39,75,40,41,76],"605":[37,72,73,74,38,95,39,75,40,41,76],"619":[37,72,73,74,38,95,39,75,40,41,76],"630":[37,72,73,74,39,75,40,76],"633":[37,72,73,74,39,75,40,76],"636":[37,72,73,74,39,75,40,76]},"packages/@n8n/api-types/src/schemas/instance-ai.schema.ts":{"35":[37,72,73,74,38,95,39,75,40,76],"45":[37,72,73,74,38,95,39,75,40,76],"66":[37,72,73,74,38,95,39,75,40,76],"189":[37,72,73,74,38,95,39,75,40,41,76],"531":[37,72,73,74,38,95,39,75,40,76],"535":[37,72,73,74,38,95,39,75,40,76],"539":[37,72,73,74,38,95,39,75,40,76],"543":[37,72,73,74,38,95,39,75,40,76],"546":[37,72,73,74,38,95,39,75,40,76],"554":[37,72,73,74,38,95,39,75,40,76],"591":[37,72,73,74,38,95,39,75,40,76],"592":[37,72,73,74,38,95,39,75,40,76],"997":[37,72,73,74,38,95,39,75,40,76],"1391":[37,72,73,74,38,95,39,75,40,76],"1410":[37,72,73,74,38,95,39,75,40,76],"1534":[37,72,73,74,38,95,39,75,40,41,76]},"packages/frontend/@n8n/design-system/src/components/AskAssistantChat/messages/MessageRating.vue":{"15":[19,37,72,73,74,38,95,75,40,41],"62":[19,37,72,73,74,38,95,75,40,41]},"packages/frontend/@n8n/design-system/src/components/N8nAiActivityStepChevron/AiActivityStepChevron.vue":{"10":[37,72,73,74,38,39,75,40,41]},"packages/frontend/@n8n/design-system/src/components/N8nAnimatedCollapsibleContent/AnimatedCollapsibleContent.vue":{"6":[37,72,73,74,38,39,75,40,41]},"packages/frontend/@n8n/design-system/src/components/N8nChatInput/useChatInputAutoFocus.ts":{"36":[37,72,74,38,95,39,75,40,41,76]},"packages/frontend/@n8n/design-system/src/components/N8nIcon/custom/filled-square.svg":{"10":"b:OIAfAOIDAAAAHQCoAAAAAAAAAAAAAAAA"},"packages/frontend/editor-ui/src/app/layouts/InstanceAiLayout.vue":{"9":[37,72,73,74,38,95,39,75,40,41,76],"20":[37,72,73,74,38,95,39,75,40,41,76],"22":[37,72,73,74,38,95,39,75,40,41,76]},"packages/frontend/editor-ui/src/app/utils/rbac/middleware/custom.ts":{"4":"b:IAECbP4DD8Rsn4PBAAOmACAAQAADAAAA"},"packages/frontend/editor-ui/src/experiments/instanceAiBrowserUse/useInstanceAiBrowserUseExperiment.ts":{"9":[37,72,73,74,38,95,39,75,40,41,76]},"packages/frontend/editor-ui/src/experiments/instanceAiComputerUse/useInstanceAiComputerUseExperiment.ts":{"6":[37,72,73,74,38,95,39,75,40,41,76]},"packages/frontend/editor-ui/src/experiments/instanceAiMcpConnections/useInstanceAiMcpConnectionsExperiment.ts":{"6":[37,72,73,74,38,95,39,75,40,41,76],"9":[37,72,73,74,38,95,39,75,40,41,76]},"packages/frontend/editor-ui/src/experiments/instanceAiPersonalizedPromptSuggestions/profileOverride.ts":{"60":[37,72,74,38,95,39,75,40,41,76],"73":[37,72,74,38,95,39,75,40,41,76],"106":[37,72,74,38,95,39,75,40,41,76],"114":[37,72,74,38,95,39,75,40,41,76],"130":[37,72,74,38,95,39,75,40,41,76]},"packages/frontend/editor-ui/src/experiments/instanceAiPersonalizedPromptSuggestions/useInstanceAiPersonalizedPromptSuggestionsExperiment.ts":{"8":[37,72,74,38,95,39,75,40,41,76],"11":[37,72,74,38,95,39,75,40,41,76],"14":[37,72,74,38,95,39,75,40,41,76],"29":[37,72,74,38,95,39,75,40,41,76]},"packages/frontend/editor-ui/src/experiments/instanceAiProactiveAgent/useInstanceAiProactiveAgentExperiment.ts":{"6":[37,72,74,38,95,39,75,40,41,76],"9":[37,72,74,38,95,39,75,40,41,76]},"packages/frontend/editor-ui/src/experiments/instanceAiPromptSuggestionsV2/useInstanceAiPromptSuggestionsV2Experiment.ts":{"6":[37,72,74,38,95,39,75,40,41,76],"9":[37,72,74,38,95,39,75,40,41,76]},"packages/frontend/editor-ui/src/experiments/instanceAiSplitEmptyState/useInstanceAiSplitEmptyStateExperiment.ts":{"5":[37,72,74,38,95,39,75,40,41,76],"7":[37,72,74,38,95,39,75,40,41,76],"10":[37,72,74,38,95,39,75,40,41,76]},"packages/frontend/editor-ui/src/experiments/instanceAiTemplateExamples/instanceAiTemplateExamples.store.ts":{"14":[37,72,74,38,95,39,75,40,41,76]},"packages/frontend/editor-ui/src/experiments/instanceAiTemplateExamples/useInstanceAiTemplateExamplesExperiment.ts":{"6":[37,72,74,38,95,39,75,40,41,76],"9":[37,72,74,38,95,39,75,40,41,76],"13":[37,72,74,38,95,39,75,40,41,76]},"packages/frontend/editor-ui/src/experiments/instanceAiWorkflowPreviewSuggestions/components/WorkflowPreviewSuggestions.vue":{"10":[37,72,74,38,95,39,75,40,41,76],"36":[37,72,74,38,95,39,75,40,41],"53":[37,72,74,38,95,39,75,40,41],"102":[37,72,74,38,95,39,75,40,41,76],"108":[37,72,74,38,95,39,75,40,41,76]},"packages/frontend/editor-ui/src/experiments/instanceAiWorkflowPreviewSuggestions/useInstanceAiWorkflowPreviewSuggestionsExperiment.ts":{"4":[37,72,74,38,95,39,75,40,41,76],"5":[37,72,74,38,95,39,75,40,41,76]},"packages/frontend/editor-ui/src/features/ai/chatHub/composables/useChatHubMarkdownOptions.ts":{"25":[37,38,41,48],"40":"b:OAAAAOIDAQAADwAoAAAAAAAAAAAAAAAA","56":"b:OAAAAOIDAQAADwAoAAAAAAAAAAAAAAAA","75":"b:OAAAAOIDAQAADwAoAAAAAAAAAAAAAAAA","85":[3,48],"104":[48],"116":[48],"117":[48],"138":"b:OAAAAOIDAQAADwAoAAAAAAAAAAAAAAAA","139":"b:OAAAAOIDAQAADwAoAAAAAAAAAAAAAAAA","141":[37,38,41,48],"144":[37,38,41,48],"156":"b:OAAAAOIDAQAADwAoAAAAAAAAAAAAAAAA","161":[48],"179":"b:OAAAAOIDAQAADwAoAAAAAAAAAAAAAAAA","183":[48],"190":[48],"199":"b:OAAAAOIDAQAADwAoAAAAAAAAAAAAAAAA","206":"b:OAAAAOIDAQAADwAoAAAAAAAAAAAAAAAA","215":[48],"226":[48],"227":[48],"228":[48],"229":[48],"230":[48]},"packages/frontend/editor-ui/src/features/ai/instanceAi/InstanceAiEmptyView.vue":{"107":[37,72,74,38,95,39,75,40,41,76],"116":[37,72,74,38,95,39,75,40,41],"141":[37,72,74,38,95,39,75,40,41,76],"146":[37,72,74,38,95,39,75,40,41,76],"170":[37,72,74,38,95,39,75,40,41,76],"174":[37,72,74,38,95,39,75,40,41,76],"177":[37,72,74,38,95,39,75,40,41,76],"185":[37,72,74,38,95,39,75,40,41,76],"200":[37,72,74,38,95,39,75,40,41,76],"227":[37,72,74,38,95,39,75,40,41,76],"261":[37,72,74,38,95,39,75,40,41,76],"268":[37,72,74,38,95,39,75,40,41,76],"292":[37,72,74,38,95,39,75,40,41,76],"308":[37,72,74,38,95,39,75,40,41,76],"372":[37,72,74,38,95,39,75,40,41,76],"391":[37,72,74,38,95,39,75,40,41,76],"435":[37,72,74,38,95,39,75,40,41,76],"455":[37,72,74,38,95,39,75,40,41],"459":[37,72,74,38,95,39,75,40,41,76],"460":[37,72,74,38,95,39,75,40,41,76],"465":[37,72,74,38,95,39,75,40,41]},"packages/frontend/editor-ui/src/features/ai/instanceAi/InstanceAiThreadView.vue":{"100":[37,72,73,74,38,95,39,75,40,41,76],"108":[37,72,73,74,38,95,39,75,40,41,76],"109":[37,72,73,74,38,95,39,75,40,41,76],"113":[37,72,73,74,38,95,39,75,40,41,76],"120":[37,72,73,74,38,95,39,75,40,41,76],"125":[37,72,73,74,38,95,39,75,40,41,76],"137":[37,72,73,74,38,95,39,75,40,41,76],"152":[37,72,73,74,38,95,39,75,40,41,76],"153":[37,72,73,74,38,95,39,75,40,41,76],"157":[37,72,73,74,38,95,39,75,40,41,76],"178":[37,72,73,74,38,95,39,75,40,41,76],"189":[37,72,73,74,38,95,39,75,40,41,76],"190":[37,72,73,74,38,95,39,75,40,41,76],"196":[37,72,73,74,38,95,39,75,40,41,76],"199":[37,72,73,74,38,95,39,75,40,41,76],"204":[37,72,73,74,38,95,39,75,40,41,76],"211":[37,72,73,74,38,95,39,75,40,41,76],"218":[37,72,73,74,38,95,39,75,40,41,76],"222":[37,40,41],"228":[40],"259":[37,72,73,74,38,95,39,75,40,41,76],"264":[73,74,75,76],"274":[37,72,73,74,38,95,39,75,40,41,76],"277":[37,72,73,74,38,95,39,75,40,41,76],"280":[37,72,73,74,38,95,39,75,40,41,76],"283":[37,72,73,74,38,95,39,75,40,41,76],"287":[37,72,73,74,38,95,39,75,40,41,76],"290":[37,72,73,74,38,95,39,75,40,41,76],"298":[37,72,73,74,38,95,39,75,40,41,76],"301":[37,72,73,74,38,95,39,75,40,41,76],"304":[37,72,73,74,38,95,39,75,40,41,76],"307":[37,72,73,74,38,95,39,75,40,41,76],"314":[37,72,73,74,38,95,39,75,40,41,76],"315":[37,72,73,74,38,95,39,75,40,41,76],"325":[37,72,73,74,38,95,39,75,40,41,76],"335":[37,38,39,40,41],"343":[37],"349":[37,38,39,40,41],"365":[37,72,73,74,38,95,39,75,40,41,76],"379":[73,74,75,76],"387":[38,75,40],"395":[37,72,73,74,38,95,39,75,40,41,76],"404":[37,72,73,74,38,95,39,75,40,41,76],"405":[73,74,75,76],"420":[37,72,73,74,38,95,39,75,40,41,76],"431":[37,72,73,74,38,95,39,75,40,41,76],"432":[37,72,73,74,38,95,39,75,40,41,76],"438":[37,72,73,74,38,95,39,75,40,41,76],"453":[37,72,73,74,38,95,39,75,40,41,76],"457":[37,72,73,74,38,95,39,75,40,41,76],"471":[37,72,73,74,38,95,39,75,40,41,76],"485":[37,72,73,74,38,95,39,75,40,41,76],"492":[37,72,73,74,38,95,39,75,40,41,76],"502":[73,74,75,76],"506":[37,72,73,74,38,95,39,75,40,41,76],"542":[73,74,75,76],"551":[73,74,75,76],"573":[37,72,73,74,38,95,39,75,40,41,76],"580":[37,72,73,74,38,95,39,75,40,41,76],"589":[37,72,73,74,38,95,39,75,40,41,76],"597":[75],"728":[37,72,73,74,38,95,39,75,40,41,76],"734":[37,72,74,38,95,39,75,40,41,76],"749":[37,72,73,74,38,95,39,75,40,41,76],"766":[37,72,73,74,38,95,39,75,40,41,76],"786":[37,72,73,74,38,95,39,75,40,41,76],"819":[37,72,73,74,38,95,39,75,40,41,76],"822":[37,72,73,74,38,95,39,75,40,41,76],"833":[73],"845":[37,72,73,74,38,95,39,75,40,41,76],"870":[37,72,73,74,38,95,39,75,40,41,76],"900":[37,72,73,74,38,95,39,75,40,41,76],"937":[37,72,73,74,38,95,39,75,40,41,76],"967":[37,72,73,74,38,95,39,75,40,41,76],"986":[37,72,73,74,38,95,39,75,40,41,76]},"packages/frontend/editor-ui/src/features/ai/instanceAi/InstanceAiView.vue":{"37":[75],"65":[75],"81":[37,72,73,74,38,95,39,75,40,41,76],"102":[37,72,73,74,38,95,39,75,40,41,76],"104":[37,72,73,74,38,95,39,75,40,41,76],"117":[37,72,73,74,38,95,39,75,40,41,76],"151":[37,72,73,74,38,95,39,75,40,41,76],"161":[75]},"packages/frontend/editor-ui/src/features/ai/instanceAi/agentTimeline.utils.ts":{"22":[37,72,74,38,39,75,40,41],"63":[37,72,38,39,40,41],"83":[37,72,73,74,38,95,39,75,40,41],"96":[37,72,73,74,38,39,75,40,41],"107":[37,72,73,74,38,39,75,40,41],"126":[37,72,73,74,38,39,75,40,41],"148":[37,72,73,74,38,39,75,40,41],"153":[37,72,74,38,39,75,40,41],"161":[37,72,73,74,38,39,75,40,41],"240":[37,72,73,74,38,39,75,40,41],"257":[73]},"packages/frontend/editor-ui/src/features/ai/instanceAi/builderAgents.ts":{"13":[37,72,73,74,38,95,39,75,40,41,76],"30":[37,72,73,74,38,95,39,75,40,41,76],"41":[37,72,73,74,38,95,39,75,40,41,76],"104":[37,72,73,74,38,95,39,75,40,41,76],"106":[37,72,73,74,38,95,39,75,40,41,76]},"packages/frontend/editor-ui/src/features/ai/instanceAi/canvasPreview.utils.ts":{"59":[37,72,73,74,38,95,39,75,40,41,76],"82":[37,73,38,40,41],"94":[37,72,73,74,38,95,39,75,40,41,76],"123":[37,72,73,74,38,95,39,75,40,41,76],"149":[37,72,73,74,38,95,39,75,40,41,76],"187":[37,72,73,74,38,95,39,75,40,41,76],"237":[37,38,39,40,41],"359":[37,72,73,74,38,95,39,75,40,41,76],"383":[37,72,73,74,38,95,39,75,40,41,76],"508":[37,72,73,74,38,95,39,75,40,41,76],"534":[37,38,39,40,41],"542":[37,38,39,40,41]},"packages/frontend/editor-ui/src/features/ai/instanceAi/components/AgentActivityTree.vue":{"10":[37,72,73,74,38,95,39,75,40,41]},"packages/frontend/editor-ui/src/features/ai/instanceAi/components/AgentTimeline.vue":{"27":[37,72,73,74,38,95,39,75,40,41],"95":[37,72,73,74,38,95,39,75,40,41],"98":[37,72,73,74,38,95,39,75,40,41],"107":[37,72,73,74,38,95,39,75,40,41],"115":[37,72,73,74,38,95,39,75,40,41],"245":[37,72,73,74,38,95,39,75,40,41]},"packages/frontend/editor-ui/src/features/ai/instanceAi/components/ConnectionsCard.vue":{"32":[37,72,73,74,38,95,39,75,40,41,76],"45":[37,72,73,74,38,95,39,75,40,41,76],"56":[37,72,73,74,38,95,39,75,40,41,76],"159":[37,72,73,74,38,95,39,75,40,41,76]},"packages/frontend/editor-ui/src/features/ai/instanceAi/components/InstanceAiArtifactsPanel.vue":{"20":[37,72,73,74,38,95,39,75,40,41,76],"25":[37,72,73,74,38,95,39,75,40,41,76],"26":[37,72,73,74,38,95,39,75,40,41,76],"63":[37],"82":[37,72,73,74,38,95,39,75,40,41,76],"83":[37,72,73,74,38,95,39,75,40,41,76],"84":[37,72,73,74,38,95,39,75,40,41,76],"98":[37,72,73,74,38,95,39,75,40,41,76],"114":[37,38,40,41],"129":[37,38,40,41],"157":[37,72,73,74,38,95,39,75,40,41,76],"193":[37,72,73,74,38,95,39,75,40,41,76],"203":[37,72,73,74,38,95,39,75,40,41,76],"257":[37,72,73,74,38,95,39,75,40,41,76],"263":[37,38,40,41],"269":[37]},"packages/frontend/editor-ui/src/features/ai/instanceAi/components/InstanceAiConfirmationPanel.vue":{"36":[37,72,73,74,38,95,39,75,40,41,76],"44":[38,40],"52":[38,40],"104":[37,72,73,74,38,95,39,75,40,41,76],"121":[38,40],"134":[38,40],"156":[38,40],"167":[38,40],"195":[38,40],"217":[38,40],"251":[40],"398":[37,72,73,74,38,95,39,75,40,41,76],"581":[38,40],"588":[38,40]},"packages/frontend/editor-ui/src/features/ai/instanceAi/components/InstanceAiEmptyState.vue":{"5":[37,72,74,38,95,39,75,40,41,76],"22":[37,72,74,38,95,39,75,40,41,76]},"packages/frontend/editor-ui/src/features/ai/instanceAi/components/InstanceAiInput.vue":{"113":[37,72,73,74,38,95,39,75,40,41,76],"116":[37,72,73,74,38,95,39,75,40,41,76],"125":[37,72,73,74,38,95,39,75,40,41,76],"126":[37,72,73,74,38,95,39,75,40,41,76],"147":[37,72,74,38,95,39,75,40,41,76],"168":[37,72,74,38,95,39,75,40,41,76],"171":[37,72,73,74,38,95,39,75,40,41,76],"173":[37,72,73,74,38,95,39,75,40,41,76],"174":[37,72,73,74,38,95,39,75,40,41,76],"176":[37,72,74,38,95,39,75,40,41],"177":[37,72,73,74,38,95,39,75,40,41,76],"180":[37,72,73,74,38,95,39,75,40,41,76],"181":[37,72,73,74,38,95,39,75,40,41,76],"189":[37,72,74,38,95,39,75,40,41,76],"192":[37,72,73,74,38,95,39,75,40,41,76],"196":[37,72,73,74,38,95,39,75,40,41,76],"198":[37,72,73,74,38,95,39,75,40,41,76],"227":[37,72,73,74,38,95,39,75,40,41,76],"244":[37,72,74,38,95,39,75,40,41],"250":[37,72,73,74,38,95,39,75,40,41,76],"260":[37,72,74,38,95,39,75,40,41],"265":[37,72,74,38,95,39,75,40,41],"270":[37,72,74,38,95,39,75,40,41],"274":[37,72,74,38,95,39,75,40,41],"293":[37,72,74,38,95,39,75,40,41],"302":[72],"323":[72],"349":[37,72,74,38,95,39,75,40,41],"362":[37,72,74,38,95,39,75,40,41],"422":[37,72,73,74,38,95,39,75,40,41,76],"432":[37,72,73,74,38,95,39,75,40,41,76],"455":[37,72,73,74,38,95,39,75,40,41,76],"516":[72],"527":[37,72,73,74,38,95,39,75,40,41,76],"534":[37,72,74,38,95,39,75,40,41],"538":[37,72,74,38,95,39,75,40,41]},"packages/frontend/editor-ui/src/features/ai/instanceAi/components/InstanceAiMarkdown.vue":{"7":[37,72,73,74,38,39,75,40,41],"56":[37,38,41],"74":[37,72,73,74,38,39,75,40,41],"76":[37,38,41],"88":[37,38,41],"130":[37,38,40,41],"161":[37,72,73,74,38,75,40,41],"167":[37,38,40,41],"172":[37,38,40,41],"193":[37,38,41],"203":[37,72,73,74,38,39,75,40,41],"235":[37,38,41],"246":[37,38,41],"264":[37,38,41],"283":[37,72,73,74,38,39,75,40,41],"306":[37,38,41],"405":[37,72,74,38,39,75,40,41],"412":[37,72,73,74,38,39,75,40,41]},"packages/frontend/editor-ui/src/features/ai/instanceAi/components/InstanceAiMessage.vue":{"20":[37,72,73,74,38,95,39,75,40,41,76],"29":[37,72,73,74,38,95,39,75,40,41,76],"30":[37,72,73,74,38,95,39,75,40,41],"31":[37,72,73,74,38,95,39,75,40,41],"33":[37,72,73,74,38,39,75,40,41],"39":[37,72,73,74,38,95,39,75,40,41],"45":[40],"48":[37,72,73,74,38,95,39,75,40,41],"53":[37,72,73,74,38,95,39,75,40,41],"67":[40],"74":[40],"84":[37,72,74,38,95,39,75,40,41,76],"87":[37,72,73,74,38,95,39,75,40,41],"93":[37,72,73,74,38,95,39,75,40,41],"95":[37,72,73,74,38,95,39,75,40,41],"102":[37,72,73,74,38,95,39,75,40,41],"115":[37,72,73,74,38,95,39,75,40,41,76],"125":[72],"142":[95],"148":[95],"156":[40],"168":[40]},"packages/frontend/editor-ui/src/features/ai/instanceAi/components/InstanceAiPreviewTabBar.vue":{"19":[37,72,73,74,38,95,39,75,40,41,76],"41":[37,72,73,74,38,95,39,75,40,41,76],"47":[37,38,39,40,41],"53":[37,38,39,40,41],"57":[37,38,39,40,41],"84":[37,72,73,74,38,95,39,75,40,41,76],"85":[37,38,39,40,41],"88":[37,38,39,40,41],"114":[37,72,73,74,38,95,39,75,40,41,76],"134":[37,72,73,74,38,95,39,75,40,41,76],"136":[37,38,39,40,41],"138":[37,38,39,40,41],"140":[37,38,39,40,41],"141":[37,38,39,40,41]},"packages/frontend/editor-ui/src/features/ai/instanceAi/components/InstanceAiStatusBar.vue":{"18":[37,72,73,74,38,95,39,75,40,41,76],"25":[37,72,73,74,38,95,39,75,40,41,76],"31":[37,72,73,74,38,95,39,75,40,41,76],"41":[73],"53":[40],"62":[37,72,73,74,38,95,39,75,40,41,76],"72":[37,72,73,74,38,95,39,75,40,41,76],"76":[37,72,73,74,38,95,39,75,40,41,76],"80":[37,72,74,38,39,75,40,41],"91":[37,72,73,74,38,95,39,75,40,41,76],"98":[37,72,73,74,38,95,39,75,40,41,76],"107":[37,72,73,74,38,95,39,75,40,41,76],"111":[37,74,95,39,75],"122":[75],"127":[37,72,73,74,38,95,39,75,40,41,76]},"packages/frontend/editor-ui/src/features/ai/instanceAi/components/InstanceAiViewHeader.vue":{"12":[37,72,73,74,38,95,39,75,40,41,76],"19":[37,72,73,74,38,95,39,75,40,41,76],"31":[37,72,73,74,38,95,39,75,40,41,76],"36":[37,72,73,74,38,95,39,75,40,41,76]},"packages/frontend/editor-ui/src/features/ai/instanceAi/components/ThinkingBlock.vue":{"35":[37,72,74,38,39,75,40,41],"52":[37,72,74,38,39,75,40,41],"56":[37,72,74,38,39,75,40,41],"57":[37,72,74,38,75,40,41],"63":[37,72,38,39,40,41],"71":[37,72,74,38,75,40,41],"81":[37,72,74,38,39,75,40,41],"99":[37,72,74,38,39,75,40,41],"107":[37,72,74,38,39,75,40,41],"110":[37,72,74,38,39,75,40,41],"115":[37,74,38,39,40,41],"132":[75],"138":[37,72,74,38,39,75,40,41],"146":[37,72,74,38,39,75,40,41],"152":[37,72,74,38,75,40,41],"169":[37,72,74,38,39,40,41],"179":[37,72,74,38,39,75,40,41],"217":[37,72,74,38,39,75,40,41],"224":[37,72,74,38,39,75,40,41],"226":[37,72,74,38,39,75,40,41],"233":[37,72,74,38,39,75,40,41]},"packages/frontend/editor-ui/src/features/ai/instanceAi/components/TimelineTextSegment.vue":{"10":[37,72,73,74,38,39,75,40,41]},"packages/frontend/editor-ui/src/features/ai/instanceAi/composables/useCreditWarningBanner.ts":{"9":[37,72,73,74,38,95,39,75,40,41,76],"12":[37,72,73,74,38,95,39,75,40,41,76],"23":[37,72,73,74,38,95,39,75,40,41,76]},"packages/frontend/editor-ui/src/features/ai/instanceAi/composables/useInstanceAiHandoff.ts":{"49":[73,74,75,76],"50":[73,74,75,76],"52":[73,74,75,76],"109":[76],"117":[73,74,75,76],"135":[73,74,75,76],"153":[73,74,75,76],"169":[76],"187":[76]},"packages/frontend/editor-ui/src/features/ai/instanceAi/constants.ts":{"28":[37,72,74,38,95,39,75,40,41,76]},"packages/frontend/editor-ui/src/features/ai/instanceAi/instanceAi.api.ts":{"26":[37,72,74,38,95,39,75,40,41,76],"49":[37,72,74,38,95,39,75,40,41,76],"107":[38,40],"124":[37,72,73,74,38,95,39,75,40,41,76],"181":[37,72,73,74,38,95,39,75,40,41,76],"203":[74,75]},"packages/frontend/editor-ui/src/features/ai/instanceAi/instanceAi.handoffContext.ts":{"16":[37,72,73,74,38,95,39,75,40,41,76]},"packages/frontend/editor-ui/src/features/ai/instanceAi/instanceAi.liveRunState.ts":{"9":[73,74,38,75,40,76],"19":[39],"44":[38,40],"61":[38,40],"73":[38,40],"89":[38,40]},"packages/frontend/editor-ui/src/features/ai/instanceAi/instanceAi.memory.api.ts":{"12":[37,72,73,74,38,95,39,75,40,41,76],"18":[75],"22":[75],"42":[73,74,75,76],"59":[73,74,38,75,40,76]},"packages/frontend/editor-ui/src/features/ai/instanceAi/instanceAi.reducer.ts":{"53":[37,72,73,74,38,95,39,75,40,41],"60":[37,72,73,74,38,95,39,75,40,41,76],"65":[37,72,73,74,38,95,39,75,40,41,76],"110":[37,72,74,38,95,39,75,40,41,76],"119":[37,72,73,74,38,95,39,75,40,41,76],"127":[37,72,74,38,95,39,75,40,41,76],"144":[37,72,74,38,95,39,75,40,41,76],"149":[37,72,73,74,38,95,39,75,40,41,76],"179":[37,72,73,74,38,95,39,75,40,41],"193":[37,72,73,74,38,95,39,75,40,41,76]},"packages/frontend/editor-ui/src/features/ai/instanceAi/instanceAi.settings.api.ts":{"11":[37,72,73,74,38,95,39,75,40,41,76],"24":[37,72,73,74,38,95,39,75,40,41,76],"37":[37,72,73,74,38,95,39,75,40,41,76],"43":[37,72,73,74,38,95,39,75,40,41,76]},"packages/frontend/editor-ui/src/features/ai/instanceAi/instanceAi.threadRuntime.ts":{"104":[37,72,73,74,38,95,39,75,40,41,76],"119":[37,72,73,74,38,95,39,75,40,41,76],"157":[37,72,74,38,95,39,75,40,41,76],"175":[37,72,73,74,38,95,39,75,40,41,76],"266":[73,74,75],"298":[37,72,73,74,38,95,39,75,40,41,76],"340":[37,38,39,40,41],"354":[40,41],"361":[37,38,39,40,41],"364":[37,38,41],"381":[37,72,73,74,38,95,39,75,40,41,76],"382":[37,72,73,74,38,95,39,75,40,41,76],"383":[37,72,73,74,38,95,39,75,40,41,76],"384":[37,72,73,74,38,95,39,75,40,41,76],"386":[37,72,73,74,38,95,39,75,40,41,76],"387":[37,38,39,40,41],"388":[37,72,73,74,38,95,39,75,40,41,76],"389":[37,72,73,74,38,95,39,75,40,41,76],"403":[37,72,73,74,38,95,39,75,40,41,76],"414":[37,72,73,74,38,95,39,75,40,41,76],"426":[37,72,73,74,38,95,39,75,40,41,76],"427":[37,38,39,40,41],"454":[37,72,74,38,95,39,75,40,41,76],"457":[37,72,74,38,95,39,75,40,41,76],"464":[37,72,73,74,38,95,39,75,40,41,76],"466":[37,72,73,74,38,95,39,75,40,41,76],"473":[37,72,73,74,38,95,39,75,40,41,76],"490":[37,72,73,74,38,95,39,75,40,41,76],"493":[37,72,73,74,38,95,75,40,41,76],"499":[73],"510":[37,72,73,74,38,95,39,75,40,41,76],"520":[37,72,73,74,38,95,39,75,40,41,76],"522":[38,40],"530":[39],"539":[38,40],"554":[40],"569":[40],"595":[37,72,73,74,38,95,39,75,40,41,76],"635":[37,72,73,74,38,95,39,75,40,41,76],"738":[37,72,73,74,38,95,39,75,40,41,76],"758":[37,72,73,74,38,95,39,75,40,41,76],"763":[73],"820":[37,72,73,74,38,95,39,75,40,41,76],"838":[37,72,73,74,38,95,39,75,40,41,76],"843":[37,72,73,74,38,95,39,75,40,41,76],"850":[37,72,73,74,38,95,39,75,40,41,76],"855":[73,74],"867":[75],"876":[75],"895":[75],"900":[73,74,75,76],"911":[73,74,75,76],"926":[73,74,75],"927":[73,74,75],"953":[73,74,38,75,40,76],"985":[37,72,74,38,95,39,75,40,41,76],"996":[37,72,74,38,95,39,75,40,41,76],"1022":[37,72,74,38,95,39,75,40,41,76],"1047":[37,72,74,38,95,39,75,40,41,76],"1088":[37,72,74,38,95,39,75,40,41,76],"1164":[37,72,74,38,95,39,75,40,41,76],"1172":[38,40]},"packages/frontend/editor-ui/src/features/ai/instanceAi/instanceAiMcp.store.ts":{"22":[37,72,73,74,38,95,39,75,40,41,76]},"packages/frontend/editor-ui/src/features/ai/instanceAi/instanceAiMcp.telemetry.ts":{"4":[37,72,73,74,38,95,39,75,40,41,76]},"packages/frontend/editor-ui/src/features/ai/instanceAi/instanceAiPermissions.ts":{"9":[37,72,73,74,38,95,39,75,40,41,76]},"packages/frontend/editor-ui/src/features/ai/instanceAi/instanceAiPromptSuggestions.telemetry.ts":{"45":[37,72,74,38,95,39,75,40,41,76],"48":[37,72,74,38,95,39,75,40,41,76],"63":[37,72,73,74,38,95,39,75,40,41,76],"68":[37,72,74,38,95,39,75,40,41,76],"117":[37,72,73,74,38,95,39,75,40,41,76]},"packages/frontend/editor-ui/src/features/ai/instanceAi/module.descriptor.ts":{"21":[37,72,73,74,38,95,39,75,40,41,76],"22":[37,72,74,38,95,39,75,40,41,76],"23":[37,72,73,74,38,95,39,75,40,41,76],"48":[76],"170":[37,72,73,74,38,95,39,75,40,41,76]},"packages/frontend/editor-ui/src/features/ai/instanceAi/toolLabels.ts":{"136":[37,72,73,74,38,95,39,75,40,41,76],"139":[37,72,38,39,40,41]},"packages/frontend/editor-ui/src/features/ai/instanceAi/useCanvasPreview.ts":{"37":[37,72,73,74,38,95,39,75,40,41,76],"43":[37,72,73,74,38,95,39,75,40,41,76],"61":[37,38,39,40,41],"62":[37,38,39,40,41],"66":[37,38,39,40,41],"67":[37,38,39,40,41],"76":[37,38,39,40,41],"77":[37,38,39,40,41],"86":[37,38,39,40,41],"97":[37,38,39,40,41],"104":[37,72,73,74,38,95,39,75,40,41,76],"110":[37,72,73,74,38,95,39,75,40,41,76],"117":[37,72,73,74,38,95,39,75,40,41,76],"133":[40],"138":[37,41],"147":[37],"182":[37,38,39,40,41],"184":[37,38,39,40,41],"194":[37,72,73,74,38,95,39,75,40,41,76],"214":[37,72,73,74,38,95,39,75,40,41,76],"215":[37,38,39,40,41],"234":[37,72,73,74,38,95,39,75,40,41,76],"245":[37,72,73,74,38,95,39,75,40,41,76],"263":[37,72,73,74,38,95,39,75,40,41,76],"274":[37,72,73,74,38,95,39,75,40,41,76],"290":[37,72,73,74,38,95,39,75,40,41,76],"301":[37,72,73,74,38,95,39,75,40,41,76],"321":[37,72,73,74,38,95,39,75,40,41,76],"332":[37,72,73,74,38,95,39,75,40,41,76],"349":[37,72,73,74,38,95,39,75,40,41,76],"360":[37,72,73,74,38,95,39,75,40,41,76],"375":[37,72,73,74,38,95,39,75,40,41,76],"401":[37,72,73,74,38,95,39,75,40,41,76],"412":[37,72,73,74,38,95,39,75,40,41,76]},"packages/frontend/editor-ui/src/features/ai/instanceAi/useResourceRegistry.ts":{"37":[37,38,39,40,41],"59":[37,38,39,40,41],"88":[38,40],"92":[38,40],"138":[37,72,38,39,40,41],"296":[37,72,73,74,38,95,39,75,40,41,76],"311":[37,72,73,74,38,95,39,75,40,41,76],"326":[37,72,73,74,38,95,39,75,40,41,76],"345":[37,72,73,74,38,95,39,75,40,41,76],"368":[37,72,73,74,38,95,39,75,40,41,76],"405":[37,72,73,74,38,95,39,75,40,41,76],"421":[37,72,73,74,38,95,39,75,40,41,76],"449":[37,72,73,74,38,95,39,75,40,41,76],"462":[37,72,73,74,38,95,39,75,40,41,76],"480":[37,38,39,40,41]},"packages/frontend/editor-ui/src/features/ai/instanceAi/useResponseFeedback.ts":{"11":[37,72,73,74,38,95,75,40,41],"13":[73],"16":[37,72,73,74,38,95,75,40,41],"17":[37,72,38,40,41],"18":[73],"21":[37,72,73,74,38,95,75,40,41],"22":[37,72,38,40,41],"24":[73],"47":[37,72,73,74,38,95,39,75,40,41,76],"72":[37,72,73,74,38,95,39,75,40,41],"161":[75]},"packages/frontend/editor-ui/src/features/ai/instanceAi/useTransitionGate.ts":{"3":[37,72,73,74,38,95,39,75,40,41,76],"7":[37,72,73,74,38,95,39,75,40,41,76],"9":[37,72,73,74,38,95,39,75,40,41,76],"10":[37,72,73,74,38,95,39,75,40,41,76],"18":[73,74,75,76],"23":[73,74,75,76]},"packages/frontend/editor-ui/src/features/ai/shared/components/ChatInputBase.vue":{"48":[37,72,73,74,38,95,39,75,40,41,76],"89":[37,72,74,38,95,39,75,40,41,76],"93":[72],"119":[37,72,74,38,95,39,75,40,41],"165":[37,72,74,38,95,39,75,40,41],"168":[37,72,74,38,95,39,75,40,41,76],"169":[37,72,74,38,95,39,75,40,41],"171":[37,72,73,74,38,95,39,75,40,41,76],"174":[37,72,73,74,38,95,39,75,40,41,76],"182":[37,72,73,74,38,95,39,75,40,41,76]},"packages/frontend/editor-ui/src/features/core/dataTable/components/dataGrid/ColumnHeader.vue":{"31":[36,94],"32":[36,94],"34":"b:AAAAAPADAAAAHwDAAAAAAAAAAAAAAAAA","55":[36],"64":[36],"68":[36],"72":[36],"76":[36,94],"86":[36,94],"90":[36,94],"97":[36,94],"102":[36,94],"113":[36,94],"122":[36,94],"124":[36],"130":[36,94],"150":[36,94],"154":[36,94],"172":[36],"182":[36,94],"194":[36,94],"200":[36,94],"253":[36]},"localhost-32995/assets/_plugin-vue_export-helper-Da88TEg1.js":{"2":[6]},"localhost-32995/assets/chunk-CC9Q-vWm.js":{"8":[6],"9":[6],"10":[6],"19":[6],"23":[6],"29":[6],"33":[6],"34":[6]},"packages/@n8n/utils/src/result.ts":{"7":"b:QEABAACAQAUAAAAAAIAAAIC+PjAAACAA","12":"b:wrVBgACZ8AEAAAAAADQAMNiXv3AAACAA","23":[16,6,181,156,157]},"packages/@n8n/utils/src/string/truncate.ts":{"1":"b:eBEAIB4ADYRkAABoAAOEACDIQwADAAAA","11":"b:OAAIAAKAAAAAAAAoAAAAAIQCBAAAAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nDropdownMenu/DropdownMenuItem.vue":{"58":"b:cMEHgBBAP2UICABagIkjAQSAVYABAAAA","60":[4,5,91],"62":[4,5,91],"69":"b:cMEHgBBAP2UICABagIkjAQSAVYABAAAA","70":"b:cMEHgBBAP2UICABagIkjAQSAVYABAAAA","72":[4,5,91,48,104,49],"82":"b:cMEHgBBAP2UICABagIkjAQSAVYABAAAA","86":"b:cMEHgBBAP2UICABagIkjAQSAVYABAAAA","90":"b:cMEHgBBAP2UICABagIkjAQSAVYABAAAA","94":"b:AAECAAAADyQAAABSgAEiAAAAEAABAAAA","96":[4,5,91],"100":"b:cMEHgBBAP2UICABagIkjAQSAVYABAAAA","107":"b:cMEHgBBAP2UICABagIkjAQSAVYABAAAA","111":[5],"117":[4,5,91],"127":[5,91],"135":[5,91],"159":"b:cMEHgBBAP2UICABagIkjAQSAVYABAAAA","160":"b:cMEHgBBAP2UICABagIkjAQSAVYABAAAA","169":[4,5,91],"179":"b:cMEHgBBAP2UICABagIkjAQSAVYABAAAA","182":"b:cMEHgBBAP2UICABagIkjAQSAVYABAAAA","192":[4,5,91,48,104,49],"200":[4,5,91,48,104,49],"202":[48,104,49],"232":[4,5,91,48,104,49],"239":[4,5,91],"309":[4,5,91],"316":[4,5,91],"319":[4,5,91],"322":[4,5,91],"381":"b:cMEHgBBAP2UICABagIkjAQSAVYABAAAA","383":"b:QMEHgABAP2UIAABagIkjAQSAVYABAAAA","395":"b:AAECAAAADyQAAABSgAEiAAAAEAABAAAA","400":"b:AAECAAAADyQAAABSgAEiAAAAEAABAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nDropdownMenu/DropdownMenuItems.vue":{"32":"b:cMEHgBBAP2UICABagIkjAQSAVYABAAAA","48":"b:cMEDgBBAP2UICABagIkjAQSAVYABAAAA","69":"b:cMEHgBBAP2UICABagIkjAQSAVYABAAAA","71":"b:cMEDgBBAP2UICABagIkjAQSAVYABAAAA","78":"b:cMEHgBBAP2UICABagIkjAQSAVYABAAAA","80":[4,5,91,48,104,49],"81":"b:cMEHgBBAP2UICABagIkjAQSAVYABAAAA","84":"b:cMAFgBBAMUEICAAIAIgBAQSARYAAAAAA","87":"b:cMAFgBBAM0EICAAIAIkBAQSARYAAAAAA","90":"b:cMAFgBBAP2UICABYAIkjAQSAVYABAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nIcon/custom/node-execution-error.svg":{"11":[15,16,1,7,34,134,135,6,143,156,157]},"packages/frontend/@n8n/design-system/src/components/N8nIcon/custom/node-validation-error.svg":{"11":"b:wvXPMIQIaMBQQAACu9AAUtCILroCAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nIcon/custom/status-waiting.svg":{"8":[6]},"packages/frontend/@n8n/design-system/src/components/N8nIcon/custom/text.svg":{"8":[1,6]},"packages/frontend/@n8n/design-system/src/components/N8nIcon/nodes/ai-agent.svg":{"11":[1,7,8,10,12,108,124,6,13,14]},"packages/frontend/@n8n/design-system/src/components/N8nIcon/nodes/if.svg":{"11":"b:QAAIAAAQAAAAAAAAAAAAAKHAA4ACAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nIcon/nodes/loop-over-items.svg":{"11":[97,53,55,111,130,6,139,159]},"packages/frontend/@n8n/design-system/src/components/N8nIcon/nodes/schedule-trigger.svg":{"11":"b:wuXKMAUYyMAMIAAC/9AA89y+fKoBAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nIcon/nodes/wait.svg":{"11":[22,84,85,41,53,135,6]},"packages/frontend/@n8n/design-system/src/components/N8nInfoAccordion/InfoAccordion.vue":{"29":"b:QgCAEAAAEAABAAACoAAAQMCCBHABAAAA","40":"b:QgCAEAAAEAABAAACoAAAQMCCBHABAAAA","54":"b:QgCAEAAAEAABAAACoAAAQMCCBHABAAAA","61":"b:QgCAEAAAEAABAAACoAAAQMCCBHABAAAA"},"packages/frontend/@n8n/design-system/src/v2/components/Checkbox/Checkbox.vue":{"12":"b:QgAAAACAGAAJAAEAAAACRsAAAHgDAAAA","15":"b:QgAAAACAGAAJAAEAAAACRsAAAHgDAAAA","20":"b:QgAAAACAGAAJAAEAAAACRsAAAHgDAAAA","23":"b:QgAAAACAGAAJAAEAAAACRsAAAHgDAAAA","29":[113,80,121,6,160,161],"33":"b:QgAAAACAGAAJAAEAAAACRsAAAHgDAAAA","38":"b:QgAAAACAGAAJAAEAAAACRsAAAHgDAAAA","46":"b:QgAAAACAGAAJAAEAAAACRsAAAHgDAAAA","48":"b:QgAAAAAAEAABAAEAAAACRsAAAHADAAAA","57":"b:QgAAAACAGAAJAAEAAAACRsAAAHgDAAAA","58":"b:QgAAAAAAEAABAAAAAAAARMAAAHADAAAA"},"packages/frontend/editor-ui/src/app/components/DropArea/DropArea.vue":{"8":"b:QsAAAACByAEEAAACgGAAsISrZQAAAAAA","13":"b:QsAAAACAyAEEAAACgGAAsISrZQAAAAAA"},"packages/frontend/editor-ui/src/app/composables/useAutocompleteTelemetry.ts":{"12":"b:wuUAAACAYAUAAAAAADAAEIC8HgIAAAAA","57":[58,138,139,140,141,14,146,147,148],"61":[58,138,139,140,141,14,146,147,148],"96":[58,138,139,140,141,14,146,147,148],"103":"b:wuUAAACAYAUAAAAAADAAEIC8HgIAAAAA","109":"b:wuUAAACAYAUAAAAAADAAEIC8HgIAAAAA"},"packages/frontend/editor-ui/src/app/composables/useLoadingService.ts":{"14":"b:AGAAAAgQGABUQAEAIAEAYAGFRYEDAAAA","26":[78,80,68,161],"32":[78,80,159,68,161],"39":"b:wuHfkAVIqAFEAAAA4CUAct2+X38DAAAA"},"packages/frontend/editor-ui/src/app/utils/expressions.ts":{"12":[185],"16":[185,139,140],"20":[185,58],"21":[185,58],"29":"b:wqUAAAAAIAAAAAAAADAAEACUGgAAAAAC","33":"b:goQAAAAAAAAAAAAAADAAEACUGAAAAAAC","37":"b:goQAAAAAAAAAAAAAADAAEBC0GAAAAAAC","43":[15,185,140,143,147,148],"47":[15,185,140,143,147,148],"51":[15,185,140,143,147,148],"55":[15,185,140,143,147,148],"59":[15,185,140,143,147,148],"63":"b:wuUAAACAYAUAAAAAADAAEJC8HgAAAAAC","78":"b:AqEAAAAAIAAAAAAAAAAAAACUGgAAAAAC","121":"b:wuUAAACAYAUAAAAAADAAEIC8HgAAAAAC","145":"b:kkHFsA0YCKwQAAAisBSJcFy8HAMAAAAC","155":"b:kkHEMA0YCKwQAAAioBSJcFyoHAIAAAAC"},"packages/frontend/editor-ui/src/features/ai/evaluation.ee/composables/useAddExecutionToDataset.ts":{"15":[52,126,134,135,64,6,156,157,158]},"packages/frontend/editor-ui/src/features/execution/executions/components/ExecutionStopAllText.vue":{"11":[1,52,126,134,135,64,6,156,157,158],"18":[1,52,126,134,135,64,6,156,157,158],"25":[1,52,126,134,135,64,6,156,157,158]},"packages/frontend/editor-ui/src/features/execution/executions/components/ExecutionsFilter.vue":{"41":[1,52,126,134,135,64,6,156,157,158],"63":[156],"66":[156],"67":[156],"69":[1,52,126,134,135,64,6,156,157,158],"87":[156],"104":[156],"113":[156],"120":[156],"127":[156],"139":[156],"155":[1,52,126,134,135,64,6,156,157,158],"205":[156],"219":[1,52,126,134,135,64,6,156,157,158],"222":[1,52,126,134,135,64,6,156,157,158],"233":[1,52,126,134,135,64,6,156,157,158],"249":[156],"256":[156],"300":[156],"352":[156],"364":[156],"373":[156],"375":[156],"378":[156],"410":[156],"429":[156],"455":[156],"477":[156],"501":[156]},"packages/frontend/editor-ui/src/features/execution/executions/components/workflow/ExecutionPreviewHost.vue":{"18":[52,126,134,135,64,6,156,157,158],"33":[52,134,135,64,6,156,157,158],"36":[52,126,134,135,64,6,156,157,158],"44":[52,134,135,64,6,156,157,158],"56":[52,126,134,135,64,6,156,157,158],"57":[52,134,135,64,6,156,157,158],"58":[52,126,134,135,64,6,156,157,158],"60":[52,134,135,64,6,156,157,158],"71":[52,126,134,135,64,6,156,157,158],"110":[52,126,134,135,64,6,156,157,158],"111":[52,126,134,135,64,6,156,157,158],"113":[126,134,135,157,158],"116":[52,126,134,135,64,6,156,157,158]},"packages/frontend/editor-ui/src/features/execution/executions/components/workflow/WorkflowExecutionsCard.vue":{"41":[52,126,134,135,64,6,156,157,158],"44":[52,126,134,135,64,6,156,157,158],"48":[52,126,134,135,64,6,156,157,158],"49":[134,156,157],"59":[52,126,134,135,64,6,156,157,158],"62":[52,126,134,135,64,6,156,157,158],"63":[52,126,134,135,64,6,156,157,158],"65":[52,126,134,135,64,6,156,157,158],"71":[52,126,134,135,64,6,156,157,158],"106":[52,126,134,135,64,6,156,157,158],"121":[158],"139":[52,126,134,135,64,6,156,157,158],"183":[52,126,134,135,64,6,156,157,158]},"packages/frontend/editor-ui/src/features/execution/executions/components/workflow/WorkflowExecutionsInfoAccordion.vue":{"58":[1,52,126,134,135,64,6,156,157,158],"78":[1,52,126,134,135,64,6,156,157,158],"88":[1,52,126,134,135,64,6,156,157,158],"96":[1,52,126,134,135,64,6,156,157,158],"109":[1,52,126,134,135,64,6,156,157,158],"110":[1,52,126,134,135,64,6,156,157,158],"123":[1,52,126,134,135,64,6,156,157,158],"130":[1,52,126,134,135,64,6,156,157,158]},"packages/frontend/editor-ui/src/features/execution/executions/components/workflow/WorkflowExecutionsLandingPage.vue":{"22":[1,126,134,135,64,6,156,157,158],"25":[1,126,134,135,64,6,156,157,158],"27":[157],"45":[157],"48":[157],"56":[1,126,134,135,64,6,156,157,158]},"packages/frontend/editor-ui/src/features/execution/executions/components/workflow/WorkflowExecutionsList.vue":{"12":[1,52,126,134,135,64,6,156,157,158],"42":[1,52,126,134,135,64,6,156,157,158],"43":[52,126,134,135,64,6,156,157,158],"48":[1,52,126,134,135,64,6,156,157,158],"52":[135],"73":[1,126,134,157,158],"81":[1,52,126,134,135,64,6,156,157,158],"92":[6],"94":[156]},"packages/frontend/editor-ui/src/features/execution/executions/components/workflow/WorkflowExecutionsPreview.vue":{"54":[52,126,134,135,64,6,156,157,158],"58":[52,126,134,135,64,6,156,157,158],"59":[52,126,134,135,64,6,156,157,158],"60":[52,126,134,135,64,6,156,157,158],"63":[52,126,134,135,64,6,156,157,158],"74":[52,126,134,135,64,6,156,157,158],"83":[52,126,134,135,64,6,156,157,158],"107":[52,126,134,135,64,6,156,157,158],"111":[135],"122":[52,126,134,135,64,6,156,157,158],"128":[52,126,134,135,64,6,156,157,158],"138":[52,126,134,135,64,6,156,157,158],"150":[52,126,134,135,64,6,156,157,158],"172":[52,126,134,135,64,6,156,157,158],"178":[52,126,134,135,64,6,156,157,158],"180":[52,126,134,135,64,6,156,157,158],"207":[135],"277":[158],"285":[158],"298":[52,126,134,135,64,6,156,157,158],"318":[52,126,134,135,64,6,156,157,158],"349":[52,126,134,135,64,6,156,157,158],"361":[52,126,134,135,64,6,156,157,158],"395":[52,126,134,135,64,6,156,157,158],"407":[52,126,134,135,64,6,156,157,158],"410":[134],"435":[134,156],"438":[134,156],"441":[134,156]},"packages/frontend/editor-ui/src/features/execution/executions/components/workflow/WorkflowExecutionsSidebar.vue":{"26":[1,52,126,134,135,64,6,156,157,158],"63":[64,157],"70":[1,52,126,134,135,64,6,156,157,158],"79":[52,126,134,135,64,6,156,157,158],"83":[1,52,126,134,135,64,6,156,157,158],"87":[1,52,126,134,135,64,6,156,157,158],"97":[1,52,126,134,135,64,6,156,157,158],"98":[52,126,134,135,64,6,156,157,158],"107":[52,126,134,135,64,6,156,157,158],"116":[52,126,134,135,64,6,156,157,158],"125":[52,126,134,135,64,6,156,157,158],"136":[156],"143":[6],"147":[126,134,156],"169":[1,52,126,134,135,64,6,156,157,158],"176":[1,52,126,134,135,64,6,156,157,158],"218":[1,52,126,134,135,64,6,156,157,158],"223":[135,156],"231":[1,52,126,134,135,64,6,156,157,158],"234":[52,126,134,135,64,6,156,157,158]},"packages/frontend/editor-ui/src/features/execution/executions/composables/useExecutionHelpers.ts":{"24":[52,126,134,135,64,6,156,157,158],"69":[52,126,134,135,64,6,156,157,158],"74":[52,126,134,135,64,6,156,157,158],"87":[151,152]},"packages/frontend/editor-ui/src/features/execution/executions/composables/useExecutionPreviewDocument.ts":{"51":[52,126,134,135,64,6,156,157,158],"95":[52,134,135,64,6,156,157,158],"107":[52,134,135,64,6,156,157,158],"136":[52,126,134,135,64,6,156,157,158],"163":[52,126,134,135,64,6,156,157,158],"197":[157],"198":[157],"286":[52,134,135,64,6,156,157,158],"302":[126,134,135,157,158]},"packages/frontend/editor-ui/src/features/execution/executions/views/WorkflowExecutionsView.vue":{"37":[1,52,126,134,135,64,6,156,157,158],"42":[1,52,126,134,135,64,6,156,157,158],"51":[1,52,126,134,135,64,6,156,157,158],"52":[52,126,134,135,64,6,156,157,158],"70":[1,52,126,134,135,64,6,156,157,158],"74":[1,52,126,134,135,64,6,156,157,158],"81":[1,52,126,134,135,64,6,156,157,158],"83":[126,134,135,64,6,156,157,158],"88":[1,52,126,134,135,64,6,156,157,158],"99":[1,126,134,157,158],"104":[1,52,126,134,135,64,6,156,157,158],"130":[52],"138":[1,52,126,134,135,64,6,156,157,158],"150":[1,52,126,134,135,64,6,156,157,158],"162":[6],"197":[156],"224":[135],"230":[135],"301":[64,157],"307":[1,52,126,134,135,64,6,156,157,158],"313":[64,157]},"packages/frontend/editor-ui/src/features/execution/logs/components/LogsViewConsumedTokenCountText.vue":{"8":[48,6],"20":[48]},"packages/frontend/editor-ui/src/features/ndv/panel/components/InputNodeSelect.vue":{"18":"b:wFEAAAAAAAAAAAAAABAAAADIAwAAAAAA","29":"b:wFEAAAAAAAAAAAAAABAAAADIAwAAAAAA","33":"b:wFEAAAAAAAAAAAAAABAAAADIAwAAAAAA","40":"b:wFEAAAAAAAAAAAAAABAAAADIAwAAAAAA","43":[12,6,139,142,143,144,145],"56":[12,6,139,142,143,144,145],"58":[12,6,139,142,143,144,145],"65":[12,6,139,142,143,144,145],"70":[12,6,139,142,143,144,145],"79":[12,6,139,142,143,144,145],"106":[12,6,139,142,143,144,145],"110":[12,6,139,142,143,144,145],"116":[139,145],"118":"b:wFEAAAAAAAAAAAAAABAAAADIAwAAAAAA","133":"b:wFEAAAAAAAAAAAAAABAAAADIAwAAAAAA","139":"b:wFEAAAAAAAAAAAAAABAAAADIAwAAAAAA"},"packages/frontend/editor-ui/src/features/ndv/parameters/components/AssignmentCollection/Assignment.vue":{"35":"b:QMAAAACBUAEAAAACgCAAgIAqBQAAAAAA","39":"b:QMAAAACBUAEAAAACgCAAgIAqBQAAAAAA","57":"b:QMAAAACBUAEAAAACgCAAgIAqBQAAAAAA","81":"b:QMAAAACBUAEAAAACgCAAgIAqBQAAAAAA","90":"b:QMAAAACBUAEAAAACgCAAgIAqBQAAAAAA","104":"b:QMAAAACBUAEAAAACgCAAgIAqBQAAAAAA","106":"b:QMAAAACBUAEAAAACgCAAgIAqBQAAAAAA","115":"b:QMAAAACBUAEAAAACgCAAgIAqBQAAAAAA","117":[15,47,54,56,135,6,139,141,14,146],"119":[89,103,139],"123":[89,103],"131":[89,103,139,141,14,146],"139":[89,103,127,139,141,14],"143":[139],"156":"b:QMAAAACBUAEAAAACgCAAgIAqBQAAAAAA","213":"b:QMAAAACBUAEAAAACgCAAgIAqBQAAAAAA","229":"b:QMAAAACBUAEAAAACgCAAgIAqBQAAAAAA","233":"b:QMAAAACBUAEAAAACgCAAgIAqBQAAAAAA","243":"b:QMAAAACBUAEAAAACgCAAgIAqBQAAAAAA"},"packages/frontend/editor-ui/src/features/ndv/parameters/components/AssignmentCollection/AssignmentCollection.vue":{"39":"b:QsAAAACB2AEEAAACgGAAsISrZQAAAAAA","53":"b:QsAAAACB2AEEAAACgGAAsISrZQAAAAAA","56":"b:QMAAAACBUAEAAAAAACAAgIAKAQAAAAAA","72":"b:QMAAAACBUAEAAAACgCAAgIAqBQAAAAAA","77":"b:QsAAAACB2AEEAAACgGAAsISrZQAAAAAA","78":"b:QsAAAACB2AEEAAACgGAAsISrZQAAAAAA","90":[139],"91":"b:QsAAAACB2AEEAAACgGAAsISrZQAAAAAA","92":"b:QsAAAACB2AEEAAACgGAAsISrZQAAAAAA","107":"b:QsAAAACB2AEEAAACgGAAsISrZQAAAAAA","116":"b:QsAAAACB2AEEAAACgGAAsISrZQAAAAAA","117":[89,103,137,139,141,14,146],"119":[89,103,137,139,141,14,146],"129":[89,103,137,141,146],"150":[89,103,127,139,141,14],"158":"b:QMAAAACBUAEAAAACgCAAgIAqBQAAAAAA","168":"b:QsAAAACB2AEEAAACgGAAsISrZQAAAAAA","185":"b:QsAAAACB2AEEAAACgGAAsISrZQAAAAAA","220":"b:QMAAAACBUAEAAAACgCAAgIAqBQAAAAAA","229":[89,103,127,139,141,14],"257":"b:QsAAAACByAEEAAACgGAAsISrZQAAAAAA"},"packages/frontend/editor-ui/src/features/ndv/parameters/components/AssignmentCollection/TypeSelect.vue":{"17":"b:QMAAAACBUAEAAAACgCAAgIAqBQAAAAAA","30":"b:QMAAAACBUAEAAAACgCAAgIAqBQAAAAAA","32":"b:QMAAAACBUAEAAAACgCAAgIAqBQAAAAAA","33":"b:QMAAAACBUAEAAAACgCAAgIAqBQAAAAAA","43":[89,103],"45":"b:QMAAAACBUAEAAAACgCAAgIAqBQAAAAAA","58":"b:QMAAAACAUAEAAAACgCAAgIAqBQAAAAAA","60":"b:QMAAAACAUAEAAAACgCAAgIAqBQAAAAAA"},"packages/frontend/editor-ui/src/features/ndv/parameters/components/ExpressionParameterInput.vue":{"26":"b:wuUAAACAYAUAAAAAADAAEIC8HgIAAAAA","67":"b:wuUAAACAYAUAAAAAADAAEIC8HgIAAAAA","68":"b:wuUAAACAYAUAAAAAADAAEIC8HgIAAAAA","78":"b:AEAAAAAAAAQAAAAAAAAAAAA8HgAAAAAA","84":"b:AEAAAAAAAAQAAAAAAAAAAAA8HgAAAAAA","89":[58,138,139,140,141,14,145,147,148],"97":"b:wuUAAAAAQAQAAAAAACAAAIC8GgAAAAAA","100":"b:QuAAAAAAAAQAAAAAAAAAAIA8GgAAAAAA","128":"b:QuEAAACAYAUAAAAAAAAAAIC8HgAAAAAA","137":"b:wuUAAACAYAUAAAAAADAAEIC8HgIAAAAA","148":[139],"160":[139],"166":[139],"175":[139],"181":"b:wuUAAACAYAUAAAAAADAAEIC8HgIAAAAA","185":"b:wqUAAAAAQAQAAAAAADAAEIAUAgAAAAAA","189":[139],"196":"b:wuUAAAAAQAQAAAAAACAAAIC8GgAAAAAA","199":"b:wuUAAACAYAUAAAAAADAAEIC8HgIAAAAA","219":"b:wuUAAACAYAUAAAAAADAAEIC8HgIAAAAA"},"packages/frontend/editor-ui/src/features/ndv/parameters/components/InputTriple/InputTriple.vue":{"10":"b:QMAAAACRUAEAAAACgCAAoIAqBQAAAAAA","13":"b:QMAAAACRUAEAAAACgCAAoIAqBQAAAAAA","21":"b:QMAAAACRUAEAAAACgCAAoIAqBQAAAAAA","22":"b:QMAAAACRUAEAAAACgCAAoIAqBQAAAAAA"},"packages/frontend/editor-ui/src/features/ndv/parameters/components/ParameterInputHint.vue":{"13":"b:wuACAACAQAUAAAAAwRAAIpS+XggAAAAA","21":"b:wuACAACAQAUAAAAAwRAAIpS+XggAAAAA","23":[54],"27":"b:QMAAAACAQAUAAAAAAAAAAIC8HgAAAAAA","39":"b:wuACAACAQAUAAAAAwRAAIpS+XggAAAAA","43":"b:wuACAACAQAUAAAAAwRAAIpS+XggAAAAA"},"packages/frontend/editor-ui/src/features/ndv/parameters/components/ResourceLocator/ResourceLocator.vue":{"81":"b:wmECIIAACMAAAAAAAxAAAgCACAgAAAAA","173":[14,147],"182":"b:wmECIIAACMAAAAAAAxAAAgCACAgAAAAA","195":"b:wmECIIAACMAAAAAAAxAAAgCACAgAAAAA","197":[29,8,51,62,63,143,13,14,147,155],"204":[143,147],"207":[143,147],"216":[1,7,143,147],"229":"b:wmECIIAACMAAAAAAAxAAAgCACAgAAAAA","242":"b:wmECIIAACMAAAAAAAxAAAgCACAgAAAAA","246":"b:wmECIIAACMAAAAAAAxAAAgCACAgAAAAA","250":"b:wmECIIAACMAAAAAAAxAAAgCACAgAAAAA","251":"b:wmECIIAACMAAAAAAAxAAAgCACAgAAAAA","263":"b:wmECIIAACMAAAAAAAxAAAgCACAgAAAAA","317":"b:wmECIIAACMAAAAAAAxAAAgCACAgAAAAA","327":"b:wmECIIAACMAAAAAAAxAAAgCACAgAAAAA","329":"b:wmECIIAACMAAAAAAAxAAAgCACAgAAAAA","342":"b:wmECIIAACMAAAAAAAxAAAgCACAgAAAAA","344":"b:wmECIIAACMAAAAAAAxAAAgCACAgAAAAA","347":[147],"355":"b:wmECIIAACMAAAAAAAxAAAgCACAgAAAAA","357":"b:wmECIIAACMAAAAAAAxAAAgCACAgAAAAA","364":"b:wmECIIAACMAAAAAAAxAAAgCACAgAAAAA","368":"b:wmECIIAACMAAAAAAAxAAAgCACAgAAAAA","369":"b:wmECIIAACMAAAAAAAxAAAgCACAgAAAAA","370":"b:wmECIIAACMAAAAAAAxAAAgCACAgAAAAA","373":[1,7,143,147],"377":"b:wmECIIAACMAAAAAAAxAAAgCACAgAAAAA","391":"b:wmECIIAACMAAAAAAAxAAAgCACAgAAAAA","397":"b:wmECIIAACMAAAAAAAxAAAgCACAgAAAAA","403":"b:wmECIIAACMAAAAAAAxAAAgCACAgAAAAA","407":"b:wmECIIAACMAAAAAAAxAAAgCACAgAAAAA","494":"b:wmECIIAACMAAAAAAAxAAAgCACAgAAAAA","500":[143,147],"508":"b:wmECIIAACMAAAAAAAxAAAgCACAgAAAAA","519":[14,147],"529":[143,147],"549":"b:wmECIIAACMAAAAAAAxAAAgCACAgAAAAA","550":[143,14,147],"570":"b:wmECIIAACMAAAAAAAxAAAgCACAgAAAAA","572":[1,7,29,8,51,108,14,147],"617":"b:wmECIIAACMAAAAAAAxAAAgCACAgAAAAA","621":"b:wmECIAAACMAAAAAAAxAAAgCACAgAAAAA","626":"b:wmECIIAACMAAAAAAAxAAAgCACAgAAAAA","631":"b:wmAAAAAACAAAAAAAAxAAAgCAAAgAAAAA","639":"b:wmECIAAACMAAAAAAAhAAAgCACAgAAAAA","646":"b:wmECIIAACMAAAAAAAxAAAgCACAgAAAAA","652":[14,147],"679":"b:wmECIIAACMAAAAAAAxAAAgCACAgAAAAA","702":[147],"722":"b:wmECIIAACMAAAAAAAxAAAgCACAgAAAAA","724":"b:wmECIIAACMAAAAAAAxAAAgCACAgAAAAA","729":"b:wmECIIAACMAAAAAAAxAAAgCACAgAAAAA","737":[14,147],"754":[147],"760":[147],"766":[14,147],"785":[14,147],"807":[143,147],"814":[147],"826":[1,7,143,147],"837":[1,7,143,147],"954":[1,7,147],"967":[1,7,147],"975":[143,14,147],"1000":"b:wmECIAAACMAAAAAAAhAAAgCACAgAAAAA","1008":[143,147],"1014":[143,147],"1027":[143,14,147],"1070":"b:wmECIIAACMAAAAAAAxAAAgCACAgAAAAA","1100":[143,147],"1107":[143,147],"1112":[147],"1140":"b:wmECIIAACMAAAAAAAxAAAgCACAgAAAAA","1160":"b:wmECIIAACMAAAAAAAxAAAgCACAgAAAAA","1169":"b:wmECIIAACMAAAAAAAxAAAgCACAgAAAAA","1191":"b:wmECIIAACMAAAAAAAxAAAgCACAgAAAAA","1243":"b:wmECAIAAAAAAAAAAAxAAAgCACAAAAAAA","1270":[14,147]},"packages/frontend/editor-ui/src/features/ndv/parameters/components/ResourceLocator/ResourceLocatorDropdown.vue":{"14":"b:wmECIIAACMAAAAAAAxAAAgCACAsAAAAA","59":[147],"77":[51,147,153],"79":[51,147,153],"119":"b:wmECIIAACMAAAAAAAxAAAgCACAsAAAAA","120":[51,143,147,153],"126":[51,143,147,153],"135":"b:wmECIIAACMAAAAAAAxAAAgCACAsAAAAA","139":"b:wmAAAAAACAAAAAAAAxAAAgCAAAsAAAAA","199":[153],"203":[153],"216":[51,153],"219":[51,153],"230":[153],"245":"b:gmECIAAACMAAAAAAAhAAAAAACAIAAAAA","251":"b:wmECIIAACMAAAAAAAxAAAgCACAsAAAAA","253":"b:wmECIIAACMAAAAAAAxAAAgCACAsAAAAA","267":"b:wmECIIAACMAAAAAAAxAAAgCACAsAAAAA","279":"b:wmECIIAACMAAAAAAAxAAAgCACAsAAAAA","282":[51,143,147,153],"304":[51,147,153],"341":[51,153],"343":[51,153],"351":[51,147,153],"362":[153],"363":[153],"390":[147]},"packages/frontend/editor-ui/src/features/ndv/runData/components/RunDataJson.vue":{"22":[12,135,6,139,144],"59":[12,135,6,139,144],"61":[12,135,6,139,144],"63":[139],"71":[12,135,6,139,144],"82":[12,135,6,139,144],"83":[12,135,6,139,144],"85":[139],"98":[139],"122":[12,135,6,139,144],"126":[12,135,6,139,144],"130":[12,135,6,139,144],"166":[12,135,6,139,144],"181":[12,135,6,139,144]},"packages/frontend/editor-ui/src/features/ndv/runData/components/RunDataJsonActions.vue":{"64":[12,135,6,139,144]},"packages/frontend/editor-ui/src/features/ndv/runData/components/RunDataParsedAiContent.vue":{"31":[6],"33":[6],"54":[6],"63":[6],"72":[6],"76":[6]},"packages/frontend/editor-ui/src/features/ndv/runData/components/RunDataPinButton.vue":{"6":"b:QADAgACZEAEAACAA4AQAANj+l0EAAAAA","24":"b:QADAgACZEAEAACAA4AQAANj+l0EAAAAA","27":"b:QADAgACZEAEAACAA4AQAANj+l0EAAAAA","31":[101,103,137,142],"49":"b:QADAgACZEAEAACAA4AQAANj+l0EAAAAA","66":[134,146]},"packages/frontend/editor-ui/src/features/ndv/runData/components/VirtualSchemaHeader.vue":{"22":"b:QNBBgACZsAEAAAAAAAQAoNj/t0EAAAAA","24":"b:QNBBgACZsAEAAAAAAAQAoNj/t0EAAAAA","70":[136],"72":[136],"77":[137,149],"79":[137,149],"82":[137,149],"84":[137,149]},"packages/frontend/editor-ui/src/features/ndv/runData/components/ai/utils.ts":{"38":[6],"51":[6],"52":[6],"53":[6]},"packages/frontend/editor-ui/src/features/ndv/runData/components/error/NodeErrorView.vue":{"47":[15,16,1,7,134,6,143],"70":[15,16,1,7,134,6,143],"74":[15,143],"87":[15,16,1,7,134,6,143],"98":[15,16,1,7,134,6,143],"102":[1,7,134],"111":[15,16,1,7,134,6,143],"118":[1,7,134],"142":[15,16,1,7,134,6,143],"159":[15,16,1,7,134,6,143],"164":[15,16],"171":[15,16,1,7,134,6,143],"178":[15,1,7,134,143],"194":[15,16,1,7,143],"198":[15,16,1,7,143],"223":[15,16,1,7,134,6,143],"235":[15,16,1,7,134,6,143],"238":[15,16,1,7,134,6,143],"263":[15,143],"273":[15,143],"279":[15,143],"293":[15,143],"415":[15,16,1,7,134,143],"449":[15,16],"480":[15,16,1,7,134,6,143],"536":[15,16,1,7,134,6,143],"575":[1,7,134]},"packages/frontend/editor-ui/src/features/shared/editors/components/InlineExpressionEditor/InlineExpressionEditorInput.vue":{"26":"b:wuUAAACAYAUAAAAAADAAEIC8HgIAAAAA","39":"b:wuUAAACAYAUAAAAAADAAEIC8HgIAAAAA","51":"b:wuUAAACAYAUAAAAAADAAEIC8HgIAAAAA","75":"b:wuUAAACAYAUAAAAAADAAEIC8HgIAAAAA","81":"b:QuEAAACAYAUAAAAAAAAAAIC8HgAAAAAA","88":"b:wuUAAACAYAUAAAAAADAAEIC8HgIAAAAA","97":"b:AEAAAAAAAAQAAAAAAAAAAAA8HgAAAAAA","104":"b:AEAAAAAAAAQAAAAAAAAAAAA8HgAAAAAA","107":[58,138,139,140,141,14,146,147,148],"118":"b:wuUAAACAYAUAAAAAADAAEIC8HgIAAAAA"},"packages/frontend/editor-ui/src/features/shared/editors/components/InlineExpressionEditor/InlineExpressionEditorOutput.vue":{"32":"b:wuUAAACAYAUAAAAAADAAEJC8HgIAAAAA","38":"b:wqUAAAAAQAQAAAAAADAAEJAUAgAAAAAA","45":"b:wuUAAACAYAUAAAAAADAAEJC8HgIAAAAA","66":"b:AEAAAAAAAAQAAAAAAAAAABA8HgAAAAAA","72":"b:AEAAAAAAAAQAAAAAAAAAABA8HgAAAAAA"},"packages/frontend/editor-ui/src/features/shared/editors/components/InlineExpressionEditor/theme.ts":{"4":"b:wuUAAACAYAUAAAAAADAAEJC8HgIAAAAA","18":"b:wuUAAACAYAUAAAAAADAAEIC8HgIAAAAA","58":"b:wuUAAACAYAUAAAAAADAAEJC8HgIAAAAA"},"packages/frontend/editor-ui/src/features/shared/editors/composables/useExpressionEditor.ts":{"60":"b:wuUAAACAYAUAAAAAADAAEJC8HgIAAAAA","71":[58,138,139,140,141,14,146,147,148],"86":"b:wuUAAACAYAUAAAAAADAAEJC8HgIAAAAA","104":"b:wuUAAACAYAUAAAAAADAAEJC8HgIAAAAA","109":"b:wuUAAACAYAUAAAAAADAAEJC8HgAAAAAA","123":"b:wuUAAACAYAUAAAAAADAAEJC8HgAAAAAA","143":"b:wuUAAACAYAUAAAAAADAAEJC8HgAAAAAA","177":"b:wuUAAACAYAUAAAAAADAAEJC8HgIAAAAA","181":"b:QuEAAACAYAUAAAAAAAAAAJC8HgAAAAAA","187":"b:wuUAAACAYAUAAAAAADAAEJC8HgIAAAAA","198":"b:wuUAAACAYAUAAAAAADAAEJC8HgIAAAAA","202":"b:wuUAAACAYAUAAAAAADAAEJC8HgIAAAAA","213":"b:guUAAAAAAAQAAAAAABAAEBC8GgAAAAAA","221":"b:guUAAAAAAAQAAAAAABAAEBC8HgAAAAAA","228":[132,138,139,140,141,14,146,147,148],"239":"b:wuUAAACAYAUAAAAAADAAEIC8HgIAAAAA","250":"b:wuUAAACAYAUAAAAAADAAEJC8HgIAAAAA","277":"b:AEAAAAAAAAQAAAAAAAAAABA8HgAAAAAA","282":"b:AEAAAAAAAAQAAAAAAAAAAAA8HgAAAAAA","294":"b:AEAAAAAAAAQAAAAAAAAAABA0HgAAAAAA","310":"b:wuUAAACAYAUAAAAAADAAEJC8HgIAAAAA","318":"b:wuUAAACAYAUAAAAAADAAEJC8HgIAAAAA","328":"b:wuUAAACAYAUAAAAAADAAEJC8HgIAAAAA","340":"b:wuUAAACAYAUAAAAAADAAEJC8HgIAAAAA","351":"b:wuUAAACAYAUAAAAAADAAEJC8HgIAAAAA","355":"b:wqUAAAAAQAQAAAAAADAAEJAUAgAAAAAA","362":[1,7,10,108,109,124,138,140],"364":[1,7,10,108,109,124,138,140],"370":[1,7,10,108,109,124,138,140],"380":"b:wuUAAACAYAUAAAAAADAAEJC8HgAAAAAA","448":"b:wuUAAACAYAUAAAAAADAAEJC8HgIAAAAA","450":"b:wuUAAACAYAUAAAAAADAAEJC8HgIAAAAA","451":"b:QuEAAACAYAUAAAAAAAAAAJC8HgAAAAAA","454":"b:QuEAAACAYAUAAAAAAAAAAJC8HgAAAAAA","455":"b:QuEAAACAYAUAAAAAAAAAAJC8HgAAAAAA","485":"b:wuUAAACAYAUAAAAAADAAEIC8HgIAAAAA","488":"b:QuEAAACAYAUAAAAAAAAAAIC8HgAAAAAA","518":"b:QuEAAACAYAUAAAAAAAAAAIC8HgAAAAAA","534":"b:wuUAAACAYAUAAAAAADAAEJC8HgIAAAAA","535":"b:wuUAAACAYAUAAAAAADAAEJC8HgIAAAAA","545":[58,138,139,140,141,14,146,147,148],"565":[58,138,139,140,141,14,146,147,148]},"packages/frontend/editor-ui/src/features/shared/editors/plugins/codemirror/completions/addCompletions.ts":{"8":"b:wuUAAACAYAUAAAAAADAAEJC8HgIAAAAA","15":"b:wuUAAACAYAUAAAAAADAAEJC8HgIAAAAA"},"packages/frontend/editor-ui/src/features/shared/editors/plugins/codemirror/expressionCloseBrackets.ts":{"10":"b:wuUAAACAYAUAAAAAADAAEJC8HgIAAAAA","14":"b:AEAAAAAAAAQAAAAAAAAAABA8HAAAAAAA","36":"b:wuUAAACAYAUAAAAAADAAEJC8HgIAAAAA"},"packages/frontend/editor-ui/src/features/shared/editors/plugins/codemirror/expressionDeprecations.ts":{"11":"b:wuUAAACAYAUAAAAAADAAEJC8HgAAAAAA","15":"b:wuUAAACAYAUAAAAAADAAEJC8HgAAAAAA"},"packages/frontend/editor-ui/src/features/workflows/canvas/components/elements/background/CanvasBackgroundStripedPattern.vue":{"6":"b:QAAYAOADEAANADAAAAAACOAAAHACAAAC","13":"b:QAAYAOADEAANADAAAAAACOAAAHACAAAC","14":"b:QAAYAOADEAANADAAAAAACOAAAHACAAAC","15":"b:QAAYAOADEAANADAAAAAACOAAAHACAAAC"},"packages/frontend/editor-ui/src/features/workflows/canvas/components/elements/edges/CanvasEdgeToolbar.vue":{"12":"b:QAAAAAACiAAAAAAAAIABUAUAAAEAAAAC","27":"b:QAAAAAACiAAAAAAAAIABUAUAAAEAAAAC","31":"b:QAAAAAACiAAAAAAAAIABUAUAAAEAAAAC","41":[185,55,111,124],"45":[185,124,128,130],"47":"b:QAAAAAACiAAAAAAAAIABUAUAAAEAAAAC","55":"b:QAAAAAACiAAAAAAAAIABUAUAAAEAAAAC","72":"b:QAAAAAACiAAAAAAAAIABUAUAAAEAAAAC"},"packages/frontend/editor-ui/src/features/workflows/canvas/components/elements/edges/CanvasEdgeTooltip.vue":{"9":"b:QAAAAAACiAAAAAAAAIABUAUAAAEAAAAC","20":"b:QAAAAAACiAAAAAAAAIABUAUAAAEAAAAC"},"packages/frontend/editor-ui/src/features/workflows/canvas/components/elements/nodes/render-types/CanvasNodeAddNodes.vue":{"18":"b:wuXH8A1YqcGMIAAC/9kBc968fa4BAAAC","26":"b:wuXH8A1YqcGMIAAC/9kBc968fa4BAAAC","30":"b:wuXH8A1YqcGMIAAC/9kBc968fa4BAAAC","35":"b:wuXHsAVYqMEEIAAC/9gBc968faoBAAAC","40":[185],"44":[185],"48":"b:AIACEAAACAAAIAAAGAAAQJQQAAABAAAC","55":[185],"63":"b:wuXH8A1YqcGMIAAC/9kBc968fa4BAAAC","71":[185],"83":[185]},"packages/frontend/editor-ui/src/features/workflows/canvas/components/elements/nodes/render-types/parts/CanvasNodeDisabledStrikeThrough.vue":{"6":"b:QAAAAAAAAAABAAAAAAAAUgQAAHgAAAAC","10":"b:QAAAAAAAAAABAAAAAAAAUgQAAHgAAAAC","19":"b:QAAAAAAAAAABAAAAAAAAUgQAAHgAAAAC"},"packages/@n8n/backend-network/src/http/env-proxy-http-agent.ts":{"23":"b:AYXN0KUl2IjdfRCAAAAAAAACAABoAAgA","28":"b:AYXN0KUl2IjdfRCAAAAAAAACAABoAAgA","32":"b:AYXN0KUl2IjdfRCAAAAAAAACAABoAAgA"},"packages/cli/src/modules/data-table/data-table-file-cleanup.service.ts":{"13":"b:QoEKdLQDEIQcJCMwEAEEEAAAAAAAAAAC","20":"b:QoEKdLQDEIQcJCMwEAEEEAAAAAAAAAAC","29":"b:QoEKdLQDEIQcJCMwEAEEEAAAAAAAAAAC","33":"b:QoEKdLQDEIQcJCMwEAEEEAAAAAAAAAAC","38":"b:QoEKdLQDEIQcJCMwEAEEEAAAAAAAAAAC","49":"b:QoEKdLQDEIQcJCMwEAEEEAAAAAAAAAAC","82":"b:QoEKdLQDEIQcJCMwEAEEEAAAAAAAAAAC"},"packages/cli/src/services/naming.service.ts":{"6":"b:UsXf8A0IiIWcYBAAFViAEgCQAAAAAAAC","11":"b:UsXf8A0IiIWcYBAAFViAEgCQAAAAAAAC","15":"b:UsXf8A0IiIWcYBAAFViAEgCQAAAAAAAC","19":"b:UsXf8A0IiIWcYBAAFViAEgCQAAAAAAAC","28":"b:UsXf8A0IiIWcYBAAFViAEgCQAAAAAAAC"},"packages/cli/src/wait-tracker.ts":{"35":"b:QoEKdLQDGIQcJCMoAAEEAAAAAAAAAKAC","50":"b:QoEKdLQDGIQcJCMoAAEEAAAAAAAAAKAC","62":"b:QoEKdLQDGIQcJCMoAAEEAAAAAAAAAKAC","66":"b:QoEKdLQDGIQcJCMoAAEEAAAAAAAAAKAC","71":"b:QoEKdLQDGIQcJCMoAAEEAAAAAAAAAKAC","73":"b:QoEKdLQDGIQcJCMoAAEEAAAAAAAAAKAC","82":"b:QoEKdLQDGIQcJCMoAAEEAAAAAAAAAKAC","91":"b:QoEKdLQDGIQcJCMoAAEEAAAAAAAAAKAC","104":"b:QoEKdLQDGIQcJCMoAAEEAAAAAAAAAKAC","112":"b:QoEKdLQDGIQcJCMoAAEEAAAAAAAAAKAC","120":"b:QoEKdLQDGIQcJCMoAAEEAAAAAAAAAKAC","191":"b:QoEKdLQDGIQcJCMoAAEEAAAAAAAAAKAC","202":"b:QoEKdLQDGIQcJCMoAAEEAAAAAAAAAKAC","214":"b:QoEKdLQDGIQcJCMoAAEEAAAAAAAAAKAC","235":"b:QoEKdLQDGIQcJCMoAAEEAAAAAAAAAKAC","238":"b:QoEKdLQDGIQcJCMoAAEEAAAAAAAAAKAC","252":"b:QoEKdLQDGIQcJCMoAAEEAAAAAAAAAKAC","256":"b:QoEKdLQDGIQcJCMoAAEEAAAAAAAAAKAC"},"localhost-33979/assets/_plugin-vue_export-helper-Da88TEg1.js":{"2":[77,44,102,116,121,128,131,132,156]},"localhost-33979/assets/chunk-CC9Q-vWm.js":{"8":[77,44,102,116,121,128,131,132,156],"9":[77,44,102,116,121,128,131,132,156],"10":[77,44,102,116,121,128,131,132,156],"19":[77,44,102,116,121,128,131,132,156],"23":[77,44,102,116,121,128,131,132,156],"29":[77,44,102,116,121,128,131,132,156],"33":[77,44,102,116,121,128,131,132,156],"34":[77,44,102,116,121,128,131,132,156]},"localhost-33979/assets/verified-L2wNNiNu.js":{"8":[77,44,121,131,132]},"packages/@n8n/utils/src/search/re-rank-search-results.ts":{"1":"b:guXCMAUYiMAEIAAC/9AAc9y9fQoAAAAC","6":"b:guXCMAUYiMAEIAAC/9AAc9y9fQoAAAAC","9":"b:guXCMAUYiMAEIAAC/9AAc9y9fQoAAAAC","23":"b:guFCMAEYCMAAIAAC3xAAc9y8fAoAAAAC"},"packages/@n8n/utils/src/search/sublime-search.ts":{"25":"b:guXCMIUYiMAEIAAC/9AAc9y9fQoAAAAC","41":"b:guXCMIUYiMAEIAAC/9AAc9y9fQoAAAAC","182":"b:guXCMIUYiMAEIAAC/9AAc9y9fQoAAAAC","205":"b:guXCMIUYiMAEIAAC/9AAc9y9fQoAAAAC","222":"b:guXCMIUYiMAEIAAC/9AAc9y9fQoAAAAC","231":"b:guXCMIUYiMAEIAAC/9AAc9y9fQoAAAAC","285":"b:guFCMIEYCMAAIAAC3xAAc9y8fAoAAAAC"},"packages/frontend/@n8n/design-system/src/components/N8nIcon/custom/toolbox.svg":{"10":"b:gsHCMAUIiMAAIAAAttAAcly9fQoAAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nIcon/nodes/edit-image.svg":{"11":[96,77,161]},"packages/frontend/@n8n/design-system/src/components/N8nIcon/nodes/form-trigger.svg":{"11":"b:guXCMAUYiMAEIAAC/9AAc9y8fCoBAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nIcon/nodes/sub-workflow-trigger.svg":{"11":"b:guXCMAUYiMAEIAAC/9AAc9y8fCoBAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nIcon/nodes/webhook.svg":{"11":"b:guXCcAWYmMAEICAC/9AAc9y+fCoDAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nLoading/Loading.vue":{"38":"b:guXCPB0Yj8R9IINT/9OGc/y9fSoDAAAA","47":"b:guXCPB0Yj8R9IINT/9OGc/y9fSoDAAAA","49":[92,36,94,68],"60":"b:guXCPB0Yj8R9IINT/9OGc/y9fSoDAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nNodeCreatorNode/NodeCreatorNode.vue":{"32":"b:guXCMAUYiMAEIAAC/9AAc9y9fSoBAAAA","33":"b:guXCMAUYiMAEIAAC/9AAc9y9fSoBAAAA","58":"b:AkEAIAAQCMAAAAAABAAAIBCAAAgAAAAA","61":[1,10,13]},"packages/frontend/editor-ui/src/app/composables/useCalloutHelpers.ts":{"33":"b:guXCMAUYiMAEIAAC/9AAc9y9fSoBAAAA","111":[1,7,8,10,108,124,135,13,14]},"packages/frontend/editor-ui/src/features/shared/nodeCreator/components/ItemTypes/CategoryItem.vue":{"12":"b:guWCMAEYCcAAIAAC/xAAc9y8fQoAAAAA","16":"b:guWCMAEYCcAAIAAC/xAAc9y8fQoAAAAA","20":"b:guWCMAEYCcAAIAAC/xAAc9y8fQoAAAAA"},"packages/frontend/editor-ui/src/features/shared/nodeCreator/components/ItemTypes/NodeItem.vue":{"39":"b:guXCMAUYicAEIAAC/9AAc9y9fSoBAAAA","59":"b:guXCMAUYicAEIAAC/9AAc9y9fSoBAAAA","79":"b:guXCMAUYicAEIAAC/9AAc9y9fSoBAAAA","93":"b:guXCMAUYicAEIAAC/9AAc9y9fSoBAAAA","94":"b:guXCMAUYicAEIAAC/9AAc9y9fSoBAAAA","99":"b:guXCMAUYicAEIAAC/9AAc9y9fSoBAAAA","103":"b:guXCMAUYicAEIAAC/9AAc9y9fSoBAAAA","107":"b:guXCMAUYicAEIAAC/9AAc9y9fSoBAAAA","111":"b:guXCMAUYicAEIAAC/9AAc9y9fSoBAAAA","117":"b:guXCMAUYicAEIAAC/9AAc9y9fSoBAAAA","119":"b:guXCMAUYicAEIAAC/9AAc9y9fSoBAAAA","124":"b:guXCMAUYicAEIAAC/9AAc9y9fSoBAAAA","125":"b:guXCMAUYicAEIAAC/9AAc9y9fSoBAAAA","127":"b:guXCMAUYicAEIAAC/9AAc9y9fSoBAAAA","136":"b:guXCMAUYicAEIAAC/9AAc9y9fSoBAAAA","140":"b:guXCMAUYicAEIAAC/9AAc9y9fSoBAAAA","144":"b:guXCMAUYicAEIAAC/9AAc9y9fSoBAAAA","148":[48],"152":"b:guXCMAUYicAEIAAC/9AAc9y9fSoBAAAA","164":"b:guXCMAUYicAEIAAC/9AAc9y9fSoBAAAA","166":[48],"180":[48],"182":[48],"187":[48],"190":"b:guXCMAUYicAEIAAC/9AAc9y9fSoBAAAA","210":"b:guXCMAUYicAEIAAC/9AAc9y9fSoBAAAA","223":"b:gmECMAEQCcAAIAACiRAAchyIJAgAAAAA","224":[48],"226":"b:gmECMAEQCcAAIAACiRAAchyIJAgAAAAA","236":[100,48],"238":[48],"241":[100,48],"255":"b:guXCMAUYicAEIAAC/9AAc9y9fSoBAAAA","267":[48]},"packages/frontend/editor-ui/src/features/shared/nodeCreator/components/ItemTypes/SubcategoryItem.vue":{"13":"b:guXCMAUYicAEIAAC/9AAc9y9fSoBAAAA","15":"b:guXCMAUYicAEIAAC/9AAc9y9fSoBAAAA","16":"b:guXCMAUYicAEIAAC/9AAc9y9fSoBAAAA"},"packages/frontend/editor-ui/src/features/shared/nodeCreator/components/ItemTypes/ViewItem.vue":{"13":"b:gsHCMAUIicAAIAAAttAAcly9fQoAAAAA"},"packages/frontend/editor-ui/src/features/shared/nodeCreator/components/Modes/NodesMode.vue":{"55":"b:guXCMAUYicAEIAAC/9AAc9y9fSoBAAAA","69":"b:guXCMAUYicAEIAAC/9AAc9y9fSoBAAAA","71":"b:guXCMAUYicAEIAAC/9AAc9y9fSoBAAAA","74":"b:guXCMAUYicAEIAAC/9AAc9y9fSoBAAAA","76":"b:guXCMAUYicAEIAAC/9AAc9y9fSoBAAAA","88":"b:guXCMAUYicAEIAAC/9AAc9y9fSoBAAAA","98":"b:guXCMAUYicAEIAAC/9AAc9y9fSoBAAAA","112":"b:guXCMAUYicAEIAAC/9AAc9y9fSoBAAAA","211":"b:gEGCMAEACcAAAAAArQAAchyUPQoAAAAA","270":[1,10,77,97,48],"290":[1,10,77,97,48],"306":"b:guXCMAUYicAEIAAC/9AAc9y9fSoBAAAA","314":[48],"318":[28,48,124],"326":[28,48,124],"334":[28,48,124],"340":[48],"343":"b:guXCMAUYicAEIAAC/9AAc9y9fSoBAAAA","352":"b:guXCMAUYicAEIAAC/9AAc9y9fSoBAAAA","362":[48],"363":[48]},"packages/frontend/editor-ui/src/features/shared/nodeCreator/components/Panel/NoResults.vue":{"20":[77,48],"41":[77,48],"46":[77,48],"56":[77,48]},"packages/frontend/editor-ui/src/features/shared/nodeCreator/components/Panel/NoResultsIcon.vue":{"8":[77,48]},"packages/frontend/editor-ui/src/features/shared/nodeCreator/components/Panel/NodesListPanel.vue":{"43":"b:guXCMAUYicAEIAAC/9AAc9y9fSoBAAAA","53":"b:guXCMAUYicAEIAAC/9AAc9y9fSoBAAAA","55":"b:guXCMAUYicAEIAAC/9AAc9y9fSoBAAAA","57":"b:guXCMAUYicAEIAAC/9AAc9y9fSoBAAAA","59":"b:guXCMAUYicAEIAAC/9AAc9y9fSoBAAAA","61":"b:guXCMAUYicAEIAAC/9AAc9y9fSoBAAAA","63":"b:guXCMAUYicAEIAAC/9AAc9y9fSoBAAAA","83":"b:guXCMAUYicAEIAAC/9AAc9y9fSoBAAAA","88":"b:guXCMAUYicAEIAAC/9AAc9y9fSoBAAAA","90":[100,48],"94":"b:guXCMAUYicAEIAAC/9AAc9y9fSoBAAAA","101":"b:guXCMAUYicAEIAAC/9AAc9y9fSoBAAAA","113":"b:guXCMAUYicAEIAAC/9AAc9y9fQoAAAAA","135":"b:guXCMAUYicAEIAAC/9AAc9y9fQoBAAAA","145":"b:guXCMAUYicAEIAAC/9AAc9y9fQoBAAAA","156":[15,28,96,77,48,124,126,130,14],"164":"b:gmWCMAEACcAAIAAAvwAAchyUPQoAAAAA","169":"b:guXCMAUYicAEIAAC/9AAc9y9fSoBAAAA","173":"b:guXCMAUYicAEIAAC/9AAc9y9fSoBAAAA","181":"b:guXCMAUYicAEIAAC/9AAc9y9fSoBAAAA","187":"b:guXCMAUYicAEIAAC/9AAc9y9fSoBAAAA","188":"b:guXCMAUYicAEIAAC/9AAc9y9fSoBAAAA","237":[77,98,48],"239":"b:guXCMAUYicAEIAAC/9AAc9y9fSoBAAAA","251":[28,96,77,48,126,130]},"packages/frontend/editor-ui/src/features/shared/nodeCreator/components/Panel/SearchBar.vue":{"16":"b:guXCMAUYicAEIAAC/9AAc9y9fSoBAAAA","26":"b:guXCMAUYicAEIAAC/9AAc9y9fSoBAAAA","30":"b:guXCMAUYicAEIAAC/9AAc9y9fQoBAAAA","35":[48],"39":"b:guXCMAUYicAEIAAC/9AAc9y9fSoBAAAA","44":"b:guXCMAUYicAEIAAC/9AAc9y9fSoBAAAA","52":"b:guXCMAUYicAEIAAC/9AAc9y9fSoBAAAA"},"packages/frontend/editor-ui/src/features/shared/nodeCreator/components/Renderers/CategorizedItemsRenderer.vue":{"28":"b:guWCMAEYCcAAIAAC/xAAc9y8fQoAAAAA","38":"b:guWCMAEYCcAAIAAC/xAAc9y8fQoAAAAA","39":"b:guWCMAEYCcAAIAAC/xAAc9y8fQoAAAAA","41":"b:guWCMAEYCcAAIAAC/xAAc9y8fQoAAAAA","45":[99,48],"49":[99,48],"62":"b:guWCMAEYCcAAIAAC/xAAc9y8fQoAAAAA","68":[48],"74":[48],"83":"b:guWCMAEYCcAAIAAC/xAAc9y8fQoAAAAA","84":[48],"92":[48],"97":[28,48,124],"103":[48],"106":"b:guWCMAEYCcAAIAAC/xAAc9y8fQoAAAAA","122":"b:guWCMAEYCcAAIAAC/xAAc9y8fQoAAAAA","126":[48],"132":[48],"153":"b:guWCMAEYCcAAIAAC/xAAc9y8fQoAAAAA","154":[96,48,143,14,147,148]},"packages/frontend/editor-ui/src/features/shared/nodeCreator/components/Renderers/ItemsRenderer.vue":{"30":"b:guXCMAUYicAEIAAC/9AAc9y9fSoBAAAA","45":"b:guXCMAUYicAEIAAC/9AAc9y9fSoBAAAA","47":"b:guXCMAUYicAEIAAC/9AAc9y9fSoBAAAA","49":"b:guXCMAUYicAEIAAC/9AAc9y9fSoBAAAA","53":"b:guXCMAUYicAEIAAC/9AAc9y9fSoBAAAA","63":"b:guXCMAUYicAEIAAC/9AAc9y9fSoBAAAA","80":"b:guXCMAUYicAEIAAC/9AAc9y9fSoBAAAA","106":[48],"110":[48],"114":[48],"118":[48],"122":"b:guXCMAUYicAEIAAC/9AAc9y9fSoBAAAA","126":"b:guXCMAUYicAEIAAC/9AAc9y9fSoBAAAA","133":"b:guXCMAUYicAEIAAC/9AAc9y9fSoBAAAA","134":"b:guXCMAUYicAEIAAC/9AAc9y9fQoAAAAA","141":"b:guXCMAUYicAEIAAC/9AAc9y9fSoBAAAA","154":"b:guXCMAUYicAEIAAC/9AAc9y9fSoBAAAA","163":[1,97,48,13],"166":[48],"185":"b:guXCMAUYicAEIAAC/9AAc9y9fSoBAAAA"},"packages/frontend/editor-ui/src/features/shared/nodeCreator/composables/useActions.ts":{"69":"b:gEGCMAEACMAAAAAArQAAchyUPQoAAAAA","76":"b:gEGCMAEACMAAAAAArQAAchyUPQoAAAAA","80":"b:gEGCMAEACMAAAAAArQAAchyUPQoAAAAA","81":"b:gEGCMAEACMAAAAAArQAAchyUPQoAAAAA","101":"b:gEGCMAEACMAAAAAArQAAchyUPQoAAAAA","102":"b:gEGCMAEACMAAAAAArQAAchyUPQoAAAAA","107":"b:gEGCMAEACMAAAAAArQAAchyUPQoAAAAA","158":"b:gEGCMAEACMAAAAAArQAAchyUPQoAAAAA","174":"b:gEGCMAEACMAAAAAAqQAAchyUPAoAAAAA","177":"b:AEECMAEACMAAAAAACAAAchyULAgAAAAA","192":"b:gEGCMAEACMAAAAAAqQAAchyUPAoAAAAA","221":"b:guXCMAUYiMAEIAAC/9AAc9y9fSoBAAAA","247":"b:guXCMAUIiMAAIAAC99AAc969fCoBAAAA","255":"b:guXCMAUYiMAEIAAC/9AAc969fCoBAAAA","257":"b:guXCMAUYiMAEIAAC/9AAc969fCoBAAAA","260":"b:guXCMAUYiMAEIAAC/9AAc969fCoBAAAA","273":"b:guXCMAUYiMAEIAAC/9AAc969fCoBAAAA","283":"b:guXCMAUYiMAEIAAC/9AAc969fCoBAAAA","287":[1],"291":"b:guXCMAUYiMAEIAAC/9AAc969fCoBAAAA","295":"b:guXCMAUYiMAEIAAC/9AAc969fCoBAAAA","310":"b:guXCMAUYiMAEIAAC/9AAc969fCoBAAAA","319":"b:guXCMAUYiMAEIAAC/9AAc969fCoBAAAA","327":"b:guXCMAUYiMAEIAAC/9AAc969fCoBAAAA","369":"b:guXCMAUYiMAEIAAC/9AAc969fCoBAAAA","405":"b:gEGCMAEACMAAAAAAqwAAchyUPAoAAAAA","410":"b:gEGCMAEACMAAAAAAqwAAchyUPAoAAAAA","420":"b:gEGCMAEACMAAAAAAqQAAchyUPAoAAAAA"},"packages/frontend/editor-ui/src/features/shared/nodeCreator/views/viewsData.ts":{"122":[14],"139":[14],"141":[14],"145":[14],"148":[14],"174":[14],"187":[14],"237":[1,7,10,13,14],"240":[1,7,10,13,14],"250":[1,7,10,13,14],"376":"b:guXCMAUYiMAEIAAC/9AAc9y8fCoBAAAA","502":"b:gsHCMAUIiMAAIAAAttAAcly9fQoAAAAA","632":"b:gsHCMAUIiMAAIAAAttAAcly9fQoAAAAA"},"packages/frontend/editor-ui/src/features/workflows/canvas/components/elements/edges/CanvasConnectionLine.vue":{"11":"b:ggQAEAAAAAAAIAAABkAAMAEBEAEAAAAC","17":"b:ggQAEAAAAAAAIAAABkAAMAEBEAEAAAAC","21":"b:ggQAEAAAAAAAIAAABkAAMAEBEAEAAAAC","28":"b:ggQAEAAAAAAAIAAABkAAMAEBEAEAAAAC","34":"b:ggQAEAAAAAAAIAAABkAAMAEBEAEAAAAC","38":"b:ggQAEAAAAAAAIAAABkAAMAEBEAEAAAAC","46":"b:ggQAEAAAAAAAIAAABkAAMAEBEAEAAAAC","47":"b:ggQAEAAAAAAAAAAABkAAMAABEAEAAAAC","51":"b:ggQAEAAAAAAAIAAABkAAMAEBEAEAAAAC"},"packages/core/src/execution-engine/node-execution-context/utils/extract-value.ts":{"20":[44,47,135,6,143,144,14,147],"39":[44,47,135,6,143,144,14,147],"59":[44,47,135,6,143,144,14,147],"68":[44,47,135,6,143,144,14,147],"101":[44,47,135,6,143,144,14,147],"121":[44,47,135,6,143,144,14,147],"155":[44,47,135,6,143,144,14,147]},"packages/core/src/nodes-loader/output-schema-resolver.ts":{"53":[44,47,53,54,55,111,136,140,145,149],"61":[44,47,53,54,55,111,136,140,145,149],"65":[44,47,53,54,55,111,136,140,145,149],"81":[44,47,53,54,55,111,136,140,145,149],"82":[44,47,53,54,55,111,136,140,145,149],"83":[44,47,53,54,55,111,136,140,145,149],"85":[44,47,53,54,55,111,136,140,145,149],"88":[44,47,53,54,55,111,136,140,145,149],"90":[44,47,53,54,55,111,136,140,145,149],"91":[44,47,53,54,55,111,136,140,145,149],"92":[44,47,53,54,55,111,136,140,145,149],"93":[44,47,53,54,55,111,136,140,145,149],"94":[44,47,53,54,55,111,136,140,145,149],"109":[44,47,53,54,55,111,136,140,145,149],"114":[44,47,53,54,55,111,136,140,145,149],"116":[44,47,53,54,55,111,136,140,145,149],"127":[44,47,53,54,55,111,136,140,145,149],"160":[44,47,53,54,55,111,136,140,145,149]},"packages/nodes-base/nodes/If/If.node.ts":{"8":[44,133,135,6,142,143,159,161]},"packages/nodes-base/nodes/If/V1/IfV1.node.ts":{"16":"b:QAAAAAAQAAAAAAAAAAAAAKDAA4ACAAAA","310":"b:QAAAAAAQAAAAAAAAAAAAAKDAA4ACAAAA","319":"b:QAAAAAAQAAAAAAAAAAAAAKDAA4ACAAAA","321":"b:QAAAAAAQAAAAAAAAAAAAAKDAA4ACAAAA","327":"b:QAAAAAAQAAAAAAAAAAAAAKDAA4ACAAAA","329":"b:QAAAAAAQAAAAAAAAAAAAAKDAA4ACAAAA","331":"b:QAAAAAAQAAAAAAAAAAAAAKDAA4ACAAAA","333":"b:QAAAAAAQAAAAAAAAAAAAAKDAA4ACAAAA","335":"b:QAAAAAAQAAAAAAAAAAAAAKDAA4ACAAAA","337":"b:QAAAAAAQAAAAAAAAAAAAAKDAA4ACAAAA","339":"b:QAAAAAAQAAAAAAAAAAAAAKDAA4ACAAAA","340":"b:QAAAAAAQAAAAAAAAAAAAAKDAA4ACAAAA","341":"b:QAAAAAAQAAAAAAAAAAAAAKDAA4ACAAAA","343":"b:QAAAAAAQAAAAAAAAAAAAAKDAA4ACAAAA","345":"b:QAAAAAAQAAAAAAAAAAAAAKDAA4ACAAAA","347":"b:QAAAAAAQAAAAAAAAAAAAAKDAA4ACAAAA","349":"b:QAAAAAAQAAAAAAAAAAAAAKDAA4ACAAAA","351":"b:QAAAAAAQAAAAAAAAAAAAAKDAA4ACAAAA","353":"b:QAAAAAAQAAAAAAAAAAAAAKDAA4ACAAAA","359":"b:QAAAAAAQAAAAAAAAAAAAAKDAA4ACAAAA","367":"b:QAAAAAAQAAAAAAAAAAAAAKDAA4ACAAAA","381":"b:QAAAAAAQAAAAAAAAAAAAAKDAA4ACAAAA","398":"b:QAAAAAAQAAAAAAAAAAAAAKDAA4ACAAAA"},"packages/nodes-base/nodes/If/V2/IfV2.node.ts":{"20":[44,133,135,6,142,143,159,161],"95":[44,133,135,6,142,143,159,161],"99":[44,133,135,6,142,143,159,161]},"packages/nodes-base/nodes/If/V2/utils.ts":{"3":"b:QAAAAAAQIAAAAAAAAAAAIKDIAIACAAAA","7":"b:QAAAAAAQIAAAAAAAAAAAIKDIAIACAAAA","8":"b:QAAAAAAQIAAAAAAAAAAAIKDIAIACAAAA"},"packages/nodes-base/nodes/NoOp/NoOp.node.ts":{"26":"b:ABAAAAAxqAIAAAAAAgAAAIAcBALgAEAB"},"packages/workflow/src/node-parameters/filter-parameter.ts":{"24":[44,135,6,143,144],"32":[44,135,6,143,144],"66":[44,135,6,143,144],"71":[44,135,6,143,144],"106":[44,135,6,143,144],"118":[44,135,6,143,144],"124":[44,135,6,143,144],"196":[44,135,6,143,144],"209":[44,135,6,143,144],"211":[44,135,6,143,144],"222":[44,135,6,143,144],"419":[44,135,6,143,144],"423":[44,135,6,143,144],"437":[44,135,6,143,144],"442":[44,135,6,143,144]},"packages/frontend/@n8n/design-system/src/components/N8nIcon/nodes/information-extractor.svg":{"11":[29,8,44,51,62,63,14,155]},"packages/frontend/@n8n/design-system/src/components/N8nIcon/nodes/no-operation.svg":{"11":"b:AFEAIAARqMAAAAAAgoAAAIQcBooCAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nIcon/nodes/read-write-files-from-disk.svg":{"11":[44,46]},"packages/frontend/@n8n/design-system/src/components/N8nIcon/nodes/switch.svg":{"11":[44,107,125,130,139,144,145,159]},"packages/frontend/editor-ui/src/app/utils/parameterUtils.ts":{"13":"b:kmHFsA0YCMQQAAAisBSBcFy+HAMAAAAA"},"packages/frontend/editor-ui/src/features/ndv/parameters/components/FilterConditions/CombinatorSelect.vue":{"14":[44],"22":[44],"31":[44],"32":[44]},"packages/frontend/editor-ui/src/features/ndv/parameters/components/FilterConditions/Condition.vue":{"43":[44,125],"63":[44,125],"67":[44,125],"79":[44,125],"97":[44,125],"110":[44,125],"120":[44,125],"131":[44,125],"143":[44],"145":[44],"166":[44],"170":[44],"215":[44,125],"221":[44,125],"250":[44,125],"269":[44,125],"278":[44,125],"306":[44,125],"316":[44]},"packages/frontend/editor-ui/src/features/ndv/parameters/components/FilterConditions/FilterConditions.vue":{"39":[44,125],"62":[44,125],"71":[44],"75":[44,125],"78":[44,125],"90":[44],"94":[44,125],"96":[44],"100":[44,125],"105":[44,125],"134":[44,125],"135":[44,125],"144":[44,125],"145":[44,125],"152":[44,125],"160":[44],"164":[44],"172":[44],"176":[44,125],"178":[44,125],"210":[44,125],"232":[44],"233":[44]},"packages/frontend/editor-ui/src/features/ndv/parameters/components/FilterConditions/OperatorSelect.vue":{"19":[44,125],"30":[44,125],"31":[44,125],"34":[44,125],"36":[44,125],"38":[44,125],"41":[44,125],"42":[44,125],"55":[44,125],"73":[44,125],"86":[44,125],"88":[44,125]},"packages/frontend/editor-ui/src/features/ndv/parameters/components/FilterConditions/utils.ts":{"19":[44,125],"81":[44,125],"124":[44,125],"145":[44,125]},"packages/frontend/editor-ui/src/features/ndv/runData/schemaPreview.api.ts":{"13":"b:AAAAAACQ4AAAAAAAAIAAABATp0AAAAAA","17":[111,137,149],"27":"b:AAAAAACQ4AAAAAAAAIAAABATp0AAAAAA"},"packages/frontend/editor-ui/src/features/shared/editors/components/InlineExpressionEditor/InlineExpressionTip.vue":{"21":"b:AEDEgAQYAAQAAAACoAAAAFA8HgAAAAAA","33":"b:AEAAAAAAAAQAAAAAAAAAABA8HgAAAAAA","37":[138,139,140,141,14,145,146],"39":"b:AEDEgAQYAAQAAAACoAAAAFA8HgAAAAAA","57":"b:AEAAAAAAAAQAAAAAAAAAAAA8HgAAAAAA","83":"b:AEDEgAQYAAQAAAACoAAAAFA8GAAAAAAA","88":"b:AEDEgAQYAAQAAAACoAAAAFA8HgAAAAAA","96":"b:AEDEgAQYAAQAAAACoAAAAFA8HgAAAAAA","97":"b:AEAAAAAAAAQAAAAAAAAAAAA8HgAAAAAA","106":"b:AEDEgAQYAAQAAAACoAAAAFA8HgAAAAAA","110":"b:AEDEgAQYAAQAAAACoAAAAFA8HgAAAAAA","114":[139,141,14,146],"120":[139,141,14,146]},"packages/nodes-base/nodes/Schedule/GenericFunctions.ts":{"7":"b:QIAKAAAAQAIIAAAAQQAAgoAaBAiQAEAA","40":"b:QIAKAAAAQAIIAAAAQQAAgoAaBAiQAEAA","109":"b:QIAKAAAAQAIIAAAAQQAAgoAaBAiQAEAA","114":"b:QIAKAAAAQAIIAAAAQQAAgoAaBAiQAEAA","156":"b:QIAKAAAAQAIIAAAAQQAAgoAaBAiQAEAA","175":"b:QIAKAAAAQAIIAAAAQQAAgoAaBAiQAEAA","236":"b:QIAKAAAAQAIIAAAAQQAAgoAaBAiQAEAA","262":"b:QIAKAAAAQAIIAAAAQQAAgoAaBAiQAEAA","269":"b:QIAKAAAAQAIIAAAAQQAAgoAaBAiQAEAA"},"packages/nodes-base/nodes/Schedule/ScheduleTrigger.node.ts":{"445":"b:QIAKAAAAQAIIAAAAQQAAgIAaBAiQAEAA","469":"b:QIAKAAAAQAIIAAAAQQAAgIAaBAiQAEAA","510":"b:QIAKAAAAQAIIAAAAQQAAgIAaBAiQAEAA","527":"b:QIAKAAAAQAIIAAAAQQAAgIAaBAiQAEAA","542":"b:QIAKAAAAQAIIAAAAQQAAgIAaBAiQAEAA"},"packages/frontend/editor-ui/src/features/ndv/parameters/components/FixedCollection/FixedCollectionParameterLegacy.vue":{"66":"b:guHGoAAASMBAAAACYwAAIgT+ZwgAAAAA","71":"b:AsDGgAAAQABAAAAC4QAAIgS+ZwgAAAAA","73":"b:AsDGgAAAQABAAAAC4QAAIgS+ZwgAAAAA","76":"b:AsDGgAAAQABAAAAC4QAAIgS+ZwgAAAAA","80":"b:guHGoAAASMBAAAAC4wAAIgT+ZwgAAAAA","81":"b:gGHAIAAACMAAAAAAIgAAAADKAwgAAAAA","82":"b:AsDGgAAAQABAAAACYQAAIgS+ZwgAAAAA","83":"b:AsDGgAAAQABAAAAC4QAAIgS+ZwgAAAAA","85":"b:guHGoAAASMBAAAACYwAAIgT+ZwgAAAAA","92":"b:guHGoAAASMBAAAAC4wAAIgT+ZwgAAAAA","94":"b:guHGoAAASMBAAAAC4wAAIgT+ZwgAAAAA","105":"b:guHGoAAASMBAAAAC4wAAIgT+ZwgAAAAA","108":[97,101,103],"111":"b:guHGoAAASMBAAAAC4wAAIgT+ZwgAAAAA","115":[23,101],"122":[23,101],"126":[23,101],"140":"b:guHGoAAASMBAAAAC4wAAIgT+ZwgAAAAA","155":[23,101],"160":[23,101],"191":[23,101],"196":"b:AsDGgAAAQABAAAAC4QAAIgS+ZwgAAAAA","207":[23,101],"232":"b:AsDGgAAAQABAAAAC4QAAIgS+ZwgAAAAA","242":[23,101],"263":[101],"276":[101],"299":"b:guHGoAAASMBAAAAC4wAAIgT+ZwgAAAAA","300":"b:AGHEoAAACAAAAAAAoAAAIAAMAAAAAAAA","307":"b:guHGoAAASMBAAAAC4wAAIgT+ZwgAAAAA","339":[22,23,101,125,139,14],"362":[22,23,101,125,139,14],"368":[22,23,101,125,139,14],"382":[22,23,101,125,139,14],"389":[22,23,101,103,125,138,139],"421":"b:AsDGgAAAQABAAAACYQAAIgS+ZwgAAAAA","423":"b:guHGoAAASMBAAAAC4wAAIgT+ZwgAAAAA","439":"b:AsDGgAAAQABAAAAC4QAAIgS+ZwgAAAAA","461":"b:AsDGgAAAQABAAAACYQAAIgS+ZwgAAAAA","531":[101],"532":[23,101],"636":[22,23,101,125,139,14],"644":[137,139,143,144,145]},"packages/@n8n/api-types/src/dto/api-keys/create-api-key-request.dto.ts":{"5":[173,90,171,105,52,59,62,119,63,120]},"packages/@n8n/api-types/src/dto/api-keys/update-api-key-request.dto.ts":{"7":[173,90,171,105,52,59,62,119,63,120]},"packages/@n8n/api-types/src/dto/invitation/invite-users-request.dto.ts":{"12":[173,90,171,105,52,62,119,63,120]},"packages/@n8n/api-types/src/schemas/scopes.schema.ts":{"14":[173,90,171,105,52,59,62,119,63,120]},"packages/@n8n/backend-common/src/logging/logger.ts":{"20":"b:eoEBEQQBEAQSHDMAAABAAECCABAEAIEA","30":"b:eoEBEQQBEAQSHDMAAABAAECCABAEAIEA","41":"b:eoEBEQQBEAQSHDMAAABAAECCABAEAIEA","71":"b:eoEBEQQBEAQSHDMAAABAAECCABAEAIEA","76":"b:eoEBEQQBEAQSHDMAAABAAECCABAEAIEA","88":"b:eoEBEQQBEAQSHDMAAABAAECCABAEAIEA","109":"b:eoEBEQQBEAQSHDMAAABAAECCABAEAIEA","130":"b:eoEBEQQBEAQSHDMAAABAAECCABAEAIEA","142":"b:eoEBEQQBEAQSHDMAAABAAECCABAEAIEA","151":"b:eoEBEQQBEAQSHDMAAABAAECCABAEAIEA","159":"b:eoEBEQQBEAQSHDMAAABAAECCABAEAIEA","163":"b:eoEBEQQBEAQSHDMAAABAAECCABAEAIEA","169":"b:eoEBEQQBEAQSHDMAAABAAECCABAEAIEA","170":"b:eoEBEQQBEAQSHDMAAABAAECCABAEAIEA","176":"b:eoEBEQQBEAQSHDMAAABAAECCABAEAIEA","182":"b:eoEBEQQBEAQSHDMAAABAAECCABAEAIEA","192":"b:eoEBEQQBEAQSHDMAAABAAECCABAEAIEA","195":"b:eoEBEQQBEAQSHDMAAABAAECCABAEAIEA","198":"b:eoEBEQQBEAQSHDMAAABAAECCABAEAIEA","208":"b:eoEBEQQBEAQSHDMAAABAAECCABAEAIEA","214":"b:eoEBEQQBEAQSHDMAAABAAECCABAEAIEA","221":"b:eoEBEQQBEAQSHDMAAABAAECCABAEAIEA","223":"b:eoEBEQQBEAQSHDMAAABAAECCABAEAIEA","231":"b:eoEBEQQBEAQSHDMAAABAAECCABAEAIEA","245":"b:eoEBEQQBEAQSHDMAAABAAECCABAEAIEA","264":"b:eoEBEQQBEAQSHDMAAABAAECCABAEAIEA","268":"b:eoEBEQQBEAQSHDMAAABAAECCABAEAIEA","272":"b:eoEBEQQBEAQSHDMAAABAAECCABAEAIEA","276":"b:eoEBEQQBEAQSHDMAAABAAECCABAEAIEA","284":"b:eoEBEQQBEAQSHDMAAABAAECCABAEAIEA"},"packages/@n8n/db/src/entities/webhook-entity.ts":{"33":"b:AAjAQwKAEBACAAAAAAAAAAAAAAAVA6AA","39":"b:AAjAQwKAEBACAAAAAAAAAAAAAAAVA6AA","43":"b:AAjAQwKAEBACAAAAAAAAAAAAAAAVA6AA","44":"b:AAjAQwKAEBACAAAAAAAAAAAAAAAVA6AA","50":"b:AAjAQwKAEBACAAAAAAAAAAAAAAAVA6AA","51":"b:AAjAQwKAEBACAAAAAAAAAAAAAAAVA6AA","54":"b:AAjAQwKAEBACAAAAAAAAAAAAAAAVA6AA"},"packages/@n8n/db/src/repositories/shared-credentials.repository.ts":{"12":"b:ekkKKA6kHMxUAIAAoBSgAUAAAAAIAAAA","16":"b:ekkKKA6kHMxUAIAAoBSgAUAAAAAIAAAA","26":"b:ekkKKA6kHMxUAIAAoBSgAUAAAAAIAAAA","36":"b:ekkKKA6kHMxUAIAAoBSgAUAAAAAIAAAA","41":"b:ekkKKA6kHMxUAIAAoBSgAUAAAAAIAAAA","52":"b:ekkKKA6kHMxUAIAAoBSgAUAAAAAIAAAA","61":"b:ekkKKA6kHMxUAIAAoBSgAUAAAAAIAAAA","73":"b:ekkKKA6kHMxUAIAAoBSgAUAAAAAIAAAA","76":"b:ekkKKA6kHMxUAIAAoBSgAUAAAAAIAAAA","85":"b:ekkKKA6kHMxUAIAAoBSgAUAAAAAIAAAA","94":"b:ekkKKA6kHMxUAIAAoBSgAUAAAAAIAAAA","99":"b:ekkKKA6kHMxUAIAAoBSgAUAAAAAIAAAA","112":"b:ekkKKA6kHMxUAIAAoBSgAUAAAAAIAAAA","128":"b:ekkKKA6kHMxUAIAAoBSgAUAAAAAIAAAA","156":"b:ekkKKA6kHMxUAIAAoBSgAUAAAAAIAAAA","165":"b:ekkKKA6kHMxUAIAAoBSgAUAAAAAIAAAA","166":"b:ekkKKA6kHMxUAIAAoBSgAUAAAAAIAAAA","181":"b:ekkKKA6kHMxUAIAAoBSgAUAAAAAIAAAA","182":"b:ekkKKA6kHMxUAIAAoBSgAUAAAAAIAAAA","192":"b:ekkKKA6kHMxUAIAAoBSgAUAAAAAIAAAA"},"packages/@n8n/db/src/repositories/workflow-publish-history.repository.ts":{"9":"b:AAjAQwKgEBICAAAAAAAAAgAAAAL0AuAB","13":"b:AAjAQwKgEBICAAAAAAAAAgAAAAL0AuAB","31":"b:AAjAQwKgEBICAAAAAAAAAgAAAAL0AuAB","42":"b:AAjAQwKgEBICAAAAAAAAAgAAAAL0AuAB"},"packages/@n8n/db/src/subscribers/user-subscriber.ts":{"12":"b:Qk2AAwCgEJICAAIEAAAQAQAAAAHkKUAA","15":"b:Qk2AAwCgEJICAAIEAAAQAQAAAAHkKUAA","19":"b:Qk2AAwCgEJICAAIEAAAQAQAAAAHkKUAA","24":"b:Qk2AAwCgEJICAAIEAAAQAQAAAAHkKUAA"},"packages/@n8n/db/src/utils/is-string-array.ts":{"1":"b:AAAATKIDHsBsgINBAAEAAyAAAAEBBAAA","2":"b:AAAATKIDHsBsgINBAAEAAyAAAAEBBAAA"},"packages/@n8n/db/src/utils/is-unique-constraint-error.ts":{"6":"b:AgEAEAIBEAASDDJQAABAAACCCAAGABEA"},"packages/@n8n/errors/src/index.ts":{"2":"b:UoABEQQBEAAQEAAwAABAAECCABAAAIAA"},"packages/@n8n/permissions/src/public-api-permissions.ee.ts":{"208":"b:AAAAAAAAEMgAAAAEAAKgAQAAAAAAKAEA","215":"b:AAAAAAAAEMgAAAAEAAKgAQAAAAAAKAEA","222":"b:AAAAAAAAEMgAAAAEAAKgAQAAAAAAKAEA","225":"b:AAAAAAAAEMgAAAAEAAKgAQAAAAAAKAEA"},"packages/@n8n/permissions/src/types.ee.ts":{"75":"b:AAAAAAAAEMgAAAAEAAKAAQAAAAAAKAEA","129":"b:AAAAAAAAEMgAAAAEAAKAAQAAAAAAKAEA"},"packages/@n8n/utils/src/errors/ensure-error.ts":{"2":"b:AgkAEQIAEAASDDJQAABAAACCCAAUEAEA"},"packages/cli/src/activation-errors.service.ts":{"9":"b:AAjAQwKgEBICAAAAAAAAAgAAAAL0AuAB","11":"b:AAjAQwKgEBICAAAAAAAAAgAAAAL0AuAB","15":"b:AAjAQwKgEBICAAAAAAAAAgAAAAL0AuAB","19":"b:AAjAQwKgEBICAAAAAAAAAgAAAAL0AuAB","27":"b:AAjAQwKgEBICAAAAAAAAAgAAAAL0AuAB","35":"b:AAjAQwKgEBICAAAAAAAAAgAAAAL0AuAB"},"packages/cli/src/active-workflow-manager.ts":{"73":"b:AAjAQQKgEBAKAAAAAAAAAgAAACpVAuAB","96":"b:AAjAQQKgEBAKAAAAAAAAAgAAACpVAuAB","107":"b:AAjAQQKgEBAKAAAAAAAAAgAAACpVAuAB","114":"b:AAjAQQKgEBAKAAAAAAAAAgAAACpVAuAB","136":"b:AAjAQQKgEBAKAAAAAAAAAgAAACpVAuAB","143":"b:AAjAQQKgEBAKAAAAAAAAAgAAACpVAuAB","154":"b:AAjAQQKgEBAKAAAAAAAAAgAAACpVAuAB","243":"b:AAjAQQKgEBAKAAAAAAAAAgAAACpVAuAB","288":"b:AAjAQQKgEBAKAAAAAAAAAgAAACpVAuAB","324":"b:AAjAQQKgEBAKAAAAAAAAAgAAACpVAuAB","344":"b:AAjAQQKgEBAKAAAAAAAAAgAAACpVAuAB","358":"b:AAjAQQKgEBAKAAAAAAAAAgAAACpVAuAB","386":"b:AAjAQQKgEBAKAAAAAAAAAgAAACpVAuAB","400":"b:AAjAQQKgEBAKAAAAAAAAAgAAACpVAuAB","419":"b:AAjAQQKgEBAKAAAAAAAAAgAAACpVAuAB","432":"b:AAjAQQKgEBAKAAAAAAAAAgAAACpVAuAB","491":"b:AAjAQQKgEBAKAAAAAAAAAgAAACpVAuAB","498":"b:AAjAQQKgEBAKAAAAAAAAAgAAACpVAuAB","507":"b:AAjAQQKgEBAKAAAAAAAAAgAAACpVAuAB","535":"b:AAjAQQKgEBAKAAAAAAAAAgAAACpVAuAB","649":"b:AAjAQQKgEBAKAAAAAAAAAgAAACpVAuAB","651":"b:AAjAQQKgEBAKAAAAAAAAAgAAACpVAuAB","688":"b:AAjAQQKgEBAKAAAAAAAAAgAAACpVAuAB","696":"b:AAjAQQKgEBAKAAAAAAAAAgAAACpVAuAB","701":"b:AAjAQQKgEBAKAAAAAAAAAgAAACpVAuAB","722":"b:AAjAQQKgEBAKAAAAAAAAAgAAACpVAuAB","811":"b:AAjAQQKgEBAKAAAAAAAAAgAAACpVAuAB","812":"b:AAjAQQKgEBAKAAAAAAAAAgAAACpVAuAB","815":"b:AAjAQQKgEBAKAAAAAAAAAgAAACpVAuAB","825":"b:AAjAQQKgEBAKAAAAAAAAAgAAACpVAuAB","842":"b:AAjAQQKgEBAKAAAAAAAAAgAAACpVAuAB","849":"b:AAjAQQKgEBAKAAAAAAAAAgAAACpVAuAB","904":"b:AAjAQQKgEBAKAAAAAAAAAgAAACpVAuAB","914":"b:AAjAQQKgEBAKAAAAAAAAAgAAACpVAuAB","927":"b:AAjAQQKgEBAKAAAAAAAAAgAAACpVAuAB","971":"b:AAjAQQKgEBAKAAAAAAAAAgAAACpVAuAB","990":"b:AAjAQQKgEBAKAAAAAAAAAgAAACpVAuAB","1010":"b:AAjAQQKgEBAKAAAAAAAAAgAAACpVAuAB","1043":"b:AAjAQQKgEBAKAAAAAAAAAgAAACpVAuAB","1080":"b:AAjAQQKgEBAKAAAAAAAAAgAAACpVAuAB","1083":"b:AAjAQQKgEBAKAAAAAAAAAgAAACpVAuAB","1126":"b:AAjAQQKgEBAKAAAAAAAAAgAAACpVAuAB","1133":"b:AAjAQQKgEBAKAAAAAAAAAgAAACpVAuAB","1149":"b:AAjAQQKgEBAKAAAAAAAAAgAAACpVAuAB"},"packages/cli/src/controllers/api-keys.controller.ts":{"26":[173,90,171,105,52,59,62,119,63,120],"36":[173,90,171,105,52,59,62,119,63,120],"43":[173,90,171,105,52,59,62,119,63,120],"67":[173,90,171,105,52,59,62,119,63,120],"81":[173,90,171,105,52,59,62,119,63,120],"96":[173,90,171,105,52,59,62,119,63,120],"114":[173,90,171,105,52,59,62,119,63,120],"129":[173,90,171,105,52,59,62,119,63,120]},"packages/cli/src/controllers/invitation.controller.ts":{"25":[173,90,171,105,52,62,119,63,120],"44":[173,90,171,105,52,62,119,63,120],"76":[173,90,171,105,52,62,119,63,120],"95":[173,90,171,105,52,62,119,63,120],"120":[173,90,171,105,52,62,119,63,120],"164":[173,90,171,105,52,62,119,63,120]},"packages/cli/src/controllers/security-settings.controller.ts":{"15":[52],"25":[52],"41":[52],"83":[52],"87":[52],"92":[52]},"packages/cli/src/errors/http-error-classifier.ts":{"6":"b:KgkAEQAAEAASDDJAAABAAACCCAAEAAEA","41":"b:KgkAEQAAEAASDDJAAABAAACCCAAEAAEA","58":"b:KgkAEQAAEAASDDJAAABAAACCCAAEAAEA"},"packages/cli/src/errors/http-error-serializers.ts":{"13":"b:KgkAEQAAEAASDDJAAABAAACCCAAEAAEA","51":"b:KgkAEQAAEAASDDJAAABAAACCCAAEAAEA"},"packages/cli/src/errors/response-errors/abstract/response.error.ts":{"19":"b:KAkAAAAAEAACDDIAAAAAAAAAAAgEEBEA"},"packages/cli/src/errors/response-errors/payment-required.error.ts":{"4":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA"},"packages/cli/src/errors/response-errors/scope-forbidden.error.ts":{"4":[52]},"packages/cli/src/errors/response-errors/unprocessable.error.ts":{"4":[52]},"packages/cli/src/execution-lifecycle/execute-error-workflow.ts":{"22":"b:OADAAwCgEBICAAAwAAAAAAAAgAD0A+AB","96":"b:OADAAwCgEBICAAAwAAAAAAAAgAD0A+AB","103":"b:OADAAwCgEBICAAAwAAAAAAAAgAD0A+AB","119":"b:OADAAwCgEBICAAAwAAAAAAAAgAD0A+AB","124":"b:OADAAwCgEBICAAAwAAAAAAAAgAD0A+AB"},"packages/cli/src/executions/parse-range-query.middleware.ts":{"15":"b:QgDgAwChEBIDAAAAAAAAAEAAADDgCuAA","20":"b:QgDgAwChEBIDAAAAAAAAAEAAADDgCuAA"},"packages/cli/src/middlewares/list-query/dtos/base.filter.dto.ts":{"7":"b:AAACbAwAHsRsgIMBAAAAAyAAAAEAAAAA","23":"b:AAACbAwAHsRsgIMBAAAAAyAAAAEAAAAA"},"packages/cli/src/middlewares/list-query/dtos/workflow.filter.dto.ts":{"74":"b:AAAATAAAHsBsgINBAAEAAyAAAAEAAAAA"},"packages/cli/src/middlewares/list-query/filter.ts":{"11":"b:AAACbAwAHsx8gIMRAAAIAyAAAAEABAAA"},"packages/cli/src/middlewares/list-query/pagination.ts":{"10":"b:AAACbAwAHsx8gIMRAAAIAyAAAAEABAAA"},"packages/cli/src/middlewares/list-query/select.ts":{"11":"b:AAACbAwAHsx8gIMRAAAIAyAAAAEABAAA"},"packages/cli/src/middlewares/list-query/sort-by.ts":{"12":"b:AAACbAwAHsx8gIMRAAAIAyAAAAEABAAA"},"packages/cli/src/modules/insights/database/entities/insights-raw.ts":{"13":[70,35,98,11,103,52,60,141,152,67],"27":[70,35,98,11,103,52,60,141,152,67],"38":[70,35,98,11,103,52,60,141,152,67]},"packages/cli/src/modules/provisioning.ee/provisioning.service.ee.ts":{"43":"b:AAEKbB4AHsQMAAAAAAAgAAAAAAAAAAAA","60":"b:AAEKbB4AHsQMAAAAAAAgAAAAAAAAAAAA","64":"b:AAEKbB4AHsQMAAAAAAAgAAAAAAAAAAAA","72":"b:AAEKbB4AHsQMAAAAAAAgAAAAAAAAAAAA","126":"b:AAEKbB4AHsQMAAAAAAAgAAAAAAAAAAAA","177":"b:AAEKbB4AHsQMAAAAAAAgAAAAAAAAAAAA","243":"b:AAEKbB4AHsQMAAAAAAAgAAAAAAAAAAAA","256":"b:AAEKbB4AHsQMAAAAAAAgAAAAAAAAAAAA","286":"b:AAEKbB4AHsQMAAAAAAAgAAAAAAAAAAAA","288":"b:AAEKbB4AHsQMAAAAAAAgAAAAAAAAAAAA","291":"b:AAEKbB4AHsQMAAAAAAAgAAAAAAAAAAAA","308":"b:AAEKbB4AHsQMAAAAAAAgAAAAAAAAAAAA","369":"b:AAEKbB4AHsQMAAAAAAAgAAAAAAAAAAAA","402":"b:AAEKbB4AHsQMAAAAAAAgAAAAAAAAAAAA","418":"b:AAEKbB4AHsQMAAAAAAAgAAAAAAAAAAAA","422":"b:AAEKbB4AHsQMAAAAAAAgAAAAAAAAAAAA","441":"b:AAEKbB4AHsQMAAAAAAAgAAAAAAAAAAAA","456":"b:AAEKbB4AHsQMAAAAAAAgAAAAAAAAAAAA","464":"b:AAEKbB4AHsQMAAAAAAAgAAAAAAAAAAAA","472":"b:AAEKbB4AHsQMAAAAAAAgAAAAAAAAAAAA","480":"b:AAEKbB4AHsQMAAAAAAAgAAAAAAAAAAAA","485":"b:AAEKbB4AHsQMAAAAAAAgAAAAAAAAAAAA","490":"b:AAEKbB4AHsQMAAAAAAAgAAAAAAAAAAAA","499":"b:AAEKbB4AHsQMAAAAAAAgAAAAAAAAAAAA","503":"b:AAEKbB4AHsQMAAAAAAAgAAAAAAAAAAAA","511":"b:AAEKbB4AHsQMAAAAAAAgAAAAAAAAAAAA","518":"b:AAEKbB4AHsQMAAAAAAAgAAAAAAAAAAAA","557":"b:AAEKbB4AHsQMAAAAAAAgAAAAAAAAAAAA","578":"b:AAEKbB4AHsQMAAAAAAAgAAAAAAAAAAAA","585":"b:AAEKbB4AHsQMAAAAAAAgAAAAAAAAAAAA","593":"b:AAEKbB4AHsQMAAAAAAAgAAAAAAAAAAAA","652":"b:AAEKbB4AHsQMAAAAAAAgAAAAAAAAAAAA","679":"b:AAEKbB4AHsQMAAAAAAAgAAAAAAAAAAAA","689":"b:AAEKbB4AHsQMAAAAAAAgAAAAAAAAAAAA","701":"b:AAEKbB4AHsQMAAAAAAAgAAAAAAAAAAAA","703":"b:AAEKbB4AHsQMAAAAAAAgAAAAAAAAAAAA","708":"b:AAEKbB4AHsQMAAAAAAAgAAAAAAAAAAAA","730":"b:AAEKbB4AHsQMAAAAAAAgAAAAAAAAAAAA","781":"b:AAEKbB4AHsQMAAAAAAAgAAAAAAAAAAAA","790":"b:AAEKbB4AHsQMAAAAAAAgAAAAAAAAAAAA","814":"b:AAEKbB4AHsQMAAAAAAAgAAAAAAAAAAAA","825":"b:AAEKbB4AHsQMAAAAAAAgAAAAAAAAAAAA","831":"b:AAEKbB4AHsQMAAAAAAAgAAAAAAAAAAAA"},"packages/cli/src/modules/redaction/executions/strategies/full-item-redaction.strategy.ts":{"17":[52],"20":[52],"25":[52],"80":[52],"93":[52],"111":[52]},"packages/cli/src/modules/redaction/redaction-enforcement.service.ts":{"20":"b:QkENEqEDmIBGAAAAIAQAAkASAAAAAAEA","24":"b:QkENEqEDmIBGAAAAIAQAAkASAAAAAAEA","50":"b:QkENEqEDmIBGAAAAIAQAAkASAAAAAAEA"},"packages/cli/src/modules/redaction/redaction-policy.ts":{"37":[52],"48":[52]},"packages/cli/src/public-api/index.ts":{"41":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","43":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","50":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","70":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","80":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","87":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","156":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","164":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","172":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","174":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","180":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","217":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","231":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","237":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","245":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","247":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","257":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","261":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","296":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","300":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","304":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","315":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","337":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","354":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","362":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","380":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","394":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","398":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","400":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","413":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA"},"packages/cli/src/public-api/v1/handlers/audit/audit.handler.ts":{"15":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA"},"packages/cli/src/public-api/v1/handlers/credentials/credentials.handler.ts":{"60":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","74":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","99":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","113":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","134":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","144":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","170":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","194":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","209":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","243":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","254":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA"},"packages/cli/src/public-api/v1/handlers/credentials/credentials.mapper.ts":{"8":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA"},"packages/cli/src/public-api/v1/handlers/credentials/credentials.middleware.ts":{"21":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","28":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","36":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","41":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","55":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","70":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","89":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA"},"packages/cli/src/public-api/v1/handlers/credentials/credentials.service.ts":{"30":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","34":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","55":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","60":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","61":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","70":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","77":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","81":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","98":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","153":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","194":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","259":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","272":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","287":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","293":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","308":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","323":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","324":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","327":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","341":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","352":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","478":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","486":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA"},"packages/cli/src/public-api/v1/handlers/data-tables/data-tables.columns.handler.ts":{"14":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","36":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","46":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","70":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","81":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA"},"packages/cli/src/public-api/v1/handlers/data-tables/data-tables.handler.ts":{"30":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","51":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","73":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","110":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","128":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","153":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","181":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","216":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA"},"packages/cli/src/public-api/v1/handlers/data-tables/data-tables.rows.handler.ts":{"25":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","40":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","64":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","107":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","136":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","167":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","198":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","217":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA"},"packages/cli/src/public-api/v1/handlers/data-tables/data-tables.service.ts":{"9":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","32":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","33":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","41":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA"},"packages/cli/src/public-api/v1/handlers/discover/discover.handler.ts":{"18":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","30":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA"},"packages/cli/src/public-api/v1/handlers/discover/discover.service.ts":{"63":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","75":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","82":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","166":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","174":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","215":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","220":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","224":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","232":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","256":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA"},"packages/cli/src/public-api/v1/handlers/evaluations/evaluations.handler.ts":{"35":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","46":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","67":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","86":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","110":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","136":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","151":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","164":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","178":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA"},"packages/cli/src/public-api/v1/handlers/evaluations/evaluations.mapper.ts":{"29":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","45":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA"},"packages/cli/src/public-api/v1/handlers/executions/executions.handler.ts":{"30":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","41":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","61":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","105":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","157":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","181":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","182":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","245":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","273":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","296":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","298":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","327":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","349":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","351":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA"},"packages/cli/src/public-api/v1/handlers/executions/executions.service.ts":{"9":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","10":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","18":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","27":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","32":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","48":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA"},"packages/cli/src/public-api/v1/handlers/folders/folders.handler.ts":{"26":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","49":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","77":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","96":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","116":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","134":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA"},"packages/cli/src/public-api/v1/handlers/insights/insights.handler.ts":{"15":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","29":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","49":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","52":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","57":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA"},"packages/cli/src/public-api/v1/handlers/ldap/ldap.handler.ts":{"35":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","46":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","67":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","82":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA"},"packages/cli/src/public-api/v1/handlers/ldap/ldap.mapper.ts":{"6":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","13":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","26":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA"},"packages/cli/src/public-api/v1/handlers/log-streaming/log-streaming.handler.ts":{"23":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","31":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","53":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","61":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","70":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","79":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","103":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","129":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","147":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA"},"packages/cli/src/public-api/v1/handlers/log-streaming/log-streaming.mapper.ts":{"24":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","31":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA"},"packages/cli/src/public-api/v1/handlers/otel/otel.handler.ts":{"26":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","35":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","50":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","70":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA"},"packages/cli/src/public-api/v1/handlers/otel/otel.mapper.ts":{"5":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA"},"packages/cli/src/public-api/v1/handlers/projects/projects.handler.ts":{"48":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","60":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","74":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","93":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","113":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","135":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","164":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","182":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","201":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","219":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA"},"packages/cli/src/public-api/v1/handlers/security-policy/security-policy.handler.ts":{"17":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","28":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","37":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA"},"packages/cli/src/public-api/v1/handlers/source-control/source-control.handler.ts":{"23":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA"},"packages/cli/src/public-api/v1/handlers/sso-saml/sso-saml.handler.ts":{"26":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","36":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA"},"packages/cli/src/public-api/v1/handlers/sso-saml/sso-saml.mapper.ts":{"20":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","61":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA"},"packages/cli/src/public-api/v1/handlers/tags/tags.handler.ts":{"21":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","36":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","58":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","74":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA"},"packages/cli/src/public-api/v1/handlers/users/users.handler.ee.ts":{"39":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","61":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","94":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","110":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","119":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA"},"packages/cli/src/public-api/v1/handlers/users/users.service.ee.ts":{"8":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","20":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","29":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","44":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","50":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","65":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","72":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","77":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA"},"packages/cli/src/public-api/v1/handlers/variables/variables.handler.ts":{"29":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","42":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","55":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","65":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA"},"packages/cli/src/public-api/v1/handlers/workflows/workflows.handler.ts":{"31":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","65":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","87":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","105":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","113":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","130":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","146":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","182":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","209":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","275":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","324":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","347":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","398":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","425":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","427":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","464":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","480":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA"},"packages/cli/src/public-api/v1/handlers/workflows/workflows.service.ts":{"18":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","22":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","41":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","57":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","63":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","97":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","101":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","102":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","105":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","116":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","118":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","125":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA"},"packages/cli/src/public-api/v1/shared/middlewares/global.middleware.ts":{"21":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","26":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","57":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","60":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","63":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","104":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","107":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","119":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","121":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","127":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","128":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","150":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","158":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","160":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","167":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","174":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","189":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","190":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA"},"packages/cli/src/public-api/v1/shared/public-api-scope-lookup.ts":{"9":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","19":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","27":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","33":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","43":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","63":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","75":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA"},"packages/cli/src/public-api/v1/shared/services/utils.service.ts":{"9":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA"},"packages/cli/src/requests.ts":{"54":"b:OAACbO4DHsRsgINBAAECAyAAQAECBAAA","63":"b:OAACbO4DHsRsgINBAAECAyAAQAECBAAA","66":"b:OAACbO4DHsRsgINBAAECAyAAQAECBAAA"},"packages/cli/src/response-helper.ts":{"16":"b:KgkAEQIAEAASDDJQAABAAACCCAAGABEA","49":"b:KgkAEQIAEAASDDJQAABAAACCCAAGABEA","99":"b:KgkAEQIAEAASDDJQAABAAACCCAAGABEA","113":"b:KgkAEQIAEAASDDJQAABAAACCCAAGABEA","117":"b:KgkAEQIAEAASDDJQAABAAACCCAAGABEA"},"packages/cli/src/scheduling/schedule-trigger-node/schedule-trigger-job-registrar.ts":{"106":"b:AAjAQQKgEBIKAAAAAAAAAgAAACr1AuAB","130":"b:AAjAQQKgEBIKAAAAAAAAAgAAACr1AuAB","149":"b:AAjAQQKgEBIKAAAAAAAAAgAAACr1AuAB","158":"b:AAjAQQKgEBIKAAAAAAAAAgAAACr1AuAB","164":"b:AAjAQQKgEBIKAAAAAAAAAgAAACr1AuAB","196":"b:AAjAQQKgEBIKAAAAAAAAAgAAACr1AuAB","205":"b:AAjAQQKgEBIKAAAAAAAAAgAAACr1AuAB","229":"b:AAjAQQKgEBIKAAAAAAAAAgAAACr1AuAB","271":"b:AAjAQQKgEBIKAAAAAAAAAgAAACr1AuAB","277":"b:AAjAQQKgEBIKAAAAAAAAAgAAACr1AuAB","317":"b:AAjAQQKgEBIKAAAAAAAAAgAAACr1AuAB","332":"b:AAjAQQKgEBIKAAAAAAAAAgAAACr1AuAB","348":"b:AAjAQQKgEBIKAAAAAAAAAgAAACr1AuAB","357":"b:AAjAQQKgEBIKAAAAAAAAAgAAACr1AuAB","359":"b:AAjAQQKgEBIKAAAAAAAAAgAAACr1AuAB","376":"b:AAjAQQKgEBIKAAAAAAAAAgAAACr1AuAB","380":"b:AAjAQQKgEBIKAAAAAAAAAgAAACr1AuAB"},"packages/cli/src/services/api-key-auth.strategy.ts":{"17":[173,90,171,105,52,62,119,63,120],"23":[173,90,171,105,52,62,119,63,120],"66":[173,90,171,105,52,62,119,63,120],"72":[173,90,171,105,52,62,119,63,120],"79":[173,90,171,105,52,62,119,63,120],"84":[173,90,171,105,52,62,119,63,120]},"packages/cli/src/services/auth-strategy.registry.ts":{"24":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAQKAEA","27":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAQKAEA","31":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAQKAEA","49":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAQKAEA"},"packages/cli/src/services/public-api-key.service.ts":{"37":"b:AAAAAAAAEMgAAAAEAAKgAQAAAAAAKAAA","41":"b:AAAAAAAAEMgAAAAEAAKgAQAAAAAAKAAA","48":"b:AAAAAAAAEMgAAAAEAAKgAQAAAAAAKAAA","66":"b:AAAAAAAAEMgAAAAEAAKgAQAAAAAAKAAA","82":"b:AAAAAAAAEMgAAAAEAAKgAQAAAAAAKAAA","130":"b:AAAAAAAAEMgAAAAEAAKgAQAAAAAAKAAA","141":"b:AAAAAAAAEMgAAAAEAAKgAQAAAAAAKAAA","166":"b:AAAAAAAAEMgAAAAEAAKgAQAAAAAAKAAA","177":"b:AAAAAAAAEMgAAAAEAAKgAQAAAAAAKAAA","205":"b:AAAAAAAAEMgAAAAEAAKgAQAAAAAAKAAA","233":"b:AAAAAAAAEMgAAAAEAAKgAQAAAAAAKAAA","251":"b:AAAAAAAAEMgAAAAEAAKgAQAAAAAAKAAA","263":"b:AAAAAAAAEMgAAAAEAAKgAQAAAAAAKAAA","264":"b:AAAAAAAAEMgAAAAEAAKgAQAAAAAAKAAA","270":"b:AAAAAAAAEMgAAAAEAAKgAQAAAAAAKAAA","275":"b:AAAAAAAAEMgAAAAEAAKgAQAAAAAAKAAA","286":"b:AAAAAAAAEMgAAAAEAAKgAQAAAAAAKAAA","308":"b:AAAAAAAAEMgAAAAEAAKgAQAAAAAAKAAA","332":"b:AAAAAAAAEMgAAAAEAAKgAQAAAAAAKAAA","341":"b:AAAAAAAAEMgAAAAEAAKgAQAAAAAAKAAA","350":"b:AAAAAAAAEMgAAAAEAAKgAQAAAAAAKAAA","355":"b:AAAAAAAAEMgAAAAEAAKgAQAAAAAAKAAA","357":"b:AAAAAAAAEMgAAAAEAAKgAQAAAAAAKAAA","360":"b:AAAAAAAAEMgAAAAEAAKgAQAAAAAAKAAA","370":"b:AAAAAAAAEMgAAAAEAAKgAQAAAAAAKAAA","379":"b:AAAAAAAAEMgAAAAEAAKgAQAAAAAAKAAA","380":"b:AAAAAAAAEMgAAAAEAAKgAQAAAAAAKAAA","385":"b:AAAAAAAAEMgAAAAEAAKgAQAAAAAAKAAA","387":"b:AAAAAAAAEMgAAAAEAAKgAQAAAAAAKAAA"},"packages/cli/src/services/role-cache.service.ts":{"37":"b:cgDgTwahHtIHgINBAAAAAyAAAADgAiEA","45":"b:cgDgTwahHtIHgINBAAAAAyAAAADgAiEA","54":"b:cgDgTwahHtIHgINBAAAAAyAAAADgAiEA","68":"b:cgDgTwahHtIHgINBAAAAAyAAAADgAiEA","77":"b:cgDgTwahHtIHgINBAAAAAyAAAADgAiEA","93":"b:cgDgTwahHtIHgINBAAAAAyAAAADgAiEA","105":"b:cgDgTwahHtIHgINBAAAAAyAAAADgAiEA","112":"b:cgDgTwahHtIHgINBAAAAAyAAAADgAiEA"},"packages/cli/src/services/security-settings.service.ts":{"54":[52],"69":[52],"99":[52],"149":[52],"157":[52],"177":[52],"184":[52],"187":[52],"196":[52],"200":[52],"204":[52],"209":[52]},"packages/cli/src/user-management/email/user-management-mailer.ts":{"28":"b:AAAAAAIAGMAEAAAEAAKAAQAAAAAAqAAA","32":"b:AAAAAAIAGMAEAAAEAAKAAQAAAAAAqAAA","63":"b:AAAAAAIAGMAEAAAEAAKAAQAAAAAAqAAA","80":"b:AAAAAAIAGMAEAAAEAAKAAQAAAAAAqAAA","91":"b:AAAAAAIAGMAEAAAEAAKAAQAAAAAAqAAA","102":"b:AAAAAAIAGMAEAAAEAAKAAQAAAAAAqAAA","130":"b:AAAAAAIAGMAEAAAEAAKAAQAAAAAAqAAA","159":"b:AAAAAAIAGMAEAAAEAAKAAQAAAAAAqAAA","185":"b:AAAAAAIAGMAEAAAEAAKAAQAAAAAAqAAA","206":"b:AAAAAAIAGMAEAAAEAAKAAQAAAAAAqAAA","216":"b:AAAAAAIAGMAEAAAEAAKAAQAAAAAAqAAA","233":"b:AAAAAAIAGMAEAAAEAAKAAQAAAAAAqAAA","247":"b:AAAAAAIAGMAEAAAEAAKAAQAAAAAAqAAA","261":"b:AAAAAAIAGMAEAAAEAAKAAQAAAAAAqAAA","265":"b:AAAAAAIAGMAEAAAEAAKAAQAAAAAAqAAA","270":"b:AAAAAAIAGMAEAAAEAAKAAQAAAAAAqAAA","286":"b:AAAAAAIAGMAEAAAEAAKAAQAAAAAAqAAA","290":"b:AAAAAAIAGMAEAAAEAAKAAQAAAAAAqAAA","295":"b:AAAAAAIAGMAEAAAEAAKAAQAAAAAAqAAA","311":"b:AAAAAAIAGMAEAAAEAAKAAQAAAAAAqAAA","315":"b:AAAAAAIAGMAEAAAEAAKAAQAAAAAAqAAA","320":"b:AAAAAAIAGMAEAAAEAAKAAQAAAAAAqAAA","329":"b:AAAAAAIAGMAEAAAEAAKAAQAAAAAAqAAA","334":"b:AAAAAAIAGMAEAAAEAAKAAQAAAAAAqAAA","335":"b:AAAAAAIAGMAEAAAEAAKAAQAAAAAAqAAA","348":"b:AAAAAAIAGMAEAAAEAAKAAQAAAAAAqAAA","353":"b:AAAAAAIAGMAEAAAEAAKAAQAAAAAAqAAA","358":"b:AAAAAAIAGMAEAAAEAAKAAQAAAAAAqAAA","374":"b:AAAAAAIAGMAEAAAEAAKAAQAAAAAAqAAA"},"packages/cli/src/webhooks/live-webhooks.ts":{"36":"b:AAjAAwCAEBACAAAAAAAAAAAAAAAEA6AA","46":"b:AAjAAwCAEBACAAAAAAAAAAAAAAAEA6AA","50":"b:AAjAAwCAEBACAAAAAAAAAAAAAAAEA6AA","58":"b:AAjAAwCAEBACAAAAAAAAAAAAAAAEA6AA","63":"b:AAjAAwCAEBACAAAAAAAAAAAAAAAEA6AA","78":"b:AAjAAwCAEBACAAAAAAAAAAAAAAAEA6AA","99":"b:AAjAAwCAEBACAAAAAAAAAAAAAAAEA6AA","128":"b:AAjAAwCAEBACAAAAAAAAAAAAAAAEA6AA","138":"b:AAjAAwCAEBACAAAAAAAAAAAAAAAEA6AA","162":"b:AAjAAwCAEBACAAAAAAAAAAAAAAAEA6AA","175":"b:AAjAAwCAEBACAAAAAAAAAAAAAAAEA6AA","190":"b:AAjAAwCAEBACAAAAAAAAAAAAAAAEA6AA","202":"b:AAjAAwCAEBACAAAAAAAAAAAAAAAEA6AA","216":"b:AAjAAwCAEBACAAAAAAAAAAAAAAAEA6AA","232":"b:AAjAAwCAEBACAAAAAAAAAAAAAAAEA6AA"},"packages/cli/src/webhooks/webhook-on-received-response-extractor.ts":{"13":"b:QADAQwCAEBAAAAAAoAAAAAAAACAEA4AA"},"packages/cli/src/webhooks/webhook-request-sanitizer.ts":{"12":"b:QAjAQwCAEBACAAAAgAAAAAAABAAEAyAA","18":"b:QAjAQwCAEBACAAAAgAAAAAAABAAEAyAA","19":"b:QAjAQwCAEBACAAAAgAAAAAAABAAEAyAA","32":"b:QAjAQwCAEBACAAAAgAAAAAAABAAEAyAA","40":"b:QAjAQwCAEBACAAAAgAAAAAAABAAEAyAA"},"packages/cli/src/workflows/triggers/trigger-execution-context.factory.ts":{"62":"b:AAjAQQKgEBICAAAAAAAAAgAAAAL0AuAB","83":"b:AAjAQQKgEBICAAAAAAAAAgAAAAL0AuAB","99":"b:AAjAQQKgEBICAAAAAAAAAgAAAAL0AuAB","100":"b:AAjAQQKgEBICAAAAAAAAAgAAAAL0AuAB","114":"b:AAjAQQKgEBICAAAAAAAAAgAAAAL0AuAB","125":"b:AAjAQQKgEBICAAAAAAAAAgAAAAL0AuAB","139":"b:AAjAQQKgEBICAAAAAAAAAgAAAAL0AuAB","158":"b:AAjAQQKgEBICAAAAAAAAAgAAAAL0AuAB","171":"b:AAjAQQKgEBICAAAAAAAAAgAAAAL0AuAB","175":"b:AAjAQQKgEBICAAAAAAAAAgAAAAL0AuAB","179":"b:AAjAQQKgEBICAAAAAAAAAgAAAAL0AuAB","190":"b:AAjAQQKgEBICAAAAAAAAAgAAAAL0AuAB","217":"b:AAjAQQKgEBICAAAAAAAAAgAAAAL0AuAB","228":"b:AAjAQQKgEBICAAAAAAAAAgAAAAL0AuAB","229":"b:AAjAQQKgEBICAAAAAAAAAgAAAAL0AuAB","241":"b:AAjAQQKgEBICAAAAAAAAAgAAAAL0AuAB","253":"b:AAjAQQKgEBICAAAAAAAAAgAAAAL0AuAB","260":"b:AAjAQQKgEBICAAAAAAAAAgAAAAL0AuAB","264":"b:AAjAQQKgEBICAAAAAAAAAgAAAAL0AuAB","267":"b:AAjAQQKgEBICAAAAAAAAAgAAAAL0AuAB","281":"b:AAjAQQKgEBICAAAAAAAAAgAAAAL0AuAB","301":"b:AAjAQQKgEBICAAAAAAAAAgAAAAL0AuAB","317":"b:AAjAQQKgEBICAAAAAAAAAgAAAAL0AuAB","350":"b:AAjAQQKgEBICAAAAAAAAAgAAAAL0AuAB"},"packages/community-packages.handler.ts":{"32":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","47":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","56":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","74":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA"},"packages/community-packages.mapper.ts":{"4":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","8":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","12":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","17":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","22":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","36":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA"},"packages/core/src/errors/error-reporter.ts":{"101":"b:UoABEQQBEAAQEAAwAABAAECCABAAAIAA","118":"b:UoABEQQBEAAQEAAwAABAAECCABAAAIAA","126":"b:UoABEQQBEAAQEAAwAABAAECCABAAAIAA","154":"b:UoABEQQBEAAQEAAwAABAAECCABAAAIAA","160":"b:UoABEQQBEAAQEAAwAABAAECCABAAAIAA","181":"b:UoABEQQBEAAQEAAwAABAAECCABAAAIAA","187":"b:UoABEQQBEAAQEAAwAABAAECCABAAAIAA","192":"b:UoABEQQBEAAQEAAwAABAAECCABAAAIAA","236":"b:UoABEQQBEAAQEAAwAABAAECCABAAAIAA","237":"b:UoABEQQBEAAQEAAwAABAAECCABAAAIAA","283":"b:UoABEQQBEAAQEAAwAABAAECCABAAAIAA","284":"b:UoABEQQBEAAQEAAwAABAAECCABAAAIAA","297":"b:UoABEQQBEAAQEAAwAABAAECCABAAAIAA","324":"b:UoABEQQBEAAQEAAwAABAAECCABAAAIAA","328":"b:UoABEQQBEAAQEAAwAABAAECCABAAAIAA","343":"b:UoABEQQBEAAQEAAwAABAAECCABAAAIAA","349":"b:UoABEQQBEAAQEAAwAABAAECCABAAAIAA","396":"b:UoABEQQBEAAQEAAwAABAAECCABAAAIAA","402":"b:UoABEQQBEAAQEAAwAABAAECCABAAAIAA","406":"b:UoABEQQBEAAQEAAwAABAAECCABAAAIAA","410":"b:UoABEQQBEAAQEAAwAABAAECCABAAAIAA","417":"b:UoABEQQBEAAQEAAwAABAAECCABAAAIAA","422":"b:UoABEQQBEAAQEAAwAABAAECCABAAAIAA","426":"b:UoABEQQBEAAQEAAwAABAAECCABAAAIAA","430":"b:UoABEQQBEAAQEAAwAABAAECCABAAAIAA","440":"b:UoABEQQBEAAQEAAwAABAAECCABAAAIAA","462":"b:UoABEQQBEAAQEAAwAABAAECCABAAAIAA"},"packages/core/src/execution-engine/node-execution-context/utils/webhook-helper-functions.ts":{"13":"b:QADAQwCAEBACAAAAgAAAAAAABCAEAyAA","33":"b:QADAQwCAEBACAAAAgAAAAAAABCAEAyAA"},"packages/n8n-packages.handler.ts":{"50":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","79":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","86":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","96":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","98":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","99":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","110":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","119":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","175":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","182":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA"},"packages/nodes-base/nodes/Webhook/Webhook.node.ts":{"204":"b:QADAUwCCMBACAAAAgAAAAAAABCAUAyAA","216":"b:QADAUwCCMBACAAAAgAAAAAAABCAUAyAA","335":"b:QADAUwCCMBACAAAAgAAAAAAABCAUAyAA","339":"b:QADAUwCCMBACAAAAgAAAAAAABCAUAyAA"},"packages/nodes-base/nodes/Webhook/description.ts":{"19":"b:QADAUwCCMBACAAAAgAAAAAAABCAUAgAA","56":"b:QADAUwCCMBACAAAAgAAAAAAABCAUAgAA"},"packages/nodes-base/nodes/Webhook/error.ts":{"4":"b:QAnKU4CGUBASAAAAoAAAAAAABAAUAgAA"},"packages/nodes-base/nodes/Webhook/utils.ts":{"37":"b:QAnKU4CGUBASAAAAoAAAAAAABCAUA6AA","54":"b:QAnKU4CGUBASAAAAoAAAAAAABCAUA6AA","68":"b:QAnKU4CGUBASAAAAoAAAAAAABCAUA6AA","79":"b:QAnKU4CGUBASAAAAoAAAAAAABCAUA6AA","89":"b:QAnKU4CGUBASAAAAoAAAAAAABCAUA6AA","106":"b:QAnKU4CGUBASAAAAoAAAAAAABCAUA6AA","117":"b:QAnKU4CGUBASAAAAoAAAAAAABCAUA6AA","119":"b:QAnKU4CGUBASAAAAoAAAAAAABCAUA6AA","130":"b:QAnKU4CGUBASAAAAoAAAAAAABCAUA6AA","140":"b:QAnKU4CGUBASAAAAoAAAAAAABCAUA6AA","155":"b:QAnKU4CGUBASAAAAoAAAAAAABCAUA6AA","166":"b:QAnKU4CGUBASAAAAoAAAAAAABCAUA6AA","204":"b:QAnKU4CGUBASAAAAoAAAAAAABCAUA6AA","209":"b:QAnKU4CGUBASAAAAoAAAAAAABCAUA6AA","235":"b:QAnKU4CGUBASAAAAoAAAAAAABCAUA6AA","358":"b:QAnKU4CGUBASAAAAoAAAAAAABCAUA6AA","423":"b:QAnKU4CGUBASAAAAoAAAAAAABCAUA6AA","441":"b:QAnKU4CGUBASAAAAoAAAAAAABCAUA6AA"},"packages/package-failure-classifier.ts":{"20":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA"},"packages/utils/import-package-upload.ts":{"25":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","38":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","46":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","50":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA","53":"b:AAAAAAAAEMAAAAAEAAKAAQAAAAAAKAEA"},"packages/workflow/src/errors/index.ts":{"5":"b:eokBEQQBEAASHDJwAABAAECCABAEAKEA","14":"b:eokBEQQBEAASHDJwAABAAECCABAEAKEA","17":"b:eokBEQQBEAASHDJwAABAAECCABAEAKEA","23":"b:eokBEQQBEAASHDJwAABAAECCABAEAKEA","27":"b:eokBEQQBEAASHDJwAABAAECCABAEAKEA","29":"b:eokBEQQBEAASHDJwAABAAECCABAEAKEA","30":"b:eokBEQQBEAASHDJwAABAAECCABAEAKEA","33":"b:eokBEQQBEAASHDJwAABAAECCABAEAKEA","35":"b:eokBEQQBEAASHDJwAABAAECCABAEAKEA"},"packages/workflow/src/node-validation.ts":{"19":"b:AAjAQwKgEBICAAAAAAAAAgAAAAL0AuAB","59":"b:AAjAQwKgEBICAAAAAAAAAgAAAAL0AuAB","84":"b:AAjAQwKgEBICAAAAAAAAAgAAAAL0AuAB"},"packages/workflow/src/workflow-validation.ts":{"16":"b:AAjAQwKgEBICAAAAAAAAAgAAAAL0AuAB"},"localhost-39571/assets/_plugin-vue_export-helper-Da88TEg1.js":{"2":[52]},"localhost-39571/assets/chunk-CC9Q-vWm.js":{"8":[52],"9":[52],"10":[52],"19":[52],"23":[52],"29":[52],"33":[52],"34":[52]},"packages/frontend/@n8n/design-system/src/components/N8nAlertDialog/AlertDialog.vue":{"74":[71,52],"87":[52],"94":[71,52],"99":[71,52],"102":[52],"103":[52]},"packages/frontend/@n8n/design-system/src/components/N8nBadge/Badge.vue":{"23":"b:AiUAIB4AHcRsAAhQAAOkAyAAQDADAAAA","25":"b:AiUAIB4AHcRsAAhQAAOkAyAAQDADAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nDialog/Dialog.vue":{"95":[83,71,52,117,121],"99":[83,71,52,117,121],"108":[83,71,52,117,121]},"packages/frontend/@n8n/design-system/src/components/N8nDialog/DialogContent.vue":{"59":[83,71,52,117,121],"81":[83,71,52,117,121],"84":[52],"85":[52],"101":[83,71,52,117,121],"112":[52],"113":[52],"114":[52]},"packages/frontend/@n8n/design-system/src/components/N8nDialog/DialogDescription.vue":{"15":[52],"16":[52]},"packages/frontend/@n8n/design-system/src/components/N8nDialog/DialogFooter.vue":{"6":[52]},"packages/frontend/@n8n/design-system/src/components/N8nDialog/DialogHeader.vue":{"6":[52]},"packages/frontend/@n8n/design-system/src/components/N8nDialog/DialogOverlay.vue":{"15":[83,71,52,117,121]},"packages/frontend/@n8n/design-system/src/components/N8nDialog/DialogTitle.vue":{"15":[52],"16":[52]},"packages/frontend/@n8n/design-system/src/components/N8nIcon/custom/mcp.svg":{"13":"b:AiQAABAAHyzkAANUAAN6ACAAQAADAAAA"},"packages/frontend/@n8n/design-system/src/components/PreviewTag/PreviewTag.vue":{"12":"b:AAECAAgAGCyQAAMUAAB6AAAACAAAAAAA","13":"b:AAECAAgAGCyQAAMUAAB6AAAACAAAAAAA"},"packages/frontend/editor-ui/src/app/components/EnterpriseEdition.ee.vue":{"17":"b:kkECIAwAHsQQAAAQEBCAAQAACAABAAAA","18":"b:kkECIAwAHsQQAAAQEBCAAQAACAABAAAA"},"packages/frontend/editor-ui/src/app/components/Modal.vue":{"59":"b:ssECIByAH+wYQAJ4EBCuAwQACI0BAAAA","81":"b:ssECIByAHuwYQAJ4EBCuAwQACI0BAAAA","90":"b:ssECIByAHqQYAAJ4ABCmAwQACI0BAAAA","97":[29,8,36,58,119,63],"102":"b:ssECIByAHqwYAAJ4ABCuAwQACI0BAAAA","109":[29,8,36,58,119,63,130],"113":"b:skECIAyAG6QYAAIwABCmAwQACI0BAAAA","125":"b:MAEAIAQACKQYAAAwAACEAAAACIAAAAAA","129":"b:ssECIByAH+wYQAJ4EBCuAwQACI0BAAAA","162":"b:ssECIByAH+wYQAJ4EBCuAwQACI0BAAAA","164":"b:ssECIByADKwYQAB4EBCIAgAACAgBAAAA","178":[50,51,130]},"packages/frontend/editor-ui/src/app/components/RedactionMembersModal.vue":{"9":[52,121],"24":[52,121],"26":[52,121],"41":[52,121]},"packages/frontend/editor-ui/src/app/components/SettingsSidebar.vue":{"23":"b:AAAAAAAAECyAAAMUAAB6AAAAAAAAAAAA","26":"b:AAAAAAAAECyAAAMUAAB6AAAAAAAAAAAA","37":"b:AAAAAAAAECyAAAMUAAB6AAAAAAAAAAAA","41":"b:AAAAAAAAECyAAAMUAAB6AAAAAAAAAAAA"},"packages/frontend/editor-ui/src/app/components/WorkflowSettings/WorkflowSettings.vue":{"72":[52,121],"213":[52,121],"220":[52,121],"223":[52,121],"226":[52,121],"227":[52,121],"228":[52,121],"229":[52,121],"232":[52,121],"237":[52,121],"239":[52],"241":[52],"247":[52,121],"251":[52,121],"253":[52,121],"257":[52,121],"262":[52,121],"277":[52,121],"281":[52,121],"285":[52,121],"286":[52,121],"294":[52,121],"313":[52,121],"329":[52,121],"333":[52],"345":[52,121],"349":[52],"360":[52],"379":[52,121],"381":[52,121],"386":[52,121],"390":[52,121],"401":[52,121],"415":[52,121],"424":[52],"440":[52,121],"470":[52,121],"494":[52,121],"518":[52,121],"541":[52,121],"564":[52,121],"591":[52,121],"647":[52,121],"671":[52],"757":[52],"835":[52,121],"989":[52],"993":[52,121],"1008":[52,121],"1016":[52,121],"1019":[52,121],"1027":[52,121],"1030":[52,121],"1034":[52,121],"1045":[52,121],"1046":[52,121],"1050":[52,121],"1055":[52,121],"1065":[52,121],"1067":[52,121],"1072":[52,121],"1147":[52,121],"1151":[52,121],"1157":[52,121],"1163":[52,121],"1165":[52,121],"1198":[52,121],"1202":[52,121],"1207":[52,121],"1214":[52,121],"1216":[52,121],"1227":[52,121],"1231":[52,121],"1236":[52,121],"1243":[52,121],"1245":[52,121],"1256":[52,121],"1260":[52,121],"1265":[52,121],"1272":[52,121],"1274":[52,121],"1285":[52,121],"1289":[52,121],"1294":[52,121],"1301":[52,121],"1303":[52,121],"1314":[52,121],"1318":[52,121],"1323":[52,121],"1330":[52,121],"1332":[52,121],"1343":[52,121],"1346":[52,121],"1369":[121],"1383":[52,121],"1393":[52,121],"1399":[52],"1410":[52,121],"1426":[52,121],"1428":[52,121],"1450":[52,121],"1475":[121],"1489":[52,121],"1499":[52,121],"1508":[52],"1524":[52,121],"1538":[52,121],"1540":[52,121],"1553":[52,121],"1557":[52,121],"1562":[52,121],"1652":[52,121],"1657":[52,121],"1663":[52,121],"1671":[52,121],"1673":[52,121],"1684":[52,121],"1774":[52,121]},"packages/frontend/editor-ui/src/app/layouts/SettingsLayout.vue":{"27":"b:AAAAAAAAECyAAAMUAAB6AAAAAAAAAAAA","34":"b:AAAAAAAAECyAAAMUAAB6AAAAAAAAAAAA","36":"b:AAAAAAAAECyAAAMUAAB6AAAAAAAAAAAA"},"packages/frontend/editor-ui/src/app/stores/workflows.ee.store.ts":{"13":[52,62,120,121,160],"18":[52,121],"19":[52,121],"34":[120],"43":[120],"49":[120]},"packages/frontend/editor-ui/src/app/utils/rbac/middleware/rbac.ts":{"11":"b:AAAAAAAAECyAAAEQAABqAAAAAAAAAAAA"},"packages/frontend/editor-ui/src/app/utils/rbacUtils.ts":{"4":"b:AAAAAAAAECyAAAEQAABqAAAAAAAAAAAA","12":"b:AAAAAAAAECyAAAEQAABqAAAAAAAAAAAA","23":"b:AAAAAAAAECyAAAEQAABqAAAAAAAAAAAA","34":[58,117]},"packages/frontend/editor-ui/src/features/ndv/panel/components/RedactedDataState.vue":{"24":[52],"26":[52],"33":[52],"49":[52]},"packages/frontend/editor-ui/src/features/settings/security/DataRedactionSection.vue":{"40":[52],"59":[52],"64":[52],"65":[52],"74":[52],"78":[52],"79":[52],"87":[52],"96":[52],"123":[52],"141":[52],"154":[52],"172":[52],"178":[52],"184":[52],"228":[52],"274":[52],"277":[52]},"packages/frontend/editor-ui/src/features/settings/security/SecuritySettings.vue":{"42":[52],"46":[52],"72":[52],"86":[52],"93":[52],"127":[52],"149":[52],"170":[52],"186":[52],"189":[52],"202":[52],"213":[52],"231":[52],"265":[52],"273":[52],"279":[52],"318":[52],"321":[52],"330":[52],"336":[52],"375":[52],"378":[52]},"packages/@n8n/api-types/src/dto/user/user-update-request.dto.ts":{"6":[116,81],"13":[116,81],"15":[116,81]},"packages/cli/src/controllers/me.controller.ts":{"32":[1,8,10,109,116,81,121,6,14],"48":[1,8,10,109,116,81,121,6,14],"108":[1,8,10,109,116,81,121,6,14],"120":[1,8,10,109,116,81,121,6,14],"174":[1,8,10,109,116,81,121,6,14],"183":[1,8,10,109,116,81,121,6,14],"193":[1,8,10,109,116,81,121,6,14],"231":[1,8,10,109,116,81,121,6,14],"264":[1,8,10,109,116,81,121,6,14],"309":[1,8,10,109,116,81,121,6,14]},"packages/@n8n/agents/src/index.ts":{"101":[37,72,74,95,39,75,40,76],"127":[37,72,74,95,39,75,40,76],"149":[37,72,74,95,39,75,40,76],"150":[37,72,74,95,39,75,40,76],"151":[37,72,74,95,39,75,40,76],"153":[37,72,74,95,39,75,40,76],"204":[37,72,74,95,39,75,40,76],"283":[37,72,74,95,39,75,40,76],"288":[37,72,74,95,39,75,40,76],"342":[37,72,74,95,39,75,40,76],"389":[37,72,74,95,39,75,40,76],"390":[37,72,74,95,39,75,40,76],"391":[37,72,74,95,39,75,40,76],"392":[37,72,74,95,39,75,40,76],"393":[37,72,74,95,39,75,40,76],"429":[37,72,74,95,39,75,40,76]},"packages/@n8n/agents/src/runtime/loop/agent-runtime.ts":{"186":[37,72,74,38,95,39,75,40,76],"206":[37,72,74,38,95,39,75,40,76],"217":[37,72,74,38,95,39,75,40,76],"225":[37,72,74,38,95,39,75,40,76],"231":[37,72,74,38,95,39,75,40,76],"236":[37,72,74,38,95,39,75,40,76],"247":[37,72,74,38,95,39,75,40,76],"261":[37,72,74,38,95,39,75,40,76],"284":[37,72,74,38,95,39,75,40,76],"292":[37,72,74,38,95,39,75,40,76],"307":[37,72,74,38,95,39,75,40,76],"313":[37,72,74,38,95,39,75,40,76],"335":[37,72,74,38,95,39,75,40,76],"352":[37,72,74,38,95,39,75,40,76],"391":[37,72,74,38,95,39,75,40,76],"423":[37,72,74,38,95,39,75,40,76],"449":[37,72,74,38,95,39,75,40,76],"464":[37,72,74,38,95,39,75,40,76],"467":[37,72,74,38,95,39,75,40,76],"481":[37,72,74,38,95,39,75,40,76],"544":[37,72,74,38,95,39,75,40,76],"553":[37,72,74,38,95,39,75,40,76],"567":[37,72,74,38,95,39,75,40,76],"589":[37,72,74,38,95,39,75,40,76],"608":[37,72,74,38,95,39,75,40,76],"614":[37,72,74,38,95,39,75,40,76],"617":[37,72,74,38,95,39,75,40,76],"630":[37,72,74,38,95,39,75,40,76],"643":[37,72,74,38,95,39,75,40,76],"651":[37,72,74,38,95,39,75,40,76],"655":[37,72,74,38,95,39,75,40,76],"656":[37,72,74,38,95,39,75,40,76],"657":[37,72,74,38,95,39,75,40,76],"658":[37,72,74,38,95,39,75,40,76],"659":[37,72,74,38,95,39,75,40,76],"660":[37,72,74,38,95,39,75,40,76],"661":[37,72,74,38,95,39,75,40,76],"662":[37,72,74,38,95,39,75,40,76],"667":[37,72,74,38,95,39,75,40,76],"700":[37,72,74,38,95,39,75,40,76],"726":[37,72,74,38,95,39,75,40,76],"734":[37,72,74,38,95,39,75,40,76],"896":[37,72,74,38,95,39,75,40,76],"903":[37,72,74,38,95,39,75,40,76],"905":[37,72,74,38,95,39,75,40,76],"921":[37,72,74,38,95,39,75,40,76],"926":[37,72,74,38,95,39,75,40,76],"931":[37,72,74,38,95,39,75,40,76],"932":[37,72,74,38,95,39,75,40,76],"933":[37,72,74,38,95,39,75,40,76],"934":[37,72,74,38,95,39,75,40,76],"944":[37,72,74,38,95,39,75,40,76],"983":[37,72,74,38,95,39,75,40,76],"1008":[37,72,74,38,95,39,75,40,76],"1013":[37,72,74,38,95,39,75,40,76],"1014":[37,72,74,38,95,39,75,40,76],"1021":[37,72,74,38,95,39,75,40,76],"1026":[37,72,74,38,95,39,75,40,76],"1031":[37,72,74,38,95,39,75,40,76],"1042":[37,72,74,38,95,39,75,40,76],"1057":[37,72,74,38,95,39,75,40,76]},"packages/@n8n/agents/src/runtime/loop/execution-counter.ts":{"14":[37,72,74,95,39,75,40,76],"22":[37,72,74,95,39,75,40,76],"24":[37,72,74,95,39,75,40,76],"27":[37,72,74,95,39,75,40,76],"29":[37,72,74,95,39,75,40,76],"32":[37,72,74,95,39,75,40,76]},"packages/@n8n/agents/src/runtime/loop/runtime-context.ts":{"29":[37,72,74,95,39,75,40,76],"31":[37,72,74,95,39,75,40,76],"35":[37,72,74,95,39,75,40,76],"63":[37,72,74,95,39,75,40,76],"68":[37,72,74,95,39,75,40,76],"73":[37,72,74,95,39,75,40,76],"106":[37,72,74,95,39,75,40,76],"136":[37,72,74,95,39,75,40,76],"141":[37,72,74,95,39,75,40,76],"160":[37,72,74,95,39,75,40,76],"178":[37,72,74,95,39,75,40,76],"193":[37,72,74,95,39,75,40,76],"209":[37,72,74,95,39,75,40,76],"231":[37,72,74,95,39,75,40,76],"236":[37,72,74,95,39,75,40,76],"267":[37,72,74,95,39,75,40,76],"279":[37,72,74,95,39,75,40,76]},"packages/@n8n/agents/src/runtime/loop/runtime-helpers.ts":{"14":[37,72,74,95,39,75,40,76],"22":[37,72,74,95,39,75,40,76],"33":[37,72,74,95,39,75,40,76],"37":[37,72,74,95,39,75,40,76],"61":[37,72,74,95,39,75,40,76],"84":[37,72,74,95,39,75,40,76],"86":[37,72,74,95,39,75,40,76],"87":[37,72,74,95,39,75,40,76],"95":[37,72,74,95,39,75,40,76],"98":[37,72,74,95,39,75,40,76],"99":[37,72,74,95,39,75,40,76],"100":[37,72,74,95,39,75,40,76],"105":[37,72,74,95,39,75,40,76]},"packages/@n8n/agents/src/runtime/loop/stream-sink.ts":{"42":[37,72,74,38,95,39,75,40,76],"48":[37,72,74,38,95,39,75,40,76],"60":[37,72,74,38,95,39,75,40,76],"75":[37,72,74,38,95,39,75,40,76],"89":[37,72,74,38,95,39,75,40,76],"94":[37,72,74,38,95,39,75,40,76],"102":[37,72,74,38,95,39,75,40,76],"202":[37,72,74,38,95,39,75,40,76],"227":[37,72,74,38,95,39,75,40,76],"254":[37,72,74,38,95,39,75,40,76]},"packages/@n8n/agents/src/runtime/mcp/mcp-tool-resolver.ts":{"27":[37,72,74,95,39,75,40,76],"28":[37,72,74,95,39,75,40,76],"31":[37,72,74,95,39,75,40,76],"35":[37,72,74,95,39,75,40,76],"46":[37,72,74,95,39,75,40,76],"65":[37,72,74,95,39,75,40,76],"66":[37,72,74,95,39,75,40,76],"85":[37,72,74,95,39,75,40,76],"89":[37,72,74,95,39,75,40,76],"90":[37,72,74,95,39,75,40,76],"93":[37,72,74,95,39,75,40,76],"110":[37,72,74,95,39,75,40,76],"115":[37,72,74,95,39,75,40,76],"131":[37,72,74,95,39,75,40,76],"134":[37,72,74,95,39,75,40,76],"149":[37,72,74,95,39,75,40,76]},"packages/@n8n/agents/src/runtime/memory/episodic-memory.ts":{"79":[37,72,74,95,39,75,40,76],"85":[37,72,74,95,39,75,40,76],"100":[37,72,74,95,39,75,40,76],"119":[37,72,74,95,39,75,40,76],"133":[37,72,74,95,39,75,40,76],"157":[37,72,74,95,39,75,40,76],"177":[37,72,74,95,39,75,40,76],"192":[37,72,74,95,39,75,40,76],"206":[37,72,74,95,39,75,40,76],"207":[37,72,74,95,39,75,40,76],"215":[37,72,74,95,39,75,40,76],"222":[37,72,74,95,39,75,40,76],"225":[37,72,74,95,39,75,40,76],"226":[37,72,74,95,39,75,40,76],"229":[37,72,74,95,39,75,40,76],"236":[37,72,74,95,39,75,40,76],"239":[37,72,74,95,39,75,40,76],"240":[37,72,74,95,39,75,40,76],"243":[37,72,74,95,39,75,40,76],"244":[37,72,74,95,39,75,40,76],"270":[37,72,74,95,39,75,40,76],"271":[37,72,74,95,39,75,40,76],"279":[37,72,74,95,39,75,40,76],"286":[37,72,74,95,39,75,40,76],"290":[37,72,74,95,39,75,40,76],"294":[37,72,74,95,39,75,40,76],"301":[37,72,74,95,39,75,40,76],"307":[37,72,74,95,39,75,40,76],"329":[37,72,74,95,39,75,40,76],"340":[37,72,74,95,39,75,40,76],"357":[37,72,74,95,39,75,40,76],"366":[37,72,74,95,39,75,40,76],"376":[37,72,74,95,39,75,40,76],"382":[37,72,74,95,39,75,40,76],"390":[37,72,74,95,39,75,40,76],"403":[37,72,74,95,39,75,40,76],"418":[37,72,74,95,39,75,40,76],"425":[37,72,74,95,39,75,40,76],"426":[37,72,74,95,39,75,40,76],"431":[37,72,74,95,39,75,40,76],"439":[37,72,74,95,39,75,40,76],"444":[37,72,74,95,39,75,40,76],"453":[37,72,74,95,39,75,40,76],"460":[37,72,74,95,39,75,40,76],"482":[37,72,74,95,39,75,40,76],"486":[37,72,74,95,39,75,40,76],"490":[37,72,74,95,39,75,40,76],"503":[37,72,74,95,39,75,40,76],"505":[37,72,74,95,39,75,40,76],"516":[37,72,74,95,39,75,40,76],"526":[37,72,74,95,39,75,40,76],"529":[37,72,74,95,39,75,40,76],"533":[37,72,74,95,39,75,40,76],"537":[37,72,74,95,39,75,40,76],"541":[37,72,74,95,39,75,40,76],"542":[37,72,74,95,39,75,40,76],"545":[37,72,74,95,39,75,40,76],"548":[37,72,74,95,39,75,40,76],"552":[37,72,74,95,39,75,40,76],"566":[37,72,74,95,39,75,40,76],"570":[37,72,74,95,39,75,40,76],"580":[37,72,74,95,39,75,40,76],"589":[37,72,74,95,39,75,40,76],"603":[37,72,74,95,39,75,40,76]},"packages/@n8n/agents/src/runtime/memory/memory-orchestrator.ts":{"38":[37,72,74,38,95,39,75,40,76],"45":[37,72,74,38,95,39,75,40,76],"68":[37,72,74,38,95,39,75,40,76],"75":[37,72,74,38,95,39,75,40,76],"105":[37,72,74,38,95,39,75,40,76],"122":[37,72,74,38,95,39,75,40,76],"137":[37,72,74,38,95,39,75,40,76],"169":[37,72,74,38,95,39,75,40,76],"212":[37,72,74,38,95,39,75,40,76],"244":[37,72,74,38,95,39,75,40,76],"270":[37,72,74,38,95,39,75,40,76],"294":[37,72,74,38,95,39,75,40,76],"316":[37,72,74,38,95,39,75,40,76],"332":[37,72,74,38,95,39,75,40,76],"352":[37,72,74,38,95,39,75,40,76],"365":[37,72,74,38,95,39,75,40,76],"373":[37,72,74,38,95,39,75,40,76],"374":[37,72,74,38,95,39,75,40,76],"375":[37,72,74,38,95,39,75,40,76],"384":[37,72,74,38,95,39,75,40,76],"412":[37,72,74,38,95,39,75,40,76],"424":[37,72,74,38,95,39,75,40,76],"433":[37,72,74,38,95,39,75,40,76],"438":[37,72,74,38,95,39,75,40,76],"457":[37,72,74,38,95,39,75,40,76]},"packages/@n8n/agents/src/runtime/memory/memory-store.ts":{"53":[37,72,74,95,39,75,40,76],"61":[37,72,74,95,39,75,40,76],"65":[37,72,74,95,39,75,40,76],"81":[37,72,74,95,39,75,40,76],"107":[37,72,74,95,39,75,40,76],"109":[37,72,74,95,39,75,40,76],"111":[37,72,74,95,39,75,40,76],"112":[37,72,74,95,39,75,40,76],"114":[37,72,74,95,39,75,40,76],"115":[37,72,74,95,39,75,40,76],"117":[37,72,74,95,39,75,40,76],"119":[37,72,74,95,39,75,40,76],"124":[37,72,74,95,39,75,40,76],"129":[37,72,74,95,39,75,40,76],"144":[37,72,74,95,39,75,40,76],"152":[37,72,74,95,39,75,40,76],"153":[37,72,74,95,39,75,40,76],"156":[37,72,74,95,39,75,40,76],"160":[37,72,74,95,39,75,40,76],"175":[37,72,74,95,39,75,40,76],"185":[37,72,74,95,39,75,40,76],"189":[37,72,74,95,39,75,40,76],"191":[37,72,74,95,39,75,40,76],"198":[37,72,74,95,39,75,40,76],"208":[37,72,74,95,39,75,40,76],"214":[37,72,74,95,39,75,40,76],"234":[37,72,74,95,39,75,40,76],"239":[37,72,74,95,39,75,40,76],"244":[37,72,74,95,39,75,40,76],"251":[37,72,74,95,39,75,40,76],"275":[37,72,74,95,39,75,40,76],"282":[37,72,74,95,39,75,40,76],"284":[37,72,74,95,39,75,40,76],"289":[37,72,74,95,39,75,40,76],"292":[37,72,74,95,39,75,40,76],"301":[37,72,74,95,39,75,40,76],"314":[37,72,74,95,39,75,40,76],"326":[37,72,74,95,39,75,40,76],"335":[37,72,74,95,39,75,40,76],"355":[37,72,74,95,39,75,40,76],"361":[37,72,74,95,39,75,40,76],"366":[37,72,74,95,39,75,40,76],"375":[37,72,74,95,39,75,40,76],"382":[37,72,74,95,39,75,40,76],"386":[37,72,74,95,39,75,40,76],"392":[37,72,74,95,39,75,40,76],"396":[37,72,74,95,39,75,40,76],"407":[37,72,74,95,39,75,40,76],"428":[37,72,74,95,39,75,40,76],"433":[37,72,74,95,39,75,40,76],"444":[37,72,74,95,39,75,40,76],"452":[37,72,74,95,39,75,40,76],"483":[37,72,74,95,39,75,40,76],"489":[37,72,74,95,39,75,40,76],"512":[37,72,74,95,39,75,40,76],"522":[37,72,74,95,39,75,40,76],"533":[37,72,74,95,39,75,40,76],"539":[37,72,74,95,39,75,40,76],"545":[37,72,74,95,39,75,40,76],"555":[37,72,74,95,39,75,40,76],"560":[37,72,74,95,39,75,40,76],"561":[37,72,74,95,39,75,40,76],"565":[37,72,74,95,39,75,40,76],"571":[37,72,74,95,39,75,40,76],"572":[37,72,74,95,39,75,40,76],"578":[37,72,74,95,39,75,40,76],"594":[37,72,74,95,39,75,40,76],"595":[37,72,74,95,39,75,40,76],"604":[37,72,74,95,39,75,40,76],"615":[37,72,74,95,39,75,40,76],"623":[37,72,74,95,39,75,40,76],"633":[37,72,74,95,39,75,40,76],"652":[37,72,74,95,39,75,40,76],"665":[37,72,74,95,39,75,40,76],"675":[37,72,74,95,39,75,40,76],"685":[37,72,74,95,39,75,40,76],"689":[37,72,74,95,39,75,40,76]},"packages/@n8n/agents/src/runtime/memory/observation-log-defaults.ts":{"309":[37,72,74,95,39,75,40,76],"324":[37,72,74,95,39,75,40,76],"328":[37,72,74,95,39,75,40,76],"566":[37,72,74,95,39,75,40,76],"578":[37,72,74,95,39,75,40,76],"582":[37,72,74,95,39,75,40,76]},"packages/@n8n/agents/src/runtime/memory/observation-log-renderer.ts":{"25":[37,72,74,95,39,75,40,76],"31":[37,72,74,95,39,75,40,76],"37":[37,72,74,95,39,75,40,76],"42":[37,72,74,95,39,75,40,76],"46":[37,72,74,95,39,75,40,76],"50":[37,72,74,95,39,75,40,76]},"packages/@n8n/agents/src/runtime/memory/observation-log-store.ts":{"21":[37,72,74,95,39,75,40,76],"28":[37,72,74,95,39,75,40,76],"31":[37,72,74,95,39,75,40,76],"34":[37,72,74,95,39,75,40,76],"37":[37,72,74,95,39,75,40,76]},"packages/@n8n/agents/src/runtime/memory/strip-orphaned-tool-messages.ts":{"13":[37,72,74,95,39,75,40,76],"22":[37,72,74,95,39,75,40,76]},"packages/@n8n/agents/src/runtime/model/lazy-ai.ts":{"5":[37,72,74,95,39,75,40,76]},"packages/@n8n/agents/src/runtime/model/message-list.ts":{"28":[37,72,74,38,95,39,75,40,76],"42":[37,72,74,38,95,39,75,40,76],"82":[37,72,74,38,95,39,75,40,76],"108":[37,72,74,38,95,39,75,40,76],"131":[37,72,74,38,95,39,75,40,76],"137":[37,72,74,38,95,39,75,40,76],"138":[37,72,74,38,95,39,75,40,76],"171":[37,72,74,38,95,39,75,40,76],"177":[37,72,74,38,95,39,75,40,76],"185":[37,72,74,38,95,39,75,40,76],"193":[37,72,74,38,95,39,75,40,76],"208":[37,72,74,38,95,39,75,40,76],"242":[37,72,74,38,95,39,75,40,76],"258":[37,72,74,38,95,39,75,40,76],"265":[37,72,74,38,95,39,75,40,76],"273":[37,72,74,38,95,39,75,40,76],"276":[37,72,74,38,95,39,75,40,76],"287":[37,72,74,38,95,39,75,40,76],"308":[37,72,74,38,95,39,75,40,76],"309":[37,72,74,38,95,39,75,40,76],"316":[37,72,74,38,95,39,75,40,76],"317":[37,72,74,38,95,39,75,40,76],"324":[37,72,74,38,95,39,75,40,76],"325":[37,72,74,38,95,39,75,40,76],"328":[37,72,74,38,95,39,75,40,76],"329":[37,72,74,38,95,39,75,40,76],"338":[37,72,74,38,95,39,75,40,76]},"packages/@n8n/agents/src/runtime/model/messages.ts":{"43":[37,72,74,95,39,75,40,76],"47":[37,72,74,95,39,75,40,76],"51":[37,72,74,95,39,75,40,76],"55":[37,72,74,95,39,75,40,76],"63":[37,72,74,95,39,75,40,76],"69":[37,72,74,95,39,75,40,76],"98":[37,72,74,95,39,75,40,76],"102":[37,72,74,95,39,75,40,76],"106":[37,72,74,95,39,75,40,76],"110":[37,72,74,95,39,75,40,76],"114":[37,72,74,95,39,75,40,76],"119":[37,72,74,95,39,75,40,76],"121":[37,72,74,95,39,75,40,76],"128":[37,72,74,95,39,75,40,76],"138":[37,72,74,95,39,75,40,76],"161":[37,72,74,95,39,75,40,76],"204":[37,72,74,95,39,75,40,76],"236":[37,72,74,95,39,75,40,76],"304":[37,72,74,95,39,75,40,76],"309":[37,72,74,95,39,75,40,76],"318":[37,72,74,95,39,75,40,76],"394":[37,72,74,95,39,75,40,76],"409":[37,72,74,95,39,75,40,76],"453":[37,72,74,95,39,75,40,76],"474":[37,72,74,95,39,75,40,76]},"packages/@n8n/agents/src/runtime/model/model-factory.ts":{"27":[37,72,74,95,39,75,40,76],"39":[37,72,74,95,39,75,40,76],"46":[37,72,74,95,39,75,40,76],"75":[37,72,74,95,39,75,40,76],"86":[37,72,74,95,39,75,40,76],"99":[37,72,74,95,39,75,40,76],"117":[37,72,74,95,39,75,40,76],"124":[37,72,74,95,39,75,40,76],"130":[37,72,74,95,39,75,40,76],"136":[37,72,74,95,39,75,40,76],"142":[37,72,74,95,39,75,40,76],"148":[37,72,74,95,39,75,40,76],"154":[37,72,74,95,39,75,40,76],"160":[37,72,74,95,39,75,40,76],"167":[37,72,74,95,39,75,40,76],"180":[37,72,74,95,39,75,40,76],"198":[37,72,74,95,39,75,40,76],"218":[37,72,74,95,39,75,40,76],"262":[37,72,74,95,39,75,40,76],"301":[37,72,74,95,39,75,40,76]},"packages/@n8n/agents/src/runtime/model/prompt-cache.ts":{"13":[37,72,74,95,39,75,40,76],"16":[37,72,74,95,39,75,40,76],"29":[37,72,74,95,39,75,40,76],"39":[37,72,74,95,39,75,40,76],"49":[37,72,74,95,39,75,40,76],"58":[37,72,74,95,39,75,40,76],"70":[37,72,74,95,39,75,40,76],"84":[37,72,74,95,39,75,40,76],"91":[37,72,74,95,39,75,40,76],"103":[37,72,74,95,39,75,40,76],"118":[37,72,74,95,39,75,40,76],"143":[37,72,74,95,39,75,40,76],"155":[37,72,74,95,39,75,40,76],"195":[37,72,74,95,39,75,40,76]},"packages/@n8n/agents/src/runtime/model/provider-quirks.ts":{"41":[37,72,74,95,39,75,40,76],"69":[37,72,74,95,39,75,40,76],"84":[37,72,74,95,39,75,40,76],"97":[37,72,74,95,39,75,40,76],"104":[37,72,74,95,39,75,40,76],"108":[37,72,74,95,39,75,40,76],"113":[37,72,74,95,39,75,40,76]},"packages/@n8n/agents/src/runtime/model/raw-error.ts":{"36":[37,72,74,95,39,75,40,76],"44":[37,72,74,95,39,75,40,76],"55":[37,72,74,95,39,75,40,76],"63":[37,72,74,95,39,75,40,76]},"packages/@n8n/agents/src/runtime/model/raw-usage.ts":{"32":[37,72,74,95,39,75,40,76],"43":[37,72,74,95,39,75,40,76],"46":[37,72,74,95,39,75,40,76],"76":[37,72,74,95,39,75,40,76],"84":[37,72,74,95,39,75,40,76]},"packages/@n8n/agents/src/runtime/state/background-task-tracker.ts":{"1":[37,72,74,95,39,75,40,76],"4":[37,72,74,95,39,75,40,76],"8":[37,72,74,95,39,75,40,76],"10":[37,72,74,95,39,75,40,76],"16":[37,72,74,95,39,75,40,76]},"packages/@n8n/agents/src/runtime/state/event-bus.ts":{"1":[37,72,74,95,39,75,40,76],"18":[37,72,74,95,39,75,40,76],"29":[37,72,74,95,39,75,40,76],"31":[37,72,74,95,39,75,40,76],"34":[37,72,74,95,39,75,40,76],"38":[37,72,74,95,39,75,40,76],"42":[37,72,74,95,39,75,40,76],"46":[37,72,74,95,39,75,40,76],"70":[37,72,74,95,39,75,40,76],"79":[37,72,74,95,39,75,40,76],"88":[37,72,74,95,39,75,40,76],"92":[37,72,74,95,39,75,40,76],"100":[37,72,74,95,39,75,40,76],"107":[37,72,74,95,39,75,40,76],"108":[37,72,74,95,39,75,40,76],"124":[37,72,74,95,39,75,40,76],"133":[37,72,74,95,39,75,40,76],"135":[37,72,74,95,39,75,40,76],"141":[37,72,74,95,39,75,40,76],"145":[37,72,74,95,39,75,40,76],"153":[37,72,74,95,39,75,40,76]},"packages/@n8n/agents/src/runtime/state/run-state.ts":{"11":[37,72,74,38,95,39,75,40,76],"14":[37,72,74,38,95,39,75,40,76],"18":[37,72,74,38,95,39,75,40,76],"22":[37,72,74,38,95,39,75,40,76],"29":[37,72,74,38,95,39,75,40,76],"42":[37,72,74,38,95,39,75,40,76],"47":[37,72,74,38,95,39,75,40,76],"57":[37,72,74,38,95,39,75,40,76],"67":[37,72,74,38,95,39,75,40,76],"75":[37,72,74,38,95,39,75,40,76],"84":[37,72,74,38,95,39,75,40,76],"98":[37,72,74,38,95,39,75,40,76],"108":[37,72,74,38,95,39,75,40,76]},"packages/@n8n/agents/src/runtime/streaming/stream-session.ts":{"45":[37,72,74,95,39,75,40,76],"52":[37,72,74,95,39,75,40,76],"61":[37,72,74,95,39,75,40,76],"71":[37,72,74,95,39,75,40,76],"76":[37,72,74,95,39,75,40,76],"88":[37,72,74,95,39,75,40,76],"89":[37,72,74,95,39,75,40,76],"111":[37,72,74,95,39,75,40,76]},"packages/@n8n/agents/src/runtime/streaming/stream-writer-guard.ts":{"15":[37,72,74,95,39,75,40,76],"17":[37,72,74,95,39,75,40,76],"20":[37,72,74,95,39,75,40,76],"23":[37,72,74,95,39,75,40,76],"28":[37,72,74,95,39,75,40,76],"38":[37,72,74,95,39,75,40,76],"55":[37,72,74,95,39,75,40,76]},"packages/@n8n/agents/src/runtime/streaming/stream.ts":{"12":[37,72,74,95,39,75,40,76],"21":[37,72,74,95,39,75,40,76],"74":[37,72,74,95,39,75,40,76]},"packages/@n8n/agents/src/runtime/telemetry/runtime-telemetry.ts":{"32":[37,72,74,95,39,75,40,76],"40":[37,72,74,95,39,75,40,76],"48":[37,72,74,95,39,75,40,76],"56":[37,72,74,95,39,75,40,76],"67":[37,72,74,95,39,75,40,76],"79":[37,72,74,95,39,75,40,76],"90":[37,72,74,95,39,75,40,76],"97":[37,72,74,95,39,75,40,76],"101":[37,72,74,95,39,75,40,76],"102":[37,72,74,95,39,75,40,76],"112":[37,72,74,95,39,75,40,76],"136":[37,72,74,95,39,75,40,76],"144":[37,72,74,95,39,75,40,76],"155":[37,72,74,95,39,75,40,76],"160":[37,72,74,95,39,75,40,76],"168":[37,72,74,95,39,75,40,76],"194":[37,72,74,95,39,75,40,76],"208":[37,72,74,95,39,75,40,76],"239":[37,72,74,95,39,75,40,76],"262":[37,72,74,95,39,75,40,76],"299":[37,72,74,95,39,75,40,76],"304":[37,72,74,95,39,75,40,76],"308":[37,72,74,95,39,75,40,76]},"packages/@n8n/agents/src/runtime/telemetry/telemetry-options.ts":{"11":[37,72,74,95,39,75,40,76]},"packages/@n8n/agents/src/runtime/tools/deferred-tool-manager.ts":{"42":[37,72,74,95,39,75,40,76],"47":[37,72,74,95,39,75,40,76],"52":[37,72,74,95,39,75,40,76],"90":[37,72,74,95,39,75,40,76],"106":[37,72,74,95,39,75,40,76],"110":[37,72,74,95,39,75,40,76],"114":[37,72,74,95,39,75,40,76],"118":[37,72,74,95,39,75,40,76],"123":[37,72,74,95,39,75,40,76],"125":[37,72,74,95,39,75,40,76],"126":[37,72,74,95,39,75,40,76],"129":[37,72,74,95,39,75,40,76],"144":[37,72,74,95,39,75,40,76],"151":[37,72,74,95,39,75,40,76],"158":[37,72,74,95,39,75,40,76],"165":[37,72,74,95,39,75,40,76],"172":[37,72,74,95,39,75,40,76],"174":[37,72,74,95,39,75,40,76],"178":[37,72,74,95,39,75,40,76],"183":[37,72,74,95,39,75,40,76],"187":[37,72,74,95,39,75,40,76],"191":[37,72,74,95,39,75,40,76],"220":[37,72,74,95,39,75,40,76],"228":[37,72,74,95,39,75,40,76],"239":[37,72,74,95,39,75,40,76]},"packages/@n8n/agents/src/runtime/tools/delegate-sub-agent-tool.ts":{"229":[37,72,74,95,39,75,40,76],"254":[37,72,74,95,39,75,40,76],"269":[37,72,74,95,39,75,40,76],"276":[37,72,74,95,39,75,40,76],"314":[37,72,74,95,39,75,40,76],"338":[37,72,74,95,39,75,40,76],"341":[37,72,74,95,39,75,40,76],"386":[37,72,74,95,39,75,40,76],"395":[37,72,74,95,39,75,40,76],"399":[37,72,74,95,39,75,40,76],"406":[37,72,74,95,39,75,40,76],"413":[37,72,74,95,39,75,40,76],"438":[37,72,74,95,39,75,40,76],"483":[37,72,74,95,39,75,40,76],"511":[37,72,74,95,39,75,40,76],"523":[37,72,74,95,39,75,40,76],"546":[37,72,74,95,39,75,40,76],"558":[37,72,74,95,39,75,40,76],"562":[37,72,74,95,39,75,40,76],"585":[37,72,74,95,39,75,40,76],"620":[37,72,74,95,39,75,40,76],"633":[37,72,74,95,39,75,40,76],"647":[37,72,74,95,39,75,40,76],"680":[37,72,74,95,39,75,40,76],"687":[37,72,74,95,39,75,40,76],"688":[37,72,74,95,39,75,40,76]},"packages/@n8n/agents/src/runtime/tools/tool-adapter.ts":{"34":[37,72,74,38,95,39,75,40,76],"47":[37,72,74,38,95,39,75,40,76],"68":[37,72,74,38,95,39,75,40,76],"103":[37,72,74,38,95,39,75,40,76],"118":[37,72,74,38,95,39,75,40,76],"152":[37,72,74,38,95,39,75,40,76],"158":[37,72,74,38,95,39,75,40,76]},"packages/@n8n/agents/src/runtime/tools/tool-call-executor.ts":{"145":[37,72,74,38,95,39,75,40,76],"149":[37,72,74,38,95,39,75,40,76],"156":[37,72,74,38,95,39,75,40,76],"178":[37,72,74,38,95,39,75,40,76],"180":[37,72,74,38,95,39,75,40,76],"184":[37,72,74,38,95,39,75,40,76],"188":[37,72,74,38,95,39,75,40,76],"192":[37,72,74,38,95,39,75,40,76],"197":[37,72,74,38,95,39,75,40,76],"204":[37,72,74,38,95,39,75,40,76],"251":[37,72,74,38,95,39,75,40,76],"291":[37,72,74,38,95,39,75,40,76],"296":[37,72,74,38,95,39,75,40,76],"297":[37,72,74,38,95,39,75,40,76],"331":[37,72,74,38,95,39,75,40,76],"464":[37,72,74,38,95,39,75,40,76],"637":[37,72,74,38,95,39,75,40,76],"692":[37,72,74,38,95,39,75,40,76],"705":[37,72,74,38,95,39,75,40,76],"711":[37,72,74,38,95,39,75,40,76],"713":[37,72,74,38,95,39,75,40,76],"719":[37,72,74,38,95,39,75,40,76],"736":[37,72,74,38,95,39,75,40,76],"760":[37,72,74,38,95,39,75,40,76],"776":[37,72,74,38,95,39,75,40,76],"797":[37,72,74,38,95,39,75,40,76],"799":[37,72,74,38,95,39,75,40,76],"803":[37,72,74,38,95,39,75,40,76],"814":[37,72,74,38,95,39,75,40,76],"837":[37,72,74,38,95,39,75,40,76]},"packages/@n8n/agents/src/sdk/abort.ts":{"6":[37,72,74,95,39,75,40,76],"12":[37,72,74,95,39,75,40,76],"20":[37,72,74,95,39,75,40,76],"35":[37,72,74,95,39,75,40,76],"39":[37,72,74,95,39,75,40,76],"46":[37,72,74,95,39,75,40,76],"47":[37,72,74,95,39,75,40,76]},"packages/@n8n/agents/src/sdk/agent.ts":{"213":[37,72,74,38,95,39,75,40,76],"217":[37,72,74,38,95,39,75,40,76],"221":[37,72,74,38,95,39,75,40,76],"237":[37,72,74,38,95,39,75,40,76],"250":[37,72,74,38,95,39,75,40,76],"256":[37,72,74,38,95,39,75,40,76],"263":[37,72,74,38,95,39,75,40,76],"265":[37,72,74,38,95,39,75,40,76],"274":[37,72,74,38,95,39,75,40,76],"282":[37,72,74,38,95,39,75,40,76],"300":[37,72,74,38,95,39,75,40,76],"309":[37,72,74,38,95,39,75,40,76],"320":[37,72,74,38,95,39,75,40,76],"326":[37,72,74,38,95,39,75,40,76],"331":[37,72,74,38,95,39,75,40,76],"357":[37,72,74,38,95,39,75,40,76],"363":[37,72,74,38,95,39,75,40,76],"370":[37,72,74,38,95,39,75,40,76],"376":[37,72,74,38,95,39,75,40,76],"382":[37,72,74,38,95,39,75,40,76],"404":[37,72,74,38,95,39,75,40,76],"431":[37,72,74,38,95,39,75,40,76],"453":[37,72,74,38,95,39,75,40,76],"473":[37,72,74,38,95,39,75,40,76],"479":[37,72,74,38,95,39,75,40,76],"492":[37,72,74,38,95,39,75,40,76],"503":[37,72,74,38,95,39,75,40,76],"515":[37,72,74,38,95,39,75,40,76],"538":[37,72,74,38,95,39,75,40,76],"553":[37,72,74,38,95,39,75,40,76],"568":[37,72,74,38,95,39,75,40,76],"570":[37,72,74,38,95,39,75,40,76],"593":[37,72,74,38,95,39,75,40,76],"599":[37,72,74,38,95,39,75,40,76],"607":[37,72,74,38,95,39,75,40,76],"624":[37,72,74,38,95,39,75,40,76],"640":[37,72,74,38,95,39,75,40,76],"667":[37,72,74,38,95,39,75,40,76],"681":[37,72,74,38,95,39,75,40,76],"698":[37,72,74,38,95,39,75,40,76],"704":[37,72,74,38,95,39,75,40,76],"709":[37,72,74,38,95,39,75,40,76],"724":[37,72,74,38,95,39,75,40,76],"751":[37,72,74,38,95,39,75,40,76],"781":[37,72,74,38,95,39,75,40,76],"797":[37,72,74,38,95,39,75,40,76],"809":[37,72,74,38,95,39,75,40,76],"819":[37,72,74,38,95,39,75,40,76],"836":[37,72,74,38,95,39,75,40,76],"840":[37,72,74,38,95,39,75,40,76],"847":[37,72,74,38,95,39,75,40,76],"860":[37,72,74,38,95,39,75,40,76],"866":[37,72,74,38,95,39,75,40,76],"867":[37,72,74,38,95,39,75,40,76],"880":[37,72,74,38,95,39,75,40,76],"894":[37,72,74,38,95,39,75,40,76],"904":[37,72,74,38,95,39,75,40,76],"910":[37,72,74,38,95,39,75,40,76],"916":[37,72,74,38,95,39,75,40,76],"938":[37,72,74,38,95,39,75,40,76],"939":[37,72,74,38,95,39,75,40,76],"940":[37,72,74,38,95,39,75,40,76],"950":[37,72,74,38,95,39,75,40,76],"962":[37,72,74,38,95,39,75,40,76],"987":[37,72,74,38,95,39,75,40,76],"988":[37,72,74,38,95,39,75,40,76],"1000":[37,72,74,38,95,39,75,40,76],"1084":[37,72,74,38,95,39,75,40,76],"1094":[37,72,74,38,95,39,75,40,76],"1108":[37,72,74,38,95,39,75,40,76],"1132":[37,72,74,38,95,39,75,40,76],"1143":[37,72,74,38,95,39,75,40,76],"1207":[37,72,74,38,95,39,75,40,76],"1212":[37,72,74,38,95,39,75,40,76],"1218":[37,72,74,38,95,39,75,40,76],"1229":[37,72,74,38,95,39,75,40,76],"1232":[37,72,74,38,95,39,75,40,76],"1237":[37,72,74,38,95,39,75,40,76],"1252":[37,72,74,38,95,39,75,40,76],"1270":[37,72,74,38,95,39,75,40,76],"1277":[37,72,74,38,95,39,75,40,76],"1286":[37,72,74,38,95,39,75,40,76],"1290":[37,72,74,38,95,39,75,40,76],"1295":[37,72,74,38,95,39,75,40,76],"1298":[37,72,74,38,95,39,75,40,76]},"packages/@n8n/agents/src/sdk/cancellation.ts":{"18":[37,72,39,40],"22":[37,72,39,40]},"packages/@n8n/agents/src/sdk/catalog.ts":{"82":[37,72,74,95,39,75,40,76],"96":[37,72,74,95,39,75,40,76],"128":[37,72,74,95,39,75,40,76],"200":[37,72,74,95,39,75,40,76],"204":[37,72,74,95,39,75,40,76],"208":[37,72,74,95,39,75,40,76],"226":[37,72,74,95,39,75,40,76],"243":[37,72,74,95,39,75,40,76],"263":[37,72,74,95,39,75,40,76]},"packages/@n8n/agents/src/sdk/guardrails/patterns.ts":{"31":[37,72,74,95,39,75,40,76],"39":[37,72,74,95,39,75,40,76],"45":[37,72,74,95,39,75,40,76],"72":[37,72,74,95,39,75,40,76],"76":[37,72,74,95,39,75,40,76],"92":[37,72,74,95,39,75,40,76],"108":[37,72,74,95,39,75,40,76],"128":[37,72,74,95,39,75,40,76],"139":[37,72,74,95,39,75,40,76],"146":[37,72,74,95,39,75,40,76],"149":[37,72,74,95,39,75,40,76],"184":[37,72,74,95,39,75,40,76],"233":[37,72,74,95,39,75,40,76],"236":[37,72,74,95,39,75,40,76]},"packages/@n8n/agents/src/sdk/guardrails/redactor.ts":{"32":[37,72,74,95,39,75,40,76],"44":[37,72,74,95,39,75,40,76],"56":[37,72,74,95,39,75,40,76],"57":[37,72,74,95,39,75,40,76],"67":[37,72,74,95,39,75,40,76],"87":[37,72,74,95,39,75,40,76],"98":[37,72,74,95,39,75,40,76],"104":[37,72,74,95,39,75,40,76],"109":[37,72,74,95,39,75,40,76],"122":[37,72,74,95,39,75,40,76],"163":[37,72,74,95,39,75,40,76],"177":[37,72,74,95,39,75,40,76]},"packages/@n8n/agents/src/sdk/guardrails/streaming-redactor.ts":{"38":[37,72,74,95,39,75,40,76],"46":[37,72,74,95,39,75,40,76],"79":[37,72,74,95,39,75,40,76]},"packages/@n8n/agents/src/sdk/memory.ts":{"14":[37,72,74,95,39,75,40,76],"38":[37,72,74,95,39,75,40,76],"58":[37,72,74,95,39,75,40,76],"87":[37,72,74,95,39,75,40,76],"126":[37,72,74,95,39,75,40,76],"181":[37,72,74,95,39,75,40,76],"191":[37,72,74,95,39,75,40,76],"209":[37,72,74,95,39,75,40,76],"220":[37,72,74,95,39,75,40,76],"228":[37,72,74,95,39,75,40,76]},"packages/@n8n/agents/src/sdk/message.ts":{"3":[37,72,74,95,39,75,40,76],"19":[37,72,74,95,39,75,40,76],"23":[37,72,74,95,39,75,40,76]},"packages/@n8n/agents/src/sdk/telemetry.ts":{"16":[37,72,74,95,75,40,76],"18":[37,72,74,95,75,40,76],"41":[37,72,74,95,75,40,76],"57":[37,72,74,95,75,40,76],"65":[37,72,74,95,75,40,76],"69":[37,72,74,95,75,40,76],"73":[37,72,74,95,75,40,76],"77":[37,72,74,95,75,40,76],"81":[37,72,74,95,75,40,76],"85":[37,72,74,95,75,40,76],"96":[37,72,74,95,75,40,76],"145":[37,72,74,95,75,40,76],"180":[37,72,74,95,75,40,76],"186":[37,72,74,95,75,40,76],"194":[37,72,74,95,75,40,76],"199":[37,72,74,95,75,40,76],"205":[37,72,74,95,75,40,76],"211":[37,72,74,95,75,40,76],"217":[37,72,74,95,75,40,76],"223":[37,72,74,95,75,40,76],"229":[37,72,74,95,75,40,76],"239":[37,72,74,95,75,40,76],"245":[37,72,74,95,75,40,76],"256":[37,72,74,95,75,40,76],"268":[37,72,74,95,75,40,76],"274":[37,72,74,95,75,40,76],"289":[37,72,74,95,75,40,76],"307":[37,72,74,95,75,40,76],"314":[37,72,74,95,75,40,76]},"packages/@n8n/agents/src/sdk/tool.ts":{"31":[37,72,74,95,39,75,40,76],"45":[37,72,74,95,39,75,40,76],"61":[37,72,74,95,39,75,40,76],"73":[37,72,74,95,39,75,40,76],"168":[37,72,74,95,39,75,40,76],"173":[37,72,74,95,39,75,40,76],"187":[37,72,74,95,39,75,40,76],"193":[37,72,74,95,39,75,40,76],"200":[37,72,74,95,39,75,40,76],"207":[37,72,74,95,39,75,40,76],"214":[37,72,74,95,39,75,40,76],"224":[37,72,74,95,39,75,40,76],"234":[37,72,74,95,39,75,40,76],"246":[37,72,74,95,39,75,40,76],"255":[37,72,74,95,39,75,40,76],"261":[37,72,74,95,39,75,40,76],"267":[37,72,74,95,39,75,40,76],"278":[37,72,74,95,39,75,40,76],"289":[37,72,74,95,39,75,40,76],"354":[37,72,74,95,39,75,40,76],"390":[37,72,74,95,39,75,40,76]},"packages/@n8n/agents/src/skills/prompt.ts":{"7":[37,72,74,95,39,75,40,76],"14":[37,72,74,95,39,75,40,76],"44":[37,72,74,95,39,75,40,76],"56":[37,72,74,95,39,75,40,76],"60":[37,72,74,95,39,75,40,76]},"packages/@n8n/agents/src/skills/registry.ts":{"24":[37,72,74,95,39,75,40,76],"34":[37,72,74,95,39,75,40,76],"36":[37,72,74,95,39,75,40,76],"40":[37,72,74,95,39,75,40,76],"47":[37,72,74,95,39,75,40,76],"64":[37,72,74,95,39,75,40,76],"69":[37,72,74,95,39,75,40,76],"79":[37,72,74,95,39,75,40,76],"82":[37,72,74,95,39,75,40,76],"89":[37,72,74,95,39,75,40,76],"95":[37,72,74,95,39,75,40,76],"98":[37,72,74,95,39,75,40,76],"102":[37,72,74,95,39,75,40,76],"123":[37,72,74,95,39,75,40,76],"126":[37,72,74,95,39,75,40,76],"154":[37,72,74,95,39,75,40,76],"158":[37,72,74,95,39,75,40,76],"167":[37,72,74,95,39,75,40,76],"173":[37,72,74,95,39,75,40,76],"207":[37,72,74,95,39,75,40,76],"233":[37,72,74,95,39,75,40,76],"254":[37,72,74,95,39,75,40,76],"283":[37,72,74,95,39,75,40,76],"289":[37,72,74,95,39,75,40,76],"307":[37,72,74,95,39,75,40,76],"324":[37,72,74,95,39,75,40,76],"335":[37,72,74,95,39,75,40,76],"346":[37,72,74,95,39,75,40,76],"351":[37,72,74,95,39,75,40,76],"354":[37,72,74,95,39,75,40,76],"357":[37,72,74,95,39,75,40,76],"366":[37,72,74,95,39,75,40,76],"372":[37,72,74,95,39,75,40,76],"389":[37,72,74,95,39,75,40,76],"407":[37,72,74,95,39,75,40,76],"411":[37,72,74,95,39,75,40,76],"416":[37,72,74,95,39,75,40,76],"420":[37,72,74,95,39,75,40,76],"424":[37,72,74,95,39,75,40,76],"428":[37,72,74,95,39,75,40,76],"438":[37,72,74,95,39,75,40,76],"445":[37,72,74,95,39,75,40,76],"452":[37,72,74,95,39,75,40,76],"459":[37,72,74,95,39,75,40,76],"464":[37,72,74,95,39,75,40,76],"465":[37,72,74,95,39,75,40,76],"466":[37,72,74,95,39,75,40,76],"472":[37,72,74,95,39,75,40,76],"478":[37,72,74,95,39,75,40,76],"482":[37,72,74,95,39,75,40,76]},"packages/@n8n/agents/src/skills/tools.ts":{"49":[37,72,74,95,39,75,40,41,76],"63":[37,72,74,95,39,75,40,41,76],"100":[37,72,74,95,39,75,40,41,76],"104":[37,72,74,95,39,75,40,41,76],"112":[37,72,74,95,39,75,40,41,76],"124":[37,72,74,95,39,75,40,41,76],"130":[37,72,74,95,39,75,40,41,76],"147":[37,72,74,95,39,75,40,41,76],"216":[37,72,74,95,39,75,40,41,76],"221":[37,72,74,95,39,75,40,41,76],"222":[37,72,74,95,39,75,40,41,76],"238":[37,72,74,95,39,75,40,41,76],"243":[37,72,74,95,39,75,40,41,76],"248":[37,72,74,95,39,75,40,41,76],"254":[37,72,74,95,39,75,40,41,76],"266":[37,72,74,95,39,75,40,41,76],"271":[37,72,74,95,39,75,40,41,76],"277":[37,72,74,95,39,75,40,41,76],"288":[37,72,74,95,39,75,40,41,76],"292":[37,72,74,95,39,75,40,41,76],"299":[37,72,74,95,39,75,40,41,76]},"packages/@n8n/agents/src/skills/validator.ts":{"43":[37,72,74,95,39,75,40,76],"56":[37,72,74,95,39,75,40,76],"85":[37,72,74,95,39,75,40,76],"121":[37,72,74,95,39,75,40,76],"141":[37,72,74,95,39,75,40,76],"189":[37,72,74,95,39,75,40,76],"206":[37,72,74,95,39,75,40,76],"215":[37,72,74,95,39,75,40,76],"235":[37,72,74,95,39,75,40,76],"256":[37,72,74,95,39,75,40,76],"257":[37,72,74,95,39,75,40,76],"270":[37,72,74,95,39,75,40,76],"289":[37,72,74,95,39,75,40,76],"308":[37,72,74,95,39,75,40,76],"359":[37,72,74,95,39,75,40,76],"383":[37,72,74,95,39,75,40,76],"404":[37,72,74,95,39,75,40,76],"420":[37,72,74,95,39,75,40,76],"462":[37,72,74,95,39,75,40,76],"482":[37,72,74,95,39,75,40,76],"486":[37,72,74,95,39,75,40,76]},"packages/@n8n/agents/src/utils/parse.ts":{"13":[37,72,39,40],"26":[37,72,39,40]},"packages/@n8n/agents/src/utils/zod.ts":{"6":[37,72,74,95,39,75,40,76],"14":[37,72,74,95,39,75,40,76]},"packages/@n8n/agents/src/workspace/filesystem/base-filesystem.ts":{"39":[37,72,74,95,39,75,40,41,76],"44":[37,72,74,95,39,75,40,41,76],"64":[37,72,74,95,39,75,40,41,76],"83":[37,72,74,95,39,75,40,41,76],"87":[37,72,74,95,39,75,40,41,76],"96":[37,72,74,95,39,75,40,41,76],"114":[37,72,74,95,39,75,40,41,76],"139":[37,72,74,95,39,75,40,41,76]},"packages/@n8n/agents/src/workspace/filesystem/n8n-sandbox-filesystem.ts":{"22":[37,72,74,95,39,75,40,41,76],"37":[37,72,74,95,39,75,40,41,76],"42":[37,72,74,95,39,75,40,41,76],"50":[37,72,74,95,39,75,40,41,76],"58":[37,72,74,95,39,75,40,41,76],"64":[37,72,74,95,39,75,40,41,76],"65":[37,72,74,95,39,75,40,41,76],"74":[37,72,74,95,39,75,40,41,76],"75":[37,72,74,95,39,75,40,41,76],"86":[37,72,74,95,39,75,40,41,76],"87":[37,72,74,95,39,75,40,41,76],"92":[37,72,74,95,39,75,40,41,76],"93":[37,72,74,95,39,75,40,41,76],"101":[37,72,74,95,39,75,40,41,76],"102":[37,72,74,95,39,75,40,41,76],"112":[37,72,74,95,39,75,40,41,76],"113":[37,72,74,95,39,75,40,41,76],"122":[37,72,74,95,39,75,40,41,76],"123":[37,72,74,95,39,75,40,41,76],"128":[37,72,74,95,39,75,40,41,76],"132":[37,72,74,95,39,75,40,41,76],"133":[37,72,74,95,39,75,40,41,76],"135":[37,72,74,95,39,75,40,41,76],"143":[37,72,74,95,39,75,40,41,76],"155":[37,72,74,95,39,75,40,41,76],"156":[37,72,74,95,39,75,40,41,76]},"packages/@n8n/agents/src/workspace/index.ts":{"1":[37,72,74,95,39,75,40,76],"7":[37,72,74,95,39,75,40,76],"8":[37,72,74,95,39,75,40,76]},"packages/@n8n/agents/src/workspace/lifecycle.ts":{"14":[37,72,74,95,39,75,40,76]},"packages/@n8n/agents/src/workspace/sandbox/base-sandbox.ts":{"17":[37,72,74,95,39,75,40,41,76],"37":[37,72,74,95,39,75,40,41,76],"50":[37,72,74,95,39,75,40,41,76],"70":[37,72,74,95,39,75,40,41,76],"74":[37,72,74,95,39,75,40,41,76],"86":[37,72,74,95,39,75,40,41,76],"94":[37,72,74,95,39,75,40,41,76],"95":[37,72,74,95,39,75,40,41,76],"118":[37,72,74,95,39,75,40,41,76],"123":[37,72,74,95,39,75,40,41,76],"128":[37,72,74,95,39,75,40,41,76],"132":[37,72,74,95,39,75,40,41,76],"135":[37,72,74,95,39,75,40,41,76],"142":[37,72,74,95,39,75,40,41,76],"154":[37,72,74,95,39,75,40,41,76],"163":[37,72,74,95,39,75,40,41,76],"167":[37,72,74,95,39,75,40,41,76],"183":[37,72,74,95,39,75,40,41,76],"199":[37,72,74,95,39,75,40,41,76]},"packages/@n8n/agents/src/workspace/sandbox/create-workspace.ts":{"12":[37,72,74,95,39,75,40,76],"19":[37,72,74,95,39,75,40,76],"73":[37,72,74,95,39,75,40,76]},"packages/@n8n/agents/src/workspace/sandbox/index.ts":{"21":[37,72,74,95,39,75,40,41,76]},"packages/@n8n/agents/src/workspace/sandbox/n8n-sandbox-sandbox.ts":{"16":[37,72,74,95,39,75,40,41,76],"20":[37,72,74,95,39,75,40,41,76],"22":[37,72,74,95,39,75,40,41,76],"47":[37,72,74,95,39,75,40,41,76],"57":[37,72,74,95,39,75,40,41,76],"61":[37,72,74,95,39,75,40,41,76],"75":[37,72,74,95,39,75,40,41,76],"85":[37,72,74,95,39,75,40,41,76],"90":[37,72,74,95,39,75,40,41,76],"107":[37,72,74,95,39,75,40,41,76],"115":[37,72,74,95,39,75,40,41,76],"144":[37,72,74,95,39,75,40,41,76],"149":[37,72,74,95,39,75,40,41,76],"159":[37,72,74,95,39,75,40,41,76],"165":[37,72,74,95,39,75,40,41,76],"170":[37,72,74,95,39,75,40,41,76]},"packages/@n8n/agents/src/workspace/sandbox/run-in-sandbox.ts":{"37":[37,72,74,95,39,75,40,76],"60":[37,72,74,95,39,75,40,76]},"packages/@n8n/agents/src/workspace/sandbox/workspace-root.ts":{"10":[37,72,74,95,39,75,40,76],"29":[37,72,74,95,39,75,40,76],"40":[37,72,74,95,39,75,40,76],"47":[37,72,74,95,39,75,40,76],"59":[37,72,74,95,39,75,40,76]},"packages/@n8n/agents/src/workspace/tools/append-file.ts":{"7":[37,72,74,95,39,75,40,76],"21":[37,72,74,95,39,75,40,76]},"packages/@n8n/agents/src/workspace/tools/batch-str-replace-file.ts":{"40":[37,72,74,95,39,75,40,76],"47":[37,72,74,95,39,75,40,76],"53":[37,72,74,95,39,75,40,76],"60":[37,72,74,95,39,75,40,76]},"packages/@n8n/agents/src/workspace/tools/copy-file.ts":{"7":[37,72,74,95,39,75,40,76],"25":[37,72,74,95,39,75,40,76]},"packages/@n8n/agents/src/workspace/tools/delete-file.ts":{"7":[37,72,74,95,39,75,40,76],"25":[37,72,74,95,39,75,40,76]},"packages/@n8n/agents/src/workspace/tools/execute-command.ts":{"7":[37,72,74,95,39,75,40,76],"26":[37,72,74,95,39,75,40,76]},"packages/@n8n/agents/src/workspace/tools/file-stat.ts":{"7":[37,72,74,95,39,75,40,76],"25":[37,72,74,95,39,75,40,76]},"packages/@n8n/agents/src/workspace/tools/list-files.ts":{"7":[37,72,74,95,39,75,40,76],"29":[37,72,74,95,39,75,40,76]},"packages/@n8n/agents/src/workspace/tools/mkdir.ts":{"7":[37,72,74,95,39,75,40,76],"21":[37,72,74,95,39,75,40,76]},"packages/@n8n/agents/src/workspace/tools/move-file.ts":{"7":[37,72,74,95,39,75,40,76],"25":[37,72,74,95,39,75,40,76]},"packages/@n8n/agents/src/workspace/tools/read-file.ts":{"7":[37,72,74,95,39,75,40,76],"21":[37,72,74,95,39,75,40,76]},"packages/@n8n/agents/src/workspace/tools/rmdir.ts":{"7":[37,72,74,95,39,75,40,76],"25":[37,72,74,95,39,75,40,76]},"packages/@n8n/agents/src/workspace/tools/str-replace-file.ts":{"23":[37,72,74,95,39,75,40,76],"30":[37,72,74,95,39,75,40,76],"37":[37,72,74,95,39,75,40,76]},"packages/@n8n/agents/src/workspace/tools/workspace-tools.ts":{"23":[37,72,74,95,39,75,40,76]},"packages/@n8n/agents/src/workspace/tools/write-file.ts":{"7":[37,72,74,95,39,75,40,76],"25":[37,72,74,95,39,75,40,76]},"packages/@n8n/agents/src/workspace/workspace.ts":{"24":[37,72,74,95,39,75,40,41,76],"31":[37,72,74,95,39,75,40,41,76],"35":[37,72,74,95,39,75,40,41,76],"39":[37,72,74,95,39,75,40,41,76],"43":[37,72,74,95,39,75,40,41,76],"59":[37,72,74,95,39,75,40,41,76],"80":[37,72,74,95,39,75,40,41,76],"93":[37,72,74,95,39,75,40,41,76],"97":[37,72,74,95,39,75,40,41,76],"126":[37,72,74,95,39,75,40,41,76]},"packages/@n8n/ai-utilities/src/node-catalog/index.ts":{"14":[37,72,74,95,39,75,40,41,76,172,164],"32":[37,72,74,95,39,75,40,41,76,172,164]},"packages/@n8n/ai-utilities/src/node-catalog/suggested.ts":{"25":[37,72,74,95,39,75,40,41,76,172,164],"34":[37,72,74,95,39,75,40,41,76,172,164],"59":[37,72,74,95,39,75,40,41,76,172,164],"93":[37,72,74,95,39,75,40,41,76,172,164]},"packages/@n8n/api-types/src/schemas/timezone.schema.ts":{"3":[3,4,5,37,72,74,95,39,75,40,76]},"packages/@n8n/backend-network/src/http/undici/transport.ts":{"86":[20,27,37,72,74,95,39,75,40,76,179],"101":[20,27,37,72,74,95,39,75,40,76,179],"123":[20,27,37,72,74,95,39,75,40,76,179],"131":[20,27,37,72,74,95,39,75,40,76,179],"148":[20,27,37,72,74,95,39,75,40,76,179],"156":[20,27,37,72,74,95,39,75,40,76,179],"159":[20,27,37,72,74,95,39,75,40,76,179],"161":[20,27,37,72,74,95,39,75,40,76,179],"165":[20,27,37,72,74,95,39,75,40,76,179],"167":[20,27,37,72,74,95,39,75,40,76,179],"180":[20,27,37,72,74,95,39,75,40,76,179],"181":[20,27,37,72,74,95,39,75,40,76,179],"189":[20,27,37,72,74,95,39,75,40,76,179],"196":[20,27,37,72,74,95,39,75,40,76,179],"216":[20,27,37,72,74,95,39,75,40,76,179],"219":[20,27,37,72,74,95,39,75,40,76,179],"224":[20,27,37,72,74,95,39,75,40,76,179],"225":[20,27,37,72,74,95,39,75,40,76,179],"248":[20,27,37,72,74,95,39,75,40,76,179],"259":[20,27,37,72,74,95,39,75,40,76,179],"268":[20,27,37,72,74,95,39,75,40,76,179]},"packages/@n8n/db/src/repositories/ai-builder-temporary-workflow.repository.ts":{"9":[37,72,74,95,39,75,40,41,76],"13":[37,72,74,95,39,75,40,41,76],"23":[37,72,74,95,39,75,40,41,76],"27":[37,72,74,95,39,75,40,41,76],"34":[37,72,74,95,39,75,40,41,76]},"packages/@n8n/db/src/repositories/base-repository.ts":{"22":[9,37,72,73,74,39,75,40,76,172]},"packages/@n8n/db/src/repositories/instance-credential-assignment.repository.ts":{"10":[37,72,73,74,95,39,75,40,76],"14":[37,72,73,74,95,39,75,40,76],"28":[37,72,73,74,95,39,75,40,76],"53":[37,72,73,74,95,39,75,40,76],"54":[37,72,73,74,95,39,75,40,76],"63":[37,72,73,74,95,39,75,40,76],"68":[37,72,73,74,95,39,75,40,76],"77":[37,72,73,74,95,39,75,40,76],"83":[37,72,73,74,95,39,75,40,76],"86":[37,72,73,74,95,39,75,40,76],"101":[37,72,73,74,95,39,75,40,76]},"packages/@n8n/db/src/repositories/settings.repository.ts":{"11":[37,72,73,74,38,39,75,40,76],"15":[37,72,73,74,38,39,75,40,76],"20":[37,72,73,74,38,39,75,40,76],"24":[37,72,73,74,38,39,75,40,76],"33":[37,72,73,74,38,39,75,40,76],"37":[37,72,73,74,38,39,75,40,76]},"packages/@n8n/db/src/services/db-lock.service.ts":{"15":[37,72,73,74,39,75,40,76],"52":[37,72,73,74,39,75,40,76],"105":[37,72,73,74,39,75,40,76],"128":[37,72,73,74,39,75,40,76],"137":[37,72,73,74,39,75,40,76],"144":[37,72,73,74,39,75,40,76],"178":[37,72,73,74,39,75,40,76],"183":[37,72,73,74,39,75,40,76],"196":[37,72,73,74,39,75,40,76],"204":[37,72,73,74,39,75,40,76],"210":[37,72,73,74,39,75,40,76],"231":[37,72,73,74,39,75,40,76],"252":[37,72,73,74,39,75,40,76],"255":[37,72,73,74,39,75,40,76],"263":[37,72,73,74,39,75,40,76],"286":[37,72,73,74,39,75,40,76],"308":[37,72,73,74,39,75,40,76],"309":[37,72,73,74,39,75,40,76],"328":[37,72,73,74,39,75,40,76]},"packages/@n8n/db/src/services/typeorm-transaction.ts":{"16":[9,37,72,73,74,39,75,40,76,172],"21":[9,37,72,73,74,39,75,40,76,172],"29":[9,37,72,73,74,39,75,40,76,172],"36":[9,37,72,73,74,39,75,40,76,172],"59":[9,37,72,73,74,39,75,40,76,172]},"packages/@n8n/instance-ai/src/agent/apply-agent-thinking.ts":{"5":[37,72,74,95,39,75,40,76],"9":[37,72,74,95,39,75,40,76],"14":[37,72,74,95,39,75,40,76],"19":[37,72,74,95,39,75,40,76],"24":[37,72,74,95,39,75,40,76],"36":[37,72,74,95,39,75,40,76]},"packages/@n8n/instance-ai/src/agent/computer-use-prompt.ts":{"6":[37,72,74,95,39,75,40,76]},"packages/@n8n/instance-ai/src/agent/instance-agent.ts":{"30":[37,72,74,95,39,75,40,76],"51":[37,72,74,95,39,75,40,76],"83":[37,72,74,95,39,75,40,76],"92":[37,72,74,95,39,75,40,76],"95":[37,72,74,95,39,75,40,76],"158":[37,72,74,95,39,75,40,76]},"packages/@n8n/instance-ai/src/agent/mcp-tool-name-validation.ts":{"8":[37,72,74,95,39,75,40,76],"20":[37,72,74,95,39,75,40,76],"29":[37,72,74,95,39,75,40,76],"36":[37,72,74,95,39,75,40,76],"47":[37,72,74,95,39,75,40,76],"55":[37,72,74,95,39,75,40,76]},"packages/@n8n/instance-ai/src/agent/runtime-workspace.ts":{"10":[37,72,74,95,39,75,40,76]},"packages/@n8n/instance-ai/src/agent/sanitize-mcp-schemas.ts":{"33":[37,72,74,95,39,75,40,76],"95":[37,72,74,95,39,75,40,76],"115":[37,72,74,95,39,75,40,76],"191":[37,72,74,95,39,75,40,76],"213":[37,72,74,95,39,75,40,76],"231":[37,72,74,95,39,75,40,76],"249":[37,72,74,95,39,75,40,76],"266":[37,72,74,95,39,75,40,76],"283":[37,72,74,95,39,75,40,76],"302":[37,72,74,95,39,75,40,76],"338":[37,72,74,95,39,75,40,76],"339":[37,72,74,95,39,75,40,76],"342":[37,72,74,95,39,75,40,76],"345":[37,72,74,95,39,75,40,76],"352":[37,72,74,95,39,75,40,76],"366":[37,72,74,95,39,75,40,76],"379":[37,72,74,95,39,75,40,76],"380":[37,72,74,95,39,75,40,76],"385":[37,72,74,95,39,75,40,76],"394":[37,72,74,95,39,75,40,76],"402":[37,72,74,95,39,75,40,76],"416":[37,72,74,95,39,75,40,76],"450":[37,72,74,95,39,75,40,76],"451":[37,72,74,95,39,75,40,76],"473":[37,72,74,95,39,75,40,76],"488":[37,72,74,95,39,75,40,76],"490":[37,72,74,95,39,75,40,76],"503":[37,72,74,95,39,75,40,76],"524":[37,72,74,95,39,75,40,76],"676":[37,72,74,95,39,75,40,76],"697":[37,72,74,95,39,75,40,76],"713":[37,72,74,95,39,75,40,76]},"packages/@n8n/instance-ai/src/agent/shared-prompts.ts":{"22":[37,72,74,95,39,75,40,76]},"packages/@n8n/instance-ai/src/agent/sub-agent-briefing.ts":{"41":[37,72,74,95,39,75,40,76],"71":[37,72,74,95,39,75,40,76]},"packages/@n8n/instance-ai/src/agent/system-prompt.ts":{"28":[37,72,74,95,39,75,40,76],"41":[37,72,74,95,39,75,40,76],"48":[37,72,74,95,39,75,40,76],"70":[37,72,74,95,39,75,40,76],"84":[37,72,74,95,39,75,40,76],"92":[37,72,74,95,39,75,40,76],"96":[37,72,74,95,39,75,40,76],"118":[37,72,74,95,39,75,40,76]},"packages/@n8n/instance-ai/src/domain-access/domain-access-tracker.ts":{"60":[37,72,74,95,39,75,40,76],"74":[37,72,74,95,39,75,40,76],"85":[37,72,74,95,39,75,40,76],"90":[37,72,74,95,39,75,40,76],"95":[37,72,74,95,39,75,40,76],"99":[37,72,74,95,39,75,40,76],"108":[37,72,74,95,39,75,40,76],"113":[37,72,74,95,39,75,40,76],"119":[37,72,74,95,39,75,40,76],"124":[37,72,74,95,39,75,40,76]},"packages/@n8n/instance-ai/src/domain-access/domain-gating.ts":{"69":[37,72,74,95,39,75,40,76],"122":[37,72,74,95,39,75,40,76],"161":[37,72,74,95,39,75,40,76],"203":[37,72,74,95,39,75,40,76]},"packages/@n8n/instance-ai/src/domain-access/index.ts":{"1":[37,72,74,95,39,75,40,76]},"packages/@n8n/instance-ai/src/error-formatting.ts":{"20":[37,72,74,95,39,75,40,76],"31":[37,72,74,95,39,75,40,76],"46":[37,72,74,95,39,75,40,76],"51":[37,72,74,95,39,75,40,76],"64":[37,72,74,95,39,75,40,76],"66":[37,72,74,95,39,75,40,76],"69":[37,72,74,95,39,75,40,76],"76":[37,72,74,95,39,75,40,76],"90":[37,72,74,95,39,75,40,76],"101":[37,72,74,95,39,75,40,76],"110":[37,72,74,95,39,75,40,76],"117":[37,72,74,95,39,75,40,76],"121":[37,72,74,95,39,75,40,76],"128":[37,72,74,95,39,75,40,76],"132":[37,72,74,95,39,75,40,76],"157":[37,72,74,95,39,75,40,76],"171":[37,72,74,95,39,75,40,76]},"packages/@n8n/instance-ai/src/index.ts":{"47":[37,72,73,74,38,95,39,75,40,41,76],"49":[37,72,73,74,38,95,39,75,40,41,76],"52":[37,72,73,74,38,95,39,75,40,41,76],"53":[37,72,73,74,38,95,39,75,40,41,76],"58":[37,72,73,74,38,95,39,75,40,41,76],"60":[37,72,73,74,38,95,39,75,40,41,76],"67":[37,72,73,74,38,95,39,75,40,41,76],"77":[37,72,73,74,38,95,39,75,40,41,76],"86":[37,72,73,74,38,95,39,75,40,41,76],"89":[37,72,73,74,38,95,39,75,40,41,76],"92":[37,72,73,74,38,95,39,75,40,41,76],"95":[37,72,73,74,38,95,39,75,40,41,76],"97":[37,72,73,74,38,95,39,75,40,41,76],"99":[37,72,73,74,38,95,39,75,40,41,76],"102":[37,72,73,74,38,95,39,75,40,41,76],"104":[37,72,73,74,38,95,39,75,40,41,76],"106":[37,72,73,74,38,95,39,75,40,41,76],"108":[37,72,73,74,38,95,39,75,40,41,76],"110":[37,72,73,74,38,95,39,75,40,41,76],"113":[37,72,73,74,38,95,39,75,40,41,76],"115":[37,72,73,74,38,95,39,75,40,41,76],"116":[37,72,73,74,38,95,39,75,40,41,76],"118":[37,72,73,74,38,95,39,75,40,41,76],"121":[37,72,73,74,38,95,39,75,40,41,76],"124":[37,72,73,74,38,95,39,75,40,41,76],"126":[37,72,73,74,38,95,39,75,40,41,76],"127":[37,72,73,74,38,95,39,75,40,41,76],"129":[37,72,73,74,38,95,39,75,40,41,76],"132":[37,72,73,74,38,95,39,75,40,41,76],"135":[37,72,73,74,38,95,39,75,40,41,76],"138":[37,72,73,74,38,95,39,75,40,41,76],"141":[37,72,73,74,38,95,39,75,40,41,76],"144":[37,72,73,74,38,95,39,75,40,41,76],"147":[37,72,73,74,38,95,39,75,40,41,76],"150":[37,72,73,74,38,95,39,75,40,41,76],"153":[37,72,73,74,38,95,39,75,40,41,76],"156":[37,72,73,74,38,95,39,75,40,41,76],"159":[37,72,73,74,38,95,39,75,40,41,76],"162":[37,72,73,74,38,95,39,75,40,41,76],"165":[37,72,73,74,38,95,39,75,40,41,76],"167":[37,72,73,74,38,95,39,75,40,41,76],"169":[37,72,73,74,38,95,39,75,40,41,76],"172":[37,72,73,74,38,95,39,75,40,41,76],"175":[37,72,73,74,38,95,39,75,40,41,76],"178":[37,72,73,74,38,95,39,75,40,41,76],"181":[37,72,73,74,38,95,39,75,40,41,76],"191":[37,72,73,74,38,95,39,75,40,41,76],"192":[37,72,73,74,38,95,39,75,40,41,76],"194":[37,72,73,74,38,95,39,75,40,41,76],"211":[37,72,73,74,38,95,39,75,40,41,76],"215":[37,72,73,74,38,95,39,75,40,41,76],"220":[37,72,73,74,38,95,39,75,40,41,76],"223":[37,72,73,74,38,95,39,75,40,41,76],"226":[37,72,73,74,38,95,39,75,40,41,76],"229":[37,72,73,74,38,95,39,75,40,41,76],"232":[37,72,73,74,38,95,39,75,40,41,76],"236":[37,72,73,74,38,95,39,75,40,41,76],"239":[37,72,73,74,38,95,39,75,40,41,76],"243":[37,72,73,74,38,95,39,75,40,41,76],"247":[37,72,73,74,38,95,39,75,40,41,76],"251":[37,72,73,74,38,95,39,75,40,41,76],"264":[37,72,73,74,38,95,39,75,40,41,76],"266":[37,72,73,74,38,95,39,75,40,41,76],"287":[37,72,73,74,38,95,39,75,40,41,76],"291":[37,72,73,74,38,95,39,75,40,41,76],"295":[37,72,73,74,38,95,39,75,40,41,76],"298":[37,72,73,74,38,95,39,75,40,41,76],"301":[37,72,73,74,38,95,39,75,40,41,76],"306":[37,72,73,74,38,95,39,75,40,41,76],"310":[37,72,73,74,38,95,39,75,40,41,76],"314":[37,72,73,74,38,95,39,75,40,41,76],"318":[37,72,73,74,38,95,39,75,40,41,76],"320":[37,72,73,74,38,95,39,75,40,41,76],"323":[37,72,73,74,38,95,39,75,40,41,76],"326":[37,72,73,74,38,95,39,75,40,41,76],"330":[37,72,73,74,38,95,39,75,40,41,76],"343":[37,72,73,74,38,95,39,75,40,41,76],"346":[37,72,73,74,38,95,39,75,40,41,76],"350":[37,72,73,74,38,95,39,75,40,41,76],"353":[37,72,73,74,38,95,39,75,40,41,76],"356":[37,72,73,74,38,95,39,75,40,41,76],"359":[37,72,73,74,38,95,39,75,40,41,76],"362":[37,72,73,74,38,95,39,75,40,41,76],"365":[37,72,73,74,38,95,39,75,40,41,76],"368":[37,72,73,74,38,95,39,75,40,41,76],"371":[37,72,73,74,38,95,39,75,40,41,76],"372":[37,72,73,74,38,95,39,75,40,41,76],"375":[37,72,73,74,38,95,39,75,40,41,76],"377":[37,72,73,74,38,95,39,75,40,41,76],"378":[37,72,73,74,38,95,39,75,40,41,76],"381":[37,72,73,74,38,95,39,75,40,41,76],"385":[37,72,73,74,38,95,39,75,40,41,76],"389":[37,72,73,74,38,95,39,75,40,41,76],"393":[37,72,73,74,38,95,39,75,40,41,76],"395":[37,72,73,74,38,95,39,75,40,41,76],"396":[37,72,73,74,38,95,39,75,40,41,76],"399":[37,72,73,74,38,95,39,75,40,41,76],"403":[37,72,73,74,38,95,39,75,40,41,76],"405":[37,72,73,74,38,95,39,75,40,41,76],"408":[37,72,73,74,38,95,39,75,40,41,76],"410":[37,72,73,74,38,95,39,75,40,41,76],"411":[37,72,73,74,38,95,39,75,40,41,76],"412":[37,72,73,74,38,95,39,75,40,41,76],"415":[37,72,73,74,38,95,39,75,40,41,76],"419":[37,72,73,74,38,95,39,75,40,41,76],"422":[37,72,73,74,38,95,39,75,40,41,76],"426":[37,72,73,74,38,95,39,75,40,41,76],"429":[37,72,73,74,38,95,39,75,40,41,76],"432":[37,72,73,74,38,95,39,75,40,41,76],"435":[37,72,73,74,38,95,39,75,40,41,76],"439":[37,72,73,74,38,95,39,75,40,41,76],"442":[37,72,73,74,38,95,39,75,40,41,76],"444":[37,72,73,74,38,95,39,75,40,41,76],"450":[37,72,73,74,38,95,39,75,40,41,76],"453":[37,72,73,74,38,95,39,75,40,41,76],"457":[37,72,73,74,38,95,39,75,40,41,76],"462":[37,72,73,74,38,95,39,75,40,41,76],"464":[37,72,73,74,38,95,39,75,40,41,76],"475":[37,72,73,74,38,95,39,75,40,41,76],"477":[37,72,73,74,38,95,39,75,40,41,76],"496":[37,72,73,74,38,95,39,75,40,41,76],"503":[37,72,73,74,38,95,39,75,40,41,76],"517":[37,72,73,74,38,95,39,75,40,41,76],"519":[37,72,73,74,38,95,39,75,40,41,76],"522":[37,72,73,74,38,95,39,75,40,41,76],"525":[37,72,73,74,38,95,39,75,40,41,76],"529":[37,72,73,74,38,95,39,75,40,41,76],"544":[37,72,73,74,38,95,39,75,40,41,76],"547":[37,72,73,74,38,95,39,75,40,41,76],"549":[37,72,73,74,38,95,39,75,40,41,76],"551":[37,72,73,74,38,95,39,75,40,41,76],"553":[37,72,73,74,38,95,39,75,40,41,76],"555":[37,72,73,74,38,95,39,75,40,41,76],"557":[37,72,73,74,38,95,39,75,40,41,76],"576":[37,72,73,74,38,95,39,75,40,41,76],"580":[37,72,73,74,38,95,39,75,40,41,76],"582":[37,72,73,74,38,95,39,75,40,41,76],"672":[37,72,73,74,38,95,39,75,40,41,76],"675":[37,72,73,74,38,95,39,75,40,41,76],"677":[37,72,73,74,38,95,39,75,40,41,76],"679":[37,72,73,74,38,95,39,75,40,41,76],"688":[37,72,73,74,38,95,39,75,40,41,76],"690":[37,72,73,74,38,95,39,75,40,41,76],"692":[37,72,73,74,38,95,39,75,40,41,76],"694":[37,72,73,74,38,95,39,75,40,41,76],"697":[37,72,73,74,38,95,39,75,40,41,76]},"packages/@n8n/instance-ai/src/knowledge-base/build-templates-index.ts":{"36":[37,72,74,95,39,75,40,41,76],"40":[37,72,74,95,39,75,40,41,76],"41":[37,72,74,95,39,75,40,41,76],"49":[37,72,74,95,39,75,40,41,76],"56":[37,72,74,95,39,75,40,41,76],"60":[37,72,74,95,39,75,40,41,76],"64":[37,72,74,95,39,75,40,41,76],"87":[37,72,74,95,39,75,40,41,76],"108":[37,72,74,95,39,75,40,41,76],"118":[37,72,74,95,39,75,40,41,76],"126":[37,72,74,95,39,75,40,41,76],"129":[37,72,74,95,39,75,40,41,76],"141":[37,72,74,95,39,75,40,41,76],"151":[37,72,74,95,39,75,40,41,76],"152":[37,72,74,95,39,75,40,41,76],"161":[37,72,74,95,39,75,40,41,76]},"packages/@n8n/instance-ai/src/knowledge-base/extract-builder-templates-archive.ts":{"7":[37,72,74,95,39,75,40,41,76],"11":[37,72,74,95,39,75,40,41,76],"16":[37,72,74,95,39,75,40,41,76],"17":[37,72,74,95,39,75,40,41,76],"20":[37,72,74,95,39,75,40,41,76],"26":[37,72,74,95,39,75,40,41,76],"35":[37,72,74,95,39,75,40,41,76],"76":[37,72,74,95,39,75,40,41,76],"89":[37,72,74,95,39,75,40,41,76],"101":[37,72,74,95,39,75,40,41,76]},"packages/@n8n/instance-ai/src/knowledge-base/materialize-knowledge-base.ts":{"100":[37,72,74,95,39,75,40,41,76],"164":[37,72,74,95,39,75,40,41,76],"195":[37,72,74,95,39,75,40,41,76],"196":[37,72,74,95,39,75,40,41,76],"208":[37,72,74,95,39,75,40,41,76],"283":[37,72,74,95,39,75,40,41,76],"303":[37,72,74,95,39,75,40,41,76],"309":[37,72,74,95,39,75,40,41,76],"313":[37,72,74,95,39,75,40,41,76],"320":[37,72,74,95,39,75,40,41,76],"321":[37,72,74,95,39,75,40,41,76],"323":[37,72,74,95,39,75,40,41,76]},"packages/@n8n/instance-ai/src/mcp/mcp-client-manager.ts":{"61":[37,72,74,95,39,75,40,76],"74":[37,72,74,95,39,75,40,76],"98":[37,72,74,95,39,75,40,76],"102":[37,72,74,95,39,75,40,76],"103":[37,72,74,95,39,75,40,76],"117":[37,72,74,95,39,75,40,76],"118":[37,72,74,95,39,75,40,76],"127":[37,72,74,95,39,75,40,76],"132":[37,72,74,95,39,75,40,76],"157":[37,72,74,95,39,75,40,76],"162":[37,72,74,95,39,75,40,76],"177":[37,72,74,95,39,75,40,76],"190":[37,72,74,95,39,75,40,76],"195":[37,72,74,95,39,75,40,76],"198":[37,72,74,95,39,75,40,76],"210":[37,72,74,95,39,75,40,76],"224":[37,72,74,95,39,75,40,76],"252":[37,72,74,95,39,75,40,76],"273":[37,72,74,95,39,75,40,76],"275":[37,72,74,95,39,75,40,76]},"packages/@n8n/instance-ai/src/memory/title-utils.ts":{"13":[37,72,74,95,39,75,40,76],"21":[37,72,74,95,39,75,40,76],"35":[37,72,74,95,39,75,40,76]},"packages/@n8n/instance-ai/src/planned-tasks/planned-task-service.ts":{"20":[37,72,74,95,39,75,40,76],"26":[37,72,74,95,39,75,40,76],"27":[37,72,74,95,39,75,40,76],"30":[37,72,74,95,39,75,40,76],"35":[37,72,74,95,39,75,40,76],"36":[37,72,74,95,39,75,40,76],"50":[37,72,74,95,39,75,40,76],"63":[37,72,74,95,39,75,40,76],"83":[37,72,74,95,39,75,40,76],"87":[37,72,74,95,39,75,40,76],"104":[37,72,74,95,39,75,40,76],"109":[37,72,74,95,39,75,40,76],"118":[37,72,74,95,39,75,40,76],"120":[37,72,74,95,39,75,40,76],"139":[37,72,74,95,39,75,40,76],"156":[37,72,74,95,39,75,40,76],"157":[37,72,74,95,39,75,40,76],"172":[37,72,74,95,39,75,40,76],"173":[37,72,74,95,39,75,40,76],"181":[37,72,74,95,39,75,40,76],"185":[37,72,74,95,39,75,40,76],"190":[37,72,74,95,39,75,40,76],"191":[37,72,74,95,39,75,40,76],"202":[37,72,74,95,39,75,40,76],"207":[37,72,74,95,39,75,40,76],"208":[37,72,74,95,39,75,40,76],"219":[37,72,74,95,39,75,40,76],"224":[37,72,74,95,39,75,40,76],"225":[37,72,74,95,39,75,40,76],"232":[37,72,74,95,39,75,40,76],"261":[37,72,74,95,39,75,40,76],"268":[37,72,74,95,39,75,40,76],"269":[37,72,74,95,39,75,40,76],"283":[37,72,74,95,39,75,40,76],"311":[37,72,74,95,39,75,40,76],"318":[37,72,74,95,39,75,40,76],"325":[37,72,74,95,39,75,40,76],"332":[37,72,74,95,39,75,40,76],"333":[37,72,74,95,39,75,40,76],"347":[37,72,74,95,39,75,40,76],"361":[37,72,74,95,39,75,40,76],"374":[37,72,74,95,39,75,40,76],"375":[37,72,74,95,39,75,40,76],"393":[37,72,74,95,39,75,40,76],"422":[37,72,74,95,39,75,40,76],"427":[37,72,74,95,39,75,40,76],"428":[37,72,74,95,39,75,40,76],"437":[37,72,74,95,39,75,40,76],"448":[37,72,74,95,39,75,40,76],"454":[37,72,74,95,39,75,40,76],"478":[37,72,74,95,39,75,40,76],"501":[37,72,74,95,39,75,40,76],"504":[37,72,74,95,39,75,40,76],"514":[37,72,74,95,39,75,40,76],"520":[37,72,74,95,39,75,40,76],"534":[37,72,74,95,39,75,40,76],"549":[37,72,74,95,39,75,40,76],"550":[37,72,74,95,39,75,40,76]},"packages/@n8n/instance-ai/src/runtime/background-task-manager.ts":{"125":[37,72,73,74,177,95,39,75,40,41,76],"127":[37,72,73,74,177,95,39,75,40,41,76],"131":[37,72,73,74,177,95,39,75,40,41,76],"158":[37,72,73,74,177,95,39,75,40,41,76],"159":[37,72,73,74,177,95,39,75,40,41,76],"162":[37,72,73,74,177,95,39,75,40,41,76],"164":[37,72,73,74,177,95,39,75,40,41,76],"174":[37,72,73,74,177,95,39,75,40,41,76],"179":[37,72,73,74,177,95,39,75,40,41,76],"186":[37,72,73,74,177,95,39,75,40,41,76],"204":[37,72,73,74,177,95,39,75,40,41,76],"216":[37,72,73,74,177,95,39,75,40,41,76],"230":[37,72,73,74,177,95,39,75,40,41,76],"243":[37,72,73,74,177,95,39,75,40,41,76],"250":[37,72,73,74,177,95,39,75,40,41,76],"272":[37,72,73,74,177,95,39,75,40,41,76],"325":[37,72,73,74,177,95,39,75,40,41,76],"342":[37,72,73,74,177,95,39,75,40,41,76],"346":[37,72,73,74,177,95,39,75,40,41,76],"352":[37,72,73,74,177,95,39,75,40,41,76],"353":[37,72,73,74,177,95,39,75,40,41,76],"397":[37,72,73,74,177,95,39,75,40,41,76],"423":[37,72,73,74,177,95,39,75,40,41,76]},"packages/@n8n/instance-ai/src/runtime/memory-task-registry.ts":{"11":[37,72,73,74,39,75,40,41,76],"43":[37,72,73,74,39,75,40,41,76],"47":[37,72,73,74,39,75,40,41,76]},"packages/@n8n/instance-ai/src/runtime/orchestrator-identity.ts":{"6":[37,72,73,74,39,75,40,76]},"packages/@n8n/instance-ai/src/runtime/orchestrator-run-control.ts":{"20":[37,72,74,38,95,39,75,40,76],"26":[37,72,74,38,95,39,75,40,76],"29":[37,72,74,38,95,39,75,40,76],"32":[37,72,74,38,95,39,75,40,76],"38":[37,72,74,38,95,39,75,40,76],"44":[37,72,74,38,95,39,75,40,76]},"packages/@n8n/instance-ai/src/runtime/resumable-stream-executor.ts":{"92":[37,72,74,38,95,39,75,40,76],"100":[37,72,74,38,95,39,75,40,76],"108":[37,72,74,38,95,39,75,40,76],"112":[37,72,74,38,95,39,75,40,76],"116":[37,72,74,38,95,39,75,40,76],"126":[37,72,74,38,95,39,75,40,76],"136":[37,72,74,38,95,39,75,40,76],"168":[37,72,74,38,95,39,75,40,76],"181":[37,72,74,38,95,39,75,40,76],"199":[37,72,74,38,95,39,75,40,76],"219":[37,72,74,38,95,39,75,40,76],"253":[37,72,74,38,95,39,75,40,76],"317":[37,72,74,38,95,39,75,40,76],"491":[37,72,74,38,95,39,75,40,76],"603":[37,72,74,38,95,39,75,40,76],"618":[37,72,74,38,95,39,75,40,76],"628":[37,72,74,38,95,39,75,40,76],"632":[37,72,74,38,95,39,75,40,76],"647":[37,72,74,38,95,39,75,40,76],"648":[37,72,74,38,95,39,75,40,76],"669":[37,72,74,38,95,39,75,40,76],"677":[37,72,74,38,95,39,75,40,76],"688":[37,72,74,38,95,39,75,40,76],"702":[37,72,74,38,95,39,75,40,76]},"packages/@n8n/instance-ai/src/runtime/run-state-registry.ts":{"139":[37,72,73,74,38,95,39,75,40,41,76],"177":[37,72,73,74,38,95,39,75,40,41,76],"187":[37,72,73,74,38,95,39,75,40,41,76],"200":[37,72,73,74,38,95,39,75,40,41,76],"201":[37,72,73,74,38,95,39,75,40,41,76],"213":[37,72,73,74,38,95,39,75,40,41,76],"217":[37,72,73,74,38,95,39,75,40,41,76],"221":[37,72,73,74,38,95,39,75,40,41,76],"225":[37,72,73,74,38,95,39,75,40,41,76],"229":[37,72,73,74,38,95,39,75,40,41,76],"238":[37,72,73,74,38,95,39,75,40,41,76],"239":[37,72,73,74,38,95,39,75,40,41,76],"244":[37,72,73,74,38,95,39,75,40,41,76],"248":[37,72,73,74,38,95,39,75,40,41,76],"253":[37,72,73,74,38,95,39,75,40,41,76],"257":[37,72,73,74,38,95,39,75,40,41,76],"261":[37,72,73,74,38,95,39,75,40,41,76],"265":[37,72,73,74,38,95,39,75,40,41,76],"276":[37,72,73,74,38,95,39,75,40,41,76],"280":[37,72,73,74,38,95,39,75,40,41,76],"288":[37,72,73,74,38,95,39,75,40,41,76],"302":[37,72,73,74,38,95,39,75,40,41,76],"309":[37,72,73,74,38,95,39,75,40,41,76],"317":[37,72,73,74,38,95,39,75,40,41,76],"341":[37,72,73,74,38,95,39,75,40,41,76],"349":[37,72,73,74,38,95,39,75,40,41,76],"353":[37,72,73,74,38,95,39,75,40,41,76],"361":[37,72,73,74,38,95,39,75,40,41,76],"368":[37,72,73,74,38,95,39,75,40,41,76],"375":[37,72,73,74,38,95,39,75,40,41,76],"382":[37,72,73,74,38,95,39,75,40,41,76],"395":[37,72,73,74,38,95,39,75,40,41,76],"417":[37,72,73,74,38,95,39,75,40,41,76],"421":[37,72,73,74,38,95,39,75,40,41,76],"425":[37,72,73,74,38,95,39,75,40,41,76],"449":[37,72,73,74,38,95,39,75,40,41,76],"548":[37,72,73,74,38,95,39,75,40,41,76],"557":[37,72,73,74,38,95,39,75,40,41,76],"566":[37,72,73,74,38,95,39,75,40,41,76],"607":[37,72,73,74,38,95,39,75,40,41,76],"615":[37,72,73,74,38,95,39,75,40,41,76]},"packages/@n8n/instance-ai/src/runtime/stream-runner.ts":{"50":[37,72,74,38,95,39,75,40,76],"62":[37,72,74,38,95,39,75,40,76],"74":[37,72,74,38,95,39,75,40,76]},"packages/@n8n/instance-ai/src/runtime/terminal-response-guard.ts":{"48":[37,72,74,38,95,39,75,40,76],"52":[37,72,74,38,95,39,75,40,76],"64":[37,72,74,38,95,39,75,40,76],"74":[37,72,74,38,95,39,75,40,76],"76":[37,72,74,38,95,39,75,40,76],"175":[37,72,74,38,95,39,75,40,76],"217":[37,72,74,38,95,39,75,40,76],"224":[37,72,74,38,95,39,75,40,76],"227":[37,72,74,38,95,39,75,40,76],"230":[37,72,74,38,95,39,75,40,76],"232":[37,72,74,38,95,39,75,40,76],"237":[37,72,74,38,95,39,75,40,76],"242":[37,72,74,38,95,39,75,40,76],"248":[37,72,74,38,95,39,75,40,76],"268":[37,72,74,38,95,39,75,40,76],"289":[37,72,74,38,95,39,75,40,76]},"packages/@n8n/instance-ai/src/skills/materialize-runtime-skills.ts":{"95":[37,72,74,95,39,75,40,41,76],"99":[37,72,74,95,39,75,40,41,76],"108":[37,72,74,95,39,75,40,41,76],"111":[37,72,74,95,39,75,40,41,76],"114":[37,72,74,95,39,75,40,41,76],"133":[37,72,74,95,39,75,40,41,76],"137":[37,72,74,95,39,75,40,41,76],"170":[37,72,74,95,39,75,40,41,76],"182":[37,72,74,95,39,75,40,41,76],"185":[37,72,74,95,39,75,40,41,76],"194":[37,72,74,95,39,75,40,41,76],"197":[37,72,74,95,39,75,40,41,76],"205":[37,72,74,95,39,75,40,41,76],"208":[37,72,74,95,39,75,40,41,76],"213":[37,72,74,95,39,75,40,41,76],"225":[37,72,74,95,39,75,40,41,76],"243":[37,72,74,95,39,75,40,41,76],"278":[37,72,74,95,39,75,40,41,76],"286":[37,72,74,95,39,75,40,41,76],"302":[37,72,74,95,39,75,40,41,76],"314":[37,72,74,95,39,75,40,41,76],"333":[37,72,74,95,39,75,40,41,76],"355":[37,72,74,95,39,75,40,41,76],"382":[37,72,74,95,39,75,40,41,76],"390":[37,72,74,95,39,75,40,41,76],"401":[37,72,74,95,39,75,40,41,76],"402":[37,72,74,95,39,75,40,41,76],"405":[37,72,74,95,39,75,40,41,76],"422":[37,72,74,95,39,75,40,41,76],"434":[37,72,74,95,39,75,40,41,76],"458":[37,72,74,95,39,75,40,41,76],"521":[37,72,74,95,39,75,40,41,76],"533":[37,72,74,95,39,75,40,41,76],"535":[37,72,74,95,39,75,40,41,76],"543":[37,72,74,95,39,75,40,41,76],"553":[37,72,74,95,39,75,40,41,76],"566":[37,72,74,95,39,75,40,41,76],"569":[37,72,74,95,39,75,40,41,76],"572":[37,72,74,95,39,75,40,41,76],"580":[37,72,74,95,39,75,40,41,76],"583":[37,72,74,95,39,75,40,41,76],"594":[37,72,74,95,39,75,40,41,76],"602":[37,72,74,95,39,75,40,41,76]},"packages/@n8n/instance-ai/src/skills/runtime-skills.ts":{"11":[37,72,74,95,39,75,40,76],"18":[37,72,74,95,39,75,40,76]},"packages/@n8n/instance-ai/src/skills/skill-gates.ts":{"14":[37,72,74,95,39,75,40,76]},"packages/@n8n/instance-ai/src/storage/index.ts":{"2":[37,72,73,74,95,39,75,40,76],"11":[37,72,73,74,95,39,75,40,76]},"packages/@n8n/instance-ai/src/storage/iteration-log.ts":{"30":[37,72,73,74,39,75,40,76],"33":[37,72,73,74,39,75,40,76]},"packages/@n8n/instance-ai/src/storage/planned-task-storage.ts":{"38":[37,72,73,74,95,39,75,40,76],"44":[37,72,73,74,95,39,75,40,76],"46":[37,72,73,74,95,39,75,40,76],"52":[37,72,73,74,95,39,75,40,76],"55":[37,72,73,74,95,39,75,40,76],"68":[37,72,73,74,95,39,75,40,76],"76":[37,72,73,74,95,39,75,40,76],"96":[37,72,73,74,95,39,75,40,76],"99":[37,72,73,74,95,39,75,40,76]},"packages/@n8n/instance-ai/src/storage/terminal-outcome-storage.ts":{"26":[37,72,73,74,39,75,40,76],"28":[37,72,73,74,39,75,40,76],"31":[37,72,73,74,39,75,40,76],"46":[37,72,73,74,39,75,40,76],"49":[37,72,73,74,39,75,40,76],"70":[37,72,73,74,39,75,40,76],"73":[37,72,73,74,39,75,40,76],"77":[37,72,73,74,39,75,40,76]},"packages/@n8n/instance-ai/src/storage/thread-iteration-log-storage.ts":{"12":[37,72,73,74,39,75,40,76],"14":[37,72,73,74,39,75,40,76],"17":[37,72,73,74,39,75,40,76],"34":[37,72,73,74,39,75,40,76],"42":[37,72,73,74,39,75,40,76],"45":[37,72,73,74,39,75,40,76],"53":[37,72,73,74,39,75,40,76]},"packages/@n8n/instance-ai/src/storage/thread-patch.ts":{"21":[37,72,73,74,39,75,40,76],"30":[37,72,73,74,39,75,40,76],"37":[37,72,73,74,39,75,40,76],"44":[37,72,73,74,39,75,40,76],"53":[37,72,73,74,39,75,40,76],"64":[37,72,73,74,39,75,40,76],"90":[37,72,73,74,39,75,40,76]},"packages/@n8n/instance-ai/src/storage/thread-task-storage.ts":{"10":[37,72,73,74,95,39,75,40,76],"12":[37,72,73,74,95,39,75,40,76],"19":[37,72,73,74,95,39,75,40,76],"22":[37,72,73,74,95,39,75,40,76]},"packages/@n8n/instance-ai/src/storage/workflow-loop-storage.ts":{"35":[37,72,73,74,95,39,75,40,41,76],"43":[37,72,73,74,95,39,75,40,41,76],"52":[37,72,73,74,95,39,75,40,41,76],"59":[37,72,73,74,95,39,75,40,41,76],"61":[37,72,73,74,95,39,75,40,41,76],"69":[37,72,73,74,95,39,75,40,41,76],"77":[37,72,73,74,95,39,75,40,41,76],"90":[37,72,73,74,95,39,75,40,41,76],"100":[37,72,73,74,95,39,75,40,41,76],"105":[37,72,73,74,95,39,75,40,41,76],"113":[37,72,73,74,95,39,75,40,41,76],"139":[37,72,73,74,95,39,75,40,41,76],"148":[37,72,73,74,95,39,75,40,41,76],"175":[37,72,73,74,95,39,75,40,41,76],"182":[37,72,73,74,95,39,75,40,41,76],"202":[37,72,73,74,95,39,75,40,41,76],"208":[37,72,73,74,95,39,75,40,41,76]},"packages/@n8n/instance-ai/src/stream/consume-with-hitl.ts":{"53":[37,72,74,95,39,75,40,76],"78":[37,72,74,95,39,75,40,76],"103":[37,72,74,95,39,75,40,76],"166":[37,72,74,95,39,75,40,76]},"packages/@n8n/instance-ai/src/stream/map-chunk.ts":{"22":[37,72,74,38,95,39,75,40,76],"27":[37,72,74,38,95,39,75,40,76],"35":[37,72,74,38,95,39,75,40,76],"43":[37,72,74,38,95,39,75,40,76],"47":[37,72,74,38,95,39,75,40,76],"49":[37,72,74,38,95,39,75,40,76],"59":[37,72,74,38,95,39,75,40,76],"63":[37,72,74,38,95,39,75,40,76],"110":[37,72,74,38,95,39,75,40,76],"134":[37,72,74,38,95,39,75,40,76],"139":[37,72,74,38,95,39,75,40,76],"163":[37,72,74,38,95,39,75,40,76],"179":[37,72,74,38,95,39,75,40,76],"222":[37,72,74,38,95,39,75,40,76],"226":[37,72,74,38,95,39,75,40,76],"229":[37,72,74,38,95,39,75,40,76],"230":[37,72,74,38,95,39,75,40,76],"231":[37,72,74,38,95,39,75,40,76],"235":[37,72,74,38,95,39,75,40,76],"241":[37,72,74,38,95,39,75,40,76],"247":[37,72,74,38,95,39,75,40,76],"262":[37,72,74,38,95,39,75,40,76],"269":[37,72,74,38,95,39,75,40,76],"278":[37,72,74,38,95,39,75,40,76],"293":[37,72,74,38,95,39,75,40,76],"312":[37,72,74,38,95,39,75,40,76],"377":[37,72,74,38,95,39,75,40,76],"390":[37,72,74,38,95,39,75,40,76],"407":[37,72,74,38,95,39,75,40,76],"418":[37,72,74,38,95,39,75,40,76],"424":[37,72,74,38,95,39,75,40,76],"429":[37,72,74,38,95,39,75,40,76],"447":[37,72,74,38,95,39,75,40,76]},"packages/@n8n/instance-ai/src/stream/output-redaction.ts":{"75":[37,72,74,95,39,75,40,76],"87":[37,72,74,95,39,75,40,76],"103":[37,72,74,95,39,75,40,76],"110":[37,72,74,95,39,75,40,76],"133":[37,72,74,95,39,75,40,76],"139":[37,72,74,95,39,75,40,76],"149":[37,72,74,95,39,75,40,76],"184":[37,72,74,95,39,75,40,76],"188":[37,72,74,95,39,75,40,76],"191":[37,72,74,95,39,75,40,76],"197":[37,72,74,95,39,75,40,76],"205":[37,72,74,95,39,75,40,76],"224":[37,72,74,95,39,75,40,76],"230":[37,72,74,95,39,75,40,76],"235":[37,72,74,95,39,75,40,76]},"packages/@n8n/instance-ai/src/stream/usage-accumulator.ts":{"83":[37,72,74,95,39,75,40,76],"119":[37,72,74,95,39,75,40,76],"124":[37,72,74,95,39,75,40,76],"150":[37,72,74,95,39,75,40,76]},"packages/@n8n/instance-ai/src/stream/work-summary-accumulator.ts":{"37":[37,72,74,95,39,75,40,76],"75":[37,72,74,95,39,75,40,76],"76":[37,72,74,95,39,75,40,76],"80":[37,72,74,95,39,75,40,76]},"packages/@n8n/instance-ai/src/tool-registry.ts":{"5":[37,72,74,95,39,75,40,76],"15":[37,72,74,95,39,75,40,76],"23":[37,72,74,95,39,75,40,76],"36":[37,72,74,95,39,75,40,76],"49":[37,72,74,95,39,75,40,76],"53":[37,72,74,95,39,75,40,76]},"packages/@n8n/instance-ai/src/tools/credentials.tool.ts":{"173":[37,72,74,95,39,75,40,76],"177":[37,72,74,95,39,75,40,76],"180":[37,72,74,95,39,75,40,76],"182":[37,72,74,95,39,75,40,76],"213":[37,72,74,95,39,75,40,76],"217":[37,72,74,95,39,75,40,76],"218":[37,72,74,95,39,75,40,76],"225":[37,72,74,95,39,75,40,76],"284":[37,72,74,95,39,75,40,76],"314":[37,72,74,95,39,75,40,76],"326":[37,72,74,95,39,75,40,76],"341":[37,72,74,95,39,75,40,76],"345":[37,72,74,95,39,75,40,76],"377":[37,72,74,95,39,75,40,76],"384":[37,72,74,95,39,75,40,76],"401":[37,72,74,95,39,75,40,76],"406":[37,72,74,95,39,75,40,76],"426":[37,72,74,95,39,75,40,76],"434":[37,72,74,95,39,75,40,76],"442":[37,72,74,95,39,75,40,76],"496":[37,72,74,95,39,75,40,76],"509":[37,72,74,95,39,75,40,76],"520":[37,72,74,95,39,75,40,76]},"packages/@n8n/instance-ai/src/tools/data-tables.tool.ts":{"66":[37,72,74,95,39,75,40,76],"84":[37,72,74,95,39,75,40,76],"261":[37,72,74,95,39,75,40,76],"284":[37,72,74,95,39,75,40,76],"292":[37,72,74,95,39,75,40,76],"303":[37,72,74,95,39,75,40,76],"329":[37,72,74,95,39,75,40,76],"381":[37,72,74,95,39,75,40,76],"413":[37,72,74,95,39,75,40,76],"449":[37,72,74,95,39,75,40,76],"483":[37,72,74,95,39,75,40,76],"517":[37,72,74,95,39,75,40,76],"550":[37,72,74,95,39,75,40,76],"583":[37,72,74,95,39,75,40,76],"600":[37,72,74,95,39,75,40,76],"634":[37,72,74,95,39,75,40,76],"649":[37,72,74,95,39,75,40,76]},"packages/@n8n/instance-ai/src/tools/evals/analyze-agent-input-columns.service.ts":{"12":[37,72,74,95,39,75,40,76],"16":[37,72,74,95,39,75,40,76],"22":[37,72,74,95,39,75,40,76]},"packages/@n8n/instance-ai/src/tools/evals/apply-pin-data.service.ts":{"14":[37,72,74,95,39,75,40,76]},"packages/@n8n/instance-ai/src/tools/evals/column-ref-utils.ts":{"6":[37,72,74,95,39,75,40,76],"7":[37,72,74,95,39,75,40,76],"10":[37,72,74,95,39,75,40,76],"14":[37,72,74,95,39,75,40,76],"18":[37,72,74,95,39,75,40,76],"29":[37,72,74,95,39,75,40,76],"33":[37,72,74,95,39,75,40,76],"37":[37,72,74,95,39,75,40,76],"43":[37,72,74,95,39,75,40,76],"54":[37,72,74,95,39,75,40,76],"80":[37,72,74,95,39,75,40,76],"106":[37,72,74,95,39,75,40,76],"128":[37,72,74,95,39,75,40,76],"136":[37,72,74,95,39,75,40,76],"145":[37,72,74,95,39,75,40,76],"152":[37,72,74,95,39,75,40,76],"156":[37,72,74,95,39,75,40,76],"160":[37,72,74,95,39,75,40,76],"164":[37,72,74,95,39,75,40,76],"204":[37,72,74,95,39,75,40,76],"219":[37,72,74,95,39,75,40,76],"235":[37,72,74,95,39,75,40,76],"250":[37,72,74,95,39,75,40,76],"262":[37,72,74,95,39,75,40,76],"270":[37,72,74,95,39,75,40,76],"286":[37,72,74,95,39,75,40,76],"297":[37,72,74,95,39,75,40,76]},"packages/@n8n/instance-ai/src/tools/evals/describe-metric-for-workflow.service.ts":{"14":[37,72,74,95,39,75,40,76],"34":[37,72,74,95,39,75,40,76],"43":[37,72,74,95,39,75,40,76],"62":[37,72,74,95,39,75,40,76],"68":[37,72,74,95,39,75,40,76],"75":[37,72,74,95,39,75,40,76],"84":[37,72,74,95,39,75,40,76],"121":[37,72,74,95,39,75,40,76]},"packages/@n8n/instance-ai/src/tools/evals/detect-agent-named-refs.service.ts":{"24":[37,72,74,95,39,75,40,76],"34":[37,72,74,95,39,75,40,76],"35":[37,72,74,95,39,75,40,76],"42":[37,72,74,95,39,75,40,76],"50":[37,72,74,95,39,75,40,76],"79":[37,72,74,95,39,75,40,76]},"packages/@n8n/instance-ai/src/tools/evals/detect-ai-nodes.ts":{"60":[37,72,74,95,39,75,40,76],"64":[37,72,74,95,39,75,40,76],"71":[37,72,74,95,39,75,40,76],"74":[37,72,74,95,39,75,40,76],"77":[37,72,74,95,39,75,40,76]},"packages/@n8n/instance-ai/src/tools/evals/detect-tool-refs.service.ts":{"26":[37,72,74,95,39,75,40,76],"57":[37,72,74,95,39,75,40,76],"58":[37,72,74,95,39,75,40,76],"67":[37,72,74,95,39,75,40,76],"76":[37,72,74,95,39,75,40,76]},"packages/@n8n/instance-ai/src/tools/evals/ensure-eval-data-table.service.ts":{"14":[37,72,74,95,39,75,40,76],"18":[37,72,74,95,39,75,40,76],"30":[37,72,74,95,39,75,40,76],"35":[37,72,74,95,39,75,40,76],"54":[37,72,74,95,39,75,40,76],"58":[37,72,74,95,39,75,40,76],"92":[37,72,74,95,39,75,40,76],"99":[37,72,74,95,39,75,40,76]},"packages/@n8n/instance-ai/src/tools/evals/eval-data-requirements.service.ts":{"38":[37,72,74,95,39,75,40,76],"45":[37,72,74,95,39,75,40,76],"55":[37,72,74,95,39,75,40,76],"63":[37,72,74,95,39,75,40,76],"65":[37,72,74,95,39,75,40,76],"67":[37,72,74,95,39,75,40,76],"76":[37,72,74,95,39,75,40,76],"91":[37,72,74,95,39,75,40,76],"92":[37,72,74,95,39,75,40,76],"95":[37,72,74,95,39,75,40,76],"99":[37,72,74,95,39,75,40,76],"102":[37,72,74,95,39,75,40,76],"106":[37,72,74,95,39,75,40,76],"108":[37,72,74,95,39,75,40,76],"118":[37,72,74,95,39,75,40,76],"120":[37,72,74,95,39,75,40,76],"128":[37,72,74,95,39,75,40,76],"137":[37,72,74,95,39,75,40,76],"148":[37,72,74,95,39,75,40,76],"162":[37,72,74,95,39,75,40,76],"168":[37,72,74,95,39,75,40,76],"173":[37,72,74,95,39,75,40,76],"178":[37,72,74,95,39,75,40,76],"185":[37,72,74,95,39,75,40,76],"190":[37,72,74,95,39,75,40,76],"193":[37,72,74,95,39,75,40,76],"195":[37,72,74,95,39,75,40,76],"202":[37,72,74,95,39,75,40,76],"208":[37,72,74,95,39,75,40,76],"213":[37,72,74,95,39,75,40,76],"215":[37,72,74,95,39,75,40,76]},"packages/@n8n/instance-ai/src/tools/evals/evals.tool.ts":{"159":[37,72,74,95,39,75,40,76],"173":[37,72,74,95,39,75,40,76],"174":[37,72,74,95,39,75,40,76],"176":[37,72,74,95,39,75,40,76],"188":[37,72,74,95,39,75,40,76],"193":[37,72,74,95,39,75,40,76],"197":[37,72,74,95,39,75,40,76],"201":[37,72,74,95,39,75,40,76],"208":[37,72,74,95,39,75,40,76],"215":[37,72,74,95,39,75,40,76],"219":[37,72,74,95,39,75,40,76],"227":[37,72,74,95,39,75,40,76],"235":[37,72,74,95,39,75,40,76],"258":[37,72,74,95,39,75,40,76],"262":[37,72,74,95,39,75,40,76],"269":[37,72,74,95,39,75,40,76],"298":[37,72,74,95,39,75,40,76],"307":[37,72,74,95,39,75,40,76],"311":[37,72,74,95,39,75,40,76],"320":[37,72,74,95,39,75,40,76],"330":[37,72,74,95,39,75,40,76],"347":[37,72,74,95,39,75,40,76],"380":[37,72,74,95,39,75,40,76],"394":[37,72,74,95,39,75,40,76],"433":[37,72,74,95,39,75,40,76],"457":[37,72,74,95,39,75,40,76],"466":[37,72,74,95,39,75,40,76],"467":[37,72,74,95,39,75,40,76],"491":[37,72,74,95,39,75,40,76],"547":[37,72,74,95,39,75,40,76],"549":[37,72,74,95,39,75,40,76],"559":[37,72,74,95,39,75,40,76]},"packages/@n8n/instance-ai/src/tools/evals/extract-rows-from-history.service.ts":{"27":[37,72,74,95,39,75,40,76],"45":[37,72,74,95,39,75,40,76],"72":[37,72,74,95,39,75,40,76],"76":[37,72,74,95,39,75,40,76]},"packages/@n8n/instance-ai/src/tools/evals/format-eval-setup-task.ts":{"28":[37,72,74,95,39,75,40,76],"32":[37,72,74,95,39,75,40,76],"36":[37,72,74,95,39,75,40,76],"60":[37,72,74,95,39,75,40,76],"87":[37,72,74,95,39,75,40,76],"99":[37,72,74,95,39,75,40,76],"100":[37,72,74,95,39,75,40,76],"117":[37,72,74,95,39,75,40,76],"149":[37,72,74,95,39,75,40,76],"158":[37,72,74,95,39,75,40,76],"162":[37,72,74,95,39,75,40,76],"173":[37,72,74,95,39,75,40,76],"176":[37,72,74,95,39,75,40,76],"183":[37,72,74,95,39,75,40,76],"188":[37,72,74,95,39,75,40,76],"189":[37,72,74,95,39,75,40,76],"191":[37,72,74,95,39,75,40,76]},"packages/@n8n/instance-ai/src/tools/evals/generate-sample-rows.service.ts":{"63":[37,72,74,95,39,75,40,76],"67":[37,72,74,95,39,75,40,76],"70":[37,72,74,95,39,75,40,76],"86":[37,72,74,95,39,75,40,76],"92":[37,72,74,95,39,75,40,76],"99":[37,72,74,95,39,75,40,76],"101":[37,72,74,95,39,75,40,76],"108":[37,72,74,95,39,75,40,76],"112":[37,72,74,95,39,75,40,76],"126":[37,72,74,95,39,75,40,76],"130":[37,72,74,95,39,75,40,76],"151":[37,72,74,95,39,75,40,76],"168":[37,72,74,95,39,75,40,76],"188":[37,72,74,95,39,75,40,76],"214":[37,72,74,95,39,75,40,76],"232":[37,72,74,95,39,75,40,76],"235":[37,72,74,95,39,75,40,76],"269":[37,72,74,95,39,75,40,76],"289":[37,72,74,95,39,75,40,76],"302":[37,72,74,95,39,75,40,76],"303":[37,72,74,95,39,75,40,76],"306":[37,72,74,95,39,75,40,76],"314":[37,72,74,95,39,75,40,76]},"packages/@n8n/instance-ai/src/tools/evals/generate-tool-ref-pin-data.service.ts":{"37":[37,72,74,95,39,75,40,76],"53":[37,72,74,95,39,75,40,76],"61":[37,72,74,95,39,75,40,76],"77":[37,72,74,95,39,75,40,76],"89":[37,72,74,95,39,75,40,76],"92":[37,72,74,95,39,75,40,76],"104":[37,72,74,95,39,75,40,76],"113":[37,72,74,95,39,75,40,76],"121":[37,72,74,95,39,75,40,76],"133":[37,72,74,95,39,75,40,76]},"packages/@n8n/instance-ai/src/tools/evals/metric-catalog.ts":{"70":[37,72,74,95,39,75,40,76],"74":[37,72,74,95,39,75,40,76],"75":[37,72,74,95,39,75,40,76],"78":[37,72,74,95,39,75,40,76],"94":[37,72,74,95,39,75,40,76]},"packages/@n8n/instance-ai/src/tools/executions.tool.ts":{"154":[37,72,74,95,39,75,40,76],"163":[37,72,74,95,39,75,40,76],"167":[37,72,74,95,39,75,40,76],"171":[37,72,74,95,39,75,40,76],"185":[37,72,74,95,39,75,40,76],"193":[37,72,74,95,39,75,40,76],"200":[37,72,74,95,39,75,40,76],"227":[37,72,74,95,39,75,40,76],"230":[37,72,74,95,39,75,40,76],"231":[37,72,74,95,39,75,40,76],"300":[37,72,74,95,39,75,40,76],"304":[37,72,74,95,39,75,40,76],"314":[37,72,74,95,39,75,40,76],"328":[37,72,74,95,39,75,40,76],"334":[37,72,74,95,39,75,40,76],"345":[37,72,74,95,39,75,40,76]},"packages/@n8n/instance-ai/src/tools/filesystem/create-tools-from-mcp-server.ts":{"23":[37,72,74,95,39,75,40,76],"81":[37,72,74,95,39,75,40,76],"87":[37,72,74,95,39,75,40,76],"100":[37,72,74,95,39,75,40,76],"112":[37,72,74,95,39,75,40,76],"117":[37,72,74,95,39,75,40,76],"118":[37,72,74,95,39,75,40,76],"154":[37,72,74,95,39,75,40,76],"178":[37,72,74,95,39,75,40,76],"202":[37,72,74,95,39,75,40,76],"206":[37,72,74,95,39,75,40,76],"212":[37,72,74,95,39,75,40,76],"224":[37,72,74,95,39,75,40,76],"225":[37,72,74,95,39,75,40,76],"239":[37,72,74,95,39,75,40,76],"240":[37,72,74,95,39,75,40,76],"266":[37,72,74,95,39,75,40,76],"328":[37,72,74,95,39,75,40,76],"374":[37,72,74,95,39,75,40,76],"381":[37,72,74,95,39,75,40,76],"393":[37,72,74,95,39,75,40,76]},"packages/@n8n/instance-ai/src/tools/index.ts":{"9":[37,72,74,95,39,75,40,76],"11":[37,72,74,95,39,75,40,76],"15":[37,72,74,95,39,75,40,76],"18":[37,72,74,95,39,75,40,76],"21":[37,72,74,95,39,75,40,76],"24":[37,72,74,95,39,75,40,76],"27":[37,72,74,95,39,75,40,76],"30":[37,72,74,95,39,75,40,76],"32":[37,72,74,95,39,75,40,76],"34":[37,72,74,95,39,75,40,76],"36":[37,72,74,95,39,75,40,76],"38":[37,72,74,95,39,75,40,76],"42":[37,72,74,95,39,75,40,76],"46":[37,72,74,95,39,75,40,76],"50":[37,72,74,95,39,75,40,76],"54":[37,72,74,95,39,75,40,76],"58":[37,72,74,95,39,75,40,76],"61":[37,72,74,95,39,75,40,76],"65":[37,72,74,95,39,75,40,76],"69":[37,72,74,95,39,75,40,76],"72":[37,72,74,95,39,75,40,76],"75":[37,72,74,95,39,75,40,76],"78":[37,72,74,95,39,75,40,76],"82":[37,72,74,95,39,75,40,76],"86":[37,72,74,95,39,75,40,76],"89":[37,72,74,95,39,75,40,76],"96":[37,72,74,95,39,75,40,76],"129":[37,72,74,95,39,75,40,76],"161":[37,72,74,95,39,75,40,76]},"packages/@n8n/instance-ai/src/tools/n8n-docs/ranking.ts":{"24":[37,72,74,95,39,75,40,76],"32":[37,72,74,95,39,75,40,76],"35":[37,72,74,95,39,75,40,76],"53":[37,72,74,95,39,75,40,76],"71":[37,72,74,95,39,75,40,76],"75":[37,72,74,95,39,75,40,76],"76":[37,72,74,95,39,75,40,76],"79":[37,72,74,95,39,75,40,76],"85":[37,72,74,95,39,75,40,76],"90":[37,72,74,95,39,75,40,76],"94":[37,72,74,95,39,75,40,76],"98":[37,72,74,95,39,75,40,76],"102":[37,72,74,95,39,75,40,76],"113":[37,72,74,95,39,75,40,76],"203":[37,72,74,95,39,75,40,76],"210":[37,72,74,95,39,75,40,76],"211":[37,72,74,95,39,75,40,76],"212":[37,72,74,95,39,75,40,76],"215":[37,72,74,95,39,75,40,76],"221":[37,72,74,95,39,75,40,76],"226":[37,72,74,95,39,75,40,76]},"packages/@n8n/instance-ai/src/tools/n8n-docs/registry.ts":{"52":[37,72,74,95,39,75,40,76],"67":[37,72,74,95,39,75,40,76],"71":[37,72,74,95,39,75,40,76],"86":[37,72,74,95,39,75,40,76],"91":[37,72,74,95,39,75,40,76],"116":[37,72,74,95,39,75,40,76],"136":[37,72,74,95,39,75,40,76],"140":[37,72,74,95,39,75,40,76],"169":[37,72,74,95,39,75,40,76],"174":[37,72,74,95,39,75,40,76],"217":[37,72,74,95,39,75,40,76],"233":[37,72,74,95,39,75,40,76],"269":[37,72,74,95,39,75,40,76],"287":[37,72,74,95,39,75,40,76]},"packages/@n8n/instance-ai/src/tools/n8n-docs.tool.ts":{"13":[37,72,74,95,39,75,40,76],"16":[37,72,74,95,39,75,40,76],"19":[37,72,74,95,39,75,40,76],"53":[37,72,74,95,39,75,40,76],"58":[37,72,74,95,39,75,40,76],"67":[37,72,74,95,39,75,40,76],"83":[37,72,74,95,39,75,40,76],"104":[37,72,74,95,39,75,40,76],"124":[37,72,74,95,39,75,40,76],"144":[37,72,74,95,39,75,40,76],"157":[37,72,74,95,39,75,40,76],"176":[37,72,74,95,39,75,40,76],"177":[37,72,74,95,39,75,40,76],"203":[37,72,74,95,39,75,40,76],"209":[37,72,74,95,39,75,40,76]},"packages/@n8n/instance-ai/src/tools/nodes/node-search-engine.ts":{"49":[37,72,74,95,39,75,40,76],"61":[37,72,74,95,39,75,40,76],"66":[37,72,74,95,39,75,40,76],"70":[37,72,74,95,39,75,40,76],"71":[37,72,74,95,39,75,40,76],"78":[37,72,74,95,39,75,40,76],"84":[37,72,74,95,39,75,40,76],"101":[37,72,74,95,39,75,40,76],"103":[37,72,74,95,39,75,40,76],"121":[37,72,74,95,39,75,40,76],"126":[37,72,74,95,39,75,40,76],"134":[37,72,74,95,39,75,40,76],"138":[37,72,74,95,39,75,40,76],"168":[37,72,74,95,39,75,40,76],"177":[37,72,74,95,39,75,40,76],"183":[37,72,74,95,39,75,40,76],"212":[37,72,74,95,39,75,40,76],"230":[37,72,74,95,39,75,40,76],"236":[37,72,74,95,39,75,40,76],"243":[37,72,74,95,39,75,40,76],"247":[37,72,74,95,39,75,40,76],"255":[37,72,74,95,39,75,40,76],"259":[37,72,74,95,39,75,40,76],"275":[37,72,74,95,39,75,40,76],"282":[37,72,74,95,39,75,40,76],"295":[37,72,74,95,39,75,40,76],"306":[37,72,74,95,39,75,40,76],"310":[37,72,74,95,39,75,40,76],"317":[37,72,74,95,39,75,40,76],"319":[37,72,74,95,39,75,40,76],"325":[37,72,74,95,39,75,40,76],"329":[37,72,74,95,39,75,40,76],"331":[37,72,74,95,39,75,40,76],"345":[37,72,74,95,39,75,40,76],"393":[37,72,74,95,39,75,40,76],"407":[37,72,74,95,39,75,40,76],"421":[37,72,74,95,39,75,40,76],"448":[37,72,74,95,39,75,40,76],"449":[37,72,74,95,39,75,40,76],"458":[37,72,74,95,39,75,40,76],"481":[37,72,74,95,39,75,40,76],"489":[37,72,74,95,39,75,40,76]},"packages/@n8n/instance-ai/src/tools/nodes/preferred-chat-model.ts":{"19":[37,72,74,95,39,75,40,76]},"packages/@n8n/instance-ai/src/tools/nodes.tool.ts":{"146":[37,72,74,95,39,75,40,41,76],"157":[37,72,74,95,39,75,40,41,76],"182":[37,72,74,95,39,75,40,41,76],"193":[37,72,74,95,39,75,40,41,76],"194":[37,72,74,95,39,75,40,41,76],"206":[37,72,74,95,39,75,40,41,76],"210":[37,72,74,95,39,75,40,41,76],"225":[37,72,74,95,39,75,40,41,76],"250":[37,72,74,95,39,75,40,41,76],"256":[37,72,74,95,39,75,40,41,76],"265":[37,72,74,95,39,75,40,41,76],"299":[37,72,74,95,39,75,40,41,76],"313":[37,72,74,95,39,75,40,41,76],"322":[37,72,74,95,39,75,40,41,76],"348":[37,72,74,95,39,75,40,41,76],"376":[37,72,74,95,39,75,40,41,76],"418":[37,72,74,95,39,75,40,41,76],"434":[37,72,74,95,39,75,40,41,76]},"packages/@n8n/instance-ai/src/tools/orchestration/agent-persistence.ts":{"7":[37,72,74,95,39,75,40,76],"11":[37,72,74,95,39,75,40,76],"21":[37,72,74,95,39,75,40,76],"31":[37,72,74,95,39,75,40,76]},"packages/@n8n/instance-ai/src/tools/orchestration/agent-preview-session-binding.ts":{"22":[37,72,74,95,39,75,40,76],"27":[37,72,74,95,39,75,40,76],"41":[37,72,74,95,39,75,40,76],"55":[37,72,74,95,39,75,40,76],"69":[37,72,74,95,39,75,40,76]},"packages/@n8n/instance-ai/src/tools/orchestration/complete-checkpoint.tool.ts":{"40":[37,72,74,95,39,75,40,76],"45":[37,72,74,95,39,75,40,76],"50":[37,72,74,95,39,75,40,76],"57":[37,72,74,95,39,75,40,76],"62":[37,72,74,95,39,75,40,76],"63":[37,72,74,95,39,75,40,76],"64":[37,72,74,95,39,75,40,76],"65":[37,72,74,95,39,75,40,76],"82":[37,72,74,95,39,75,40,76],"85":[37,72,74,95,39,75,40,76],"86":[37,72,74,95,39,75,40,76],"110":[37,72,74,95,39,75,40,76],"120":[37,72,74,95,39,75,40,76]},"packages/@n8n/instance-ai/src/tools/orchestration/display-utils.ts":{"2":[37,72,74,95,39,75,40,76]},"packages/@n8n/instance-ai/src/tools/orchestration/eval-data-agent.tool.ts":{"12":[37,72,74,95,39,75,40,76],"26":[37,72,74,95,39,75,40,76],"27":[37,72,74,95,39,75,40,76],"74":[37,72,74,95,39,75,40,76],"79":[37,72,74,95,39,75,40,76],"80":[37,72,74,95,39,75,40,76],"89":[37,72,74,95,39,75,40,76],"102":[37,72,74,95,39,75,40,76],"108":[37,72,74,95,39,75,40,76],"111":[37,72,74,95,39,75,40,76]},"packages/@n8n/instance-ai/src/tools/orchestration/eval-setup-agent.tool.ts":{"48":[37,72,74,95,39,75,40,76],"94":[37,72,74,95,39,75,40,76],"137":[37,72,74,95,39,75,40,76],"138":[37,72,74,95,39,75,40,76],"259":[37,72,74,95,39,75,40,76],"275":[37,72,74,95,39,75,40,76]},"packages/@n8n/instance-ai/src/tools/orchestration/plan.tool.ts":{"53":[37,72,74,95,39,75,40,76],"68":[37,72,74,95,39,75,40,76],"69":[37,72,74,95,39,75,40,76],"74":[37,72,74,95,39,75,40,76],"75":[37,72,74,95,39,75,40,76],"78":[37,72,74,95,39,75,40,76],"99":[37,72,74,95,39,75,40,76],"149":[37,72,74,95,39,75,40,76],"172":[37,72,74,95,39,75,40,76],"255":[37,72,74,95,39,75,40,76]},"packages/@n8n/instance-ai/src/tools/orchestration/report-verification-verdict.tool.ts":{"74":[37,72,74,95,39,75,40,76],"107":[37,72,74,95,39,75,40,76],"120":[37,72,74,95,39,75,40,76]},"packages/@n8n/instance-ai/src/tools/orchestration/tracing-utils.ts":{"27":[37,72,74,95,39,75,40,76],"50":[37,72,74,95,39,75,40,76],"57":[37,72,74,95,39,75,40,76],"61":[37,72,74,95,39,75,40,76],"90":[37,72,74,95,39,75,40,76],"137":[37,72,74,95,39,75,40,76],"150":[37,72,74,95,39,75,40,76],"162":[37,72,74,95,39,75,40,76],"173":[37,72,74,95,39,75,40,76],"182":[37,72,74,95,39,75,40,76]},"packages/@n8n/instance-ai/src/tools/orchestration/verification/analyze-result.ts":{"28":[37,72,74,95,39,75,40,41,76],"37":[37,72,74,95,39,75,40,41,76],"51":[37,72,74,95,39,75,40,41,76],"55":[37,72,74,95,39,75,40,41,76],"68":[37,72,74,95,39,75,40,41,76],"77":[37,72,74,95,39,75,40,41,76],"88":[37,72,74,95,39,75,40,41,76],"95":[37,72,74,95,39,75,40,41,76],"109":[37,72,74,95,39,75,40,41,76],"121":[37,72,74,95,39,75,40,41,76],"132":[37,72,74,95,39,75,40,41,76],"133":[37,72,74,95,39,75,40,41,76],"136":[37,72,74,95,39,75,40,41,76],"202":[37,72,74,95,39,75,40,41,76],"209":[37,72,74,95,39,75,40,41,76],"223":[37,72,74,95,39,75,40,41,76],"259":[37,72,74,95,39,75,40,41,76],"263":[37,72,74,95,39,75,40,41,76],"271":[37,72,74,95,39,75,40,41,76],"292":[37,72,74,95,39,75,40,41,76],"306":[37,72,74,95,39,75,40,41,76],"308":[37,72,74,95,39,75,40,41,76],"309":[37,72,74,95,39,75,40,41,76],"343":[37,72,74,95,39,75,40,41,76]},"packages/@n8n/instance-ai/src/tools/orchestration/verification/finalize-result.ts":{"21":[37,72,74,95,39,75,40,41,76],"84":[37,72,74,95,39,75,40,41,76],"135":[37,72,74,95,39,75,40,41,76]},"packages/@n8n/instance-ai/src/tools/orchestration/verification/prepare-run.ts":{"12":[37,72,74,95,39,75,40,41,76],"20":[37,72,74,95,39,75,40,41,76],"21":[37,72,74,95,39,75,40,41,76],"24":[37,72,74,95,39,75,40,41,76],"35":[37,72,74,95,39,75,40,41,76],"71":[37,72,74,95,39,75,40,41,76],"77":[37,72,74,95,39,75,40,41,76],"79":[37,72,74,95,39,75,40,41,76]},"packages/@n8n/instance-ai/src/tools/orchestration/verification/reconcile-plan.ts":{"15":[37,72,74,95,39,75,40,41,76],"36":[37,72,74,95,39,75,40,41,76]},"packages/@n8n/instance-ai/src/tools/orchestration/verification/resolve-target.ts":{"29":[37,72,74,95,39,75,40,41,76]},"packages/@n8n/instance-ai/src/tools/orchestration/verify-built-workflow.tool.ts":{"114":[37,72,74,95,39,75,40,41,76],"125":[37,72,74,95,39,75,40,41,76],"189":[37,72,74,95,39,75,40,41,76]},"packages/@n8n/instance-ai/src/tools/research.tool.ts":{"20":[37,72,74,95,39,75,40,76],"88":[37,72,74,95,39,75,40,76],"143":[37,72,74,95,39,75,40,76],"220":[37,72,74,95,39,75,40,76],"249":[37,72,74,95,39,75,40,76],"262":[37,72,74,95,39,75,40,76]},"packages/@n8n/instance-ai/src/tools/shared/ask-user.tool.ts":{"5":[37,72,74,95,39,75,40,76],"41":[37,72,74,95,39,75,40,76],"85":[37,72,74,95,39,75,40,76],"106":[37,72,74,95,39,75,40,76],"108":[37,72,74,95,39,75,40,76]},"packages/@n8n/instance-ai/src/tools/task-control.tool.ts":{"43":[37,72,74,95,39,75,40,76],"58":[37,72,74,95,39,75,40,76],"69":[37,72,74,95,39,75,40,76],"96":[37,72,74,95,39,75,40,76],"102":[37,72,74,95,39,75,40,76]},"packages/@n8n/instance-ai/src/tools/web-research/sanitize-web-content.ts":{"12":[37,72,74,95,39,75,40,41,76],"27":[37,72,74,95,39,75,40,41,76],"32":[37,72,74,95,39,75,40,41,76],"47":[37,72,74,95,39,75,40,41,76]},"packages/@n8n/instance-ai/src/tools/workflows/apply-workflow-credentials.tool.ts":{"24":[37,72,74,95,39,75,40,76],"38":[37,72,74,95,39,75,40,76]},"packages/@n8n/instance-ai/src/tools/workflows/build-workflow.tool.ts":{"95":[37,72,74,95,39,75,40,41,76],"185":[37,72,74,95,39,75,40,41,76],"203":[37,72,74,95,39,75,40,41,76],"236":[37,72,74,95,39,75,40,41,76],"246":[37,72,74,95,39,75,40,41,76],"262":[37,72,74,95,39,75,40,41,76],"277":[37,72,74,95,39,75,40,41,76],"319":[37,72,74,95,39,75,40,41,76],"546":[37,72,74,95,39,75,40,41,76],"644":[37,72,74,95,39,75,40,41,76],"683":[37,72,74,95,39,75,40,41,76],"747":[37,72,74,95,39,75,40,41,76],"748":[37,72,74,95,39,75,40,41,76],"750":[37,72,74,95,39,75,40,41,76],"753":[37,72,74,95,39,75,40,41,76],"754":[37,72,74,95,39,75,40,41,76],"759":[37,72,74,95,39,75,40,41,76],"888":[37,72,74,95,39,75,40,41,76]},"packages/@n8n/instance-ai/src/tools/workflows/classify-node-destructiveness.service.ts":{"81":[37,72,74,95,39,75,40,41,76],"180":[37,72,74,95,39,75,40,41,76],"192":[37,72,74,95,39,75,40,41,76],"208":[37,72,74,95,39,75,40,41,76],"216":[37,72,74,95,39,75,40,41,76],"291":[37,72,74,95,39,75,40,41,76],"320":[37,72,74,95,39,75,40,41,76],"351":[37,72,74,95,39,75,40,41,76],"358":[37,72,74,95,39,75,40,41,76],"367":[37,72,74,95,39,75,40,41,76],"379":[37,72,74,95,39,75,40,41,76],"392":[37,72,74,95,39,75,40,41,76],"402":[37,72,74,95,39,75,40,41,76],"409":[37,72,74,95,39,75,40,41,76],"445":[37,72,74,95,39,75,40,41,76],"446":[37,72,74,95,39,75,40,41,76]},"packages/@n8n/instance-ai/src/tools/workflows/credential-url-resolver.ts":{"24":[37,39,40,41],"32":[37,39,40,41],"33":[37,39,40,41],"41":[37,39,40,41],"43":[37,39,40,41],"78":[37,39,40,41],"94":[37,39,40,41]},"packages/@n8n/instance-ai/src/tools/workflows/credential-utils.ts":{"34":[37,72,74,95,39,75,40,76],"45":[37,72,74,95,39,75,40,76],"61":[37,72,74,95,39,75,40,76],"94":[37,72,74,95,39,75,40,76]},"packages/@n8n/instance-ai/src/tools/workflows/detect-array-input-collapse.ts":{"31":[37,72,74,95,39,75,40,41,76],"44":[37,72,74,95,39,75,40,41,76],"55":[37,72,74,95,39,75,40,41,76],"78":[37,72,74,95,39,75,40,41,76],"90":[37,72,74,95,39,75,40,41,76]},"packages/@n8n/instance-ai/src/tools/workflows/detect-unparseable-openai-schema.ts":{"14":[37,72,74,95,39,75,40,41,76],"38":[37,72,74,95,39,75,40,41,76]},"packages/@n8n/instance-ai/src/tools/workflows/detect-wrong-kind-locator.ts":{"6":[37,72,74,95,39,75,40,41,76],"10":[37,72,74,95,39,75,40,41,76],"11":[37,72,74,95,39,75,40,41,76],"15":[37,72,74,95,39,75,40,41,76],"18":[37,72,74,95,39,75,40,41,76],"21":[37,72,74,95,39,75,40,41,76],"22":[37,72,74,95,39,75,40,41,76],"25":[37,72,74,95,39,75,40,41,76],"34":[37,72,74,95,39,75,40,41,76],"39":[37,72,74,95,39,75,40,41,76],"48":[37,72,74,95,39,75,40,41,76],"68":[37,72,74,95,39,75,40,41,76],"114":[37,72,74,95,39,75,40,41,76]},"packages/@n8n/instance-ai/src/tools/workflows/generate-simulation-fixtures.service.ts":{"89":[37,72,74,95,39,75,40,76],"116":[37,72,74,95,39,75,40,76],"125":[37,72,74,95,39,75,40,76],"126":[37,72,74,95,39,75,40,76],"129":[37,72,74,95,39,75,40,76],"137":[37,72,74,95,39,75,40,76],"142":[37,72,74,95,39,75,40,76],"143":[37,72,74,95,39,75,40,76],"151":[37,72,74,95,39,75,40,76],"155":[37,72,74,95,39,75,40,76],"160":[37,72,74,95,39,75,40,76],"163":[37,72,74,95,39,75,40,76],"170":[37,72,74,95,39,75,40,76],"180":[37,72,74,95,39,75,40,76],"192":[37,72,74,95,39,75,40,76],"226":[37,72,74,95,39,75,40,76]},"packages/@n8n/instance-ai/src/tools/workflows/plan-verification-simulation.ts":{"68":[37,72,74,95,39,75,40,41,76],"72":[37,72,74,95,39,75,40,41,76],"105":[37,72,74,95,39,75,40,41,76],"111":[37,72,74,95,39,75,40,41,76],"114":[37,72,74,95,39,75,40,41,76],"140":[37,72,74,95,39,75,40,41,76],"147":[37,72,74,95,39,75,40,41,76],"156":[37,72,74,95,39,75,40,41,76],"173":[37,72,74,95,39,75,40,41,76],"180":[37,72,74,95,39,75,40,41,76],"183":[37,72,74,95,39,75,40,41,76],"189":[37,72,74,95,39,75,40,41,76],"201":[37,72,74,95,39,75,40,41,76],"226":[37,72,74,95,39,75,40,41,76],"232":[37,72,74,95,39,75,40,41,76],"235":[37,72,74,95,39,75,40,41,76],"238":[37,72,74,95,39,75,40,41,76],"248":[37,72,74,95,39,75,40,41,76],"253":[37,72,74,95,39,75,40,41,76],"257":[37,72,74,95,39,75,40,41,76],"264":[37,72,74,95,39,75,40,41,76],"265":[37,72,74,95,39,75,40,41,76],"291":[37,72,74,95,39,75,40,41,76],"319":[37,72,74,95,39,75,40,41,76],"339":[37,72,74,95,39,75,40,41,76],"340":[37,72,74,95,39,75,40,41,76],"341":[37,72,74,95,39,75,40,41,76]},"packages/@n8n/instance-ai/src/tools/workflows/reconcile-simulation-plan.ts":{"40":[37,72,74,95,39,75,40,76],"50":[37,72,74,95,39,75,40,76],"54":[37,72,74,95,39,75,40,76],"58":[37,72,74,95,39,75,40,76],"76":[37,72,74,95,39,75,40,76],"88":[37,72,74,95,39,75,40,76],"94":[37,72,74,95,39,75,40,76],"99":[37,72,74,95,39,75,40,76],"102":[37,72,74,95,39,75,40,76],"104":[37,72,74,95,39,75,40,76],"105":[37,72,74,95,39,75,40,76],"115":[37,72,74,95,39,75,40,76],"120":[37,72,74,95,39,75,40,76],"132":[37,72,74,95,39,75,40,76],"140":[37,72,74,95,39,75,40,76],"147":[37,72,74,95,39,75,40,76],"166":[37,72,74,95,39,75,40,76],"188":[37,72,74,95,39,75,40,76]},"packages/@n8n/instance-ai/src/tools/workflows/resolve-credentials.ts":{"36":[37,72,74,95,39,75,40,41,76],"74":[37,72,74,95,39,75,40,41,76],"119":[37,72,74,95,39,75,40,41,76],"132":[37,72,74,95,39,75,40,41,76],"138":[37,72,74,95,39,75,40,41,76],"172":[37,72,74,95,39,75,40,41,76],"178":[37,72,74,95,39,75,40,41,76],"190":[37,72,74,95,39,75,40,41,76],"211":[37,72,74,95,39,75,40,41,76],"227":[37,72,74,95,39,75,40,41,76],"317":[37,72,74,95,39,75,40,41,76],"326":[37,72,74,95,39,75,40,41,76],"335":[37,72,74,95,39,75,40,41,76],"346":[37,72,74,95,39,75,40,41,76],"355":[37,72,74,95,39,75,40,41,76]},"packages/@n8n/instance-ai/src/tools/workflows/setup-workflow.service.ts":{"42":[37,72,74,95,39,75,40,41,76],"46":[37,72,74,95,39,75,40,41,76],"59":[37,72,74,95,39,75,40,41,76],"138":[37,72,74,95,39,75,40,41,76],"159":[37,72,74,95,39,75,40,41,76],"164":[37,72,74,95,39,75,40,41,76],"171":[37,72,74,95,39,75,40,41,76],"181":[37,72,74,95,39,75,40,41,76],"200":[37,72,74,95,39,75,40,41,76],"208":[37,72,74,95,39,75,40,41,76],"235":[37,72,74,95,39,75,40,41,76],"251":[37,72,74,95,39,75,40,41,76],"262":[37,72,74,95,39,75,40,41,76],"269":[37,72,74,95,39,75,40,41,76],"270":[37,72,74,95,39,75,40,41,76],"310":[37,72,74,95,39,75,40,41,76],"326":[37,72,74,95,39,75,40,41,76],"330":[37,72,74,95,39,75,40,41,76],"351":[37,72,74,95,39,75,40,41,76],"379":[37,72,74,95,39,75,40,41,76],"388":[37,72,74,95,39,75,40,41,76],"401":[37,72,74,95,39,75,40,41,76],"442":[37,72,74,95,39,75,40,41,76],"489":[37,72,74,95,39,75,40,41,76],"500":[37,72,74,95,39,75,40,41,76],"502":[37,72,74,95,39,75,40,41,76],"568":[37,72,74,95,39,75,40,41,76],"583":[37,72,74,95,39,75,40,41,76],"646":[37,72,74,95,39,75,40,41,76],"668":[37,72,74,95,39,75,40,41,76],"689":[37,72,74,95,39,75,40,41,76],"709":[37,72,74,95,39,75,40,41,76],"716":[37,72,74,95,39,75,40,41,76],"717":[37,72,74,95,39,75,40,41,76],"722":[37,72,74,95,39,75,40,41,76],"750":[37,72,74,95,39,75,40,41,76],"757":[37,72,74,95,39,75,40,41,76],"771":[37,72,74,95,39,75,40,41,76],"777":[37,72,74,95,39,75,40,41,76],"794":[37,72,74,95,39,75,40,41,76],"845":[37,72,74,95,39,75,40,41,76],"902":[37,72,74,95,39,75,40,41,76],"918":[37,72,74,95,39,75,40,41,76],"933":[37,72,74,95,39,75,40,41,76],"972":[37,72,74,95,39,75,40,41,76],"997":[37,72,74,95,39,75,40,41,76],"1059":[37,72,74,95,39,75,40,41,76],"1113":[37,72,74,95,39,75,40,41,76],"1133":[37,72,74,95,39,75,40,41,76],"1148":[37,72,74,95,39,75,40,41,76],"1187":[37,72,74,95,39,75,40,41,76],"1196":[37,72,74,95,39,75,40,41,76],"1210":[37,72,74,95,39,75,40,41,76],"1219":[37,72,74,95,39,75,40,41,76],"1233":[37,72,74,95,39,75,40,41,76]},"packages/@n8n/instance-ai/src/tools/workflows/summarize-workflow.ts":{"13":[37,72,74,95,39,75,40,76],"19":[37,72,74,95,39,75,40,76],"30":[37,72,74,95,39,75,40,76],"36":[37,72,74,95,39,75,40,76],"45":[37,72,74,95,39,75,40,76],"51":[37,72,74,95,39,75,40,76],"70":[37,72,74,95,39,75,40,76],"79":[37,72,74,95,39,75,40,76],"91":[37,72,74,95,39,75,40,76],"93":[37,72,74,95,39,75,40,76]},"packages/@n8n/instance-ai/src/tools/workflows/validate-workflow.service.ts":{"45":[37,72,74,95,39,75,40,76],"49":[37,72,74,95,39,75,40,76],"61":[37,72,74,95,39,75,40,76],"62":[37,72,74,95,39,75,40,76],"69":[37,72,74,95,39,75,40,76],"74":[37,72,74,95,39,75,40,76],"81":[37,72,74,95,39,75,40,76],"86":[37,72,74,95,39,75,40,76],"91":[37,72,74,95,39,75,40,76],"100":[37,72,74,95,39,75,40,76],"113":[37,72,74,95,39,75,40,76],"137":[37,72,74,95,39,75,40,76],"185":[37,72,74,95,39,75,40,76],"231":[37,72,74,95,39,75,40,76],"235":[37,72,74,95,39,75,40,76],"256":[37,72,74,95,39,75,40,76],"302":[37,72,74,95,39,75,40,76],"317":[37,72,74,95,39,75,40,76],"334":[37,72,74,95,39,75,40,76],"418":[37,72,74,95,39,75,40,76],"428":[37,72,74,95,39,75,40,76],"455":[37,72,74,95,39,75,40,76],"465":[37,72,74,95,39,75,40,76],"474":[37,72,74,95,39,75,40,76],"478":[37,72,74,95,39,75,40,76],"499":[37,72,74,95,39,75,40,76],"519":[37,72,74,95,39,75,40,76],"594":[37,72,74,95,39,75,40,76],"631":[37,72,74,95,39,75,40,76],"672":[37,72,74,95,39,75,40,76],"690":[37,72,74,95,39,75,40,76]},"packages/@n8n/instance-ai/src/tools/workflows/workflow-build-context.ts":{"10":[37,72,74,38,95,39,75,40,41,76],"15":[37,72,74,38,95,39,75,40,41,76],"26":[37,72,74,38,95,39,75,40,41,76],"56":[37,72,74,38,95,39,75,40,41,76],"96":[37,72,74,38,95,39,75,40,41,76],"100":[37,72,74,38,95,39,75,40,41,76],"101":[37,72,74,38,95,39,75,40,41,76],"105":[37,72,74,38,95,39,75,40,41,76],"112":[37,72,74,38,95,39,75,40,41,76]},"packages/@n8n/instance-ai/src/tools/workflows/workflow-build-remediation.ts":{"6":[37,72,74,95,39,75,40,76],"11":[37,72,74,95,39,75,40,76],"15":[37,72,74,95,39,75,40,76],"30":[37,72,74,95,39,75,40,76],"40":[37,72,74,95,39,75,40,76],"52":[37,72,74,95,39,75,40,76],"60":[37,72,74,95,39,75,40,76],"107":[37,72,74,95,39,75,40,76]},"packages/@n8n/instance-ai/src/tools/workflows/workflow-build-reporting.ts":{"8":[37,72,74,95,39,75,40,41,76],"54":[37,72,74,95,39,75,40,41,76],"69":[37,72,74,95,39,75,40,41,76]},"packages/@n8n/instance-ai/src/tools/workflows/workflow-build-routing.ts":{"15":[37,72,74,95,39,75,40,41,76],"24":[37,72,74,95,39,75,40,41,76],"47":[37,72,74,95,39,75,40,41,76],"58":[37,72,74,95,39,75,40,41,76],"100":[37,72,74,95,39,75,40,41,76]},"packages/@n8n/instance-ai/src/tools/workflows/workflow-build-telemetry.ts":{"16":[37,72,74,95,39,75,40,41,76]},"packages/@n8n/instance-ai/src/tools/workflows/workflow-file-bindings.ts":{"25":[37,72,74,38,95,39,75,40,41,76],"29":[37,72,74,38,95,39,75,40,41,76],"33":[37,72,74,38,95,39,75,40,41,76],"38":[37,72,74,38,95,39,75,40,41,76],"48":[37,72,74,38,95,39,75,40,41,76],"64":[37,72,74,38,95,39,75,40,41,76],"79":[37,72,74,38,95,39,75,40,41,76],"92":[37,72,74,38,95,39,75,40,41,76],"112":[37,72,74,38,95,39,75,40,41,76],"127":[37,72,74,38,95,39,75,40,41,76],"139":[37,72,74,38,95,39,75,40,41,76],"156":[37,72,74,38,95,39,75,40,41,76],"176":[37,72,74,38,95,39,75,40,41,76]},"packages/@n8n/instance-ai/src/tools/workflows/workflow-json-utils.ts":{"37":[37,72,74,38,95,39,75,40,41,76],"46":[37,72,74,38,95,39,75,40,41,76],"58":[37,72,74,38,95,39,75,40,41,76],"68":[37,72,74,38,95,39,75,40,41,76],"100":[37,72,74,38,95,39,75,40,41,76],"110":[37,72,74,38,95,39,75,40,41,76],"114":[37,72,74,38,95,39,75,40,41,76],"137":[37,72,74,38,95,39,75,40,41,76],"173":[37,72,74,38,95,39,75,40,41,76],"184":[37,72,74,38,95,39,75,40,41,76],"204":[37,72,74,38,95,39,75,40,41,76],"208":[37,72,74,38,95,39,75,40,41,76],"214":[37,72,74,38,95,39,75,40,41,76],"225":[37,72,74,38,95,39,75,40,41,76],"229":[37,72,74,38,95,39,75,40,41,76],"237":[37,72,74,38,95,39,75,40,41,76],"245":[37,72,74,38,95,39,75,40,41,76],"263":[37,72,74,38,95,39,75,40,41,76],"279":[37,72,74,38,95,39,75,40,41,76],"295":[37,72,74,38,95,39,75,40,41,76],"315":[37,72,74,38,95,39,75,40,41,76],"316":[37,72,74,38,95,39,75,40,41,76]},"packages/@n8n/instance-ai/src/tools/workflows/workflow-source-compiler.ts":{"50":[37,72,74,95,39,75,40,41,76],"59":[37,72,74,95,39,75,40,41,76],"64":[37,72,74,95,39,75,40,41,76],"72":[37,72,74,95,39,75,40,41,76],"74":[37,72,74,95,39,75,40,41,76],"77":[37,72,74,95,39,75,40,41,76],"98":[37,72,74,95,39,75,40,41,76],"127":[37,72,74,95,39,75,40,41,76],"145":[37,72,74,95,39,75,40,41,76],"147":[37,72,74,95,39,75,40,41,76],"150":[37,72,74,95,39,75,40,41,76],"156":[37,72,74,95,39,75,40,41,76],"160":[37,72,74,95,39,75,40,41,76],"175":[37,72,74,95,39,75,40,41,76],"179":[37,72,74,95,39,75,40,41,76],"203":[37,72,74,95,39,75,40,41,76],"204":[37,72,74,95,39,75,40,41,76],"224":[37,72,74,95,39,75,40,41,76],"306":[37,72,74,95,39,75,40,41,76],"342":[37,72,74,95,39,75,40,41,76]},"packages/@n8n/instance-ai/src/tools/workflows/workflow-validation-warnings.ts":{"7":[37,72,74,95,39,75,40,41,76],"20":[37,72,74,95,39,75,40,41,76]},"packages/@n8n/instance-ai/src/tools/workflows.tool.ts":{"282":[37,72,74,38,95,39,75,40,76],"286":[37,72,74,38,95,39,75,40,76],"315":[37,72,74,38,95,39,75,40,76],"321":[37,72,74,38,95,39,75,40,76],"325":[37,72,74,38,95,39,75,40,76],"355":[37,72,74,38,95,39,75,40,76],"361":[37,72,74,38,95,39,75,40,76],"362":[37,72,74,38,95,39,75,40,76],"365":[37,72,74,38,95,39,75,40,76],"375":[37,72,74,38,95,39,75,40,76],"412":[37,72,74,38,95,39,75,40,76],"413":[37,72,74,38,95,39,75,40,76],"426":[37,72,74,38,95,39,75,40,76],"441":[37,72,74,38,95,39,75,40,76],"464":[37,72,74,38,95,39,75,40,76],"497":[37,72,74,38,95,39,75,40,76],"532":[37,72,74,38,95,39,75,40,76],"554":[37,72,74,38,95,39,75,40,76],"576":[37,72,74,38,95,39,75,40,76],"597":[37,72,74,38,95,39,75,40,76],"623":[37,72,74,38,95,39,75,40,76],"643":[37,72,74,38,95,39,75,40,76],"659":[37,72,74,38,95,39,75,40,76],"672":[37,72,74,38,95,39,75,40,76],"673":[37,72,74,38,95,39,75,40,76],"678":[37,72,74,38,95,39,75,40,76],"709":[37,72,74,38,95,39,75,40,76],"774":[37,72,74,38,95,39,75,40,76],"794":[37,72,74,38,95,39,75,40,76],"803":[37,72,74,38,95,39,75,40,76],"848":[37,72,74,38,95,39,75,40,76],"935":[37,72,74,38,95,39,75,40,76],"949":[37,72,74,38,95,39,75,40,76],"980":[37,72,74,38,95,39,75,40,76],"991":[37,72,74,38,95,39,75,40,76],"998":[37,72,74,38,95,39,75,40,76],"1005":[37,72,74,38,95,39,75,40,76],"1043":[37,72,74,38,95,39,75,40,76],"1054":[37,72,74,38,95,39,75,40,76],"1071":[37,72,74,38,95,39,75,40,76],"1100":[37,72,74,38,95,39,75,40,76],"1148":[37,72,74,38,95,39,75,40,76],"1153":[37,72,74,38,95,39,75,40,76],"1154":[37,72,74,38,95,39,75,40,76],"1161":[37,72,74,38,95,39,75,40,76],"1176":[37,72,74,38,95,39,75,40,76],"1194":[37,72,74,38,95,39,75,40,76]},"packages/@n8n/instance-ai/src/tools/workspace.tool.ts":{"108":[37,72,74,95,39,75,40,76],"143":[37,72,74,95,39,75,40,76],"148":[37,72,74,95,39,75,40,76],"153":[37,72,74,95,39,75,40,76],"185":[37,72,74,95,39,75,40,76],"220":[37,72,74,95,39,75,40,76],"228":[37,72,74,95,39,75,40,76],"276":[37,72,74,95,39,75,40,76],"312":[37,72,74,95,39,75,40,76],"346":[37,72,74,95,39,75,40,76],"353":[37,72,74,95,39,75,40,76],"367":[37,72,74,95,39,75,40,76]},"packages/@n8n/instance-ai/src/tracing/langsmith-tracing.ts":{"98":[37,72,74,38,95,39,75,40,41,76],"106":[37,72,74,38,95,39,75,40,41,76],"109":[37,72,74,38,95,39,75,40,41,76],"117":[37,72,74,38,95,39,75,40,41,76],"122":[37,72,74,38,95,39,75,40,41,76],"126":[37,72,74,38,95,39,75,40,41,76],"132":[37,72,74,38,95,39,75,40,41,76],"133":[37,72,74,38,95,39,75,40,41,76],"146":[37,72,74,38,95,39,75,40,41,76],"148":[37,72,74,38,95,39,75,40,41,76],"158":[37,72,74,38,95,39,75,40,41,76],"195":[37,72,74,38,95,39,75,40,41,76],"203":[37,72,74,38,95,39,75,40,41,76],"210":[37,72,74,38,95,39,75,40,41,76],"215":[37,72,74,38,95,39,75,40,41,76],"219":[37,72,74,38,95,39,75,40,41,76],"232":[37,72,74,38,95,39,75,40,41,76],"252":[37,72,74,38,95,39,75,40,41,76],"258":[37,72,74,38,95,39,75,40,41,76],"284":[37,72,74,38,95,39,75,40,41,76],"322":[37,72,74,38,95,39,75,40,41,76],"330":[37,72,74,38,95,39,75,40,41,76],"395":[37,72,74,38,95,39,75,40,41,76],"460":[37,72,74,38,95,39,75,40,41,76],"482":[37,72,74,38,95,39,75,40,41,76],"496":[37,72,74,38,95,39,75,40,41,76],"506":[37,72,74,38,95,39,75,40,41,76],"522":[37,72,74,38,95,39,75,40,41,76],"534":[37,72,74,38,95,39,75,40,41,76],"538":[37,72,74,38,95,39,75,40,41,76],"545":[37,72,74,38,95,39,75,40,41,76],"557":[37,72,74,38,95,39,75,40,41,76],"584":[37,72,74,38,95,39,75,40,41,76],"590":[37,72,74,38,95,39,75,40,41,76],"602":[37,72,74,38,95,39,75,40,41,76],"617":[37,72,74,38,95,39,75,40,41,76],"636":[37,72,74,38,95,39,75,40,41,76],"655":[37,72,74,38,95,39,75,40,41,76],"664":[37,72,74,38,95,39,75,40,41,76],"667":[37,72,74,38,95,39,75,40,41,76],"740":[37,72,74,38,95,39,75,40,41,76],"748":[37,72,74,38,95,39,75,40,41,76],"752":[37,72,74,38,95,39,75,40,41,76],"776":[37,72,74,38,95,39,75,40,41,76],"783":[37,72,74,38,95,39,75,40,41,76],"787":[37,72,74,38,95,39,75,40,41,76],"788":[37,72,74,38,95,39,75,40,41,76],"793":[37,72,74,38,95,39,75,40,41,76],"814":[37,72,74,38,95,39,75,40,41,76],"841":[37,72,74,38,95,39,75,40,41,76],"852":[37,72,74,38,95,39,75,40,41,76],"867":[37,72,74,38,95,39,75,40,41,76],"887":[37,72,74,38,95,39,75,40,41,76],"892":[37,72,74,38,95,39,75,40,41,76],"899":[37,72,74,38,95,39,75,40,41,76],"915":[37,72,74,38,95,39,75,40,41,76],"921":[37,72,74,38,95,39,75,40,41,76],"929":[37,72,74,38,95,39,75,40,41,76],"946":[37,72,74,38,95,39,75,40,41,76],"991":[37,72,74,38,95,39,75,40,41,76],"1011":[37,72,74,38,95,39,75,40,41,76],"1017":[37,72,74,38,95,39,75,40,41,76],"1023":[37,72,74,38,95,39,75,40,41,76],"1071":[37,72,74,38,95,39,75,40,41,76],"1115":[37,72,74,38,95,39,75,40,41,76],"1130":[37,72,74,38,95,39,75,40,41,76],"1169":[37,72,74,38,95,39,75,40,41,76],"1182":[37,72,74,38,95,39,75,40,41,76],"1201":[37,72,74,38,95,39,75,40,41,76],"1213":[37,72,74,38,95,39,75,40,41,76],"1217":[37,72,74,38,95,39,75,40,41,76],"1236":[37,72,74,38,95,39,75,40,41,76],"1239":[37,72,74,38,95,39,75,40,41,76],"1243":[37,72,74,38,95,39,75,40,41,76],"1250":[37,72,74,38,95,39,75,40,41,76],"1260":[37,72,74,38,95,39,75,40,41,76],"1265":[37,72,74,38,95,39,75,40,41,76],"1273":[37,72,74,38,95,39,75,40,41,76],"1283":[37,72,74,38,95,39,75,40,41,76],"1297":[37,72,74,38,95,39,75,40,41,76],"1300":[37,72,74,38,95,39,75,40,41,76],"1305":[37,72,74,38,95,39,75,40,41,76],"1320":[37,72,74,38,95,39,75,40,41,76],"1329":[37,72,74,38,95,39,75,40,41,76],"1332":[37,72,74,38,95,39,75,40,41,76],"1336":[37,72,74,38,95,39,75,40,41,76],"1340":[37,72,74,38,95,39,75,40,41,76],"1365":[37,72,74,38,95,39,75,40,41,76],"1373":[37,72,74,38,95,39,75,40,41,76],"1389":[37,72,74,38,95,39,75,40,41,76],"1397":[37,72,74,38,95,39,75,40,41,76],"1409":[37,72,74,38,95,39,75,40,41,76],"1440":[37,72,74,38,95,39,75,40,41,76],"1447":[37,72,74,38,95,39,75,40,41,76],"1455":[37,72,74,38,95,39,75,40,41,76],"1500":[37,72,74,38,95,39,75,40,41,76],"1524":[37,72,74,38,95,39,75,40,41,76],"1544":[37,72,74,38,95,39,75,40,41,76],"1545":[37,72,74,38,95,39,75,40,41,76],"1546":[37,72,74,38,95,39,75,40,41,76],"1547":[37,72,74,38,95,39,75,40,41,76],"1548":[37,72,74,38,95,39,75,40,41,76],"1549":[37,72,74,38,95,39,75,40,41,76],"1550":[37,72,74,38,95,39,75,40,41,76],"1572":[37,72,74,38,95,39,75,40,41,76],"1581":[37,72,74,38,95,39,75,40,41,76],"1607":[37,72,74,38,95,39,75,40,41,76],"1608":[37,72,74,38,95,39,75,40,41,76],"1622":[37,72,74,38,95,39,75,40,41,76],"1647":[37,72,74,38,95,39,75,40,41,76],"1682":[37,72,74,38,95,39,75,40,41,76],"1691":[37,72,74,38,95,39,75,40,41,76],"1701":[37,72,74,38,95,39,75,40,41,76],"1737":[37,72,74,38,95,39,75,40,41,76],"1754":[37,72,74,38,95,39,75,40,41,76],"1774":[37,72,74,38,95,39,75,40,41,76],"1804":[37,72,74,38,95,39,75,40,41,76],"1814":[37,72,74,38,95,39,75,40,41,76],"1838":[37,72,74,38,95,39,75,40,41,76],"1860":[37,72,74,38,95,39,75,40,41,76],"1890":[37,72,74,38,95,39,75,40,41,76],"1941":[37,72,74,38,95,39,75,40,41,76],"1951":[37,72,74,38,95,39,75,40,41,76],"2000":[37,72,74,38,95,39,75,40,41,76],"2016":[37,72,74,38,95,39,75,40,41,76]},"packages/@n8n/instance-ai/src/tracing/trace-labels.ts":{"1":[37,72,74,95,39,75,40,76],"9":[37,72,74,95,39,75,40,76],"13":[37,72,74,95,39,75,40,76],"24":[37,72,74,95,39,75,40,76],"28":[37,72,74,95,39,75,40,76]},"packages/@n8n/instance-ai/src/tracing/trace-payloads.ts":{"60":[37,72,74,95,39,75,40,76],"77":[37,72,74,95,39,75,40,76],"111":[37,72,74,95,39,75,40,76],"119":[37,72,74,95,39,75,40,76],"133":[37,72,74,95,39,75,40,76],"134":[37,72,74,95,39,75,40,76],"136":[37,72,74,95,39,75,40,76],"139":[37,72,74,95,39,75,40,76],"142":[37,72,74,95,39,75,40,76],"155":[37,72,74,95,39,75,40,76],"177":[37,72,74,95,39,75,40,76],"200":[37,72,74,95,39,75,40,76],"214":[37,72,74,95,39,75,40,76],"226":[37,72,74,95,39,75,40,76],"262":[37,72,74,95,39,75,40,76],"275":[37,72,74,95,39,75,40,76],"290":[37,72,74,95,39,75,40,76],"297":[37,72,74,95,39,75,40,76],"304":[37,72,74,95,39,75,40,76],"320":[37,72,74,95,39,75,40,76],"347":[37,72,74,95,39,75,40,76],"381":[37,72,74,95,39,75,40,76],"418":[37,72,74,95,39,75,40,76],"443":[37,72,74,95,39,75,40,76],"445":[37,72,74,95,39,75,40,76],"449":[37,72,74,95,39,75,40,76],"451":[37,72,74,95,39,75,40,76],"458":[37,72,74,95,39,75,40,76],"462":[37,72,74,95,39,75,40,76],"472":[37,72,74,95,39,75,40,76],"487":[37,72,74,95,39,75,40,76],"513":[37,72,74,95,39,75,40,76],"544":[37,72,74,95,39,75,40,76],"555":[37,72,74,95,39,75,40,76],"570":[37,72,74,95,39,75,40,76],"584":[37,72,74,95,39,75,40,76],"591":[37,72,74,95,39,75,40,76],"604":[37,72,74,95,39,75,40,76],"622":[37,72,74,95,39,75,40,76],"644":[37,72,74,95,39,75,40,76],"672":[37,72,74,95,39,75,40,76],"724":[37,72,74,95,39,75,40,76],"769":[37,72,74,95,39,75,40,76],"806":[37,72,74,95,39,75,40,76],"819":[37,72,74,95,39,75,40,76],"827":[37,72,74,95,39,75,40,76],"860":[37,72,74,95,39,75,40,76],"865":[37,72,74,95,39,75,40,76],"875":[37,72,74,95,39,75,40,76],"884":[37,72,74,95,39,75,40,76],"895":[37,72,74,95,39,75,40,76],"932":[37,72,74,95,39,75,40,76],"966":[37,72,74,95,39,75,40,76],"975":[37,72,74,95,39,75,40,76],"990":[37,72,74,95,39,75,40,76],"994":[37,72,74,95,39,75,40,76],"1009":[37,72,74,95,39,75,40,76],"1036":[37,72,74,95,39,75,40,76],"1060":[37,72,74,95,39,75,40,76],"1067":[37,72,74,95,39,75,40,76],"1071":[37,72,74,95,39,75,40,76],"1079":[37,72,74,95,39,75,40,76],"1102":[37,72,74,95,39,75,40,76],"1120":[37,72,74,95,39,75,40,76],"1143":[37,72,74,95,39,75,40,76],"1170":[37,72,74,95,39,75,40,76],"1202":[37,72,74,95,39,75,40,76],"1211":[37,72,74,95,39,75,40,76],"1213":[37,72,74,95,39,75,40,76],"1228":[37,72,74,95,39,75,40,76],"1240":[37,72,74,95,39,75,40,76],"1250":[37,72,74,95,39,75,40,76],"1251":[37,72,74,95,39,75,40,76],"1257":[37,72,74,95,39,75,40,76],"1266":[37,72,74,95,39,75,40,76],"1279":[37,72,74,95,39,75,40,76],"1292":[37,72,74,95,39,75,40,76],"1314":[37,72,74,95,39,75,40,76],"1332":[37,72,74,95,39,75,40,76],"1344":[37,72,74,95,39,75,40,76],"1356":[37,72,74,95,39,75,40,76],"1369":[37,72,74,95,39,75,40,76]},"packages/@n8n/instance-ai/src/tracing/trace-replay.ts":{"45":[37,72,74,95,39,75,40,41,76],"61":[37,72,74,95,39,75,40,41,76],"76":[37,72,74,95,39,75,40,41,76],"96":[37,72,74,95,39,75,40,41,76],"137":[37,72,74,95,39,75,40,41,76],"152":[37,72,74,95,39,75,40,41,76],"158":[37,72,74,95,39,75,40,41,76],"162":[37,72,74,95,39,75,40,41,76],"200":[37,72,74,95,39,75,40,41,76],"224":[37,72,74,95,39,75,40,41,76],"240":[37,72,74,95,39,75,40,41,76],"262":[37,72,74,95,39,75,40,41,76],"267":[37,72,74,95,39,75,40,41,76],"275":[37,72,74,95,39,75,40,41,76],"279":[37,72,74,95,39,75,40,41,76],"303":[37,72,74,95,39,75,40,41,76],"312":[37,72,74,95,39,75,40,41,76],"328":[37,72,74,95,39,75,40,41,76],"346":[37,72,74,95,39,75,40,41,76],"364":[37,72,74,95,39,75,40,41,76],"368":[37,72,74,95,39,75,40,41,76],"369":[37,72,74,95,39,75,40,41,76],"377":[37,72,74,95,39,75,40,41,76],"383":[37,72,74,95,39,75,40,41,76]},"packages/@n8n/instance-ai/src/utils/agent-feature-enabled.ts":{"3":[37,72,74,95,39,75,40,76],"7":[37,72,74,95,39,75,40,76],"8":[37,72,74,95,39,75,40,76]},"packages/@n8n/instance-ai/src/utils/agent-tree.ts":{"4":[37,72,73,74,39,75,40,76],"12":[37,72,73,74,39,75,40,76]},"packages/@n8n/instance-ai/src/utils/eval-agents.ts":{"35":[37,72,74,95,39,75,40,76],"44":[37,72,74,95,39,75,40,76],"67":[37,72,74,95,39,75,40,76],"73":[37,72,74,95,39,75,40,76],"109":[37,72,74,95,39,75,40,76],"119":[37,72,74,95,39,75,40,76],"147":[37,72,74,95,39,75,40,76]},"packages/@n8n/instance-ai/src/utils/format-timestamp.ts":{"2":[37,72,74,95,39,75,40,76]},"packages/@n8n/instance-ai/src/utils/generate-validated-json.ts":{"33":[37,72,74,95,39,75,40,76],"39":[37,72,74,95,39,75,40,76]},"packages/@n8n/instance-ai/src/utils/stream-helpers.ts":{"20":[37,72,74,38,95,39,75,40,76],"46":[37,72,74,38,95,39,75,40,76],"50":[37,72,74,38,95,39,75,40,76]},"packages/@n8n/instance-ai/src/workflow-builder/build-failure-tracker.ts":{"7":[37,72,74,95,39,75,40,41,76],"9":[37,72,74,95,39,75,40,41,76],"36":[37,72,74,95,39,75,40,41,76],"63":[37,72,74,95,39,75,40,41,76],"85":[37,72,74,95,39,75,40,41,76]},"packages/@n8n/instance-ai/src/workflow-loop/guidance.ts":{"7":[37,72,74,95,39,75,40,76],"15":[37,72,74,95,39,75,40,76]},"packages/@n8n/instance-ai/src/workflow-loop/index.ts":{"61":[37,72,74,95,39,75,40,41,76]},"packages/@n8n/instance-ai/src/workflow-loop/remediation.ts":{"11":[37,72,74,38,95,39,75,40,76],"22":[37,72,74,38,95,39,75,40,76],"29":[37,72,74,38,95,39,75,40,76]},"packages/@n8n/instance-ai/src/workflow-loop/runtime.ts":{"10":[37,72,74,95,39,75,40,41,76],"29":[37,72,74,95,39,75,40,41,76],"31":[37,72,74,95,39,75,40,41,76],"46":[37,72,74,95,39,75,40,41,76]},"packages/@n8n/instance-ai/src/workflow-loop/setup-verification-policy.ts":{"3":[37,72,74,95,39,75,40,41,76],"9":[37,72,74,95,39,75,40,41,76],"13":[37,72,74,95,39,75,40,41,76],"22":[37,72,74,95,39,75,40,41,76]},"packages/@n8n/instance-ai/src/workflow-loop/verification-obligation.ts":{"36":[37,72,74,95,39,75,40,41,76],"46":[37,72,74,95,39,75,40,41,76],"54":[37,72,74,95,39,75,40,41,76],"58":[37,72,74,95,39,75,40,41,76],"69":[37,72,74,95,39,75,40,41,76],"103":[37,72,74,95,39,75,40,41,76],"112":[37,72,74,95,39,75,40,41,76],"159":[37,72,74,95,39,75,40,41,76],"163":[37,72,74,95,39,75,40,41,76],"195":[37,72,74,95,39,75,40,41,76],"207":[37,72,74,95,39,75,40,41,76]},"packages/@n8n/instance-ai/src/workflow-loop/workflow-loop-controller.ts":{"38":[37,72,74,95,39,75,40,41,76],"66":[37,72,74,95,39,75,40,41,76],"223":[37,72,74,95,39,75,40,41,76],"384":[37,72,74,95,39,75,40,41,76],"473":[37,72,74,95,39,75,40,41,76],"477":[37,72,74,95,39,75,40,41,76],"481":[37,72,74,95,39,75,40,41,76],"492":[37,72,74,95,39,75,40,41,76],"508":[37,72,74,95,39,75,40,41,76],"523":[37,72,74,95,39,75,40,41,76],"525":[37,72,74,95,39,75,40,41,76],"530":[37,72,74,95,39,75,40,41,76],"535":[37,72,74,95,39,75,40,41,76],"550":[37,72,74,95,39,75,40,41,76],"553":[37,72,74,95,39,75,40,41,76]},"packages/@n8n/instance-ai/src/workflow-loop/workflow-loop-state.ts":{"47":[37,72,73,74,39,75,40,41,76],"57":[37,72,73,74,39,75,40,41,76],"63":[37,72,73,74,39,75,40,41,76]},"packages/@n8n/instance-ai/src/workflow-loop/workflow-task-service.ts":{"11":[37,72,74,95,39,75,40,41,76],"22":[37,72,74,95,39,75,40,41,76],"29":[37,72,74,95,39,75,40,41,76],"33":[37,72,74,95,39,75,40,41,76],"37":[37,72,74,95,39,75,40,41,76],"42":[37,72,74,95,39,75,40,41,76],"47":[37,72,74,95,39,75,40,41,76],"48":[37,72,74,95,39,75,40,41,76],"49":[37,72,74,95,39,75,40,41,76],"54":[37,72,74,95,39,75,40,41,76],"59":[37,72,74,95,39,75,40,41,76]},"packages/@n8n/instance-ai/src/workspace/builder-templates-service.ts":{"143":[37,72,74,95,39,75,40,41,76],"157":[37,72,74,95,39,75,40,41,76],"161":[37,72,74,95,39,75,40,41,76],"165":[37,72,74,95,39,75,40,41,76],"170":[37,72,74,95,39,75,40,41,76],"193":[37,72,74,95,39,75,40,41,76],"201":[37,72,74,95,39,75,40,41,76],"212":[37,72,74,95,39,75,40,41,76],"220":[37,72,74,95,39,75,40,41,76],"225":[37,72,74,95,39,75,40,41,76],"234":[37,72,74,95,39,75,40,41,76],"279":[37,72,74,95,39,75,40,41,76],"288":[37,72,74,95,39,75,40,41,76],"297":[37,72,74,95,39,75,40,41,76],"307":[37,72,74,95,39,75,40,41,76],"343":[37,72,74,95,39,75,40,41,76],"365":[37,72,74,95,39,75,40,41,76],"445":[37,72,74,95,39,75,40,41,76],"478":[37,72,74,95,39,75,40,41,76],"510":[37,72,74,95,39,75,40,41,76],"516":[37,72,74,95,39,75,40,41,76],"525":[37,72,74,95,39,75,40,41,76],"534":[37,72,74,95,39,75,40,41,76],"540":[37,72,74,95,39,75,40,41,76],"545":[37,72,74,95,39,75,40,41,76],"546":[37,72,74,95,39,75,40,41,76],"549":[37,72,74,95,39,75,40,41,76],"564":[37,72,74,95,39,75,40,41,76],"572":[37,72,74,95,39,75,40,41,76],"589":[37,72,74,95,39,75,40,41,76],"606":[37,72,74,95,39,75,40,41,76]},"packages/@n8n/instance-ai/src/workspace/compute-workspace-content-hash.ts":{"4":[37,72,74,95,39,75,40,41,76],"6":[37,72,74,95,39,75,40,41,76]},"packages/@n8n/instance-ai/src/workspace/create-workspace.ts":{"38":[37,72,74,95,39,75,40,76],"39":[37,72,74,95,39,75,40,76],"40":[37,72,74,95,39,75,40,76],"41":[37,72,74,95,39,75,40,76],"45":[37,72,74,95,39,75,40,76],"49":[37,72,74,95,39,75,40,76],"67":[37,72,74,95,39,75,40,76],"79":[37,72,74,95,39,75,40,76],"162":[37,72,74,95,39,75,40,76]},"packages/@n8n/instance-ai/src/workspace/lazy-runtime-workspace.ts":{"54":[37,72,74,95,39,75,40,41,76],"71":[37,72,74,95,39,75,40,41,76],"72":[37,72,74,95,39,75,40,41,76],"84":[37,72,74,95,39,75,40,41,76],"86":[37,72,74,95,39,75,40,41,76],"90":[37,72,74,95,39,75,40,41,76],"95":[37,72,74,95,39,75,40,41,76],"99":[37,72,74,95,39,75,40,41,76],"101":[37,72,74,95,39,75,40,41,76],"105":[37,72,74,95,39,75,40,41,76],"119":[37,72,74,95,39,75,40,41,76],"123":[37,72,74,95,39,75,40,41,76],"129":[37,72,74,95,39,75,40,41,76],"141":[37,72,74,95,39,75,40,41,76],"150":[37,72,74,95,39,75,40,41,76],"159":[37,72,74,95,39,75,40,41,76],"168":[37,72,74,95,39,75,40,41,76],"181":[37,72,74,95,39,75,40,41,76],"186":[37,72,74,95,39,75,40,41,76],"189":[37,72,74,95,39,75,40,41,76],"194":[37,72,74,95,39,75,40,41,76],"198":[37,72,74,95,39,75,40,41,76],"202":[37,72,74,95,39,75,40,41,76],"208":[37,72,74,95,39,75,40,41,76],"212":[37,72,74,95,39,75,40,41,76],"229":[37,72,74,95,39,75,40,41,76],"233":[37,72,74,95,39,75,40,41,76],"237":[37,72,74,95,39,75,40,41,76],"241":[37,72,74,95,39,75,40,41,76],"245":[37,72,74,95,39,75,40,41,76],"249":[37,72,74,95,39,75,40,41,76],"253":[37,72,74,95,39,75,40,41,76],"257":[37,72,74,95,39,75,40,41,76],"261":[37,72,74,95,39,75,40,41,76],"265":[37,72,74,95,39,75,40,41,76],"269":[37,72,74,95,39,75,40,41,76],"273":[37,72,74,95,39,75,40,41,76],"275":[37,72,74,95,39,75,40,41,76],"282":[37,72,74,95,39,75,40,41,76],"293":[37,72,74,95,39,75,40,41,76],"298":[37,72,74,95,39,75,40,41,76],"301":[37,72,74,95,39,75,40,41,76],"306":[37,72,74,95,39,75,40,41,76],"312":[37,72,74,95,39,75,40,41,76],"319":[37,72,74,95,39,75,40,41,76],"323":[37,72,74,95,39,75,40,41,76],"327":[37,72,74,95,39,75,40,41,76],"348":[37,72,74,95,39,75,40,41,76],"363":[37,72,74,95,39,75,40,41,76],"364":[37,72,74,95,39,75,40,41,76],"369":[37,72,74,95,39,75,40,41,76]},"packages/@n8n/instance-ai/src/workspace/pack-workspace-sdk.ts":{"56":[37,72,74,95,39,75,40,76],"64":[37,72,74,95,39,75,40,76],"123":[37,72,74,95,39,75,40,76],"148":[37,72,74,95,39,75,40,76],"160":[37,72,74,95,39,75,40,76],"174":[37,72,74,95,39,75,40,76],"179":[37,72,74,95,39,75,40,76]},"packages/@n8n/instance-ai/src/workspace/prebaked-workspace-bundle.ts":{"30":[37,72,74,95,39,75,40,76],"63":[37,72,74,95,39,75,40,76],"66":[37,72,74,95,39,75,40,76],"75":[37,72,74,95,39,75,40,76],"101":[37,72,74,95,39,75,40,76]},"packages/@n8n/instance-ai/src/workspace/sandbox-fs.ts":{"56":[37,72,74,95,39,75,40,41,76],"60":[37,72,74,95,39,75,40,41,76],"62":[37,72,74,95,39,75,40,41,76],"63":[37,72,74,95,39,75,40,41,76],"67":[37,72,74,95,39,75,40,41,76],"76":[37,72,74,95,39,75,40,41,76],"82":[37,72,74,95,39,75,40,41,76],"115":[37,72,74,95,39,75,40,41,76],"140":[37,72,74,95,39,75,40,41,76],"147":[37,72,74,95,39,75,40,41,76],"148":[37,72,74,95,39,75,40,41,76],"196":[37,72,74,95,39,75,40,41,76],"202":[37,72,74,95,39,75,40,41,76],"217":[37,72,74,95,39,75,40,41,76]},"packages/@n8n/instance-ai/src/workspace/sandbox-setup.ts":{"64":[37,72,74,38,95,39,75,40,41,76],"69":[37,72,74,38,95,39,75,40,41,76],"78":[37,72,74,38,95,39,75,40,41,76],"91":[37,72,74,38,95,39,75,40,41,76],"124":[37,72,74,38,95,39,75,40,41,76],"140":[37,72,74,38,95,39,75,40,41,76],"178":[37,72,74,38,95,39,75,40,41,76],"185":[37,72,74,38,95,39,75,40,41,76],"209":[37,72,74,38,95,39,75,40,41,76],"225":[37,72,74,38,95,39,75,40,41,76],"288":[37,72,74,38,95,39,75,40,41,76],"305":[37,72,74,38,95,39,75,40,41,76],"315":[37,72,74,38,95,39,75,40,41,76],"321":[37,72,74,38,95,39,75,40,41,76],"329":[37,72,74,38,95,39,75,40,41,76],"343":[37,72,74,38,95,39,75,40,41,76],"364":[37,72,74,38,95,39,75,40,41,76],"383":[37,72,74,38,95,39,75,40,41,76],"400":[37,72,74,38,95,39,75,40,41,76],"405":[37,72,74,38,95,39,75,40,41,76],"424":[37,72,74,38,95,39,75,40,41,76],"430":[37,72,74,38,95,39,75,40,41,76],"437":[37,72,74,38,95,39,75,40,41,76],"455":[37,72,74,38,95,39,75,40,41,76],"464":[37,72,74,38,95,39,75,40,41,76],"482":[37,72,74,38,95,39,75,40,41,76],"487":[37,72,74,38,95,39,75,40,41,76],"496":[37,72,74,38,95,39,75,40,41,76],"501":[37,72,74,38,95,39,75,40,41,76]},"packages/@n8n/instance-ai/src/workspace/scoped-workspace.ts":{"20":[37,72,74,95,39,75,40,41,76],"25":[37,72,74,95,39,75,40,41,76],"39":[37,72,74,95,39,75,40,41,76],"44":[37,72,74,95,39,75,40,41,76],"48":[37,72,74,95,39,75,40,41,76],"52":[37,72,74,95,39,75,40,41,76],"56":[37,72,74,95,39,75,40,41,76],"60":[37,72,74,95,39,75,40,41,76],"64":[37,72,74,95,39,75,40,41,76],"68":[37,72,74,95,39,75,40,41,76],"72":[37,72,74,95,39,75,40,41,76],"77":[37,72,74,95,39,75,40,41,76],"81":[37,72,74,95,39,75,40,41,76],"85":[37,72,74,95,39,75,40,41,76],"89":[37,72,74,95,39,75,40,41,76],"93":[37,72,74,95,39,75,40,41,76],"101":[37,72,74,95,39,75,40,41,76],"109":[37,72,74,95,39,75,40,41,76],"113":[37,72,74,95,39,75,40,41,76],"117":[37,72,74,95,39,75,40,41,76],"121":[37,72,74,95,39,75,40,41,76],"125":[37,72,74,95,39,75,40,41,76],"133":[37,72,74,95,39,75,40,41,76],"140":[37,72,74,95,39,75,40,41,76],"154":[37,72,74,95,39,75,40,41,76],"158":[37,72,74,95,39,75,40,41,76],"162":[37,72,74,95,39,75,40,41,76],"166":[37,72,74,95,39,75,40,41,76],"170":[37,72,74,95,39,75,40,41,76],"174":[37,72,74,95,39,75,40,41,76],"178":[37,72,74,95,39,75,40,41,76],"184":[37,72,74,95,39,75,40,41,76]},"packages/@n8n/instance-ai/src/workspace/snapshot-image-context.ts":{"18":[37,72,74,95,39,75,40,76],"30":[37,72,74,95,39,75,40,76],"48":[37,72,74,95,39,75,40,76],"62":[37,72,74,95,39,75,40,76],"68":[37,72,74,95,39,75,40,76],"78":[37,72,74,95,39,75,40,76]},"packages/@n8n/instance-ai/src/workspace/snapshot-manager.ts":{"44":[37,72,74,95,39,75,40,76],"45":[37,72,74,95,39,75,40,76],"47":[37,72,74,95,39,75,40,76],"62":[37,72,74,95,39,75,40,76],"71":[37,72,74,95,39,75,40,76],"76":[37,72,74,95,39,75,40,76],"99":[37,72,74,95,39,75,40,76],"129":[37,72,74,95,39,75,40,76],"158":[37,72,74,95,39,75,40,76],"194":[37,72,74,95,39,75,40,76],"199":[37,72,74,95,39,75,40,76],"209":[37,72,74,95,39,75,40,76],"214":[37,72,74,95,39,75,40,76],"232":[37,72,74,95,39,75,40,76]},"packages/@n8n/instance-ai/src/workspace/template-telemetry.ts":{"59":[37,72,74,95,39,75,40,41,76],"74":[37,72,74,95,39,75,40,41,76],"78":[37,72,74,95,39,75,40,41,76],"88":[37,72,74,95,39,75,40,41,76],"97":[37,72,74,95,39,75,40,41,76],"125":[37,72,74,95,39,75,40,41,76],"144":[37,72,74,95,39,75,40,41,76],"149":[37,72,74,95,39,75,40,41,76],"159":[37,72,74,95,39,75,40,41,76],"166":[37,72,74,95,39,75,40,41,76],"176":[37,72,74,95,39,75,40,41,76],"187":[37,72,74,95,39,75,40,41,76],"195":[37,72,74,95,39,75,40,41,76],"200":[37,72,74,95,39,75,40,41,76],"224":[37,72,74,95,39,75,40,41,76],"229":[37,72,74,95,39,75,40,41,76],"260":[37,72,74,95,39,75,40,41,76],"279":[37,72,74,95,39,75,40,41,76]},"packages/@n8n/instance-ai/src/workspace/workspace-file-content.ts":{"2":[37,72,74,95,39,75,40,76],"7":[37,72,74,95,39,75,40,76]},"packages/@n8n/instance-ai/src/workspace/workspace-files.ts":{"35":[37,72,74,95,39,75,40,41,76],"39":[37,72,74,95,39,75,40,41,76],"47":[37,72,74,95,39,75,40,41,76],"59":[37,72,74,95,39,75,40,41,76],"107":[37,72,74,95,39,75,40,41,76],"119":[37,72,74,95,39,75,40,41,76],"158":[37,72,74,95,39,75,40,41,76],"164":[37,72,74,95,39,75,40,41,76]},"packages/@n8n/instance-ai/src/workspace/workspace-manifest.ts":{"17":[37,72,74,95,39,75,40,76]},"packages/@n8n/instance-ai/src/workspace/workspace-paths.ts":{"1":[37,72,74,95,39,75,40,41,76],"8":[37,72,74,95,39,75,40,41,76],"16":[37,72,74,95,39,75,40,41,76],"24":[37,72,74,95,39,75,40,41,76]},"packages/@n8n/utils/src/is-record.ts":{"1":"b:egAAAOADAAAAHwAAAAAAAAAAAAAQMAAA"},"packages/@n8n/utils/src/json/to-json-value.ts":{"16":[37,72,39,40,41],"49":[37,72,39,40,41]},"packages/@n8n/utils/src/lazy-import.ts":{"15":[20,37,72,74,95,39,75,40,76,172,164]},"packages/@n8n/workflow-sdk/src/codegen/code-generator.ts":{"84":[38,40],"92":[38,40],"141":[38,40],"170":[38,40],"181":[38,40],"205":[38,40],"209":[38,40],"251":[38,40],"282":[38,40],"331":[38,40],"360":[38,40],"371":[38,40],"395":[38,40],"410":[38,40],"456":[38,40],"462":[38,40],"478":[38,40],"512":[38,40],"527":[38,40],"563":[38,40],"606":[38,40],"646":[38,40],"667":[38,40],"693":[38,40],"708":[38,40],"728":[38,40],"735":[38,40],"743":[38,40],"754":[38,40],"788":[38,40],"801":[38,40],"816":[38,40],"821":[38,40],"836":[38,40],"841":[38,40],"857":[38,40],"876":[38,40],"891":[38,40],"946":[38,40],"954":[38,40],"964":[38,40],"982":[38,40],"992":[38,40],"1020":[38,40],"1047":[38,40],"1075":[38,40],"1157":[38,40],"1169":[38,40],"1179":[38,40],"1187":[38,40],"1201":[38,40],"1217":[38,40],"1273":[38,40],"1328":[38,40],"1457":[38,40]},"packages/@n8n/workflow-sdk/src/codegen/composite-builder.ts":{"50":[38,40],"77":[38,40],"144":[38,40],"185":[38,40],"328":[38,40],"355":[38,40],"387":[38,40],"454":[38,40],"491":[38,40],"528":[38,40],"586":[38,40],"701":[38,40],"814":[38,40],"969":[38,40],"1067":[38,40],"1162":[38,40],"1196":[38,40],"1275":[38,40],"1284":[38,40]},"packages/@n8n/workflow-sdk/src/codegen/composite-handlers/build-utils.ts":{"19":[38,40],"42":[38,40],"53":[38,40],"65":[38,40],"75":[38,40],"82":[38,40],"89":[38,40],"103":[38,40],"134":[38,40],"142":[38,40],"146":[38,40],"157":[38,40],"166":[38,40],"171":[38,40]},"packages/@n8n/workflow-sdk/src/codegen/execution-status.ts":{"17":[38,40],"46":[38,40],"61":[38,40],"63":[38,40]},"packages/@n8n/workflow-sdk/src/codegen/expression-annotator.ts":{"11":[38,40],"35":[38,40]},"packages/@n8n/workflow-sdk/src/codegen/graph-annotator.ts":{"16":[38,40],"20":[38,40],"79":[38,40],"104":[38,40],"114":[38,40],"141":[38,40],"157":[38,40]},"packages/@n8n/workflow-sdk/src/codegen/index.ts":{"54":"b:AAAAAOADAAAAHQCAAAAAAAAAAAAQEAAA","55":"b:AAAAAOADAAAAHQCAAAAAAAAAAAAQEAAA","57":"b:AAAAAOADAAAAHQCAAAAAAAAAAAAQEAAA","74":"b:AAAAAOADAAAAHQCAAAAAAAAAAAAQEAAA","101":"b:AAAAAOADAAAAHQCAAAAAAAAAAAAQEAAA"},"packages/@n8n/workflow-sdk/src/codegen/node-type-utils.ts":{"10":[38,40],"17":[38,40],"24":[38,40],"32":[38,40],"39":[38,40]},"packages/@n8n/workflow-sdk/src/codegen/output-utils.ts":{"24":[38,40],"41":[38,40],"62":[38,40],"75":[38,40],"92":[38,40],"107":[38,40],"114":[38,40]},"packages/@n8n/workflow-sdk/src/codegen/semantic-graph.ts":{"20":[38,40],"44":[38,40],"52":[38,40],"60":[38,40],"98":[38,40],"139":[38,40],"148":[38,40],"151":[38,40],"168":[38,40],"206":[38,40],"240":[38,40],"329":[38,40],"352":[38,40],"384":[38,40]},"packages/@n8n/workflow-sdk/src/codegen/semantic-registry.ts":{"42":[38,40],"48":[38,40],"57":[38,40],"60":[38,40],"76":[38,40],"85":[38,40],"105":[38,40],"137":[38,40],"158":[38,40],"169":[38,40],"199":[38,40]},"packages/@n8n/workflow-sdk/src/codegen/string-utils.ts":{"9":[38,40],"24":[38,40],"32":[38,40],"39":[38,40],"47":[38,40],"55":[38,40]},"packages/@n8n/workflow-sdk/src/codegen/subnode-generator.ts":{"28":[38,40],"36":[38,40],"76":[38,40],"94":[38,40],"101":[38,40],"104":[38,40],"114":[38,40],"142":[38,40],"147":[38,40],"156":[38,40],"165":[38,40],"178":[38,40],"187":[38,40],"199":[38,40],"208":[38,40],"251":[38,40],"281":[38,40],"300":[38,40],"326":[38,40],"335":[38,40],"356":[38,40],"378":[38,40],"387":[38,40]},"packages/@n8n/workflow-sdk/src/codegen/variable-names.ts":{"102":[38,40],"115":[38,40],"120":[38,40],"151":[38,40],"162":[38,40]},"packages/@n8n/workflow-sdk/src/constants/node-types.ts":{"24":[37,38,39,40,41],"31":[37,38,39,40,41],"38":[37,38,39,40,41],"45":[37,38,39,40,41],"52":[37,38,39,40,41],"59":[37,38,39,40,41],"66":[37,38,39,40,41],"73":[37,38,39,40,41]},"packages/@n8n/workflow-sdk/src/generate-types/index.ts":{"82":[38,39,40,41,172,164]},"packages/@n8n/workflow-sdk/src/index.ts":{"113":"b:AAAAAOADAAAAHQCAAAAAAAAAAAAQEAAA","126":"b:AAAAAOADAAAAHQCAAAAAAAAAAAAQEAAA","165":"b:AAAAAOADAAAAHQCAAAAAAAAAAAAQEAAA"},"packages/@n8n/workflow-sdk/src/mock-data/index.ts":{"27":[38,39,40,41,172,164]},"packages/@n8n/workflow-sdk/src/prompts/best-practices/guides/chatbot.ts":{"4":[37,39,40,41],"125":[37,39,40,41]},"packages/@n8n/workflow-sdk/src/prompts/best-practices/guides/content-generation.ts":{"4":[37,39,40,41],"111":[37,39,40,41]},"packages/@n8n/workflow-sdk/src/prompts/best-practices/guides/data-extraction.ts":{"4":[37,39,40,41],"118":[37,39,40,41]},"packages/@n8n/workflow-sdk/src/prompts/best-practices/guides/data-persistence.ts":{"4":[37,39,40,41],"193":[37,39,40,41]},"packages/@n8n/workflow-sdk/src/prompts/best-practices/guides/data-transformation.ts":{"4":[37,39,40,41],"142":[37,39,40,41]},"packages/@n8n/workflow-sdk/src/prompts/best-practices/guides/document-processing.ts":{"4":[37,39,40,41],"328":[37,39,40,41]},"packages/@n8n/workflow-sdk/src/prompts/best-practices/guides/form-input.ts":{"4":[37,39,40,41],"169":[37,39,40,41]},"packages/@n8n/workflow-sdk/src/prompts/best-practices/guides/notification.ts":{"4":[37,39,40,41],"131":[37,39,40,41]},"packages/@n8n/workflow-sdk/src/prompts/best-practices/guides/scheduling.ts":{"4":[37,39,40,41],"148":[37,39,40,41]},"packages/@n8n/workflow-sdk/src/prompts/best-practices/guides/scraping-and-research.ts":{"4":[37,39,40,41],"154":[37,39,40,41]},"packages/@n8n/workflow-sdk/src/prompts/best-practices/guides/triage.ts":{"4":[37,39,40,41],"145":[37,39,40,41]},"packages/@n8n/workflow-sdk/src/prompts/best-practices/guides/web-app.ts":{"4":[37,39,40,41],"200":[37,39,40,41]},"packages/@n8n/workflow-sdk/src/prompts/best-practices/index.ts":{"1":[37,39,40,41],"4":[37,39,40,41],"7":[37,39,40,41],"8":[37,39,40,41],"14":[37,39,40,41],"19":[37,39,40,41],"20":[37,39,40,41]},"packages/@n8n/workflow-sdk/src/prompts/sdk-reference/index.ts":{"3":[37,72,74,95,39,75,40,76]},"packages/@n8n/workflow-sdk/src/types/base.ts":{"168":[38,40],"199":[38,40],"238":[38,40],"285":[38,40],"638":[38,40],"654":[38,40]},"packages/@n8n/workflow-sdk/src/utils/trigger-detection.ts":{"30":[38,40]},"packages/@n8n/workflow-sdk/src/validation/display-options.ts":{"77":[37,39,40,41],"90":[37,39,40,41],"98":[37,39,40,41],"115":[37,39,40,41],"156":[37,39,40,41],"157":[37,39,40,41],"167":[37,39,40,41],"231":[37,39,40,41],"274":[37,39,40,41],"289":[37,39,40,41],"311":[37,39,40,41],"332":[37,39,40,41],"333":[37,39,40,41],"349":[37,39,40,41]},"packages/@n8n/workflow-sdk/src/validation/index.ts":{"68":"b:AAAAAOADAAAAHQCAAAAAAAAAAAAQEAAA","95":"b:AAAAAOADAAAAHQCAAAAAAAAAAAAQEAAA","143":"b:AAAAAOADAAAAHQCAAAAAAAAAAAAQEAAA","208":"b:AAAAAOADAAAAHQCAAAAAAAAAAAAQEAAA","271":"b:AAAAAOADAAAAHQCAAAAAAAAAAAAQEAAA","291":"b:AAAAAOADAAAAHQCAAAAAAAAAAAAQEAAA","309":"b:AAAAAOADAAAAHQCAAAAAAAAAAAAQEAAA","374":"b:AAAAAOADAAAAHQCAAAAAAAAAAAAQEAAA","386":"b:AAAAAOADAAAAHQCAAAAAAAAAAAAQEAAA","477":"b:AAAAAOADAAAAHQCAAAAAAAAAAAAQEAAA","545":"b:AAAAAOADAAAAHQCAAAAAAAAAAAAQEAAA","602":"b:AAAAAOADAAAAHQCAAAAAAAAAAAAQEAAA","635":"b:AAAAAOADAAAAHQCAAAAAAAAAAAAQEAAA","698":"b:AAAAAOADAAAAHQCAAAAAAAAAAAAQEAAA","706":"b:AAAAAOADAAAAHQCAAAAAAAAAAAAQEAAA","707":"b:AAAAAOADAAAAHQCAAAAAAAAAAAAQEAAA","729":"b:AAAAAOADAAAAHQCAAAAAAAAAAAAQEAAA","742":"b:AAAAAOADAAAAHQCAAAAAAAAAAAAQEAAA","756":"b:AAAAAOADAAAAHQCAAAAAAAAAAAAQEAAA","778":"b:AAAAAOADAAAAHQCAAAAAAAAAAAAQEAAA","863":"b:AAAAAOADAAAAHQCAAAAAAAAAAAAQEAAA","904":"b:AAAAAOADAAAAHQCAAAAAAAAAAAAQEAAA","939":"b:AAAAAOADAAAAHQCAAAAAAAAAAAAQEAAA","951":"b:AAAAAOADAAAAHQCAAAAAAAAAAAAQEAAA","975":"b:AAAAAOADAAAAHQCAAAAAAAAAAAAQEAAA","1007":"b:AAAAAOADAAAAHQCAAAAAAAAAAAAQEAAA","1037":"b:AAAAAOADAAAAHQCAAAAAAAAAAAAQEAAA","1050":"b:AAAAAOADAAAAHQCAAAAAAAAAAAAQEAAA","1057":"b:AAAAAOADAAAAHQCAAAAAAAAAAAAQEAAA","1065":"b:AAAAAOADAAAAHQCAAAAAAAAAAAAQEAAA","1070":"b:AAAAAOADAAAAHQCAAAAAAAAAAAAQEAAA","1077":"b:AAAAAOADAAAAHQCAAAAAAAAAAAAQEAAA","1102":"b:AAAAAOADAAAAHQCAAAAAAAAAAAAQEAAA","1160":"b:AAAAAOADAAAAHQCAAAAAAAAAAAAQEAAA","1204":"b:AAAAAOADAAAAHQCAAAAAAAAAAAAQEAAA","1253":"b:AAAAAOADAAAAHQCAAAAAAAAAAAAQEAAA"},"packages/@n8n/workflow-sdk/src/validation/input-resolver.ts":{"16":[37,39,40,41],"31":[37,39,40,41]},"packages/@n8n/workflow-sdk/src/validation/output-resolver.ts":{"16":[37,39,40,41],"31":[37,39,40,41]},"packages/@n8n/workflow-sdk/src/validation/resolve-schema.ts":{"17":[37,39,40,41],"65":[37,39,40,41],"74":[37,39,40,41],"86":[37,39,40,41],"95":[37,39,40,41],"142":[37,39,40,41],"150":[37,39,40,41],"166":[37,39,40,41],"202":[37,39,40,41],"220":[37,39,40,41],"223":[37,39,40,41],"225":[37,39,40,41]},"packages/@n8n/workflow-sdk/src/validation/schema-helpers.ts":{"8":[37,39,40,41],"33":[37,39,40,41]},"packages/@n8n/workflow-sdk/src/validation/schema-validator.ts":{"38":[37,72,74,95,39,75,40,41,76,172,164],"46":[37,72,74,95,39,75,40,41,76,172,164],"59":[37,72,74,95,39,75,40,41,76,172,164],"75":[37,72,74,95,39,75,40,41,76,172,164],"94":[37,72,74,95,39,75,40,41,76,172,164],"120":[37,72,74,95,39,75,40,41,76,172,164],"138":[37,72,74,95,39,75,40,41,76,172,164],"151":[37,72,74,95,39,75,40,41,76,172,164],"183":[37,72,74,95,39,75,40,41,76,172,164],"192":[37,72,74,95,39,75,40,41,76,172,164],"212":[37,72,74,95,39,75,40,41,76,172,164],"256":[37,72,74,95,39,75,40,41,76,172,164],"284":[37,72,74,95,39,75,40,41,76,172,164],"300":[37,72,74,95,39,75,40,41,76,172,164],"313":[37,72,74,95,39,75,40,41,76,172,164],"335":[37,72,74,95,39,75,40,41,76,172,164],"357":[37,72,74,95,39,75,40,41,76,172,164],"389":[37,72,74,95,39,75,40,41,76,172,164],"412":[37,72,74,95,39,75,40,41,76,172,164],"420":[37,72,74,95,39,75,40,41,76,172,164],"422":[37,72,74,95,39,75,40,41,76,172,164],"469":[37,72,74,95,39,75,40,41,76,172,164],"479":[37,72,74,95,39,75,40,41,76,172,164],"482":[37,72,74,95,39,75,40,41,76,172,164],"503":[37,72,74,95,39,75,40,41,76,172,164],"515":[37,72,74,95,39,75,40,41,76,172,164],"518":[37,72,74,95,39,75,40,41,76,172,164],"520":[37,72,74,95,39,75,40,41,76,172,164],"539":[37,72,74,95,39,75,40,41,76,172,164],"548":[37,72,74,95,39,75,40,41,76,172,164],"550":[37,72,74,95,39,75,40,41,76,172,164],"558":[37,72,74,95,39,75,40,41,76,172,164],"573":[37,72,74,95,39,75,40,41,76,172,164],"588":[37,72,74,95,39,75,40,41,76,172,164],"590":[37,72,74,95,39,75,40,41,76,172,164],"611":[37,72,74,95,39,75,40,41,76,172,164],"626":[37,72,74,95,39,75,40,41,76,172,164],"627":[37,72,74,95,39,75,40,41,76,172,164],"636":[37,72,74,95,39,75,40,41,76,172,164],"653":[37,72,74,95,39,75,40,41,76,172,164],"679":[37,72,74,95,39,75,40,41,76,172,164],"680":[37,72,74,95,39,75,40,41,76,172,164]},"packages/cli/src/credentials/instance-credential-broker.ts":{"28":[37,72,73,74,95,39,75,40,76],"35":[37,72,73,74,95,39,75,40,76],"39":[37,72,73,74,95,39,75,40,76],"44":[37,72,73,74,95,39,75,40,76],"62":[37,72,73,74,95,39,75,40,76],"67":[37,72,73,74,95,39,75,40,76],"75":[37,72,73,74,95,39,75,40,76],"103":[37,72,73,74,95,39,75,40,76]},"packages/cli/src/credentials/instance-credential-use.registry.ts":{"14":[37,72,73,74,39,75,40,76],"18":[37,72,73,74,39,75,40,76],"39":[37,72,73,74,39,75,40,76]},"packages/cli/src/metrics/prometheus/instance-ai-metrics.service.ts":{"17":[37,72,73,74,95,39,75,40,76],"23":[37,72,73,74,95,39,75,40,76],"28":[37,72,73,74,95,39,75,40,76],"72":[37,72,73,74,95,39,75,40,76],"141":[37,72,73,74,95,39,75,40,76],"145":[37,72,73,74,95,39,75,40,76],"148":[37,72,73,74,95,39,75,40,76],"151":[37,72,73,74,95,39,75,40,76],"154":[37,72,73,74,95,39,75,40,76],"158":[37,72,73,74,95,39,75,40,76],"161":[37,72,73,74,95,39,75,40,76],"164":[37,72,73,74,95,39,75,40,76],"170":[37,72,73,74,95,39,75,40,76]},"packages/cli/src/modules/instance-ai/browser/browser-use-server.ts":{"22":[37,72,73,74,39,75,40,76],"24":[37,72,73,74,39,75,40,76],"25":[37,72,73,74,39,75,40,76],"28":[37,72,73,74,39,75,40,76],"31":[37,72,73,74,39,75,40,76],"43":[37,72,73,74,39,75,40,76],"47":[37,72,73,74,39,75,40,76],"52":[37,72,73,74,39,75,40,76],"53":[37,72,73,74,39,75,40,76],"64":[37,72,73,74,39,75,40,76]},"packages/cli/src/modules/instance-ai/browser/composite-local-mcp-server.ts":{"4":[37,72,74,95,39,75,40,76],"7":[37,72,74,95,39,75,40,76],"25":[37,72,74,95,39,75,40,76],"26":[37,72,74,95,39,75,40,76],"29":[37,72,74,95,39,75,40,76],"33":[37,72,74,95,39,75,40,76],"37":[37,72,74,95,39,75,40,76],"43":[37,72,74,95,39,75,40,76],"58":[37,72,74,95,39,75,40,76],"59":[37,72,74,95,39,75,40,76],"62":[37,72,74,95,39,75,40,76]},"packages/cli/src/modules/instance-ai/browser/instance-ai-browser-session.service.ts":{"62":[37,72,73,74,95,39,75,40,76],"74":[37,72,73,74,95,39,75,40,76],"92":[37,72,73,74,95,39,75,40,76],"101":[37,72,73,74,95,39,75,40,76],"110":[37,72,73,74,95,39,75,40,76],"115":[37,72,73,74,95,39,75,40,76],"119":[37,72,73,74,95,39,75,40,76],"129":[37,72,73,74,95,39,75,40,76],"144":[37,72,73,74,95,39,75,40,76],"153":[37,72,73,74,95,39,75,40,76],"160":[37,72,73,74,95,39,75,40,76],"161":[37,72,73,74,95,39,75,40,76],"176":[37,72,73,74,95,39,75,40,76],"198":[37,72,73,74,95,39,75,40,76],"211":[37,72,73,74,95,39,75,40,76],"229":[37,72,73,74,95,39,75,40,76],"241":[37,72,73,74,95,39,75,40,76],"256":[37,72,73,74,95,39,75,40,76],"261":[37,72,73,74,95,39,75,40,76],"273":[37,72,73,74,95,39,75,40,76],"284":[37,72,73,74,95,39,75,40,76],"300":[37,72,73,74,95,39,75,40,76],"305":[37,72,73,74,95,39,75,40,76],"309":[37,72,73,74,95,39,75,40,76],"316":[37,72,73,74,95,39,75,40,76],"319":[37,72,73,74,95,39,75,40,76],"324":[37,72,73,74,95,39,75,40,76],"327":[37,72,73,74,95,39,75,40,76]},"packages/cli/src/modules/instance-ai/eval/thread-credential-allowlist.service.ts":{"10":[37,72,74,95,39,75,40,41,76],"13":[37,72,74,95,39,75,40,41,76],"17":[37,72,74,95,39,75,40,41,76],"21":[37,72,74,95,39,75,40,41,76]},"packages/cli/src/modules/instance-ai/event-bus/durable-event-log.ts":{"69":[37,72,73,74,95,39,75,40,41,76],"73":[37,72,73,74,95,39,75,40,41,76],"75":[37,72,73,74,95,39,75,40,41,76],"138":[37,72,73,74,95,39,75,40,41,76],"147":[37,72,73,74,95,39,75,40,41,76],"158":[37,72,73,74,95,39,75,40,41,76],"161":[37,72,73,74,95,39,75,40,41,76],"163":[37,72,73,74,95,39,75,40,41,76],"171":[37,72,73,74,95,39,75,40,41,76],"175":[37,72,73,74,95,39,75,40,41,76],"187":[37,72,73,74,95,39,75,40,41,76],"208":[37,72,73,74,95,39,75,40,41,76],"223":[37,72,73,74,95,39,75,40,41,76],"238":[37,72,73,74,95,39,75,40,41,76],"250":[37,72,73,74,95,39,75,40,41,76],"263":[37,72,73,74,95,39,75,40,41,76],"268":[37,72,73,74,95,39,75,40,41,76],"278":[37,72,73,74,95,39,75,40,41,76],"296":[37,72,73,74,95,39,75,40,41,76],"298":[37,72,73,74,95,39,75,40,41,76],"324":[37,72,73,74,95,39,75,40,41,76],"327":[37,72,73,74,95,39,75,40,41,76],"406":[37,72,73,74,95,39,75,40,41,76],"416":[37,72,73,74,95,39,75,40,41,76],"442":[37,72,73,74,95,39,75,40,41,76],"518":[37,72,73,74,95,39,75,40,41,76],"532":[37,72,73,74,95,39,75,40,41,76],"558":[37,72,73,74,95,39,75,40,41,76],"574":[37,72,73,74,95,39,75,40,41,76],"579":[37,72,73,74,95,39,75,40,41,76],"601":[37,72,73,74,95,39,75,40,41,76],"614":[37,72,73,74,95,39,75,40,41,76],"649":[37,72,73,74,95,39,75,40,41,76],"659":[37,72,73,74,95,39,75,40,41,76],"673":[37,72,73,74,95,39,75,40,41,76]},"packages/cli/src/modules/instance-ai/event-bus/durable-log-metrics.ts":{"62":[37,72,73,74,39,75,40,76],"64":[37,72,73,74,39,75,40,76],"71":[37,72,73,74,39,75,40,76],"78":[37,72,73,74,39,75,40,76],"83":[37,72,73,74,39,75,40,76],"88":[37,72,73,74,39,75,40,76],"99":[37,72,73,74,39,75,40,76],"111":[37,72,73,74,39,75,40,76],"115":[37,72,73,74,39,75,40,76],"119":[37,72,73,74,39,75,40,76],"123":[37,72,73,74,39,75,40,76]},"packages/cli/src/modules/instance-ai/event-bus/in-process-event-bus.ts":{"70":[37,72,73,74,95,39,75,40,41,76],"106":[37,72,73,74,95,39,75,40,41,76],"115":[37,72,73,74,95,39,75,40,41,76],"141":[37,72,73,74,95,39,75,40,41,76],"157":[37,72,73,74,95,39,75,40,41,76],"174":[37,72,73,74,95,39,75,40,41,76],"199":[37,72,73,74,95,39,75,40,41,76],"223":[37,72,73,74,95,39,75,40,41,76],"257":[37,72,73,74,95,39,75,40,41,76],"261":[37,72,73,74,95,39,75,40,41,76],"265":[37,72,73,74,95,39,75,40,41,76],"271":[37,72,73,74,95,39,75,40,41,76],"292":[37,72,73,74,95,39,75,40,41,76],"304":[37,72,73,74,95,39,75,40,41,76],"320":[37,72,73,74,95,39,75,40,41,76],"334":[37,72,73,74,95,39,75,40,41,76],"353":[37,72,73,74,95,39,75,40,41,76],"355":[37,72,73,74,95,39,75,40,41,76],"359":[37,72,73,74,95,39,75,40,41,76],"371":[37,72,73,74,95,39,75,40,41,76],"374":[37,72,73,74,95,39,75,40,41,76],"377":[37,72,73,74,95,39,75,40,41,76],"381":[37,72,73,74,95,39,75,40,41,76],"385":[37,72,73,74,95,39,75,40,41,76],"386":[37,72,73,74,95,39,75,40,41,76],"394":[37,72,73,74,95,39,75,40,41,76],"398":[37,72,73,74,95,39,75,40,41,76],"420":[37,72,73,74,95,39,75,40,41,76],"433":[37,72,73,74,95,39,75,40,41,76],"444":[37,72,73,74,95,39,75,40,41,76],"454":[37,72,73,74,95,39,75,40,41,76],"466":[37,72,73,74,95,39,75,40,41,76]},"packages/cli/src/modules/instance-ai/filesystem/local-gateway-registry.ts":{"27":[37,72,74,38,95,39,75,40,76],"34":[37,72,74,38,95,39,75,40,76],"42":[37,72,74,38,95,39,75,40,76],"56":[37,72,74,38,95,39,75,40,76],"72":[37,72,74,38,95,39,75,40,76],"86":[37,72,74,38,95,39,75,40,76],"98":[37,72,74,38,95,39,75,40,76],"110":[37,72,74,38,95,39,75,40,76],"124":[37,72,74,38,95,39,75,40,76],"129":[37,72,74,38,95,39,75,40,76],"137":[37,72,74,38,95,39,75,40,76],"142":[37,72,74,38,95,39,75,40,76],"147":[37,72,74,38,95,39,75,40,76],"155":[37,72,74,38,95,39,75,40,76],"165":[37,72,74,38,95,39,75,40,76],"170":[37,72,74,38,95,39,75,40,76],"193":[37,72,74,38,95,39,75,40,76],"198":[37,72,74,38,95,39,75,40,76],"208":[37,72,74,38,95,39,75,40,76],"216":[37,72,74,38,95,39,75,40,76],"218":[37,72,74,38,95,39,75,40,76],"219":[37,72,74,38,95,39,75,40,76],"223":[37,72,74,38,95,39,75,40,76]},"packages/cli/src/modules/instance-ai/instance-ai-credit.service.ts":{"15":[37,72,74,95,39,75,40,76],"48":[37,72,74,95,39,75,40,76],"71":[37,72,74,95,39,75,40,76],"209":[37,72,74,95,39,75,40,76],"225":[37,72,74,95,39,75,40,76],"244":[37,72,74,95,39,75,40,76],"275":[37,72,74,95,39,75,40,76],"301":[37,72,74,95,39,75,40,76]},"packages/cli/src/modules/instance-ai/instance-ai-error-reporter.service.ts":{"18":[37,72,74,95,39,75,40,76],"29":[37,72,74,95,39,75,40,76],"36":[37,72,74,95,39,75,40,76],"40":[37,72,74,95,39,75,40,76],"44":[37,72,74,95,39,75,40,76],"48":[37,72,74,95,39,75,40,76],"87":[37,72,74,95,39,75,40,76],"101":[37,72,74,95,39,75,40,76]},"packages/cli/src/modules/instance-ai/instance-ai-gateway.service.ts":{"26":[37,72,74,38,95,39,75,40,76],"36":[37,72,74,38,95,39,75,40,76],"42":[37,72,74,38,95,39,75,40,76],"46":[37,72,74,38,95,39,75,40,76],"50":[37,72,74,38,95,39,75,40,76],"54":[37,72,74,38,95,39,75,40,76],"58":[37,72,74,38,95,39,75,40,76],"62":[37,72,74,38,95,39,75,40,76],"66":[37,72,74,38,95,39,75,40,76],"70":[37,72,74,38,95,39,75,40,76],"75":[37,72,74,38,95,39,75,40,76],"79":[37,72,74,38,95,39,75,40,76],"83":[37,72,74,38,95,39,75,40,76],"87":[37,72,74,38,95,39,75,40,76],"96":[37,72,74,38,95,39,75,40,76],"101":[37,72,74,38,95,39,75,40,76],"108":[37,72,74,38,95,39,75,40,76],"112":[37,72,74,38,95,39,75,40,76],"122":[37,72,74,38,95,39,75,40,76],"126":[37,72,74,38,95,39,75,40,76]},"packages/cli/src/modules/instance-ai/instance-ai-memory.service.ts":{"48":[37,72,73,74,39,75,40,76],"57":[37,72,73,74,39,75,40,76],"67":[37,72,73,74,39,75,40,76],"80":[37,72,73,74,39,75,40,76],"87":[37,72,73,74,39,75,40,76],"105":[37,72,73,74,39,75,40,76],"110":[37,72,73,74,39,75,40,76],"114":[37,72,73,74,39,75,40,76],"127":[37,72,73,74,39,75,40,76],"148":[37,72,73,74,39,75,40,76],"219":[37,72,73,74,39,75,40,76],"239":[37,72,73,74,39,75,40,76],"252":[37,72,73,74,39,75,40,76],"264":[37,72,73,74,39,75,40,76],"271":[37,72,73,74,39,75,40,76],"312":[37,72,73,74,39,75,40,76],"333":[37,72,73,74,39,75,40,76],"337":[37,72,73,74,39,75,40,76],"349":[37,72,73,74,39,75,40,76],"353":[37,72,73,74,39,75,40,76],"372":[37,72,73,74,39,75,40,76],"373":[37,72,73,74,39,75,40,76],"385":[37,72,73,74,39,75,40,76],"436":[37,72,73,74,39,75,40,76],"487":[37,72,73,74,39,75,40,76],"495":[37,72,73,74,39,75,40,76],"515":[37,72,73,74,39,75,40,76],"527":[37,72,73,74,39,75,40,76],"538":[37,72,73,74,39,75,40,76],"548":[37,72,73,74,39,75,40,76],"557":[37,72,73,74,39,75,40,76],"570":[37,72,73,74,39,75,40,76],"574":[37,72,73,74,39,75,40,76],"578":[37,72,73,74,39,75,40,76],"584":[37,72,73,74,39,75,40,76],"601":[37,72,73,74,39,75,40,76],"615":[37,72,73,74,39,75,40,76],"665":[37,72,73,74,39,75,40,76],"683":[37,72,73,74,39,75,40,76]},"packages/cli/src/modules/instance-ai/instance-ai-model.service.ts":{"34":[37,72,73,74,95,39,75,40,76],"41":[37,72,73,74,95,39,75,40,76],"56":[37,72,73,74,95,39,75,40,76],"60":[37,72,73,74,95,39,75,40,76],"86":[37,72,73,74,95,39,75,40,76],"99":[37,72,73,74,95,39,75,40,76],"121":[37,72,73,74,95,39,75,40,76],"147":[37,72,73,74,95,39,75,40,76],"154":[37,72,73,74,95,39,75,40,76]},"packages/cli/src/modules/instance-ai/instance-ai-run-pin-data.ts":{"28":[37,39,40,41],"90":[37,39,40,41],"94":[37,39,40,41],"99":[37,39,40,41],"140":[37,39,40,41],"164":[37,39,40,41],"179":[37,39,40,41],"194":[37,39,40,41],"231":[37,39,40,41]},"packages/cli/src/modules/instance-ai/instance-ai-settings.service.ts":{"94":[37,72,73,74,38,95,39,75,40,76],"108":[37,72,73,74,38,95,39,75,40,76],"119":[37,72,73,74,38,95,39,75,40,76],"138":[37,72,73,74,38,95,39,75,40,76],"159":[37,72,73,74,38,95,39,75,40,76],"175":[37,72,73,74,38,95,39,75,40,76],"186":[37,72,73,74,38,95,39,75,40,76],"221":[37,72,73,74,38,95,39,75,40,76],"303":[37,72,73,74,38,95,39,75,40,76],"322":[37,72,73,74,38,95,39,75,40,76],"327":[37,72,73,74,38,95,39,75,40,76],"332":[37,72,73,74,38,95,39,75,40,76],"361":[37,72,73,74,38,95,39,75,40,76],"392":[37,72,73,74,38,95,39,75,40,76],"427":[37,72,73,74,38,95,39,75,40,76],"509":[37,72,73,74,38,95,39,75,40,76],"542":[37,72,73,74,38,95,39,75,40,76],"678":[37,72,73,74,38,95,39,75,40,76],"700":[37,72,73,74,38,95,39,75,40,76],"758":[37,72,73,74,38,95,39,75,40,76],"804":[37,72,73,74,38,95,39,75,40,76],"822":[37,72,73,74,38,95,39,75,40,76],"836":[37,72,73,74,38,95,39,75,40,76],"910":[37,72,73,74,38,95,39,75,40,76],"919":[37,72,73,74,38,95,39,75,40,76],"945":[37,72,73,74,38,95,39,75,40,76],"966":[37,72,73,74,38,95,39,75,40,76],"971":[37,72,73,74,38,95,39,75,40,76],"978":[37,72,73,74,38,95,39,75,40,76],"985":[37,72,73,74,38,95,39,75,40,76],"992":[37,72,73,74,38,95,39,75,40,76],"1017":[37,72,73,74,38,95,39,75,40,76],"1051":[37,72,73,74,38,95,39,75,40,76],"1077":[37,72,73,74,38,95,39,75,40,76],"1086":[37,72,73,74,38,95,39,75,40,76],"1095":[37,72,73,74,38,95,39,75,40,76],"1101":[37,72,73,74,38,95,39,75,40,76],"1111":[37,72,73,74,38,95,39,75,40,76],"1116":[37,72,73,74,38,95,39,75,40,76],"1121":[37,72,73,74,38,95,39,75,40,76],"1130":[37,72,73,74,38,95,39,75,40,76],"1135":[37,72,73,74,38,95,39,75,40,76],"1139":[37,72,73,74,38,95,39,75,40,76],"1144":[37,72,73,74,38,95,39,75,40,76],"1162":[37,72,73,74,38,95,39,75,40,76],"1167":[37,72,73,74,38,95,39,75,40,76],"1174":[37,72,73,74,38,95,39,75,40,76],"1205":[37,72,73,74,38,95,39,75,40,76],"1208":[37,72,73,74,38,95,39,75,40,76],"1231":[37,72,73,74,38,95,39,75,40,76],"1239":[37,72,73,74,38,95,39,75,40,76],"1290":[37,72,73,74,38,95,39,75,40,76],"1296":[37,72,73,74,38,95,39,75,40,76],"1302":[37,72,73,74,38,95,39,75,40,76],"1310":[37,72,73,74,38,95,39,75,40,76],"1336":[37,72,73,74,38,95,39,75,40,76],"1351":[37,72,73,74,38,95,39,75,40,76],"1355":[37,72,73,74,38,95,39,75,40,76],"1372":[37,72,73,74,38,95,39,75,40,76],"1377":[37,72,73,74,38,95,39,75,40,76],"1407":[37,72,73,74,38,95,39,75,40,76],"1411":[37,72,73,74,38,95,39,75,40,76],"1428":[37,72,73,74,38,95,39,75,40,76],"1441":[37,72,73,74,38,95,39,75,40,76],"1446":[37,72,73,74,38,95,39,75,40,76],"1449":[37,72,73,74,38,95,39,75,40,76],"1455":[37,72,73,74,38,95,39,75,40,76],"1456":[37,72,73,74,38,95,39,75,40,76],"1465":[37,72,73,74,38,95,39,75,40,76],"1482":[37,72,73,74,38,95,39,75,40,76]},"packages/cli/src/modules/instance-ai/instance-ai-temporary-workflow.service.ts":{"8":[37,72,74,95,75,40,41,76],"35":[37,72,74,95,75,40,41,76],"51":[37,72,74,95,75,40,41,76],"75":[37,72,74,95,75,40,41,76],"88":[37,72,74,95,75,40,41,76],"145":[37,72,74,95,75,40,41,76],"149":[37,72,74,95,75,40,41,76]},"packages/cli/src/modules/instance-ai/instance-ai-terminal-outcome.service.ts":{"30":[37,72,73,74,38,95,39,75,40,76],"34":[37,72,73,74,38,95,39,75,40,76],"38":[37,72,73,74,38,95,39,75,40,76],"59":[37,72,73,74,38,95,39,75,40,76],"68":[37,72,73,74,38,95,39,75,40,76],"203":[37,72,73,74,38,95,39,75,40,76],"217":[37,72,73,74,38,95,39,75,40,76],"250":[37,72,73,74,38,95,39,75,40,76],"270":[37,72,73,74,38,95,39,75,40,76],"283":[37,72,73,74,38,95,39,75,40,76],"321":[37,72,73,74,38,95,39,75,40,76],"326":[37,72,73,74,38,95,39,75,40,76],"356":[37,72,73,74,38,95,39,75,40,76],"380":[37,72,73,74,38,95,39,75,40,76],"386":[37,72,73,74,38,95,39,75,40,76],"394":[37,72,73,74,38,95,39,75,40,76],"400":[37,72,73,74,38,95,39,75,40,76],"442":[37,72,73,74,38,95,39,75,40,76],"465":[37,72,73,74,38,95,39,75,40,76],"498":[37,72,73,74,38,95,39,75,40,76],"504":[37,72,73,74,38,95,39,75,40,76],"517":[37,72,73,74,38,95,39,75,40,76]},"packages/cli/src/modules/instance-ai/instance-ai-test.controller.ts":{"18":[37,72,73,74,177,39,75,40],"28":[37,72,73,74,177,39,75,40],"40":[37,72,73,74,177,39,75,40],"46":[37,72,73,74,177,39,75,40],"52":[37,72,73,74,177,39,75,40],"66":[37,72,73,74,177,39,75,40],"73":[37,72,73,74,177,39,75,40],"92":[37,72,73,74,177,39,75,40],"119":[37,72,73,74,177,39,75,40]},"packages/cli/src/modules/instance-ai/instance-ai.adapter.service.ts":{"170":[37,72,74,38,95,39,75,40,41,76],"221":[37,72,74,38,95,39,75,40,41,76],"226":[37,72,74,38,95,39,75,40,41,76],"230":[37,72,74,38,95,39,75,40,41,76],"236":[37,72,74,38,95,39,75,40,41,76],"280":[37,72,74,38,95,39,75,40,41,76],"285":[37,72,74,38,95,39,75,40,41,76],"370":[37,72,74,38,95,39,75,40,41,76],"377":[37,72,74,38,95,39,75,40,41,76],"397":[37,72,74,38,95,39,75,40,41,76],"416":[37,72,74,38,95,39,75,40,41,76],"433":[37,72,74,38,95,39,75,40,41,76],"438":[37,72,74,38,95,39,75,40,41,76],"455":[37,72,74,38,95,39,75,40,41,76],"466":[37,72,74,38,95,39,75,40,41,76],"469":[37,72,74,38,95,39,75,40,41,76],"475":[37,72,74,38,95,39,75,40,41,76],"491":[37,72,74,38,95,39,75,40,41,76],"500":[37,72,74,38,95,39,75,40,41,76],"508":[37,72,74,38,95,39,75,40,41,76],"512":[37,72,74,38,95,39,75,40,41,76],"515":[37,72,74,38,95,39,75,40,41,76],"519":[37,72,74,38,95,39,75,40,41,76],"526":[37,72,74,38,95,39,75,40,41,76],"532":[37,72,74,38,95,39,75,40,41,76],"545":[37,72,74,38,95,39,75,40,41,76],"565":[37,72,74,38,95,39,75,40,41,76],"570":[37,72,74,38,95,39,75,40,41,76],"583":[37,72,74,38,95,39,75,40,41,76],"585":[37,72,74,38,95,39,75,40,41,76],"597":[37,72,74,38,95,39,75,40,41,76],"609":[37,72,74,38,95,39,75,40,41,76],"617":[37,72,74,38,95,39,75,40,41,76],"625":[37,72,74,38,95,39,75,40,41,76],"636":[37,72,74,38,95,39,75,40,41,76],"655":[37,72,74,38,95,39,75,40,41,76],"680":[37,72,74,38,95,39,75,40,41,76],"686":[37,72,74,38,95,39,75,40,41,76],"696":[37,72,74,38,95,39,75,40,41,76],"704":[37,72,74,38,95,39,75,40,41,76],"716":[37,72,74,38,95,39,75,40,41,76],"741":[37,72,74,38,95,39,75,40,41,76],"776":[37,72,74,38,95,39,75,40,41,76],"852":[37,72,74,38,95,39,75,40,41,76],"933":[37,72,74,38,95,39,75,40,41,76],"946":[37,72,74,38,95,39,75,40,41,76],"959":[37,72,74,38,95,39,75,40,41,76],"979":[37,72,74,38,95,39,75,40,41,76],"991":[37,72,74,38,95,39,75,40,41,76],"1009":[37,72,74,38,95,39,75,40,41,76],"1021":[37,72,74,38,95,39,75,40,41,76],"1038":[37,72,74,38,95,39,75,40,41,76],"1047":[37,72,74,38,95,39,75,40,41,76],"1069":[37,72,74,38,95,39,75,40,41,76],"1108":[37,72,74,38,95,39,75,40,41,76],"1120":[37,72,74,38,95,39,75,40,41,76],"1136":[37,72,74,38,95,39,75,40,41,76],"1231":[37,72,74,38,95,39,75,40,41,76],"1250":[37,72,74,38,95,39,75,40,41,76],"1271":[37,72,74,38,95,39,75,40,41,76],"1272":[37,72,74,38,95,39,75,40,41,76],"1281":[37,72,74,38,95,39,75,40,41,76],"1282":[37,72,74,38,95,39,75,40,41,76],"1356":[37,72,74,38,95,39,75,40,41,76],"1365":[37,72,74,38,95,39,75,40,41,76],"1374":[37,72,74,38,95,39,75,40,41,76],"1398":[37,72,74,38,95,39,75,40,41,76],"1403":[37,72,74,38,95,39,75,40,41,76],"1418":[37,72,74,38,95,39,75,40,41,76],"1443":[37,72,74,38,95,39,75,40,41,76],"1449":[37,72,74,38,95,39,75,40,41,76],"1452":[37,72,74,38,95,39,75,40,41,76],"1461":[37,72,74,38,95,39,75,40,41,76],"1462":[37,72,74,38,95,39,75,40,41,76],"1477":[37,72,74,38,95,39,75,40,41,76],"1480":[37,72,74,38,95,39,75,40,41,76],"1496":[37,72,74,38,95,39,75,40,41,76],"1504":[37,72,74,38,95,39,75,40,41,76],"1513":[37,72,74,38,95,39,75,40,41,76],"1517":[37,72,74,38,95,39,75,40,41,76],"1544":[37,72,74,38,95,39,75,40,41,76],"1560":[37,72,74,38,95,39,75,40,41,76],"1574":[37,72,74,38,95,39,75,40,41,76],"1586":[37,72,74,38,95,39,75,40,41,76],"1631":[37,72,74,38,95,39,75,40,41,76],"1671":[37,72,74,38,95,39,75,40,41,76],"1690":[37,72,74,38,95,39,75,40,41,76],"1704":[37,72,74,38,95,39,75,40,41,76],"1715":[37,72,74,38,95,39,75,40,41,76],"1764":[37,72,74,38,95,39,75,40,41,76],"1771":[37,72,74,38,95,39,75,40,41,76],"1785":[37,72,74,38,95,39,75,40,41,76],"1786":[37,72,74,38,95,39,75,40,41,76],"1790":[37,72,74,38,95,39,75,40,41,76],"1795":[37,72,74,38,95,39,75,40,41,76],"1797":[37,72,74,38,95,39,75,40,41,76],"1812":[37,72,74,38,95,39,75,40,41,76],"1820":[37,72,74,38,95,39,75,40,41,76],"1825":[37,72,74,38,95,39,75,40,41,76],"1830":[37,72,74,38,95,39,75,40,41,76],"1835":[37,72,74,38,95,39,75,40,41,76],"1847":[37,72,74,38,95,39,75,40,41,76],"1860":[37,72,74,38,95,39,75,40,41,76],"1868":[37,72,74,38,95,39,75,40,41,76],"1870":[37,72,74,38,95,39,75,40,41,76],"1887":[37,72,74,38,95,39,75,40,41,76],"1895":[37,72,74,38,95,39,75,40,41,76],"1901":[37,72,74,38,95,39,75,40,41,76],"1919":[37,72,74,38,95,39,75,40,41,76],"1929":[37,72,74,38,95,39,75,40,41,76],"1947":[37,72,74,38,95,39,75,40,41,76],"1954":[37,72,74,38,95,39,75,40,41,76],"1958":[37,72,74,38,95,39,75,40,41,76],"1965":[37,72,74,38,95,39,75,40,41,76],"1974":[37,72,74,38,95,39,75,40,41,76],"1980":[37,72,74,38,95,39,75,40,41,76],"1990":[37,72,74,38,95,39,75,40,41,76],"1999":[37,72,74,38,95,39,75,40,41,76],"2007":[37,72,74,38,95,39,75,40,41,76],"2016":[37,72,74,38,95,39,75,40,41,76],"2032":[37,72,74,38,95,39,75,40,41,76],"2042":[37,72,74,38,95,39,75,40,41,76],"2054":[37,72,74,38,95,39,75,40,41,76],"2067":[37,72,74,38,95,39,75,40,41,76],"2088":[37,72,74,38,95,39,75,40,41,76],"2109":[37,72,74,38,95,39,75,40,41,76],"2144":[37,72,74,38,95,39,75,40,41,76],"2161":[37,72,74,38,95,39,75,40,41,76],"2180":[37,72,74,38,95,39,75,40,41,76],"2213":[37,72,74,38,95,39,75,40,41,76],"2242":[37,72,74,38,95,39,75,40,41,76],"2257":[37,72,74,38,95,39,75,40,41,76],"2266":[37,72,74,38,95,39,75,40,41,76],"2281":[37,72,74,38,95,39,75,40,41,76],"2293":[37,72,74,38,95,39,75,40,41,76],"2310":[37,72,74,38,95,39,75,40,41,76],"2320":[37,72,74,38,95,39,75,40,41,76],"2324":[37,72,74,38,95,39,75,40,41,76],"2327":[37,72,74,38,95,39,75,40,41,76],"2328":[37,72,74,38,95,39,75,40,41,76],"2329":[37,72,74,38,95,39,75,40,41,76],"2333":[37,72,74,38,95,39,75,40,41,76],"2339":[37,72,74,38,95,39,75,40,41,76],"2346":[37,72,74,38,95,39,75,40,41,76],"2350":[37,72,74,38,95,39,75,40,41,76],"2360":[37,72,74,38,95,39,75,40,41,76],"2367":[37,72,74,38,95,39,75,40,41,76],"2382":[37,72,74,38,95,39,75,40,41,76],"2385":[37,72,74,38,95,39,75,40,41,76],"2388":[37,72,74,38,95,39,75,40,41,76],"2392":[37,72,74,38,95,39,75,40,41,76],"2395":[37,72,74,38,95,39,75,40,41,76],"2449":[37,72,74,38,95,39,75,40,41,76],"2453":[37,72,74,38,95,39,75,40,41,76],"2461":[37,72,74,38,95,39,75,40,41,76],"2478":[37,72,74,38,95,39,75,40,41,76],"2487":[37,72,74,38,95,39,75,40,41,76],"2490":[37,72,74,38,95,39,75,40,41,76],"2495":[37,72,74,38,95,39,75,40,41,76],"2511":[37,72,74,38,95,39,75,40,41,76],"2516":[37,72,74,38,95,39,75,40,41,76],"2531":[37,72,74,38,95,39,75,40,41,76],"2537":[37,72,74,38,95,39,75,40,41,76],"2583":[37,72,74,38,95,39,75,40,41,76],"2605":[37,72,74,38,95,39,75,40,41,76],"2673":[37,72,74,38,95,39,75,40,41,76],"2674":[37,72,74,38,95,39,75,40,41,76],"2708":[37,72,74,38,95,39,75,40,41,76],"2713":[37,72,74,38,95,39,75,40,41,76],"2724":[37,72,74,38,95,39,75,40,41,76],"2728":[37,72,74,38,95,39,75,40,41,76],"2734":[37,72,74,38,95,39,75,40,41,76],"2736":[37,72,74,38,95,39,75,40,41,76],"2745":[37,72,74,38,95,39,75,40,41,76],"2750":[37,72,74,38,95,39,75,40,41,76],"2757":[37,72,74,38,95,39,75,40,41,76],"2775":[37,72,74,38,95,39,75,40,41,76],"2787":[37,72,74,38,95,39,75,40,41,76],"2803":[37,72,74,38,95,39,75,40,41,76],"2817":[37,72,74,38,95,39,75,40,41,76],"2838":[37,72,74,38,95,39,75,40,41,76],"2843":[37,72,74,38,95,39,75,40,41,76],"2846":[37,72,74,38,95,39,75,40,41,76],"2855":[37,72,74,38,95,39,75,40,41,76],"2885":[37,72,74,38,95,39,75,40,41,76],"2963":[37,72,74,38,95,39,75,40,41,76],"2969":[37,72,74,38,95,39,75,40,41,76],"3002":[37,72,74,38,95,39,75,40,41,76],"3009":[37,72,74,38,95,39,75,40,41,76],"3031":[37,72,74,38,95,39,75,40,41,76],"3044":[37,72,74,38,95,39,75,40,41,76],"3057":[37,72,74,38,95,39,75,40,41,76],"3089":[37,72,74,38,95,39,75,40,41,76],"3133":[37,72,74,38,95,39,75,40,41,76],"3163":[37,72,74,38,95,39,75,40,41,76],"3177":[37,72,74,38,95,39,75,40,41,76],"3185":[37,72,74,38,95,39,75,40,41,76],"3193":[37,72,74,38,95,39,75,40,41,76],"3200":[37,72,74,38,95,39,75,40,41,76],"3225":[37,72,74,38,95,39,75,40,41,76],"3263":[37,72,74,38,95,39,75,40,41,76],"3264":[37,72,74,38,95,39,75,40,41,76],"3308":[37,72,74,38,95,39,75,40,41,76],"3345":[37,72,74,38,95,39,75,40,41,76],"3376":[37,72,74,38,95,39,75,40,41,76],"3417":[37,72,74,38,95,39,75,40,41,76],"3431":[37,72,74,38,95,39,75,40,41,76],"3457":[37,72,74,38,95,39,75,40,41,76],"3458":[37,72,74,38,95,39,75,40,41,76],"3461":[37,72,74,38,95,39,75,40,41,76],"3465":[37,72,74,38,95,39,75,40,41,76],"3480":[37,72,74,38,95,39,75,40,41,76],"3508":[37,72,74,38,95,39,75,40,41,76],"3541":[37,72,74,38,95,39,75,40,41,76],"3545":[37,72,74,38,95,39,75,40,41,76],"3547":[37,72,74,38,95,39,75,40,41,76],"3593":[37,72,74,38,95,39,75,40,41,76],"3599":[37,72,74,38,95,39,75,40,41,76],"3608":[37,72,74,38,95,39,75,40,41,76],"3609":[37,72,74,38,95,39,75,40,41,76],"3614":[37,72,74,38,95,39,75,40,41,76],"3633":[37,72,74,38,95,39,75,40,41,76],"3646":[37,72,74,38,95,39,75,40,41,76],"3669":[37,72,74,38,95,39,75,40,41,76],"3683":[37,72,74,38,95,39,75,40,41,76],"3697":[37,72,74,38,95,39,75,40,41,76]},"packages/cli/src/modules/instance-ai/instance-ai.controller.ts":{"85":[37,72,73,74,38,95,39,75,40,41,76],"95":[37,72,73,74,38,95,39,75,40,41,76],"103":[37,72,73,74,38,95,39,75,40,41,76],"115":[37,72,73,74,38,95,39,75,40,41,76],"142":[37,72,73,74,38,95,39,75,40,41,76],"148":[37,72,73,74,38,95,39,75,40,41,76],"158":[37,72,73,74,38,95,39,75,40,41,76],"168":[37,72,73,74,38,95,39,75,40,41,76],"185":[37,72,73,74,38,95,39,75,40,41,76],"192":[37,72,73,74,38,95,39,75,40,41,76],"222":[37,72,73,74,38,95,39,75,40,41,76],"254":[37,72,73,74,38,95,39,75,40,41,76],"270":[37,72,73,74,38,95,39,75,40,41,76],"281":[37,72,73,74,38,95,39,75,40,41,76],"287":[37,72,73,74,38,95,39,75,40,41,76],"297":[37,72,73,74,38,95,39,75,40,41,76],"385":[37,72,73,74,38,95,39,75,40,41,76],"422":[37,72,73,74,38,95,39,75,40,41,76],"441":[37,72,73,74,38,95,39,75,40,41,76],"478":[37,72,73,74,38,95,39,75,40,41,76],"482":[37,72,73,74,38,95,39,75,40,41,76],"570":[37,72,73,74,38,95,39,75,40,41,76],"578":[37,72,73,74,38,95,39,75,40,41,76],"601":[37,72,73,74,38,95,39,75,40,41,76],"610":[37,72,73,74,38,95,39,75,40,41,76],"624":[37,72,73,74,38,95,39,75,40,41,76],"630":[37,72,73,74,38,95,39,75,40,41,76],"644":[37,72,73,74,38,95,39,75,40,41,76],"661":[37,72,73,74,38,95,39,75,40,41,76],"670":[37,72,73,74,38,95,39,75,40,41,76],"676":[37,72,73,74,38,95,39,75,40,41,76],"698":[37,72,73,74,38,95,39,75,40,41,76],"707":[37,72,73,74,38,95,39,75,40,41,76],"714":[37,72,73,74,38,95,39,75,40,41,76],"719":[37,72,73,74,38,95,39,75,40,41,76],"723":[37,72,73,74,38,95,39,75,40,41,76],"741":[37,72,73,74,38,95,39,75,40,41,76],"756":[37,72,73,74,38,95,39,75,40,41,76],"762":[37,72,73,74,38,95,39,75,40,41,76],"776":[37,72,73,74,38,95,39,75,40,41,76],"782":[37,72,73,74,38,95,39,75,40,41,76],"788":[37,72,73,74,38,95,39,75,40,41,76],"795":[37,72,73,74,38,95,39,75,40,41,76],"836":[37,72,73,74,38,95,39,75,40,41,76],"850":[37,72,73,74,38,95,39,75,40,41,76],"867":[37,72,73,74,38,95,39,75,40,41,76],"926":[37,72,73,74,38,95,39,75,40,41,76],"939":[37,72,73,74,38,95,39,75,40,41,76],"952":[37,72,73,74,38,95,39,75,40,41,76],"971":[37,72,73,74,38,95,39,75,40,41,76],"983":[37,72,73,74,38,95,39,75,40,41,76],"1001":[37,72,73,74,38,95,39,75,40,41,76],"1021":[37,72,73,74,38,95,39,75,40,41,76],"1069":[37,72,73,74,38,95,39,75,40,41,76],"1084":[37,72,73,74,38,95,39,75,40,41,76],"1103":[37,72,73,74,38,95,39,75,40,41,76],"1116":[37,72,73,74,38,95,39,75,40,41,76],"1139":[37,72,73,74,38,95,39,75,40,41,76],"1143":[37,72,73,74,38,95,39,75,40,41,76],"1149":[37,72,73,74,38,95,39,75,40,41,76],"1155":[37,72,73,74,38,95,39,75,40,41,76],"1161":[37,72,73,74,38,95,39,75,40,41,76],"1181":[37,72,73,74,38,95,39,75,40,41,76],"1212":[37,72,73,74,38,95,39,75,40,41,76],"1229":[37,72,73,74,38,95,39,75,40,41,76],"1250":[37,72,73,74,38,95,39,75,40,41,76],"1263":[37,72,73,74,38,95,39,75,40,41,76],"1276":[37,72,73,74,38,95,39,75,40,41,76],"1293":[37,72,73,74,38,95,39,75,40,41,76],"1301":[37,72,73,74,38,95,39,75,40,41,76],"1309":[37,72,73,74,38,95,39,75,40,41,76],"1317":[37,72,73,74,38,95,39,75,40,41,76],"1327":[37,72,73,74,38,95,39,75,40,41,76],"1342":[37,72,73,74,38,95,39,75,40,41,76],"1353":[37,72,73,74,38,95,39,75,40,41,76],"1365":[37,72,73,74,38,95,39,75,40,41,76],"1390":[37,72,73,74,38,95,39,75,40,41,76],"1403":[37,72,73,74,38,95,39,75,40,41,76]},"packages/cli/src/modules/instance-ai/instance-ai.service.ts":{"196":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","201":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","222":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","224":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","235":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","250":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","273":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","297":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","308":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","321":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","325":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","357":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","373":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","380":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","384":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","425":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","469":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","473":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","569":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","606":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","641":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","644":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","670":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","671":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","675":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","687":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","690":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","704":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","708":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","719":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","729":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","742":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","749":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","762":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","772":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","788":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","805":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","810":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","823":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","847":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","851":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","860":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","867":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","876":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","880":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","915":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","916":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","926":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","930":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","961":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","989":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","993":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","994":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","1004":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","1008":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","1017":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","1059":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","1070":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","1078":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","1083":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","1087":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","1102":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","1145":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","1154":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","1169":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","1196":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","1200":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","1213":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","1228":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","1242":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","1249":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","1253":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","1257":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","1276":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","1284":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","1303":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","1311":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","1383":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","1384":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","1385":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","1387":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","1399":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","1434":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","1442":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","1446":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","1450":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","1461":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","1465":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","1473":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","1523":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","1535":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","1641":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","1648":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","1654":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","1656":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","1672":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","1673":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","1684":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","1689":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","1702":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","1706":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","1710":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","1711":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","1714":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","1731":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","1761":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","1784":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","1787":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","1801":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","1823":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","1831":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","1855":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","1860":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","1863":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","1868":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","1873":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","1877":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","1878":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","1880":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","1885":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","1918":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","1928":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","1951":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","1952":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","1983":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","2000":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","2013":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","2020":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","2038":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","2060":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","2138":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","2155":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","2229":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","2236":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","2239":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","2246":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","2250":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","2266":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","2272":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","2286":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","2303":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","2311":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","2345":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","2347":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","2368":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","2373":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","2374":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","2376":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","2377":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","2379":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","2381":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","2410":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","2424":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","2436":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","2455":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","2479":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","2508":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","2520":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","2532":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","2543":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","2594":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","2626":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","2680":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","2690":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","2699":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","2727":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","2731":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","2771":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","2828":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","2832":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","2842":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","2855":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","2868":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","2886":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","2903":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","2966":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","2969":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","2974":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","2993":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","2995":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","2999":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","3001":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","3005":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","3007":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","3021":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","3023":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","3034":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","3049":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","3060":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","3079":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","3093":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","3095":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","3103":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","3120":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","3122":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","3124":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","3131":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","3148":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","3193":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","3228":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","3248":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","3251":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","3254":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","3278":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","3415":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","3440":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","3497":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","3501":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","3528":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","3531":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","3576":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","3634":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","3648":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","3656":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","3668":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","4074":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","4090":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","4120":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","4129":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","4171":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","4183":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","4200":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","4208":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","4226":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","4254":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","4262":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","4294":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","4369":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","4388":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","4394":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","4432":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","4457":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","4504":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","4574":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","4582":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","4593":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","4598":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","4606":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","4648":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","4808":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","4863":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","4866":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","4875":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","4888":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","5285":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","5305":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","5330":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","5344":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","5371":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","5529":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","5534":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","5540":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","5588":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","5640":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","5683":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","5715":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","5737":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","5796":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","5823":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","5845":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","5848":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","5860":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","5872":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","5936":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","5939":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","5945":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA"},"packages/cli/src/modules/instance-ai/internal-messages.ts":{"55":[37,72,74,95,39,75,40,76],"64":[37,72,74,95,39,75,40,76],"83":[37,72,74,95,39,75,40,76],"98":[37,72,74,95,39,75,40,76]},"packages/cli/src/modules/instance-ai/liveness/instance-ai-liveness.service.ts":{"17":[37,72,73,74,95,39,75,40,41,76],"106":[37,72,73,74,95,39,75,40,41,76],"108":[37,72,73,74,95,39,75,40,41,76],"112":[37,72,73,74,95,39,75,40,41,76],"115":[37,72,73,74,95,39,75,40,41,76],"120":[37,72,73,74,95,39,75,40,41,76],"129":[37,72,73,74,95,39,75,40,41,76],"133":[37,72,73,74,95,39,75,40,41,76],"137":[37,72,73,74,95,39,75,40,41,76],"143":[37,72,73,74,95,39,75,40,41,76],"152":[37,72,73,74,95,39,75,40,41,76],"156":[37,72,73,74,95,39,75,40,41,76],"197":[37,72,73,74,95,39,75,40,41,76],"216":[37,72,73,74,95,39,75,40,41,76],"226":[37,72,73,74,95,39,75,40,41,76],"235":[37,72,73,74,95,39,75,40,41,76],"239":[37,72,73,74,95,39,75,40,41,76]},"packages/cli/src/modules/instance-ai/mcp/instance-ai-mcp-registry.service.ts":{"54":[37,72,74,95,39,75,40,76],"59":[37,72,74,95,39,75,40,76],"63":[37,72,74,95,39,75,40,76],"68":[37,72,74,95,39,75,40,76],"72":[37,72,74,95,39,75,40,76],"77":[37,72,74,95,39,75,40,76],"88":[37,72,74,95,39,75,40,76],"100":[37,72,74,95,39,75,40,76],"105":[37,72,74,95,39,75,40,76],"108":[37,72,74,95,39,75,40,76],"127":[37,72,74,95,39,75,40,76],"132":[37,72,74,95,39,75,40,76],"144":[37,72,74,95,39,75,40,76],"159":[37,72,74,95,39,75,40,76],"163":[37,72,74,95,39,75,40,76],"223":[37,72,74,95,39,75,40,76],"236":[37,72,74,95,39,75,40,76],"254":[37,72,74,95,39,75,40,76],"302":[37,72,74,95,39,75,40,76],"304":[37,72,74,95,39,75,40,76],"314":[37,72,74,95,39,75,40,76],"320":[37,72,74,95,39,75,40,76],"321":[37,72,74,95,39,75,40,76],"323":[37,72,74,95,39,75,40,76],"396":[37,72,74,95,39,75,40,76],"422":[37,72,74,95,39,75,40,76],"440":[37,72,74,95,39,75,40,76],"454":[37,72,74,95,39,75,40,76],"507":[37,72,74,95,39,75,40,76],"528":[37,72,74,95,39,75,40,76],"559":[37,72,74,95,39,75,40,76]},"packages/cli/src/modules/instance-ai/node-definition-resolver.ts":{"15":[37,72,74,95,39,75,40,76],"16":[37,72,74,95,39,75,40,76],"19":[37,72,74,95,39,75,40,76],"46":[37,72,74,95,39,75,40,76],"72":[37,72,74,95,39,75,40,76],"91":[37,72,74,95,39,75,40,76],"119":[37,72,74,95,39,75,40,76],"129":[37,72,74,95,39,75,40,76],"134":[37,72,74,95,39,75,40,76],"135":[37,72,74,95,39,75,40,76],"172":[37,72,74,95,39,75,40,76],"173":[37,72,74,95,39,75,40,76],"181":[37,72,74,95,39,75,40,76],"192":[37,72,74,95,39,75,40,76],"196":[37,72,74,95,39,75,40,76],"219":[37,72,74,95,39,75,40,76],"247":[37,72,74,95,39,75,40,76],"248":[37,72,74,95,39,75,40,76],"250":[37,72,74,95,39,75,40,76],"251":[37,72,74,95,39,75,40,76],"293":[37,72,74,95,39,75,40,76],"316":[37,72,74,95,39,75,40,76],"320":[37,72,74,95,39,75,40,76],"324":[37,72,74,95,39,75,40,76],"325":[37,72,74,95,39,75,40,76],"335":[37,72,74,95,39,75,40,76],"341":[37,72,74,95,39,75,40,76],"373":[37,72,74,95,39,75,40,76],"396":[37,72,74,95,39,75,40,76]},"packages/cli/src/modules/instance-ai/observability.ts":{"16":[95,40,76]},"packages/cli/src/modules/instance-ai/output-redaction-config.ts":{"18":[37,72,74,95,39,75,40,76],"24":[37,72,74,95,39,75,40,76],"25":[37,72,74,95,39,75,40,76]},"packages/cli/src/modules/instance-ai/planned-task-action-runner.ts":{"94":[37,72,74,95,75,40,76],"96":[37,72,74,95,75,40,76],"115":[37,72,74,95,75,40,76],"131":[37,72,74,95,75,40,76],"157":[37,72,74,95,75,40,76],"169":[37,72,74,95,75,40,76],"194":[37,72,74,95,75,40,76],"212":[37,72,74,95,75,40,76],"234":[37,72,74,95,75,40,76],"252":[37,72,74,95,75,40,76]},"packages/cli/src/modules/instance-ai/repositories/instance-ai-event-log.repository.ts":{"17":[37,72,73,74,95,39,75,40,76],"22":[37,72,73,74,95,39,75,40,76],"39":[37,72,73,74,95,39,75,40,76],"45":[37,72,73,74,95,39,75,40,76],"64":[37,72,73,74,95,39,75,40,76],"69":[37,72,73,74,95,39,75,40,76],"74":[37,72,73,74,95,39,75,40,76],"77":[37,72,73,74,95,39,75,40,76],"84":[37,72,73,74,95,39,75,40,76],"89":[37,72,73,74,95,39,75,40,76],"93":[37,72,73,74,95,39,75,40,76],"101":[37,72,73,74,95,39,75,40,76],"118":[37,72,73,74,95,39,75,40,76],"125":[37,72,73,74,95,39,75,40,76],"144":[37,72,73,74,95,39,75,40,76]},"packages/cli/src/modules/instance-ai/repositories/instance-ai-pending-confirmation.repository.ts":{"8":[37,74,38,75,40,41],"21":[37,74,38,75,40,41],"25":[37,74,38,75,40,41],"48":[37,74,38,75,40,41],"53":[37,74,38,75,40,41],"58":[37,74,38,75,40,41],"63":[37,74,38,75,40,41],"76":[37,74,38,75,40,41],"81":[37,74,38,75,40,41],"87":[37,74,38,75,40,41],"96":[37,74,38,75,40,41]},"packages/cli/src/modules/instance-ai/repositories/instance-ai-thread-grant.repository.ts":{"8":[37,72,74,95,39,75,40,76],"16":[37,72,74,95,39,75,40,76],"25":[37,72,74,95,39,75,40,76],"30":[37,72,74,95,39,75,40,76]},"packages/cli/src/modules/instance-ai/run-trace-metadata.ts":{"33":[37,72,74,95,75,40,76],"40":[37,72,74,95,75,40,76],"48":[37,72,74,95,75,40,76],"77":[37,72,74,95,75,40,76],"89":[37,72,74,95,75,40,76]},"packages/cli/src/modules/instance-ai/sandbox/instance-ai-sandbox.service.ts":{"41":[37,72,74,38,95,39,75,40,41,76],"45":[37,72,74,38,95,39,75,40,41,76],"53":[37,72,74,38,95,39,75,40,41,76],"61":[37,72,74,38,95,39,75,40,41,76],"65":[37,72,74,38,95,39,75,40,41,76],"78":[37,72,74,38,95,39,75,40,41,76],"91":[37,72,74,38,95,39,75,40,41,76],"173":[37,72,74,38,95,39,75,40,41,76],"175":[37,72,74,38,95,39,75,40,41,76],"179":[37,72,74,38,95,39,75,40,41,76],"183":[37,72,74,38,95,39,75,40,41,76],"242":[37,72,74,38,95,39,75,40,41,76],"251":[37,72,74,38,95,39,75,40,41,76],"260":[37,72,74,38,95,39,75,40,41,76],"263":[37,72,74,38,95,39,75,40,41,76],"293":[37,72,74,38,95,39,75,40,41,76],"340":[37,72,74,38,95,39,75,40,41,76],"350":[37,72,74,38,95,39,75,40,41,76],"357":[37,72,74,38,95,39,75,40,41,76],"360":[37,72,74,38,95,39,75,40,41,76],"367":[37,72,74,38,95,39,75,40,41,76],"407":[37,72,74,38,95,39,75,40,41,76],"421":[37,72,74,38,95,39,75,40,41,76],"429":[37,72,74,38,95,39,75,40,41,76],"440":[37,72,74,38,95,39,75,40,41,76],"456":[37,72,74,38,95,39,75,40,41,76],"460":[37,72,74,38,95,39,75,40,41,76],"464":[37,72,74,38,95,39,75,40,41,76],"468":[37,72,74,38,95,39,75,40,41,76],"474":[37,72,74,38,95,39,75,40,41,76],"482":[37,72,74,38,95,39,75,40,41,76],"489":[37,72,74,38,95,39,75,40,41,76],"501":[37,72,74,38,95,39,75,40,41,76]},"packages/cli/src/modules/instance-ai/sandbox-provider.ts":{"6":[37,72,74,95,39,75,40,76],"14":[37,72,74,95,39,75,40,76]},"packages/cli/src/modules/instance-ai/storage/db-snapshot-storage.ts":{"14":[37,72,73,74,39,75,40,76],"37":[37,72,73,74,39,75,40,76],"39":[37,72,73,74,39,75,40,76],"76":[37,72,73,74,39,75,40,76],"99":[37,72,73,74,39,75,40,76],"165":[37,72,73,74,39,75,40,76],"175":[37,72,73,74,39,75,40,76],"180":[37,72,73,74,39,75,40,76],"199":[37,72,73,74,39,75,40,76]},"packages/cli/src/modules/instance-ai/storage/typeorm-agent-checkpoint-store.ts":{"13":[37,72,74,38,95,39,75,40,76],"15":[37,72,74,38,95,39,75,40,76],"47":[37,72,74,38,95,39,75,40,76],"56":[37,72,74,38,95,39,75,40,76],"64":[37,72,74,38,95,39,75,40,76],"80":[37,72,74,38,95,39,75,40,76],"89":[37,72,74,38,95,39,75,40,76],"103":[37,72,74,38,95,39,75,40,76],"115":[37,72,74,38,95,39,75,40,76],"122":[37,72,74,38,95,39,75,40,76]},"packages/cli/src/modules/instance-ai/storage/typeorm-agent-memory.ts":{"45":[37,72,73,74,95,39,75,40,76],"53":[37,72,73,74,95,39,75,40,76],"63":[37,72,73,74,95,39,75,40,76],"74":[37,72,73,74,95,39,75,40,76],"78":[37,72,73,74,95,39,75,40,76],"84":[37,72,73,74,95,39,75,40,76],"89":[37,72,73,74,95,39,75,40,76],"109":[37,72,73,74,95,39,75,40,76],"117":[37,72,73,74,95,39,75,40,76],"124":[37,72,73,74,95,39,75,40,76],"129":[37,72,73,74,95,39,75,40,76],"135":[37,72,73,74,95,39,75,40,76],"141":[37,72,73,74,95,39,75,40,76],"157":[37,72,73,74,95,39,75,40,76],"164":[37,72,73,74,95,39,75,40,76],"185":[37,72,73,74,95,39,75,40,76],"200":[37,72,73,74,95,39,75,40,76],"206":[37,72,73,74,95,39,75,40,76],"214":[37,72,73,74,95,39,75,40,76],"219":[37,72,73,74,95,39,75,40,76],"244":[37,72,73,74,95,39,75,40,76],"245":[37,72,73,74,95,39,75,40,76],"273":[37,72,73,74,95,39,75,40,76],"291":[37,72,73,74,95,39,75,40,76],"295":[37,72,73,74,95,39,75,40,76],"322":[37,72,73,74,95,39,75,40,76],"326":[37,72,73,74,95,39,75,40,76],"340":[37,72,73,74,95,39,75,40,76],"344":[37,72,73,74,95,39,75,40,76],"349":[37,72,73,74,95,39,75,40,76],"355":[37,72,73,74,95,39,75,40,76],"356":[37,72,73,74,95,39,75,40,76],"360":[37,72,73,74,95,39,75,40,76],"373":[37,72,73,74,95,39,75,40,76],"382":[37,72,73,74,95,39,75,40,76],"389":[37,72,73,74,95,39,75,40,76],"409":[37,72,73,74,95,39,75,40,76],"422":[37,72,73,74,95,39,75,40,76],"428":[37,72,73,74,95,39,75,40,76],"443":[37,72,73,74,95,39,75,40,76],"450":[37,72,73,74,95,39,75,40,76],"457":[37,72,73,74,95,39,75,40,76],"474":[37,72,73,74,95,39,75,40,76],"479":[37,72,73,74,95,39,75,40,76],"488":[37,72,73,74,95,39,75,40,76],"509":[37,72,73,74,95,39,75,40,76],"515":[37,72,73,74,95,39,75,40,76],"521":[37,72,73,74,95,39,75,40,76],"525":[37,72,73,74,95,39,75,40,76],"532":[37,72,73,74,95,39,75,40,76],"536":[37,72,73,74,95,39,75,40,76],"540":[37,72,73,74,95,39,75,40,76],"547":[37,72,73,74,95,39,75,40,76],"551":[37,72,73,74,95,39,75,40,76],"555":[37,72,73,74,95,39,75,40,76],"567":[37,72,73,74,95,39,75,40,76],"571":[37,72,73,74,95,39,75,40,76],"576":[37,72,73,74,95,39,75,40,76],"588":[37,72,73,74,95,39,75,40,76]},"packages/cli/src/modules/instance-ai/storage/typeorm-observation-log-store.ts":{"30":[37,72,74,95,39,75,40,76],"47":[37,72,74,95,39,75,40,76],"55":[37,72,74,95,39,75,40,76],"60":[37,72,74,95,39,75,40,76],"74":[37,72,74,95,39,75,40,76],"77":[37,72,74,95,39,75,40,76],"83":[37,72,74,95,39,75,40,76],"98":[37,72,74,95,39,75,40,76],"101":[37,72,74,95,39,75,40,76],"125":[37,72,74,95,39,75,40,76],"132":[37,72,74,95,39,75,40,76],"138":[37,72,74,95,39,75,40,76],"144":[37,72,74,95,39,75,40,76],"148":[37,72,74,95,39,75,40,76],"160":[37,72,74,95,39,75,40,76],"166":[37,72,74,95,39,75,40,76],"205":[37,72,74,95,39,75,40,76],"206":[37,72,74,95,39,75,40,76],"211":[37,72,74,95,39,75,40,76],"224":[37,72,74,95,39,75,40,76],"239":[37,72,74,95,39,75,40,76],"290":[37,72,74,95,39,75,40,76]},"packages/cli/src/modules/instance-ai/suspended-thread-persistence.service.ts":{"7":[37,74,38,39,75,40,41],"50":[37,74,38,39,75,40,41],"56":[37,74,38,39,75,40,41],"71":[37,74,38,39,75,40,41],"82":[37,74,38,39,75,40,41],"118":[37,74,38,39,75,40,41],"129":[37,74,38,39,75,40,41]},"packages/cli/src/modules/instance-ai/trace-replay-state.ts":{"12":[37,72,74,177,95,39,75,40,76],"19":[37,72,74,177,95,39,75,40,76],"23":[37,72,74,177,95,39,75,40,76],"28":[37,72,74,177,95,39,75,40,76],"32":[37,72,74,177,95,39,75,40,76],"63":[37,72,74,177,95,39,75,40,76],"81":[37,72,74,177,95,39,75,40,76],"85":[37,72,74,177,95,39,75,40,76],"90":[37,72,74,177,95,39,75,40,76],"94":[37,72,74,177,95,39,75,40,76],"98":[37,72,74,177,95,39,75,40,76],"118":[37,72,74,177,95,39,75,40,76],"128":[37,72,74,177,95,39,75,40,76],"154":[37,72,74,177,95,39,75,40,76],"166":[37,72,74,177,95,39,75,40,76]},"packages/cli/src/modules/instance-ai/tracing/instance-ai-tracing.service.ts":{"34":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","111":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","119":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","145":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","149":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","153":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","154":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","157":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","161":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","170":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","172":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","175":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","220":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","224":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","264":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","270":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","292":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","307":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","324":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","333":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","340":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","391":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","427":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","447":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","468":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","473":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","516":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","520":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","524":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA","528":"b:AAAAAOADAAAAHwCAAAAAAAAAAAAAAAIA"},"packages/cli/src/modules/instance-ai/workflow-verification-obligation-service.ts":{"19":[37,40,41],"27":[37,40,41],"29":[37,40,41],"33":[37,40,41],"39":[37,40,41],"49":[37,40,41],"57":[37,40,41],"71":[37,40,41],"82":[37,40,41]},"packages/cli/src/modules/instance-ai/workflow-verification-task-projector.ts":{"43":[37,72,74,95,39,75,40,76],"48":[37,72,74,95,39,75,40,76],"52":[37,72,74,95,39,75,40,76],"60":[37,72,74,95,39,75,40,76],"65":[37,72,74,95,39,75,40,76],"85":[37,72,74,95,39,75,40,76],"100":[37,72,74,95,39,75,40,76],"115":[37,72,74,95,39,75,40,76],"127":[37,72,74,95,39,75,40,76],"145":[37,72,74,95,39,75,40,76],"161":[37,72,74,95,39,75,40,76],"170":[37,72,74,95,39,75,40,76],"178":[37,72,74,95,39,75,40,76],"188":[37,72,74,95,39,75,40,76],"197":[37,72,74,95,39,75,40,76],"214":[37,72,74,95,39,75,40,76],"222":[37,72,74,95,39,75,40,76],"240":[37,72,74,95,39,75,40,76],"281":[37,72,74,95,39,75,40,76],"289":[37,72,74,95,39,75,40,76],"314":[37,72,74,95,39,75,40,76],"336":[37,72,74,95,39,75,40,76],"382":[37,72,74,95,39,75,40,76],"392":[37,72,74,95,39,75,40,76],"398":[37,72,74,95,39,75,40,76]},"packages/cli/src/modules/instance-registry/checks/check.service.ts":{"46":"b:QAEAAJABCwAAAAJCBIAAFkCYAAAAAAAA","57":"b:QAEAAJABCwAAAAJCBIAAFkCYAAAAAAAA","63":"b:QAEAAJABCwAAAAJCBIAAFkCYAAAAAAAA","75":"b:QAEAAJABCwAAAAJCBIAAFkCYAAAAAAAA","83":"b:QAEAAJABCwAAAAJCBIAAFkCYAAAAAAAA","88":"b:QAEAAJABCwAAAAJCBIAAFkCYAAAAAAAA","90":"b:QAEAAJABCwAAAAJCBIAAFkCYAAAAAAAA","96":"b:QAEAAJABCwAAAAJCBIAAFkCYAAAAAAAA","111":"b:QAEAAJABCwAAAAJCBIAAFkCYAAAAAAAA","115":"b:QAEAAJABCwAAAAJCBIAAFkCYAAAAAAAA","133":"b:QAEAAJABCwAAAAJCBIAAFkCYAAAAAAAA","148":"b:QAEAAJABCwAAAAJCBIAAFkCYAAAAAAAA","157":"b:QAEAAJABCwAAAAJCBIAAFkCYAAAAAAAA","193":"b:QAEAAJABCwAAAAJCBIAAFkCYAAAAAAAA","214":"b:QAEAAJABCwAAAAJCBIAAFkCYAAAAAAAA","228":"b:QAEAAJABCwAAAAJCBIAAFkCYAAAAAAAA","239":"b:QAEAAJABCwAAAAJCBIAAFkCYAAAAAAAA","245":"b:QAEAAJABCwAAAAJCBIAAFkCYAAAAAAAA","254":"b:QAEAAJABCwAAAAJCBIAAFkCYAAAAAAAA","277":"b:QAEAAJABCwAAAAJCBIAAFkCYAAAAAAAA","301":"b:QAEAAJABCwAAAAJCBIAAFkCYAAAAAAAA"},"packages/cli/src/modules/instance-registry/stale-member-cleanup.service.ts":{"17":"b:QAEAAJABCwAAAAJCBIAAFkCYAAAAAAAA","25":"b:QAEAAJABCwAAAAJCBIAAFkCYAAAAAAAA","30":"b:QAEAAJABCwAAAAJCBIAAFkCYAAAAAAAA","34":"b:QAEAAJABCwAAAAJCBIAAFkCYAAAAAAAA","42":"b:QAEAAJABCwAAAAJCBIAAFkCYAAAAAAAA","47":"b:QAEAAJABCwAAAAJCBIAAFkCYAAAAAAAA","59":"b:QAEAAJABCwAAAAJCBIAAFkCYAAAAAAAA"},"packages/cli/src/node-catalog/node-catalog.service.ts":{"24":"b:AAQQAKADAAAAHQCAAAAAAAAAAAAQEAAA","25":"b:AAQQAKADAAAAHQCAAAAAAAAAAAAQEAAA","27":"b:AAQQAKADAAAAHQCAAAAAAAAAAAAQEAAA","33":"b:AAQQAKADAAAAHQCAAAAAAAAAAAAQEAAA","36":"b:AAQQAKADAAAAHQCAAAAAAAAAAAAQEAAA","44":"b:AAQQAKADAAAAHQCAAAAAAAAAAAAQEAAA","82":"b:AAQQAKADAAAAHQCAAAAAAAAAAAAQEAAA","85":"b:AAQQAKADAAAAHQCAAAAAAAAAAAAQEAAA","142":"b:AAQQAKADAAAAHQCAAAAAAAAAAAAQEAAA","146":"b:AAQQAKADAAAAHQCAAAAAAAAAAAAQEAAA","149":"b:AAQQAKADAAAAHQCAAAAAAAAAAAAQEAAA","154":"b:AAQQAKADAAAAHQCAAAAAAAAAAAAQEAAA","161":"b:AAQQAKADAAAAHQCAAAAAAAAAAAAQEAAA","175":"b:AAQQAKADAAAAHQCAAAAAAAAAAAAQEAAA","195":"b:AAQQAKADAAAAHQCAAAAAAAAAAAAQEAAA","207":"b:AAQQAKADAAAAHQCAAAAAAAAAAAAQEAAA","209":"b:AAQQAKADAAAAHQCAAAAAAAAAAAAQEAAA","256":"b:AAQQAKADAAAAHQCAAAAAAAAAAAAQEAAA","274":"b:AAQQAKADAAAAHQCAAAAAAAAAAAAQEAAA","285":"b:AAQQAKADAAAAHQCAAAAAAAAAAAAQEAAA","304":"b:AAQQAKADAAAAHQCAAAAAAAAAAAAQEAAA","323":"b:AAQQAKADAAAAHQCAAAAAAAAAAAAQEAAA","335":"b:AAQQAKADAAAAHQCAAAAAAAAAAAAQEAAA","347":"b:AAQQAKADAAAAHQCAAAAAAAAAAAAQEAAA","378":"b:AAQQAKADAAAAHQCAAAAAAAAAAAAQEAAA","424":"b:AAQQAKADAAAAHQCAAAAAAAAAAAAQEAAA","430":"b:AAQQAKADAAAAHQCAAAAAAAAAAAAQEAAA","433":"b:AAQQAKADAAAAHQCAAAAAAAAAAAAQEAAA","438":"b:AAQQAKADAAAAHQCAAAAAAAAAAAAQEAAA","443":"b:AAQQAKADAAAAHQCAAAAAAAAAAAAQEAAA","447":"b:AAQQAKADAAAAHQCAAAAAAAAAAAAQEAAA"},"packages/cli/src/telemetry/index.ts":{"116":[37,72,73,74,39,75,40,76,172,164],"128":[37,72,73,74,39,75,40,76,172,164],"173":[37,72,73,74,39,75,40,76,172,164],"203":[37,72,73,74,39,75,40,76,172,164],"212":[37,72,73,74,39,75,40,76,172,164],"214":[37,72,73,74,39,75,40,76,172,164],"221":[37,72,73,74,39,75,40,76,172,164],"265":[37,72,73,74,39,75,40,76,172,164],"266":[37,72,73,74,39,75,40,76,172,164],"290":[37,72,73,74,39,75,40,76,172,164],"294":[37,72,73,74,39,75,40,76,172,164],"295":[37,72,73,74,39,75,40,76,172,164],"315":[37,72,73,74,39,75,40,76,172,164],"324":[37,72,73,74,39,75,40,76,172,164],"329":[37,72,73,74,39,75,40,76,172,164],"330":[37,72,73,74,39,75,40,76,172,164],"332":[37,72,73,74,39,75,40,76,172,164],"335":[37,72,73,74,39,75,40,76,172,164],"336":[37,72,73,74,39,75,40,76,172,164],"357":[37,72,73,74,39,75,40,76,172,164],"404":[37,72,73,74,39,75,40,76,172,164],"417":[37,72,73,74,39,75,40,76,172,164],"443":[37,72,73,74,39,75,40,76,172,164],"472":[37,72,73,74,39,75,40,76,172,164],"496":[37,72,73,74,39,75,40,76,172,164],"503":[37,72,73,74,39,75,40,76,172,164],"533":[37,72,73,74,39,75,40,76,172,164],"558":[37,72,73,74,39,75,40,76,172,164],"564":[37,72,73,74,39,75,40,76,172,164],"613":[37,72,73,74,39,75,40,76,172,164],"617":[37,72,73,74,39,75,40,76,172,164],"621":[37,72,73,74,39,75,40,76,172,164],"625":[37,72,73,74,39,75,40,76,172,164]},"packages/cli/src/utils/ai-proxy-fetch.ts":{"15":[20,37,72,74,95,39,75,40,76],"34":[20,37,72,74,95,39,75,40,76],"52":[20,37,72,74,95,39,75,40,76]},"packages/workflow/src/schemas.ts":{"89":[93,37,39,40,41],"102":[93,37,39,40,41],"166":[93,37,39,40,41],"240":[93,37,39,40,41],"265":[93,37,39,40,41],"431":[93,37,39,40,41],"508":[93,37,39,40,41],"509":[93,37,39,40,41],"522":[93,37,39,40,41]},"packages/frontend/@n8n/design-system/src/components/N8nAvatar/Avatar.vue":{"16":"b:AAAABAYADsQEAAJUAAKwAQAAAAABAAAA","29":"b:AAAABAYADsQEAAJUAAKwAQAAAAABAAAA","30":"b:AAAABAYADsQEAAJUAAKwAQAAAAABAAAA","32":"b:AAAABAYADsQEAAJUAAKwAQAAAAABAAAA","34":"b:AAAABAYADsQEAAJUAAKwAQAAAAABAAAA","37":"b:AAAABAYADsQEAAJUAAKwAQAAAAABAAAA","38":"b:AAAABAYADsQEAAJUAAKwAQAAAAABAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nFormInput/FormInput.vue":{"56":"b:AAAABAIACIQEAINFAAIUAAAAAAEBAAAA","82":"b:AAAABAIACIQEAINFAAIUAAAAAAEBAAAA","122":"b:AAAABAAACIAEAANBAAIUAAAAAAEBAAAA","128":"b:AAAABAAACIAEAANBAAIUAAAAAAABAAAA","133":"b:AAAABAAACIAEAINBAAIUAAAAAAEBAAAA","137":[81],"143":"b:AAAABAIACIQEAINFAAIUAAAAAAEBAAAA","161":"b:AAAABAIACIQEAINFAAIUAAAAAAEBAAAA","167":"b:AAAABAIACIQEAINFAAIUAAAAAAEBAAAA","173":[88,90,105,51,80,114,116,81],"176":"b:AAAABAIACIQEAINFAAIUAAAAAAEBAAAA","214":"b:AAAABAIACIQEAINFAAIUAAAAAAEBAAAA","215":[105,81],"232":[80],"234":[80]},"packages/frontend/@n8n/design-system/src/components/N8nFormInputs/FormInputs.vue":{"20":[87,88,90,51,116,81],"38":[87,88,90,51,116,81],"39":[87,88,90,51,116,81],"44":[88,51,116,81],"45":[88,51,116,81],"56":[87,88,90,51,116,81],"57":[87,88,90,51,116,81],"60":[87,88,90,51,116,81],"64":[87,88,90,51,116,81],"65":[81],"68":[81],"69":[81],"70":[81],"75":[88,51,116,81],"81":[87,88,90,51,116,81],"108":[81],"117":[88,51,116,81],"124":[88,51,116,81],"137":[87,88,90,51,116,81],"148":[87,88,90,51,116,81],"152":[87,88,90,51,116,81],"155":[87,88,90,51,116,81],"179":[88,51,116,81],"180":[87,88,90,51,116,81]},"packages/frontend/@n8n/design-system/src/components/ResizeObserver/ResizeObserver.vue":{"6":[87,88,90,51,116,81],"21":[87,88,90,51,116,81],"23":[87,88,90,51,116,81],"25":[87,88,90,51,116,81],"30":[87,88,90,51,116,81],"36":[87,88,90,51,116,81],"37":[87,88,90,51,116,81],"38":[87,88,90,51,116,81],"47":[88,51,81],"52":[87,88,90,51,116,81]},"packages/frontend/@n8n/design-system/src/utils/labelUtil.ts":{"1":"b:AAAABAYADsQEAAJUAAKwAQAAAAABAAAA","4":"b:AAAABAYADsQEAAJUAAKwAQAAAAABAAAA","6":"b:AAAABAYADsQEAAJUAAKwAQAAAAABAAAA","11":"b:AAAABAYADsQEAAJUAAKwAQAAAAABAAAA"},"packages/frontend/editor-ui/src/app/utils/rbac/checks/isInstanceOwner.ts":{"4":[90,71,116,81]},"packages/frontend/editor-ui/src/features/core/auth/views/SettingsPersonalView.vue":{"92":[90,116,81],"96":[90,116,81],"100":[90,116,81],"104":[90,116,81],"113":[90,116,81],"117":[90,116,81],"120":[90,116,81],"123":[90,116,81],"129":[90,116,81],"135":[90,116,81],"139":[90,116,81],"143":[90,116,81],"147":[90,116,81],"189":[90,116,81],"232":[116,81],"236":[90,116,81],"241":[116,81],"257":[116,81],"264":[81],"294":[116,81],"309":[116,81],"317":[116,81],"325":[81],"344":[81],"364":[81],"386":[90,116,81],"387":[90,116,81],"426":[90,116,81],"435":[90,116,81],"441":[90,116,81],"476":[90,116,81]},"packages/@n8n/db/src/repositories/test-run.repository.ee.ts":{"18":[121],"22":[121],"45":[121],"53":[121],"57":[121],"62":[121],"71":[121],"82":[121],"89":[121],"97":[121],"101":[121],"120":[121],"133":[121],"145":[121],"155":[121],"170":[121],"180":[121]},"packages/@n8n/db/src/repositories/workflow-tag-mapping.repository.ts":{"8":"b:QkENEAEAiIBEAAAAIAQAAkASAAABAAAA","12":"b:QkENEAEAiIBEAAAAIAQAAkASAAABAAAA","13":"b:QkENEAEAiIBEAAAAIAQAAkASAAABAAAA","16":"b:QkENEAEAiIBEAAAAIAQAAkASAAABAAAA"},"packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/OpenAi.node.ts":{"12":[121]},"packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/helpers/binary-data.ts":{"11":[121]},"packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/helpers/description.ts":{"3":[121],"20":[121],"38":[121]},"packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/helpers/error-handling.ts":{"11":[1,7,12,121,13],"15":[1,7,12,121,13],"19":[1,7,12,121,13],"36":[1,7,12,121,13]},"packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/helpers/modelFiltering.ts":{"9":[121]},"packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/helpers/polling.ts":{"4":[121],"34":[121]},"packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/helpers/utils.ts":{"17":[121],"27":[121],"39":[121],"50":[121],"58":[121],"66":[121],"88":[121]},"packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/methods/listSearch.ts":{"13":[121],"36":[121],"45":[121],"46":[121],"49":[121],"63":[121],"69":[121],"75":[121],"82":[121],"85":[121],"89":[121],"92":[121],"96":[121],"121":[121],"126":[121],"130":[121],"173":[121]},"packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/methods/loadOptions.ts":{"5":[121]},"packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/transport/index.ts":{"15":[121]},"packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/v1/OpenAiV1.node.ts":{"22":[121],"82":[121]},"packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/v1/actions/assistant/create.operation.ts":{"176":[121],"185":[121]},"packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/v1/actions/assistant/deleteAssistant.operation.ts":{"18":[121]},"packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/v1/actions/assistant/list.operation.ts":{"25":[121],"62":[121]},"packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/v1/actions/assistant/message.operation.ts":{"157":[121],"164":[121],"224":[121],"231":[121]},"packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/v1/actions/assistant/update.operation.ts":{"120":[121],"126":[121],"132":[121],"207":[121],"213":[121],"214":[121],"217":[121],"223":[121],"224":[121],"228":[121]},"packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/v1/actions/audio/generate.operation.ts":{"134":[121]},"packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/v1/actions/audio/transcribe.operation.ts":{"58":[121]},"packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/v1/actions/audio/translate.operation.ts":{"50":[121]},"packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/v1/actions/descriptions.ts":{"3":[121]},"packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/v1/actions/file/deleteFile.operation.ts":{"51":[121]},"packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/v1/actions/file/list.operation.ts":{"53":[121],"63":[121]},"packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/v1/actions/file/upload.operation.ts":{"57":[121]},"packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/v1/actions/image/analyze.operation.ts":{"131":[121],"158":[121],"172":[121]},"packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/v1/actions/image/generate.operation.ts":{"255":[121],"291":[121]},"packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/v1/actions/router.ts":{"16":[121]},"packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/v1/actions/text/classify.operation.ts":{"53":[121]},"packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/v1/actions/text/message.operation.ts":{"240":[121],"244":[121],"360":[121]},"packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/v2/OpenAiV2.node.ts":{"23":[121],"92":[121]},"packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/v2/actions/audio/generate.operation.ts":{"134":[121]},"packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/v2/actions/audio/transcribe.operation.ts":{"58":[121]},"packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/v2/actions/audio/translate.operation.ts":{"50":[121]},"packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/v2/actions/conversation/create.operation.ts":{"82":[121]},"packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/v2/actions/conversation/get.operation.ts":{"27":[121]},"packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/v2/actions/conversation/remove.operation.ts":{"27":[121]},"packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/v2/actions/conversation/update.operation.ts":{"34":[121]},"packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/v2/actions/descriptions.ts":{"3":[121]},"packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/v2/actions/file/deleteFile.operation.ts":{"51":[121]},"packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/v2/actions/file/list.operation.ts":{"62":[121],"72":[121]},"packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/v2/actions/file/upload.operation.ts":{"65":[121]},"packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/v2/actions/image/analyze.operation.ts":{"133":[121],"157":[121],"169":[121]},"packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/v2/actions/image/edit.operation.ts":{"584":[121],"618":[121],"695":[121]},"packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/v2/actions/image/generate.operation.ts":{"73":[121],"118":[121]},"packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/v2/actions/router.ts":{"17":[121]},"packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/v2/actions/text/classify.operation.ts":{"58":[121]},"packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/v2/actions/text/helpers/responses.ts":{"14":[121],"16":[121],"19":[121],"22":[121],"25":[121],"31":[121],"106":[121]},"packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/v2/actions/text/response.operation.ts":{"583":[121],"587":[121],"629":[121],"632":[121],"652":[121],"659":[121],"717":[121],"719":[121],"736":[121]},"packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/v2/actions/video/generate.operation.ts":{"92":[121],"132":[121],"135":[121]},"packages/cli/src/evaluation.ee/test-runs.controller.ee.ts":{"25":[121],"34":[121],"57":[121],"81":[121],"87":[121],"88":[121],"99":[121],"111":[121],"121":[121],"136":[121],"145":[121],"159":[121],"178":[121],"200":[121]},"packages/core/src/execution-engine/active-workflow-triggers.ts":{"37":"b:AAAAAgKgAAIKAAAAAAAAAgAAACr1AuAB","49":"b:AAAAAgKgAAIKAAAAAAAAAgAAACr1AuAB","68":"b:AAAAAgKgAAIKAAAAAAAAAgAAACr1AuAB","82":"b:AAAAAgKgAAIKAAAAAAAAAgAAACr1AuAB","89":"b:AAAAAgKgAAIKAAAAAAAAAgAAACr1AuAB","96":"b:AAAAAgKgAAIKAAAAAAAAAgAAACr1AuAB","105":"b:AAAAAgKgAAIKAAAAAAAAAgAAACr1AuAB","121":"b:AAAAAgKgAAIKAAAAAAAAAgAAACr1AuAB","134":"b:AAAAAgKgAAIKAAAAAAAAAgAAACr1AuAB","154":"b:AAAAAgKgAAIKAAAAAAAAAgAAACr1AuAB","167":"b:AAAAAgKgAAIKAAAAAAAAAgAAACr1AuAB","168":"b:AAAAAgKgAAIKAAAAAAAAAgAAACr1AuAB","194":"b:AAAAAgKgAAIKAAAAAAAAAgAAACr1AuAB","295":"b:AAAAAgKgAAIKAAAAAAAAAgAAACr1AuAB","327":"b:AAAAAgKgAAIKAAAAAAAAAgAAACr1AuAB","361":"b:AAAAAgKgAAIKAAAAAAAAAgAAACr1AuAB","388":"b:AAAAAgKgAAIKAAAAAAAAAgAAACr1AuAB","441":"b:AAAAAgKgAAIKAAAAAAAAAgAAACr1AuAB","451":"b:AAAAAgKgAAIKAAAAAAAAAgAAACr1AuAB","485":"b:AAAAAgKgAAIKAAAAAAAAAgAAACr1AuAB","494":"b:AAAAAgKgAAIKAAAAAAAAAgAAACr1AuAB","508":"b:AAAAAgKgAAIKAAAAAAAAAgAAACr1AuAB","531":"b:AAAAAgKgAAIKAAAAAAAAAgAAACr1AuAB","541":"b:AAAAAgKgAAIKAAAAAAAAAgAAACr1AuAB","552":"b:AAAAAgKgAAIKAAAAAAAAAgAAACr1AuAB","569":"b:AAAAAgKgAAIKAAAAAAAAAgAAACr1AuAB"},"packages/core/src/execution-engine/scheduled-task-manager.ts":{"34":"b:AAAAAgIAAAIKAAAAAAAAAgAAACj1AkAB","40":"b:AAAAAgIAAAIKAAAAAAAAAgAAACj1AkAB","48":"b:AAAAAgIAAAIKAAAAAAAAAgAAACj1AkAB","57":"b:AAAAAgIAAAIKAAAAAAAAAgAAACj1AkAB","67":"b:AAAAAgIAAAIKAAAAAAAAAgAAACj1AkAB","107":"b:AAAAAgIAAAIKAAAAAAAAAgAAACj1AkAB","110":"b:AAAAAgIAAAIKAAAAAAAAAgAAACj1AkAB","151":"b:AAAAAgIAAAIKAAAAAAAAAgAAACj1AkAB","175":"b:AAAAAgIAAAIKAAAAAAAAAgAAACj1AkAB","179":"b:AAAAAgIAAAIKAAAAAAAAAgAAACj1AkAB","192":"b:AAAAAgIAAAIKAAAAAAAAAgAAACj1AkAB","219":"b:AAAAAgIAAAIKAAAAAAAAAgAAACj1AkAB","224":"b:AAAAAgIAAAIKAAAAAAAAAgAAACj1AkAB","228":"b:AAAAAgIAAAIKAAAAAAAAAgAAACj1AkAB","234":"b:AAAAAgIAAAIKAAAAAAAAAgAAACj1AkAB","238":"b:AAAAAgIAAAIKAAAAAAAAAgAAACj1AkAB","246":"b:AAAAAgIAAAIKAAAAAAAAAgAAACj1AkAB","261":"b:AAAAAgIAAAIKAAAAAAAAAgAAACj1AkAB","279":"b:AAAAAgIAAAIKAAAAAAAAAgAAACj1AkAB"},"packages/core/src/execution-engine/workflow-active-triggers-state.ts":{"14":"b:AAAAAACgAAIKAAAAAAAAAgAAAALwAOAB","20":"b:AAAAAACgAAIKAAAAAAAAAgAAAALwAOAB","25":"b:AAAAAACgAAIKAAAAAAAAAgAAAALwAOAB","30":"b:AAAAAACgAAIKAAAAAAAAAgAAAALwAOAB","35":"b:AAAAAACgAAIKAAAAAAAAAgAAAALwAOAB","43":"b:AAAAAACgAAIKAAAAAAAAAgAAAALwAOAB","48":"b:AAAAAACgAAIKAAAAAAAAAgAAAALwAOAB","52":"b:AAAAAACgAAIKAAAAAAAAAgAAAALwAOAB","63":"b:AAAAAACgAAIKAAAAAAAAAgAAAALwAOAB","68":"b:AAAAAACgAAIKAAAAAAAAAgAAAALwAOAB","73":"b:AAAAAACgAAIKAAAAAAAAAgAAAALwAOAB","78":"b:AAAAAACgAAIKAAAAAAAAAgAAAALwAOAB","83":"b:AAAAAACgAAIKAAAAAAAAAgAAAALwAOAB","88":"b:AAAAAACgAAIKAAAAAAAAAgAAAALwAOAB","93":"b:AAAAAACgAAIKAAAAAAAAAgAAAALwAOAB"},"packages/frontend/@n8n/design-system/src/components/N8nCallout/Callout.vue":{"9":"b:mmUCIAyByISYAimQCxCEEtD8PwoBAAAA","33":"b:mmUCIAyByISYAimQCxCEEtD8PwoBAAAA","42":"b:mmUAAACByASIAimAAhAEEtB8JwoBAAAA","46":"b:mmUAAACByASIAimAAhAEEtB8JwoBAAAA","55":"b:mmUCIAyByISYAimQCxCEEtD8PwoBAAAA","64":"b:mmUCIAyByISYAimQCxCEEtD8PwoBAAAA"},"packages/frontend/editor-ui/src/app/components/ActivationModal.vue":{"22":[47,121,67,155,160],"31":[47,121,67,155,160],"33":[47,121,67,155,160],"94":[121],"98":[47,121,67,155,160],"103":[47,121,67,155,160],"105":[47,121,67,155,160],"110":[47,121,67,155,160],"122":[47,121,67,155,160],"125":[47,121,67,155,160]},"packages/frontend/editor-ui/src/app/components/MainHeader/WorkflowPublishModal.vue":{"31":[47,121,67,155,160],"46":[47,121,67,155,160],"50":[47,121,67,155,160],"54":[47,121,67,155,160],"60":[47,121,67,155,160],"70":[47,121,67,155,160],"72":[47,121,67,155,160],"81":[47,121,67,155,160],"87":[47,121,67,155,160],"107":[47,121,67,155,160],"111":[47,121,67,155,160],"129":[47,121,67,155,160],"133":[47,121,67,155,160],"136":[47],"158":[47,121,67,155,160],"201":[47,121,67,155,160],"249":[47,121,67,155,160],"262":[47,121,67,155,160],"263":[47,121,67,155,160],"265":[47,121,67,155,160],"309":[47,121,67,155,160]},"packages/frontend/editor-ui/src/app/components/WorkflowVersionForm.vue":{"16":[47,121,67,155,160],"26":[47,121,67,155,160],"38":[47,121,67,155,160],"47":[47,121,67,155,160],"62":[47,121,67,155,160]},"packages/frontend/editor-ui/src/app/composables/canvasConnectionReplacement.utils.ts":{"85":"b:gmXCMAUIiMAAAAAAstAAcl69fAoAAAAA","93":"b:gkHCMAUICMAAAAAAsBAAcl68bAoAAAAA","101":"b:gmXCMAUIiMAAAAAAstAAcl69fAoAAAAA","191":[55,111,124],"211":[55,111,124],"220":[55,111,124],"227":[55,111,124],"251":[55,111,124],"266":[55,111,124]},"packages/frontend/editor-ui/src/app/composables/useWorkflowActivate.ts":{"88":[47,121,67,155,160],"185":[160]},"packages/frontend/editor-ui/src/features/ai/evaluation.ee/composables/useWorkflowEvaluationState.ts":{"25":[121],"26":[121],"51":[121]},"packages/frontend/editor-ui/src/features/ai/evaluation.ee/evaluation.api.ts":{"70":[83,121],"74":[83,121],"143":[83],"147":[83],"178":[83]},"packages/frontend/editor-ui/src/features/ai/evaluation.ee/views/EvaluationsRootView.vue":{"44":[83,121],"46":[83],"47":[83],"52":[83],"56":[83],"91":[83,121],"101":[83,121],"118":[83,121]},"packages/frontend/editor-ui/src/features/shared/nodeCreator/components/ItemTypes/ActionItem.vue":{"19":"b:gEGCMAEACcAAAAAArQAAchyUPQoAAAAA","35":"b:gEGCMAEACcAAAAAArQAAchyUPQoAAAAA","40":[48],"42":"b:gEGCMAEACcAAAAAArQAAchyUPQoAAAAA","45":[48],"75":[48],"88":[48],"94":[48],"99":"b:gEGCMAEACcAAAAAArQAAchyUPQoAAAAA","111":"b:gEGCMAEACcAAAAAArQAAchyUPQoAAAAA","117":"b:gEGCMAEACcAAAAAArQAAchyUPQoAAAAA"},"packages/frontend/editor-ui/src/features/shared/nodeCreator/components/ItemTypes/LabelItem.vue":{"11":"b:AEECMAAACcAAAAAACAAAAgCULAgAAAAA"},"packages/frontend/editor-ui/src/features/shared/nodeCreator/components/Modes/ActionsMode.vue":{"41":"b:gEGCMAEACcAAAAAArQAAchyUPQoAAAAA","63":"b:gEGCMAAACcAAAAAAKQAAABCAPAoAAAAA","66":"b:gEGCMAEACcAAAAAArQAAchyUPQoAAAAA","69":"b:AEGCMAEACcAAAAAApAAAchyUPQoAAAAA","76":[7,28,8,96,99,48,143,14,147,148,149],"86":"b:gEGCMAAACcAAAAAAKQAAABCAPAoAAAAA","92":"b:gEGCMAEACcAAAAAArQAAchyUPQoAAAAA","93":"b:gEGCMAEACcAAAAAArQAAchyUPQoAAAAA","98":"b:gEGCMAEACcAAAAAArQAAchyUPQoAAAAA","100":"b:gEGCMAEACcAAAAAArQAAchyUPQoAAAAA","102":"b:gEGCMAEACcAAAAAArQAAchyUPQoAAAAA","104":"b:gEGCMAEACcAAAAAArQAAchyUPQoAAAAA","109":"b:gEGCMAEACcAAAAAArQAAchyUPQoAAAAA","118":"b:gEGCMAAACcAAAAAAKQAAABCAPAoAAAAA","127":"b:gEGCMAEACcAAAAAArQAAchyUPQoAAAAA","130":"b:gEGCMAEACcAAAAAArQAAchyUPQoAAAAA","137":"b:gEGCMAEACcAAAAAArQAAchyUPQoAAAAA","139":"b:gEGCMAEACcAAAAAArQAAchyUPQoAAAAA","151":[48],"157":[48],"161":"b:gEGCMAEACcAAAAAArQAAchyUPQoAAAAA","165":[48],"169":[48],"171":[48],"176":"b:gEGCMAEACcAAAAAAqQAAchyUPAoAAAAA","189":"b:gEGCMAEACcAAAAAAqQAAchyUPAoAAAAA","212":"b:gEGCMAEACcAAAAAArQAAchyUPQoAAAAA","215":"b:gEGCMAEACcAAAAAArQAAchyUPQoAAAAA","223":"b:gEGCMAEACcAAAAAArQAAchyUPQoAAAAA","234":[48],"238":[48],"258":"b:gEGCMAEACcAAAAAArQAAchyUPQoAAAAA","262":"b:gEGCMAEACcAAAAAArQAAchyUPQoAAAAA","263":"b:gEGCMAEACcAAAAAArQAAchyUPQoAAAAA","280":"b:gEGCMAAACcAAAAAAKQAAABCAPAoAAAAA","293":[96,48,143,14,147,148],"299":[96,48,143,14,147,148],"311":[48],"320":"b:gEGCMAEACcAAAAAArQAAchyUPQoAAAAA","335":[96,99,48,143,14,147,148],"339":[48],"341":[48]},"packages/frontend/editor-ui/src/features/shared/nodeCreator/components/OrderSwitcher.vue":{"11":"b:gEGCMAEACcAAAAAArQAAchyUPQoAAAAA"},"packages/frontend/editor-ui/src/features/workflows/workflowHistory/utils.ts":{"9":[133],"15":[47,121,67,155,160],"19":[133],"37":[133],"69":[133],"74":[133]},"packages/nodes-base/nodes/StickyNote/StickyNote.node.ts":{"57":[53,128,135,151,155,159,161]},"packages/workflow/src/graph/graph-utils.ts":{"41":[128],"62":[128],"80":[128],"88":[128],"95":[128],"103":[128],"114":[128],"115":[128],"123":[128],"133":[128],"134":[128],"145":[128],"157":[128],"170":[128],"174":[128],"209":[128],"218":[128],"239":[128]},"packages/frontend/@n8n/design-system/src/components/N8nKeyboardShortcut/N8nKeyboardShortcut.vue":{"7":"b:AgAAAAAAAAAAAAAAACABYAcCBAEAAAAA","10":"b:AgAAAAAAAAAAAAAAACABYAcCBAEAAAAA","25":"b:AgAAAAAAAAAAAAAAACABYAcCBAEAAAAA","29":"b:AgAAAAAAAAAAAAAAACABYAcCBAEAAAAA","33":"b:AgAAAAAAAAAAAAAAACABYAcCBAEAAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nMarkdown/Markdown.vue":{"37":[53,111,128,129,135,151,159,68,161],"82":[53,111,128,129,135,151,159,68,161],"89":[161],"110":[53,111,128,129,135,151,159,161],"114":[135],"123":[53,111,128,129,135,151,159,161],"152":[129,159,161],"185":[53,111,128,129,135,151,159,68,161],"242":[129],"260":[53,111,128,129,135,151,159,68,161],"278":[68,161]},"packages/frontend/@n8n/design-system/src/components/N8nMarkdown/youtube.ts":{"34":[53,111,128,129,135,151,159,68,161],"42":[53,129,135,151,159,161]},"packages/frontend/@n8n/design-system/src/components/N8nSticky/Sticky.vue":{"16":[53,111,128,129,135,151,159,161],"28":[53,111,128,129,135,151,159,161],"32":[53,111,128,129,135,151,159,161],"36":[53,111,128,129,135,151,159,161],"38":[53,111,128,129,135,151,159,161],"43":[53,111,128,129,135,151,159,161],"58":[53,111,128,129,135,151,159,161],"65":[53,111,128,129,135,151,159,161],"99":[53,111,128,129,135,151,159,161]},"packages/frontend/editor-ui/src/features/workflows/canvas/components/elements/groups/CanvasNodeGroupTitleBar.vue":{"34":[185,128],"76":[185,128],"77":[185,128],"80":[185,128],"81":[185,128],"82":[185,128],"83":[185,128],"87":[185,128],"89":[185,128],"103":[185,128],"114":[185,128],"121":[185,128],"135":[185,128],"137":[185,128],"144":[185,128],"148":[185],"152":[185,128],"156":[185],"162":[185,128],"169":[185,128],"173":[185],"188":[185,128],"195":[185,128],"210":[185,128],"214":[185,128],"215":[185,128],"221":[185,128],"244":[185,128],"245":[185,128],"248":[185,128],"252":[185,128],"255":[185,128],"262":[185,128],"269":[185,128],"274":[185,128],"275":[185],"281":[185],"283":[185],"290":[185],"296":[185,128],"300":[185,128],"304":[185],"311":[185],"315":[185],"322":[185],"331":[185],"335":[185],"340":[185],"349":[185],"362":[185],"376":[185,128],"389":[185],"393":[185,128],"408":[185],"438":[185,128],"453":[185,128],"476":[185,128],"605":[185],"626":[185],"633":[185],"640":[185],"656":[185],"674":[185]},"packages/frontend/editor-ui/src/features/workflows/canvas/components/elements/nodes/render-types/CanvasNodeStickyNote.vue":{"15":[185,53,111,128,129,135,151,159,161],"27":[185,53,111,128,129,135,151,159,161],"29":[185,53,111,128,129,135,151,159,161],"39":[185],"57":[185],"63":[185],"75":[185],"83":[185,129],"91":[185,53,111,128,129,135,151,159,161],"95":[185,128],"98":[185,53,111,128,129,135,151,159,161]},"packages/frontend/editor-ui/src/features/workflows/canvas/components/elements/nodes/toolbar/CanvasNodeStickyColorSelector.vue":{"10":[185,53,111,128,129,135,151,159,161],"17":[185,129],"21":[185,129],"27":[185],"31":[185,129],"35":[185],"40":[185],"44":[185],"49":[185],"53":[185,129],"60":[185],"61":[185],"66":[185,53,111,128,129,135,151,159,161],"70":[185,128],"73":[185,53,111,128,129,135,151,159,161],"86":[185,53,111,128,129,135,151,159,161],"95":[185,129],"98":[185,129],"135":[185]},"packages/frontend/editor-ui/src/features/workflows/canvas/nodeGroups.utils.ts":{"7":[128],"16":[128]},"packages/core/src/execution-engine/partial-execution-utils/clean-run-data.ts":{"12":[15,16,131,135,6,137,144,148]},"packages/core/src/execution-engine/partial-execution-utils/directed-graph.ts":{"40":[15,16,131,135,6,137,144,148],"45":[15,16,131,135,6,137,144,148],"49":[15,16,131,135,6,137,144,148],"58":[15,16,131,135,6,137,144,148],"72":[15,16,131,135,6,137,144,148],"86":[15,16,131,135,6,137,144,148],"91":[15,16,131,135,6,137,144,148],"116":[15,16,131,135,6,137,144,148],"172":[15,16,131,135,6,137,144,148],"201":[15,16,131,135,6,137,144,148],"216":[15,16,131,135,6,137,144,148],"233":[15,16,131,135,6,137,144,148],"255":[15,16,131,135,6,137,144,148],"259":[15,16,131,135,6,137,144,148],"276":[15,16,131,135,6,137,144,148],"293":[15,16,131,135,6,137,144,148],"297":[15,16,131,135,6,137,144,148],"347":[15,16,131,135,6,137,144,148],"355":[15,16,131,135,6,137,144,148],"366":[15,16,131,135,6,137,144,148],"414":[15,16,131,135,6,137,144,148],"454":[15,16,131,135,6,137,144,148],"458":[15,16,131,135,6,137,144,148],"466":[15,16,131,135,6,137,144,148],"499":[15,16,131,135,6,137,144,148],"537":[15,16,131,135,6,137,144,148],"543":[15,16,131,135,6,137,144,148],"566":[15,16,131,135,6,137,144,148]},"packages/core/src/execution-engine/partial-execution-utils/filter-disabled-nodes.ts":{"5":[15,16,131,135,6,137,144,148],"12":[15,16,131,135,6,137,144,148]},"packages/core/src/execution-engine/partial-execution-utils/find-start-nodes.ts":{"13":[15,16,131,135,6,137,144,148],"29":[15,16,131,135,6,137,144,148],"50":[15,16,131,135,6,137,144,148],"137":[15,16,131,135,6,137,144,148],"159":[15,16,131,135,6,137,144,148]},"packages/core/src/execution-engine/partial-execution-utils/find-subgraph.ts":{"6":[15,16,131,135,6,137,144,148],"39":[15,16,131,135,6,137,144,148],"45":[15,16,131,135,6,137,144,148],"88":[15,16,131,135,6,137,144,148]},"packages/core/src/execution-engine/partial-execution-utils/find-trigger-for-partial-execution.ts":{"12":[15,16,131,135,6,137,144,148],"14":[15,16,131,135,6,137,144,148],"17":[15,16,131,135,6,137,144,148],"29":[15,16,131,135,6,137,144,148],"30":[15,16,131,135,6,137,144,148],"31":[15,16,131,135,6,137,144,148],"36":[15,16,131,135,6,137,144,148],"61":[15,16,131,135,6,137,144,148],"78":[15,16,131,135,6,137,144,148],"98":[15,16,131,135,6,137,144,148],"112":[15,16,131,135,6,137,144,148],"115":[15,16,131,135,6,137,144,148],"121":[15,16,131,135,6,137,144,148]},"packages/core/src/execution-engine/partial-execution-utils/get-incoming-data.ts":{"3":[15,16,131,135,6,137,144,148],"13":[15,16,131,135,6,137,144,148],"17":[15,16,131,135,6,137,144,148]},"packages/core/src/execution-engine/partial-execution-utils/get-source-data-groups.ts":{"5":[15,16,131,135,6,137,144,145,148],"28":[15,16,131,135,6,137,144,145,148],"86":[15,16,131,135,6,137,144,145,148],"118":[15,16,131,135,6,137,144,145,148],"119":[15,16,131,135,6,137,144,145,148],"126":[15,16,131,135,6,137,144,145,148],"139":[15,16,131,135,6,137,144,145,148],"156":[15,16,131,135,6,137,144,145,148],"157":[15,16,131,135,6,137,144,145,148]},"packages/core/src/execution-engine/partial-execution-utils/handle-cycles.ts":{"15":[15,16,131,135,6,137,144,148],"26":[15,16,131,135,6,137,144,148],"41":[15,16,131,135,6,137,144,148]},"packages/core/src/execution-engine/partial-execution-utils/recreate-node-execution-stack.ts":{"20":[15,16,131,135,6,137,144,148],"39":[15,16,131,135,6,137,144,148],"72":[15,16,131,135,6,137,144,148],"103":[15,16,131,135,6,137,144,148]},"packages/core/src/execution-engine/partial-execution-utils/run-data-utils.ts":{"11":[15,16,131,135,6,137,144,148],"17":[15,16,131,135,6,137,144,148],"20":[15,16,131,135,6,137,144,148]},"packages/frontend/@n8n/design-system/src/components/N8nCircleLoader/CircleLoader.vue":{"3":[131],"15":[131],"16":[131],"18":[131],"22":[131],"23":[131],"24":[131],"28":[131]},"packages/frontend/@n8n/design-system/src/components/N8nIcon/custom/node-dirty.svg":{"11":[103,106,55,131,137,146,148,152]},"packages/frontend/@n8n/design-system/src/components/N8nIcon/custom/triangle.svg":{"8":[16,103,106,131,134,137,146,148,152]},"packages/frontend/@n8n/design-system/src/components/N8nIcon/nodes/compare-datasets.svg":{"11":[28,32,124,125,126,130,131,143]},"packages/frontend/@n8n/design-system/src/components/N8nIcon/nodes/convert-to-file.svg":{"11":"b:AAAAEAEAAAAAAAAAgAAAcByAAAAAAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nIcon/nodes/extract-from-file.svg":{"11":"b:AAAAEAFAAAAAAAAAAAAAcByAAAAAAAAA"},"packages/frontend/editor-ui/src/app/utils/forceParse.ts":{"9":[16,131,140]},"packages/frontend/editor-ui/src/features/ai/assistant/assistant.api.ts":{"15":[19,185,20],"42":[15,16,17,18,185],"71":[16,185],"83":[185,131],"95":[185],"105":[185],"125":[19,185,20],"132":[185],"136":[185],"143":[185],"151":[185],"160":[185]},"packages/frontend/editor-ui/src/features/shared/editors/components/CodeNodeEditor/AskAI/AskAI.vue":{"28":"b:AAABEAEAAAAAAAAAAAABcAyAAAAAAAAA","57":"b:AAABEAEAAAAAAAAAAAABcAyAAAAAAAAA","64":[131],"67":[131],"70":"b:AAABEAEAAAAAAAAAAAABcAyAAAAAAAAA","76":[131],"93":"b:AAABEAEAAAAAAAAAAAABcAyAAAAAAAAA","100":"b:AAABEAEAAAAAAAAAAAABcAyAAAAAAAAA","101":"b:AAABEAEAAAAAAAAAAAABcAyAAAAAAAAA","103":"b:AAABEAEAAAAAAAAAAAABcAyAAAAAAAAA","104":"b:AAABEAEAAAAAAAAAAAABcAyAAAAAAAAA","107":[131],"108":[131],"110":[131],"118":[131],"133":[131],"141":[131],"145":[131],"150":[131],"216":[131],"221":[131],"243":"b:AAABEAEAAAAAAAAAAAABcAyAAAAAAAAA","250":[131],"254":"b:AAABEAEAAAAAAAAAAAABcAyAAAAAAAAA","259":"b:AAABEAEAAAAAAAAAAAABcAyAAAAAAAAA","293":[131],"304":"b:AAABEAEAAAAAAAAAAAABcAyAAAAAAAAA","308":[131]},"packages/frontend/editor-ui/src/features/shared/editors/components/CodeNodeEditor/CodeNodeEditor.vue":{"43":"b:AAABEAEAAAAAAAAAAAABcAyAAEAAAAAA","71":[16,112,126,131,143],"72":"b:AAABEAEAAAAAAAAAAAABcAyAAEAAAAAA","73":[16,112,126,131,143],"76":"b:AAABEAEAAAAAAAAAAAABcAyAAEAAAAAA","77":[28,32,124,125,126,130,131,143],"78":"b:AAABEAEAAAAAAAAAAAABcAyAAEAAAAAA","85":"b:AAABEAEAAAAAAAAAAAABcAyAAEAAAAAA","86":"b:AAABEAEAAAAAAAAAAAABcAyAAEAAAAAA","87":"b:AAABEAEAAAAAAAAAAAABcAyAAEAAAAAA","90":"b:AAABEAEAAAAAAAAAAAABcAyAAEAAAAAA","97":"b:AAABEAEAAAAAAAAAAAABcAyAAEAAAAAA","100":"b:AAABEAEAAAAAAAAAAAABcAyAAEAAAAAA","109":"b:AAAAEAEAAAAAAAAAAAABcAyAAEAAAAAA","114":"b:AAABEAEAAAAAAAAAAAABcAyAAEAAAAAA","118":"b:AAABEAEAAAAAAAAAAAABcAyAAEAAAAAA","124":[131],"140":[131],"152":[16,112,131,143],"158":[16],"160":[16],"165":[16,112,131,143],"166":[16,112,131,143],"199":[131],"203":[131],"225":"b:AAABEAEAAAAAAAAAAAABcAyAAEAAAAAA","239":"b:AAABEAEAAAAAAAAAAAABcAyAAAAAAAAA","245":"b:AAABEAEAAAAAAAAAAAABcAyAAAAAAAAA","252":"b:AAABEAEAAAAAAAAAAAABcAyAAAAAAAAA","271":"b:AAABEAEAAAAAAAAAAAABcAyAAAAAAAAA","291":[32,158]},"packages/frontend/editor-ui/src/features/shared/editors/components/CodeNodeEditor/linter.ts":{"23":"b:AAABEAEAAAAAAAAAAAABcAyAAEAAAAAA","29":"b:AAABEAEAAAAAAAAAAAABcAyAAEAAAAAA","38":[16,112,126,131,143],"62":[131],"68":[131],"95":[16,112,126,131,143],"136":[16,112,126,131,143],"138":[16,112,126,131,143],"142":[16,112,126,131,143],"144":[16,112,126,131,143],"146":[16,112,126,131,143],"151":[131],"180":[131],"222":[131],"230":[131],"257":[16,112,126,131,143],"264":[131],"288":[16,112,126,131,143],"298":[131],"321":[16,112,126,131,143],"328":[131],"329":[131],"346":[131],"352":[131],"377":[16,112,126,131,143],"399":[16,112,126,131,143],"415":[16,112,126,131,143],"422":[131],"466":[131],"513":[16,112,126,131,143],"554":[16,112,126,131,143],"588":[16,112,126,131,143],"599":[131],"626":[131],"632":[131],"633":[131]},"packages/frontend/editor-ui/src/features/shared/editors/components/CodeNodeEditor/utils.ts":{"10":[16,112,126,131,143],"19":[16,112,126,131,143],"30":[16,112,126,131,143]},"packages/frontend/editor-ui/src/features/shared/editors/composables/useCodeEditor.ts":{"70":"b:AAABEAEAAAAAAAAAAAABcAyAAEAAAAAA","113":"b:AAABEAEAAAAAAAAAAAABcAyAAEAAAAAA","114":"b:AAABEAEAAAAAAAAAAAABcAyAAEAAAAAA","125":"b:AAABEAEAAAAAAAAAAAABcAyAAEAAAAAA","134":"b:AAABEAEAAAAAAAAAAAABcAyAAEAAAAAA","166":"b:AAABEAEAAAAAAAAAAAABcAyAAAAAAAAA","170":"b:AAABEAEAAAAAAAAAAAABcAyAAEAAAAAA","181":"b:AAABEAEAAAAAAAAAAAABcAyAAEAAAAAA","185":[16,32,112,126,131,143],"195":"b:AAABEAEAAAAAAAAAAAABcAyAAAAAAAAA","203":"b:AAABEAEAAAAAAAAAAAABcAyAAAAAAAAA","224":"b:AAABEAEAAAAAAAAAAAABcAyAAEAAAAAA","239":"b:AAABEAEAAAAAAAAAAAABcAyAAEAAAAAA","253":[16,112,131,143],"262":[143],"267":[143],"275":"b:AAABEAEAAAAAAAAAAAABcAyAAEAAAAAA","370":[16,28,32,124,125,126,130,131,143],"382":"b:AAABEAEAAAAAAAAAAAABcAyAAEAAAAAA","386":"b:AAAAEAEAAAAAAAAAAAABcAyAAEAAAAAA","417":[16,131,143],"443":[16,131,143]},"packages/frontend/editor-ui/src/features/shared/editors/plugins/codemirror/typescript/client/completions.ts":{"19":[131],"30":[131]},"packages/frontend/editor-ui/src/features/shared/editors/plugins/codemirror/typescript/client/linter.ts":{"4":[131],"8":[131]},"packages/frontend/editor-ui/src/features/shared/editors/plugins/codemirror/typescript/client/useTypescript.ts":{"28":"b:AAABEAEAAAAAAAAAAAABcAyAAEAAAAAA","37":"b:AAABEAEAAAAAAAAAAAABcAyAAEAAAAAA","44":"b:AAABEAEAAAAAAAAAAAABcAyAAEAAAAAA","68":[16,131],"116":[16,131],"124":[16,131,143],"134":"b:AAABEAEAAAAAAAAAAAABcAyAAEAAAAAA","135":"b:AAABEAEAAAAAAAAAAAABcAyAAEAAAAAA","141":[131],"149":"b:AAAAEAEAAAAAAAAAAAABcAyAAEAAAAAA"},"packages/cli/src/services/pruning/executions-pruning.service.ts":{"44":[132],"55":[132],"61":[132],"70":[132],"81":[132],"90":[132],"92":[132],"99":[132],"100":[132],"102":[132],"103":[132],"113":[132],"125":[132],"135":[132],"144":[132]},"packages/nodes-base/nodes/Html/Html.node.ts":{"19":[132],"23":[132],"24":[132],"383":[132],"429":[132],"435":[132],"451":[132],"474":[132],"579":[132]},"packages/nodes-base/nodes/Html/utils.ts":{"10":[132],"12":[132],"13":[132],"21":[132],"29":[132],"39":[132]},"packages/nodes-base/nodes/Postgres/Postgres.node.ts":{"8":[132]},"packages/nodes-base/nodes/Postgres/transport/index.ts":{"27":[132],"47":[132],"50":[132],"56":[132],"91":[132],"92":[132],"96":[132],"100":[132],"106":[132],"114":[132],"121":[132],"138":[132],"156":[132],"157":[132],"162":[132],"168":[132],"186":[132],"208":[132]},"packages/nodes-base/nodes/Postgres/v1/PostgresV1.node.ts":{"288":[132],"297":[132],"331":[132]},"packages/nodes-base/nodes/Postgres/v1/genericFunctions.ts":{"13":[132],"27":[132],"33":[132],"36":[132],"55":[132],"62":[132],"78":[132],"83":[132],"88":[132],"92":[132],"105":[132],"119":[132],"121":[132],"136":[132],"157":[132],"182":[132],"198":[132],"200":[132],"224":[132],"231":[132],"255":[132],"291":[132],"307":[132],"308":[132],"314":[132],"327":[132],"346":[132],"383":[132],"398":[132],"399":[132],"405":[132],"418":[132],"425":[132],"450":[132],"494":[132],"510":[132],"511":[132],"517":[132],"520":[132],"537":[132],"546":[132],"558":[132],"561":[132],"587":[132],"626":[132],"641":[132],"642":[132],"648":[132],"651":[132],"668":[132],"677":[132],"690":[132],"693":[132],"721":[132]},"packages/nodes-base/nodes/Postgres/v2/PostgresV2.node.ts":{"16":[132],"25":[132]},"packages/nodes-base/nodes/Postgres/v2/actions/database/deleteTable.operation.ts":{"88":[132]},"packages/nodes-base/nodes/Postgres/v2/actions/database/executeQuery.operation.ts":{"55":[132],"65":[132],"119":[132],"120":[132]},"packages/nodes-base/nodes/Postgres/v2/actions/database/insert.operation.ts":{"161":[132]},"packages/nodes-base/nodes/Postgres/v2/actions/database/select.operation.ts":{"79":[132]},"packages/nodes-base/nodes/Postgres/v2/actions/database/update.operation.ts":{"195":[132],"279":[132],"286":[132],"333":[132]},"packages/nodes-base/nodes/Postgres/v2/actions/database/upsert.operation.ts":{"194":[132],"286":[132],"298":[132]},"packages/nodes-base/nodes/Postgres/v2/actions/router.ts":{"15":[132]},"packages/nodes-base/nodes/Postgres/v2/helpers/utils.ts":{"27":[132],"36":[132],"46":[132],"50":[132],"51":[132],"54":[132],"58":[132],"63":[132],"70":[132],"79":[132],"87":[132],"130":[132],"151":[132],"180":[132],"192":[132],"207":[132],"226":[132],"234":[132],"311":[132],"314":[132],"315":[132],"322":[132],"353":[132],"356":[132],"359":[132],"366":[132],"376":[132],"392":[132],"413":[132],"453":[132],"495":[132],"501":[132],"517":[132],"518":[132],"526":[132],"527":[132],"530":[132],"537":[132],"540":[132],"547":[132],"562":[132],"592":[132],"608":[132],"613":[132],"620":[132],"625":[132],"628":[132],"645":[132],"652":[132],"673":[132],"676":[132],"694":[132],"711":[132],"752":[132],"754":[132],"766":[132],"772":[132],"781":[132]},"packages/nodes-base/nodes/Postgres/v2/methods/credentialTest.ts":{"10":[132]},"packages/nodes-base/nodes/Postgres/v2/methods/listSearch.ts":{"6":[132],"17":[132],"24":[132],"40":[132]},"packages/nodes-base/nodes/Postgres/v2/methods/loadOptions.ts":{"7":[132],"23":[132],"30":[132],"38":[132],"43":[132]},"packages/nodes-base/nodes/Postgres/v2/methods/resourceMapping.ts":{"61":[132],"76":[132],"98":[132],"100":[132]},"packages/nodes-base/utils/connection-pool-manager.ts":{"53":[132],"66":[132],"68":[132],"74":[132],"81":[132],"104":[132],"131":[132],"139":[132],"152":[132],"172":[132]},"packages/nodes-base/utils/postgres.ts":{"12":[132],"32":[132],"34":[132],"38":[132],"41":[132],"47":[132],"55":[132]},"packages/@n8n/codemirror-lang-sql/src/complete.ts":{"192":[132],"193":[132]},"packages/frontend/@n8n/design-system/src/components/N8nIcon/custom/node-pin.svg":{"11":"b:AEAAAAAAIAAAACAAAAAAgNT+BwECAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nIcon/nodes/data-table.svg":{"11":[1,29,93,8,51,62,63,132,143,14,155]},"packages/frontend/@n8n/design-system/src/components/N8nIcon/nodes/html.svg":{"11":[132]},"packages/frontend/editor-ui/src/features/ndv/parameters/components/ParameterIssues.vue":{"9":"b:AGHEAIQASAhAAAAgIAAAABCICAIAAAAA","17":"b:AGDEAIAASAgAAAAgIAAAABCICAIAAAAA"},"packages/frontend/editor-ui/src/features/shared/editors/components/HtmlEditor/HtmlEditor.vue":{"51":[132],"63":[132],"68":[132],"97":[132],"100":[132],"237":[132],"243":[132],"247":[132],"260":[132],"265":[132]},"packages/frontend/editor-ui/src/features/shared/editors/components/HtmlEditor/utils.ts":{"44":[132]},"packages/frontend/editor-ui/src/features/shared/editors/components/InlineExpressionEditor/ExpressionOutput.vue":{"20":"b:AEAAAAAAAAQAAAAAAAAAABA8HgAAAAAA","30":"b:AEAAAAAAAAQAAAAAAAAAABA8HgAAAAAA","35":"b:AEAAAAAAAAQAAAAAAAAAABA8FgAAAAAA","49":"b:AEAAAAAAAAQAAAAAAAAAABA8HgAAAAAA","50":"b:AEAAAAAAAAQAAAAAAAAAABA8FgAAAAAA","53":"b:AEAAAAAAAAQAAAAAAAAAABA8HgAAAAAA","71":"b:AEAAAAAAAAQAAAAAAAAAABA8FgAAAAAA","82":"b:AEAAAAAAAAQAAAAAAAAAABA8FgAAAAAA","85":"b:AEAAAAAAAAQAAAAAAAAAABA8HgAAAAAA","95":[140],"105":"b:AEAAAAAAAAQAAAAAAAAAABA8HgAAAAAA","106":"b:AEAAAAAAAAQAAAAAAAAAABA8FgAAAAAA","119":"b:AEAAAAAAAAQAAAAAAAAAABA8HgAAAAAA","132":"b:AEAAAAAAAAQAAAAAAAAAABA8HgAAAAAA","137":[58,132,138,139,140,141,14,147,148],"141":[140],"142":"b:AEAAAAAAAAQAAAAAAAAAABA8HgAAAAAA"},"packages/frontend/editor-ui/src/features/shared/editors/components/InlineExpressionEditor/OutputItemSelect.vue":{"7":"b:AEAAAAAAAAQAAAAAAAAAABA8HgAAAAAA","10":"b:AEAAAAAAAAQAAAAAAAAAABA8HgAAAAAA","11":"b:AEAAAAAAAAQAAAAAAAAAABA8HgAAAAAA","12":"b:AEAAAAAAAAQAAAAAAAAAABA8HgAAAAAA","13":"b:AEAAAAAAAAQAAAAAAAAAABA8HgAAAAAA","14":"b:AEAAAAAAAAQAAAAAAAAAABA8HgAAAAAA","17":"b:AEAAAAAAAAQAAAAAAAAAABA8HgAAAAAA","18":"b:AEAAAAAAAAQAAAAAAAAAABA8HgAAAAAA","19":"b:AEAAAAAAAAQAAAAAAAAAABA8HgAAAAAA","20":"b:AEAAAAAAAAQAAAAAAAAAABA8HgAAAAAA","21":"b:AEAAAAAAAAQAAAAAAAAAABA8HgAAAAAA","25":"b:AEAAAAAAAAQAAAAAAAAAABA8HgAAAAAA","31":[14,145],"35":[14],"37":"b:AEAAAAAAAAQAAAAAAAAAABA8HgAAAAAA","70":"b:AEAAAAAAAAQAAAAAAAAAABA8HgAAAAAA"},"packages/frontend/editor-ui/src/features/shared/editors/components/SqlEditor/SqlEditor.vue":{"42":[132],"80":[132],"82":[132],"88":[132],"130":[132],"135":[132],"140":[132],"146":[132],"154":[132],"158":[132],"160":[132],"163":[132],"208":[132],"213":[132]},"packages/frontend/editor-ui/src/features/shared/editors/plugins/codemirror/completions/blank.completions.ts":{"8":"b:AEAAAAAAAAQAAAAAAAAAABA8HAAAAAAA"},"packages/frontend/editor-ui/src/features/shared/editors/plugins/codemirror/completions/bracketAccess.completions.ts":{"17":"b:AEAAAAAAAAQAAAAAAAAAABA8HAAAAAAA","53":[138,139,140,14],"62":[139],"70":[138,139,140,14],"74":[138,139,140,14],"75":[138,139,140,14]},"packages/frontend/editor-ui/src/features/shared/editors/plugins/codemirror/completions/dollar.completions.ts":{"29":"b:AEAAAAAAAAQAAAAAAAAAABA8HAAAAAAA","43":[58,132,138,139,140,141,14,146,148],"60":[58,132,138,139,140,141,14,146,148],"138":[132,138,139,140,141,14,146,148],"153":[132,138,139,140,141,14,146,148],"155":[132,138,139,140,141,14,146,148]},"packages/frontend/editor-ui/src/features/shared/editors/plugins/codemirror/completions/nonDollar.completions.ts":{"9":"b:AEAAAAAAAAQAAAAAAAAAABA8HAAAAAAA"},"packages/@n8n/db/src/repositories/tag.repository.ts":{"11":[112,64,150,154,156,157,158,159],"15":[112,64,150,154,156,157,158,159],"26":[112,64,150,154,156,157,158,159],"35":[112,64,150,154,156,157,158,159],"47":[112,64,150,154,156,157,158,159],"64":[112,64,150,154,156,157,158,159],"76":[112,64,150,154,156,157,158,159]},"localhost-34679/assets/_plugin-vue_export-helper-Da88TEg1.js":{"2":[40]},"localhost-34679/assets/chunk-CC9Q-vWm.js":{"8":[40],"9":[40],"10":[40],"19":[40],"23":[40],"29":[40],"33":[40],"34":[40]},"packages/@n8n/api-types/src/push/heartbeat.ts":{"9":[0,93,39,40,51]},"packages/frontend/editor-ui/src/app/components/WorkflowCanvasHost.vue":{"9":[37,38,39,40,41],"35":[37,38,39,40,41],"45":[37,38,39,40,41],"50":[37,38,39,40,41],"60":[37,38,39,40,41]},"packages/frontend/editor-ui/src/app/components/WorkflowCanvasHostBody.vue":{"20":[37,38,39,40,41],"53":[37,38,39,40,41],"71":[37,38,39,40,41],"89":[37,38,39,40,41],"99":[37,38,39,40,41],"101":[40],"106":[37,41],"120":[37,38,39,40,41],"126":[37,38,39,40,41],"127":[37,38,39,40,41]},"packages/frontend/editor-ui/src/features/ai/instanceAi/components/ApprovalOptionList.vue":{"23":[38,40],"32":[38,40],"39":[38,40],"59":[38,40],"89":[38,40],"90":[38]},"packages/frontend/editor-ui/src/features/ai/instanceAi/components/ConfirmationFooter.vue":{"15":[38,39,40]},"packages/frontend/editor-ui/src/features/ai/instanceAi/components/ConfirmationPreview.vue":{"2":[38,40]},"packages/frontend/editor-ui/src/features/ai/instanceAi/components/InstanceAiWorkflowPreview.vue":{"31":[37,38,39,40,41],"48":[37,38,39,40,41],"105":[37,38,39,40,41],"106":[37,38,39,40,41],"124":[37,38,39,40,41],"141":[37,38,39,40,41],"170":[37,38,39,40,41]},"packages/frontend/editor-ui/src/features/ai/instanceAi/composables/useInstanceAiWorkflowPreviewExecution.ts":{"18":[38],"22":[38],"42":[37,38,39,40,41],"55":[38],"70":[37,38,39,40,41],"74":[38],"89":[37,38,39,40,41],"97":[37,38,39,40,41],"121":[37,38,39,40,41],"134":[37,38,39,40,41],"165":[37,38,39,40,41],"173":[37,38,39,40,41],"174":[37,38,39,40,41],"181":[37,38,39,40,41],"188":[37,38,39,40,41],"194":[37,41]},"packages/frontend/editor-ui/src/features/ai/instanceAi/confirmationKinds.ts":{"16":[38,39,40]},"packages/@n8n/db/src/repositories/scheduled-job.repository.ts":{"61":"b:AAAAAAIAAAIKAAAAAAAAAAAAACi1AgQB","69":"b:AAAAAAIAAAIKAAAAAAAAAAAAACi1AgQB","86":"b:AAAAAAIAAAIKAAAAAAAAAAAAACi1AgQB","120":"b:AAAAAAIAAAIKAAAAAAAAAAAAACi1AgQB","129":"b:AAAAAAIAAAIKAAAAAAAAAAAAACi1AgQB","135":"b:AAAAAAIAAAIKAAAAAAAAAAAAACi1AgQB","140":"b:AAAAAAIAAAIKAAAAAAAAAAAAACi1AgQB","143":"b:AAAAAAIAAAIKAAAAAAAAAAAAACi1AgQB","164":"b:AAAAAAIAAAIKAAAAAAAAAAAAACi1AgQB","189":"b:AAAAAAIAAAIKAAAAAAAAAAAAACi1AgQB","205":"b:AAAAAAIAAAIKAAAAAAAAAAAAACi1AgQB","213":"b:AAAAAAIAAAIKAAAAAAAAAAAAACi1AgQB","223":"b:AAAAAAIAAAIKAAAAAAAAAAAAACi1AgQB","236":"b:AAAAAAIAAAIKAAAAAAAAAAAAACi1AgQB","249":"b:AAAAAAIAAAIKAAAAAAAAAAAAACi1AgQB","260":"b:AAAAAAIAAAIKAAAAAAAAAAAAACi1AgQB","261":"b:AAAAAAIAAAIKAAAAAAAAAAAAACi1AgQB","263":"b:AAAAAAIAAAIKAAAAAAAAAAAAACi1AgQB","269":"b:AAAAAAIAAAIKAAAAAAAAAAAAACi1AgQB","270":"b:AAAAAAIAAAIKAAAAAAAAAAAAACi1AgQB","279":"b:AAAAAAIAAAIKAAAAAAAAAAAAACi1AgQB","280":"b:AAAAAAIAAAIKAAAAAAAAAAAAACi1AgQB","293":"b:AAAAAAIAAAIKAAAAAAAAAAAAACi1AgQB","297":"b:AAAAAAIAAAIKAAAAAAAAAAAAACi1AgQB","298":"b:AAAAAAIAAAIKAAAAAAAAAAAAACi1AgQB"},"packages/@n8n/scheduler/src/core/provisioning/provision.ts":{"22":"b:AAAAAAIAAAIKAAAAAAAAAAAAACi1AgAB","26":"b:AAAAAAIAAAIKAAAAAAAAAAAAACi1AgAB","28":"b:AAAAAAIAAAIKAAAAAAAAAAAAACi1AgAB","29":"b:AAAAAAIAAAIKAAAAAAAAAAAAACi1AgAB","48":"b:AAAAAAIAAAIKAAAAAAAAAAAAACi1AgAB","49":"b:AAAAAAIAAAIKAAAAAAAAAAAAACi1AgAB","53":"b:AAAAAAIAAAIKAAAAAAAAAAAAACi1AgAB","54":"b:AAAAAAIAAAIKAAAAAAAAAAAAACi1AgAB","59":"b:AAAAAAIAAAIKAAAAAAAAAAAAACi1AgAB","69":"b:AAAAAAIAAAIKAAAAAAAAAAAAACi1AgAB","72":"b:AAAAAAIAAAIKAAAAAAAAAAAAACi1AgAB","79":"b:AAAAAAIAAAIKAAAAAAAAAAAAACi1AgAB"},"packages/@n8n/scheduler/src/core/provisioning/provisioner.ts":{"39":"b:AAAAAAIAAAIKAAAAAAAAAAAAACi1AgAB","44":"b:AAAAAAIAAAIKAAAAAAAAAAAAACi1AgAB","46":"b:AAAAAAIAAAIKAAAAAAAAAAAAACi1AgAB","49":"b:AAAAAAIAAAIKAAAAAAAAAAAAACi1AgAB","51":"b:AAAAAAIAAAIKAAAAAAAAAAAAACi1AgAB"},"packages/@n8n/scheduler/src/observability/provisioner-tracing.ts":{"23":"b:AAAAAAIAAAIKAAAAAAAAAAAAACi1AgAB","25":"b:AAAAAAIAAAIKAAAAAAAAAAAAACi1AgAB","28":"b:AAAAAAIAAAIKAAAAAAAAAAAAACi1AgAB","43":"b:AAAAAAIAAAIKAAAAAAAAAAAAACi1AgAB","46":"b:AAAAAAIAAAIKAAAAAAAAAAAAACi1AgAB","65":"b:AAAAAAIAAAIKAAAAAAAAAAAAACi1AgAB","67":"b:AAAAAAIAAAIKAAAAAAAAAAAAACi1AgAB","68":"b:AAAAAAIAAAIKAAAAAAAAAAAAACi1AgAB","69":"b:AAAAAAIAAAIKAAAAAAAAAAAAACi1AgAB","82":"b:AAAAAAIAAAIKAAAAAAAAAAAAACi1AgAB"},"packages/cli/src/errors/response-errors/bad-request.error.ts":{"4":[162,8,65,155],"8":[162,8,65,155]},"packages/cli/src/errors/response-errors/not-found.error.ts":{"4":[162,84,85,92,33,180,11,50,51],"14":[162,84,85,92,33,180,11,50,51]},"packages/cli/src/errors/response-errors/webhook-not-found.error.ts":{"3":[162,11],"36":[162,11]},"packages/cli/src/scheduling/durable-job-provisioner.ts":{"76":"b:AAAAAAIAAAIKAAAAAAAAAAAAACi1AgAB","86":"b:AAAAAAIAAAIKAAAAAAAAAAAAACi1AgAB","87":"b:AAAAAAIAAAIKAAAAAAAAAAAAACi1AgAB","101":"b:AAAAAAIAAAIKAAAAAAAAAAAAACi1AgAB","112":"b:AAAAAAIAAAIKAAAAAAAAAAAAACi1AgAB","121":"b:AAAAAAIAAAIKAAAAAAAAAAAAACi1AgAB","132":"b:AAAAAAIAAAIKAAAAAAAAAAAAACi1AgAB","140":"b:AAAAAAIAAAIKAAAAAAAAAAAAACi1AgAB","146":"b:AAAAAAIAAAIKAAAAAAAAAAAAACi1AgAB","147":"b:AAAAAAIAAAIKAAAAAAAAAAAAACi1AgAB","152":"b:AAAAAAIAAAIKAAAAAAAAAAAAACi1AgAB","155":"b:AAAAAAIAAAIKAAAAAAAAAAAAACi1AgAB","163":"b:AAAAAAIAAAIKAAAAAAAAAAAAACi1AgAB","165":"b:AAAAAAIAAAIKAAAAAAAAAAAAACi1AgAB","180":"b:AAAAAAIAAAIKAAAAAAAAAAAAACi1AgAB","187":"b:AAAAAAIAAAIKAAAAAAAAAAAAACi1AgAB","189":"b:AAAAAAIAAAIKAAAAAAAAAAAAACi1AgAB","212":"b:AAAAAAIAAAIKAAAAAAAAAAAAACi1AgAB","219":"b:AAAAAAIAAAIKAAAAAAAAAAAAACi1AgAB","224":"b:AAAAAAIAAAIKAAAAAAAAAAAAACi1AgAB","226":"b:AAAAAAIAAAIKAAAAAAAAAAAAACi1AgAB","230":"b:AAAAAAIAAAIKAAAAAAAAAAAAACi1AgAB","232":"b:AAAAAAIAAAIKAAAAAAAAAAAAACi1AgAB","235":"b:AAAAAAIAAAIKAAAAAAAAAAAAACi1AgAB","247":"b:AAAAAAIAAAIKAAAAAAAAAAAAACi1AgAB","252":"b:AAAAAAIAAAIKAAAAAAAAAAAAACi1AgAB","259":"b:AAAAAAIAAAIKAAAAAAAAAAAAACi1AgAB","260":"b:AAAAAAIAAAIKAAAAAAAAAAAAACi1AgAB","262":"b:AAAAAAIAAAIKAAAAAAAAAAAAACi1AgAB","264":"b:AAAAAAIAAAIKAAAAAAAAAAAAACi1AgAB","278":"b:AAAAAAIAAAIKAAAAAAAAAAAAACi1AgAB","321":"b:AAAAAAIAAAIKAAAAAAAAAAAAACi1AgAB"},"packages/cli/src/scheduling/poll-trigger-node/poll-trigger-job-registrar.ts":{"29":"b:AAAAAAIAAAAKAAAAAAAAAAAAACi1AgAB","44":"b:AAAAAAIAAAAKAAAAAAAAAAAAACi1AgAB","79":"b:AAAAAAIAAAAKAAAAAAAAAAAAACi1AgAB","87":"b:AAAAAAIAAAAKAAAAAAAAAAAAACi1AgAB","108":"b:AAAAAAIAAAAKAAAAAAAAAAAAACi1AgAB","117":"b:AAAAAAIAAAAKAAAAAAAAAAAAACi1AgAB","127":"b:AAAAAAIAAAAKAAAAAAAAAAAAACi1AgAB","141":"b:AAAAAAIAAAAKAAAAAAAAAAAAACi1AgAB","152":"b:AAAAAAIAAAAKAAAAAAAAAAAAACi1AgAB","164":"b:AAAAAAIAAAAKAAAAAAAAAAAAACi1AgAB"},"packages/cli/src/scheduling/scheduler-tracer.ts":{"13":"b:AAAAAAIAAAAKAAAAAAAAAAAAACgVAgAB","15":"b:AAAAAAIAAAAKAAAAAAAAAAAAACgVAgAB"},"packages/core/src/observability/tracing/noop-tracing.ts":{"4":"b:AAAAAgIAAAIKAAAAAAAAAAAAACi1AgAB","11":"b:AAAAAgIAAAIKAAAAAAAAAAAAACi1AgAB","15":"b:AAAAAgIAAAIKAAAAAAAAAAAAACi1AgAB","19":"b:AAAAAgIAAAIKAAAAAAAAAAAAACi1AgAB","23":"b:AAAAAgIAAAIKAAAAAAAAAAAAACi1AgAB","27":"b:AAAAAgIAAAIKAAAAAAAAAAAAACi1AgAB","31":"b:AAAAAgIAAAIKAAAAAAAAAAAAACi1AgAB","35":"b:AAAAAgIAAAIKAAAAAAAAAAAAACi1AgAB","39":"b:AAAAAgIAAAIKAAAAAAAAAAAAACi1AgAB","43":"b:AAAAAgIAAAIKAAAAAAAAAAAAACi1AgAB","47":"b:AAAAAgIAAAIKAAAAAAAAAAAAACi1AgAB","51":"b:AAAAAgIAAAIKAAAAAAAAAAAAACi1AgAB","59":"b:AAAAAgIAAAIKAAAAAAAAAAAAACi1AgAB","62":"b:AAAAAgIAAAIKAAAAAAAAAAAAACi1AgAB","66":"b:AAAAAgIAAAIKAAAAAAAAAAAAACi1AgAB"},"packages/nodes-base/nodes/Form/FormTrigger.node.ts":{"8":[162,23,101,155]},"packages/nodes-base/nodes/Form/utils/utils.ts":{"49":"b:QAHKAICGQAAQAAAAIAAAAIAAACgEAIAA","60":"b:QAHKAICGQAAQAAAAIAAAAIAAACgEAIAA","73":"b:QAHKAICGQAAQAAAAIAAAAIAAACgEAIAA","151":"b:QAHKAICGQAAQAAAAIAAAAIAAACgEAIAA","152":"b:QAHKAICGQAAQAAAAIAAAAIAAACgEAIAA","155":"b:QAHKAICGQAAQAAAAIAAAAIAAACgEAIAA","156":"b:QAHKAICGQAAQAAAAIAAAAIAAACgEAIAA","168":"b:QAHKAICGQAAQAAAAIAAAAIAAACgEAIAA","188":"b:QAHKAICGQAAQAAAAIAAAAIAAACgEAIAA","200":"b:QAHKAICGQAAQAAAAIAAAAIAAACgEAIAA","211":"b:QAHKAICGQAAQAAAAIAAAAIAAACgEAIAA","219":"b:QAHKAICGQAAQAAAAIAAAAIAAACgEAIAA","298":"b:QAHKAICGQAAQAAAAIAAAAIAAACgEAIAA","318":"b:QAHKAICGQAAQAAAAIAAAAIAAACgEAIAA","340":"b:QAHKAICGQAAQAAAAIAAAAIAAACgEAIAA","346":"b:QAHKAICGQAAQAAAAIAAAAIAAACgEAIAA","385":"b:QAHKAICGQAAQAAAAIAAAAIAAACgEAIAA","436":"b:QAHKAICGQAAQAAAAIAAAAIAAACgEAIAA","465":"b:QAHKAICGQAAQAAAAIAAAAIAAACgEAIAA","547":"b:QAHKAICGQAAQAAAAIAAAAIAAACgEAIAA","587":"b:QAHKAICGQAAQAAAAIAAAAIAAACgEAIAA","640":"b:QAHKAICGQAAQAAAAIAAAAIAAACgEAIAA","641":"b:QAHKAICGQAAQAAAAIAAAAIAAACgEAIAA","650":"b:QAHKAICGQAAQAAAAIAAAAIAAACgEAIAA","652":"b:QAHKAICGQAAQAAAAIAAAAIAAACgEAIAA","668":"b:QAHKAICGQAAQAAAAIAAAAIAAACgEAIAA","689":"b:QAHKAICGQAAQAAAAIAAAAIAAACgEAIAA","718":"b:QAHKAICGQAAQAAAAIAAAAIAAACgEAIAA","756":"b:QAHKAICGQAAQAAAAIAAAAIAAACgEAIAA","765":"b:QAHKAICGQAAQAAAAIAAAAIAAACgEAIAA","939":"b:QAHKAICGQAAQAAAAIAAAAIAAACgEAIAA","970":"b:QAHKAICGQAAQAAAAIAAAAIAAACgEAIAA"},"packages/nodes-base/nodes/Form/v1/FormTriggerV1.node.ts":{"91":[162,23,101,155],"98":[162,23,101,155]},"packages/nodes-base/nodes/Form/v2/FormTriggerV2.node.ts":{"155":[162,23,101,155],"277":[162,23,101,155],"284":[162,23,101,155]},"packages/@n8n/api-types/src/dto/project/list-projects-query.dto.ts":{"13":"b:AAAABAYADsQEAABQAAKAAQAAAAABAAAA","14":"b:AAAABAYADsQEAABQAAKAAQAAAAABAAAA","17":"b:AAAABAYADsQEAABQAAKAAQAAAAABAAAA","26":"b:AAAABAYADsQEAABQAAKAAQAAAAABAAAA","34":"b:AAAABAYADsQEAABQAAKAAQAAAAABAAAA","35":"b:AAAABAYADsQEAABQAAKAAQAAAAABAAAA","38":"b:AAAABAYADsQEAABQAAKAAQAAAAABAAAA","41":"b:AAAABAYADsQEAABQAAKAAQAAAAABAAAA"},"packages/@n8n/db/src/repositories/folder.repository.ts":{"11":"b:AAAATAAAD8BsgINBAAEgASAAAAAAAAAA","15":"b:AAAATAAAD8BsgINBAAEgASAAAAAAAAAA","23":"b:AAAATAAAD8BsgINBAAEgASAAAAAAAAAA","28":"b:AAAATAAAD8BsgINBAAEgASAAAAAAAAAA","39":"b:AAAATAAAD8BsgINBAAEgASAAAAAAAAAA","51":"b:AAAATAAAD8BsgINBAAEgASAAAAAAAAAA","56":"b:AAAATAAAD8BsgINBAAEgASAAAAAAAAAA","66":"b:AAAATAAAD8BsgINBAAEgASAAAAAAAAAA","85":"b:AAAATAAAD8BsgINBAAEgASAAAAAAAAAA","98":"b:AAAATAAAD8BsgINBAAEgASAAAAAAAAAA","104":"b:AAAATAAAD8BsgINBAAEgASAAAAAAAAAA","136":"b:AAAATAAAD8BsgINBAAEgASAAAAAAAAAA","140":"b:AAAATAAAD8BsgINBAAEgASAAAAAAAAAA","144":"b:AAAATAAAD8BsgINBAAEgASAAAAAAAAAA","148":"b:AAAATAAAD8BsgINBAAEgASAAAAAAAAAA","165":"b:AAAATAAAD8BsgINBAAEgASAAAAAAAAAA","198":"b:AAAATAAAD8BsgINBAAEgASAAAAAAAAAA","209":"b:AAAATAAAD8BsgINBAAEgASAAAAAAAAAA","225":"b:AAAATAAAD8BsgINBAAEgASAAAAAAAAAA","235":"b:AAAATAAAD8BsgINBAAEgASAAAAAAAAAA","240":"b:AAAATAAAD8BsgINBAAEgASAAAAAAAAAA","254":"b:AAAATAAAD8BsgINBAAEgASAAAAAAAAAA","260":"b:AAAATAAAD8BsgINBAAEgASAAAAAAAAAA","276":"b:AAAATAAAD8BsgINBAAEgASAAAAAAAAAA","295":"b:AAAATAAAD8BsgINBAAEgASAAAAAAAAAA","312":"b:AAAATAAAD8BsgINBAAEgASAAAAAAAAAA","352":"b:AAAATAAAD8BsgINBAAEgASAAAAAAAAAA","386":"b:AAAATAAAD8BsgINBAAEgASAAAAAAAAAA","395":"b:AAAATAAAD8BsgINBAAEgASAAAAAAAAAA","419":"b:AAAATAAAD8BsgINBAAEgASAAAAAAAAAA","422":"b:AAAATAAAD8BsgINBAAEgASAAAAAAAAAA"},"packages/@n8n/db/src/repositories/secrets-provider-connection.repository.ee.ts":{"8":"b:ekkKJA6kDMxUAABQsBKAAAAACAAJAAAA","12":"b:ekkKJA6kDMxUAABQsBKAAAAACAAJAAAA","16":"b:ekkKJA6kDMxUAABQsBKAAAAACAAJAAAA","25":"b:ekkKJA6kDMxUAABQsBKAAAAACAAJAAAA","33":"b:ekkKJA6kDMxUAABQsBKAAAAACAAJAAAA","36":"b:ekkKJA6kDMxUAABQsBKAAAAACAAJAAAA","55":"b:ekkKJA6kDMxUAABQsBKAAAAACAAJAAAA","89":"b:ekkKJA6kDMxUAABQsBKAAAAACAAJAAAA","119":"b:ekkKJA6kDMxUAABQsBKAAAAACAAJAAAA","125":"b:ekkKJA6kDMxUAABQsBKAAAAACAAJAAAA","140":"b:ekkKJA6kDMxUAABQsBKAAAAACAAJAAAA","164":"b:ekkKJA6kDMxUAABQsBKAAAAACAAJAAAA","182":"b:ekkKJA6kDMxUAABQsBKAAAAACAAJAAAA","190":"b:ekkKJA6kDMxUAABQsBKAAAAACAAJAAAA","200":"b:ekkKJA6kDMxUAABQsBKAAAAACAAJAAAA","210":"b:ekkKJA6kDMxUAABQsBKAAAAACAAJAAAA","222":"b:ekkKJA6kDMxUAABQsBKAAAAACAAJAAAA"},"packages/@n8n/db/src/utils/sql.ts":{"4":"b:AAACbAQADMRkgIMBAAAEACAAAAAAAAAA"},"packages/@n8n/decorators/src/timed.ts":{"16":"b:AAAATAAADsBsgINBAAEAASAAAAAAAAAA","17":"b:AAAATAAADsBsgINBAAEAASAAAAAAAAAA","18":"b:AAAATAAADsBsgINBAAEAASAAAAAAAAAA","23":"b:AAAATAAADsBsgINBAAEAASAAAAAAAAAA"},"packages/cli/src/middlewares/list-query/dtos/pagination.dto.ts":{"6":"b:AAAATAAADsBsgINBAAEAASAAAAEAAAAA","15":"b:AAAATAAADsBsgINBAAEAASAAAAEAAAAA"},"packages/cli/src/middlewares/list-query/dtos/workflow.sort-by.dto.ts":{"6":"b:AAAATAAADsBsgINBAAEAASAAAAEAAAAA","13":"b:AAAATAAADsBsgINBAAEAASAAAAEAAAAA"},"packages/cli/src/modules/data-table/data-table-aggregate.service.ts":{"13":"b:AAAATAAADABkgINBAAAgASAAAAACAAAA","20":"b:AAAATAAADABkgINBAAAgASAAAAACAAAA","21":"b:AAAATAAADABkgINBAAAgASAAAAACAAAA","23":"b:AAAATAAADABkgINBAAAgASAAAAACAAAA","30":"b:AAAATAAADABkgINBAAAgASAAAAACAAAA","33":"b:AAAATAAADABkgINBAAAgASAAAAACAAAA"},"packages/cli/src/modules/external-secrets.ee/external-secrets-providers.ee.ts":{"13":[26,33,94,105,51,179,58,63,66,160],"23":[26,33,94,105,51,179,58,63,66,160],"27":[26,33,94,105,51,179,58,63,66,160],"31":[26,33,94,105,51,179,58,63,66,160],"35":[26,33,94,105,51,179,58,63,66,160]},"packages/cli/src/modules/external-secrets.ee/providers/aws-secrets-manager.ts":{"114":[26,33,94,105,51,179,58,63,66,160],"122":[26,33,94,105,51,179,58,63,66,160],"158":[26,33,94,105,51,179,58,63,66,160],"173":[26,33,94,105,51,179,58,63,66,160],"188":[26,33,94,105,51,179,58,63,66,160],"192":[26,33,94,105,51,179,58,63,66,160],"199":[26,33,94,105,51,179,58,63,66,160],"213":[26,33,94,105,51,179,58,63,66,160],"217":[26,33,94,105,51,179,58,63,66,160],"221":[26,33,94,105,51,179,58,63,66,160],"225":[26,33,94,105,51,179,58,63,66,160],"231":[26,33,94,105,51,179,58,63,66,160],"241":[26,33,94,105,51,179,58,63,66,160],"250":[26,33,94,105,51,179,58,63,66,160],"277":[26,33,94,105,51,179,58,63,66,160],"278":[26,33,94,105,51,179,58,63,66,160],"283":[26,33,94,105,51,179,58,63,66,160],"287":[26,33,94,105,51,179,58,63,66,160],"291":[26,33,94,105,51,179,58,63,66,160],"316":[26,33,94,105,51,179,58,63,66,160],"334":[26,33,94,105,51,179,58,63,66,160]},"packages/cli/src/modules/external-secrets.ee/providers/azure-key-vault/azure-key-vault.ts":{"81":[26,33,94,105,51,58,63,66,160],"86":[26,33,94,105,51,58,63,66,160],"92":[26,33,94,105,51,58,63,66,160],"114":[26,33,94,105,51,58,63,66,160],"130":[26,33,94,105,51,58,63,66,160],"134":[26,33,94,105,51,58,63,66,160],"143":[26,33,94,105,51,58,63,66,160],"222":[26,33,94,105,51,58,63,66,160],"226":[26,33,94,105,51,58,63,66,160],"230":[26,33,94,105,51,58,63,66,160],"234":[26,33,94,105,51,58,63,66,160],"245":[26,33,94,105,51,58,63,66,160],"267":[26,33,94,105,51,58,63,66,160]},"packages/cli/src/modules/external-secrets.ee/providers/gcp-secrets-manager/gcp-secrets-manager.ts":{"47":[26,33,94,105,51,58,63,66,160],"52":[26,33,94,105,51,58,63,66,160],"64":[26,33,94,105,51,58,63,66,160],"89":[26,33,94,105,51,58,63,66,160],"104":[26,33,94,105,51,58,63,66,160],"108":[26,33,94,105,51,58,63,66,160],"116":[26,33,94,105,51,58,63,66,160],"129":[26,33,94,105,51,58,63,66,160],"182":[26,33,94,105,51,58,63,66,160],"209":[26,33,94,105,51,58,63,66,160],"213":[26,33,94,105,51,58,63,66,160],"217":[26,33,94,105,51,58,63,66,160],"221":[26,33,94,105,51,58,63,66,160],"245":[26,33,94,105,51,58,63,66,160],"256":[26,33,94,105,51,58,63,66,160]},"packages/cli/src/modules/external-secrets.ee/providers/infisical.ts":{"164":[26,33,94,105,51,58,63,66,160],"172":[26,33,94,105,51,58,63,66,160],"177":[26,33,94,105,51,58,63,66,160],"184":[26,33,94,105,51,58,63,66,160],"210":[26,33,94,105,51,58,63,66,160],"221":[26,33,94,105,51,58,63,66,160],"242":[26,33,94,105,51,58,63,66,160],"276":[26,33,94,105,51,58,63,66,160],"289":[26,33,94,105,51,58,63,66,160],"291":[26,33,94,105,51,58,63,66,160],"294":[26,33,94,105,51,58,63,66,160],"295":[26,33,94,105,51,58,63,66,160],"304":[26,33,94,105,51,58,63,66,160],"307":[26,33,94,105,51,58,63,66,160],"314":[26,33,94,105,51,58,63,66,160],"320":[26,33,94,105,51,58,63,66,160],"336":[26,33,94,105,51,58,63,66,160],"348":[26,33,94,105,51,58,63,66,160],"364":[26,33,94,105,51,58,63,66,160],"368":[26,33,94,105,51,58,63,66,160],"372":[26,33,94,105,51,58,63,66,160],"376":[26,33,94,105,51,58,63,66,160],"386":[26,33,94,105,51,58,63,66,160],"411":[26,33,94,105,51,58,63,66,160]},"packages/cli/src/modules/external-secrets.ee/providers/one-password.ts":{"61":[26,33,94,105,51,58,63,66,160],"66":[26,33,94,105,51,58,63,66,160],"92":[26,33,94,105,51,58,63,66,160],"118":[26,33,94,105,51,58,63,66,160],"137":[26,33,94,105,51,58,63,66,160],"141":[26,33,94,105,51,58,63,66,160],"188":[26,33,94,105,51,58,63,66,160],"192":[26,33,94,105,51,58,63,66,160],"196":[26,33,94,105,51,58,63,66,160],"200":[26,33,94,105,51,58,63,66,160],"204":[26,33,94,105,51,58,63,66,160]},"packages/cli/src/modules/external-secrets.ee/providers/vault.ts":{"278":[26,33,94,105,51,58,63,66,160],"286":[26,33,94,105,51,58,63,66,160],"291":[26,33,94,105,51,58,63,66,160],"298":[26,33,94,105,51,58,63,66,160],"325":[26,33,94,105,51,58,63,66,160],"342":[26,33,94,105,51,58,63,66,160],"349":[26,33,94,105,51,58,63,66,160],"366":[26,33,94,105,51,58,63,66,160],"375":[26,33,94,105,51,58,63,66,160],"402":[26,33,94,105,51,58,63,66,160],"428":[26,33,94,105,51,58,63,66,160],"451":[26,33,94,105,51,58,63,66,160],"465":[26,33,94,105,51,58,63,66,160],"500":[26,33,94,105,51,58,63,66,160],"534":[26,33,94,105,51,58,63,66,160],"535":[26,33,94,105,51,58,63,66,160],"542":[26,33,94,105,51,58,63,66,160],"546":[26,33,94,105,51,58,63,66,160],"557":[26,33,94,105,51,58,63,66,160],"560":[26,33,94,105,51,58,63,66,160],"568":[26,33,94,105,51,58,63,66,160],"571":[26,33,94,105,51,58,63,66,160],"584":[26,33,94,105,51,58,63,66,160],"590":[26,33,94,105,51,58,63,66,160],"606":[26,33,94,105,51,58,63,66,160],"613":[26,33,94,105,51,58,63,66,160],"621":[26,33,94,105,51,58,63,66,160],"636":[26,33,94,105,51,58,63,66,160],"669":[26,33,94,105,51,58,63,66,160],"673":[26,33,94,105,51,58,63,66,160],"677":[26,33,94,105,51,58,63,66,160],"678":[26,33,94,105,51,58,63,66,160],"684":[26,33,94,105,51,58,63,66,160],"696":[26,33,94,105,51,58,63,66,160],"707":[26,33,94,105,51,58,63,66,160],"715":[26,33,94,105,51,58,63,66,160],"723":[26,33,94,105,51,58,63,66,160]},"packages/cli/src/modules/external-secrets.ee/secrets-providers-connections.controller.ee.ts":{"32":[26,33,94,105,51,58,63,66,160],"41":[26,33,94,105,51,58,63,66,160],"81":[26,33,94,105,51,58,63,66,160],"104":[26,33,94,105,51,58,63,66,160],"122":[26,33,94,105,51,58,63,66,160],"135":[26,33,94,105,51,58,63,66,160],"138":[26,33,94,105,51,58,63,66,160],"145":[26,33,94,105,51,58,63,66,160],"157":[26,33,94,105,51,58,63,66,160],"172":[26,33,94,105,51,58,63,66,160]},"packages/cli/src/modules/external-secrets.ee/secrets-providers-connections.service.ee.ts":{"39":"b:GkECJA4ACMQUAABYEBKAAAAACAABAAAA","53":"b:GkECJA4ACMQUAABYEBKAAAAACAABAAAA","79":"b:GkECJA4ACMQUAABYEBKAAAAACAABAAAA","107":"b:GkECJA4ACMQUAABYEBKAAAAACAABAAAA","124":"b:GkECJA4ACMQUAABYEBKAAAAACAABAAAA","141":"b:GkECJA4ACMQUAABYEBKAAAAACAABAAAA","146":"b:GkECJA4ACMQUAABYEBKAAAAACAABAAAA","147":"b:GkECJA4ACMQUAABYEBKAAAAACAABAAAA","152":"b:GkECJA4ACMQUAABYEBKAAAAACAABAAAA","153":"b:GkECJA4ACMQUAABYEBKAAAAACAABAAAA","168":"b:GkECJA4ACMQUAABYEBKAAAAACAABAAAA","190":"b:GkECJA4ACMQUAABYEBKAAAAACAABAAAA","212":"b:GkECJA4ACMQUAABYEBKAAAAACAABAAAA","221":"b:GkECJA4ACMQUAABYEBKAAAAACAABAAAA","244":"b:GkECJA4ACMQUAABYEBKAAAAACAABAAAA","252":"b:GkECJA4ACMQUAABYEBKAAAAACAABAAAA","262":"b:GkECJA4ACMQUAABYEBKAAAAACAABAAAA","266":"b:GkECJA4ACMQUAABYEBKAAAAACAABAAAA","274":"b:GkECJA4ACMQUAABYEBKAAAAACAABAAAA","282":"b:GkECJA4ACMQUAABYEBKAAAAACAABAAAA","286":"b:GkECJA4ACMQUAABYEBKAAAAACAABAAAA","288":"b:GkECJA4ACMQUAABYEBKAAAAACAABAAAA","295":"b:GkECJA4ACMQUAABYEBKAAAAACAABAAAA","310":"b:GkECJA4ACMQUAABYEBKAAAAACAABAAAA","320":"b:GkECJA4ACMQUAABYEBKAAAAACAABAAAA","338":"b:GkECJA4ACMQUAABYEBKAAAAACAABAAAA","339":"b:GkECJA4ACMQUAABYEBKAAAAACAABAAAA","350":"b:GkECJA4ACMQUAABYEBKAAAAACAABAAAA","376":"b:GkECJA4ACMQUAABYEBKAAAAACAABAAAA","403":"b:GkECJA4ACMQUAABYEBKAAAAACAABAAAA","407":"b:GkECJA4ACMQUAABYEBKAAAAACAABAAAA","420":"b:GkECJA4ACMQUAABYEBKAAAAACAABAAAA","437":"b:GkECJA4ACMQUAABYEBKAAAAACAABAAAA","441":"b:GkECJA4ACMQUAABYEBKAAAAACAABAAAA","468":"b:GkECJA4ACMQUAABYEBKAAAAACAABAAAA","472":"b:GkECJA4ACMQUAABYEBKAAAAACAABAAAA","485":"b:GkECJA4ACMQUAABYEBKAAAAACAABAAAA","501":"b:GkECJA4ACMQUAABYEBKAAAAACAABAAAA","523":"b:GkECJA4ACMQUAABYEBKAAAAACAABAAAA"},"packages/cli/src/modules/external-secrets.ee/secrets-providers-project.controller.ee.ts":{"32":[26,33,94,105,51,58,63,66,160],"42":[26,33,94,105,51,58,63,66,160],"56":[26,33,94,105,51,58,63,66,160],"89":[26,33,94,105,51,58,63,66,160],"96":[26,33,94,105,51,58,63,66,160],"101":[26,33,94,105,51,58,63,66,160],"125":[26,33,94,105,51,58,63,66,160],"160":[26,33,94,105,51,58,63,66,160],"181":[26,33,94,105,51,58,63,66,160]},"packages/cli/src/modules/external-secrets.ee/secrets-providers-types.controller.ee.ts":{"16":[26,33,94,105,51,58,63,66,160],"25":[26,33,94,105,51,58,63,66,160],"41":[26,33,94,105,51,58,63,66,160],"44":[26,33,94,105,51,58,63,66,160],"51":[26,33,94,105,51,58,63,66,160]},"packages/cli/src/modules/external-secrets.ee/types.ts":{"29":[26,33,94,105,51,179,63,66,160],"52":[26,33,94,105,51,179,63,66,160],"75":[26,33,94,105,51,179,63,66,160],"92":[26,33,94,105,51,179,63,66,160],"93":[26,33,94,105,51,179,63,66,160],"99":[26,33,94,105,51,179,63,66,160]},"packages/cli/src/modules/insights/database/repositories/insights-by-period-query.helper.ts":{"23":"b:AAACbAQADMRkgIMBAAAEACAAAAAAAAAA","55":"b:AAACbAQADMRkgIMBAAAEACAAAAAAAAAA"},"packages/cli/src/modules/insights/database/repositories/insights-by-period.repository.ts":{"38":"b:AAACbAQADMRkgIMBAAAEACAAAAAAAAAA","39":"b:AAACbAQADMRkgIMBAAAEACAAAAAAAAAA","40":"b:AAACbAQADMRkgIMBAAAEACAAAAAAAAAA","41":"b:AAACbAQADMRkgIMBAAAEACAAAAAAAAAA","42":"b:AAACbAQADMRkgIMBAAAEACAAAAAAAAAA","43":"b:AAACbAQADMRkgIMBAAAEACAAAAAAAAAA","44":"b:AAACbAQADMRkgIMBAAAEACAAAAAAAAAA","51":"b:AAACbAQADMRkgIMBAAAEACAAAAAAAAAA","55":"b:AAACbAQADMRkgIMBAAAEACAAAAAAAAAA","79":"b:AAACbAQADMRkgIMBAAAEACAAAAAAAAAA","83":"b:AAACbAQADMRkgIMBAAAEACAAAAAAAAAA","87":"b:AAACbAQADMRkgIMBAAAEACAAAAAAAAAA","97":"b:AAACbAQADMRkgIMBAAAEACAAAAAAAAAA","111":"b:AAACbAQADMRkgIMBAAAEACAAAAAAAAAA","119":"b:AAACbAQADMRkgIMBAAAEACAAAAAAAAAA","137":"b:AAACbAQADMRkgIMBAAAEACAAAAAAAAAA","160":"b:AAACbAQADMRkgIMBAAAEACAAAAAAAAAA","204":"b:AAACbAQADMRkgIMBAAAEACAAAAAAAAAA","240":"b:AAACbAQADMRkgIMBAAAEACAAAAAAAAAA","259":"b:AAACbAQADMRkgIMBAAAEACAAAAAAAAAA","305":"b:AAACbAQADMRkgIMBAAAEACAAAAAAAAAA","310":"b:AAACbAQADMRkgIMBAAAEACAAAAAAAAAA","323":"b:AAACbAQADMRkgIMBAAAEACAAAAAAAAAA","392":"b:AAACbAQADMRkgIMBAAAEACAAAAAAAAAA","407":"b:AAACbAQADMRkgIMBAAAEACAAAAAAAAAA","431":"b:AAACbAQADMRkgIMBAAAEACAAAAAAAAAA","440":"b:AAACbAQADMRkgIMBAAAEACAAAAAAAAAA"},"packages/cli/src/modules/insights/insights.controller.ts":{"22":"b:AAACbAQADMRkgIMBAAAEACAAAAAAAAAA","26":"b:AAACbAQADMRkgIMBAAAEACAAAAAAAAAA","43":"b:AAACbAQADMRkgIMBAAAEACAAAAAAAAAA","64":"b:AAACbAQADMRkgIMBAAAEACAAAAAAAAAA","86":"b:AAACbAQADMRkgIMBAAAEACAAAAAAAAAA","103":"b:AAACbAQADMRkgIMBAAAEACAAAAAAAAAA","110":"b:AAACbAQADMRkgIMBAAAEACAAAAAAAAAA","124":"b:AAACbAQADMRkgIMBAAAEACAAAAAAAAAA","128":"b:AAACbAQADMRkgIMBAAAEACAAAAAAAAAA","142":"b:AAACbAQADMRkgIMBAAAEACAAAAAAAAAA","158":"b:AAACbAQADMRkgIMBAAAEACAAAAAAAAAA"},"packages/cli/src/modules/insights/insights.service.ts":{"20":"b:AAACbAQADMRkgIMBAAAEACAAAAAAAAAA","32":"b:AAACbAQADMRkgIMBAAAEACAAAAAAAAAA","50":"b:AAACbAQADMRkgIMBAAAEACAAAAAAAAAA","57":"b:AAACbAQADMRkgIMBAAAEACAAAAAAAAAA","63":"b:AAACbAQADMRkgIMBAAAEACAAAAAAAAAA","68":"b:AAACbAQADMRkgIMBAAAEACAAAAAAAAAA","73":"b:AAACbAQADMRkgIMBAAAEACAAAAAAAAAA","95":"b:AAACbAQADMRkgIMBAAAEACAAAAAAAAAA","103":"b:AAACbAQADMRkgIMBAAAEACAAAAAAAAAA","132":"b:AAACbAQADMRkgIMBAAAEACAAAAAAAAAA","167":"b:AAACbAQADMRkgIMBAAAEACAAAAAAAAAA","200":"b:AAACbAQADMRkgIMBAAAEACAAAAAAAAAA","211":"b:AAACbAQADMRkgIMBAAAEACAAAAAAAAAA","232":"b:AAACbAQADMRkgIMBAAAEACAAAAAAAAAA","265":"b:AAACbAQADMRkgIMBAAAEACAAAAAAAAAA","290":"b:AAACbAQADMRkgIMBAAAEACAAAAAAAAAA"},"localhost-39695/assets/_plugin-vue_export-helper-Da88TEg1.js":{"2":[26,54,56,143,14,150,153]},"localhost-39695/assets/chunk-CC9Q-vWm.js":{"8":[26,54,56,143,14,150,153],"9":[26,54,56,143,14,150,153],"10":[26,54,56,143,14,150,153],"19":[26,54,56,143,14,150,153],"23":[26,54,56,143,14,150,153],"29":[26,54,56,143,14,150,153],"33":[26,54,56,143,14,150,153],"34":[26,54,56,143,14,150,153]},"packages/frontend/@n8n/design-system/src/components/N8nCard/Card.vue":{"9":"b:GAEALBwAD6xkAANIAAOIACAAQAADAAAA","14":"b:GAEALBwAD6xkAANIAAOIACAAQAADAAAA","19":"b:GAEALBwAD6xkAANIAAOIACAAQAADAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nDataTableServer/N8nDataTableServer.vue":{"52":"b:AAAABAIACIQEAABQAAIgAAAAAAABAAAA","95":"b:AAAABAIACIQEAABQAAIgAAAAAAABAAAA","96":[92,33,94,105,51,58,117,63],"104":"b:AAAABAIACIQEAABQAAIgAAAAAAABAAAA","111":"b:AAAABAIACIQEAABQAAIgAAAAAAABAAAA","115":"b:AAAABAIACIQEAABQAAIgAAAAAAABAAAA","121":"b:AAAABAIACIQEAABQAAIgAAAAAAABAAAA","133":"b:AAAABAIACIQEAABQAAIgAAAAAAABAAAA","137":"b:AAAABAIACIQEAABQAAIgAAAAAAABAAAA","146":"b:AAAABAIACIQEAABQAAIgAAAAAAABAAAA","151":"b:AAAABAIACIQEAABQAAIgAAAAAAABAAAA","180":"b:AAAABAIACIQEAABQAAIgAAAAAAABAAAA","181":"b:AAAABAIACIQEAABQAAIgAAAAAAABAAAA","192":[26,33,94,105,51,58,117,63,66,160],"218":"b:AAAABAIACIQEAABQAAIgAAAAAAABAAAA","226":[33,105],"229":[33,105],"241":"b:AAAABAIACIQEAABQAAIgAAAAAAABAAAA","290":"b:AAAABAIACIQEAABQAAIgAAAAAAABAAAA","322":[33,105],"340":"b:AAAABAIACIQEAABQAAIgAAAAAAABAAAA","344":[117],"356":[33,105],"389":"b:AAAABAIACIQEAABQAAIgAAAAAAABAAAA","406":"b:AAAABAIACIQEAABQAAIgAAAAAAABAAAA","407":"b:AAAABAIACIQEAABQAAIgAAAAAAABAAAA","466":"b:AAAABAIACIQEAABQAAIgAAAAAAABAAAA","467":"b:AAAABAIACIQEAABQAAIgAAAAAAABAAAA","472":"b:AAAABAIACIQEAABQAAIgAAAAAAABAAAA","511":[92,117],"513":[92,117]},"packages/frontend/@n8n/design-system/src/components/N8nEmptyState/EmptyState.vue":{"35":"b:AAECJB4AD+yEAABQAAPOAAAAAAABAAAA","55":"b:AAECJBwAD+yEAABQAAHOAAAAAAABAAAA","64":"b:AAECJB4ADuSEAABQAAPGAAAAAAABAAAA","73":"b:AAECJBwAD+yEAABAAAHOAAAAAAABAAAA","81":[29,35,94,114,58,59,115,61]},"packages/frontend/@n8n/design-system/src/components/N8nIconPicker/IconPicker.vue":{"45":[26,91,33,94,105,51,58,63,66,160],"78":[51],"80":[51],"82":[51],"84":[51],"88":[51],"91":[51],"95":[51],"99":[51],"100":[51],"113":[51],"123":[26,91,33,94,105,51,58,63,66,160],"135":[26,91,33,94,105,51,63,66,160],"150":[51],"151":[51],"156":[51],"161":[51],"166":[51],"182":[51],"192":[51],"216":[51],"217":[51],"218":[26,91,33,94,105,51,58,63,66,160],"237":[26,91,33,94,105,51,58,63,66,160],"267":[51],"291":[51],"303":[51],"318":[51],"325":[51],"337":[51],"356":[51],"368":[51],"399":[51],"411":[51],"418":[51]},"packages/frontend/@n8n/design-system/src/components/N8nIconPicker/useIconPickerSearch.ts":{"25":[26,91,33,94,105,51,58,63,66,160],"67":[51],"105":[51],"111":[51],"121":[51],"127":[51]},"packages/frontend/@n8n/design-system/src/components/N8nUserInfo/UserInfo.vue":{"22":[26,33,94,105,51,58,117,63,66,160],"29":[26,33,94,105,51,58,117,63,66,160],"35":[26,33,94,105,51,58,117,63,66,160],"50":[26,33,94,105,51,58,117,63,66,160],"58":[26,33,94,105,51,58,117,63,66,160]},"packages/frontend/@n8n/design-system/src/components/N8nUserSelect/UserSelect.vue":{"24":[26,33,94,105,51,58,63,66,160],"43":[26,33,94,105,51,58,63,66,160],"46":[33,94,105,51,58,63],"66":[26,33,94,105,51,58,63,66,160],"67":[33,94,105,51,58,63],"83":[33,105],"91":[33],"92":[33,105],"94":[33,94,105,51,58,63],"96":[26,33,94,105,51,58,63,66,160],"115":[26,33,94,105,51,58,63,66,160],"117":[26,33,94,105,51,58,63,66,160],"120":[33,94,105,51,58,63],"127":[33,94,105,51,58,63]},"packages/frontend/editor-ui/src/app/components/Badge.vue":{"8":[26],"17":[26]},"packages/frontend/editor-ui/src/app/components/TimeAgo.vue":{"20":"b:AAEAJBwAD4R0AABQAAOAACAAQAADAAAA","21":"b:AAEAJBwAD4R0AABQAAOAACAAQAADAAAA","31":"b:AAEAJBwAD4R0AABQAAOAACAAQAADAAAA","37":"b:AAEAJBwAD4R0AABQAAOAACAAQAADAAAA","41":"b:AAEAJBwAD4R0AABQAAOAACAAQAADAAAA"},"packages/frontend/editor-ui/src/app/components/VersionCard.vue":{"13":[26],"17":[26]},"packages/frontend/editor-ui/src/app/components/layouts/EmptyStateLayout.vue":{"21":[26,27,51,80,81,66],"44":[51],"49":[26,27,51,80,81,66],"60":[26,27,51,80,81,66],"83":[26,27,51,80,81,66],"91":[26,27,51,80,81,66],"117":[26,27,51,80,81,66],"127":[80],"192":[26,27,51,80,81,66],"201":[26,27,51,80,81,66]},"packages/frontend/editor-ui/src/app/components/layouts/ResourcesListEmptyState.vue":{"61":[114],"84":"b:AAECJBwADsQEAABAAAGEAAAAAAABAAAA","99":[29,35,94,114,58]},"packages/frontend/editor-ui/src/app/components/layouts/ResourcesListLayout.vue":{"150":"b:AAECJBwAD8RsAABAAAOEACAAQAADAAAA","151":[160],"154":"b:AAECJBwAD8RkAABAAAOEACAAQAADAAAA","167":"b:AAECJBwAD8RsAABAAAOEACAAQAADAAAA","177":"b:AAECJBwAD8RkAABAAAOEACAAQAADAAAA","181":"b:AAECIBwAD4RkAABAAAOEACAAQAADAAAA","185":[29,8,34,35,50,51,114,58,119,63],"210":[8,35,50,114,119,63],"254":"b:AAECJBwAD8RsAABAAAOEACAAQAADAAAA","255":[160],"264":"b:AAECJBwAD8RsAABAAAOEACAAQAADAAAA","271":"b:AAECJBwAD8RsAABAAAOEACAAQAADAAAA","272":[36,48,104,49,50,51,66,160],"278":"b:AAECJBwAD8RsAABAAAOEACAAQAADAAAA","285":"b:AAECJBwAD8RsAABAAAOEACAAQAADAAAA","286":[104,114,160],"290":"b:AAECJBwAD8RsAABAAAOEACAAQAADAAAA","291":[17,29,34,119,63],"297":"b:AAECJBwAD8RsAABAAAOEACAAQAADAAAA","304":"b:AAECJBwAD8RsAABAAAOEACAAQAADAAAA","311":"b:AAECJBwAD8RsAABAAAOEACAAQAADAAAA","323":"b:AAAAABAADMAMAABAAAMAACAAQAABAAAA","328":[29,8,104,49,114,58,119,63,160],"342":"b:AAECJBwAD8RkAABAAAOEACAAQAADAAAA","356":"b:AAEAIBwAD4RkAABAAAOEACAAQAADAAAA","361":[160],"366":"b:AAECJBwAD8RkAABAAAOEACAAQAADAAAA","370":[94],"378":[8],"389":"b:AAACIBQAD4AEAABAAAGAAAAAAAAAAAAA","399":"b:AAACIBQAD4AEAABAAAGAAAAAAAABAAAA","407":[34,36,50,51,119,63,66],"414":"b:AAACIBQAD4AEAABAAAGAAAAAAAABAAAA","429":[114],"433":[160],"437":"b:AAACIBQAD4AEAABAAAGAAAAAAAAAAAAA","438":"b:AAACIBQAD4AEAABAAAGAAAAAAAAAAAAA","451":[29,8,34,35,50,51,58,119,63],"458":[114],"465":[8],"471":[160],"475":[104,114,160],"489":[8,94],"538":"b:AAECJBwAD8RsAABAAAOEACAAQAADAAAA","588":"b:AAAABBAAD8BsAABAAAMAACAAQAADAAAA","604":"b:AAECJBwAD8RsAABAAAOEACAAQAADAAAA","622":"b:AAEAIBwAD4RkAABAAAOEACAAQAADAAAA","637":"b:AAEAIBwAD4RkAABAAAOEACAAQAADAAAA","647":[8],"648":"b:AAEAIBwAD4RkAABAAAOEACAAQAADAAAA","650":"b:AAEAIBwAD4RkAABAAAOEACAAQAADAAAA","668":[160],"686":"b:AAEAIBwAD4RkAABAAAOEACAAQAADAAAA","692":"b:AAEAIBwAD4RkAABAAAOEACAAQAADAAAA","719":[29,8,34,35,50,51,58,119,63],"730":"b:AAAAABAADwBkAABAAAMAACAAQAADAAAA","762":[114],"766":[114],"777":[114,160]},"packages/frontend/editor-ui/src/app/composables/useAutoScrollOnDrag.ts":{"13":"b:AAAADBAAD8BsAINBAAMAACAAQAADAAAA","20":"b:AAAADBAAD8BsAINBAAMAACAAQAADAAAA","99":[48],"103":"b:AAAADBAAD8BsAINBAAMAACAAQAADAAAA","108":"b:AAAADBAAD8BsAINBAAMAACAAQAADAAAA","117":"b:AAAABBAAD8BsAAJBAAMAACAAQAADAAAA","125":"b:AAAABBAADMAMAAJBAAMAACAAQAABAAAA"},"packages/frontend/editor-ui/src/app/composables/useDependencies.ts":{"14":"b:AAECLBwAD8RsAINBAAOAACAAQAADAAAA","18":"b:AAECIBwAD4RkAABAAAOAACAAQAADAAAA","59":[34,36,105,51,119,63,161],"67":"b:AAEAIBwAD4RkAABAAAOAACAAQAADAAAA","70":[34,36,105,51,119,63,161],"73":"b:AAEAIBwAD4RkAABAAAOAACAAQAADAAAA"},"packages/frontend/editor-ui/src/app/composables/useLatestFetch.ts":{"4":"b:AAAADBAAD8BsAINBAAMAACAAQAADAAAA","6":"b:AAAADBAAD8BkAANAAAMAACAAQAADAAAA"},"packages/frontend/editor-ui/src/app/composables/useN8nLocalStorage.ts":{"26":"b:AAECJBwAD8RsAABAAAOEACAAQAADAAAA","29":"b:AAAABBAAD8BsAABAAAMAACAAQAADAAAA","33":[94],"58":"b:AAAABBAAD8BsAABAAAMAACAAQAADAAAA"},"packages/frontend/editor-ui/src/app/composables/useResourcesListI18n.ts":{"8":"b:AAECJBwAD8RsAABAAAOEACAAQAADAAAA","15":"b:AAEAIBwAD4RkAABAAAOEACAAQAADAAAA"},"packages/frontend/editor-ui/src/app/layouts/DefaultLayout.vue":{"8":"b:AAECLB4AD8R8AINBAAOEACAAQAADAAAA","10":"b:AAECLB4AD8R8AINBAAOEACAAQAADAAAA"},"packages/frontend/editor-ui/src/app/utils/modules/tabUtils.ts":{"11":"b:AAECJB4AD8RsAABAAAOEACAAQAADAAAA","34":"b:AAECJB4AD8RsAABAAAOEACAAQAADAAAA","38":"b:AAECJB4AD8RsAABAAAOEACAAQAADAAAA"},"packages/frontend/editor-ui/src/app/views/WorkflowsView.vue":{"183":"b:AAAADBAAD8BsAINBAAMAACAAQAADAAAA","235":[48,104,49],"280":[48,104,49],"282":[48,104,49],"287":[48,104,49],"289":[48,104,49],"294":"b:AAAABBAAD8BsAABAAAMAACAAQAADAAAA","298":"b:AAAADBAAD8BsAINBAAMAACAAQAADAAAA","302":"b:AAAAABAADwBkAABAAAMAACAAQAADAAAA","304":"b:AAAAABAADwBkAABAAAMAACAAQAADAAAA","308":"b:AAAAABAADwBkAABAAAMAACAAQAADAAAA","312":"b:AAAAABAADwBkAABAAAMAACAAQAADAAAA","319":"b:AAAADBAAD8BsAINBAAMAACAAQAADAAAA","323":[36,48,104,49,50,51,66,160],"327":[36,48,104,49,50,51,66,160],"333":"b:AAAAABAADwBkAABAAAMAACAAQAADAAAA","337":"b:AAAADBAAD8BsAINBAAMAACAAQAADAAAA","341":"b:AAAAABAADwBkAABAAAMAACAAQAADAAAA","345":"b:AAAADBAAD8BsAINBAAMAACAAQAADAAAA","350":"b:AAAADBAAD8BsAINBAAMAACAAQAADAAAA","357":[36,48,104,49,50,51,66,160],"363":"b:AAAAABAADwBkAABAAAMAACAAQAADAAAA","369":[48,104,49],"375":[48,104,49],"380":[36,48,104,49,50,51,66,160],"386":"b:AAAAABAADwBkAABAAAMAACAAQAADAAAA","388":[36,48,104,49,50,51,66,160],"392":[36,48,104,49,50,51,66,160],"405":[36,48,104,49,50,51,66,160],"430":[36,48,104,49,50,51,66,160],"453":[36,48,104,49,50,51,66,160],"465":[36,48,104,49,50,51,66,160],"493":[36,48,104,49,50,51,66,160],"502":[48,104,49],"506":"b:AAAABBAAD8BsAABAAAMAACAAQAADAAAA","507":"b:AAAAABAADwBkAABAAAMAACAAQAADAAAA","545":[160],"560":"b:AAAABBAAD8BsAABAAAMAACAAQAADAAAA","574":[48,104,49,50,51,66,160],"585":[48,104,49,50,51,66],"594":"b:AAAABBAAD8BsAABAAAMAACAAQAADAAAA","600":"b:AAAABBAAD8BsAABAAAMAACAAQAADAAAA","604":"b:AAAABBAAD8BsAABAAAMAACAAQAADAAAA","617":"b:AAAABBAAD8BsAABAAAMAACAAQAADAAAA","621":"b:AAAADBAAD8BsAANAAAMAACAAQAADAAAA","626":"b:AAAABBAAD8BsAABAAAMAACAAQAADAAAA","638":"b:AAAADBAAD8BsAINBAAMAACAAQAADAAAA","648":"b:AAAADBAAD8BsAINBAAMAACAAQAADAAAA","650":[48,104,49],"669":[50,51,160],"679":[49],"712":"b:AAAABBAAD8BsAABAAAMAACAAQAADAAAA","722":[26,94,104,49,50,51,62,63,66,160],"726":[26,94,104,49,50,51,62,63,66,160],"734":"b:AAAADBAAD8BsAINBAAMAACAAQAADAAAA","754":"b:AAAABBAADMAMAAJBAAMAACAAQAABAAAA","772":"b:AAAADBAADABkAINBAAIAACAAQAADAAAA","788":"b:AAAADBAAD8BsAINBAAMAACAAQAADAAAA","845":"b:AAAADBAAD8BsAINBAAMAACAAQAADAAAA","859":[160],"893":"b:AAAAABAADwBkAABAAAMAACAAQAADAAAA","894":[48,104,49],"912":"b:AAAAABAADwBkAABAAAMAACAAQAADAAAA","913":"b:AAAAABAADwBkAABAAAMAACAAQAADAAAA","946":"b:AAAADBAAD8BsAINBAAMAACAAQAADAAAA","967":[36,48,104,49,50,51,66,160],"975":[104,160],"986":"b:AAAABBAAD8BsAABAAAMAACAAQAADAAAA","1016":[36,48,104,49,50,51,66,160],"1056":"b:AAAADBAAD8BsAINBAAMAACAAQAADAAAA","1061":"b:AAAADBAAD8BsAINBAAMAACAAQAADAAAA","1090":[160],"1132":[51],"1145":[51],"1268":[160],"1269":[160],"1278":[48,49],"1279":[48,49],"1285":[49],"1309":[48,104,49],"1332":[48],"1354":[48],"1362":[48],"1381":[48],"1413":[104,49],"1644":[104,49],"1705":[104,49],"1743":[104],"1789":[49],"1824":[104,49],"1843":[49],"1859":[48,49],"1986":[49,50,51],"2013":[50,51],"2060":[48,49],"2073":[48],"2131":[49],"2210":"b:AAAABBAAD8BsAABAAAMAACAAQAADAAAA","2247":"b:AAAAABAADwBkAABAAAMAACAAQAADAAAA","2314":"b:AAAAABAADwBkAABAAAMAACAAQAADAAAA","2350":"b:AAAAABAADwBkAABAAAMAACAAQAADAAAA","2360":[48,104,49],"2363":[48,104,49],"2364":[48,104,49],"2404":"b:AAAAABAADwBkAABAAAMAACAAQAADAAAA","2407":"b:AAAAABAADwBkAABAAAMAACAAQAADAAAA","2408":"b:AAAAABAADwBkAABAAAMAACAAQAADAAAA","2446":[26,94,104,49,50,51,62,63,66,160],"2465":[160],"2478":[160],"2493":[160],"2509":[160],"2513":"b:AAAAABAADwBkAABAAAMAACAAQAADAAAA","2521":[48,104,49,50,51,66],"2523":[48,104,49,50,51,66]},"packages/frontend/editor-ui/src/experiments/credentialsAppSelection/stores/credentialsAppSelection.store.ts":{"14":"b:AAAADBAAD8BsAINBAAMAACAAQAADAAAA","23":[26,27,51,80,81,66],"25":[26,27,51,80,81,66],"29":[26,27,51,80,81,66],"35":[26,27,51,80,81,66]},"packages/frontend/editor-ui/src/experiments/personalizedTemplates/stores/personalizedTemplates.store.ts":{"66":"b:AAAADBAAD8BsAINBAAMAACAAQAADAAAA","91":"b:AAAABBAAD8BkAABAAAMAACAAQAADAAAA","164":"b:AAAADBAAD8BsAINBAAMAACAAQAADAAAA"},"packages/frontend/editor-ui/src/experiments/personalizedTemplatesV3/stores/personalizedTemplatesV3.store.ts":{"13":"b:AAAADBAAD8BsAINBAAMAACAAQAADAAAA","27":"b:AAAABBAADsAEAABAAAMAACAAQAABAAAA","36":"b:AAAADBAAD8BsAINBAAMAACAAQAADAAAA","130":"b:AAAADBAAD8BsAINBAAMAACAAQAADAAAA"},"packages/frontend/editor-ui/src/experiments/surfaceMcpToNewCloudUsers/composables/useSurfaceMcpEmptyState.ts":{"15":[26,27,51,80,81,66],"25":[26,27,51,80,81,66],"43":[26,27,51,80,81,66],"47":[26,27,51,80,81,66],"55":[26,27,51,80,81,66],"72":[26,27,51,80,81,66]},"packages/frontend/editor-ui/src/experiments/surfaceMcpToNewCloudUsers/composables/useSurfaceMcpToNewCloudUsersEligibility.ts":{"7":[26,27,51,80,81,66],"13":[26,27,51,80,81,66]},"packages/frontend/editor-ui/src/experiments/surfaceMcpToNewCloudUsers/stores/surfaceMcpToNewCloudUsers.store.ts":{"26":[26,27,51,80,81,66]},"packages/frontend/editor-ui/src/experiments/templateRecoV2/stores/templateRecoV2.store.ts":{"14":"b:AAAADBAAD8BsAINBAAMAACAAQAADAAAA","21":"b:AAAABBAADsAEAABAAAMAACAAQAABAAAA"},"packages/frontend/editor-ui/src/features/agents/composables/useAgentPermissions.ts":{"34":"b:AAECLB4AD8RsAANAAAOEACAAQAADAAAA"},"packages/frontend/editor-ui/src/features/ai/mcpAccess/mcp.store.ts":{"39":"b:AAAADBAAD8BsAINBAAMAACAAQAADAAAA"},"packages/frontend/editor-ui/src/features/collaboration/projects/components/ProjectCreateResource.vue":{"14":"b:AAECJBwAD8RsAABAAAOEACAAQAADAAAA","23":"b:AAECJBwAD8RsAABAAAOEACAAQAADAAAA","34":[94,104,49],"35":"b:AAECJBwAD8RsAABAAAOEACAAQAADAAAA"},"packages/frontend/editor-ui/src/features/collaboration/projects/components/ProjectDeleteDialog.vue":{"62":[26,33,94,105,51,58,63,66,160]},"packages/frontend/editor-ui/src/features/collaboration/projects/components/ProjectExternalSecrets.vue":{"27":[26,33,94,105,51,58,63,66,160],"53":[26,33,94,105,51,58,63,66,160],"58":[26,33,94,105,51,58,63,66,160],"62":[58],"66":[26,33,94,105,51,58,63,66,160],"70":[26,33,94,105,51,58,63,66,160],"77":[26,33,94,105,51,58,63,66,160],"90":[26,33,94,105,51,58,63,66,160],"117":[26,33,94,105,51,58,63,66,160],"121":[26,33,94,105,51,58,63,66,160],"132":[26,33,94,105,51,58,63,66,160],"149":[58],"150":[58],"162":[58],"172":[58],"180":[26,33,94,105,51,58,63,66,160],"183":[58],"184":[58],"188":[26,33,94,105,51,58,63,66,160],"264":[26,33,94,105,51,58,63,66,160],"265":[26,33,94,105,51,58,63,66,160],"275":[26,33,94,105,51,58,63,66,160],"291":[26,33,94,105,51,58,63,66,160],"307":[26,33,94,105,51,58,63,66,160],"309":[26,33,94,105,51,58,63,66,160],"312":[26,33,94,105,51,58,63,66,160],"315":[26,33,94,105,51,58,63,66,160],"324":[26,33,94,105,51,58,63,66,160],"332":[26,33,94,105,51,58,63,66,160],"349":[58],"360":[58],"376":[58],"397":[58],"401":[58]},"packages/frontend/editor-ui/src/features/collaboration/projects/components/ProjectHeader.vue":{"46":"b:AAEAJAoAD8QEAABAAAMAAAAAAAABAAAA","48":"b:AAECJB4AD8RsAABAAAOEACAAQAADAAAA","50":"b:AAEAJAoAD8QEAABAAAMAAAAAAAABAAAA","70":"b:AAEAJAoAD8QEAABAAAMAAAAAAAABAAAA","82":"b:AAECJB4AD8RsAABAAAOEACAAQAADAAAA","86":"b:AAECJB4AD8QMAABAAAOAAAAAAAABAAAA","90":"b:AAECJB4AD8RsAABAAAOEACAAQAADAAAA","107":"b:AAECJB4AD8QMAABAAAOAAAAAAAABAAAA","110":[114],"113":[114],"117":"b:AAACJBQADIAEAABAAAKAAAAAAAABAAAA","121":"b:AAECJB4AD8RsAABAAAOEACAAQAADAAAA","128":"b:AAECJBwAD8RsAABAAAOEACAAQAADAAAA","135":"b:AAECJB4AD8RsAABAAAOEACAAQAADAAAA","149":"b:AAECJB4AD8RsAABAAAOEACAAQAADAAAA","162":"b:AAAABBAAD8BsAABAAAMAACAAQAADAAAA","172":[17,29,8,34,35,50,51,58,119,63],"182":[36,94],"192":[114],"210":"b:AAECJBwAD8RsAABAAAOEACAAQAADAAAA","217":"b:AAECJBwAD8RsAABAAAOEACAAQAADAAAA","233":"b:AAECJBwAD8RsAABAAAOEACAAQAADAAAA","309":"b:AAECJB4AD8RsAABAAAOEACAAQAADAAAA","315":[17,29,8,34,104,62,119,63,66,160],"325":[104,62,63,66,160],"336":[17,29,8,34,104,62,119,63,66,160],"349":[104,62,63,66,160],"359":[17,29,8,34,119,63],"371":[104,49],"374":[36,94],"380":[114],"390":"b:AAECJB4AD8RsAABAAAOEACAAQAADAAAA","400":"b:AAECJB4AD8RsAABAAAOEACAAQAADAAAA","420":"b:AAEAJAoAD8QEAABAAAMAAAAAAAABAAAA","428":"b:AAEAJAoAD8QEAABAAAMAAAAAAAABAAAA","438":"b:AAECJB4AD8RsAABAAAOEACAAQAADAAAA","452":[105],"463":"b:AAECIBQAAsAEAABAAAGEAAAAAAABAAAA","480":"b:AAECJB4AD8RsAABAAAOEACAAQAADAAAA","483":"b:AAACJBQADIRsAABAAAKEACAAQAADAAAA","488":[105],"515":"b:AAECJBwAD8RsAABAAAOEACAAQAADAAAA","522":[94,104,49],"523":"b:AAECJBwAD8RsAABAAAOEACAAQAADAAAA","528":"b:AAECIBQAAMAEAABAAAGEAAAAAAABAAAA"},"packages/frontend/editor-ui/src/features/collaboration/projects/components/ProjectMembersActionsCell.vue":{"6":[26,33,94,105,51,58,63,66,160],"17":[26,33,94,105,51,58,63,66,160]},"packages/frontend/editor-ui/src/features/collaboration/projects/components/ProjectMembersTable.vue":{"11":[26,33,94,105,51,58,63,66,160],"37":[26,33,94,105,51,58,63,66,160],"44":[26,33,94,105,51,58,63,66,160],"58":[26,33,94,105,51,58,63,66,160],"64":[26,33,94,105,51,58,63,66,160],"67":[33],"71":[26,33,94,105,51,58,63,66,160],"73":[33,105],"74":[26,33,94,105,51,58,63,66,160],"88":[33,105],"90":[26,33,94,105,51,58,63,66,160],"95":[26,33,94,105,51,58,63,66,160],"104":[26,33,94,105,51,58,63,66,160],"107":[26,33,94,105,51,58,63,66,160]},"packages/frontend/editor-ui/src/features/collaboration/projects/components/ProjectRoleUpgradeDialog.vue":{"14":[26,33,94,105,51,58,63,66,160],"22":[26,33,94,105,51,58,63,66,160]},"packages/frontend/editor-ui/src/features/collaboration/projects/components/ProjectTabs.vue":{"23":"b:AAECJB4AD8RsAABAAAOEACAAQAADAAAA","37":"b:AAECJB4AD8RsAABAAAOEACAAQAADAAAA","39":"b:AAECJB4AD8RsAABAAAOEACAAQAADAAAA","45":"b:AAECJB4AD8QMAABAAAOAAAAAAAABAAAA","47":"b:AAECJB4AD8RsAABAAAOEACAAQAADAAAA","90":"b:AAECJB4AD8RsAABAAAOEACAAQAADAAAA","103":"b:AAECJB4AD8RsAABAAAOEACAAQAADAAAA","144":"b:AAECJB4AD8RsAABAAAOEACAAQAADAAAA","145":"b:AAECJB4AD8RsAABAAAOEACAAQAADAAAA","161":"b:AAECJB4AD8RsAABAAAOEACAAQAADAAAA"},"packages/frontend/editor-ui/src/features/collaboration/projects/views/ProjectSettings.vue":{"63":[26,33,94,105,51,58,63,66,160],"78":[26,33,94,105,51,58,63,66,160],"118":[33,94,105,51,58,63],"122":[26,33,94,105,51,58,63,66,160],"123":[33,94,105,51,58,63],"124":[33,94,105,51,58,63],"130":[33,105],"131":[33,105],"134":[26,33,94,105,51,58,63,66,160],"142":[33,105],"148":[33,105],"168":[33,105],"191":[33],"196":[33],"227":[26,94,105,51,63,66,160],"276":[26,33,94,105,51,58,63,66,160],"288":[105],"293":[26,94,105,51,63,66,160],"316":[26,94,105,51,63,66,160],"317":[26,94,105,51,63,66,160],"324":[26,94,105,51,63,66,160],"355":[26,94,105,51,63,66,160],"374":[26,94,105,51,63,66,160],"421":[26,33,94,105,51,58,63,66,160],"428":[51],"444":[26,33,94,105,51,58,63,66,160],"446":[26,33,94,105,51,58,63,66,160],"463":[26,33,94,105,51,58,63,66,160],"464":[26,33,94,105,51,58,63,66,160],"480":[26,33,94,105,51,58,63,66,160],"485":[26,33,94,105,51,58,63,66,160],"499":[26,33,94,105,51,58,63,66,160],"517":[33,105],"521":[33,94,105,51,58,63],"561":[26,33,94,105,51,58,63,66,160],"566":[26,33,94,105,51,58,63,66,160],"568":[26,33,94,105,51,58,63,66,160],"584":[26,33,94,105,51,58,63,66,160],"594":[26,33,94,105,51,58,63,66,160],"601":[26,33,94,105,51,58,63,66,160],"629":[26,33,94,105,51,58,63,66,160],"649":[26,33,94,105,51,58,63,66,160],"678":[26,33,94,105,51,58,63,66,160],"740":[26,33,94,105,51,58,63,66,160]},"packages/frontend/editor-ui/src/features/core/folders/composables/useFolders.ts":{"12":[48],"16":[48],"20":"b:AAAADBAAD8BsAINBAAMAACAAQAADAAAA","25":[48,104,49],"29":[104,49],"63":[48],"80":[48],"86":[48,104,49],"101":[94,48,104,49,50,51,66,160],"109":[48],"130":[48,104,49]},"packages/frontend/editor-ui/src/features/execution/insights/insights.api.ts":{"16":"b:AAACZBQADIRkgINBAAKEACAAQAADAAAA","34":"b:AAACZBQADIRkgINBAAKEACAAQAADAAAA"},"packages/frontend/editor-ui/src/features/execution/insights/insights.constants.ts":{"21":"b:AAACZBQADIRkAINBAAKEACAAQAADAAAA","22":"b:AAACZBQADIRkAINBAAKEACAAQAADAAAA","23":"b:AAACZBQADIRkAINBAAKEACAAQAADAAAA","24":"b:AAACZBQADIRkAINBAAKEACAAQAADAAAA","25":"b:AAACZBQADIRkAINBAAKEACAAQAADAAAA"},"packages/frontend/editor-ui/src/features/execution/insights/insights.store.ts":{"12":"b:AAECbBwAD8RsgINBAAOkACAAQAADAAAA","17":"b:AAACbBQADIRkgINBAAKkACAAQAADAAAA","21":"b:AAACZBQADIRkgINBAAKEACAAQAADAAAA","25":"b:AAACbBQADIRkgINBAAKkACAAQAADAAAA","30":"b:AAACZBQADIRkgINBAAKEACAAQAADAAAA"},"packages/frontend/editor-ui/src/features/execution/insights/insights.utils.ts":{"17":"b:AAACZBQADIRkAINBAAKEACAAQAADAAAA","19":"b:AAACZBQADIRkAINBAAKEACAAQAADAAAA","20":"b:AAACZBQADIRkAINBAAKEACAAQAADAAAA","23":"b:AAACZBQADIRkAINBAAKEACAAQAADAAAA","24":"b:AAACZBQADIRkAINBAAKEACAAQAADAAAA","52":"b:AAACZBQADIRkAINBAAKEACAAQAADAAAA","54":"b:AAACZBQADIRkAINBAAKEACAAQAADAAAA","79":"b:AAACIBQADIRkAABAAAKEACAAQAADAAAA","96":"b:AAACIBQADIRkAABAAAKEACAAQAADAAAA","117":"b:AAACIBQADIRkAABAAAKEACAAQAADAAAA"},"packages/frontend/editor-ui/src/features/integrations/secretsProviders.ee/composables/useSecretsProvidersList.ee.ts":{"21":[58],"22":[58],"24":[26,33,94,105,51,58,63,66,160],"35":[26,33,94,105,51,58,63,66,160],"64":[58],"68":[58],"72":[26,33,94,105,51,58,63,66,160],"77":[58]},"packages/frontend/editor-ui/src/features/roles/project/projectRoleScopes.ts":{"34":[26,33,94,105,51,58,117,63,66,160],"36":[26,33,94,105,51,58,117,63,66,160]},"packages/frontend/editor-ui/src/features/workflows/composables/useWorkflowsEmptyState.ts":{"19":"b:AAAADBAADABkAANAAAIAACAAQAADAAAA","20":"b:AAAADBAAD8BsAANAAAMAACAAQAADAAAA","22":"b:AAAADBAAD8BsAANAAAMAACAAQAADAAAA","28":[26,27,51,80,81,66],"32":[26,27,51,80,81,66],"40":[26,27,51,80,81,66],"42":[80]},"packages/frontend/editor-ui/src/features/workflows/readyToRun/components/ReadyToRunButton.vue":{"13":"b:AAECJBwAD8RsAABAAAOEACAAQAADAAAA","25":"b:AAECJBwAD8RsAABAAAOEACAAQAADAAAA","31":"b:AAECJBwAD8RsAABAAAOEACAAQAADAAAA","53":"b:AAECJBwAD8RsAABAAAOEACAAQAADAAAA"},"packages/@n8n/ai-utilities/src/node-catalog/discriminator-utils.ts":{"44":[39,41,172,164],"55":[39,41,172,164],"70":[39,41,172,164],"81":[39,41,172,164],"117":[39,41,172,164],"128":[39,41,172,164],"139":[39,41,172,164],"151":[39,41,172,164],"161":[39,41,172,164],"178":[39,41,172,164]},"packages/@n8n/ai-utilities/src/node-catalog/dynamic-node-parameters.ts":{"47":[39,41,172,164],"51":[39,41,172,164],"55":[39,41,172,164],"59":[39,41,172,164],"63":[39,41,172,164],"73":[39,41,172,164],"107":[39,41,172,164],"114":[39,41,172,164],"119":[39,41,172,164],"151":[39,41,172,164],"183":[39,41,172,164],"187":[39,41,172,164],"188":[39,41,172,164],"195":[39,41,172,164],"199":[39,41,172,164],"214":[39,41,172,164],"226":[39,41,172,164],"230":[39,41,172,164],"237":[39,41,172,164],"241":[39,41,172,164],"251":[39,41,172,164],"260":[39,41,172,164],"274":[39,41,172,164]},"packages/@n8n/ai-utilities/src/node-catalog/get.ts":{"17":[39,41,172,164],"44":[39,41,172,164],"57":[39,41,172,164],"59":[39,41,172,164],"73":[39,41,172,164],"97":[39,41,172,164],"110":[39,41,172,164],"120":[39,41,172,164],"157":[39,41,172,164],"186":[39,41,172,164],"223":[39,41,172,164],"248":[39,41,172,164],"258":[39,41,172,164],"261":[39,41,172,164],"262":[39,41,172,164],"311":[39,41,172,164],"312":[39,41,172,164],"329":[39,41,172,164],"339":[39,41,172,164],"341":[39,41,172,164],"346":[39,41,172,164],"389":[39,41,172,164],"489":[39,41,172,164],"503":[39,41,172,164],"531":[39,41,172,164],"601":[39,41,172,164]},"packages/@n8n/ai-utilities/src/node-catalog/lean-node-type.ts":{"62":[39,41,172,164],"66":[39,41,172,164],"88":[39,41,172,164]},"packages/@n8n/ai-utilities/src/node-catalog/node-type-parser.ts":{"39":[39,41,172,164],"48":[39,41,172,164],"64":[39,41,172,164],"80":[39,41,172,164],"87":[39,41,172,164],"109":[39,41,172,164],"118":[39,41,172,164],"126":[39,41,172,164],"145":[39,41,172,164]},"packages/@n8n/ai-utilities/src/node-catalog/resource-operation-extractor.ts":{"56":[39,41,172,164],"82":[39,41,172,164],"87":[39,41,172,164],"98":[39,41,172,164],"122":[39,41,172,164],"127":[39,41,172,164],"152":[39,41,172,164],"171":[39,41,172,164],"198":[39,41,172,164],"216":[39,41,172,164],"250":[39,41,172,164],"288":[39,41,172,164],"295":[39,41,172,164],"305":[39,41,172,164]},"packages/@n8n/ai-utilities/src/node-catalog/search-engine.ts":{"20":[39,41,172,164],"27":[39,41,172,164],"60":[39,41,172,164],"71":[39,41,172,164],"94":[39,41,172,164],"98":[39,41,172,164],"103":[39,41,172,164],"106":[39,41,172,164],"113":[39,41,172,164],"115":[39,41,172,164],"144":[39,41,172,164],"145":[39,41,172,164],"161":[39,41,172,164],"167":[39,41,172,164],"196":[39,41,172,164],"217":[39,41,172,164],"223":[39,41,172,164],"230":[39,41,172,164],"234":[39,41,172,164],"242":[39,41,172,164],"246":[39,41,172,164],"263":[39,41,172,164],"269":[39,41,172,164],"275":[39,41,172,164],"305":[39,41,172,164],"312":[39,41,172,164],"316":[39,41,172,164],"323":[39,41,172,164],"325":[39,41,172,164],"344":[39,41,172,164],"350":[39,41,172,164],"352":[39,41,172,164],"378":[39,41,172,164],"426":[39,41,172,164],"439":[39,41,172,164],"453":[39,41,172,164],"480":[39,41,172,164],"481":[39,41,172,164],"490":[39,41,172,164],"516":[39,41,172,164],"524":[39,41,172,164],"526":[39,41,172,164]},"packages/@n8n/ai-utilities/src/node-catalog/search.ts":{"48":[39,41,172,164],"62":[39,41,172,164],"71":[39,41,172,164],"77":[39,41,172,164],"84":[39,41,172,164],"91":[39,41,172,164],"105":[39,41,172,164],"181":[39,41,172,164],"227":[39,41,172,164],"241":[39,41,172,164],"242":[39,41,172,164],"247":[39,41,172,164],"248":[39,41,172,164],"262":[39,41,172,164],"263":[39,41,172,164],"281":[39,41,172,164],"317":[39,41,172,164],"336":[39,41,172,164],"338":[39,41,172,164],"355":[39,41,172,164],"379":[39,41,172,164],"431":[39,41,172,164],"446":[39,41,172,164],"533":[39,41,172,164],"539":[39,41,172,164],"545":[39,41,172,164],"547":[39,41,172,164],"548":[39,41,172,164]},"packages/@n8n/ai-workflow-builder.ee/src/code-builder/index.ts":{"14":[172,164],"21":[172,164],"22":[172,164],"24":[172,164]},"packages/@n8n/ai-workflow-builder.ee/src/index.ts":{"7":[20,172,164],"43":[20,172,164]},"packages/@n8n/backend-common/src/modules/module-registry.ts":{"17":[3,4,5,9,38,172,164],"36":[3,4,5,9,38,172,164],"79":[3,4,5,9,38,172,164],"82":[3,4,5,9,38,172,164],"88":[3,4,5,9,38,172,164],"99":[3,4,5,9,38,172,164],"157":[3,4,5,9,38,172,164],"195":[3,4,5,9,38,172,164],"214":[3,4,5,9,38,172,164],"230":[3,4,5,9,38,172,164],"234":[3,4,5,9,38,172,164]},"packages/@n8n/decorators/src/module/module-metadata.ts":{"17":[3,4,5,9,38,172,164],"20":[3,4,5,9,38,172,164],"24":[3,4,5,9,38,172,164],"28":[3,4,5,9,38,172,164],"32":[3,4,5,9,38,172,164],"33":[3,4,5,9,38,172,164]},"packages/cli/src/errors/response-errors/auth.error.ts":{"4":[176,172,81]},"packages/cli/src/modules/data-table/data-table-proxy.service.ts":{"46":[172,164],"52":[172,164],"62":[172,164],"71":[172,164],"77":[172,164],"82":[172,164],"93":[172,164],"121":[172,164],"128":[172,164],"131":[172,164],"133":[172,164],"137":[172,164],"142":[172,164],"149":[172,164],"155":[172,164],"165":[172,164],"171":[172,164],"177":[172,164],"189":[172,164],"196":[172,164],"206":[172,164],"217":[172,164],"229":[172,164],"245":[172,164],"253":[172,164],"256":[172,164],"260":[172,164],"268":[172,164],"272":[172,164],"278":[172,164],"286":[172,164],"290":[172,164],"295":[172,164],"299":[172,164],"303":[172,164],"307":[172,164],"312":[172,164],"316":[172,164],"323":[172,164],"333":[172,164],"343":[172,164],"353":[172,164]},"packages/cli/src/modules/mcp/mcp-protected-resource.ts":{"48":[9,172,164],"59":[9,172,164],"64":[9,172,164],"67":[9,172,164],"74":[9,172,164],"90":[9,172,164],"101":[9,172,164],"106":[9,172,164],"112":[9,172,164],"116":[9,172,164]},"packages/cli/src/modules/mcp/mcp-scopes.ts":{"83":[172,164],"92":[172,164]},"packages/cli/src/modules/mcp/mcp-server-middleware.service.ts":{"27":[172,164],"39":[172,164],"66":[172,164],"67":[172,164],"113":[172,164],"126":[172,164],"138":[172,164]},"packages/cli/src/modules/mcp/mcp.controller.ts":{"26":[172,164],"32":[172,164],"42":[172,164],"69":[172,164],"86":[172,164],"119":[172,164],"198":[172,164],"217":[172,164],"225":[172,164]},"packages/cli/src/modules/mcp/mcp.service.ts":{"130":[172,164],"170":[172,164],"187":[172,164],"201":[172,164],"250":[172,164],"258":[172,164],"285":[172,164],"465":[172,164],"534":[172,164],"622":[172,164],"648":[172,164],"656":[172,164],"671":[172,164],"685":[172,164],"696":[172,164],"719":[172,164],"734":[172,164]},"packages/cli/src/modules/mcp/mcp.settings.controller.ts":{"20":[9,172,164],"31":[9,172,164],"53":[9,172,164],"59":[9,172,164],"65":[9,172,164],"72":[9,172,164],"82":[9,172,164],"106":[9,172,164]},"packages/cli/src/modules/mcp/mcp.settings.service.ts":{"48":[9,172,164],"58":[9,172,164],"74":[9,172,164],"83":[9,172,164],"99":[9,172,164],"108":[9,172,164],"166":[9,172,164],"195":[9,172,164],"205":[9,172,164],"231":[9,172,164],"255":[9,172,164],"260":[9,172,164],"276":[9,172,164],"279":[9,172,164],"299":[9,172,164],"319":[9,172,164]},"packages/cli/src/modules/mcp/mcp.typeguards.ts":{"22":[172,164],"45":[172,164],"56":[172,164],"74":[172,164]},"packages/cli/src/modules/mcp/mcp.utils.ts":{"12":[172,164],"25":[172,164],"42":[172,164],"64":[172,164],"72":[172,164]},"packages/cli/src/modules/mcp/tools/data-table/add-data-table-column.tool.ts":{"29":[172,164],"47":[172,164]},"packages/cli/src/modules/mcp/tools/data-table/add-data-table-rows.tool.ts":{"30":[172,164],"49":[172,164]},"packages/cli/src/modules/mcp/tools/data-table/create-data-table.tool.ts":{"35":[172,164],"54":[172,164],"72":[172,164]},"packages/cli/src/modules/mcp/tools/data-table/delete-data-table-column.tool.ts":{"19":[172,164],"38":[172,164]},"packages/cli/src/modules/mcp/tools/data-table/index.ts":{"1":[172,164],"2":[172,164],"3":[172,164],"5":[172,164],"6":[172,164]},"packages/cli/src/modules/mcp/tools/data-table/rename-data-table-column.tool.ts":{"28":[172,164],"46":[172,164]},"packages/cli/src/modules/mcp/tools/data-table/rename-data-table.tool.ts":{"19":[172,164],"37":[172,164]},"packages/cli/src/modules/mcp/tools/data-table/search-data-tables.tool.ts":{"28":[172,164],"47":[172,164],"73":[172,164],"79":[172,164]},"packages/cli/src/modules/mcp/tools/execute-workflow.tool.ts":{"96":[172,164],"119":[172,164],"195":[172,164],"232":[172,164],"275":[172,164],"353":[172,164],"371":[172,164],"451":[172,164],"462":[172,164],"486":[172,164]},"packages/cli/src/modules/mcp/tools/get-execution.tool.ts":{"65":[172,164],"85":[172,164],"207":[172,164],"240":[172,164],"244":[172,164]},"packages/cli/src/modules/mcp/tools/get-workflow-details.tool.ts":{"32":[172,164],"58":[172,164],"109":[172,164],"141":[172,164],"153":[172,164],"179":[172,164]},"packages/cli/src/modules/mcp/tools/get-workflow-history.tool.ts":{"61":[172,164],"81":[172,164],"136":[172,164],"147":[172,164]},"packages/cli/src/modules/mcp/tools/get-workflow-version.tool.ts":{"75":[172,164],"95":[172,164],"157":[172,164],"168":[172,164]},"packages/cli/src/modules/mcp/tools/list-credentials.tool.ts":{"112":[172,164],"132":[172,164],"189":[172,164],"219":[172,164]},"packages/cli/src/modules/mcp/tools/list-n8n-connect-services.tool.ts":{"51":[172,164],"70":[172,164]},"packages/cli/src/modules/mcp/tools/list-tags.tool.ts":{"60":[172,164],"78":[172,164],"113":[172,164],"120":[172,164]},"packages/cli/src/modules/mcp/tools/prepare-workflow-pin-data.tool.ts":{"57":[172,164],"79":[172,164],"137":[172,164],"147":[172,164],"150":[172,164],"223":[172,164]},"packages/cli/src/modules/mcp/tools/publish-workflow.tool.ts":{"40":[172,164],"61":[172,164],"78":[172,164]},"packages/cli/src/modules/mcp/tools/schemas.ts":{"18":[172,164],"23":[172,164],"24":[172,164],"27":[172,164],"31":[172,164],"56":[172,164],"102":[172,164],"106":[172,164],"107":[172,164],"110":[172,164]},"packages/cli/src/modules/mcp/tools/search-executions.tool.ts":{"64":[172,164],"84":[172,164],"134":[172,164]},"packages/cli/src/modules/mcp/tools/search-folders.tool.ts":{"35":[172,164],"55":[172,164],"93":[172,164]},"packages/cli/src/modules/mcp/tools/search-projects.tool.ts":{"62":[172,164],"82":[172,164],"114":[172,164],"115":[172,164],"119":[172,164],"127":[172,164],"135":[172,164],"142":[172,164]},"packages/cli/src/modules/mcp/tools/search-workflows.tool.ts":{"80":[172,164],"100":[172,164],"155":[172,164],"161":[172,164],"194":[172,164]},"packages/cli/src/modules/mcp/tools/test-workflow.tool.ts":{"71":[172,164],"94":[172,164],"170":[172,164],"204":[172,164],"205":[172,164],"280":[172,164]},"packages/cli/src/modules/mcp/tools/unpublish-workflow.tool.ts":{"32":[172,164],"53":[172,164],"69":[172,164]},"packages/cli/src/modules/mcp/tools/workflow-builder/create-workflow-from-code.tool.ts":{"50":[172,164],"174":[172,164],"200":[172,164],"267":[172,164],"350":[172,164],"384":[172,164]},"packages/cli/src/modules/mcp/tools/workflow-builder/delete-workflow.tool.ts":{"27":[172,164],"47":[172,164],"65":[172,164]},"packages/cli/src/modules/mcp/tools/workflow-builder/explore-node-resources.tool.ts":{"80":[172,164],"101":[172,164]},"packages/cli/src/modules/mcp/tools/workflow-builder/get-workflow-best-practices.tool.ts":{"65":[172,164],"66":[172,164],"72":[172,164],"79":[172,164],"98":[172,164],"123":[172,164],"142":[172,164]},"packages/cli/src/modules/mcp/tools/workflow-builder/get-workflow-node-types.tool.ts":{"60":[172,164],"80":[172,164]},"packages/cli/src/modules/mcp/tools/workflow-builder/get-workflow-sdk-reference.tool.ts":{"26":[172,164],"51":[172,164],"70":[172,164]},"packages/cli/src/modules/mcp/tools/workflow-builder/mcp-instructions.ts":{"42":[172,164]},"packages/cli/src/modules/mcp/tools/workflow-builder/restore-workflow-version.tool.ts":{"49":[172,164],"71":[172,164],"111":[172,164]},"packages/cli/src/modules/mcp/tools/workflow-builder/search-workflow-nodes.tool.ts":{"52":[172,164],"72":[172,164]},"packages/cli/src/modules/mcp/tools/workflow-builder/update-workflow.tool.ts":{"78":[172,164],"128":[172,164],"221":[172,164],"226":[172,164],"240":[172,164],"242":[172,164],"269":[172,164],"343":[172,164],"406":[172,164],"450":[172,164],"455":[172,164],"456":[172,164],"471":[172,164],"495":[172,164],"535":[172,164],"556":[172,164],"568":[172,164],"578":[172,164],"621":[172,164],"646":[172,164],"667":[172,164],"676":[172,164],"686":[172,164],"690":[172,164],"753":[172,164],"783":[172,164],"786":[172,164],"788":[172,164],"789":[172,164],"795":[172,164],"818":[172,164],"829":[172,164]},"packages/cli/src/modules/mcp/tools/workflow-builder/validate-node.tool.ts":{"80":[172,164],"98":[172,164],"108":[172,164],"125":[172,164],"126":[172,164],"127":[172,164]},"packages/cli/src/modules/mcp/tools/workflow-builder/validate-workflow-code.tool.ts":{"80":[172,164],"88":[172,164],"89":[172,164],"103":[172,164],"123":[172,164],"144":[172,164],"159":[172,164],"174":[172,164],"183":[172,164]},"packages/cli/src/modules/oauth-server/database/repositories/oauth-access-token.repository.ts":{"15":[9,172],"19":[9,172]},"packages/cli/src/modules/oauth-server/database/repositories/oauth-refresh-token.repository.ts":{"17":[9,172],"21":[9,172],"30":[9,172],"44":[9,172]},"packages/cli/src/modules/oauth-server/oauth-authorization-code.service.ts":{"17":[9,172],"23":[9,172],"55":[9,172],"83":[9,172],"111":[9,172],"138":[9,172],"151":[9,172],"157":[9,172]},"packages/cli/src/modules/oauth-server/oauth-consent.controller.ts":{"14":[9,172],"21":[9,172],"66":[9,172],"107":[9,172],"114":[9,172],"121":[9,172]},"packages/cli/src/modules/oauth-server/oauth-consent.service.ts":{"42":[9,172],"56":[9,172],"122":[9,172],"124":[9,172],"132":[9,172],"142":[9,172],"150":[9,172],"245":[9,172],"263":[9,172]},"packages/cli/src/modules/oauth-server/oauth-server.service.ts":{"72":[9,172],"78":[9,172],"79":[9,172],"95":[9,172],"107":[9,172],"109":[9,172],"136":[9,172],"159":[9,172],"164":[9,172],"182":[9,172],"195":[9,172],"230":[9,172],"246":[9,172],"263":[9,172],"267":[9,172],"299":[9,172],"332":[9,172],"342":[9,172],"410":[9,172],"424":[9,172],"431":[9,172],"443":[9,172],"451":[9,172],"486":[9,172],"511":[9,172],"512":[9,172],"530":[9,172],"568":[9,172],"580":[9,172],"634":[9,172],"649":[9,172]},"packages/cli/src/modules/oauth-server/oauth-session.service.ts":{"26":[9,172],"31":[9,172],"47":[9,172],"54":[9,172],"61":[9,172]},"packages/cli/src/modules/oauth-server/oauth-token.service.ts":{"37":[9,172],"47":[9,172],"51":[9,172],"87":[9,172],"94":[9,172],"109":[9,172],"114":[9,172],"179":[9,172],"186":[9,172],"226":[9,172],"239":[9,172],"301":[9,172],"308":[9,172],"323":[9,172],"347":[9,172],"359":[9,172],"373":[9,172],"384":[9,172],"403":[9,172]},"packages/cli/src/modules/oauth-server/oauth.controller.ts":{"45":[9,172],"72":[9,172],"74":[9,172],"89":[9,172],"137":[9,172],"143":[9,172],"160":[9,172],"179":[9,172],"210":[9,172],"225":[9,172],"247":[9,172],"274":[9,172],"286":[9,172]},"packages/cli/src/modules/oauth-server/oauth.errors.ts":{"8":[172],"20":[172],"28":[172],"35":[172]},"packages/cli/src/modules/oauth-server/oauth.helpers.ts":{"13":[9,172],"34":[9,172],"58":[9,172]},"packages/cli/src/modules/oauth-server/protected-resource-resolvers/utils.ts":{"8":[9,172],"15":[9,172],"35":[9,172]},"packages/cli/src/modules/oauth-server/protected-resource-resolvers/workflow-mcp-test-trigger-resource.resolver.ts":{"20":[172],"31":[172],"40":[172],"65":[172],"83":[172],"84":[172],"87":[172]},"packages/cli/src/modules/oauth-server/protected-resource-resolvers/workflow-mcp-trigger-resource.resolver.ts":{"20":[9,172],"32":[9,172],"41":[9,172],"84":[9,172],"102":[9,172],"103":[9,172],"106":[9,172]},"packages/cli/src/services/ai-gateway.service.ts":{"55":[172,164],"69":[172,164],"102":[172,164],"133":[172,164],"182":[172,164],"190":[172,164],"204":[172,164],"233":[172,164],"252":[172,164],"272":[172,164],"287":[172,164],"293":[172,164],"305":[172,164],"315":[172,164],"354":[172,164],"370":[172,164],"390":[172,164]},"packages/cli/src/services/ai-workflow-builder.service.ts":{"47":[20,39,41,10,172,164],"65":[20,39,41,10,172,164],"73":[20,39,41,10,172,164],"80":[20,39,41,10,172,164],"88":[20,39,41,10,172,164],"104":[20,39,41,10,172,164],"110":[20,39,41,10,172,164],"124":[20,39,41,10,172,164],"129":[20,39,41,10,172,164],"130":[20,39,41,10,172,164],"191":[20,39,41,10,172,164],"208":[20,39,41,10,172,164],"213":[20,39,41,10,172,164],"219":[20,39,41,10,172,164],"224":[20,39,41,10,172,164],"229":[20,39,41,10,172,164]},"packages/cli/src/services/oauth-token-verifier-proxy.service.ts":{"56":[9,172],"59":[9,172],"63":[9,172]},"packages/cli/src/services/protected-resource.registry.ts":{"112":[9,172],"114":[9,172],"128":[9,172],"130":[9,172],"134":[9,172],"138":[9,172],"143":[9,172],"146":[9,172],"162":[9,172],"194":[9,172],"200":[9,172],"205":[9,172],"221":[9,172],"230":[9,172]},"packages/nodes-base/nodes/Form/Form.node.ts":{"359":[17,19,23,8,39,42,101,47,54,135,68],"438":[17,19,23,8,39,42,101,47,54,135,68],"446":[17,19,23,8,39,42,101,47,54,135,68],"456":[17,19,23,8,39,42,101,47,54,135,68]},"packages/nodes-base/nodes/Form/utils/formCompletionUtils.ts":{"25":[17,19,23,8,39,42,101,47,54,135,68],"34":[17,19,23,8,39,42,101,47,54,135,68],"37":[17,19,23,8,39,42,101,47,54,135,68],"43":[17,19,23,8,39,42,101,47,54,135,68],"50":[17,19,23,8,39,42,101,47,54,135,68],"53":[17,19,23,8,39,42,101,47,54,135,68],"74":[17,19,23,8,39,42,101,47,54,135,68]},"packages/nodes-base/nodes/Form/utils/formNodeUtils.ts":{"21":[17,19,23,8,39,42,101,47,54,135,68],"93":[17,19,23,8,39,42,101,47,54,135,68],"96":[17,19,23,8,39,42,101,47,54,135,68]},"packages/nodes-base/nodes/Slack/Slack.node.ts":{"8":[17,19,8,39,47,54]},"packages/nodes-base/nodes/Slack/SlackTriggerHelpers.ts":{"8":[17,19,8,39,47,54],"22":[17,19,8,39,47,54],"36":[17,19,8,39,47,54],"84":[17,19,8,39,47,54],"99":[17,19,8,39,47,54],"119":[17,19,8,39,47,54],"123":[17,19,8,39,47,54]},"packages/nodes-base/nodes/Slack/V1/GenericFunctions.ts":{"16":[17,19,8,39,47,54],"19":[17,19,8,39,47,54],"20":[17,19,8,39,47,54],"26":[17,19,8,39,47,54],"27":[17,19,8,39,47,54],"33":[17,19,8,39,47,54],"104":[17,19,8,39,47,54],"140":[17,19,8,39,47,54]},"packages/nodes-base/nodes/Slack/V1/SlackV1.node.ts":{"76":[17,19,8,39,47,54],"190":[17,19,8,39,47,54],"202":[17,19,8,39,47,54],"216":[17,19,8,39,47,54],"236":[17,19,8,39,47,54],"250":[17,19,8,39,47,54],"268":[17,19,8,39,47,54],"476":[17,19,8,39,47,54],"486":[17,19,8,39,47,54]},"packages/nodes-base/nodes/Slack/V2/GenericFunctions.ts":{"40":[17,19,8,39,47,54],"47":[17,19,8,39,47,54],"50":[17,19,8,39,47,54],"51":[17,19,8,39,47,54],"57":[17,19,8,39,47,54],"58":[17,19,8,39,47,54],"64":[17,19,8,39,47,54],"160":[17,19,8,39,47,54],"173":[17,19,8,39,47,54],"209":[17,19,8,39,47,54],"271":[17,19,8,39,47,54],"303":[17,19,8,39,47,54],"378":[17,19,8,39,47,54],"388":[17,19,8,39,47,54],"415":[17,19,8,39,47,54],"431":[17,19,8,39,47,54],"475":[17,19,8,39,47,54]},"packages/nodes-base/nodes/Slack/V2/SlackHitlWebhook.ts":{"23":[17,19,8,39,47,54],"60":[17,19,8,39,47,54],"80":[17,19,8,39,47,54],"91":[17,19,8,39,47,54],"141":[17,19,8,39,47,54],"213":[17,19,8,39,47,54]},"packages/nodes-base/nodes/Slack/V2/SlackV2.node.ts":{"64":[17,19,8,39,47,54],"185":[17,19,8,39,47,54],"202":[17,19,8,39,47,54],"222":[17,19,8,39,47,54],"227":[17,19,8,39,47,54],"232":[17,19,8,39,47,54],"239":[17,19,8,39,47,54],"254":[17,19,8,39,47,54],"259":[17,19,8,39,47,54],"264":[17,19,8,39,47,54],"275":[17,19,8,39,47,54],"290":[17,19,8,39,47,54],"304":[17,19,8,39,47,54],"324":[17,19,8,39,47,54],"338":[17,19,8,39,47,54],"357":[17,19,8,39,47,54],"371":[17,19,8,39,47,54],"391":[17,19,8,39,47,54],"615":[17,19,8,39,47,54],"678":[17,19,8,39,47,54],"688":[17,19,8,39,47,54],"1586":[17,19,8,39,47,54],"1631":[17,19,8,39,47,54],"1659":[17,19,8,39,47,54]},"packages/nodes-base/utils/sendAndWait/configureWaitTillDate.util.ts":{"4":[17,19,23,8,39,42,101,47,54,135,68]},"packages/nodes-base/utils/sendAndWait/email-templates.ts":{"82":[17,19,8,39,42,47,54,135,68],"147":[17,19,8,39,42,47,54,135,68]},"packages/nodes-base/utils/sendAndWait/utils.ts":{"58":[17,19,8,39,42,47,54,135,68],"154":[17,19,8,39,42,47,54,135,68],"310":[17,19,8,39,42,47,54,135,68],"340":[17,19,8,39,42,47,54,135,68],"350":[17,19,8,39,42,47,54,135,68],"353":[17,19,8,39,42,47,54,135,68],"496":[17,19,8,39,42,47,54,135,68],"563":[17,19,8,39,42,47,54,135,68],"571":[17,19,8,39,42,47,54,135,68],"605":[17,19,8,39,42,47,54,135,68]},"packages/nodes-base/utils/webhook-signature-verification.ts":{"64":[17,19,8,39,47,54],"106":[17,19,8,39,47,54]},"packages/frontend/editor-ui/src/experiments/enhancedHitlSlack/slackHitlParameters.ts":{"13":[17,19,8,39,97,54],"14":[17,19,8,39,97,54]},"packages/frontend/editor-ui/src/features/ndv/parameters/components/ExpressionFunctionIcon.vue":{"8":"b:giUAAAAAYAQAAAAAABAAEICcGgIAAAAA"},"packages/cli/src/controllers/mfa.controller.ts":{"22":[81],"33":[81],"68":[81],"76":[81],"119":[81],"160":[81],"204":[81]},"packages/cli/src/mfa/mfa.service.ts":{"17":[81],"27":[81],"35":[81],"36":[81],"39":[81],"45":[81],"60":[81],"68":[81],"80":[81],"83":[81],"91":[81],"95":[81],"100":[81],"107":[81],"120":[81],"127":[81],"136":[81],"145":[81],"155":[81],"165":[81]},"packages/cli/src/mfa/totp.service.ts":{"6":[81],"10":[81],"26":[81],"38":[81]},"packages/nodes-base/nodes/EditImage/EditImage.node.ts":{"30":[96],"810":[96],"983":[96],"987":[96],"999":[96],"1014":[96],"1076":[96],"1113":[96],"1114":[96],"1244":[96],"1245":[96],"1267":[96],"1330":[96],"1331":[96],"1332":[96]},"packages/nodes-base/nodes/OpenWeatherMap/OpenWeatherMap.node.ts":{"195":[96]},"localhost-36003/assets/_plugin-vue_export-helper-Da88TEg1.js":{"2":[81]},"localhost-36003/assets/chunk-CC9Q-vWm.js":{"8":[81],"9":[81],"10":[81],"19":[81],"23":[81],"29":[81],"33":[81],"34":[81]},"packages/frontend/@n8n/design-system/src/components/N8nFormBox/FormBox.vue":{"34":[87,88,51,81],"41":[88,51,81],"42":[88,51,81],"43":[88,51,81],"45":[87,88,51,81],"51":[87,88,51,81],"80":[87,88,51,81]},"packages/frontend/@n8n/design-system/src/components/N8nLogo/Logo.vue":{"8":"b:OAAACAIADACkAIJpAAAAAAAAAAAAAAAA","24":"b:OAAACAIADACkAIJpAAAAAAAAAAAAAAAA","30":"b:OAAACAIADACkAIJpAAAAAAAAAAAAAAAA","42":"b:OAAACAIADACkAIJpAAAAAAAAAAAAAAAA","57":"b:OAAACAIADACkAIJpAAAAAAAAAAAAAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nLogo/logo-icon.svg":{"11":"b:OAAACAIADACkAIJpAAAAAAAAAAAAAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nLogo/logo-text.svg":{"11":"b:OAAACAIADACkAIJpAAAAAAAAAAAAAAAA"},"packages/frontend/@n8n/design-system/src/types/menu.ts":{"56":[69,51,81,130]},"packages/frontend/editor-ui/src/app/utils/rbac/checks/isGuest.ts":{"4":[87,88,51,81]},"packages/frontend/editor-ui/src/app/utils/rbac/middleware/guest.ts":{"6":[87,88,51,81]},"packages/frontend/editor-ui/src/features/core/auth/components/MfaSetupModal.vue":{"25":[81],"56":[81],"60":[81],"67":[81],"76":[81],"89":[81],"104":[81],"132":[81],"151":[81],"154":[81],"173":[81],"177":[81],"182":[81],"183":[81],"186":[81],"202":[81],"211":[81],"230":[81],"234":[81],"235":[81],"239":[81],"240":[81],"243":[81],"245":[81],"262":[81]},"packages/frontend/editor-ui/src/features/core/auth/components/PromptMfaCodeModal.vue":{"12":[81],"31":[81],"48":[81],"52":[81],"54":[81],"67":[81],"78":[81]},"packages/frontend/editor-ui/src/features/core/auth/views/AuthView.vue":{"28":[88,51,81],"32":[88,51,81],"59":[87,88,51,81]},"packages/frontend/editor-ui/src/features/core/auth/views/MfaView.vue":{"63":[81],"86":[81],"107":[81],"116":[81],"117":[81],"129":[81],"155":[81],"164":[81],"189":[81],"194":[81],"204":[81],"225":[81]},"packages/frontend/editor-ui/src/features/core/auth/views/SigninView.vue":{"45":[87,88,51,81],"46":[87,88,51,81],"89":[81],"98":[88,51,81],"102":[88,51,81],"119":[88,51,81],"127":[88,51,81],"190":[81],"195":[81]},"packages/frontend/editor-ui/src/features/core/auth/views/SignoutView.vue":{"14":[51,81],"23":[51,81]},"packages/frontend/editor-ui/src/features/settings/sso/components/SSOLogin.vue":{"23":[87,88,51,81]},"packages/@n8n/api-types/src/schemas/node-version.schema.ts":{"7":[1,28,51,143,147,148]},"packages/@n8n/expression-runtime/src/evaluator/error-classification.ts":{"5":[143]},"packages/cli/src/controllers/dynamic-node-parameters.controller.ts":{"16":[1,28,51,143,147,148,152,153],"19":[1,28,51,143,147,148,152,153],"68":[1,28,51,143,147,148,152,153],"106":[1,28,51,143,147,148,152,153],"134":[1,28,51,143,147,148,152,153],"158":[1,28,51,143,147,148,152,153]},"packages/cli/src/credential-types.ts":{"8":"b:egkIMAykDIxQAAAAoBAAAACAAAAIAAAA","10":"b:egkIMAykDIxQAAAAoBAAAACAAAAIAAAA","15":"b:egkIMAykDIxQAAAAoBAAAACAAAAIAAAA","19":"b:egkIMAykDIxQAAAAoBAAAACAAAAIAAAA","26":"b:egkIMAykDIxQAAAAoBAAAACAAAAIAAAA"},"packages/cli/src/credentials-helper.ts":{"68":"b:ekkIMAykDOxQAAAQoBAAAICASAAIAgAA","71":"b:ekkIMAykDOxQAAAQoBAAAICASAAIAgAA","74":"b:ekkIMAykDOxQAAAQoBAAAICASAAIAgAA","86":"b:ekkIMAykDOxQAAAQoBAAAICASAAIAgAA","102":"b:ekkIMAykDOxQAAAQoBAAAICASAAIAgAA","133":"b:ekkIMAykDOxQAAAQoBAAAICASAAIAgAA","134":"b:ekkIMAykDOxQAAAQoBAAAICASAAIAgAA","160":"b:ekkIMAykDOxQAAAQoBAAAICASAAIAgAA","170":"b:ekkIMAykDOxQAAAQoBAAAICASAAIAgAA","221":"b:ekkIMAykDOxQAAAQoBAAAICASAAIAgAA","237":"b:ekkIMAykDOxQAAAQoBAAAICASAAIAgAA","266":"b:ekkIMAykDOxQAAAQoBAAAICASAAIAgAA","273":"b:ekkIMAykDOxQAAAQoBAAAICASAAIAgAA","289":"b:ekkIMAykDOxQAAAQoBAAAICASAAIAgAA","320":"b:ekkIMAykDOxQAAAQoBAAAICASAAIAgAA","339":"b:ekkIMAykDOxQAAAQoBAAAICASAAIAgAA","377":"b:ekkIMAykDOxQAAAQoBAAAICASAAIAgAA","408":"b:ekkIMAykDOxQAAAQoBAAAICASAAIAgAA","417":"b:ekkIMAykDOxQAAAQoBAAAICASAAIAgAA","452":"b:ekkIMAykDOxQAAAQoBAAAICASAAIAgAA","551":"b:ekkIMAykDOxQAAAQoBAAAICASAAIAgAA","660":"b:ekkIMAykDOxQAAAQoBAAAICASAAIAgAA","685":"b:ekkIMAykDOxQAAAQoBAAAICASAAIAgAA","739":"b:ekkIMAykDOxQAAAQoBAAAICASAAIAgAA"},"packages/cli/src/services/dynamic-node-parameters.service.ts":{"65":"b:OkAAEAAACAAAAAAAAAAAAACAGAMAAAAA","73":"b:OkAAEAAACAAAAAAAAAAAAACAGAMAAAAA","113":"b:OkAAEAAACAAAAAAAAAAAAACAGAMAAAAA","114":"b:OkAAEAAACAAAAAAAAAAAAACAGAMAAAAA","123":"b:OkAAEAAACAAAAAAAAAAAAACAGAMAAAAA","132":"b:OkAAEAAACAAAAAAAAAAAAACAGAMAAAAA","144":"b:OkAAEAAACAAAAAAAAAAAAACAGAMAAAAA","155":"b:OkAAEAAACAAAAAAAAAAAAACAGAMAAAAA","189":"b:OkAAEAAACAAAAAAAAAAAAACAGAMAAAAA","251":"b:OkAAEAAACAAAAAAAAAAAAACAGAMAAAAA","262":"b:OkAAEAAACAAAAAAAAAAAAACAGAMAAAAA","266":"b:OkAAEAAACAAAAAAAAAAAAACAGAMAAAAA","280":"b:OkAAEAAACAAAAAAAAAAAAACAGAMAAAAA","286":"b:OkAAEAAACAAAAAAAAAAAAACAGAMAAAAA","298":"b:OkAAEAAACAAAAAAAAAAAAACAGAMAAAAA","304":"b:OkAAEAAACAAAAAAAAAAAAACAGAMAAAAA","317":"b:OkAAEAAACAAAAAAAAAAAAACAGAMAAAAA","330":"b:OkAAEAAACAAAAAAAAAAAAACAGAMAAAAA","356":"b:OkAAEAAACAAAAAAAAAAAAACAGAMAAAAA","399":"b:OkAAEAAACAAAAAAAAAAAAACAGAMAAAAA","403":"b:OkAAEAAACAAAAAAAAAAAAACAGAMAAAAA","429":"b:OkAAEAAACAAAAAAAAAAAAACAGAMAAAAA","438":"b:OkAAEAAACAAAAAAAAAAAAACAGAMAAAAA","450":"b:OkAAEAAACAAAAAAAAAAAAACAGAMAAAAA","454":"b:OkAAEAAACAAAAAAAAAAAAACAGAMAAAAA"},"packages/core/src/execution-engine/node-execution-context/load-options-context.ts":{"21":[1,28,3,4,5,143,14,147,148],"36":[1,28,3,4,5,143,14,147,148],"40":[1,28,3,4,5,143,14,147,148],"69":[1,28,3,4,5,143,14,147,148]},"packages/core/src/execution-engine/node-execution-context/utils/request-helpers/authentication.ts":{"22":[28,169,47,143],"150":[28,169,47,143]},"packages/nodes-base/nodes/Airtable/Airtable.node.ts":{"8":[143]},"packages/nodes-base/nodes/Airtable/v1/AirtableV1.node.ts":{"558":[143],"565":[143],"833":[143],"834":[143],"838":[143],"839":[143]},"packages/nodes-base/nodes/Airtable/v1/GenericFunctions.ts":{"29":[143],"72":[143],"100":[143]},"packages/nodes-base/nodes/Airtable/v2/AirtableV2.node.ts":{"15":[143],"29":[143]},"packages/nodes-base/nodes/Airtable/v2/actions/base/getMany.operation.ts":{"88":[143],"112":[143]},"packages/nodes-base/nodes/Airtable/v2/actions/base/getSchema.operation.ts":{"30":[143]},"packages/nodes-base/nodes/Airtable/v2/actions/record/create.operation.ts":{"51":[143]},"packages/nodes-base/nodes/Airtable/v2/actions/record/deleteRecord.operation.ts":{"36":[143]},"packages/nodes-base/nodes/Airtable/v2/actions/record/get.operation.ts":{"60":[143],"85":[143]},"packages/nodes-base/nodes/Airtable/v2/actions/record/search.operation.ts":{"147":[143],"183":[143],"214":[143],"224":[143]},"packages/nodes-base/nodes/Airtable/v2/actions/record/update.operation.ts":{"57":[143]},"packages/nodes-base/nodes/Airtable/v2/actions/record/upsert.operation.ts":{"52":[143],"120":[143],"125":[143],"142":[143]},"packages/nodes-base/nodes/Airtable/v2/actions/router.ts":{"8":[143]},"packages/nodes-base/nodes/Airtable/v2/helpers/utils.ts":{"11":[143],"16":[143],"41":[143],"50":[143],"56":[143],"63":[143],"78":[143],"97":[143],"112":[143],"121":[143],"131":[143]},"packages/nodes-base/nodes/Airtable/v2/methods/listSearch.ts":{"11":[143],"44":[143],"54":[143],"91":[143],"101":[143],"117":[143],"145":[143]},"packages/nodes-base/nodes/Airtable/v2/methods/loadOptions.ts":{"6":[143],"19":[143],"42":[143],"57":[143],"62":[143],"65":[143],"80":[143]},"packages/nodes-base/nodes/Airtable/v2/methods/resourceMapping.ts":{"48":[143],"63":[143],"76":[143],"88":[143],"90":[143],"120":[143]},"packages/nodes-base/nodes/Airtable/v2/transport/index.ts":{"20":[143],"59":[143],"87":[143],"94":[143],"131":[143]},"packages/nodes-base/nodes/DebugHelper/DebugHelper.node.ts":{"255":[106,135,136,143,151],"346":[106,135,136,143,151]},"packages/nodes-base/nodes/DebugHelper/functions.ts":{"4":[106,135,136,143,151],"14":[106,135,136,143,151]},"packages/nodes-base/nodes/DebugHelper/randomData.ts":{"26":[106,135,136,143,151],"36":[106,135,136,143,151],"48":[106,135,136,143,151],"55":[106,135,136,143,151],"59":[106,135,136,143,151],"63":[106,135,136,143,151],"79":[106,135,136,143,151],"83":[106,135,136,143,151],"87":[106,135,136,143,151],"91":[106,135,136,143,151],"95":[106,135,136,143,151],"99":[106,135,136,143,151]},"packages/nodes-base/nodes/Function/Function.node.ts":{"67":[143,68],"79":[143,68],"80":[143,68],"101":[143,68],"102":[143,68],"115":[143,68],"215":[143,68]},"packages/nodes-base/nodes/Typeform/GenericFunctions.ts":{"40":[143],"77":[143],"113":[143]},"packages/nodes-base/nodes/Typeform/TypeformTrigger.node.ts":{"122":[143],"160":[143],"179":[143],"205":[143],"230":[143],"305":[143]},"packages/nodes-base/nodes/Typeform/TypeformTriggerHelpers.ts":{"18":[143],"24":[143],"36":[143]},"packages/workflow/src/errors/abstract/execution-base.error.ts":{"27":"b:UoEAEQQAAAAAAAAwAAAAAECAAAAAAIAA","49":"b:UoEAEQQAAAAAAAAwAAAAAECAAAAAAIAA"},"packages/workflow/src/errors/abstract/node.error.ts":{"40":"b:EoEAEQQAAAAAAAAwAAAAAECAAAAAAIAA","78":"b:EoEAEQQAAAAAAAAwAAAAAECAAAAAAIAA","98":"b:EoEAEQQAAAAAAAAwAAAAAECAAAAAAIAA","106":"b:EoEAEQQAAAAAAAAwAAAAAECAAAAAAIAA","138":"b:EoEAEQQAAAAAAAAwAAAAAECAAAAAAIAA","148":"b:EoEAEQQAAAAAAAAwAAAAAECAAAAAAIAA"},"packages/workflow/src/errors/expression.error.ts":{"32":[15,143],"56":[15,143]},"packages/workflow/src/errors/node-api.error.ts":{"124":[1,28,93,34,134,143],"277":[1,28,93,34,134,143],"285":[1,28,93,34,134,143],"301":[1,28,93,34,134,143]},"packages/workflow/src/errors/node-operation.error.ts":{"12":[15,1,24,28,4,92,93,8,143,147,183],"31":[15,1,24,28,4,92,93,8,143,147,183]},"packages/workflow/src/workflow-data-proxy-helpers.ts":{"3":[143]},"localhost-39695/assets/verified-L2wNNiNu.js":{"8":[143,14]},"packages/frontend/@n8n/design-system/src/components/N8nIcon/custom/empty-output.svg":{"9":[143]},"packages/frontend/@n8n/design-system/src/components/N8nIcon/nodes/respond-to-webhook.svg":{"11":[28,89,103,143,146]},"packages/frontend/editor-ui/src/features/ndv/panel/components/TriggerPanel.vue":{"70":"b:AAGAEAAAAAAAAAACoQAAAICCLAABAAAA","74":"b:AAGAEAAAAAAAAAACoQAAAICCLAABAAAA","82":"b:AACAEAAAAAAAAAACoAAAAICCBAABAAAA","90":"b:AACAEAAAAAAAAAACoAAAAICCBAABAAAA","109":"b:AAGAEAAAAAAAAAACoQAAAICCLAABAAAA","115":[28,89,103,137,143,146,160],"123":[23,101],"131":[23,101,103,135],"135":"b:AAGAEAAAAAAAAAACoQAAAICCLAABAAAA","153":"b:AAGAEAAAAAAAAAACoQAAAICCLAABAAAA","161":"b:AACAEAAAAAAAAAACoAAAAICCBAABAAAA","165":[23,101,103,137],"169":"b:AACAEAAAAAAAAAACoAAAAICCBAABAAAA","187":[23,28,89,101,103,137,143,146,160],"189":[23,28,89,101,103,137,143,146,160],"195":[23,28,89,101,103,137,143,146,160],"197":[23,101],"203":[23,101],"216":[23,28,89,101,103,137,143,146,160],"234":[23,28,89,101,103,137,143,146,160],"244":[23,28,89,101,103,137,143,146,160],"284":[23,28,89,101,103,137,143,146,160],"374":[135],"431":[23,101],"435":[23,101],"461":[23,28,89,101,103,137,143,146,160],"478":[23,28,89,101,103,137,143,146,160],"484":[23,28,89,101,103,137,143,146,160]},"packages/frontend/editor-ui/src/features/ndv/panel/components/WireMeUp.vue":{"8":"b:gEEAEAAACIAAAAAAgBAAIACARAAAAAAA"},"packages/frontend/editor-ui/src/features/shared/editors/components/JsEditor/JsEditor.vue":{"31":[1,143],"36":[1,143],"40":[1,143],"57":[1,143],"73":[1,143],"77":[1,143],"102":[1,143],"121":[1,143]},"packages/@n8n/db/src/repositories/credential-dependency.repository.ts":{"25":"b:ekkIIAykDIxQAAAQoBAAAAAAAAAIABAA","62":"b:ekkIIAykDIxQAAAQoBAAAAAAAAAIABAA","66":"b:ekkIIAykDIxQAAAQoBAAAAAAAAAIABAA","75":"b:ekkIIAykDIxQAAAQoBAAAAAAAAAIABAA","78":"b:ekkIIAykDIxQAAAQoBAAAAAAAAAIABAA","93":"b:ekkIIAykDIxQAAAQoBAAAAAAAAAIABAA","103":"b:ekkIIAykDIxQAAAQoBAAAAAAAAAIABAA","116":"b:ekkIIAykDIxQAAAQoBAAAAAAAAAIABAA","117":"b:ekkIIAykDIxQAAAQoBAAAAAAAAAIABAA","118":"b:ekkIIAykDIxQAAAQoBAAAAAAAAAIABAA","138":"b:ekkIIAykDIxQAAAQoBAAAAAAAAAIABAA","149":"b:ekkIIAykDIxQAAAQoBAAAAAAAAAIABAA"},"packages/@n8n/nodes-langchain/nodes/embeddings/EmbeddingsHuggingFaceInference/EmbeddingsHuggingFaceInference.node.ts":{"16":[14],"86":[14],"94":[14],"132":[14]},"packages/@n8n/nodes-langchain/nodes/memory/MemoryXata/MemoryXata.node.ts":{"23":[14],"105":[14]},"packages/@n8n/nodes-langchain/nodes/memory/descriptions.ts":{"28":[1,3,4,5,13,14],"63":[1,3,4,5,13,14]},"packages/@n8n/nodes-langchain/nodes/output_parser/OutputParserStructured/OutputParserStructured.node.ts":{"30":[14],"196":[14]},"packages/@n8n/nodes-langchain/nodes/text_splitters/TextSplitterTokenSplitter/TextSplitterTokenSplitter.node.ts":{"13":[14],"62":[14]},"packages/@n8n/nodes-langchain/nodes/text_splitters/TextSplitterTokenSplitter/TokenTextSplitter.ts":{"16":[14],"28":[14],"36":[14]},"packages/@n8n/nodes-langchain/nodes/tools/ToolWikipedia/ToolWikipedia.node.ts":{"17":[1,14],"25":[1,14],"58":[1,14],"64":[1,14]},"packages/@n8n/nodes-langchain/nodes/vector_store/VectorStoreQdrant/Qdrant.utils.ts":{"9":[14],"28":[14]},"packages/@n8n/nodes-langchain/nodes/vector_store/VectorStoreSupabase/VectorStoreSupabase.node.ts":{"67":[14],"84":[14]},"packages/@n8n/nodes-langchain/nodes/vector_store/VectorStoreWeaviate/Weaviate.utils.ts":{"22":[14],"68":[14],"127":[14]},"packages/@n8n/nodes-langchain/nodes/vector_store/shared/methods/listSearch.ts":{"10":[14],"19":[14],"27":[14],"58":[14],"65":[14],"73":[14],"87":[14],"95":[14],"102":[14]},"packages/@n8n/nodes-langchain/utils/output_parsers/N8nItemListOutputParser.ts":{"10":[1,3,4,5,8,10,12,6,14],"26":[1,3,4,5,8,10,12,6,14],"29":[1,3,4,5,8,10,12,6,14],"30":[1,3,4,5,8,10,12,6,14],"42":[1,3,4,5,8,10,12,6,14],"57":[1,3,4,5,8,10,12,6,14]},"packages/@n8n/nodes-langchain/utils/output_parsers/N8nOutputFixingParser.ts":{"15":[1,3,4,5,8,10,12,6,14],"24":[1,3,4,5,8,10,12,6,14],"35":[1,3,4,5,8,10,12,6,14],"42":[1,3,4,5,8,10,12,6,14],"88":[1,3,4,5,8,10,12,6,14],"92":[1,3,4,5,8,10,12,6,14]},"packages/@n8n/nodes-langchain/utils/output_parsers/N8nOutputParser.ts":{"4":[1,3,4,5,8,10,12,6,14],"5":[1,3,4,5,8,10,12,6,14],"15":[1,3,4,5,8,10,12,6,14]},"packages/@n8n/nodes-langchain/utils/output_parsers/N8nStructuredOutputParser.ts":{"22":[1,3,4,5,8,10,12,6,14],"31":[1,3,4,5,8,10,12,6,14],"131":[1,3,4,5,8,10,12,6,14],"148":[1,3,4,5,8,10,12,6,14],"175":[1,3,4,5,8,10,12,6,14]},"packages/@n8n/nodes-langchain/utils/output_parsers/langchainParserError.ts":{"11":[1,3,4,5,93,8,10,12,6,14],"19":[1,3,4,5,93,8,10,12,6,14],"26":[1,3,4,5,93,8,10,12,6,14],"30":[1,3,4,5,93,8,10,12,6,14],"33":[1,3,4,5,93,8,10,12,6,14],"41":[1,3,4,5,93,8,10,12,6,14]},"packages/cli/src/credentials/credential-dependency.service.ts":{"18":"b:ekkIIAykDIxQAAAQoBAAAAAAAAAIABAA","23":"b:ekkIIAykDIxQAAAQoBAAAAAAAAAIABAA","38":"b:ekkIIAykDIxQAAAQoBAAAAAAAAAIABAA","45":"b:ekkIIAykDIxQAAAQoBAAAAAAAAAIABAA","63":"b:ekkIIAykDIxQAAAQoBAAAAAAAAAIABAA","81":"b:ekkIIAykDIxQAAAQoBAAAAAAAAAIABAA","97":"b:ekkIIAykDIxQAAAQoBAAAAAAAAAIABAA"},"packages/cli/src/credentials/external-secrets.utils.ts":{"14":"b:ekkIIAykDIxQAAAAoBAAAAAAAAAIAAAA","23":"b:ekkIIAykDIxQAAAAoBAAAAAAAAAIAAAA","32":"b:ekkIIAykDIxQAAAAoBAAAAAAAAAIAAAA","70":"b:ekkIIAykDIxQAAAAoBAAAAAAAAAIAAAA"},"packages/cli/src/credentials/validation.ts":{"19":"b:ekkIIAykDIxQAAAQoBAAAAAAAAAIABAA","26":"b:ekkIIAykDIxQAAAQoBAAAAAAAAAIABAA","54":"b:ekkIIAykDIxQAAAQoBAAAAAAAAAIABAA","90":"b:ekkIIAykDIxQAAAQoBAAAAAAAAAIABAA","133":"b:ekkIIAykDIxQAAAQoBAAAAAAAAAIABAA","151":"b:ekkIIAykDIxQAAAQoBAAAAAAAAAIABAA","152":"b:ekkIIAykDIxQAAAQoBAAAAAAAAAIABAA","164":"b:ekkIIAykDIxQAAAQoBAAAAAAAAAIABAA"},"packages/cli/src/credentials-overwrites.ts":{"23":"b:ekkIIAykDIxQAAAAoBAAAAAACAAIAAAA","31":"b:ekkIIAykDIxQAAAAoBAAAAAACAAIAAAA","53":"b:ekkIIAykDIxQAAAAoBAAAAAACAAIAAAA","57":"b:ekkIIAykDIxQAAAAoBAAAAAACAAIAAAA","79":"b:ekkIIAykDIxQAAAAoBAAAAAACAAIAAAA","84":"b:ekkIIAykDIxQAAAAoBAAAAAACAAIAAAA","98":"b:ekkIIAykDIxQAAAAoBAAAAAACAAIAAAA","103":"b:ekkIIAykDIxQAAAAoBAAAAAACAAIAAAA","118":"b:ekkIIAykDIxQAAAAoBAAAAAACAAIAAAA","133":"b:ekkIIAykDIxQAAAAoBAAAAAACAAIAAAA","140":"b:ekkIIAykDIxQAAAAoBAAAAAACAAIAAAA","152":"b:ekkIIAykDIxQAAAAoBAAAAAACAAIAAAA","178":"b:ekkIIAykDIxQAAAAoBAAAAAACAAIAAAA","210":"b:ekkIIAykDIxQAAAAoBAAAAAACAAIAAAA","214":"b:ekkIIAykDIxQAAAAoBAAAAAACAAIAAAA","215":"b:ekkIIAykDIxQAAAAoBAAAAAACAAIAAAA","220":"b:ekkIIAykDIxQAAAAoBAAAAAACAAIAAAA","225":"b:ekkIIAykDIxQAAAAoBAAAAAACAAIAAAA","229":"b:ekkIIAykDIxQAAAAoBAAAAAACAAIAAAA","233":"b:ekkIIAykDIxQAAAAoBAAAAAACAAIAAAA","240":"b:ekkIIAykDIxQAAAAoBAAAAAACAAIAAAA"},"packages/cli/src/modules/external-secrets.ee/external-secrets-manager.ee.ts":{"35":"b:AEEAAIAAAgQAAABAAAAAIIQgCQwAAAgA","57":"b:AEEAAIAAAgQAAABAAAAAIIQgCQwAAAgA","60":"b:AEEAAIAAAgQAAABAAAAAIIQgCQwAAAgA","80":"b:AEEAAIAAAgQAAABAAAAAIIQgCQwAAAgA","92":"b:AEEAAIAAAgQAAABAAAAAIIQgCQwAAAgA","96":"b:AEEAAIAAAgQAAABAAAAAIIQgCQwAAAgA","104":"b:AEEAAIAAAgQAAABAAAAAIIQgCQwAAAgA","108":"b:AEEAAIAAAgQAAABAAAAAIIQgCQwAAAgA","112":"b:AEEAAIAAAgQAAABAAAAAIIQgCQwAAAgA","119":"b:AEEAAIAAAgQAAABAAAAAIIQgCQwAAAgA","125":"b:AEEAAIAAAgQAAABAAAAAIIQgCQwAAAgA","138":"b:AEEAAIAAAgQAAABAAAAAIIQgCQwAAAgA","170":"b:AEEAAIAAAgQAAABAAAAAIIQgCQwAAAgA","194":"b:AEEAAIAAAgQAAABAAAAAIIQgCQwAAAgA","198":"b:AEEAAIAAAgQAAABAAAAAIIQgCQwAAAgA","202":"b:AEEAAIAAAgQAAABAAAAAIIQgCQwAAAgA","206":"b:AEEAAIAAAgQAAABAAAAAIIQgCQwAAAgA","214":"b:AEEAAIAAAgQAAABAAAAAIIQgCQwAAAgA","225":"b:AEEAAIAAAgQAAABAAAAAIIQgCQwAAAgA","237":"b:AEEAAIAAAgQAAABAAAAAIIQgCQwAAAgA","288":"b:AEEAAIAAAgQAAABAAAAAIIQgCQwAAAgA","306":"b:AEEAAIAAAgQAAABAAAAAIIQgCQwAAAgA","340":"b:AEEAAIAAAgQAAABAAAAAIIQgCQwAAAgA","350":"b:AEEAAIAAAgQAAABAAAAAIIQgCQwAAAgA","368":"b:AEEAAIAAAgQAAABAAAAAIIQgCQwAAAgA","376":"b:AEEAAIAAAgQAAABAAAAAIIQgCQwAAAgA","378":"b:AEEAAIAAAgQAAABAAAAAIIQgCQwAAAgA","384":"b:AEEAAIAAAgQAAABAAAAAIIQgCQwAAAgA","395":"b:AEEAAIAAAgQAAABAAAAAIIQgCQwAAAgA","399":"b:AEEAAIAAAgQAAABAAAAAIIQgCQwAAAgA","418":"b:AEEAAIAAAgQAAABAAAAAIIQgCQwAAAgA"},"packages/cli/src/modules/external-secrets.ee/provider-registry.service.ts":{"10":"b:AkECIAwACMQQAAAQEBCAAAAAAAAAAAgA","13":"b:AkECIAwACMQQAAAQEBCAAAAAAAAAAAgA","17":"b:AkECIAwACMQQAAAQEBCAAAAAAAAAAAgA","21":"b:AkECIAwACMQQAAAQEBCAAAAAAAAAAAgA","25":"b:AkECIAwACMQQAAAQEBCAAAAAAAAAAAgA","29":"b:AkECIAwACMQQAAAQEBCAAAAAAAAAAAgA","33":"b:AkECIAwACMQQAAAQEBCAAAAAAAAAAAgA","43":"b:AkECIAwACMQQAAAQEBCAAAAAAAAAAAgA","47":"b:AkECIAwACMQQAAAQEBCAAAAAAAAAAAgA","51":"b:AkECIAwACMQQAAAQEBCAAAAAAAAAAAgA","55":"b:AkECIAwACMQQAAAQEBCAAAAAAAAAAAgA","57":"b:AkECIAwACMQQAAAQEBCAAAAAAAAAAAgA","58":"b:AkECIAwACMQQAAAQEBCAAAAAAAAAAAgA"},"packages/cli/src/modules/external-secrets.ee/secrets-providers-completions.controller.ee.ts":{"15":"b:AkECIAwACMQQAAAQEBCAAAAACAAAAAAA","24":"b:AkECIAwACMQQAAAQEBCAAAAACAAAAAAA","55":"b:AkECIAwACMQQAAAQEBCAAAAACAAAAAAA","72":"b:AkECIAwACMQQAAAQEBCAAAAACAAAAAAA","84":"b:AkECIAwACMQQAAAQEBCAAAAACAAAAAAA"},"packages/core/src/credentials.ts":{"11":"b:ekkIIAykDIxQAAAAoBAAAAAASAAIAAAA","19":"b:ekkIIAykDIxQAAAAoBAAAAAASAAIAAAA","27":"b:ekkIIAykDIxQAAAAoBAAAAAASAAIAAAA","37":"b:ekkIIAykDIxQAAAAoBAAAAAASAAIAAAA","48":"b:ekkIIAykDIxQAAAAoBAAAAAASAAIAAAA","72":"b:ekkIIAykDIxQAAAAoBAAAAAASAAIAAAA"},"packages/nodes-base/nodes/E2eTest/E2eTest.node.ts":{"175":[14,147,148],"180":[14,147,148],"188":[14,147,148],"201":[14,147,148],"207":[14,147,148]},"packages/nodes-base/nodes/E2eTest/mock.ts":{"66":[14,147,148]},"packages/nodes-base/nodes/Google/GenericFunctions.ts":{"75":[17,8,14,145,147,149,161],"129":[17,8,14,145,147,149,161]},"packages/nodes-base/nodes/Google/Sheet/GoogleSheets.node.ts":{"8":[14,147,159,161]},"packages/nodes-base/nodes/Google/Sheet/v1/GenericFunctions.ts":{"20":[14,147,159,161],"72":[14,147,159,161],"96":[14,147,159,161],"99":[14,147,159,161]},"packages/nodes-base/nodes/Google/Sheet/v1/GoogleSheet.ts":{"48":[14,147,159,161],"66":[14,147,159,161],"78":[14,147,159,161],"97":[14,147,159,161],"119":[14,147,159,161],"138":[14,147,159,161],"156":[14,147,159,161],"175":[14,147,159,161],"194":[14,147,159,161],"218":[14,147,159,161],"250":[14,147,159,161],"270":[14,147,159,161],"286":[14,147,159,161],"300":[14,147,159,161],"386":[14,147,159,161],"470":[14,147,159,161],"542":[14,147,159,161],"574":[14,147,159,161],"576":[14,147,159,161]},"packages/nodes-base/nodes/Google/Sheet/v1/GoogleSheetsV1.node.ts":{"32":[14,147,159,161],"42":[14,147,159,161],"68":[14,147,159,161],"96":[14,147,159,161],"125":[14,147,159,161],"227":[14,147,159,161],"299":[14,147,159,161],"413":[14,147,159,161]},"packages/nodes-base/nodes/Google/Sheet/v2/GoogleSheetsV2.node.ts":{"15":[14,147,159,161],"29":[14,147,159,161]},"packages/nodes-base/nodes/Google/Sheet/v2/actions/router.ts":{"9":[14,147,159,161]},"packages/nodes-base/nodes/Google/Sheet/v2/actions/sheet/append.operation.ts":{"213":[14,147,159,161],"326":[14,147,159,161]},"packages/nodes-base/nodes/Google/Sheet/v2/actions/sheet/appendOrUpdate.operation.ts":{"264":[14,147,159,161],"365":[14,147,159,161],"374":[14,147,159,161],"392":[14,147,159,161],"424":[14,147,159,161],"447":[14,147,159,161],"521":[14,147,159,161]},"packages/nodes-base/nodes/Google/Sheet/v2/actions/sheet/clear.operation.ts":{"160":[14,147,159,161]},"packages/nodes-base/nodes/Google/Sheet/v2/actions/sheet/create.operation.ts":{"77":[14,147,159,161]},"packages/nodes-base/nodes/Google/Sheet/v2/actions/sheet/delete.operation.ts":{"114":[14,147,159,161]},"packages/nodes-base/nodes/Google/Sheet/v2/actions/sheet/read.operation.ts":{"170":[14,147,159,161]},"packages/nodes-base/nodes/Google/Sheet/v2/actions/sheet/remove.operation.ts":{"7":[14,147,159,161]},"packages/nodes-base/nodes/Google/Sheet/v2/actions/sheet/update.operation.ts":{"253":[14,147,159,161],"345":[14,147,159,161],"354":[14,147,159,161],"371":[14,147,159,161],"403":[14,147,159,161],"430":[14,147,159,161],"496":[14,147,159,161]},"packages/nodes-base/nodes/Google/Sheet/v2/actions/spreadsheet/create.operation.ts":{"118":[14,147,159,161]},"packages/nodes-base/nodes/Google/Sheet/v2/actions/spreadsheet/delete.operation.ts":{"68":[14,147,159,161]},"packages/nodes-base/nodes/Google/Sheet/v2/actions/utils/readOperation.ts":{"12":[14,147,159,161],"110":[14,147,159,161]},"packages/nodes-base/nodes/Google/Sheet/v2/helpers/GoogleSheet.ts":{"39":[14,147,159,161],"47":[14,147,159,161],"58":[14,147,159,161],"74":[14,147,159,161],"93":[14,147,159,161],"117":[14,147,159,161],"136":[14,147,159,161],"149":[14,147,159,161],"165":[14,147,159,161],"178":[14,147,159,161],"186":[14,147,159,161],"204":[14,147,159,161],"220":[14,147,159,161],"264":[14,147,159,161],"287":[14,147,159,161],"333":[14,147,159,161],"367":[14,147,159,161],"380":[14,147,159,161],"394":[14,147,159,161],"411":[14,147,159,161],"441":[14,147,159,161],"446":[14,147,159,161],"461":[14,147,159,161],"480":[14,147,159,161],"494":[14,147,159,161],"545":[14,147,159,161],"627":[14,147,159,161],"675":[14,147,159,161],"803":[14,147,159,161],"833":[14,147,159,161],"835":[14,147,159,161],"857":[14,147,159,161],"880":[14,147,159,161]},"packages/nodes-base/nodes/Google/Sheet/v2/helpers/GoogleSheets.utils.ts":{"24":[14,147,159,161],"50":[14,147,159,161],"56":[14,147,159,161],"69":[14,147,159,161],"79":[14,147,159,161],"82":[14,147,159,161],"99":[14,147,159,161],"101":[14,147,159,161],"106":[14,147,159,161],"108":[14,147,159,161],"115":[14,147,159,161],"117":[14,147,159,161],"120":[14,147,159,161],"128":[14,147,159,161],"134":[14,147,159,161],"135":[14,147,159,161],"146":[14,147,159,161],"149":[14,147,159,161],"151":[14,147,159,161],"156":[14,147,159,161],"161":[14,147,159,161],"162":[14,147,159,161],"166":[14,147,159,161],"199":[14,147,159,161],"206":[14,147,159,161],"208":[14,147,159,161],"211":[14,147,159,161],"238":[14,147,159,161],"272":[14,147,159,161],"281":[14,147,159,161],"282":[14,147,159,161],"305":[14,147,159,161],"310":[14,147,159,161],"311":[14,147,159,161],"326":[14,147,159,161],"334":[14,147,159,161],"336":[14,147,159,161],"351":[14,147,159,161],"358":[14,147,159,161],"364":[14,147,159,161],"368":[14,147,159,161],"369":[14,147,159,161],"371":[14,147,159,161]},"packages/nodes-base/nodes/Google/Sheet/v2/methods/credentialTest.ts":{"9":[14,147,159,161]},"packages/nodes-base/nodes/Google/Sheet/v2/methods/listSearch.ts":{"13":[14,147,159,161],"42":[14,147,159,161],"51":[14,147,159,161]},"packages/nodes-base/nodes/Google/Sheet/v2/methods/loadOptions.ts":{"8":[14,147,159,161],"39":[14,147,159,161],"87":[14,147,159,161],"96":[14,147,159,161],"99":[14,147,159,161],"103":[14,147,159,161],"113":[14,147,159,161],"117":[14,147,159,161]},"packages/nodes-base/nodes/Google/Sheet/v2/methods/resourceMapping.ts":{"12":[14,147,159,161],"58":[14,147,159,161],"60":[14,147,159,161]},"packages/nodes-base/nodes/Google/Sheet/v2/transport/index.ts":{"16":[14,147,159,161],"84":[14,147,159,161]},"packages/nodes-base/nodes/Notion/Notion.node.ts":{"9":[29,8,51,62,63,14,155]},"packages/nodes-base/nodes/Notion/shared/GenericFunctions.ts":{"27":[29,8,51,62,63,135,14,155],"48":[29,8,51,62,63,135,14,155],"84":[29,8,51,62,63,135,14,155],"118":[29,8,51,62,63,135,14,155],"139":[29,8,51,62,63,135,14,155],"160":[29,8,51,62,63,135,14,155],"205":[29,8,51,62,63,135,14,155],"213":[29,8,51,62,63,135,14,155],"219":[29,8,51,62,63,135,14,155],"225":[29,8,51,62,63,135,14,155],"249":[29,8,51,62,63,135,14,155],"296":[29,8,51,62,63,135,14,155],"305":[29,8,51,62,63,135,14,155],"321":[29,8,51,62,63,135,14,155],"328":[29,8,51,62,63,135,14,155],"332":[29,8,51,62,63,135,14,155],"339":[29,8,51,62,63,135,14,155],"358":[29,8,51,62,63,135,14,155],"367":[29,8,51,62,63,135,14,155],"370":[29,8,51,62,63,135,14,155],"371":[29,8,51,62,63,135,14,155],"373":[29,8,51,62,63,135,14,155],"376":[29,8,51,62,63,135,14,155],"394":[29,8,51,62,63,135,14,155],"396":[29,8,51,62,63,135,14,155],"397":[29,8,51,62,63,135,14,155],"414":[29,8,51,62,63,135,14,155],"473":[29,8,51,62,63,135,14,155],"485":[29,8,51,62,63,135,14,155],"497":[29,8,51,62,63,135,14,155],"505":[29,8,51,62,63,135,14,155],"507":[29,8,51,62,63,135,14,155],"511":[29,8,51,62,63,135,14,155],"512":[29,8,51,62,63,135,14,155],"518":[29,8,51,62,63,135,14,155],"519":[29,8,51,62,63,135,14,155],"528":[29,8,51,62,63,135,14,155],"529":[29,8,51,62,63,135,14,155],"577":[29,8,51,62,63,135,14,155],"584":[29,8,51,62,63,135,14,155],"605":[29,8,51,62,63,135,14,155],"611":[29,8,51,62,63,135,14,155],"613":[29,8,51,62,63,135,14,155],"617":[29,8,51,62,63,135,14,155],"636":[29,8,51,62,63,135,14,155],"644":[29,8,51,62,63,135,14,155],"652":[29,8,51,62,63,135,14,155],"654":[29,8,51,62,63,135,14,155],"660":[29,8,51,62,63,135,14,155],"662":[29,8,51,62,63,135,14,155],"672":[29,8,51,62,63,135,14,155],"725":[29,8,51,62,63,135,14,155],"734":[29,8,51,62,63,135,14,155],"821":[29,8,51,62,63,135,14,155],"839":[29,8,51,62,63,135,14,155],"855":[29,8,51,62,63,135,14,155],"876":[29,8,51,62,63,135,14,155],"916":[29,8,51,62,63,135,14,155],"925":[29,8,51,62,63,135,14,155],"951":[29,8,51,62,63,135,14,155],"964":[29,8,51,62,63,135,14,155],"1084":[29,8,51,62,63,135,14,155],"1098":[29,8,51,62,63,135,14,155],"1099":[29,8,51,62,63,135,14,155],"1112":[29,8,51,62,63,135,14,155],"1129":[29,8,51,62,63,135,14,155],"1134":[29,8,51,62,63,135,14,155],"1142":[29,8,51,62,63,135,14,155],"1159":[29,8,51,62,63,135,14,155],"1171":[29,8,51,62,63,135,14,155],"1211":[29,8,51,62,63,135,14,155]},"packages/nodes-base/nodes/Notion/shared/descriptions/Blocks.ts":{"164":[29,8,51,62,63,14,155],"171":[29,8,51,62,63,14,155],"407":[29,8,51,62,63,14,155],"459":[29,8,51,62,63,14,155],"475":[29,8,51,62,63,14,155],"491":[29,8,51,62,63,14,155],"501":[29,8,51,62,63,14,155],"511":[29,8,51,62,63,14,155],"526":[29,8,51,62,63,14,155],"602":[29,8,51,62,63,14,155]},"packages/nodes-base/nodes/Notion/shared/descriptions/Filters.ts":{"1":[29,8,51,62,63,135,14,155]},"packages/nodes-base/nodes/Notion/shared/methods/listSearch.ts":{"10":[29,8,51,62,63,135,14,155],"28":[29,8,51,62,63,135,14,155]},"packages/nodes-base/nodes/Notion/v1/NotionV1.node.ts":{"34":[29,8,51,62,63,14,155],"44":[29,8,51,62,63,14,155],"69":[29,8,51,62,63,14,155],"80":[29,8,51,62,63,14,155],"92":[29,8,51,62,63,14,155],"103":[29,8,51,62,63,14,155],"106":[29,8,51,62,63,14,155],"116":[29,8,51,62,63,14,155],"121":[29,8,51,62,63,14,155],"127":[29,8,51,62,63,14,155],"132":[29,8,51,62,63,14,155],"145":[29,8,51,62,63,14,155],"172":[29,8,51,62,63,14,155],"184":[29,8,51,62,63,14,155],"195":[29,8,51,62,63,14,155],"203":[29,8,51,62,63,14,155],"223":[29,8,51,62,63,14,155],"404":[29,8,51,62,63,14,155],"409":[29,8,51,62,63,14,155]},"packages/nodes-base/nodes/Notion/v2/NotionV2.node.ts":{"38":[29,8,51,62,63,14,155],"47":[29,8,51,62,63,14,155],"140":[29,8,51,62,63,14,155],"453":[29,8,51,62,63,14,155],"457":[29,8,51,62,63,14,155]},"packages/nodes-base/nodes/Notion/v2/methods/loadOptions.ts":{"11":[29,8,51,62,63,14,155],"37":[29,8,51,62,63,14,155],"49":[29,8,51,62,63,14,155],"63":[29,8,51,62,63,14,155],"75":[29,8,51,62,63,14,155],"79":[29,8,51,62,63,14,155],"91":[29,8,51,62,63,14,155],"99":[29,8,51,62,63,14,155],"105":[29,8,51,62,63,14,155],"111":[29,8,51,62,63,14,155],"125":[29,8,51,62,63,14,155],"154":[29,8,51,62,63,14,155],"166":[29,8,51,62,63,14,155],"181":[29,8,51,62,63,14,155],"189":[29,8,51,62,63,14,155]},"packages/nodes-base/nodes/Notion/v3/NotionV3.node.ts":{"15":[29,8,51,62,63,14,155],"24":[29,8,51,62,63,14,155]},"packages/nodes-base/nodes/Notion/v3/actions/block/Block.resource.ts":{"18":[29,8,51,62,63,14,155],"47":[29,8,51,62,63,14,155],"142":[29,8,51,62,63,14,155],"177":[29,8,51,62,63,14,155],"205":[29,8,51,62,63,14,155]},"packages/nodes-base/nodes/Notion/v3/actions/common.descriptions.ts":{"212":[29,8,51,62,63,14,155],"231":[29,8,51,62,63,14,155],"251":[29,8,51,62,63,14,155],"299":[29,8,51,62,63,14,155],"319":[29,8,51,62,63,14,155]},"packages/nodes-base/nodes/Notion/v3/actions/dataSource/DataSource.resource.ts":{"78":[29,8,51,62,63,14,155],"106":[29,8,51,62,63,14,155]},"packages/nodes-base/nodes/Notion/v3/actions/database/Database.resource.ts":{"47":[29,8,51,62,63,14,155]},"packages/nodes-base/nodes/Notion/v3/actions/databasePage/DataSourceFilters.ts":{"105":[29,8,51,62,63,14,155],"106":[29,8,51,62,63,14,155],"112":[29,8,51,62,63,14,155],"113":[29,8,51,62,63,14,155],"130":[29,8,51,62,63,14,155],"131":[29,8,51,62,63,14,155],"136":[29,8,51,62,63,14,155],"141":[29,8,51,62,63,14,155],"148":[29,8,51,62,63,14,155],"152":[29,8,51,62,63,14,155],"161":[29,8,51,62,63,14,155],"197":[29,8,51,62,63,14,155],"207":[29,8,51,62,63,14,155],"217":[29,8,51,62,63,14,155],"265":[29,8,51,62,63,14,155],"266":[29,8,51,62,63,14,155],"272":[29,8,51,62,63,14,155],"358":[29,8,51,62,63,14,155],"365":[29,8,51,62,63,14,155],"371":[29,8,51,62,63,14,155],"373":[29,8,51,62,63,14,155]},"packages/nodes-base/nodes/Notion/v3/actions/databasePage/DatabasePage.resource.ts":{"40":[29,8,51,62,63,14,155],"127":[29,8,51,62,63,14,155],"477":[29,8,51,62,63,14,155],"493":[29,8,51,62,63,14,155],"568":[29,8,51,62,63,14,155],"598":[29,8,51,62,63,14,155],"660":[29,8,51,62,63,14,155]},"packages/nodes-base/nodes/Notion/v3/actions/page/Page.resource.ts":{"206":[29,8,51,62,63,14,155],"230":[29,8,51,62,63,14,155],"261":[29,8,51,62,63,14,155],"285":[29,8,51,62,63,14,155],"319":[29,8,51,62,63,14,155]},"packages/nodes-base/nodes/Notion/v3/actions/router.ts":{"22":[29,8,51,62,63,14,155]},"packages/nodes-base/nodes/Notion/v3/actions/user/User.resource.ts":{"36":[29,8,51,62,63,14,155],"54":[29,8,51,62,63,14,155]},"packages/nodes-base/nodes/Notion/v3/helpers/utils.ts":{"12":[29,8,51,62,63,135,14,155],"38":[29,8,51,62,63,135,14,155],"48":[29,8,51,62,63,135,14,155],"74":[29,8,51,62,63,135,14,155],"78":[29,8,51,62,63,135,14,155],"79":[29,8,51,62,63,135,14,155],"102":[29,8,51,62,63,135,14,155],"106":[29,8,51,62,63,135,14,155],"113":[29,8,51,62,63,135,14,155],"119":[29,8,51,62,63,135,14,155],"150":[29,8,51,62,63,135,14,155],"156":[29,8,51,62,63,135,14,155],"190":[29,8,51,62,63,135,14,155],"199":[29,8,51,62,63,135,14,155],"237":[29,8,51,62,63,135,14,155],"245":[29,8,51,62,63,135,14,155],"253":[29,8,51,62,63,135,14,155],"261":[29,8,51,62,63,135,14,155],"285":[29,8,51,62,63,135,14,155],"309":[29,8,51,62,63,135,14,155],"324":[29,8,51,62,63,135,14,155],"340":[29,8,51,62,63,135,14,155],"360":[29,8,51,62,63,135,14,155],"371":[29,8,51,62,63,135,14,155],"389":[29,8,51,62,63,135,14,155],"398":[29,8,51,62,63,135,14,155],"420":[29,8,51,62,63,135,14,155]},"packages/nodes-base/nodes/Notion/v3/methods/listSearch.ts":{"10":[29,8,51,62,63,135,14,155],"15":[29,8,51,62,63,135,14,155],"21":[29,8,51,62,63,135,14,155],"26":[29,8,51,62,63,135,14,155],"32":[29,8,51,62,63,135,14,155],"38":[29,8,51,62,63,135,14,155],"47":[29,8,51,62,63,135,14,155],"64":[29,8,51,62,63,135,14,155],"68":[29,8,51,62,63,135,14,155],"88":[29,8,51,62,63,135,14,155]},"packages/nodes-base/nodes/Notion/v3/methods/loadOptions.ts":{"22":[29,8,51,62,63,135,14,155],"37":[29,8,51,62,63,135,14,155],"56":[29,8,51,62,63,135,14,155],"59":[29,8,51,62,63,135,14,155],"60":[29,8,51,62,63,135,14,155],"70":[29,8,51,62,63,135,14,155],"77":[29,8,51,62,63,135,14,155],"84":[29,8,51,62,63,135,14,155],"88":[29,8,51,62,63,135,14,155],"109":[29,8,51,62,63,135,14,155],"123":[29,8,51,62,63,135,14,155],"142":[29,8,51,62,63,135,14,155],"155":[29,8,51,62,63,135,14,155],"178":[29,8,51,62,63,135,14,155]},"packages/nodes-base/nodes/Notion/v3/transport/index.ts":{"17":[29,8,51,62,63,135,14,155],"21":[29,8,51,62,63,135,14,155],"54":[29,8,51,62,63,135,14,155],"88":[29,8,51,62,63,135,14,155]},"packages/frontend/@n8n/design-system/src/components/N8nIcon/custom/grip-lines-vertical.svg":{"9":[1,7,10,13,14]},"packages/frontend/@n8n/design-system/src/components/N8nIcon/custom/vector-square.svg":{"9":[1,7,10,13,14]},"packages/frontend/@n8n/design-system/src/components/N8nIcon/nodes/basic-llm-chain.svg":{"11":[7,13,14]},"packages/frontend/@n8n/design-system/src/components/N8nIcon/nodes/contextual-compression-retriever.svg":{"11":[14]},"packages/frontend/@n8n/design-system/src/components/N8nIcon/nodes/guardrails.svg":{"11":[14]},"packages/frontend/@n8n/design-system/src/components/N8nIcon/nodes/multiquery-retriever.svg":{"11":[14]},"packages/frontend/@n8n/design-system/src/components/N8nIcon/nodes/sentiment-analysis.svg":{"11":[14]},"packages/frontend/@n8n/design-system/src/components/N8nIcon/nodes/structured-output-parser.svg":{"11":[1,14]},"packages/frontend/@n8n/design-system/src/components/N8nIcon/nodes/summarization-chain.svg":{"11":[14]},"packages/frontend/@n8n/design-system/src/components/N8nIcon/nodes/text-classifier.svg":{"11":[14]},"packages/frontend/@n8n/design-system/src/components/N8nIcon/nodes/token-splitter.svg":{"11":[14]},"packages/frontend/@n8n/design-system/src/components/N8nIcon/nodes/workflow-retriever.svg":{"11":[14]},"packages/frontend/editor-ui/src/app/components/Banner.vue":{"48":"b:kkECIAwACIQQAAAQEBCAAAAACAAAAAAA","58":"b:kkECIAwACIQQAAAQEBCAAAAACAAAAAAA","65":"b:kkECIAwACIQQAAAQEBCAAAAACAAAAAAA","71":"b:kkECIAwACIQQAAAQEBCAAAAACAAAAAAA","78":"b:kkECIAwACIQQAAAQEBCAAAAACAAAAAAA","85":[58]},"packages/frontend/editor-ui/src/app/components/SaveButton.vue":{"12":"b:kkECIAwACKwQAAAQEBCIAAAACAAAAAAA","36":"b:kkECIAwACKwQAAAQEBCIAAAACAAAAAAA","45":"b:kkECIAwACKwQAAAQEBCIAAAACAAAAAAA","73":"b:kkEAIAQACKwQAAAQABCIAAAAAAAAAAAA"},"packages/frontend/editor-ui/src/app/utils/telemetryUtils.ts":{"5":[58,138,139,140,141,14,147,148],"13":[58,138,139,140,141,14,148],"14":[58,138,139,140,141,148],"21":[58,138,139,140,141,148],"27":[138,140,148],"28":[138,140,148],"71":[58,138,139,140,141,148]},"packages/frontend/editor-ui/src/features/credentials/components/CredentialEdit/CredentialConfig.vue":{"87":"b:kkECIAwACIQQAAAQEBCAAAAACAAAAAAA","120":"b:kkECIAwACIQQAAAQEBCAAAAACAAAAAAA","141":"b:kkECIAwACIQQAAAQEBCAAAAACAAAAAAA","144":[17,8,35,147,68],"149":"b:kkECIAwACIQQAAAQEBCAAAAACAAAAAAA","150":"b:kkECIAwACIQQAAAQEBCAAAAACAAAAAAA","161":"b:kkECIAwACIQQAAAQEBCAAAAACAAAAAAA","191":"b:kkECIAwACIQQAAAQEBCAAAAACAAAAAAA","196":[17,8,35,147,68],"204":"b:kkECIAwACIQQAAAQEBCAAAAACAAAAAAA","213":"b:kkECIAwACIQQAAAQEBCAAAAACAAAAAAA","234":"b:kkECIAwACIQQAAAQEBCAAAAACAAAAAAA","242":"b:kkECIAwACIQQAAAQEBCAAAAACAAAAAAA","250":"b:kkECIAwACIQQAAAQEBCAAAAACAAAAAAA","252":"b:kkECIAwACIQQAAAQEBCAAAAACAAAAAAA","254":"b:kkECIAwACIQQAAAQEBCAAAAACAAAAAAA","265":[17],"269":"b:kkECIAwACIQQAAAQEBCAAAAACAAAAAAA","271":"b:kkEAIAwACIQQAAAQABCAAAAACAAAAAAA","275":"b:kkECIAwACIQQAAAQEBCAAAAACAAAAAAA","282":[17,8,35,147,68],"289":"b:kkECIAwACIQQAAAQEBCAAAAACAAAAAAA","300":"b:kkECIAwACIQQAAAQEBCAAAAACAAAAAAA","309":"b:kkECIAwACIQQAAAQEBCAAAAACAAAAAAA","311":"b:kkECIAwACIQQAAAQEBCAAAAACAAAAAAA","316":"b:kkECIAwACIQQAAAQEBCAAAAACAAAAAAA","320":"b:kkECIAwACIQQAAAQABCAAAAACAAAAAAA","324":"b:kkEAIAwACIQQAAAAABCAAAAACAAAAAAA","337":[17,8],"361":[17],"379":[35],"384":"b:kkECIAwACIQQAAAQEBCAAAAACAAAAAAA","436":"b:kkECIAwACIQQAAAQABCAAAAACAAAAAAA","438":"b:kkECIAwACIQQAAAQABCAAAAACAAAAAAA","576":"b:kkECIAwACIQQAAAQEBCAAAAACAAAAAAA","634":[17,8,35,147],"635":[35,147],"639":"b:kkECIAQACIQQAAAQEBCAAAAAAAAAAAAA","646":[68],"696":"b:kkECIAwACIAQAAAQEBCAAAAACAAAAAAA","698":"b:kkECIAwACIAQAAAQEBCAAAAACAAAAAAA","700":"b:kkECIAwACIAQAAAQEBCAAAAACAAAAAAA"},"packages/frontend/editor-ui/src/features/credentials/components/CredentialEdit/CredentialEdit.vue":{"88":"b:kkECIAwACIQQAAAQEBCAAAAACAAAAAAA","90":[8,147,68],"95":[8,147,68],"172":"b:kkECIAwACIQQAAAQEBCAAAAACAAAAAAA","174":"b:kkECIAwACIQQAAAQEBCAAAAACAAAAAAA","184":"b:kkECIAwACIQQAAAQEBCAAAAACAAAAAAA","190":"b:kkECIAwACIQQAAAQEBCAAAAACAAAAAAA","191":"b:kkECIAwACIQQAAAQEBCAAAAACAAAAAAA","192":"b:kkECIAwACIQQAAAQEBCAAAAACAAAAAAA","193":"b:kkECIAwACIQQAAAQEBCAAAAACAAAAAAA","194":"b:kkECIAwACIQQAAAAEBCAAAAACAAAAAAA","195":"b:kkECIAwACIQQAAAAEBCAAAAACAAAAAAA","241":"b:kkECIAwACIQQAAAQEBCAAAAACAAAAAAA","248":"b:kkECIAwACIQQAAAQEBCAAAAACAAAAAAA","253":"b:kkEAIAwACIQQAAAQABCAAAAAAAAAAAAA","258":"b:kkECIAwACIQQAAAQEBCAAAAACAAAAAAA","264":"b:kkECIAwACIQQAAAQEBCAAAAACAAAAAAA","269":"b:kkECIAwACIQQAAAQEBCAAAAACAAAAAAA","273":"b:kkECIAwACIQQAAAQEBCAAAAACAAAAAAA","299":"b:kkECIAwACIQQAAAQEBCAAAAACAAAAAAA","309":"b:kkECIAwACIQQAAAQEBCAAAAACAAAAAAA","316":"b:kkECIAwACIQQAAAQEBCAAAAACAAAAAAA","323":[92,8,34,119,63],"325":"b:kkECIAwACIQQAAAQEBCAAAAACAAAAAAA","395":"b:kkECIAwACIQQAAAQEBCAAAAACAAAAAAA","413":"b:kkEAIAQACIQQAAAQABCAAAAACAAAAAAA","468":[92,34,119,63],"483":[119,63],"492":[92,34],"497":[17,8],"505":[17,8],"512":[17,8],"575":"b:kkEAIAwACIQQAAAAABCAAAAACAAAAAAA","579":[1,7,29,8,51,108,14,68],"583":[29,8,34,58,119,63,68],"589":"b:kkEAIAgACIAQAAAQABCAAAAAAAAAAAAA","590":"b:kkEAIAgACIAQAAAQABCAAAAAAAAAAAAA","605":"b:kkEAIAwACIQQAAAQABCAAAAACAAAAAAA","696":"b:kkEAIAwACIQQAAAQABCAAAAACAAAAAAA","762":"b:kkEAIAwACIQQAAAQABCAAAAACAAAAAAA","795":"b:kkEAIAwACIQQAAAAABCAAAAACAAAAAAA","818":"b:kkEAIAwACIQQAAAAABCAAAAACAAAAAAA","868":[92,8,34,119,63],"923":[8],"993":[35,147,68],"1083":[147],"1089":[35,147],"1096":[35,147],"1129":[35],"1144":[35],"1149":[35],"1201":[17,8],"1260":"b:kkECIAwACIQQAAAQEBCAAAAACAAAAAAA","1274":"b:kkECIAwACIQQAAAQEBCAAAAACAAAAAAA","1309":"b:kkECIAwACIQQAAAQEBCAAAAACAAAAAAA","1348":"b:kkECIAwACIQQAAAQEBCAAAAACAAAAAAA","1352":"b:kkECIAwACIQQAAAQEBCAAAAACAAAAAAA","1356":[92,34,119,63]},"packages/frontend/editor-ui/src/features/credentials/components/CredentialEdit/CredentialInputs.vue":{"25":"b:kkECIAwACIQQAAAQEBCAAAAACAAAAAAA","29":"b:kkECIAwACIQQAAAQEBCAAAAACAAAAAAA","30":"b:kkECIAwACIQQAAAQEBCAAAAACAAAAAAA","39":"b:kkEAIAwACIQQAAAAABCAAAAACAAAAAAA"},"packages/frontend/editor-ui/src/features/credentials/components/CredentialEdit/CredentialModeSelector.vue":{"27":"b:kkECIAwACIQQAAAQEBCAAAAACAAAAAAA","46":"b:kkECIAwACIQQAAAQEBCAAAAACAAAAAAA","47":"b:kkECIAwACIQQAAAQEBCAAAAACAAAAAAA","52":[17,1,7,29,8,100,51,108,14,147],"53":[17,1,7,29,8,100,51,108,14,147],"60":"b:kgECIAwACIQQAAAQEBCAAAAAAAAAAAAA","61":"b:kgECIAwACIQQAAAQEBCAAAAAAAAAAAAA","66":[17],"79":"b:kkECIAwACIQQAAAQEBCAAAAACAAAAAAA","90":"b:kkECIAwACIQQAAAQEBCAAAAACAAAAAAA","102":[17,29,8,51,14,147],"124":"b:kkECIAwACIQQAAAQEBCAAAAACAAAAAAA","146":[17,29,8,51,14,147],"168":"b:kkECIAwACIQQAAAQEBCAAAAACAAAAAAA","169":[17,29,8,51,14,147],"170":[17,29,8,51,14,147],"173":[17,29,8,51,14,147],"180":[17,29,8,51,14,147],"181":[17,29,8,51,14,147],"188":[17,8],"191":"b:kkECIAwACIQQAAAQEBCAAAAACAAAAAAA","198":[17,29,8,51,14,147],"208":[17,29,8,51,14,147],"210":[17,29,8,51,14,147]},"packages/frontend/editor-ui/src/features/credentials/components/CredentialIcon.vue":{"24":"b:ukUKIA4ADIQQAAA4EBCAAAAACAAAAAAA","26":"b:ukUKIA4ADIQQAAA4EBCAAAAACAAAAAAA","30":"b:ukUKIA4ADIQQAAA4EBCAAAAACAAAAAAA","42":"b:ukUKIA4ADIQQAAA4EBCAAAAACAAAAAAA","53":"b:ukUKIA4ADIQQAAA4EBCAAAAACAAAAAAA","59":"b:ukUKIA4ADIQQAAA4EBCAAAAACAAAAAAA","69":"b:ukUKIA4ADIQQAAA4EBCAAAAACAAAAAAA","79":"b:ukUKIA4ADIQQAAA4EBCAAAAACAAAAAAA"},"packages/frontend/editor-ui/src/features/credentials/composables/useCredentialForm.ts":{"65":"b:kkECIAwACIQQAAAQEBCAAAAACAAAAAAA","91":"b:kkECIAwACIQQAAAQEBCAAAAACAAAAAAA","96":"b:kkECIAwACIQQAAAQEBCAAAAACAAAAAAA","106":"b:kkECIAwACIQQAAAAEBCAAAAACAAAAAAA","118":[1,7,100,108],"126":"b:kkECIAwACIQQAAAQEBCAAAAACAAAAAAA","135":"b:kkECIAwACIQQAAAQEBCAAAAACAAAAAAA","150":"b:kkECIAwACIQQAAAQEBCAAAAACAAAAAAA","154":"b:kkEAIAQACIAQAAAQABCAAAAAAAAAAAAA","159":"b:kkECIAwACIQQAAAQEBCAAAAACAAAAAAA","169":"b:kkECIAwACIQQAAAQEBCAAAAACAAAAAAA","174":[17,1,7,29,8,100,51,108,14,147],"179":[17,8,35,147],"183":"b:kkECIAwACIQQAAAQEBCAAAAACAAAAAAA","196":"b:kkECIAwACIQQAAAQEBCAAAAACAAAAAAA","202":"b:kkECIAwACIQQAAAQEBCAAAAACAAAAAAA","206":"b:kkECIAwACIQQAAAQEBCAAAAACAAAAAAA","216":"b:kkECIAwACIQQAAAQEBCAAAAACAAAAAAA","220":"b:kkECIAwACIQQAAAQEBCAAAAACAAAAAAA","227":"b:kkECIAwACIQQAAAQEBCAAAAACAAAAAAA","231":"b:kkECIAwACIQQAAAQEBCAAAAACAAAAAAA","257":"b:kkEAIAwACIQQAAAQABCAAAAACAAAAAAA","260":"b:kkEAIAQACIQQAAAQABCAAAAAAAAAAAAA","266":"b:kkEAIAQACIAQAAAQABCAAAAAAAAAAAAA","267":"b:kkEAIAQACIAQAAAQABCAAAAAAAAAAAAA","274":"b:kkECIAwACIQQAAAQEBCAAAAACAAAAAAA","283":"b:kkECIAwACIQQAAAQEBCAAAAACAAAAAAA","293":"b:kkECIAwACIQQAAAQEBCAAAAACAAAAAAA","307":"b:kkECIAwACIQQAAAQEBCAAAAACAAAAAAA","337":"b:kkECIAwACIQQAAAQEBCAAAAACAAAAAAA","346":"b:kkEAIAwACIQQAAAQABCAAAAACAAAAAAA","347":"b:kkEAIAwACIQQAAAQABCAAAAACAAAAAAA","354":"b:kkECIAwACIQQAAAQEBCAAAAACAAAAAAA","370":[17,8],"395":[92,8,34],"397":[92,8,34],"403":[92,8,34],"435":[92,8,34],"451":"b:kkECIAwACIQQAAAQEBCAAAAACAAAAAAA","474":"b:kkEAIAwACIQQAAAAABCAAAAACAAAAAAA","501":[1,7,29,4,92,8,51,108,119,63,14,68],"513":[92,8,34]},"packages/frontend/editor-ui/src/features/credentials/quickConnect/components/QuickConnectButton.vue":{"9":"b:kkUCIAQACIQQAAAQEBCAAAAAAAAAAAAA","35":"b:kkUCIAQACIQQAAAQEBCAAAAAAAAAAAAA","44":"b:kkUCIAQACIQQAAAQEBCAAAAAAAAAAAAA","49":"b:kkUCIAQACIQQAAAQEBCAAAAAAAAAAAAA","64":"b:kkUCIAQACIQQAAAQEBCAAAAAAAAAAAAA"},"packages/frontend/editor-ui/src/features/ndv/parameters/components/FixedCollection/FixedCollectionParameter.vue":{"39":[22,23,101,103,125,138,139,14]},"packages/frontend/editor-ui/src/features/ndv/parameters/components/ParameterInputExpanded.vue":{"24":"b:kkECIAwACIQQAAAQEBCAAAAACAAAAAAA","61":"b:kkECIAwACIQQAAAQEBCAAAAACAAAAAAA","92":"b:kkECIAwACIQQAAAQEBCAAAAACAAAAAAA","114":"b:kkECIAwACIQQAAAQEBCAAAAACAAAAAAA","122":"b:kkECIAwACIQQAAAQEBCAAAAACAAAAAAA","129":"b:kkEAIAwACIQQAAAAABCAAAAACAAAAAAA","133":"b:kkEAIAwACIQQAAAAABCAAAAACAAAAAAA","142":[58],"146":"b:kkEAIAwACIQQAAAAABCAAAAACAAAAAAA","169":[58],"171":"b:kkECIAwACIQQAAAQEBCAAAAACAAAAAAA","184":"b:kkECIAwACIQQAAAQEBCAAAAACAAAAAAA","195":"b:kkECIAwACIQQAAAQEBCAAAAACAAAAAAA"},"packages/frontend/editor-ui/src/features/shared/nodeCreator/components/ItemTypes/LinkItem.vue":{"13":[48,14]},"packages/cli/src/modules/workflow-index/workflow-dependency-query.service.ts":{"37":"b:AAEAABQADsBsAIBBAAOAASAAQAACAAAA","48":"b:AAEAABQADsBsAIBBAAOAASAAQAACAAAA","73":"b:AAEAABQADsBsAIBBAAOAASAAQAACAAAA","151":"b:AAEAABQADsBsAIBBAAOAASAAQAACAAAA","175":"b:AAEAABQADsBsAIBBAAOAASAAQAACAAAA","231":"b:AAEAABQADsBsAIBBAAOAASAAQAACAAAA","251":"b:AAEAABQADsBsAIBBAAOAASAAQAACAAAA","318":"b:AAEAABQADsBsAIBBAAOAASAAQAACAAAA","332":"b:AAEAABQADsBsAIBBAAOAASAAQAACAAAA","340":"b:AAEAABQADsBsAIBBAAOAASAAQAACAAAA","348":"b:AAEAABQADsBsAIBBAAOAASAAQAACAAAA","361":"b:AAEAABQADsBsAIBBAAOAASAAQAACAAAA","365":"b:AAEAABQADsBsAIBBAAOAASAAQAACAAAA"},"packages/cli/src/modules/workflow-index/workflow-dependency.controller.ts":{"9":"b:AAAAAAQADsBsAIBBAAEAASAAQAAAAAAA","12":"b:AAAAAAQADsBsAIBBAAEAASAAQAAAAAAA","25":"b:AAAAAAQADsBsAIBBAAEAASAAQAAAAAAA"},"packages/frontend/@n8n/design-system/src/directives/n8n-truncate.ts":{"19":"b:AAEAIBwADYRkAABAAAOEACAAQAADAAAA"},"packages/frontend/editor-ui/src/app/api/workflow-dependencies.ts":{"9":"b:AAEAABQAD4BkAABAAAOAACAAQAADAAAA"},"packages/frontend/editor-ui/src/app/components/IntersectionObserved.vue":{"6":[48,112,150,154,159],"19":[48,112,150,154,159],"29":[48,154],"34":[48,112,150,154,159]},"packages/frontend/editor-ui/src/app/components/IntersectionObserver.vue":{"6":[48,112,150,154,66,67,159,161],"26":[48,154,67],"32":[48,112,150,154,66,67,159,161],"43":[48,112,150,154,159],"44":[48,112,150,154,159],"54":[48,112,150,154,159],"60":[48,154],"64":[48,112,150,154,66,67,159,161]},"packages/frontend/editor-ui/src/app/components/WorkflowCard.vue":{"56":"b:AAAAABAADwBkAABAAAMAACAAQAADAAAA","134":"b:AAAAABAADwBkAABAAAMAACAAQAADAAAA","137":"b:AAAAABAADQBkAABAAAIAACAAQAADAAAA","138":[160],"139":"b:AAAAABAADwBkAABAAAMAACAAQAADAAAA","141":"b:AAAAABAADwBkAABAAAMAACAAQAADAAAA","144":[51,66],"147":[51,66],"151":"b:AAAAABAADwBkAABAAAMAACAAQAADAAAA","155":"b:AAAAABAADQBkAABAAAIAACAAQAADAAAA","166":"b:AAAAABAADQBkAABAAAIAACAAQAADAAAA","186":"b:AAAAABAADwBkAABAAAMAACAAQAADAAAA","286":"b:AAAAABAADwBkAABAAAMAACAAQAADAAAA","307":"b:AAAAABAADwBkAABAAAMAACAAQAADAAAA","311":"b:AAAAABAADQBkAABAAAIAACAAQAADAAAA","317":"b:AAAAABAADwBkAABAAAMAACAAQAADAAAA","321":"b:AAAAABAADwBkAABAAAMAACAAQAADAAAA","325":"b:AAAAABAADwBkAABAAAMAACAAQAADAAAA","327":[51,133,66,160],"353":[48,49,50,51,160],"417":[160],"427":[160],"462":[160],"466":[160],"504":[160],"553":[160],"593":[160],"595":[160],"597":"b:AAAAABAADwBkAABAAAMAACAAQAADAAAA","608":"b:AAAAABAADwBkAABAAAMAACAAQAADAAAA","620":[66],"640":"b:AAAAABAADwBkAABAAAMAACAAQAADAAAA","670":"b:AAAAABAADwBkAABAAAMAACAAQAADAAAA","687":"b:AAAAABAADQBkAABAAAIAACAAQAADAAAA","713":[160]},"packages/frontend/editor-ui/src/app/components/forms/ResourceFiltersDropdown.vue":{"51":[160],"63":"b:AAEAIBwAD4RkAABAAAOEACAAQAADAAAA","66":"b:AAEAIBwAD4RkAABAAAOEACAAQAADAAAA","89":"b:AAEAIBwAD4RkAABAAAOEACAAQAADAAAA","91":[160],"115":"b:AAEAIBwAD4RkAABAAAOEACAAQAADAAAA","119":[160],"126":"b:AAEAIBwAD4RkAABAAAOEACAAQAADAAAA","130":"b:AAEAIBwAD4RkAABAAAOEACAAQAADAAAA","142":"b:AAEAIBwAD4RkAABAAAOEACAAQAADAAAA","148":[160],"164":[160],"183":[160]},"packages/frontend/editor-ui/src/features/execution/insights/components/InsightsSummary.vue":{"33":"b:AAACIBQADIRkAABAAAKEACAAQAADAAAA","46":"b:AAACIBQADIRkAABAAAKEACAAQAADAAAA","54":"b:AAACIBQADIRkAABAAAKEACAAQAADAAAA","56":"b:AAACIBQADIRkAABAAAKEACAAQAADAAAA","59":"b:AAACIBQADIRkAABAAAKEACAAQAADAAAA","60":"b:AAACIBQADIRkAABAAAKEACAAQAADAAAA","118":"b:AAACIBQADIRkAABAAAKEACAAQAADAAAA","133":"b:AAACIBQADIRkAABAAAKEACAAQAADAAAA"},"packages/frontend/editor-ui/src/features/shared/tags/components/TagsContainer.vue":{"20":[48,112,150,154,66,67,159,161],"37":[48,112,150,154,66,67,159,161],"41":[48,112,150,154,66,67,159,161],"42":[48,112,150,154,159],"48":[48,112,150,154,159],"55":[48,112,150,154,159],"63":[48,112,150,154,159],"81":[48,112,150,154,66,67,159,161],"87":[48,112,150,154,66,67,159,161],"95":[48,112,150,154,159],"101":[48,154],"112":[48,112,150,154,66,67,159,161],"117":[48,154,67],"120":[48,112,150,154,66,67,159,161],"131":[48,112,150,154,66,67,159,161],"134":[48,112,150,154,159],"137":[48,154],"152":[48,112,150,154,159]},"packages/frontend/editor-ui/src/features/shared/tags/components/WorkflowTagsContainer.vue":{"16":[48,112,150,154,66,67,159,161],"22":[48,112,150,154,66,67,159,161],"24":[48,154],"26":[48,112,150,154,66,67,159,161]},"packages/cli/src/services/workflow-loader.service.ts":{"7":[51,152,153],"9":[51,152,153]},"packages/core/src/execution-engine/node-execution-context/local-load-options-context.ts":{"16":[51,152,153],"23":[51,152,153],"42":[51,152,153],"69":[51,152,153]},"packages/core/src/execution-engine/node-execution-context/workflow-node-context.ts":{"16":[51,152,153],"22":[51,152,153]},"packages/nodes-base/nodes/ExecuteWorkflow/ExecuteWorkflow/ExecuteWorkflow.node.ts":{"303":[51,181,151,65,153]},"packages/nodes-base/nodes/ExecuteWorkflow/ExecuteWorkflow/GenericFunctions.ts":{"10":[51,181,151,65,153],"29":[51,181,151,65,153]},"packages/nodes-base/nodes/ExecuteWorkflow/ExecuteWorkflow/methods/localResourceMapping.ts":{"5":[51,181,151,152,65,153]},"packages/nodes-base/nodes/ExecuteWorkflow/ExecuteWorkflowTrigger/ExecuteWorkflowTrigger.node.ts":{"223":[51,181,151,152,65,183,153],"229":[51,181,151,152,65,183,153],"248":[51,181,151,152,65,183,153],"249":[51,181,151,152,65,183,153],"251":[51,181,151,152,65,183,153],"254":[51,181,151,152,65,183,153]},"packages/nodes-base/utils/workflow-backtracking.ts":{"24":[51,181,151,152,65,153],"44":[51,181,151,152,65,153]},"packages/nodes-base/utils/workflowInputsResourceMapping/GenericFunctions.ts":{"27":[51,181,151,152,65,153],"29":[51,181,151,152,65,153],"74":[51,181,151,152,65,153],"81":[51,181,151,152,65,153],"121":[51,181,151,152,65,153],"126":[51,181,151,152,65,153],"147":[51,181,151,152,65,153],"155":[51,181,151,152,65,153],"157":[51,181,151,152,65,153],"160":[51,181,151,152,65,153],"168":[51,181,151,152,65,153],"183":[51,181,151,152,65,153]},"packages/workflow/src/node-parameters/path-utils.ts":{"10":[51,152,153]},"packages/frontend/@n8n/design-system/src/components/N8nIcon/nodes/execute-sub-workflow.svg":{"11":[99,151,152,153]},"packages/frontend/editor-ui/src/features/ndv/parameters/components/ResourceMapper/ResourceMapper.vue":{"51":[147,148,152,153],"91":[148],"111":[147,148,152,153],"113":[148],"131":[148],"148":[147,148,152,153],"150":[148],"162":[51,147,148,152,153],"198":[148],"238":[148],"242":[51,147,148,152,153],"249":[147,148,152,153],"253":[147,148,152,153],"257":[51,147,148,152,153],"261":[51,147,148,152,153],"269":[51,147,148,152,153],"279":[148],"289":[148],"305":[51,147,148,152,153],"308":[148],"316":[51,152,153],"323":[51,147,148,152,153],"351":[51,147,148,152,153],"376":[51,147,148,152,153],"399":[51,147,148,152,153],"409":[148],"410":[148],"443":[51,147,148,152,153],"446":[148],"466":[51,147,148,152,153],"479":[51,147,148,152,153],"485":[148],"496":[148],"516":[148],"522":[148],"530":[148],"531":[148],"548":[148],"595":[51,147,148,152,153],"607":[51,147,148,152,153],"613":[148],"624":[51,147,148,152,153],"657":[148],"660":[51,152,153],"693":[51,152,153]},"packages/frontend/editor-ui/src/features/ndv/parameters/components/WorkflowSelectorParameterInput/WorkflowSelectorParameterInput.vue":{"59":[51,152,153],"110":[51,152,153],"121":[51,152,153],"131":[51,152,153],"145":[51,152,153],"157":[51,152,153],"165":[51,152,153],"169":[51,152,153],"176":[152,153],"194":[153],"204":[51,153],"209":[51,153],"217":[153],"230":[51,152,153],"246":[51,152,153],"253":[152,153],"257":[51,152,153],"258":[153],"267":[51,152,153],"268":[51,152,153],"280":[51,152,153],"287":[51,153],"292":[51,153],"325":[51,152,153],"363":[51,153],"371":[51,152,153],"389":[51,152,153],"397":[51,152,153],"414":[51,152,153],"474":[51,152,153]},"packages/frontend/editor-ui/src/features/ndv/parameters/composables/useResourceLocatorDropdown.ts":{"8":[51,152,153],"15":[51,153],"23":[51,153],"27":[51,153]},"packages/frontend/editor-ui/src/features/ndv/parameters/composables/useResourceLocatorModes.ts":{"15":[51,152,153],"21":[51,152,153],"34":[51,152,153],"35":[51,152,153],"37":[153],"51":[51,152,153]},"packages/frontend/editor-ui/src/features/ndv/parameters/composables/useWorkflowResourcesLocator.ts":{"13":[51,152,153],"17":[51,153],"29":[51,152,153],"31":[51,152,153],"40":[152,153],"48":[51,153],"56":[51,152,153],"61":[51,152,153],"71":[51,152,153],"107":[51,152,153],"113":[153],"118":[51,153]},"packages/nodes-base/nodes/StopAndError/StopAndError.node.ts":{"82":[15,16,183]},"packages/nodes-base/nodes/StopAndError/utils.ts":{"14":[15,16,183],"18":[15,16,183]},"localhost-39697/assets/_plugin-vue_export-helper-Da88TEg1.js":{"2":[30,96,108,134,136,147]},"localhost-39697/assets/chunk-CC9Q-vWm.js":{"8":[30,96,108,134,136,147],"9":[30,96,108,134,136,147],"10":[30,96,108,134,136,147],"19":[30,96,108,134,136,147],"23":[30,96,108,134,136,147],"29":[30,96,108,134,136,147],"33":[30,96,108,134,136,147],"34":[30,96,108,134,136,147]},"localhost-39697/assets/verified-L2wNNiNu.js":{"8":[96,108]},"packages/frontend/@n8n/design-system/src/components/N8nIcon/nodes/ftp.svg":{"11":[96]},"localhost-38273/assets/_plugin-vue_export-helper-Da88TEg1.js":{"2":[16,17,94,103,109,61,126,159]},"localhost-38273/assets/chunk-CC9Q-vWm.js":{"8":[16,17,94,103,109,61,126,159],"9":[16,17,94,103,109,61,126,159],"10":[16,17,94,103,109,61,126,159],"19":[16,17,94,103,109,61,126,159],"23":[16,17,94,103,109,61,126,159],"29":[16,17,94,103,109,61,126,159],"33":[16,17,94,103,109,61,126,159],"34":[16,17,94,103,109,61,126,159]},"packages/frontend/@n8n/design-system/src/components/AskAssistantButton/AskAssistantButton.vue":{"8":[15,16,17],"26":[15,16,17]},"packages/frontend/@n8n/design-system/src/components/AskAssistantChat/messages/BaseMessage.vue":{"16":[15,16,17,18,19,20],"26":[15,16,17,18,19,20]},"packages/frontend/@n8n/design-system/src/components/AskAssistantChat/messages/CodeDiffMessage.vue":{"19":[16],"20":[16],"32":[16],"40":[16],"41":[16]},"packages/frontend/@n8n/design-system/src/components/AskAssistantChat/messages/MessageWrapper.vue":{"22":[15,16,17,18,19,20],"30":[15,16,17,18,19,20],"33":[15,16,17,18,19,20],"47":[16],"48":[16]},"packages/frontend/@n8n/design-system/src/components/AskAssistantChat/messages/TextMessage.vue":{"31":[18],"40":[15,16,17,18,19,20],"42":[15,16,17,18,19,20],"52":[15,16,17,18,19,20],"53":[15,16,17,18,19,20],"58":[15,16,17,18,19,20],"80":[15,16,17,18,19,20],"89":[15,16,17,18,19,20],"137":[18]},"packages/frontend/@n8n/design-system/src/components/AskAssistantChat/messages/ThinkingMessage.vue":{"14":[15,16,17,18,19,20],"21":[15,16,17,18,19,20],"23":[15,16,17,18,19,20],"32":[15,16,17,18,19,20],"38":[15,16,17,18,19,20],"48":[15,16,17,18,19,20],"64":[15,16,17,19,20],"65":[15,16,17,19,20]},"packages/frontend/@n8n/design-system/src/components/AskAssistantChat/messages/helpers.ts":{"10":[15,16,17,18,19,20]},"packages/frontend/@n8n/design-system/src/components/AskAssistantChat/messages/useMarkdown.ts":{"6":[15,16,17,18,19,20],"20":[15,16,17,18,19,20]},"packages/frontend/@n8n/design-system/src/components/BlinkingCursor/BlinkingCursor.vue":{"2":[15,16,17,18,19]},"packages/frontend/@n8n/design-system/src/components/CodeDiff/CodeDiff.vue":{"10":[16],"43":[16],"48":[16],"85":[16],"93":[16],"120":[16],"121":[16],"137":[16],"138":[16]},"packages/frontend/@n8n/design-system/src/components/InlineAskAssistantButton/InlineAskAssistantButton.vue":{"8":[15,16,17,18],"39":[15,16,17,18],"41":[15,16,17],"44":[15,16,17,18]},"packages/frontend/@n8n/design-system/src/components/N8nIcon/nodes/stop-and-error.svg":{"11":[15,16]},"packages/frontend/@n8n/design-system/src/types/assistant.ts":{"165":[15,16,17,18,19,20],"221":[15,16,17,18,19,20],"227":[15,16,17,18,19,20],"239":[15,16,17,18,19,20]},"packages/frontend/editor-ui/src/features/ai/assistant/components/Chat/AskAssistantChat.vue":{"13":[15,16,17,18],"27":[15,16,17,18],"32":[15,16,17,18],"34":[15,16,17,18],"39":[15,18],"50":[15,18],"65":[16],"72":[16],"80":[15,16,17,18],"84":[15,16,17,18],"87":[15,18],"96":[15],"108":[15,16,17,18]},"packages/frontend/editor-ui/src/features/ai/assistant/components/Chat/AskAssistantFloatingButton.vue":{"14":[15,16,17,48],"25":[15,16,17,48],"39":[48],"43":[48],"63":[15,16,17,48],"75":[48],"80":[15,16,17,48]},"packages/frontend/editor-ui/src/features/ai/assistant/components/Chat/AskModeEmptyState.vue":{"5":[15,16,17,18],"6":[15,16,17]},"packages/frontend/editor-ui/src/features/ai/assistant/composables/useCodeDiff.ts":{"31":[16],"41":[16],"43":[16],"64":[16],"68":[16],"74":[16],"87":[16],"133":[16]},"packages/cli/src/middlewares/list-query/dtos/credentials.filter.dto.ts":{"22":[17,29,8,34,35,50,51,58,119,63]},"packages/nodes-base/nodes/Google/Gmail/GenericFunctions.ts":{"30":[17,8,145,149],"146":[17,8,145,149],"209":[17,8,145,149],"233":[17,8,145,149],"255":[17,8,145,149],"278":[17,8,145,149],"286":[17,8,145,149],"340":[17,8,145,149],"397":[17,8,145,149],"405":[17,8,145,149],"425":[17,8,145,149],"465":[17,8,145,149],"498":[17,8,145,149],"499":[17,8,145,149],"509":[17,8,145,149],"514":[17,8,145,149],"518":[17,8,145,149],"520":[17,8,145,149],"527":[17,8,145,149],"539":[17,8,145,149],"556":[17,8,145,149]},"packages/nodes-base/nodes/Google/Gmail/Gmail.node.ts":{"8":[17,8,145]},"packages/nodes-base/nodes/Google/Gmail/utils/descriptions.ts":{"3":[17,8,145,149]},"packages/nodes-base/nodes/Google/Gmail/utils/replyToEmail.ts":{"16":[17,8,145],"71":[17,8,145],"76":[17,8,145],"95":[17,8,145],"98":[17,8,145],"107":[17,8,145]},"packages/nodes-base/nodes/Google/Gmail/v1/GmailV1.node.ts":{"134":[17,8,145],"147":[17,8,145],"260":[17,8,145],"267":[17,8,145],"275":[17,8,145],"347":[17,8,145],"354":[17,8,145],"362":[17,8,145],"417":[17,8,145],"421":[17,8,145],"587":[17,8,145],"595":[17,8,145],"603":[17,8,145]},"packages/nodes-base/nodes/Google/Gmail/v2/GmailV2.node.ts":{"157":[17,8,145],"174":[17,8,145]},"packages/nodes-base/nodes/Google/Gmail/v2/loadOptions.ts":{"5":[17,8,145],"29":[17,8,145]},"packages/nodes-base/nodes/Google/Gmail/v2/utils/draft.ts":{"7":[17,8,145],"14":[17,8,145],"21":[17,8,145],"32":[17,8,145]},"packages/nodes-base/nodes/Microsoft/GenericFunctions.ts":{"10":[17],"65":[17]},"packages/nodes-base/nodes/Microsoft/Outlook/MicrosoftOutlook.node.ts":{"8":[17]},"packages/nodes-base/nodes/Microsoft/Outlook/v1/GenericFunctions.ts":{"12":[17],"57":[17],"90":[17],"123":[17],"131":[17],"155":[17],"157":[17],"161":[17],"165":[17],"176":[17]},"packages/nodes-base/nodes/Microsoft/Outlook/v1/MicrosoftOutlookV1.node.ts":{"108":[17],"119":[17],"138":[17],"264":[17],"312":[17],"315":[17],"372":[17],"589":[17]},"packages/nodes-base/nodes/Microsoft/Outlook/v2/MicrosoftOutlookV2.node.ts":{"16":[17],"27":[17]},"packages/nodes-base/nodes/Microsoft/Outlook/v2/actions/calendar/create.operation.ts":{"94":[17]},"packages/nodes-base/nodes/Microsoft/Outlook/v2/actions/calendar/delete.operation.ts":{"19":[17]},"packages/nodes-base/nodes/Microsoft/Outlook/v2/actions/calendar/get.operation.ts":{"19":[17]},"packages/nodes-base/nodes/Microsoft/Outlook/v2/actions/calendar/getAll.operation.ts":{"38":[17]},"packages/nodes-base/nodes/Microsoft/Outlook/v2/actions/calendar/update.operation.ts":{"89":[17]},"packages/nodes-base/nodes/Microsoft/Outlook/v2/actions/contact/create.operation.ts":{"42":[17]},"packages/nodes-base/nodes/Microsoft/Outlook/v2/actions/contact/delete.operation.ts":{"19":[17]},"packages/nodes-base/nodes/Microsoft/Outlook/v2/actions/contact/get.operation.ts":{"55":[17]},"packages/nodes-base/nodes/Microsoft/Outlook/v2/actions/contact/getAll.operation.ts":{"80":[17],"103":[17]},"packages/nodes-base/nodes/Microsoft/Outlook/v2/actions/contact/update.operation.ts":{"30":[17]},"packages/nodes-base/nodes/Microsoft/Outlook/v2/actions/draft/create.operation.ts":{"206":[17],"223":[17]},"packages/nodes-base/nodes/Microsoft/Outlook/v2/actions/draft/delete.operation.ts":{"19":[17]},"packages/nodes-base/nodes/Microsoft/Outlook/v2/actions/draft/get.operation.ts":{"85":[17]},"packages/nodes-base/nodes/Microsoft/Outlook/v2/actions/draft/send.operation.ts":{"29":[17],"36":[17],"37":[17],"41":[17]},"packages/nodes-base/nodes/Microsoft/Outlook/v2/actions/draft/update.operation.ts":{"184":[17]},"packages/nodes-base/nodes/Microsoft/Outlook/v2/actions/event/create.operation.ts":{"180":[17],"222":[17],"225":[17]},"packages/nodes-base/nodes/Microsoft/Outlook/v2/actions/event/delete.operation.ts":{"20":[17]},"packages/nodes-base/nodes/Microsoft/Outlook/v2/actions/event/get.operation.ts":{"56":[17]},"packages/nodes-base/nodes/Microsoft/Outlook/v2/actions/event/getAll.operation.ts":{"125":[17]},"packages/nodes-base/nodes/Microsoft/Outlook/v2/actions/event/update.operation.ts":{"217":[17]},"packages/nodes-base/nodes/Microsoft/Outlook/v2/actions/folder/create.operation.ts":{"38":[17]},"packages/nodes-base/nodes/Microsoft/Outlook/v2/actions/folder/delete.operation.ts":{"20":[17]},"packages/nodes-base/nodes/Microsoft/Outlook/v2/actions/folder/get.operation.ts":{"39":[17]},"packages/nodes-base/nodes/Microsoft/Outlook/v2/actions/folder/getAll.operation.ts":{"68":[17]},"packages/nodes-base/nodes/Microsoft/Outlook/v2/actions/folder/update.operation.ts":{"30":[17]},"packages/nodes-base/nodes/Microsoft/Outlook/v2/actions/folderMessage/getAll.operation.ts":{"227":[17]},"packages/nodes-base/nodes/Microsoft/Outlook/v2/actions/message/delete.operation.ts":{"19":[17]},"packages/nodes-base/nodes/Microsoft/Outlook/v2/actions/message/get.operation.ts":{"115":[17]},"packages/nodes-base/nodes/Microsoft/Outlook/v2/actions/message/getAll.operation.ts":{"243":[17]},"packages/nodes-base/nodes/Microsoft/Outlook/v2/actions/message/move.operation.ts":{"22":[17]},"packages/nodes-base/nodes/Microsoft/Outlook/v2/actions/message/reply.operation.ts":{"222":[17]},"packages/nodes-base/nodes/Microsoft/Outlook/v2/actions/message/send.operation.ts":{"209":[17]},"packages/nodes-base/nodes/Microsoft/Outlook/v2/actions/message/sendAndWait.operation.ts":{"31":[17]},"packages/nodes-base/nodes/Microsoft/Outlook/v2/actions/message/update.operation.ts":{"173":[17]},"packages/nodes-base/nodes/Microsoft/Outlook/v2/actions/messageAttachment/add.operation.ts":{"54":[17]},"packages/nodes-base/nodes/Microsoft/Outlook/v2/actions/messageAttachment/download.operation.ts":{"30":[17]},"packages/nodes-base/nodes/Microsoft/Outlook/v2/actions/messageAttachment/get.operation.ts":{"62":[17],"79":[17]},"packages/nodes-base/nodes/Microsoft/Outlook/v2/actions/messageAttachment/getAll.operation.ts":{"62":[17],"77":[17]},"packages/nodes-base/nodes/Microsoft/Outlook/v2/actions/router.ts":{"16":[17]},"packages/nodes-base/nodes/Microsoft/Outlook/v2/helpers/utils.ts":{"64":[17],"106":[17],"146":[17],"148":[17],"156":[17],"182":[17],"184":[17],"206":[17],"207":[17],"215":[17],"223":[17],"241":[17],"257":[17],"266":[17],"278":[17],"284":[17],"285":[17],"327":[17],"352":[17],"356":[17],"360":[17]},"packages/nodes-base/nodes/Microsoft/Outlook/v2/methods/listSearch.ts":{"9":[17],"43":[17],"53":[17],"61":[17],"69":[17],"102":[17],"113":[17],"145":[17],"156":[17],"199":[17],"210":[17],"239":[17],"245":[17],"256":[17],"293":[17]},"packages/nodes-base/nodes/Microsoft/Outlook/v2/methods/loadOptions.ts":{"8":[17],"28":[17],"48":[17]},"packages/nodes-base/nodes/Microsoft/Outlook/v2/transport/index.ts":{"27":[17],"48":[17],"72":[17],"150":[17],"184":[17],"237":[17],"273":[17],"291":[17]},"localhost-38273/assets/verified-L2wNNiNu.js":{"8":[17,103,126]},"packages/frontend/editor-ui/src/app/components/CopyInput.vue":{"37":[135],"54":[17,8,35,103,80,135,147,68]},"packages/frontend/editor-ui/src/features/credentials/components/CredentialEdit/CredentialTypeSelector.vue":{"15":[17,8,35,147,68],"42":[17,8,35,147,68],"44":[17,8,35,147,68],"63":[17,8,35,147,68],"70":[17,8,35,147,68],"79":[17,8,35,147,68],"89":[17,8,35,147,68],"99":[17,8,35,147,68],"100":[17,8,35,147,68],"101":[17,8,35,147,68],"103":[17,8,35,147,68],"108":[17,8,35,147,68],"112":[17,8,35,147,68],"115":[17,8,35,147,68]},"packages/frontend/editor-ui/src/features/credentials/components/CredentialEdit/GoogleAuthButton.vue":{"2":[17,8,35,147]},"packages/frontend/editor-ui/src/features/credentials/components/CredentialsSelectModal.vue":{"16":[17,29,8,34,35,51,58,119,63],"32":[17,29,8,34,35,51,58,119,63],"37":[17,29,8,34,35,51,58,119,63],"44":[17,29,8,34,35,51,58,119,63],"52":[17,29,8,34,35,51,58,119,63],"53":[17,29,8,34,35,51,58,119,63],"56":[17,29,8,34,35,51,58,119,63],"59":[17,29,8,34,35,51,58,119,63],"62":[17,29,8,34,35,51,58,119,63],"66":[17,29,8,34,35,51,58,119,63],"70":[17,29,8,34,35,51,58,119,63],"98":[17,29,8,34,35,51,58,119,63],"111":[17,29,8,34,35,51,58,119,63],"116":[17,29,8,34,35,51,58,119,63],"132":[17,29,8,34,35,51,58,119,63],"134":[17,29,8,34,35,51,58,119,63],"136":[17,29,8,34,35,51,58,119,63],"146":[17,29,8,34,35,51,58,119,63]},"packages/frontend/editor-ui/src/features/credentials/views/CredentialsView.vue":{"66":[17,29,34,119,63],"83":[29,8,34,35,50,51,58,119,63],"93":[29,8,34,35,50,51,58,119,63],"96":[17,29,8,34,35,50,51,58,119,63],"97":[29,8,34,35,50,51,58,119,63],"122":[17,29,8,34,35,50,51,58,119,63],"124":[17,29,8,34,35,50,51,58,119,63],"140":[29,34,35,58],"144":[29,8,34,35,51,58,119,63],"152":[29,35,58],"161":[17,29,8,34,35,50,51,58,119,63],"172":[29,8,34,35,50,51,58,119,63],"195":[17,29,8,34,35,50,51,58,119,63],"206":[17,29,8,34,35,50,51,58,119,63],"231":[17,29,8,34,35,50,51,58,119,63],"260":[8,34,50,51,119,63],"264":[17,29,8,34,35,50,51,58,119,63],"266":[29,8,34,35,51,58,119,63],"279":[17,29,8,34,35,50,51,58,119,63],"281":[17,29,8,34,35,50,51,58,119,63],"282":[17,29,8,34,35,51,58,119,63],"291":[17,29,8,34,35,50,51,58,119,63],"301":[17,29,8,34,35,50,51,58,119,63],"320":[17,29,8,34,35,50,51,58,119,63],"330":[29,8,34,35,50,51,58,119,63]},"packages/@n8n/api-types/src/dto/data-table/list-data-table-content-query.dto.ts":{"12":[36,94],"39":[36,94]},"packages/@n8n/db/src/utils/transaction.ts":{"15":[3,4,5,36,94,117]},"packages/cli/src/modules/data-table/data-table-column.repository.ts":{"21":[36,94],"31":[36,94],"33":[36,94],"44":[36,94],"63":[36,94],"73":[36,94],"77":[36,94],"89":[36,94],"99":[36,94],"100":[36,94],"125":[36,94],"126":[36,94],"139":[36,94],"145":[36,94],"164":[36,94],"170":[36,94],"190":[36,94],"191":[36,94],"196":[36,94]},"packages/cli/src/modules/data-table/data-table-ddl.service.ts":{"23":[36,94],"25":[36,94],"30":[36,94],"44":[36,94],"45":[36,94],"53":[36,94],"64":[36,94],"71":[36,94],"72":[36,94],"80":[36,94],"86":[36,94],"91":[36,94],"97":[36,94],"102":[36,94],"109":[36,94]},"packages/cli/src/modules/data-table/data-table-rows.repository.ts":{"56":[36,94],"138":[36,94],"140":[36,94],"146":[36,94],"167":[36,94],"208":[36,94],"215":[36,94],"221":[36,94],"222":[36,94],"261":[36,94],"273":[36,94],"294":[36,94],"302":[36,94],"307":[36,94],"308":[36,94],"342":[36,94],"347":[36,94],"366":[36,94],"372":[36,94],"409":[36,94],"417":[36,94],"449":[36,94],"455":[36,94],"456":[36,94],"477":[36,94],"478":[36,94],"485":[36,94],"492":[36,94],"507":[36,94],"521":[36,94],"542":[36,94],"554":[36,94],"572":[36,94],"581":[36,94],"582":[36,94],"601":[36,94],"626":[36,94],"637":[36,94],"663":[36,94],"672":[36,94],"691":[36,94],"706":[36,94],"714":[36,94]},"packages/cli/src/modules/data-table/data-table.controller.ts":{"51":[36,94],"57":[36,94],"77":[36,94],"87":[36,94],"104":[36,94],"132":[36,94],"146":[36,94],"170":[36,94],"191":[36,94],"211":[36,94],"227":[36,94],"243":[36,94],"265":[36,94],"287":[36,94],"312":[36,94],"353":[36,94],"382":[36,94],"412":[36,94],"465":[36,94],"518":[36,94]},"packages/cli/src/modules/favorites/favorites.event-relay.ts":{"10":[3,4,91,33,94,49,67,160],"17":[3,4,91,33,94,49,67,160],"19":[3,4,91,33,94,49,67,160],"21":[3,4,91,33,94,49,67,160],"23":[3,4,91,33,94,49,67,160],"25":[3,4,91,33,94,49,67,160],"27":[3,4,91,33,94,49,67,160]},"packages/frontend/@n8n/design-system/src/components/N8nBreadcrumbs/Breadcrumbs.vue":{"34":[36,94,48,104,49,160],"59":[36,94,48,104,49,160],"65":[36,94,48,104,49,160],"69":[104],"73":[104],"74":[104],"81":[104],"107":[36,94,48,104,49,160],"108":[104],"114":[104],"123":[104],"124":[104],"142":[104],"143":[104],"158":[104],"172":[36,94,48,104,49,160],"211":[104],"212":[104],"238":[104],"245":[36,94,48,104,49,160],"257":[104],"258":[104],"259":[104],"261":[36,94,48,104,49,160]},"packages/frontend/editor-ui/src/features/core/dataTable/DataTableDetailsView.vue":{"49":[36,94],"51":[36,94],"69":[36,94],"88":[36],"96":[36],"103":[36],"111":[36],"143":[36,94],"150":[36,94],"157":[94],"200":[36,94],"203":[36,94],"207":[36,94],"218":[36,94]},"packages/frontend/editor-ui/src/features/core/dataTable/DataTableView.vue":{"64":[36,94],"65":[36,94],"73":[36,94],"75":[94],"82":[36,94],"84":[36,94],"110":[36,94],"130":[36,94],"135":[36,94],"152":[94],"171":[36,94],"175":[36,94],"176":[36,94],"211":[36,94],"221":[36,94]},"packages/frontend/editor-ui/src/features/core/dataTable/components/AddDataTableModal.vue":{"120":[36,94],"140":[36,94],"141":[36,94],"149":[36,94],"188":[36,94],"267":[36,94],"334":[36,94],"339":[36,94],"345":[36,94],"357":[36,94],"358":[36,94],"361":[36,94]},"packages/frontend/editor-ui/src/features/core/dataTable/components/DataTableActions.vue":{"54":[36,94],"55":[36,94],"57":[36,94],"92":[94],"154":[94]},"packages/frontend/editor-ui/src/features/core/dataTable/components/DataTableBreadcrumbs.vue":{"16":[36,94],"38":[36,94],"42":[36,94],"46":[36,94],"81":[36],"103":[36,94],"109":[36,94],"119":[36,94],"122":[36,94]},"packages/frontend/editor-ui/src/features/core/dataTable/components/DataTableCard.vue":{"34":[36,94],"44":[36,94],"49":[36,94],"50":[36,94],"55":[36,94],"64":[36,94],"67":[36,94],"74":[36,94],"81":[36,94],"93":[36,94],"105":[36,94],"114":[36,94],"120":[36,94]},"packages/frontend/editor-ui/src/features/core/dataTable/components/DownloadDataTableModal.vue":{"25":[36,94]},"packages/frontend/editor-ui/src/features/core/dataTable/components/ImportCsvModal.vue":{"21":[36,94],"143":[36,94],"154":[36,94]},"packages/frontend/editor-ui/src/features/core/dataTable/components/dataGrid/AddColumnButton.vue":{"31":[36,94],"48":[36,94],"67":[36,94],"69":[36],"72":[36],"78":[36],"114":[36],"123":[36],"129":[36],"142":[36,94],"154":[36,94],"160":[36,94],"174":[36],"184":[36],"217":[36],"221":[36],"222":[36],"243":[36]},"packages/frontend/editor-ui/src/features/core/dataTable/components/dataGrid/AddRowButton.vue":{"5":[36,94]},"packages/frontend/editor-ui/src/features/core/dataTable/components/dataGrid/DataTableTable.vue":{"49":[36,94],"51":[36,94],"69":[36,94],"90":[36,94],"117":[36,94],"120":[36],"128":[36],"132":[36],"136":[36],"140":[36,94],"157":[36],"162":[36,94],"163":[36],"193":[36,94]},"packages/frontend/editor-ui/src/features/core/dataTable/components/dataGrid/registerAgGridModulesOnce.ts":{"27":[36,94]},"packages/frontend/editor-ui/src/features/core/dataTable/composables/useAgGrid.ts":{"23":[36,94],"34":[36,94],"86":[36,94],"98":[36,94],"118":[36],"124":[36],"133":[36],"135":[36],"145":[36],"153":[36],"173":[36],"212":[36,94],"231":[36,94]},"packages/frontend/editor-ui/src/features/core/dataTable/composables/useDataTableColumnFilters.ts":{"19":[36,94]},"packages/frontend/editor-ui/src/features/core/dataTable/composables/useDataTableColumns.ts":{"40":[36,94],"59":[36,94],"66":[36],"100":[36],"126":[36,94],"135":[36,94],"162":[36,94],"164":[36,94],"217":[36,94],"221":[36],"222":[36],"229":[36],"237":[36],"245":[36],"247":[36],"248":[36]},"packages/frontend/editor-ui/src/features/core/dataTable/composables/useDataTableOperations.ts":{"58":[36,94],"114":[36],"115":[36],"133":[36],"136":[36],"205":[36],"229":[36],"239":[36],"255":[36],"278":[36],"331":[36,94],"355":[36],"391":[36]},"packages/frontend/editor-ui/src/features/core/dataTable/composables/useDataTablePagination.ts":{"11":[36,94],"17":[36,94],"21":[36],"32":[36]},"packages/frontend/editor-ui/src/features/core/dataTable/composables/useDataTableSelection.ts":{"5":[36,94],"13":[36,94],"18":[36],"19":[36,94],"23":[36],"27":[36],"36":[36,94]},"packages/frontend/editor-ui/src/features/core/dataTable/composables/useDataTableTypes.ts":{"8":[36,94],"9":[36,94],"18":[36,94],"25":[36,94]},"packages/frontend/editor-ui/src/features/core/dataTable/module.descriptor.ts":{"13":[36,94],"14":[36,94],"15":[36,94],"25":[36,94],"37":[94]},"packages/frontend/editor-ui/src/features/core/dataTable/typeGuards.ts":{"11":[36],"21":[36,94]},"packages/frontend/editor-ui/src/features/core/dataTable/utils/columnUtils.ts":{"26":[36],"29":[36],"35":[36,94],"36":[36,94],"51":[36,94],"52":[36,94],"65":[36,94],"66":[36,94],"94":[36,94],"95":[36],"119":[36],"126":[36,94],"135":[36],"141":[36],"149":[36,94],"156":[36,94],"163":[36,94],"170":[36,94],"177":[36],"184":[36,94],"191":[36,94],"198":[36,94],"205":[36,94],"212":[36,94],"219":[36,94],"229":[36,94],"241":[36],"253":[36]},"packages/nodes-base/nodes/N8nTrigger/N8nTrigger.node.ts":{"63":[184],"86":[184]},"packages/@n8n/blob-storage/src/fs-byte-store.ts":{"20":[0,21,168,3,33,46,103,135],"22":[0,21,168,3,33,46,103,135],"26":[0,21,168,3,33,46,103,135],"41":[0,21,168,3,33,46,103,135],"46":[0,21,168,3,33,46,103,135],"56":[0,21,168,3,33,46,103,135],"69":[0,21,168,3,33,46,103,135],"74":[0,21,168,3,33,46,103,135],"80":[0,21,168,3,33,46,103,135],"87":[0,21,168,3,33,46,103,135],"93":[0,21,168,3,33,46,103,135],"94":[0,21,168,3,33,46,103,135],"95":[0,21,168,3,33,46,103,135],"96":[0,21,168,3,33,46,103,135],"97":[0,21,168,3,33,46,103,135],"101":[0,21,168,3,33,46,103,135],"117":[0,21,168,3,33,46,103,135],"130":[0,21,168,3,33,46,103,135]},"packages/core/src/binary-data/binary-data.service.ts":{"26":"b:CQAgAAJAAAACAAAAgAAAAIAAgAAAASAA","32":"b:CQAgAAJAAAACAAAAgAAAAIAAgAAAASAA","36":"b:CQAgAAJAAAACAAAAgAAAAIAAgAAAASAA","43":"b:CQAgAAJAAAACAAAAgAAAAIAAgAAAASAA","52":"b:CQAgAAJAAAACAAAAgAAAAIAAgAAAASAA","65":"b:CQAgAAJAAAACAAAAgAAAAIAAgAAAASAA","71":"b:CQAgAAJAAAACAAAAgAAAAIAAgAAAASAA","102":"b:CQAgAAJAAAACAAAAgAAAAIAAgAAAASAA","133":"b:CQAgAAJAAAACAAAAgAAAAIAAgAAAASAA","139":"b:CQAgAAJAAAACAAAAgAAAAIAAgAAAASAA","149":"b:CQAgAAJAAAACAAAAgAAAAIAAgAAAASAA","155":"b:CQAgAAJAAAACAAAAgAAAAIAAgAAAASAA","161":"b:CQAgAAJAAAACAAAAgAAAAIAAgAAAASAA","169":"b:CQAgAAJAAAACAAAAgAAAAIAAgAAAASAA","198":"b:CQAgAAJAAAACAAAAgAAAAIAAgAAAASAA","204":"b:CQAgAAJAAAACAAAAgAAAAIAAgAAAASAA","207":"b:CQAgAAJAAAACAAAAgAAAAIAAgAAAASAA","227":"b:CQAgAAJAAAACAAAAgAAAAIAAgAAAASAA","239":"b:CQAgAAJAAAACAAAAgAAAAIAAgAAAASAA","243":"b:CQAgAAJAAAACAAAAgAAAAIAAgAAAASAA","251":"b:CQAgAAJAAAACAAAAgAAAAIAAgAAAASAA","263":"b:CQAgAAJAAAACAAAAgAAAAIAAgAAAASAA","269":"b:CQAgAAJAAAACAAAAgAAAAIAAgAAAASAA","270":"b:CQAgAAJAAAACAAAAgAAAAIAAgAAAASAA","283":"b:CQAgAAJAAAACAAAAgAAAAIAAgAAAASAA"},"packages/core/src/binary-data/blob.manager.ts":{"25":[0,21,168,3,33,46,103,135],"30":[0,21,168,3,33,46,103,135],"34":[0,21,168,3,33,46,103,135],"45":[0,21,168,3,33,46,103,135],"49":[0,21,168,3,33,46,103,135],"55":[0,21,168,3,33,46,103,135],"61":[0,21,168,3,33,46,103,135],"73":[0,21,168,3,33,46,103,135],"79":[0,21,168,3,33,46,103,135],"82":[0,21,168,3,33,46,103,135],"94":[0,21,168,3,33,46,103,135],"112":[0,21,168,3,33,46,103,135],"124":[0,21,168,3,33,46,103,135],"133":[0,21,168,3,33,46,103,135],"137":[0,21,168,3,33,46,103,135],"150":[0,21,168,3,33,46,103,135],"167":[0,21,168,3,33,46,103,135],"168":[0,21,168,3,33,46,103,135],"173":[0,21,168,3,33,46,103,135],"178":[0,21,168,3,33,46,103,135],"180":[0,21,168,3,33,46,103,135],"183":[0,21,168,3,33,46,103,135],"188":[0,21,168,3,33,46,103,135]},"packages/core/src/execution-engine/node-execution-context/utils/get-secrets-proxy.ts":{"4":"b:eggAAAykAAAQAAAAoAAAAAAACAAIAAAA","6":"b:eggAAAykAAAQAAAAoAAAAAAACAAIAAAA","25":"b:eggAAAykAAAQAAAAoAAAAAAACAAIAAAA","30":"b:eggAAAykAAAQAAAAoAAAAAAACAAIAAAA","50":"b:eggAAAykAAAQAAAAoAAAAAAACAAIAAAA","66":"b:eggAAAykAAAQAAAAoAAAAAAACAAIAAAA","69":"b:eggAAAykAAAQAAAAoAAAAAAACAAIAAAA","75":"b:eggAAAykAAAQAAAAoAAAAAAACAAIAAAA","78":"b:eggAAAykAAAQAAAAoAAAAAAACAAIAAAA"},"packages/nodes-base/nodes/Files/ConvertToFile/ConvertToFile.node.ts":{"107":[103]},"packages/nodes-base/nodes/Files/ConvertToFile/actions/iCall.operation.ts":{"16":[103]},"packages/nodes-base/nodes/Files/ConvertToFile/actions/spreadsheet.operation.ts":{"94":[103]},"packages/nodes-base/nodes/Files/ConvertToFile/actions/toBinary.operation.ts":{"105":[103]},"packages/nodes-base/nodes/Files/ConvertToFile/actions/toJson.operation.ts":{"90":[103],"102":[103]},"packages/nodes-base/nodes/Files/ConvertToFile/actions/toText.operation.ts":{"78":[103]},"packages/nodes-base/nodes/HttpRequest/GenericFunctions.ts":{"31":"b:AAEEgAQIAABAAAAAgEAAEEAAAAAIAgAA","40":"b:AAEEgAQIAABAAAAAgEAAEEAAAAAIAgAA","44":"b:AAEEgAQIAABAAAAAgEAAEEAAAAAIAgAA","45":"b:AAEEgAQIAABAAAAAgEAAEEAAAAAIAgAA","48":"b:AAEEgAQIAABAAAAAgEAAEEAAAAAIAgAA","54":"b:AAEEgAQIAABAAAAAgEAAEEAAAAAIAgAA","67":"b:AAEEgAQIAABAAAAAgEAAEEAAAAAIAgAA","94":"b:AAEEgAQIAABAAAAAgEAAEEAAAAAIAgAA","129":"b:AAEEgAQIAABAAAAAgEAAEEAAAAAIAgAA","131":"b:AAEEgAQIAABAAAAAgEAAEEAAAAAIAgAA","142":"b:AAEEgAQIAABAAAAAgEAAEEAAAAAIAgAA","238":"b:AAEEgAQIAABAAAAAgEAAEEAAAAAIAgAA","243":"b:AAEEgAQIAABAAAAAgEAAEEAAAAAIAgAA","248":"b:AAEEgAQIAABAAAAAgEAAEEAAAAAIAgAA","255":"b:AAEEgAQIAABAAAAAgEAAEEAAAAAIAgAA","281":"b:AAEEgAQIAABAAAAAgEAAEEAAAAAIAgAA","296":"b:AAEEgAQIAABAAAAAgEAAEEAAAAAIAgAA","298":"b:AAEEgAQIAABAAAAAgEAAEEAAAAAIAgAA","300":"b:AAEEgAQIAABAAAAAgEAAEEAAAAAIAgAA","312":"b:AAEEgAQIAABAAAAAgEAAEEAAAAAIAgAA"},"packages/nodes-base/nodes/HttpRequest/HttpRequest.node.ts":{"9":[18,70,31,8,34,163,103,110,124,134]},"packages/nodes-base/nodes/HttpRequest/V1/HttpRequestV1.node.ts":{"42":[18,70,31,8,34,163,103,110,124,134],"616":[18,70,31,8,34,163,103,110,124,134],"812":[18,70,31,8,34,163,103,110,124,134],"889":[18,70,31,8,34,163,103,110,124,134],"1003":[18,70,31,8,34,163,103,110,124,134],"1010":[18,70,31,8,34,163,103,110,124,134],"1015":[18,70,31,8,34,163,103,110,124,134],"1159":[18,70,31,8,34,163,103,110,124,134]},"packages/nodes-base/nodes/HttpRequest/V2/HttpRequestV2.node.ts":{"44":[18,70,31,8,34,163,103,110,124,134],"629":[18,70,31,8,34,163,103,110,124,134],"874":[18,70,31,8,34,163,103,110,124,134],"951":[18,70,31,8,34,163,103,110,124,134],"1068":[18,70,31,8,34,163,103,110,124,134],"1074":[18,70,31,8,34,163,103,110,124,134],"1079":[18,70,31,8,34,163,103,110,124,134],"1092":[18,70,31,8,34,163,103,110,124,134],"1235":[18,70,31,8,34,163,103,110,124,134]},"packages/nodes-base/nodes/HttpRequest/V3/Description.ts":{"1227":[18,70,31,8,34,163,103,110,124,134]},"packages/nodes-base/nodes/HttpRequest/V3/HttpRequestV3.node.ts":{"54":"b:AAEEgAQIAABAAAAAgEAAEEAAAAAIAgAA","61":"b:AAEEgAQIAABAAAAAgEAAEEAAAAAIAgAA","65":"b:AAEEgAQIAABAAAAAgEAAEEAAAAAIAgAA","72":"b:AAEEgAQIAABAAAAAgEAAEEAAAAAIAgAA","107":"b:AAEEgAQIAABAAAAAgEAAEEAAAAAIAgAA","390":"b:AAEEgAQIAABAAAAAgEAAEEAAAAAIAgAA","507":"b:AAEEgAQIAABAAAAAgEAAEEAAAAAIAgAA","631":"b:AAEEgAQIAABAAAAAgEAAEEAAAAAIAgAA","673":"b:AAEEgAQIAABAAAAAgEAAEEAAAAAIAgAA","733":"b:AAEEgAQIAABAAAAAgEAAEEAAAAAIAgAA","752":"b:AAEEgAQIAABAAAAAgEAAEEAAAAAIAgAA","763":"b:AAEEgAQIAABAAAAAgEAAEEAAAAAIAgAA","768":"b:AAEEgAQIAABAAAAAgEAAEEAAAAAIAgAA","783":"b:AAEEgAQIAABAAAAAgEAAEEAAAAAIAgAA","789":"b:AAEEgAQIAABAAAAAgEAAEEAAAAAIAgAA","812":"b:AAEEgAQIAABAAAAAgEAAEEAAAAAIAgAA","814":"b:AAEEgAQIAABAAAAAgEAAEEAAAAAIAgAA","815":"b:AAEEgAQIAABAAAAAgEAAEEAAAAAIAgAA","959":"b:AAEEgAQIAABAAAAAgEAAEEAAAAAIAgAA","1121":"b:AAEEgAQIAABAAAAAgEAAEEAAAAAIAgAA","1184":"b:AAEEgAQIAABAAAAAgEAAEEAAAAAIAgAA"},"packages/nodes-base/nodes/HttpRequest/V3/utils/binaryData.ts":{"3":[18,70,31,8,34,163,103,110,124,134]},"packages/nodes-base/nodes/HttpRequest/V3/utils/buffer-decoding.ts":{"13":"b:AAEEgAQIAABAAAAAgEAAEEAAAAAIAgAA","36":"b:AAEEgAQIAABAAAAAgEAAEEAAAAAIAgAA"},"packages/nodes-base/nodes/HttpRequest/V3/utils/error-details.ts":{"13":[18,70,31,8,34,163,103,110,124,134],"35":[18,70,31,8,34,163,103,110,124,134],"48":[18,70,31,8,34,163,103,110,124,134]},"packages/nodes-base/nodes/HttpRequest/V3/utils/parse.ts":{"1":[18,70,31,8,34,163,103,110,124,134]},"packages/nodes-base/nodes/HttpRequest/shared/optimizeResponse.ts":{"20":"b:AAEEgAQIAABAAAAAgEAAEEAAAAAIAgAA","37":"b:AAEEgAQIAABAAAAAgEAAEEAAAAAIAgAA","38":"b:AAEEgAQIAABAAAAAgEAAEEAAAAAIAgAA","42":"b:AAEEgAQIAABAAAAAgEAAEEAAAAAIAgAA","55":"b:AAEEgAQIAABAAAAAgEAAEEAAAAAIAgAA","63":"b:AAEEgAQIAABAAAAAgEAAEEAAAAAIAgAA","92":"b:AAEEgAQIAABAAAAAgEAAEEAAAAAIAgAA","97":"b:AAEEgAQIAABAAAAAgEAAEEAAAAAIAgAA","127":"b:AAEEgAQIAABAAAAAgEAAEEAAAAAIAgAA","128":"b:AAEEgAQIAABAAAAAgEAAEEAAAAAIAgAA","179":"b:AAEEgAQIAABAAAAAgEAAEEAAAAAIAgAA","194":"b:AAEEgAQIAABAAAAAgEAAEEAAAAAIAgAA","226":"b:AAEEgAQIAABAAAAAgEAAEEAAAAAIAgAA","255":"b:AAEEgAQIAABAAAAAgEAAEEAAAAAIAgAA"},"packages/nodes-base/nodes/ICalendar/createEvent.operation.ts":{"275":[103]},"packages/nodes-base/nodes/RespondToWebhook/RespondToWebhook.node.ts":{"359":[24,168,103,65,155],"367":[24,168,103,65,155],"391":[24,168,103,65,155],"471":[24,168,103,65,155],"537":[24,168,103,65,155]},"packages/nodes-base/nodes/RespondToWebhook/utils/binary.ts":{"5":[24,103,65,155],"16":[24,103,65,155]},"packages/nodes-base/nodes/RespondToWebhook/utils/outputs.ts":{"1":[24,103,65,155]},"packages/nodes-base/utils/binary.ts":{"37":[42,46,103],"87":[42,46,103],"139":[42,46,103],"155":[42,46,103],"189":[42,46,103],"218":[42,46,103],"221":[42,46,103],"230":[42,46,103],"233":[42,46,103]},"packages/frontend/@n8n/design-system/src/components/N8nIcon/nodes/http-request.svg":{"11":"b:AiUEkAQIAARAAAAigMCAEEAABAACAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nPulse/Pulse.vue":{"6":[23,101,103,135]},"packages/frontend/editor-ui/src/features/ndv/parameters/components/ImportCurlParameter.vue":{"11":"b:ACEEgAQIAABAAAACgMAAEEAABAAAAAAA","16":"b:ACEEgAQIAABAAAACgMAAEEAABAAAAAAA"},"packages/cli/src/eventbus/event-message-classes/event-message-generic.ts":{"23":[59,60,61],"31":[59,60,61],"36":[59,60,61]},"packages/cli/src/modules/log-streaming.ee/create-message-event-bus-destination.ts":{"17":[59,60,61]},"packages/cli/src/modules/log-streaming.ee/destinations/message-event-bus-destination-sentry.ee.ts":{"16":[61],"36":[61],"56":[61],"95":[61],"105":[61],"119":[61],"123":[61]},"packages/cli/src/modules/log-streaming.ee/destinations/message-event-bus-destination-webhook.ee.ts":{"31":[59,61],"81":[59,61],"116":[59,61],"125":[59,61],"130":[59,61],"142":[59,61],"170":[59,61],"171":[59,61],"188":[59,61],"206":[59,61],"265":[59,61],"299":[59,61],"315":[59,61]},"packages/cli/src/modules/log-streaming.ee/destinations/message-event-bus-destination.ee.ts":{"49":[59,60,61],"84":[59,60,61],"88":[59,60,61],"98":[59,60,61],"117":[59,60,61],"125":[59,60,61],"130":[59,60,61],"134":[59,60,61]},"packages/cli/src/modules/log-streaming.ee/log-streaming-destination.service.ts":{"38":[59,60,61],"51":[59,60,61],"75":[59,60,61],"90":[59,60,61],"97":[59,60,61],"122":[59,60,61],"142":[59,60,61],"161":[59,60,61],"188":[59,60,61],"193":[59,60,61],"194":[59,60,61],"201":[59,60,61],"209":[59,60,61],"220":[59,60,61],"221":[59,60,61],"229":[59,60,61],"238":[59,60,61],"263":[59,60,61]},"packages/cli/src/modules/log-streaming.ee/log-streaming.controller.ts":{"23":[59,60,115,61],"30":[59,60,115,61],"39":[59,60,115,61],"46":[59,60,115,61],"57":[59,60,115,61],"80":[59,60,115,61],"91":[59,60,115,61]},"packages/cli/src/scaling/pubsub/publisher.service.ts":{"35":[25,38,165,167,57,179,59,60,61,118],"56":[25,38,165,167,57,179,59,60,61,118],"61":[25,38,165,167,57,179,59,60,61,118],"70":[25,38,165,167,57,179,59,60,61,118],"99":[25,38,165,167,57,179,59,60,61,118],"106":[25,38,165,167,57,179,59,60,61,118],"123":[25,38,165,167,57,179,59,60,61,118],"128":[25,38,165,167,57,179,59,60,61,118],"132":[25,38,165,167,57,179,59,60,61,118],"136":[25,38,165,167,57,179,59,60,61,118],"140":[25,38,165,167,57,179,59,60,61,118]},"packages/cli/src/utils/circuit-breaker.ts":{"15":[59,60,61],"147":[59,60,61],"172":[59,60,61],"176":[59,60,61],"180":[59,60,61],"184":[59,60,61],"191":[59,60,61],"197":[59,60,61],"206":[59,60,61],"234":[59,60,61],"269":[59,60,61],"270":[59,60,61]},"packages/cli/src/utils/sliding-window.ts":{"31":[59,60,61],"42":[59,60,61],"57":[59,60,61],"60":[59,60,61],"67":[59,60,61]},"packages/frontend/editor-ui/src/features/integrations/logStreaming.ee/components/EventDestinationCard.vue":{"44":[59,115,61],"52":[59,115,61],"56":[59,115,61],"72":[59,115,61],"76":[59,115,61],"87":[59,61],"100":[59],"106":[59],"110":[61],"135":[59,115,61],"141":[59,115,61],"144":[59,115,61],"148":[59,115,61],"153":[59,115,61],"157":[59,115,61],"160":[59,115,61],"176":[59]},"packages/frontend/editor-ui/src/features/integrations/logStreaming.ee/components/EventDestinationSettingsModal.vue":{"77":[59,115,61],"88":[59,115,61],"108":[59,115,61],"122":[59,115,61],"126":[59,115,61],"130":[115,61],"134":[115,61],"138":[59,115,61],"140":[59,115,61],"144":[59,115,61],"162":[59,115,61],"166":[59,115,61],"186":[59,115,61],"190":[59,115,61],"205":[59,115,61],"209":[59,115,61],"218":[59,115,61],"224":[59,115,61],"232":[59,115,61],"236":[59,115,61],"262":[59,115,61],"301":[59,115,61],"306":[61],"330":[61],"342":[59,115,61],"371":[59,115,61],"379":[59,115,61],"395":[59,115,61],"412":[59,115,61],"454":[59,115,61],"464":[59,115,61],"472":[59,115,61],"474":[59,115,61],"486":[59,115,61],"495":[59,115,61]},"packages/frontend/editor-ui/src/features/integrations/logStreaming.ee/logStreaming.store.ts":{"44":[59,115,61],"53":[59,115,61],"81":[59,115,61],"99":[59,115,61],"105":[59,115,61],"128":[59,115,61],"136":[59,115,61],"151":[59,115,61],"155":[59,115,61],"163":[59,115,61],"220":[59,115,61],"226":[61],"231":[59,115,61],"248":[59,115,61],"265":[59,115,61],"276":[59,115,61],"280":[59,115,61],"284":[61]},"packages/frontend/editor-ui/src/features/integrations/logStreaming.ee/logStreaming.utils.ts":{"13":[59,115,61],"33":[59,115,61]},"packages/frontend/editor-ui/src/features/integrations/logStreaming.ee/views/SettingsLogStreamingView.vue":{"37":[59,115,61],"45":[59,115,61],"50":[59,115,61],"54":[59,115,61],"58":[59,115,61],"60":[59,115,61],"73":[59,115,61],"75":[61],"88":[59,115,61],"94":[59,115,61],"98":[59,115,61],"103":[61],"108":[59,115,61],"133":[59,115,61],"137":[59,115,61],"152":[61],"161":[59,61],"182":[59,115,61],"204":[59,115,61],"208":[59,115,61],"214":[61],"215":[59,61],"230":[59,115,61],"248":[61]},"packages/nodes-base/nodes/N8n/GenericFunctions.ts":{"21":[99,126],"56":[99,126],"93":[99,126],"94":[99,126],"107":[99,126],"111":[99,126],"146":[99,126],"150":[99,126],"186":[99,126],"210":[99,126]},"packages/nodes-base/nodes/N8n/WorkflowLocator.ts":{"18":[99,126],"32":[99,126],"37":[99,126],"42":[99,126]},"packages/frontend/@n8n/design-system/src/components/N8nIcon/nodes/n8n.svg":{"11":[99,126]},"packages/frontend/editor-ui/src/app/composables/useIntersectionObserver.ts":{"45":[1,126,133,134,64,157,158],"56":[64,157],"62":[64,157],"82":[1,126,133,134,157,158]},"packages/nodes-base/nodes/Hubspot/HubspotTrigger.node.ts":{"17":[159,161],"320":[159,161],"323":[159,161],"326":[159,161],"329":[159,161],"337":[159,161],"384":[159,161],"418":[159,161],"447":[159,161]},"packages/nodes-base/nodes/Hubspot/V1/GenericFunctions.ts":{"15":[159,161],"78":[159,161],"123":[159,161],"144":[159,161],"155":[159,161],"1953":[159,161],"1955":[159,161],"1960":[159,161],"1963":[159,161],"1978":[159,161],"1987":[159,161],"1997":[159,161],"2008":[159,161],"2024":[159,161]},"packages/nodes-base/nodes/SplitInBatches/SplitInBatches.node.ts":{"9":[97,53,55,6,139,159]},"packages/nodes-base/nodes/SplitInBatches/v1/SplitInBatchesV1.node.ts":{"62":[97,53,55,6,139,159],"97":[97,53,55,6,139,159],"111":[97,53,55,6,139,159],"128":[97,53,55,6,139,159]},"packages/nodes-base/nodes/SplitInBatches/v2/SplitInBatchesV2.node.ts":{"64":[97,53,55,6,139,159],"102":[97,53,55,6,139,159],"116":[97,53,55,6,139,159],"135":[97,53,55,6,139,159],"147":[97,53,55,6,139,159]},"packages/nodes-base/nodes/SplitInBatches/v3/SplitInBatchesV3.node.ts":{"88":[97,53,55,6,139,159],"126":[97,53,55,6,139,159],"140":[97,53,55,6,139,159],"159":[97,53,55,6,139,159],"171":[97,53,55,6,139,159]},"packages/nodes-base/nodes/Switch/Switch.node.ts":{"9":[125,139,144,145,159]},"packages/nodes-base/nodes/Switch/V1/SwitchV1.node.ts":{"15":[125,139,144,145,159],"523":[125,139,144,145,159],"538":[125,139,144,145,159],"540":[125,139,144,145,159],"542":[125,139,144,145,159],"544":[125,139,144,145,159],"546":[125,139,144,145,159],"548":[125,139,144,145,159],"550":[125,139,144,145,159],"551":[125,139,144,145,159],"552":[125,139,144,145,159],"554":[125,139,144,145,159],"556":[125,139,144,145,159],"558":[125,139,144,145,159],"560":[125,139,144,145,159],"562":[125,139,144,145,159],"564":[125,139,144,145,159],"578":[125,139,144,145,159],"595":[125,139,144,145,159],"616":[125,139,144,145,159]},"packages/nodes-base/nodes/Switch/V2/SwitchV2.node.ts":{"17":[125,139,144,145,159],"521":[125,139,144,145,159],"523":[125,139,144,145,159],"538":[125,139,144,145,159],"553":[125,139,144,145,159],"555":[125,139,144,145,159],"557":[125,139,144,145,159],"559":[125,139,144,145,159],"561":[125,139,144,145,159],"563":[125,139,144,145,159],"565":[125,139,144,145,159],"566":[125,139,144,145,159],"567":[125,139,144,145,159],"569":[125,139,144,145,159],"571":[125,139,144,145,159],"573":[125,139,144,145,159],"575":[125,139,144,145,159],"577":[125,139,144,145,159],"579":[125,139,144,145,159],"593":[125,139,144,145,159],"610":[125,139,144,145,159],"631":[125,139,144,145,159],"652":[125,139,144,145,159],"662":[125,139,144,145,159]},"packages/nodes-base/nodes/Switch/V3/SwitchV3.node.ts":{"21":[125,139,144,145,159],"25":[125,139,144,145,159],"31":[125,139,144,145,159],"51":[125,139,144,145,159],"347":[125,139,144,145,159],"377":[125,139,144,145,159],"383":[125,139,144,145,159],"409":[125,139,144,145,159],"422":[125,139,144,145,159]},"packages/frontend/editor-ui/src/app/components/ImportWorkflowUrlModal.vue":{"10":[130,159],"16":[130,159],"20":[130,159],"24":[159],"29":[130,159],"32":[130,159],"45":[130,159],"60":[130,159],"68":[130,159],"76":[130,159]},"localhost-38115/assets/_plugin-vue_export-helper-Da88TEg1.js":{"2":[84,12,114,120,140,154]},"localhost-38115/assets/chunk-CC9Q-vWm.js":{"8":[84,12,114,120,140,154],"9":[84,12,114,120,140,154],"10":[84,12,114,120,140,154],"19":[84,12,114,120,140,154],"23":[84,12,114,120,140,154],"29":[84,12,114,120,140,154],"33":[84,12,114,120,140,154],"34":[84,12,114,120,140,154]},"packages/frontend/editor-ui/src/app/composables/useReportWorkflowFailuresToParent.ts":{"33":[84,85,122,123]},"packages/frontend/editor-ui/src/app/layouts/DemoLayout.vue":{"18":[84,85,122,123],"61":[84,85,122,123],"88":[84,85,122,123]},"packages/frontend/editor-ui/src/features/execution/logs/components/DemoFooter.vue":{"9":[84,85,122,123],"10":[84,85,122,123],"11":[84,85,122,123]},"packages/@n8n/backend-network/src/http/axios/legacy.ts":{"33":[18,31,34,169,43,47,134,137],"53":[18,31,34,169,43,47,134,137],"74":[18,31,34,169,43,47,134,137],"139":[18,31,34,169,43,47,134,137],"188":[18,31,34,169,43,47,134,137],"208":[18,31,34,169,43,47,134,137],"212":[18,31,34,169,43,47,134,137],"221":[18,31,34,169,43,47,134,137],"251":[18,31,34,169,43,47,134,137],"262":[18,31,34,169,43,47,134,137],"306":[18,31,34,169,43,47,134,137],"314":[18,31,34,169,43,47,134,137],"327":[18,31,34,169,43,47,134,137]},"packages/@n8n/backend-network/src/http/binary-buffer.ts":{"16":[0,18,31,3,169,43,46,134],"20":[0,18,31,3,169,43,46,134],"25":[0,18,31,3,169,43,46,134],"29":[0,18,31,3,169,43,46,134],"32":[0,18,31,3,169,43,46,134],"34":[0,18,31,3,169,43,46,134],"42":[0,18,31,3,169,43,46,134],"46":[0,18,31,3,169,43,46,134],"47":[0,18,31,3,169,43,46,134],"48":[0,18,31,3,169,43,46,134],"58":[0,18,31,3,169,43,46,134],"59":[0,18,31,3,169,43,46,134],"61":[0,18,31,3,169,43,46,134],"81":[0,18,31,3,169,43,46,134]},"packages/@n8n/backend-network/src/http/binary-string.ts":{"12":[18,31,169,43,134]},"packages/@n8n/backend-network/src/http/legacy-request.ts":{"42":[18,31,34,169,43,47,134,137]},"packages/@n8n/backend-network/src/http/parse-incoming-message.ts":{"3":[22,23,31,101,47],"5":[22,23,31,101,47],"55":[22,23,31,101,47],"77":[22,23,31,101,47],"108":[22,23,31,101,47]},"packages/core/src/execution-engine/node-execution-context/utils/request-helpers/legacy-request-adapter.ts":{"15":[18,31,34,169,43,47,134,137],"32":[18,31,34,169,43,47,134,137]},"localhost-45663/assets/_plugin-vue_export-helper-Da88TEg1.js":{"2":[31]},"localhost-45663/assets/chunk-CC9Q-vWm.js":{"8":[31],"9":[31],"10":[31],"19":[31],"23":[31],"29":[31],"33":[31],"34":[31]},"packages/frontend/editor-ui/src/app/composables/usePushConnection/handlers/sendConsoleMessage.ts":{"7":[18,31,34,43,48,134]},"packages/@n8n/ai-utilities/src/model-discovery/index.ts":{"39":[1,3,4,5,12,6,13],"43":[1,3,4,5,12,6,13],"58":[1,3,4,5,12,6,13],"64":[1,3,4,5,12,6,13]},"packages/@n8n/ai-utilities/src/model-discovery/providers/anthropic.ts":{"5":[1,3,4,5,12,6,13],"15":[1,3,4,5,12,6,13],"16":[1,3,4,5,12,6,13]},"packages/@n8n/ai-utilities/src/model-discovery/providers/cohere.ts":{"5":[1,3,4,5,12,6,13],"14":[1,3,4,5,12,6,13],"15":[1,3,4,5,12,6,13]},"packages/@n8n/ai-utilities/src/model-discovery/providers/google.ts":{"11":[1,3,4,5,12,6,13],"22":[1,3,4,5,12,6,13],"27":[1,3,4,5,12,6,13]},"packages/@n8n/ai-utilities/src/model-discovery/providers/groq.ts":{"5":[1,3,4,5,12,6,13],"14":[1,3,4,5,12,6,13]},"packages/@n8n/ai-utilities/src/model-discovery/providers/mistral.ts":{"5":[1,3,4,5,12,6,13],"14":[1,3,4,5,12,6,13]},"packages/@n8n/ai-utilities/src/model-discovery/providers/nvidia.ts":{"16":[1,3,4,5,12,6,13],"25":[1,3,4,5,12,6,13]},"packages/@n8n/ai-utilities/src/model-discovery/providers/openai.ts":{"10":[1,3,4,5,12,6,13],"29":[1,3,4,5,12,6,13],"37":[1,3,4,5,12,6,13]},"packages/@n8n/ai-utilities/src/model-discovery/request.ts":{"4":[1,3,4,5,12,6,13],"16":[1,3,4,5,12,6,13],"25":[1,3,4,5,12,6,13],"29":[1,3,4,5,12,6,13],"33":[1,3,4,5,12,6,13],"42":[1,3,4,5,12,6,13],"44":[1,3,4,5,12,6,13],"45":[1,3,4,5,12,6,13],"52":[1,3,4,5,12,6,13],"53":[1,3,4,5,12,6,13]},"packages/@n8n/ai-utilities/src/utils/failed-attempt-handler/n8nLlmFailedAttemptHandler.ts":{"13":[1,3,4,5,93,6],"17":[1,3,4,5,93,6]},"packages/@n8n/ai-utilities/src/utils/http-proxy-agent.ts":{"66":[1,3,4,5,6],"106":[1,3,4,5,6],"129":[1,3,4,5,6]},"packages/@n8n/ai-utilities/src/utils/n8n-llm-tracing.ts":{"56":[1,3,4,5,93,6],"94":[1,3,4,5,93,6],"109":[1,3,4,5,93,6],"112":[1,3,4,5,93,6],"124":[1,3,4,5,93,6],"125":[1,3,4,5,93,6],"129":[1,3,4,5,93,6],"134":[1,3,4,5,93,6],"139":[1,3,4,5,93,6],"140":[1,3,4,5,93,6],"190":[1,3,4,5,93,6],"215":[1,3,4,5,93,6],"251":[1,3,4,5,93,6],"259":[1,3,4,5,93,6],"292":[1,3,4,5,93,6],"296":[1,3,4,5,93,6]},"packages/@n8n/ai-utilities/src/utils/redact-headers.ts":{"13":[1,3,4,5,6],"29":[1,3,4,5,6],"32":[1,3,4,5,6],"58":[1,3,4,5,6],"64":[1,3,4,5,6]},"packages/@n8n/ai-utilities/src/utils/tokenizer/tiktoken.ts":{"9":[1,3,4,5,6],"15":[1,3,4,5,6],"18":[1,3,4,5,6],"34":[1,3,4,5,6],"43":[1,3,4,5,6]},"packages/@n8n/ai-utilities/src/utils/tokenizer/token-estimator.ts":{"33":[1,3,4,5,6],"70":[1,3,4,5,6],"129":[1,3,4,5,6],"141":[1,3,4,5,6],"169":[1,3,4,5,6]},"packages/@n8n/backend-network/src/proxy/index.ts":{"19":[1,3,4,5,163,6]},"packages/@n8n/backend-network/src/proxy/proxy-resolution.ts":{"20":[1,3,4,5,163,6],"35":[1,3,4,5,163,6],"57":[1,3,4,5,163,6],"72":[1,3,4,5,163,6],"80":[1,3,4,5,163,6]},"packages/@n8n/chat-hub/src/artifact.ts":{"3":[3,4,5,91,93,33],"19":[3,4,5]},"packages/@n8n/chat-hub/src/parser.ts":{"12":[3,4,5,91,93,33],"117":[3,4,5,91,93,33],"134":[3,4,5,91,93,33],"159":[3,4,5],"187":[3,4,5],"217":[3,4,5],"250":[3,4,5,91,93,33],"275":[3,4,5]},"packages/@n8n/db/src/repositories/project-secrets-provider-access.repository.ee.ts":{"9":[3,4,91,58],"12":[3,4,91,58],"21":[3,4,91,58],"28":[3,4,91,58],"35":[3,4,91,58],"43":[3,4,91,58],"54":[3,4,91,58]},"packages/@n8n/nodes-langchain/nodes/agents/Agent/Agent.node.ts":{"9":[1,3,4,5,8,10,12,6]},"packages/@n8n/nodes-langchain/nodes/agents/Agent/V1/AgentV1.node.ts":{"37":[1,3,4,5,8,10,12,6],"53":[1,3,4,5,8,10,12,6],"63":[1,3,4,5,8,10,12,6],"221":[1,3,4,5,8,10,12,6],"276":[1,3,4,5,8,10,12,6],"361":[1,3,4,5,8,10,12,6],"470":[1,3,4,5,8,10,12,6]},"packages/@n8n/nodes-langchain/nodes/agents/Agent/V2/AgentV2.node.ts":{"24":[1,3,4,5,8,10,12,6],"141":[1,3,4,5,8,10,12,6]},"packages/@n8n/nodes-langchain/nodes/agents/Agent/V3/AgentV3.node.ts":{"28":[1,3,4,5,8,10,12,6],"151":[1,3,4,5,8,10,12,6]},"packages/@n8n/nodes-langchain/nodes/agents/Agent/agents/ConversationalAgent/execute.ts":{"16":[1,3,4,5,8,10,12,6]},"packages/@n8n/nodes-langchain/nodes/agents/Agent/agents/OpenAiFunctionsAgent/execute.ts":{"15":[1,3,4,5,8,10,12,6]},"packages/@n8n/nodes-langchain/nodes/agents/Agent/agents/PlanAndExecuteAgent/execute.ts":{"18":[1,3,4,5,8,10,12,6]},"packages/@n8n/nodes-langchain/nodes/agents/Agent/agents/ReActAgent/execute.ts":{"21":[1,3,4,5,8,10,12,6]},"packages/@n8n/nodes-langchain/nodes/agents/Agent/agents/SqlAgent/execute.ts":{"25":[1,3,4,5,8,10,12,6],"28":[1,3,4,5,8,10,12,6],"29":[1,3,4,5,8,10,12,6],"43":[1,3,4,5,8,10,12,6],"55":[1,3,4,5,8,10,12,6],"144":[1,3,4,5,8,10,12,6],"150":[1,3,4,5,8,10,12,6]},"packages/@n8n/nodes-langchain/nodes/agents/Agent/agents/SqlAgent/other/handlers/mysql.ts":{"4":[1,3,4,5,8,10,12,6]},"packages/@n8n/nodes-langchain/nodes/agents/Agent/agents/SqlAgent/other/handlers/postgres.ts":{"6":[1,3,4,5,8,10,12,6]},"packages/@n8n/nodes-langchain/nodes/agents/Agent/agents/SqlAgent/other/handlers/sqlite.ts":{"8":[1,3,4,5,8,10,12,6],"38":[1,3,4,5,8,10,12,6]},"packages/@n8n/nodes-langchain/nodes/agents/Agent/agents/ToolsAgent/V1/execute.ts":{"36":[1,3,4,5,8,10,12,6]},"packages/@n8n/nodes-langchain/nodes/agents/Agent/agents/ToolsAgent/V2/description.ts":{"15":[1,3,4,5,8,10,12,6]},"packages/@n8n/nodes-langchain/nodes/agents/Agent/agents/ToolsAgent/V2/execute.ts":{"45":[1,3,4,5,8,10,12,6],"93":[1,3,4,5,8,10,12,6],"98":[1,3,4,5,8,10,12,6],"103":[1,3,4,5,8,10,12,6],"133":[1,3,4,5,8,10,12,6],"201":[1,3,4,5,8,10,12,6],"217":[1,3,4,5,8,10,12,6],"241":[1,3,4,5,8,10,12,6],"298":[1,3,4,5,8,10,12,6],"407":[1,3,4,5,8,10,12,6]},"packages/@n8n/nodes-langchain/nodes/agents/Agent/agents/ToolsAgent/V3/execute.ts":{"29":[1,3,4,5,8,10,12,6],"54":[1,3,4,5,8,10,12,6],"134":[1,3,4,5,8,10,12,6]},"packages/@n8n/nodes-langchain/nodes/agents/Agent/agents/ToolsAgent/V3/helpers/buildExecutionContext.ts":{"29":[1,3,4,5,8,10,12,6]},"packages/@n8n/nodes-langchain/nodes/agents/Agent/agents/ToolsAgent/V3/helpers/checkMaxIterations.ts":{"27":[1,3,4,5,8,10,12,6]},"packages/@n8n/nodes-langchain/nodes/agents/Agent/agents/ToolsAgent/V3/helpers/createAgentSequence.ts":{"25":[1,3,4,5,8,10,12,6],"66":[1,3,4,5,8,10,12,6]},"packages/@n8n/nodes-langchain/nodes/agents/Agent/agents/ToolsAgent/V3/helpers/executeBatch.ts":{"40":[1,3,4,5,8,10,12,6],"78":[1,3,4,5,8,10,12,6],"107":[1,3,4,5,8,10,12,6]},"packages/@n8n/nodes-langchain/nodes/agents/Agent/agents/ToolsAgent/V3/helpers/finalizeResult.ts":{"21":[1,3,4,5,8,10,12,6]},"packages/@n8n/nodes-langchain/nodes/agents/Agent/agents/ToolsAgent/V3/helpers/index.ts":{"11":[1,3,4,5,8,10,12,6]},"packages/@n8n/nodes-langchain/nodes/agents/Agent/agents/ToolsAgent/V3/helpers/prepareItemContext.ts":{"41":[1,3,4,5,8,10,12,6]},"packages/@n8n/nodes-langchain/nodes/agents/Agent/agents/ToolsAgent/V3/helpers/resolveSubAgentRequest.ts":{"25":[1,3,4,5,8,10,12,6],"40":[1,3,4,5,8,10,12,6],"49":[1,3,4,5,8,10,12,6]},"packages/@n8n/nodes-langchain/nodes/agents/Agent/agents/ToolsAgent/V3/helpers/runAgent.ts":{"39":[1,3,4,5,8,10,12,6]},"packages/@n8n/nodes-langchain/nodes/agents/Agent/agents/ToolsAgent/common.ts":{"32":[1,3,4,5,8,10,12,6],"45":[1,3,4,5,8,10,12,6],"56":[1,3,4,5,8,10,12,6],"60":[1,3,4,5,8,10,12,6],"78":[1,3,4,5,8,10,12,6],"112":[1,3,4,5,8,10,12,6],"133":[1,3,4,5,8,10,12,6],"218":[1,3,4,5,8,10,12,6],"228":[1,3,4,5,8,10,12,6],"229":[1,3,4,5,8,10,12,6],"280":[1,3,4,5,8,10,12,6],"285":[1,3,4,5,8,10,12,6],"288":[1,3,4,5,8,10,12,6],"290":[1,3,4,5,8,10,12,6],"335":[1,3,4,5,8,10,12,6],"355":[1,3,4,5,8,10,12,6],"356":[1,3,4,5,8,10,12,6],"364":[1,3,4,5,8,10,12,6],"365":[1,3,4,5,8,10,12,6],"391":[1,3,4,5,8,10,12,6],"411":[1,3,4,5,8,10,12,6],"412":[1,3,4,5,8,10,12,6],"415":[1,3,4,5,8,10,12,6],"478":[1,3,4,5,8,10,12,6],"512":[1,3,4,5,8,10,12,6],"528":[1,3,4,5,8,10,12,6],"543":[1,3,4,5,8,10,12,6],"558":[1,3,4,5,8,10,12,6],"615":[1,3,4,5,8,10,12,6]},"packages/@n8n/nodes-langchain/nodes/agents/Agent/agents/utils.ts":{"13":[1,3,4,5,8,10,12,6],"19":[1,3,4,5,8,10,12,6],"36":[1,3,4,5,8,10,12,6],"42":[1,3,4,5,8,10,12,6],"45":[1,3,4,5,8,10,12,6]},"packages/@n8n/nodes-langchain/nodes/agents/Agent/utils.ts":{"11":[1,3,4,5,8,10,12,6],"23":[1,3,4,5,8,10,12,6],"26":[1,3,4,5,8,10,12,6],"86":[1,3,4,5,8,10,12,6],"89":[1,3,4,5,8,10,12,6]},"packages/@n8n/nodes-langchain/nodes/llms/LMChatAnthropic/LmChatAnthropic.node.ts":{"91":[3,4,5,93,6,13],"117":[3,4,5,93,6,13],"185":[3,4,5,93,6,13],"485":[3,4,5,93,6,13],"564":[3,4,5,93,6,13],"597":[3,4,5,93,6,13],"615":[3,4,5,93,6,13],"628":[3,4,5,93,6,13]},"packages/@n8n/nodes-langchain/nodes/llms/LMChatAnthropic/methods/searchModels.ts":{"7":[3,4,5,6,13],"22":[3,4,5,6,13],"31":[3,4,5,6,13],"32":[3,4,5,6,13]},"packages/@n8n/nodes-langchain/nodes/memory/MemoryBufferWindow/MemoryBufferWindow.node.ts":{"30":[3,4,5],"34":[3,4,5],"41":[3,4,5],"63":[3,4,5],"75":[3,4,5],"158":[3,4,5]},"packages/@n8n/nodes-langchain/nodes/memory/MemoryManager/MemoryManager.node.ts":{"20":[3,4,5],"47":[3,4,5],"50":[3,4,5],"53":[3,4,5],"63":[3,4,5],"73":[3,4,5],"302":[3,4,5],"400":[3,4,5]},"packages/@n8n/nodes-langchain/utils/agent-execution/buildResponseMetadata.ts":{"17":[1,3,4,5,8,10,12,6]},"packages/@n8n/nodes-langchain/utils/agent-execution/buildSteps.ts":{"36":[1,3,4,5,8,10,12,6],"93":[1,3,4,5,8,10,12,6],"141":[1,3,4,5,8,10,12,6],"164":[1,3,4,5,8,10,12,6],"205":[1,3,4,5,8,10,12,6],"216":[1,3,4,5,8,10,12,6],"223":[1,3,4,5,8,10,12,6],"235":[1,3,4,5,8,10,12,6],"287":[1,3,4,5,8,10,12,6],"291":[1,3,4,5,8,10,12,6],"298":[1,3,4,5,8,10,12,6],"311":[1,3,4,5,8,10,12,6],"315":[1,3,4,5,8,10,12,6],"344":[1,3,4,5,8,10,12,6],"369":[1,3,4,5,8,10,12,6],"391":[1,3,4,5,8,10,12,6]},"packages/@n8n/nodes-langchain/utils/agent-execution/createEngineRequests.ts":{"16":[1,3,4,5,8,10,12,6],"25":[1,3,4,5,8,10,12,6],"43":[1,3,4,5,8,10,12,6],"76":[1,3,4,5,8,10,12,6],"150":[1,3,4,5,8,10,12,6],"157":[1,3,4,5,8,10,12,6],"213":[1,3,4,5,8,10,12,6],"221":[1,3,4,5,8,10,12,6],"224":[1,3,4,5,8,10,12,6],"229":[1,3,4,5,8,10,12,6],"231":[1,3,4,5,8,10,12,6],"271":[1,3,4,5,8,10,12,6]},"packages/@n8n/nodes-langchain/utils/agent-execution/executeEngineAction.ts":{"17":[1,3,4,5,8,10,12,6],"22":[1,3,4,5,8,10,12,6],"47":[1,3,4,5,8,10,12,6]},"packages/@n8n/nodes-langchain/utils/agent-execution/index.ts":{"13":[1,3,4,5,8,10,12,6]},"packages/@n8n/nodes-langchain/utils/agent-execution/memoryManagement.ts":{"25":[1,3,4,5,8,10,12,6],"81":[1,3,4,5,8,10,12,6],"148":[1,3,4,5,8,10,12,6],"151":[1,3,4,5,8,10,12,6],"164":[1,3,4,5,8,10,12,6],"242":[1,3,4,5,8,10,12,6],"294":[1,3,4,5,8,10,12,6]},"packages/@n8n/nodes-langchain/utils/agent-execution/processEventStream.ts":{"20":[1,3,4,5,8,10,12,6]},"packages/@n8n/nodes-langchain/utils/agent-execution/processHitlResponses.ts":{"26":[1,3,4,5,8,10,12,6],"38":[1,3,4,5,8,10,12,6],"47":[1,3,4,5,8,10,12,6],"54":[1,3,4,5,8,10,12,6],"71":[1,3,4,5,8,10,12,6],"80":[1,3,4,5,8,10,12,6],"106":[1,3,4,5,8,10,12,6]},"packages/@n8n/nodes-langchain/utils/agent-execution/serializeIntermediateSteps.ts":{"10":[1,3,4,5,8,10,12,6],"15":[1,3,4,5,8,10,12,6],"29":[1,3,4,5,8,10,12,6]},"packages/@n8n/nodes-langchain/utils/agent-execution/types.ts":{"192":[1,3,4,5,8,10,12,6],"208":[1,3,4,5,8,10,12,6],"222":[1,3,4,5,8,10,12,6]},"packages/@n8n/utils/src/files/sanitize-filename.ts":{"55":[3,48]},"packages/cli/src/credentials/credential-connection-status-proxy.ts":{"16":[3,4,91,33,180,50],"20":[3,4,91,33,180,50],"25":[3,4,91,33,180,50],"30":[3,4,91,33,180,50],"35":[3,4,91,33,180,50],"44":[3,4,91,33,180,50]},"packages/cli/src/credentials/dynamic-credentials-proxy.ts":{"30":[3,4,5,34,163,169,42,45,11,47],"32":[3,4,5,34,163,169,42,45,11,47],"36":[3,4,5,34,163,169,42,45,11,47],"46":[3,4,5,34,163,169,42,45,11,47],"58":[3,4,5,34,163,169,42,45,11,47],"64":[3,4,5,34,163,169,42,45,11,47],"87":[3,4,5,34,163,169,42,45,11,47],"115":[3,4,5,34,163,169,42,45,11,47]},"packages/cli/src/errors/response-errors/forbidden.error.ts":{"4":[3,5,74,75]},"packages/cli/src/modules/chat-hub/chat-hub-agent.repository.ts":{"8":[3,4,5,91],"12":[3,4,5,91],"23":[3,4,5,91],"31":[3,4,5,91],"36":[3,4,5,91],"43":[3,4,5,91],"52":[3,4,5,91]},"packages/cli/src/modules/chat-hub/chat-hub-agent.service.ts":{"36":[3,4,5,91,117],"51":[3,4,5,91,117],"54":[3,4,5,91,117],"57":[3,4,5,91,117],"58":[3,4,5,91,117],"77":[3,4,5,91,117],"81":[3,4,5,91,117],"89":[3,4,5,91,117],"96":[3,4,5,91,117],"124":[3,4,5,91,117],"164":[3,4,5,91,117],"183":[3,4,5,91,117],"204":[3,4,5,91,117],"216":[3,4,5,91,117],"230":[3,4,5,91,117],"264":[3,4,5,91,117],"287":[3,4,5,91,117],"309":[3,4,5,91,117],"334":[3,4,5,91,117],"349":[3,4,5,91,117],"360":[3,4,5,91,117],"375":[3,4,5,91,117],"384":[3,4,5,91,117],"394":[3,4,5,91,117],"400":[3,4,5,91,117],"403":[3,4,5,91,117],"418":[3,4,5,91,117],"426":[3,4,5,91,117],"451":[3,4,5,91,117]},"packages/cli/src/modules/chat-hub/chat-hub-credentials.service.ts":{"18":[3,4,5,91,33],"25":[3,4,5,91,33],"38":[3,4,5,91,33],"49":[3,4,5,91,33],"62":[3,4,5,91,33],"71":[3,4,5,91,33],"91":[3,4,5,91,33]},"packages/cli/src/modules/chat-hub/chat-hub-execution.service.ts":{"51":[3,4,5,92,93],"68":[3,4,5,92,93],"79":[3,4,5,92,93],"91":[3,4,5,92,93],"156":[3,4,5,92,93],"207":[3,4,5,92,93],"228":[3,4,5,92,93],"251":[3,4,5,92,93],"254":[3,4,5,92,93],"264":[3,4,5,92,93],"274":[3,4,5,92,93],"303":[3,4,5,92,93],"368":[3,4,5,92,93],"447":[3,4,5,92,93],"489":[3,4,5,92,93],"497":[3,4,5,92,93],"498":[3,4,5,92,93],"499":[3,4,5,92,93],"541":[3,4,5,92,93],"570":[3,4,5,92,93],"610":[3,4,5,92,93],"624":[3,4,5,92,93],"629":[3,4,5,92,93],"631":[3,4,5,92,93],"635":[3,4,5,92,93],"641":[3,4,5,92,93],"653":[3,4,5,92,93],"659":[3,4,5,92,93],"661":[3,4,5,92,93],"662":[3,4,5,92,93],"663":[3,4,5,92,93],"666":[3,4,5,92,93],"673":[3,4,5,92,93],"692":[3,4,5,92,93],"713":[3,4,5,92,93],"733":[3,4,5,92,93],"746":[3,4,5,92,93]},"packages/cli/src/modules/chat-hub/chat-hub-extractor.ts":{"32":[3,4,5],"52":[3,4,5],"65":[3,4,5],"71":[3,4,5]},"packages/cli/src/modules/chat-hub/chat-hub-title.service.ts":{"39":[3,4,5,91,33],"53":[3,4,5,91,33],"80":[3,4,5,91,33],"88":[3,4,5,91,33],"124":[3,4,5,91,33],"152":[3,4,5,91,33],"186":[3,4,5,91,33],"259":[3,4,5,91,33],"260":[3,4,5,91,33],"262":[3,4,5,91,33],"272":[3,4,5,91,33]},"packages/cli/src/modules/chat-hub/chat-hub-tool.repository.ts":{"10":[3,4,5,91,93],"14":[3,4,5,91,93],"22":[3,4,5,91,93],"30":[3,4,5,91,93],"35":[3,4,5,91,93],"42":[3,4,5,91,93],"50":[3,4,5,91,93],"57":[3,4,5,91,93],"68":[3,4,5,91,93],"79":[3,4,5,91,93],"90":[3,4,5,91,93],"98":[3,4,5,91,93],"101":[3,4,5,91,93],"109":[3,4,5,91,93],"112":[3,4,5,91,93],"122":[3,4,5,91,93]},"packages/cli/src/modules/chat-hub/chat-hub-tool.service.ts":{"21":[3,4,5,91,93],"29":[3,4,5,91,93],"33":[3,4,5,91,93],"37":[3,4,5,91,93],"39":[3,4,5,91,93],"42":[3,4,5,91,93],"44":[3,4,5,91,93],"47":[3,4,5,91,93],"51":[3,4,5,91,93],"55":[3,4,5,91,93],"59":[3,4,5,91,93],"63":[3,4,5,91,93],"79":[3,4,5,91,93],"86":[3,4,5,91,93],"104":[3,4,5,91,93],"110":[3,4,5,91,93],"136":[3,4,5,91,93],"137":[3,4,5,91,93],"149":[3,4,5,91,93]},"packages/cli/src/modules/chat-hub/chat-hub-workflow.service.ts":{"85":[3,4,5,91,93,33],"100":[3,4,5,91,93,33],"106":[3,4,5,91,93,33],"127":[3,4,5,91,93,33],"181":[3,4,5,91,93,33],"192":[3,4,5,91,93,33],"247":[3,4,5,91,93,33],"262":[3,4,5,91,93,33],"276":[3,4,5,91,93,33],"296":[3,4,5,91,93,33],"297":[3,4,5,91,93,33],"311":[3,4,5,91,93,33],"358":[3,4,5,91,93,33],"374":[3,4,5,91,93,33],"424":[3,4,5,91,93,33],"425":[3,4,5,91,93,33],"488":[3,4,5,91,93,33],"558":[3,4,5,91,93,33],"623":[3,4,5,91,93,33],"635":[3,4,5,91,93,33],"716":[3,4,5,91,93,33],"724":[3,4,5,91,93,33],"750":[3,4,5,91,93,33],"912":[3,4,5,91,93,33],"927":[3,4,5,91,93,33],"949":[3,4,5,91,93,33],"1019":[3,4,5,91,93,33],"1079":[3,4,5,91,93,33],"1090":[3,4,5,91,93,33],"1104":[3,4,5,91,93,33],"1120":[3,4,5,91,93,33],"1121":[3,4,5,91,93,33],"1128":[3,4,5,91,93,33],"1153":[3,4,5,91,93,33],"1166":[3,4,5,91,93,33],"1201":[3,4,5,91,93,33],"1204":[3,4,5,91,93,33],"1215":[3,4,5,91,93,33],"1226":[3,4,5,91,93,33],"1284":[3,4,5,91,93,33],"1326":[3,4,5,91,93,33],"1396":[3,4,5,91,93,33],"1426":[3,4,5,91,93,33],"1456":[3,4,5,91,93,33],"1464":[3,4,5,91,93,33],"1467":[3,4,5,91,93,33],"1521":[3,4,5,91,93,33],"1530":[3,4,5,91,93,33],"1534":[3,4,5,91,93,33],"1539":[3,4,5,91,93,33],"1552":[3,4,5,91,93,33],"1561":[3,4,5,91,93,33],"1579":[3,4,5,91,93,33],"1586":[3,4,5,91,93,33],"1615":[3,4,5,91,93,33],"1641":[3,4,5,91,93,33],"1762":[3,4,5,91,93,33],"1785":[3,4,5,91,93,33]},"packages/cli/src/modules/chat-hub/chat-hub.attachment.service.ts":{"19":[3,4,5],"28":[3,4,5],"55":[3,4,5],"85":[3,4,5],"129":[3,4,5],"132":[3,4,5],"138":[3,4,5],"146":[3,4,5],"152":[3,4,5],"154":[3,4,5],"158":[3,4,5],"170":[3,4,5],"174":[3,4,5],"196":[3,4,5],"197":[3,4,5],"211":[3,4,5]},"packages/cli/src/modules/chat-hub/chat-hub.constants.ts":{"694":[3,4,5],"719":[3,4,5]},"packages/cli/src/modules/chat-hub/chat-hub.controller.ts":{"63":[3,4,5,91,93],"73":[3,4,5,91,93],"87":[3,4,5,91,93],"97":[3,4,5,91,93],"112":[3,4,5,91,93],"122":[3,4,5,91,93],"160":[3,4,5,91,93],"169":[3,4,5,91,93],"195":[3,4,5,91,93],"225":[3,4,5,91,93],"250":[3,4,5,91,93],"284":[3,4,5,91,93],"309":[3,4,5,91,93],"343":[3,4,5,91,93],"359":[3,4,5,91,93],"375":[3,4,5,91,93],"390":[3,4,5,91,93],"402":[3,4,5,91,93],"404":[3,4,5,91,93],"409":[3,4,5,91,93],"421":[3,4,5,91,93],"436":[3,4,5,91,93],"447":[3,4,5,91,93],"453":[3,4,5,91,93],"463":[3,4,5,91,93],"474":[3,4,5,91,93],"488":[3,4,5,91,93],"511":[3,4,5,91,93],"521":[3,4,5,91,93]},"packages/cli/src/modules/chat-hub/chat-hub.models.service.ts":{"32":[3,4,5,33],"41":[3,4,5,33],"54":[3,4,5,33],"64":[3,4,5,33],"90":[3,4,5,33],"98":[3,4,5,33],"172":[3,4,5,33],"188":[3,4,5,33],"204":[3,4,5,33],"256":[3,4,5,33],"301":[3,4,5,33],"311":[3,4,5,33],"318":[3,4,5,33],"412":[3,4,5,33],"461":[3,4,5,33],"510":[3,4,5,33],"554":[3,4,5,33],"597":[3,4,5,33],"640":[3,4,5,33],"683":[3,4,5,33],"726":[3,4,5,33],"769":[3,4,5,33],"781":[3,4,5,33],"783":[3,4,5,33],"803":[3,4,5,33],"812":[3,4,5,33],"813":[3,4,5,33],"820":[3,4,5,33],"828":[3,4,5,33],"874":[3,4,5,33],"879":[3,4,5,33],"880":[3,4,5,33],"884":[3,4,5,33],"885":[3,4,5,33],"894":[3,4,5,33],"900":[3,4,5,33]},"packages/cli/src/modules/chat-hub/chat-hub.service.ts":{"56":[3,4,5,91,93,33],"76":[3,4,5,91,93,33],"87":[3,4,5,91,93,33],"113":[3,4,5,91,93,33],"163":[3,4,5,91,93,33],"187":[3,4,5,91,93,33],"194":[3,4,5,91,93,33],"198":[3,4,5,91,93,33],"225":[3,4,5,91,93,33],"233":[3,4,5,91,93,33],"249":[3,4,5,91,93,33],"262":[3,4,5,91,93,33],"271":[3,4,5,91,93,33],"281":[3,4,5,91,93,33],"293":[3,4,5,91,93,33],"301":[3,4,5,91,93,33],"318":[3,4,5,91,93,33],"322":[3,4,5,91,93,33],"331":[3,4,5,91,93,33],"375":[3,4,5,91,93,33],"376":[3,4,5,91,93,33],"383":[3,4,5,91,93,33],"422":[3,4,5,91,93,33],"436":[3,4,5,91,93,33],"460":[3,4,5,91,93,33],"474":[3,4,5,91,93,33],"506":[3,4,5,91,93,33],"550":[3,4,5,91,93,33],"587":[3,4,5,91,93,33],"615":[3,4,5,91,93,33],"628":[3,4,5,91,93,33],"707":[3,4,5,91,93,33],"745":[3,4,5,91,93,33],"760":[3,4,5,91,93,33],"773":[3,4,5,91,93,33],"779":[3,4,5,91,93,33],"852":[3,4,5,91,93,33],"878":[3,4,5,91,93,33],"898":[3,4,5,91,93,33],"911":[3,4,5,91,93,33],"917":[3,4,5,91,93,33],"1006":[3,4,5,91,93,33],"1033":[3,4,5,91,93,33],"1042":[3,4,5,91,93,33],"1054":[3,4,5,91,93,33],"1057":[3,4,5,91,93,33],"1113":[3,4,5,91,93,33],"1127":[3,4,5,91,93,33],"1139":[3,4,5,91,93,33],"1142":[3,4,5,91,93,33],"1201":[3,4,5,91,93,33],"1228":[3,4,5,91,93,33],"1271":[3,4,5,91,93,33],"1291":[3,4,5,91,93,33],"1298":[3,4,5,91,93,33]},"packages/cli/src/modules/chat-hub/chat-hub.settings.controller.ts":{"18":[3,4,5,92],"27":[3,4,5,92],"39":[3,4,5,92],"46":[3,4,5,92],"61":[3,4,5,92],"75":[3,4,5,92],"88":[3,4,5,92]},"packages/cli/src/modules/chat-hub/chat-hub.settings.service.ts":{"21":[3,4,5,91,92,117],"26":[3,4,5,91,92,117],"37":[3,4,5,91,92,117],"39":[3,4,5,91,92,117],"48":[3,4,5,91,92,117],"55":[3,4,5,91,92,117],"68":[3,4,5,91,92,117],"76":[3,4,5,91,92,117],"93":[3,4,5,91,92,117],"115":[3,4,5,91,92,117],"125":[3,4,5,91,92,117],"141":[3,4,5,91,92,117],"152":[3,4,5,91,92,117]},"packages/cli/src/modules/chat-hub/chat-message.repository.ts":{"19":[3,4,5],"26":[3,4,5],"38":[3,4,5],"48":[3,4,5],"74":[3,4,5],"108":[3,4,5],"117":[3,4,5],"122":[3,4,5],"130":[3,4,5]},"packages/cli/src/modules/chat-hub/chat-session.repository.ts":{"10":[3,4,5],"14":[3,4,5],"26":[3,4,5],"31":[3,4,5],"36":[3,4,5],"72":[3,4,5],"77":[3,4,5],"91":[3,4,5]},"packages/cli/src/modules/chat-hub/chat-stream-state.service.ts":{"68":[3,4,5],"89":[3,4,5],"115":[3,4,5],"131":[3,4,5],"151":[3,4,5],"177":[3,4,5],"187":[3,4,5],"205":[3,4,5],"233":[3,4,5],"245":[3,4,5],"251":[3,4,5],"268":[3,4,5],"283":[3,4,5],"287":[3,4,5],"291":[3,4,5],"304":[3,4,5],"319":[3,4,5],"329":[3,4,5],"342":[3,4,5],"357":[3,4,5],"367":[3,4,5],"370":[3,4,5],"380":[3,4,5]},"packages/cli/src/modules/chat-hub/chat-stream.service.ts":{"52":[3,4,5,93],"66":[3,4,5,93],"100":[3,4,5,93],"137":[3,4,5,93],"162":[3,4,5,93],"198":[3,4,5,93],"235":[3,4,5,93],"272":[3,4,5,93],"308":[3,4,5,93],"318":[3,4,5,93],"326":[3,4,5,93],"335":[3,4,5,93],"435":[3,4,5,93],"454":[3,4,5,93],"461":[3,4,5,93],"501":[3,4,5,93],"531":[3,4,5,93],"561":[3,4,5,93],"588":[3,4,5,93],"616":[3,4,5,93],"643":[3,4,5,93]},"packages/cli/src/modules/chat-hub/context-limits.ts":{"223":[3,4,5]},"packages/cli/src/modules/chat-hub/stream-capturer.ts":{"13":[3,4,5],"23":[3,4,5],"32":[3,4,5],"43":[3,4,5],"44":[3,4,5],"53":[3,4,5],"67":[3,4,5],"72":[3,4,5],"96":[3,4,5],"112":[3,4,5],"124":[3,4,5],"142":[3,4,5],"151":[3,4,5],"204":[3,4,5]},"packages/cli/src/modules/dynamic-credentials.ee/services/credential-connection-status.service.ts":{"25":[3,4,91,33,180,50],"41":[3,4,91,33,180,50],"49":[3,4,91,33,180,50],"61":[3,4,91,33,180,50],"69":[3,4,91,33,180,50],"79":[3,4,91,33,180,50],"83":[3,4,91,33,180,50],"88":[3,4,91,33,180,50],"106":[3,4,91,33,180,50],"115":[3,4,91,33,180,50],"126":[3,4,91,33,180,50],"136":[3,4,91,33,180,50],"139":[3,4,91,33,180,50],"144":[3,4,91,33,180,50],"147":[3,4,91,33,180,50],"149":[3,4,91,33,180,50],"177":[3,4,91,33,180,50],"182":[3,4,91,33,180,50]},"packages/cli/src/modules/dynamic-credentials.ee/services/dynamic-credential.service.ts":{"41":[3,4,5,33,34,163,169,42,45,11,47],"62":[3,4,5,33,34,163,169,42,45,11,47],"173":[3,4,5,33,34,163,169,42,45,11,47],"190":[3,4,5,33,34,163,169,42,45,11,47],"222":[3,4,5,33,34,163,169,42,45,11,47],"253":[3,4,5,33,34,163,169,42,45,11,47],"299":[3,4,5,33,34,163,169,42,45,11,47],"314":[3,4,5,33,34,163,169,42,45,11,47],"331":[3,4,5,33,34,163,169,42,45,11,47],"344":[3,4,5,33,34,163,169,42,45,11,47],"347":[3,4,5,33,34,163,169,42,45,11,47],"367":[3,4,5,33,34,163,169,42,45,11,47]},"packages/core/src/binary-data/utils.ts":{"3":[21,3,181,151,65],"7":[21,3,181,151,65],"12":[21,3,181,151,65],"22":[21,3,181,151,65]},"packages/nodes-base/nodes/Merge/Merge.node.ts":{"9":[3,4,5,125,135,13]},"packages/nodes-base/nodes/Merge/v1/MergeV1.node.ts":{"20":[3,4,5,125,135,13],"205":[3,4,5,125,135,13]},"packages/nodes-base/nodes/Merge/v2/MergeV2.node.ts":{"35":[3,4,5,125,135,13],"293":[3,4,5,125,135,13],"459":[3,4,5,125,135,13],"467":[3,4,5,125,135,13],"520":[3,4,5,125,135,13],"603":[3,4,5,125,135,13],"604":[3,4,5,125,135,13]},"packages/nodes-base/nodes/Merge/v2/utils.ts":{"31":[3,4,5,125,135,13],"32":[3,4,5,125,135,13],"34":[3,4,5,125,135,13],"41":[3,4,5,125,135,13],"47":[3,4,5,125,135,13],"72":[3,4,5,125,135,13],"78":[3,4,5,125,135,13],"103":[3,4,5,125,135,13],"128":[3,4,5,125,135,13],"150":[3,4,5,125,135,13],"151":[3,4,5,125,135,13],"159":[3,4,5,125,135,13],"176":[3,4,5,125,135,13],"187":[3,4,5,125,135,13],"191":[3,4,5,125,135,13],"197":[3,4,5,125,135,13],"203":[3,4,5,125,135,13],"210":[3,4,5,125,135,13],"213":[3,4,5,125,135,13],"216":[3,4,5,125,135,13],"219":[3,4,5,125,135,13],"243":[3,4,5,125,135,13],"246":[3,4,5,125,135,13],"250":[3,4,5,125,135,13],"268":[3,4,5,125,135,13],"273":[3,4,5,125,135,13],"279":[3,4,5,125,135,13],"285":[3,4,5,125,135,13],"288":[3,4,5,125,135,13],"292":[3,4,5,125,135,13],"307":[3,4,5,125,135,13],"327":[3,4,5,125,135,13],"334":[3,4,5,125,135,13],"349":[3,4,5,125,135,13],"350":[3,4,5,125,135,13]},"packages/nodes-base/nodes/Merge/v3/MergeV3.node.ts":{"15":[3,4,5,125,135,13],"26":[3,4,5,125,135,13]},"packages/nodes-base/nodes/Merge/v3/actions/mode/append.ts":{"21":[3,4,5,125,135,13]},"packages/nodes-base/nodes/Merge/v3/actions/mode/chooseBranch.ts":{"73":[3,4,5,125,135,13],"98":[3,4,5,125,135,13],"99":[3,4,5,125,135,13]},"packages/nodes-base/nodes/Merge/v3/actions/mode/combineAll.ts":{"35":[3,4,5,125,135,13]},"packages/nodes-base/nodes/Merge/v3/actions/mode/combineByFields.ts":{"261":[3,4,5,125,135,13],"274":[3,4,5,125,135,13],"297":[3,4,5,125,135,13],"305":[3,4,5,125,135,13],"358":[3,4,5,125,135,13]},"packages/nodes-base/nodes/Merge/v3/actions/mode/combineByPosition.ts":{"49":[3,4,5,125,135,13],"80":[3,4,5,125,135,13],"82":[3,4,5,125,135,13],"95":[3,4,5,125,135,13],"100":[3,4,5,125,135,13],"106":[3,4,5,125,135,13],"110":[3,4,5,125,135,13],"112":[3,4,5,125,135,13]},"packages/nodes-base/nodes/Merge/v3/actions/mode/combineBySql.ts":{"104":[3,4,5,125,135,13],"125":[3,4,5,125,135,13],"131":[3,4,5,125,135,13],"152":[3,4,5,125,135,13],"158":[3,4,5,125,135,13],"167":[3,4,5,125,135,13],"170":[3,4,5,125,135,13],"179":[3,4,5,125,135,13],"180":[3,4,5,125,135,13],"208":[3,4,5,125,135,13],"255":[3,4,5,125,135,13],"270":[3,4,5,125,135,13],"271":[3,4,5,125,135,13],"272":[3,4,5,125,135,13],"289":[3,4,5,125,135,13],"290":[3,4,5,125,135,13],"300":[3,4,5,125,135,13]},"packages/nodes-base/nodes/Merge/v3/actions/router.ts":{"7":[3,4,5,125,135,13]},"packages/nodes-base/nodes/Merge/v3/helpers/sandbox-utils.ts":{"13":[3,4,5,125,135,13],"29":[3,4,5,125,135,13],"41":[3,4,5,125,135,13],"49":[3,4,5,125,135,13],"100":[3,4,5,125,135,13],"120":[3,4,5,125,135,13],"135":[3,4,5,125,135,13],"156":[3,4,5,125,135,13],"181":[3,4,5,125,135,13]},"packages/nodes-base/nodes/Merge/v3/helpers/utils.ts":{"33":[3,4,5,125,135,13],"34":[3,4,5,125,135,13],"36":[3,4,5,125,135,13],"43":[3,4,5,125,135,13],"49":[3,4,5,125,135,13],"74":[3,4,5,125,135,13],"80":[3,4,5,125,135,13],"105":[3,4,5,125,135,13],"130":[3,4,5,125,135,13],"152":[3,4,5,125,135,13],"153":[3,4,5,125,135,13],"161":[3,4,5,125,135,13],"178":[3,4,5,125,135,13],"189":[3,4,5,125,135,13],"193":[3,4,5,125,135,13],"199":[3,4,5,125,135,13],"205":[3,4,5,125,135,13],"212":[3,4,5,125,135,13],"215":[3,4,5,125,135,13],"218":[3,4,5,125,135,13],"221":[3,4,5,125,135,13],"245":[3,4,5,125,135,13],"248":[3,4,5,125,135,13],"252":[3,4,5,125,135,13],"270":[3,4,5,125,135,13],"275":[3,4,5,125,135,13],"281":[3,4,5,125,135,13],"287":[3,4,5,125,135,13],"290":[3,4,5,125,135,13],"294":[3,4,5,125,135,13],"309":[3,4,5,125,135,13],"329":[3,4,5,125,135,13],"336":[3,4,5,125,135,13],"351":[3,4,5,125,135,13],"352":[3,4,5,125,135,13],"364":[3,4,5,125,135,13],"365":[3,4,5,125,135,13],"371":[3,4,5,125,135,13],"375":[3,4,5,125,135,13],"389":[3,4,5,125,135,13],"406":[3,4,5,125,135,13]},"packages/nodes-base/nodes/Merge/v3/methods/loadOptions.ts":{"3":[3,4,5,125,135,13],"36":[3,4,5,125,135,13]},"localhost-36427/assets/_plugin-vue_export-helper-Da88TEg1.js":{"2":[3]},"localhost-36427/assets/chunk-CC9Q-vWm.js":{"8":[3],"9":[3],"10":[3],"19":[3],"23":[3],"29":[3],"33":[3],"34":[3]},"localhost-44399/assets/_plugin-vue_export-helper-Da88TEg1.js":{"2":[3,93,33]},"localhost-44399/assets/chunk-CC9Q-vWm.js":{"8":[3,93,33],"9":[3,93,33],"10":[3,93,33],"19":[3,93,33],"23":[3,93,33],"29":[3,93,33],"33":[3,93,33],"34":[3,93,33]},"packages/@n8n/utils/src/retry.ts":{"46":[3,4,5,91,93,33]},"packages/frontend/@n8n/chat/src/components/ChatFile.vue":{"28":[3,72],"33":[3]},"packages/frontend/@n8n/design-system/src/components/N8nAiModelSelectorDropdown/AiModelSelectorDropdown.vue":{"22":[3,4,5,91,93,33],"79":[3,4,5,91,93,33],"81":[3,4,5,91,93,33],"87":[4,5,91],"97":[3,4,5,91,93,33],"111":[3,4,5,91,93,33],"117":[4,5,91,93,33],"122":[3,4,5,91,93,33],"137":[3,4,5,91,93],"146":[4,5,91],"160":[4,5,91],"177":[4,5,91],"190":[4,5,91]},"packages/frontend/editor-ui/src/app/layouts/ChatLayout.vue":{"8":[3,4,5,91,93,33],"10":[3,4,5,91,93,33]},"packages/frontend/editor-ui/src/app/utils/fileUtils.ts":{"5":[3,72],"6":[3,72]},"packages/frontend/editor-ui/src/app/utils/rbac/checks/hasRole.ts":{"5":[3,4,5,91,92,93,33]},"packages/frontend/editor-ui/src/features/ai/chatHub/ChatView.vue":{"76":[3,4,5,91,93,33],"84":[3,4,5,91,93,33],"91":[3,4,5,91,93,33],"94":[3,4,5,91,93,33],"96":[3,4,5,91,93,33],"99":[3,4,5,91,93,33],"101":[3,4,5,91,93,33],"106":[3,4,5,91,93,33],"108":[3,4,5,91,93,33],"134":[3,4],"141":[3,4,5,91,93,33],"146":[3,4,5,91,93,33],"150":[3,4,5,91,93,33],"182":[3,4,5,91,93,33],"214":[3,4,5,91,93,33],"218":[3,4,5,91,93,33],"225":[3,4,5,91,93],"236":[93],"245":[3,4,5,91,93,33],"253":[3,4,5,91,93,33],"258":[3,4,5,91,93,33],"267":[3,4,5,91,93,33],"293":[3,4,5,91,93,33],"308":[3,4,5,91,93,33],"323":[3,4,5,91,93,33],"335":[3,4,5,91,93,33],"363":[3,4,5,91,93,33],"364":[3,4,5,91,93,33],"382":[3,4,5,91,93,33],"400":[3,4,5,91,93,33],"408":[3,4,5,91,93,33],"417":[3,4,5,91,93,33],"428":[3,4,5,91,93,33],"448":[3,4,5,91,93,33],"464":[3,4,5,91,93,33],"472":[3,4,5,91,93,33],"483":[3,4,5,91,93,33],"517":[4],"525":[4],"553":[4],"576":[3,4,5,91,93],"600":[3,4,5,91,93],"604":[4],"628":[91],"707":[3,4,5,91,93,33],"736":[3,4,5,91,93,33],"748":[3,4,5,91,93,33],"769":[4]},"packages/frontend/editor-ui/src/features/ai/chatHub/chat.api.ts":{"32":[3,4,5,91,92,93,33],"44":[3,4,5,91,93,33],"79":[4],"120":[4],"160":[3,4,5,91,93,33],"183":[3,4,5,91,93,33],"232":[3,4,5,91,93,33],"240":[91],"248":[91],"256":[91],"265":[91],"299":[92],"309":[92],"322":[92],"349":[3],"362":[33],"404":[3,4,5,91,93,33],"408":[93]},"packages/frontend/editor-ui/src/features/ai/chatHub/chat.types.ts":{"114":[3,4]},"packages/frontend/editor-ui/src/features/ai/chatHub/chat.utils.ts":{"25":[3,4,5,91,93,33,75],"46":[3,4,5,91,93,33],"64":[3,4,5,91,93,33],"71":[3,4,91,93,33],"82":[91,33],"110":[3,4,5,91,93,33],"122":[3,4,5,91,93,33],"158":[91,33],"196":[3,4,5,91,93,33],"200":[4,5,91],"215":[3,4,5,91,93,33],"227":[3,4,5,91,93,33],"280":[3,4,5,91,93,33],"331":[3,4,5,91,93,33],"335":[3,4,5,91,93,33],"341":[3,4,5,91,93,33],"352":[3,4,5,91,93],"361":[3,4,5,91,93],"381":[3,4,5,91,93,33],"412":[3,4,5,91,93,33],"442":[3,4,5,91,93,33],"459":[3],"467":[3],"487":[3,4,5,91,93,33],"491":[3,4,5,91,93,33],"505":[3,4,5,91,93,33],"515":[3,4,5,91,93,33],"608":[3,4,5,91,93,33]},"packages/frontend/editor-ui/src/features/ai/chatHub/components/ChatAgentAvatar.vue":{"22":[3,4,5,91,93,33]},"packages/frontend/editor-ui/src/features/ai/chatHub/components/ChatConversationHeader.vue":{"47":[3,4,5,91,93,33],"65":[3,4,5,91,93,33],"67":[3,4,5,91,93,33],"85":[3,4,5,91,93,33]},"packages/frontend/editor-ui/src/features/ai/chatHub/components/ChatGreetings.vue":{"22":[3,4,5,91,93,33],"38":[91,33]},"packages/frontend/editor-ui/src/features/ai/chatHub/components/ChatMessage.vue":{"68":[3,4,5,91,93,33],"96":[3,4,5,91,93,33],"116":[3,4,5,91,93,33],"117":[3,4,5,91,93,33],"144":[3,4,5,91,93,33],"149":[3,4,5,91,93,33],"150":[3,4,5,91,93,33],"159":[3,4,5,91,93,33],"169":[3,4,5,91,93,33],"170":[3],"181":[4],"188":[3,4,5,91,93,33],"190":[3,4,5,91,93,33],"198":[3,4,5,91,93,33],"200":[4],"208":[4],"276":[4],"290":[4],"295":[3,4,5,91,93,33],"296":[3,4,5,91,93,33],"307":[3,4,5,91,93,33],"309":[4],"317":[3,4,5,91,93,33],"320":[3,4,5,91,93,33],"389":[4],"397":[4],"410":[3],"422":[3,4,5,91,93,33]},"packages/frontend/editor-ui/src/features/ai/chatHub/components/ChatMessageActions.vue":{"13":[3,4,5,91,93,33],"31":[4],"32":[4],"35":[3,4,5,91,93,33],"37":[3,4,5,91,93,33],"52":[3,4,5,91,93,33],"53":[3,4,5,91,93,33],"55":[4],"59":[4],"65":[3,4,5,91,93,33],"126":[4],"129":[4]},"packages/frontend/editor-ui/src/features/ai/chatHub/components/ChatPrompt.vue":{"18":[3,4,5,91,93,33],"59":[3,4,5,91,93,33],"71":[3,4,5,91,93,33],"75":[3,4,5,91,93,33],"79":[3,4,5,91,93,33],"81":[3,4,5,91,93,33],"82":[3,4,5,91,93,33],"104":[3],"150":[3,4,5,91,93,33],"211":[3,4,5,91,93,33],"212":[3,4,5,91,93,33],"228":[3,4,5,91,93,33],"289":[3,4,5,91,93,33]},"packages/frontend/editor-ui/src/features/ai/chatHub/components/ChatPromptCallouts.vue":{"19":[3,4,5,91,93,33],"35":[3,4],"45":[3,4]},"packages/frontend/editor-ui/src/features/ai/chatHub/components/ChatPromptFull.vue":{"10":[3,4,5,91,93,33],"55":[3],"71":[3,4,5,91,93,33],"75":[3,4,5,91,93,33],"87":[3,4,5,91,93,33],"103":[3,4,5,91,93,33],"112":[3,4,5,91,93,33],"125":[3,4,5,91,93,33]},"packages/frontend/editor-ui/src/features/ai/chatHub/components/ChatSessionMenuItem.vue":{"20":[3,4,5,91,93,33],"34":[3,4,5,91,93,33],"44":[3,4,5,91,93,33],"87":[3,4,5,91,93,33],"89":[3,4,5,91,93,33],"101":[3,4,5,91,93,33],"122":[3,4,5,91,93,33]},"packages/frontend/editor-ui/src/features/ai/chatHub/components/ChatSidebar.vue":{"19":[3,4,5,91,93,33],"49":[3,4,5,91,93,33],"59":[3,4,5,91,93,33],"60":[3,4,5,91,93,33],"69":[3,4,5,91,93,33]},"packages/frontend/editor-ui/src/features/ai/chatHub/components/ChatSidebarContent.vue":{"25":[3,4,5,91,93,33],"32":[3,4,5,91,93,33],"39":[3,4,5,91,93,33],"43":[3,4,5,91,93,33],"45":[3,4,5,91,93,33],"57":[3,4,5,91,93,33],"69":[3,4,5,91,93,33],"80":[3,4,5,91,93,33],"138":[3,4,5,91,93,33],"141":[3,4,5,91,93,33],"156":[3,4,5,91,93,33],"176":[3,4,5,91,93,33],"181":[3,4,5,91,93,33],"184":[3,4,5,91,93,33],"195":[3,4,5,91,93,33],"199":[3,4,5,91,93,33]},"packages/frontend/editor-ui/src/features/ai/chatHub/components/ChatSidebarLink.vue":{"26":[3,4,5,91,93,33],"45":[3,4,5,91,93,33],"62":[3,4,5,91,93,33]},"packages/frontend/editor-ui/src/features/ai/chatHub/components/ChatStarter.vue":{"36":[3,4,5,91,93,33],"38":[3,4,91],"42":[3,4,91],"46":[3,4,91],"47":[3,4,91],"49":[3,4,91],"59":[3,4,5,91,93,33],"67":[3,4,91],"80":[3,4,91],"97":[3,4,91],"111":[3,4,91],"133":[3,4,91],"148":[3,4,91],"159":[3,4,91]},"packages/frontend/editor-ui/src/features/ai/chatHub/components/ChatTypingIndicator.vue":{"2":[3,4,5,91,93,33]},"packages/frontend/editor-ui/src/features/ai/chatHub/components/CopyButton.vue":{"12":[3,4,5,91,93,33]},"packages/frontend/editor-ui/src/features/ai/chatHub/components/ModelSelector.vue":{"54":[3,4,5,91,93,33],"78":[3,4,5,91,93,33],"84":[3,4,5,91,93,33],"85":[3,4,5,91,93,33],"93":[3,4,5,91,93,33],"103":[3,4,5,91,93,33],"105":[3,4,5,91,93,33],"109":[4],"113":[4,5],"144":[4,5,91],"184":[3,4,5,91,93,33],"200":[3,4,5,91,93,33],"208":[4,5,91]},"packages/frontend/editor-ui/src/features/ai/chatHub/components/SkeletonMenuItem.vue":{"2":[3,4,5,91,93,33]},"packages/frontend/editor-ui/src/features/ai/chatHub/components/ToolsSelector.vue":{"32":[93],"34":[3,4,5,91,93,33],"36":[93],"39":[93],"41":[93],"45":[93],"50":[3,4,5,91,93,33],"53":[93],"62":[93],"104":[93],"108":[93],"116":[93],"153":[3,4,5,91,93,33],"166":[3,4,5,91,93,33],"175":[3,4,5,91,93,33],"197":[93],"204":[93],"207":[93]},"packages/frontend/editor-ui/src/features/ai/chatHub/composables/useChatArtifacts.ts":{"6":[3,4,5,91,93,33],"12":[3,4,5,91,93,33],"13":[3,4,5,91,93,33],"21":[3,4,5,91,93,33],"24":[3,4,5,91,93,33],"31":[3,4,5,91,93,33],"81":[3,4,5,91,93,33],"82":[3,4,5,91,93,33]},"packages/frontend/editor-ui/src/features/ai/chatHub/composables/useChatCredentials.ts":{"18":[3,4,5,91,93,33],"38":[3,4,5,91,93,33],"43":[3,4,5,91,93,33],"47":[3,4,5,91,93,33],"49":[3,4,5,91,93,33],"72":[4],"81":[3,4,5,91,93,33],"83":[3,4,5,91,93,33],"100":[3,4,5,91,93,33],"102":[3,4,5,91,93,33]},"packages/frontend/editor-ui/src/features/ai/chatHub/composables/useChatInputFocus.ts":{"13":[3,4,5,91,93,33]},"packages/frontend/editor-ui/src/features/ai/chatHub/composables/useChatPushHandler.ts":{"31":[3,4,5,91,93,33],"42":[3,4,5,91,93,33],"50":[3,4,5,91,93,33],"62":[3,4,5,91,93,33],"93":[3,4,5,91],"118":[3,4,5,91,93,33],"133":[93,33],"148":[3,4,5,91,93,33],"155":[4],"162":[3,4,5,91,93,33],"179":[3,4,5,91,93,33],"211":[3,4,5,91,93,33],"220":[3,4,5,91,93,33],"222":[3,4,5,91,93,33],"233":[91],"268":[4,91,33]},"packages/frontend/editor-ui/src/features/ai/chatHub/composables/useChatSession.ts":{"29":[3,4,5,91,93,33],"37":[3,4,5,91,93,33],"41":[91],"46":[3,4,5,91,93,33],"47":[3,4,5,91,93,33],"51":[3,4,5,91,93,33],"71":[3,4,5,91,93,33],"78":[3,4],"85":[3,4,5,91,93,33],"95":[3,4,5,91,93,33],"111":[3,4,5,91,93,33],"112":[3,4,5,91,93,33]},"packages/frontend/editor-ui/src/features/ai/chatHub/composables/useCustomAgent.ts":{"7":[3,4,5,91,93,33],"8":[3,4,5,91,93],"18":[3,4,5,91,93,33]},"packages/frontend/editor-ui/src/features/ai/chatHub/composables/useDynamicCredentialsStatus.ts":{"29":[3,4,5,91,93,33],"35":[3,4,5,91,93,33],"36":[3,4,5,91,93,33],"46":[33],"170":[3,4,5,91,93,33]},"packages/frontend/editor-ui/src/features/ai/chatHub/composables/useFileDrop.ts":{"3":[3,4,5,91,93,33]},"packages/frontend/editor-ui/src/features/ai/chatHub/model-selector.utils.ts":{"118":[3,4,5,91,93,33],"132":[3,4,5,91,93,33],"142":[91],"170":[33],"182":[33],"199":[3,4,5,91,93,33],"223":[3,4,5,91,93,33],"267":[3,4,5,91,93,33],"274":[3,4,5,91,93,33],"284":[3,4,5,91,93,33],"334":[3,4,5,91,93,33],"376":[3,4,5,91,93,33],"393":[3,4,5,91,93,33]},"packages/@n8n/ai-utilities/src/utils/failed-attempt-handler/n8nDefaultFailedAttemptHandler.ts":{"20":[1,93]},"packages/cli/src/tool-generation/index.ts":{"7":[93,8,12]},"packages/core/src/execution-engine/node-execution-context/utils/create-node-as-tool.ts":{"43":[93],"89":[93],"97":[93],"109":[93],"115":[93],"125":[93],"139":[93]},"packages/workflow/src/from-ai-parse-utils.ts":{"27":[93],"59":[93],"110":[93],"120":[93],"148":[93],"199":[93],"248":[93],"324":[93],"327":[93],"329":[93],"331":[93],"335":[93],"342":[93],"344":[93],"362":[93],"460":[93],"491":[93],"511":[93],"527":[93]},"packages/workflow/src/tool-helpers.ts":{"10":[93,9,11]},"packages/frontend/@n8n/design-system/src/components/N8nIcon/nodes/calculator.svg":{"11":[1,93]},"packages/frontend/@n8n/design-system/src/components/N8nIcon/nodes/call-n8n-sub-workflow-tool.svg":{"11":[1,93,10,13]},"packages/frontend/@n8n/design-system/src/components/N8nIcon/nodes/compression.svg":{"11":[1,93]},"packages/frontend/@n8n/design-system/src/components/N8nIcon/nodes/date-and-time.svg":{"11":[1,93]},"packages/frontend/@n8n/design-system/src/components/N8nIcon/nodes/rss-read.svg":{"11":[1,93]},"packages/frontend/@n8n/design-system/src/components/N8nIcon/nodes/send-mail.svg":{"11":[1,22,93,97,42]},"packages/frontend/@n8n/design-system/src/components/N8nIcon/nodes/think-tool.svg":{"11":[1,93]},"packages/frontend/@n8n/design-system/src/components/N8nIcon/nodes/totp.svg":{"11":[1,93]},"packages/frontend/@n8n/design-system/src/components/N8nSelectableList/SelectableList.vue":{"7":[93,12],"37":[93,12],"39":[93,12],"43":[93,12],"63":[93,12],"70":[93,12],"76":[93,12]},"packages/frontend/editor-ui/src/app/components/AiStarsIcon.vue":{"11":[93,8,12,13]},"packages/frontend/editor-ui/src/features/ai/chatHub/components/ToolListItem.vue":{"31":[93],"38":[93],"45":[93],"51":[93],"54":[93],"65":[93],"76":[93],"83":[93],"93":[93],"104":[93],"145":[93]},"packages/frontend/editor-ui/src/features/ai/chatHub/components/ToolsManagerModal.vue":{"50":[93],"51":[93],"55":[93],"62":[93],"79":[93],"81":[93],"105":[93],"107":[93],"115":[93],"123":[93],"137":[93],"142":[93],"143":[93],"149":[93],"156":[93],"160":[93],"174":[93],"210":[93],"214":[93],"271":[93],"294":[93],"304":[93],"330":[93],"339":[93],"359":[93],"362":[93],"384":[93],"397":[93],"406":[93],"414":[93],"429":[93],"437":[93],"444":[93],"465":[93],"466":[93]},"packages/frontend/editor-ui/src/features/ndv/parameters/components/ParameterInputOverrides/FromAiOverrideButton.vue":{"6":[93,8,12,13],"24":[93,8,12,13]},"packages/frontend/editor-ui/src/features/ndv/parameters/components/ParameterInputOverrides/FromAiOverrideField.vue":{"10":[93,12]},"packages/frontend/editor-ui/src/features/ndv/parameters/components/ParameterInputOverrides/ParameterOverrideSelectableList.vue":{"16":[93,12],"22":[93,12],"23":[93,12],"47":[93,12]},"packages/frontend/editor-ui/src/features/shared/toolConfig/NodeToolSettingsContent.vue":{"71":[93],"76":[93],"78":[93],"92":[93],"96":[93],"100":[93],"110":[93],"115":[93],"118":[93],"126":[93],"127":[93],"130":[93],"144":[93],"152":[93],"159":[93],"169":[93],"181":[93],"190":[93],"199":[93],"235":[93],"251":[93],"252":[93],"313":[93],"314":[93],"329":[93],"334":[93],"343":[93],"344":[93],"352":[93],"386":[93],"422":[93]},"packages/@n8n/blob-storage/src/json-store.ts":{"18":[33,135],"24":[33,135],"28":[33,135],"32":[33,135],"48":[33,135],"56":[33,135],"61":[33,135],"65":[33,135],"67":[33,135],"77":[33,135],"81":[33,135],"87":[33,135],"94":[33,135],"104":[33,135],"118":[33,135],"130":[33,135],"138":[33,135],"143":[33,135]},"packages/cli/src/credentials/credentials.service.ee.ts":{"29":[19,5,33,180,8,34,9,50,62,119,63,68],"42":[19,5,33,180,8,34,9,50,62,119,63,68],"81":[19,5,33,180,8,34,9,50,62,119,63,68],"83":[19,5,33,180,8,34,9,50,62,119,63,68],"87":[19,5,33,180,8,34,9,50,62,119,63,68],"98":[19,5,33,180,8,34,9,50,62,119,63,68],"102":[19,5,33,180,8,34,9,50,62,119,63,68],"190":[19,5,33,180,8,34,9,50,62,119,63,68],"203":[19,5,33,180,8,34,9,50,62,119,63,68],"251":[19,5,33,180,8,34,9,50,62,119,63,68],"253":[19,5,33,180,8,34,9,50,62,119,63,68]},"packages/cli/src/events/relays/workflow-failure-notification.event-relay.ts":{"15":[70,33,181,183],"24":[70,33,181,183],"26":[70,33,181,183],"35":[70,33,181,183]},"packages/cli/src/modules/dynamic-credentials.ee/services/credential-resolver-workflow.service.ts":{"33":[33,163],"49":[33,163],"59":[33,163],"93":[33,163],"130":[33,163],"134":[33,163],"138":[33,163],"154":[33,163],"213":[33,163],"239":[33,163],"254":[33,163],"267":[33,163]},"packages/cli/src/modules/dynamic-credentials.ee/services/dynamic-credential-cors.service.ts":{"12":[33,163],"30":[33,163],"31":[33,163],"53":[33,163],"61":[33,163]},"packages/cli/src/modules/dynamic-credentials.ee/services/dynamic-credential-web.service.ts":{"16":[33,163],"35":[33,163],"37":[33,163],"54":[33,163]},"packages/cli/src/modules/dynamic-credentials.ee/workflow-status.controller.ts":{"24":[33,163],"39":[33,163],"60":[33,163],"86":[33,163],"94":[33,163]},"packages/frontend/editor-ui/src/features/ai/chatHub/ChatWorkflowAgentsView.vue":{"30":[33],"31":[33],"32":[33],"49":[33],"68":[33],"76":[33]},"packages/frontend/editor-ui/src/features/ai/chatHub/components/ChatAgentCard.vue":{"24":[91,33],"30":[91,33],"36":[91],"43":[91,33],"53":[91,33],"56":[91,33],"74":[91],"76":[91]},"packages/frontend/editor-ui/src/features/ai/chatHub/components/ChatAgentSearchSort.vue":{"8":[91,33],"18":[91,33],"27":[91,33],"45":[91,33],"57":[91,33]},"packages/frontend/editor-ui/src/features/collaboration/projects/components/ProjectMembersRoleCell.vue":{"10":[33,105],"23":[33,105],"25":[33,105],"26":[33,105],"28":[33,105],"29":[33,105],"31":[33],"33":[33,105]},"packages/frontend/editor-ui/src/features/roles/components/CustomRolesUpgradeModal.vue":{"16":[33,105,117]},"packages/frontend/editor-ui/src/features/roles/components/RoleContactAdminModal.vue":{"9":[33,105,117],"32":[33,105,117]},"packages/frontend/editor-ui/src/features/roles/components/RoleHoverPopover.vue":{"16":[33,105,117],"76":[33,105,117]},"packages/frontend/editor-ui/src/features/roles/components/RoleSelectDropdown.vue":{"72":[33,117],"76":[33,117],"88":[33,105,117],"89":[33,105,117],"92":[33,105,117],"98":[33,105,117],"104":[33,105,117],"112":[33,105,117],"143":[33,117],"145":[33,117],"170":[33,105,117],"188":[33,105,117],"202":[33,105,117],"215":[33,105,117],"228":[33,105,117],"234":[33,105,117],"238":[33,105,117],"243":[33,105],"252":[33,105,117],"263":[33,105,117],"273":[33,105,117],"281":[33,105,117]},"packages/@n8n/agents/src/runtime/memory/observation-log-observer.ts":{"96":[37,72,74,75,41],"129":[37,72,74,75,41],"138":[37,72,74,75,41],"139":[37,72,74,75,41],"165":[37,72,74,75,41],"254":[37,72,74,75,41],"258":[37,72,74,75,41],"262":[37,72,74,75,41],"272":[37,72,74,75,41],"283":[37,72,74,75,41],"296":[37,72,74,75,41],"320":[37,72,74,75,41],"324":[37,72,74,75,41],"330":[37,72,74,75,41],"335":[37,72,74,75,41],"343":[37,72,74,75,41]},"packages/@n8n/agents/src/runtime/memory/observation-log-reflector.ts":{"64":[37,72,74,75],"79":[37,72,74,75],"80":[37,72,74,75],"81":[37,72,74,75],"106":[37,72,74,75],"110":[37,72,74,75],"111":[37,72,74,75],"120":[37,72,74,75],"122":[37,72,74,75],"126":[37,72,74,75],"127":[37,72,74,75],"130":[37,72,74,75],"131":[37,72,74,75],"132":[37,72,74,75],"133":[37,72,74,75],"143":[37,72,74,75],"159":[37,72,74,75],"166":[37,72,74,75],"198":[37,72,74,75],"226":[37,72,74,75],"228":[37,72,74,75],"238":[37,72,74,75],"251":[37,72,74,75],"265":[37,72,74,75],"274":[37,72,74,75],"283":[37,72,74,75],"295":[37,72,74,75],"300":[37,72,74,75],"317":[37,72,74,75],"336":[37,72,74,75],"342":[37,72,74,75],"345":[37,72,74,75],"352":[37,72,74,75],"356":[37,72,74,75],"359":[37,72,74,75],"364":[37,72,74,75],"370":[37,72,74,75]},"packages/@n8n/agents/src/runtime/memory/scoped-memory-task-runner.ts":{"73":[37,72,74,75],"84":[37,72,74,75],"88":[37,72,74,75],"89":[37,72,74,75],"96":[37,72,74,75],"97":[37,72,74,75],"103":[37,72,74,75],"107":[37,72,74,75],"123":[37,72,74,75],"124":[37,72,74,75],"135":[37,72,74,75],"189":[37,72,74,75],"194":[37,72,74,75],"216":[37,72,74,75],"218":[37,72,74,75],"225":[37,72,74,75],"231":[37,72,74,75],"242":[37,72,74,75],"253":[37,72,74,75],"266":[37,72,74,75],"274":[37,72,74,75]},"packages/@n8n/agents/src/runtime/memory/title-generation.ts":{"58":[37,72,74,75],"80":[37,72,74,75],"87":[37,72,74,75],"123":[37,72,74,75],"164":[37,72,74,75],"227":[37,72,74,75],"244":[37,72,74,75],"248":[37,72,74,75],"249":[37,72,74,75]},"packages/@n8n/agents/src/types/sdk/observation-log.ts":{"73":[37,72,74,75]},"packages/@n8n/instance-ai/src/runtime/liveness-policy.ts":{"28":[37,74,39,41],"56":[37,74,39,41],"58":[37,74,39,41],"59":[37,74,39,41],"62":[37,74,39,41],"93":[37,74,39,41]},"packages/cli/src/modules/instance-ai/trigger-run-data.ts":{"10":[37,41],"14":[37,41]},"localhost-46329/assets/_plugin-vue_export-helper-Da88TEg1.js":{"2":[37,38]},"localhost-46329/assets/chunk-CC9Q-vWm.js":{"8":[37,38],"9":[37,38],"10":[37,38],"19":[37,38],"23":[37,38],"29":[37,38],"33":[37,38],"34":[37,38]},"packages/@n8n/backend-network/src/http/client-request-error.ts":{"13":[34,134,181,68],"31":[34,134,181,68],"42":[34,134,181,68],"73":[34,134,181,68],"89":[34,134,181,68]},"localhost-35805/assets/_plugin-vue_export-helper-Da88TEg1.js":{"2":[78]},"localhost-35805/assets/chunk-CC9Q-vWm.js":{"8":[78],"9":[78],"10":[78],"19":[78],"23":[78],"29":[78],"33":[78],"34":[78]},"packages/frontend/editor-ui/src/app/utils/formatters/listFormatter.ts":{"15":[78,68]},"packages/frontend/editor-ui/src/features/credentials/components/CredentialPicker/CredentialPicker.vue":{"52":[5,92,78,68],"65":[5,92,78,68],"66":[5,92,68],"74":[5,92,78,68],"103":[5,92,78,68],"108":[5,92,78,68],"114":[92],"117":[68],"134":[92],"181":[68],"210":[5,92,68],"220":[5,92,78,68],"221":[5,92,78,68],"249":[5,92,68],"269":[5]},"packages/frontend/editor-ui/src/features/workflows/templates/components/AppsRequiringCredsNotice.vue":{"10":[78,68],"17":[78,68],"24":[78,68],"27":[78,68],"33":[78,68],"38":[78,68]},"packages/frontend/editor-ui/src/features/workflows/templates/components/IconSuccess.vue":{"2":[78,68]},"packages/frontend/editor-ui/src/features/workflows/templates/components/SetupTemplateFormStep.vue":{"42":[78,68],"44":[78,68],"48":[78,68],"52":[78,68],"53":[78,68],"60":[78,68],"71":[68],"85":[78,68],"98":[78,68]},"packages/frontend/editor-ui/src/features/workflows/templates/components/SetupWorkflowCredentialsModal.vue":{"13":[78,68],"29":[78,68],"46":[78,68],"52":[68],"61":[78,68],"65":[78,68],"67":[78,68],"71":[78,68],"83":[78,68],"90":[68],"98":[78,68],"105":[68]},"packages/frontend/editor-ui/src/features/workflows/templates/composables/useCredentialSetupState.ts":{"29":[78,68],"34":[78,68],"38":[78,68],"43":[78,68],"77":[78,68],"103":[78,68],"115":[78,68],"123":[78,68],"126":[78,68],"132":[78,68],"136":[78,68],"140":[78,68],"144":[68],"163":[78,68],"171":[68]},"packages/frontend/editor-ui/src/features/workflows/templates/composables/useSetupWorkflowCredentialsModalState.ts":{"9":[78,68],"15":[78,68],"34":[78,68],"40":[68],"52":[68],"66":[68]},"packages/frontend/editor-ui/src/features/workflows/templates/utils/templateTransforms.ts":{"38":[78,68],"47":[78,68],"55":[68],"73":[68],"99":[68],"122":[68],"127":[68]},"packages/@n8n/nodes-langchain/nodes/agents/Agent/AgentTool.node.ts":{"8":[12]},"packages/@n8n/nodes-langchain/nodes/agents/Agent/V2/AgentToolV2.node.ts":{"19":[12],"99":[12]},"packages/@n8n/nodes-langchain/nodes/agents/Agent/V2/utils.ts":{"11":[12],"23":[12],"26":[12],"86":[12],"89":[12]},"packages/@n8n/nodes-langchain/nodes/agents/Agent/V3/AgentToolV3.node.ts":{"22":[12],"101":[12]},"packages/@n8n/nodes-langchain/nodes/llms/LMChatOpenAi/LmChatOpenAi.node.ts":{"73":[1,12,13],"742":[1,12,13]},"packages/@n8n/nodes-langchain/nodes/llms/LMChatOpenAi/common.ts":{"15":[1,12,13],"18":[1,12,13],"21":[1,12,13],"24":[1,12,13],"27":[1,12,13],"30":[1,12,13],"86":[1,12,13]},"packages/@n8n/nodes-langchain/nodes/llms/LMChatOpenAi/methods/loadModels.ts":{"9":[1,12,13],"27":[1,12,13],"36":[1,12,13],"37":[1,12,13]},"packages/nodes-base/nodes/HackerNews/GenericFunctions.ts":{"16":[12,137,138,149],"42":[12,137,138,149]},"packages/nodes-base/nodes/HackerNews/HackerNews.node.ts":{"264":[12,137,138,149]},"packages/cli/src/modules/source-control.ee/source-control-export.service.ee.ts":{"70":[80],"94":[80],"98":[80],"102":[80],"106":[80],"114":[80],"116":[80],"123":[80],"130":[80],"152":[80],"155":[80],"164":[80],"175":[80],"208":[80],"219":[80],"232":[80],"257":[80],"273":[80],"324":[80],"346":[80],"347":[80],"380":[80],"414":[80],"419":[80],"455":[80],"486":[80],"488":[80],"497":[80],"515":[80],"518":[80],"525":[80],"527":[80],"530":[80],"545":[80],"586":[80],"605":[80],"609":[80],"616":[80],"630":[80],"646":[80]},"packages/cli/src/modules/source-control.ee/source-control-git.service.ee.ts":{"46":[80],"56":[80],"79":[80],"116":[80],"133":[80],"186":[80],"190":[80],"205":[80],"212":[80],"227":[80],"283":[80],"298":[80],"314":[80],"347":[80],"355":[80],"364":[80],"365":[80],"379":[80],"388":[80],"399":[80],"411":[80],"423":[80],"431":[80],"443":[80],"460":[80],"474":[80],"488":[80],"495":[80],"503":[80]},"packages/cli/src/modules/source-control.ee/source-control-helper.ee.ts":{"42":[80],"66":[80],"74":[80],"97":[80],"124":[80],"158":[80],"162":[80],"166":[80],"173":[80],"177":[80],"181":[80],"185":[80],"189":[80],"207":[80],"216":[80],"229":[80],"249":[80],"261":[80],"267":[80],"284":[80],"289":[80],"332":[80],"341":[80],"345":[80],"346":[80],"350":[80],"358":[80],"362":[80],"364":[80],"368":[80],"375":[80],"377":[80],"379":[80],"381":[80],"383":[80],"387":[80],"394":[80],"395":[80],"397":[80],"399":[80],"409":[80],"426":[80],"463":[80],"478":[80],"486":[80],"487":[80],"489":[80],"506":[80],"526":[80],"535":[80],"539":[80],"554":[80]},"packages/cli/src/modules/source-control.ee/source-control-preferences.service.ee.ts":{"33":[80],"45":[80],"53":[80],"60":[80],"68":[80],"78":[80],"86":[80],"94":[80],"106":[80],"115":[80],"130":[80],"138":[80],"164":[80],"178":[80],"192":[80],"205":[80],"233":[80],"237":[80],"241":[80],"245":[80],"249":[80],"253":[80],"272":[80],"324":[80],"332":[80]},"packages/cli/src/modules/source-control.ee/source-control.controller.ee.ts":{"32":[80],"41":[80],"68":[80],"133":[80],"178":[80],"188":[80],"197":[80],"228":[80],"244":[80],"253":[80],"270":[80],"287":[80],"301":[80]},"packages/cli/src/modules/source-control.ee/source-control.service.ee.ts":{"70":[80],"87":[80],"102":[80],"111":[80],"124":[80],"147":[80],"156":[80],"186":[80],"217":[80],"266":[80],"275":[80],"288":[80],"289":[80],"292":[80],"308":[80],"317":[80],"321":[80],"337":[80],"364":[80],"365":[80],"376":[80],"395":[80],"396":[80],"415":[80],"418":[80],"485":[80],"510":[80],"515":[80],"519":[80],"533":[80],"548":[80],"579":[80],"657":[80],"658":[80],"664":[80]},"localhost-45213/assets/_plugin-vue_export-helper-Da88TEg1.js":{"2":[80]},"localhost-45213/assets/chunk-CC9Q-vWm.js":{"8":[80],"9":[80],"10":[80],"19":[80],"23":[80],"29":[80],"33":[80],"34":[80]},"packages/frontend/@n8n/design-system/src/components/N8nColorPicker/ColorPicker.vue":{"21":[80],"34":[80],"59":[80]},"packages/frontend/editor-ui/src/features/integrations/sourceControl.ee/views/SettingsSourceControl.vue":{"43":[80],"44":[80],"55":[80],"86":[80],"115":[80],"133":[80],"144":[80],"153":[80],"174":[80],"178":[80],"206":[80],"284":[80],"285":[80],"335":[80],"345":[80],"349":[80],"426":[80],"444":[80],"449":[80],"450":[80],"467":[80],"489":[80],"496":[80],"515":[80],"517":[80],"536":[80]},"packages/cli/src/environments.ee/variables/variables.controller.ee.ts":{"19":[114],"22":[114],"32":[114],"50":[114],"60":[114],"80":[114]},"packages/frontend/@n8n/design-system/src/components/N8nDatatable/Datatable.vue":{"23":[114],"37":[114],"45":[114],"76":[114],"80":[114],"89":[114],"99":[114],"133":[114],"134":[114],"135":[114]},"packages/frontend/@n8n/design-system/src/components/TableBase/TableBase.vue":{"8":[114],"26":[114],"31":[114]},"packages/frontend/editor-ui/src/features/collaboration/projects/views/ProjectVariables.vue":{"68":[114],"78":[114],"82":[114],"87":[114],"91":[114],"93":[114],"94":[114],"109":[114],"114":[114],"150":[114],"184":[114],"193":[114],"197":[114],"216":[114],"222":[114],"269":[114],"281":[114],"290":[114],"314":[114],"400":[114],"434":[114],"435":[114],"451":[114],"452":[114],"463":[114]},"packages/frontend/editor-ui/src/features/settings/environments.ee/components/VariableModal.vue":{"65":[114],"70":[114],"94":[114],"98":[114],"117":[114],"132":[114],"133":[114],"136":[114],"149":[114],"175":[114],"193":[114],"194":[114],"198":[114],"203":[114],"207":[114],"243":[114],"248":[114],"265":[114],"278":[114],"291":[114],"298":[114],"321":[114],"326":[114],"332":[114],"339":[114],"341":[114],"347":[114],"361":[114]},"packages/frontend/editor-ui/src/features/settings/environments.ee/environments.api.ts":{"10":[185,114,120,140,154],"21":[185,114],"28":[185,114],"40":[185,114]},"packages/frontend/editor-ui/src/app/utils/rbac/checks/isEnterpriseFeatureEnabled.ts":{"4":[134],"15":[134]},"packages/frontend/editor-ui/src/app/utils/rbac/middleware/enterprise.ts":{"6":[134]},"packages/frontend/editor-ui/src/features/execution/executions/composables/useExecutionDebugging.ts":{"39":[48,134],"43":[48,134],"54":[48,134],"55":[48,134],"62":[48,134],"72":[48,134],"89":[48,134],"108":[48,134],"114":[48,134],"118":[48,134],"162":[48,134],"168":[48]},"packages/frontend/editor-ui/src/app/api/workflows.ee.ts":{"7":[185,120],"20":[185,50,51],"28":[185]},"packages/frontend/editor-ui/src/app/components/WorkflowShareModal.ee.vue":{"42":[62,120,160],"60":[62,120,160],"63":[62,120,160],"64":[62,120,160],"65":[120,160],"68":[62,120,160],"71":[62,120,160],"74":[62,120,160],"86":[62,120,160],"90":[62,120,160],"92":[62,120,160],"111":[62,120,160],"115":[62,120,160],"127":[120,160],"130":[62],"132":[62,120,160],"137":[62,120,160],"138":[62,120,160],"154":[120],"161":[120],"175":[120],"182":[120],"217":[120],"241":[62,120,160],"256":[62,120,160],"261":[120],"267":[62,120,160],"279":[62,120,160],"323":[62],"324":[62],"327":[62],"360":[62,120,160],"372":[62,120,160],"374":[62,120,160],"377":[62],"386":[120,160]},"packages/frontend/editor-ui/src/features/collaboration/projects/components/ProjectSharing.vue":{"70":"b:AAAAAAQADsAAAAAQAACgAQAAAAABAAAA","78":[92,34,49,50,51,117,119,63,120,160],"81":[92,34,49,50,51,117,119,63,120,160],"90":[92,34,49,50,51,117,119,63,120,160],"100":[92,34,50,119,63,120,160],"106":[92,34,119,63,120,160],"112":[92,34,49,50,51,117,119,63,120,160],"113":[92,34,49,50,51,117,119,63,120,160],"122":[92,34,49,50,51,117,119,63,120,160],"126":[92,34,49,50,51,117,119,63,120,160],"128":[92,34,49,50,51,117,119,63,120,160],"142":"b:AAAAAAQADsAAAAAQAACgAQAAAAABAAAA","158":[92,34,50,51,117,119,63,120],"164":"b:AAAAAAQADsAAAAAQAACgAQAAAAABAAAA","168":[92,34,50,51,117,119,63,120],"174":[50,51,117,119,63,120],"188":[34],"205":"b:AAAAAAQADsAAAAAQAACgAQAAAAABAAAA","206":"b:AAAAAAQADsAAAAAQAACgAQAAAAABAAAA","221":"b:AAAAAAQADsAAAAAQAACgAQAAAAABAAAA","239":[92,34,49,50,51,117,119,63,120,160],"247":[92,34,49,50,51,117,119,63,120,160],"250":[92,34,49,50,51,117,119,63,120,160],"254":[92,34,49,50,51,117,119,63,120,160],"278":[92,34,62,119,63,120,160],"279":[92,34,62,119,63,120,160],"301":[92,34,119,63,120],"318":[34]},"packages/frontend/editor-ui/src/features/collaboration/projects/components/ProjectSharingInfo.vue":{"14":"b:AAAAAAQADsAAAAAQAACgAQAAAAABAAAA","25":"b:AAAAAAQADsAAAAAQAACgAQAAAAABAAAA","31":"b:AAAAAAQADsAAAAAQAACgAQAAAAABAAAA"},"packages/frontend/editor-ui/src/features/ndv/parameters/components/ExpressionEditorModal/ExpressionEditorModalInput.vue":{"27":[140],"39":[140],"49":[140],"57":[140],"65":[140],"66":[140],"72":[140],"79":[140],"85":[140],"90":[140],"93":[140]},"packages/frontend/editor-ui/src/features/shared/tags/components/TagsDropdown.vue":{"64":[154,160],"65":[154,160],"70":[154,160],"71":[154,160],"74":[154,160],"78":[48,154,160],"87":[154,160],"88":[154],"96":[154,160],"104":[154],"122":[154,160],"126":[154],"131":[154,160],"133":[154,160],"136":[154],"152":[154,160],"153":[154,160],"154":[154,160],"163":[154,160],"172":[154,160],"183":[154],"190":[154,160],"199":[154],"200":[154],"206":[154,160],"240":[154],"247":[48,154],"255":[154,160],"265":[48,154,160]},"packages/frontend/editor-ui/src/features/shared/tags/components/TagsManager/NoTagsView.vue":{"21":[154],"22":[154],"27":[154],"31":[154],"41":[154]},"packages/frontend/editor-ui/src/features/shared/tags/components/TagsManager/TagsManager.vue":{"31":[154],"44":[154],"48":[154],"50":[154],"51":[154],"53":[154],"57":[154],"61":[154],"65":[154],"69":[154],"119":[154],"120":[154],"132":[154],"135":[154],"150":[154],"163":[154],"189":[154]},"packages/frontend/editor-ui/src/features/shared/tags/components/TagsManager/TagsView/TagsTable.vue":{"33":[154],"42":[154],"46":[154],"73":[154],"84":[154],"88":[154],"92":[154],"93":[154],"100":[154],"101":[154],"109":[154],"118":[154],"119":[154],"127":[154],"132":[154],"145":[154],"147":[154],"148":[154],"150":[154],"169":[154],"171":[154],"181":[154],"183":[154],"235":[154]},"packages/frontend/editor-ui/src/features/shared/tags/components/TagsManager/TagsView/TagsTableHeader.vue":{"21":[154],"30":[154],"36":[154],"41":[154],"42":[154],"50":[154],"56":[154]},"packages/frontend/editor-ui/src/features/shared/tags/components/TagsManager/TagsView/TagsView.vue":{"12":[154],"35":[154],"48":[154],"52":[154],"53":[154],"60":[154],"61":[154],"76":[154],"85":[154],"112":[154],"116":[154],"120":[154],"122":[154],"132":[154],"137":[154],"142":[154],"145":[154],"156":[154],"177":[154]},"packages/frontend/editor-ui/src/features/shared/tags/components/TagsManager/WorkflowTagsManager.vue":{"11":[154],"16":[154],"17":[154],"19":[154],"29":[154],"64":[154],"85":[154]},"packages/frontend/editor-ui/src/features/shared/tags/components/WorkflowTagsDropdown.vue":{"35":[154,160],"36":[154,160],"39":[154,160],"40":[154,160],"41":[154,160],"43":[154,160],"44":[154,160],"46":[154],"50":[154],"58":[154,160]},"packages/frontend/editor-ui/src/features/shared/tags/useTagPermissions.ts":{"4":[154,160],"6":[154,160],"7":[154],"8":[154],"11":[48,154,160]},"localhost-35319/assets/_plugin-vue_export-helper-Da88TEg1.js":{"2":[32,100,113,118,139,148]},"localhost-35319/assets/chunk-CC9Q-vWm.js":{"8":[32,100,113,118,139,148],"9":[32,100,113,118,139,148],"10":[32,100,113,118,139,148],"19":[32,100,113,118,139,148],"23":[32,100,113,118,139,148],"29":[32,100,113,118,139,148],"33":[32,100,113,118,139,148],"34":[32,100,113,118,139,148]},"localhost-35319/assets/verified-L2wNNiNu.js":{"8":[32,139]},"packages/frontend/editor-ui/src/features/shared/editors/components/CodeNodeEditor/completer.ts":{"23":[32],"27":[32]},"packages/cli/src/modules/instance-ai/message-parser.ts":{"77":[73,74,75,76],"83":[73,74,75,76],"89":[73,74,75,76],"91":[73,74,75,76],"98":[73,74,75,76],"100":[73,74,75,76],"107":[73,74,75,76],"112":[73,74,75,76],"116":[73,74,75,76],"122":[73,74,75,76],"156":[73,74,75,76],"159":[73,74,75,76],"180":[73,74,75,76],"209":[73,74,75,76],"258":[73,74,75,76],"270":[73,74,75,76],"289":[73,74,75,76],"310":[73,74,75,76],"323":[73,74,75,76],"327":[73,74,75,76],"331":[73,74,75,76],"335":[73,74,75,76],"346":[73,74,75,76],"377":[73,74,75,76],"385":[73,74,75,76],"399":[73,74,75,76],"408":[73,74,75,76],"421":[73,74,75,76],"486":[73,74,75,76],"624":[73,74,75,76],"634":[73,74,75,76],"664":[73,74,75,76],"673":[73,74,75,76],"677":[73,74,75,76],"694":[73,74,75,76],"700":[73,74,75,76],"712":[73,74,75,76]},"packages/cli/src/modules/instance-ai/repositories/instance-ai-checkpoint.repository.ts":{"8":[73,74,75,76],"21":[73,74,75,76],"34":[73,74,75,76]},"localhost-46769/assets/_plugin-vue_export-helper-Da88TEg1.js":{"2":[73,95]},"localhost-46769/assets/chunk-CC9Q-vWm.js":{"8":[73,95],"9":[73,95],"10":[73,95],"19":[73,95],"23":[73,95],"29":[73,95],"33":[73,95],"34":[73,95]},"packages/frontend/@n8n/design-system/src/components/N8nAiActivityStepButton/AiActivityStepButton.vue":{"4":[73],"28":[73],"38":[73]},"packages/frontend/editor-ui/src/features/ai/instanceAi/components/AgentSection.vue":{"15":[73],"21":[73],"24":[73],"26":[73],"40":[73],"50":[73],"58":[73],"64":[73],"65":[73],"66":[73],"68":[73],"81":[73]},"packages/community-packages.controller.ts":{"9":[100],"13":[100],"21":[100],"27":[100],"35":[100]},"packages/community-packages.lifecycle.service.ts":{"35":[100],"37":[100],"49":[100],"60":[100],"66":[100],"110":[100],"209":[100],"225":[100],"233":[100],"251":[100],"286":[100],"296":[100],"311":[100],"318":[100],"337":[100],"374":[100],"388":[100]},"packages/frontend/editor-ui/src/features/settings/communityNodes/communityNodes.utils.ts":{"10":[100]},"packages/frontend/editor-ui/src/features/settings/communityNodes/components/nodeCreator/CommunityNodeDetails.vue":{"19":[100,48],"28":[48],"30":[48],"39":[100,48],"41":[48],"43":[48],"74":[48],"79":[48],"97":[100,48],"115":[48],"118":[48]},"packages/frontend/editor-ui/src/features/settings/communityNodes/components/nodeCreator/CommunityNodeDocsLink.vue":{"9":[100,48],"11":[48],"14":[100,48],"23":[100,48]},"packages/frontend/editor-ui/src/features/settings/communityNodes/components/nodeCreator/CommunityNodeFooter.vue":{"15":[100],"32":[100],"52":[100],"57":[100],"72":[100],"73":[100],"79":[100],"80":[100]},"packages/frontend/editor-ui/src/features/settings/communityNodes/components/nodeCreator/CommunityNodeInfo.vue":{"17":[100,48],"29":[100,48],"33":[100,48],"41":[100,48],"43":[48],"48":[100,48],"65":[48],"101":[48],"109":[100,48],"114":[100,48],"119":[100,48],"130":[48],"132":[48],"138":[48],"144":[48],"145":[100,48],"148":[100,48],"156":[48],"163":[48],"168":[48],"169":[48],"172":[48]},"packages/frontend/editor-ui/src/features/shared/nodeCreator/components/ItemTypes/CommunityNodeItem.vue":{"11":[100,48]},"packages/frontend/editor-ui/src/features/settings/communityNodes/components/CommunityPackageCard.vue":{"43":[113],"47":[113],"55":[113],"63":[113],"80":[113],"98":[113],"103":[113],"105":[113],"108":[113],"120":[113],"135":[113],"142":[113],"151":[113],"168":[113],"176":[113]},"packages/frontend/editor-ui/src/features/settings/communityNodes/components/CommunityPackageInstallModal.vue":{"32":[113],"39":[113],"47":[113],"54":[113],"78":[113],"82":[113],"86":[113],"98":[113],"111":[113],"119":[113],"134":[113],"167":[113],"172":[113],"178":[113]},"packages/frontend/editor-ui/src/features/settings/communityNodes/components/CommunityPackageManageConfirmModal.vue":{"32":[113],"54":[113],"57":[113],"68":[113],"69":[113],"72":[113],"108":[113],"112":[113],"120":[113],"124":[113],"147":[113],"151":[113],"190":[113],"192":[113],"203":[113],"216":[113],"226":[113],"232":[113],"240":[113],"252":[113],"253":[113],"261":[113],"270":[113]},"packages/frontend/editor-ui/src/features/settings/communityNodes/composables/useInstallNode.ts":{"60":[113]},"packages/frontend/editor-ui/src/features/settings/communityNodes/views/SettingsCommunityNodesView.vue":{"40":[113],"44":[113],"48":[113],"56":[113],"77":[113],"82":[113],"94":[113],"104":[113],"110":[113],"117":[113],"122":[113],"126":[113],"130":[113],"178":[113]},"packages/@n8n/syslog-client/src/client.ts":{"78":[60],"129":[60],"147":[60],"148":[60],"162":[60],"185":[60],"197":[60],"205":[60],"214":[60],"233":[60],"253":[60],"279":[60],"293":[60],"310":[60],"322":[60],"337":[60],"342":[60],"350":[60],"365":[60],"377":[60],"391":[60],"420":[60],"438":[60],"453":[60],"468":[60],"475":[60],"483":[60],"501":[60]},"packages/@n8n/syslog-client/src/factory.ts":{"25":[60]},"packages/@n8n/syslog-client/src/index.ts":{"13":[60]},"packages/@n8n/syslog-client/src/schemas.ts":{"9":[60],"10":[60],"11":[60]},"packages/@n8n/syslog-client/src/utils.ts":{"7":[60],"15":[60],"37":[60],"69":[60]},"packages/cli/src/modules/log-streaming.ee/destinations/message-event-bus-destination-syslog.ee.ts":{"19":[60],"47":[60],"79":[60],"84":[60],"101":[60],"116":[60],"121":[60],"135":[60],"149":[60],"153":[60]},"localhost-36241/assets/_plugin-vue_export-helper-Da88TEg1.js":{"2":[115]},"localhost-36241/assets/chunk-CC9Q-vWm.js":{"8":[115],"9":[115],"10":[115],"19":[115],"23":[115],"29":[115],"33":[115],"34":[115]},"packages/cli/src/controllers/orchestration.controller.ts":{"9":[118],"20":[118]},"packages/cli/src/scaling/worker-status.service.ee.ts":{"16":[118],"23":[118],"33":[118],"48":[118],"59":[118],"91":[118],"92":[118],"102":[118]},"packages/@n8n/ai-workflow-builder.ee/src/agents/planner.agent.ts":{"62":[20],"64":[20],"109":[20],"136":[20]},"packages/@n8n/ai-workflow-builder.ee/src/agents/responder.agent.ts":{"78":[20],"104":[20],"108":[20],"112":[20],"121":[20],"186":[20],"231":[20],"269":[20]},"packages/@n8n/ai-workflow-builder.ee/src/agents/supervisor.agent.ts":{"23":[20],"66":[20],"74":[20],"122":[20]},"packages/@n8n/ai-workflow-builder.ee/src/ai-workflow-builder-agent.service.ts":{"39":[20],"64":[20],"69":[20],"90":[20],"106":[20],"163":[20],"171":[20],"175":[20],"184":[20],"190":[20],"200":[20],"237":[20],"248":[20],"271":[20],"330":[20],"367":[20],"369":[20],"376":[20],"378":[20],"383":[20],"384":[20],"389":[20],"404":[20],"427":[20],"433":[20],"451":[20],"470":[20],"486":[20],"505":[20],"545":[20]},"packages/@n8n/ai-workflow-builder.ee/src/llm-config.ts":{"26":[20],"28":[20],"43":[20],"45":[20],"64":[20],"66":[20],"90":[20],"92":[20],"112":[20],"114":[20],"144":[20],"145":[20],"147":[20],"243":[20]},"packages/@n8n/ai-workflow-builder.ee/src/multi-agent-workflow-subgraphs.ts":{"56":[20],"73":[20],"113":[20],"118":[20],"187":[20],"194":[20],"211":[20],"252":[20],"275":[20],"286":[20],"353":[20],"379":[20],"391":[20],"392":[20],"402":[20],"403":[20],"406":[20],"433":[20],"444":[20],"452":[20],"506":[20],"525":[20],"527":[20],"536":[20],"544":[20]},"packages/@n8n/ai-workflow-builder.ee/src/prompts/agents/discovery.prompt.ts":{"30":[20],"32":[20],"36":[20],"38":[20],"347":[20],"358":[20],"368":[20],"372":[20]},"packages/@n8n/ai-workflow-builder.ee/src/prompts/agents/planner.prompt.ts":{"73":[20],"98":[20],"109":[20],"122":[20],"129":[20],"130":[20]},"packages/@n8n/ai-workflow-builder.ee/src/prompts/agents/responder.prompt.ts":{"168":[20],"178":[20],"188":[20],"200":[20],"205":[20],"226":[20],"232":[20],"244":[20]},"packages/@n8n/ai-workflow-builder.ee/src/prompts/builder/prompt-builder.ts":{"17":[20],"30":[20],"44":[20],"55":[20],"79":[20],"116":[20],"129":[20],"149":[20],"175":[20],"197":[20],"228":[20],"252":[20],"268":[20],"282":[20],"302":[20],"330":[20],"357":[20]},"packages/@n8n/ai-workflow-builder.ee/src/prompts/index.ts":{"31":[20],"45":[20],"47":[20]},"packages/@n8n/ai-workflow-builder.ee/src/session-manager.service.ts":{"36":[20],"54":[20],"62":[20],"72":[20],"83":[20],"87":[20],"96":[20],"104":[20],"118":[20],"132":[20],"160":[20],"166":[20],"174":[20],"180":[20],"185":[20],"200":[20],"222":[20],"270":[20],"275":[20],"279":[20],"283":[20],"300":[20],"312":[20],"335":[20],"363":[20],"378":[20],"427":[20],"440":[20],"532":[20],"564":[20],"583":[20],"666":[20],"674":[20]},"packages/@n8n/ai-workflow-builder.ee/src/subgraphs/discovery.subgraph.ts":{"103":[20],"104":[20],"109":[20],"110":[20],"115":[20],"116":[20],"121":[20],"122":[20],"127":[20],"128":[20],"133":[20],"134":[20],"139":[20],"140":[20],"145":[20],"146":[20],"151":[20],"152":[20],"175":[20],"176":[20],"181":[20],"187":[20],"193":[20],"199":[20],"200":[20],"205":[20],"206":[20],"211":[20],"212":[20],"217":[20],"218":[20],"223":[20],"224":[20],"229":[20],"230":[20],"237":[20],"238":[20],"276":[20],"304":[20],"307":[20],"351":[20],"377":[20],"396":[20],"440":[20],"447":[20],"494":[20],"501":[20],"561":[20],"563":[20],"591":[20],"659":[20],"670":[20],"709":[20],"721":[20],"769":[20],"795":[20]},"packages/@n8n/ai-workflow-builder.ee/src/tools/get-documentation.tool.ts":{"60":[20],"81":[20],"88":[20],"113":[20],"115":[20],"136":[20],"146":[20]},"packages/@n8n/ai-workflow-builder.ee/src/tools/node-search.tool.ts":{"46":[20],"91":[20],"105":[20],"121":[20],"123":[20],"170":[20]},"packages/@n8n/ai-workflow-builder.ee/src/tools/utils/ssrf-guard.ts":{"28":[20],"38":[20],"40":[20],"41":[20],"43":[20],"53":[20],"63":[20]},"packages/@n8n/ai-workflow-builder.ee/src/tools/utils/web-fetch-security.ts":{"35":[20],"48":[20],"55":[20],"61":[20],"64":[20],"67":[20],"70":[20],"111":[20],"112":[20],"120":[20],"121":[20],"122":[20],"123":[20],"127":[20],"131":[20],"144":[20],"146":[20],"163":[20],"167":[20]},"packages/@n8n/ai-workflow-builder.ee/src/tools/web-fetch.tool.ts":{"30":[20],"69":[20],"83":[20],"86":[20]},"packages/@n8n/ai-workflow-builder.ee/src/utils/thread-id.ts":{"7":[20]},"packages/@n8n/ai-workflow-builder.ee/src/workflow-builder-agent.ts":{"166":[20],"184":[20],"196":[20],"204":[20],"213":[20],"234":[20],"315":[20],"344":[20],"362":[20],"392":[20],"410":[20],"441":[20],"463":[20],"476":[20],"515":[20],"587":[20],"602":[20],"618":[20],"668":[20],"686":[20],"690":[20]},"packages/@n8n/api-types/src/dto/ai/ai-build-request.dto.ts":{"41":[20],"51":[20],"61":[20],"63":[20],"72":[20],"75":[20],"86":[20],"95":[20]},"packages/cli/src/controllers/ai.controller.ts":{"37":[20],"45":[20],"65":[20],"74":[20],"148":[20],"153":[20],"162":[20],"187":[20],"201":[20],"219":[20],"230":[20],"250":[20],"261":[20],"272":[20],"287":[20],"301":[20],"322":[20],"338":[20]},"packages/cli/src/modules/workflow-builder/workflow-builder-session.repository.ts":{"22":[20],"26":[20],"46":[20],"73":[20],"78":[20]},"packages/frontend/editor-ui/src/features/settings/orchestration.ee/components/WorkerList.vue":{"25":[118],"35":[118],"39":[118],"41":[118],"49":[118],"60":[118]},"packages/frontend/editor-ui/src/features/settings/orchestration.ee/orchestration.store.ts":{"26":[48,118],"34":[48],"47":[48],"56":[48,118],"59":[48],"65":[48],"71":[48],"74":[48],"77":[48]},"packages/frontend/editor-ui/src/features/settings/orchestration.ee/views/WorkerView.vue":{"30":[118],"33":[118]},"packages/frontend/@n8n/design-system/src/components/N8nIcon/nodes/sort.svg":{"11":[139,144,145]},"packages/frontend/editor-ui/src/features/ndv/parameters/utils/assignmentCollection.utils.ts":{"8":[139],"18":[139]},"packages/frontend/editor-ui/src/features/ndv/parameters/components/ResourceMapper/MappingFields.vue":{"49":[148],"74":[148],"83":[148],"85":[148],"86":[148],"101":[148],"104":[148],"117":[148],"118":[148],"121":[148],"122":[148],"131":[148],"132":[148],"134":[148],"161":[148],"164":[148],"178":[148],"182":[148],"186":[148],"196":[148],"204":[148],"214":[148],"238":[148],"252":[148],"267":[148],"280":[148],"284":[148],"288":[148],"296":[148],"315":[148],"328":[148],"366":[148],"397":[148],"442":[148]},"packages/frontend/editor-ui/src/features/ndv/parameters/components/ResourceMapper/MappingModeSelect.vue":{"20":[147,148],"54":[147,148],"61":[147,148],"103":[147,148],"113":[147,148],"121":[147,148],"123":[147,148],"128":[147,148],"140":[147,148],"150":[147,148],"153":[147,148]},"packages/frontend/editor-ui/src/features/ndv/parameters/components/ResourceMapper/MatchingColumnsSelect.vue":{"27":[148],"39":[148],"50":[148],"51":[148],"62":[148],"63":[148],"69":[148],"84":[148],"103":[148],"117":[148],"130":[148],"156":[148],"169":[148],"182":[148],"191":[148],"200":[148],"202":[148],"206":[148],"211":[148]},"packages/frontend/editor-ui/src/features/ndv/parameters/composables/useNodeSpecificationValues.ts":{"5":[147,148],"6":[147,148],"10":[147,148],"16":[148],"20":[147,148],"24":[148]},"packages/cli/src/errors/response-errors/workflow-activation-bad-request.error.ts":{"8":[65]},"packages/cli/src/errors/response-errors/workflow-validation.error.ts":{"9":[65]},"packages/cli/src/executions/execution.utils.ts":{"13":[22,23,101,47,6,181,151,65,183,157],"26":[22,23,101,47,6,181,151,65,183,157]},"packages/cli/src/executions/pre-execution-checks/subworkflow-policy-checker.ts":{"17":[181,151,65],"28":[181,151,65],"61":[181,151,65],"79":[181,151,65],"88":[181,151,65],"106":[181,151,65],"110":[181,151,65],"111":[181,151,65],"122":[181,151,65]},"localhost-33101/assets/_plugin-vue_export-helper-Da88TEg1.js":{"2":[67]},"localhost-33101/assets/chunk-CC9Q-vWm.js":{"8":[67],"9":[67],"10":[67],"19":[67],"23":[67],"29":[67],"33":[67],"34":[67]},"packages/@n8n/config/src/configs/ai.config.ts":{"6":[1],"34":[1]},"packages/@n8n/nodes-langchain/nodes/memory/MemoryRedisChat/MemoryRedisChat.node.ts":{"26":[1,13],"114":[1,13],"148":[1,13],"178":[1,13]},"packages/@n8n/nodes-langchain/nodes/tools/ToolCalculator/ToolCalculator.node.ts":{"15":[1],"21":[1],"55":[1],"61":[1]},"packages/@n8n/nodes-langchain/nodes/tools/ToolCode/ToolCode.node.ts":{"46":[1,9,11],"72":[1,9,11],"92":[1,9,11],"176":[1,9,11],"329":[1,9,11],"334":[1,9,11]},"localhost-34277/assets/_plugin-vue_export-helper-Da88TEg1.js":{"2":[1,7]},"localhost-34277/assets/chunk-CC9Q-vWm.js":{"8":[1,7],"9":[1,7],"10":[1,7],"19":[1,7],"23":[1,7],"29":[1,7],"33":[1,7],"34":[1,7]},"localhost-34277/assets/verified-L2wNNiNu.js":{"8":[1,7]},"packages/frontend/@n8n/design-system/src/components/N8nIcon/nodes/item-list-output-parser.svg":{"11":[1]},"packages/frontend/@n8n/design-system/src/components/N8nIcon/nodes/model-selector.svg":{"11":[1,7,13]},"packages/frontend/@n8n/design-system/src/components/N8nIcon/nodes/simple-memory.svg":{"11":[1,13]},"packages/frontend/@n8n/design-system/src/components/N8nIcon/nodes/vector-store-question-answer-tool.svg":{"11":[1]},"packages/@n8n/client-oauth2/src/client-oauth2-token.ts":{"28":[163,169,147],"43":[163,169,147],"75":[163,169,147],"132":[163,169,147]},"packages/@n8n/client-oauth2/src/client-oauth2.ts":{"50":[35,163,147],"68":[35,163,147],"76":[35,163,147],"89":[35,163,147],"102":[35,163,147],"105":[35,163,147],"141":[35,163,147]},"packages/@n8n/client-oauth2/src/code-flow.ts":{"25":[35,163,147],"30":[35,163,147],"61":[35,163,147]},"packages/@n8n/client-oauth2/src/credential-options.ts":{"22":[163,147]},"packages/@n8n/client-oauth2/src/credentials-flow.ts":{"25":[35,163,147],"30":[35,163,147]},"packages/@n8n/client-oauth2/src/index.ts":{"2":[35,163,147],"5":[35,163,147]},"packages/@n8n/client-oauth2/src/utils.ts":{"8":[35,163,147],"22":[35,163,147],"34":[35,163,147],"51":[35,163,147],"58":[35,163,147],"65":[35,163,147]},"packages/cli/src/controllers/oauth/oauth2-credential.controller.ts":{"22":[35,163,147],"33":[35,163,147],"41":[35,163,147],"180":[35,163,147]},"packages/cli/src/oauth/oauth-browser-binding.service.ts":{"16":[35,163,147,68],"19":[35,163,147,68],"31":[35,163,147,68],"48":[35,163,147,68],"58":[35,163,147,68],"72":[35,163,147,68]},"packages/cli/src/oauth/oauth.service.ts":{"86":[35,163,147,68],"96":[35,163,147,68],"103":[35,163,147,68],"111":[35,163,147,68],"143":[35,163,147,68],"147":[35,163,147,68],"156":[35,163,147,68],"184":[35,163,147,68],"188":[35,163,147,68],"197":[35,163,147,68],"211":[35,163,147,68],"250":[35,163,147,68],"255":[35,163,147,68],"295":[35,163,147,68],"322":[35,163,147,68],"329":[35,163,147,68],"339":[35,163,147,68],"346":[35,163,147,68],"361":[35,163,147,68],"376":[35,163,147,68],"398":[35,163,147,68],"434":[35,163,147,68],"450":[35,163,147,68],"469":[35,163,147,68],"478":[35,163,147,68],"486":[35,163,147,68],"494":[35,163,147,68],"588":[35,163,147,68],"601":[35,163,147,68],"639":[35,163,147,68],"649":[35,163,147,68],"664":[35,163,147,68],"695":[35,163,147,68],"696":[35,163,147,68],"699":[35,163,147,68],"709":[35,163,147,68],"712":[35,163,147,68],"726":[35,163,147,68],"742":[35,163,147,68],"807":[35,163,147,68],"810":[35,163,147,68],"817":[35,163,147,68],"896":[35,163,147,68],"907":[35,163,147,68],"996":[35,163,147,68],"1057":[35,163,147,68],"1125":[35,163,147,68],"1141":[35,163,147,68],"1167":[35,163,147,68],"1240":[35,163,147,68],"1252":[35,163,147,68],"1296":[35,163,147,68],"1324":[35,163,147,68],"1337":[35,163,147,68],"1355":[35,163,147,68],"1400":[35,163,147,68],"1419":[35,163,147,68],"1475":[35,163,147,68],"1486":[35,163,147,68],"1512":[35,163,147,68]},"packages/cli/src/oauth/validate-oauth-url.ts":{"12":[35,163,147,68]},"packages/core/src/execution-engine/node-execution-context/utils/request-helpers/oauth.ts":{"45":[169,147],"49":[169,147],"70":[169,147],"87":[169,147],"111":[169,147],"122":[169,147],"128":[169,147],"144":[169,147],"164":[169,147],"303":[169,147],"317":[169,147],"323":[169,147],"331":[169,147],"443":[169,147],"458":[169,147],"460":[169,147],"468":[169,147],"480":[169,147],"483":[169,147],"491":[169,147],"515":[169,147],"557":[169,147],"563":[169,147]},"packages/nodes-base/nodes/Github/GenericFunctions.ts":{"16":[147],"75":[147],"96":[147],"121":[147],"126":[147]},"packages/nodes-base/nodes/Github/GithubTrigger.node.ts":{"474":[147],"506":[147],"598":[147],"634":[147]},"packages/nodes-base/nodes/Github/GithubTriggerHelpers.ts":{"15":[147]},"packages/nodes-base/nodes/Github/SearchFunctions.ts":{"33":[147],"58":[147],"69":[147],"95":[147],"106":[147],"126":[147],"136":[147],"178":[147]},"packages/@n8n/nodes-langchain/nodes/chains/ChainLLM/ChainLlm.node.ts":{"23":[7,13],"67":[7,13],"87":[7,13],"93":[7,13],"119":[7,13],"137":[7,13]},"packages/@n8n/nodes-langchain/nodes/chains/ChainLLM/methods/chainExecutor.ts":{"20":[7,13],"35":[7,13],"46":[7,13],"61":[7,13],"70":[7,13],"96":[7,13],"119":[7,13],"127":[7,13],"141":[7,13]},"packages/@n8n/nodes-langchain/nodes/chains/ChainLLM/methods/config.ts":{"21":[7,13]},"packages/@n8n/nodes-langchain/nodes/chains/ChainLLM/methods/imageUtils.ts":{"15":[7,13],"27":[7,13],"82":[7,13]},"packages/@n8n/nodes-langchain/nodes/chains/ChainLLM/methods/index.ts":{"1":[7,13]},"packages/@n8n/nodes-langchain/nodes/chains/ChainLLM/methods/processItem.ts":{"11":[7,13],"33":[7,13]},"packages/@n8n/nodes-langchain/nodes/chains/ChainLLM/methods/promptUtils.ts":{"23":[7,13],"34":[7,13],"44":[7,13],"50":[7,13],"66":[7,13],"75":[7,13],"120":[7,13],"149":[7,13],"153":[7,13],"158":[7,13],"159":[7,13],"168":[7,13]},"packages/@n8n/nodes-langchain/nodes/chains/ChainLLM/methods/responseFormatter.ts":{"6":[7,13]},"packages/@n8n/nodes-langchain/utils/output_parsers/parserInput.ts":{"10":[7,13]},"packages/frontend/@n8n/design-system/src/components/N8nIcon/nodes/chat-memory-manager.svg":{"11":[7,125]},"packages/cli/src/utils/cors.util.ts":{"3":[22,23,101,47,6,183,157]},"packages/cli/src/webhooks/waiting-forms.ts":{"18":[22,23,101],"21":[22,23,101],"25":[22,23,101],"31":[22,23,101],"42":[22,23,101],"54":[22,23,101],"74":[22,23,101],"153":[22,23,101]},"packages/cli/src/webhooks/waiting-webhooks.ts":{"48":[22,23,101,47,6,183,157],"60":[22,23,101,47,6,183,157],"68":[22,23,101,47,6,183,157],"72":[22,23,101,47,6,183,157],"76":[22,23,101,47,6,183,157],"80":[22,23,101,47,6,183,157],"90":[22,23,101,47,6,183,157],"103":[22,23,101,47,6,183,157],"109":[22,23,101,47,6,183,157],"128":[22,23,101,47,6,183,157],"141":[22,23,101,47,6,183,157],"154":[22,23,101,47,6,183,157],"165":[22,23,101,47,6,183,157],"191":[22,23,101,47,6,183,157],"212":[22,23,101,47,6,183,157],"233":[22,23,101,47,6,183,157],"323":[22,23,101,47,6,183,157],"332":[22,23,101,47,6,183,157],"383":[22,23,101,47,6,183,157],"411":[22,23,101,47,6,183,157],"424":[22,23,101,47,6,183,157]},"packages/cli/src/webhooks/webhook-form-data.ts":{"10":[22,23,101,47],"13":[22,23,101,47],"20":[22,23,101,47],"32":[22,23,101,47],"33":[22,23,101,47],"46":[22,23,101,47],"47":[22,23,101,47]},"packages/nodes-base/nodes/Wait/Wait.node.ts":{"229":[22,23,41,53,135,6,183,155,157],"501":[22,23,41,53,135,6,183,155,157],"507":[22,23,41,53,135,6,183,155,157],"522":[22,23,41,53,135,6,183,155,157],"599":[22,23,41,53,135,6,183,155,157],"600":[22,23,41,53,135,6,183,155,157],"601":[22,23,41,53,135,6,183,155,157],"612":[22,23,41,53,135,6,183,155,157],"643":[22,23,41,53,135,6,183,155,157]},"packages/nodes-base/nodes/Wait/validation.ts":{"1":[22,23,41,53,135,6,183,155,157],"6":[22,23,41,53,135,6,183,155,157]},"localhost-40291/assets/_plugin-vue_export-helper-Da88TEg1.js":{"2":[23,35,104,111,137,158]},"localhost-40291/assets/chunk-CC9Q-vWm.js":{"8":[23,35,104,111,137,158],"9":[23,35,104,111,137,158],"10":[23,35,104,111,137,158],"19":[23,35,104,111,137,158],"23":[23,35,104,111,137,158],"29":[23,35,104,111,137,158],"33":[23,35,104,111,137,158],"34":[23,35,104,111,137,158]},"localhost-46645/assets/_plugin-vue_export-helper-Da88TEg1.js":{"2":[71]},"localhost-46645/assets/chunk-CC9Q-vWm.js":{"8":[71],"9":[71],"10":[71],"19":[71],"23":[71],"29":[71],"33":[71],"34":[71]},"packages/frontend/editor-ui/src/features/settings/apiKeys/apiKeys.store.ts":{"18":[71],"24":[71]},"packages/frontend/editor-ui/src/features/settings/apiKeys/components/ApiKeyScopesModal.vue":{"7":[71],"18":[71],"27":[71]},"packages/frontend/editor-ui/src/features/settings/apiKeys/components/RevokeApiKeyConfirmModal.vue":{"23":[71],"31":[71],"43":[71]},"packages/frontend/editor-ui/src/features/settings/apiKeys/components/RotateApiKeyConfirmModal.vue":{"7":[71],"21":[71],"28":[71]},"packages/frontend/editor-ui/src/features/settings/apiKeys/views/SettingsApiView.vue":{"200":[71],"297":[71]},"packages/frontend/editor-ui/src/features/shared/banners/components/banners/BaseBanner.vue":{"18":[71],"34":[71],"44":[71],"59":[71]},"packages/frontend/editor-ui/src/features/shared/banners/components/banners/TrialBanner.vue":{"73":[71],"80":[71],"81":[71],"82":[71],"90":[71],"93":[71],"99":[71],"104":[71],"106":[71],"120":[71],"130":[71],"135":[71],"143":[71],"148":[71],"166":[71],"178":[71],"194":[71],"198":[71],"204":[71],"212":[71]},"packages/frontend/@n8n/design-system/src/components/N8nRecycleScroller/RecycleScroller.vue":{"14":[29,8,34,35,50,51,58,119,63],"30":[29,8,34,35,50,51,58,119,63],"31":[29,8,34,35,50,51,58,119,63],"48":[29,8,34,35,50,51,58,119,63],"50":[29,8,34,35,50,51,58,119,63],"60":[29,8,34,35,50,51,58,119,63],"61":[29,8,34,35,50,51,58,119,63],"72":[29,8,34,35,50,51,58,119,63],"76":[29,8,34,35,50,51,58,119,63],"77":[29,8,34,35,50,51,58,119,63],"79":[29,8,34,35,50,51,58,119,63],"81":[29,8,34,35,50,51,58,119,63],"94":[29,8,34,35,50,51,58,119,63],"102":[29,8,34,35,50,51,58,119,63],"106":[29,8,34,35,50,51,58,119,63],"116":[29,8,34,35,50,51,58,119,63],"120":[29,8,34,35,50,51,58,119,63],"132":[29,8,34,35,50,51,58,119,63],"133":[29,8,34,35,50,51,58,119,63],"141":[29,8,34,35,50,51,58,119,63],"147":[29,8,34,35,50,51,58,119,63],"148":[29,8,34,35,50,51,58,119,63],"167":[29,8,34,35,50,51,58,119,63],"170":[29,8,34,35,50,51,58,119,63],"201":[29,8,34,35,50,51,58,119,63],"208":[29,8,34,35,50,51,58,119,63],"210":[29,8,34,35,50,51,58,119,63]},"packages/frontend/editor-ui/src/features/credentials/components/CredentialCard.vue":{"68":[29,8,34,35,50,51,58,119,63],"69":[29,8,34,35,50,51,58,119,63],"72":[29,8,34,35,50,51,58,119,63],"74":[29,8,34,35,50,51,58,119,63],"82":[29,8,34,35,50,51,58,119,63],"118":[29,8,34,35,50,51,58,119,63],"127":[29,8,34,35,50,51,58,119,63],"129":[34],"156":[50],"220":[50],"229":[29,8,34,35,50,51,58,119,63],"234":[29,8,34,35,50,51,58,119,63],"237":[29,8,34,35,50,51,58,119,63],"241":[34],"250":[29,8,34,35,50,51,58,119,63],"270":[29,8,34,35,50,51,58,119,63]},"packages/frontend/editor-ui/src/features/credentials/composables/oauthCallback.ts":{"20":[35,147],"35":[35]},"localhost-41289/assets/_plugin-vue_export-helper-Da88TEg1.js":{"2":[75]},"localhost-41289/assets/chunk-CC9Q-vWm.js":{"8":[75],"9":[75],"10":[75],"19":[75],"23":[75],"29":[75],"33":[75],"34":[75]},"packages/frontend/editor-ui/src/features/ai/instanceAi/components/InstanceAiThreadList.vue":{"28":[75],"54":[75],"72":[75],"78":[75],"95":[75],"98":[75],"104":[75],"107":[75],"119":[75],"126":[75],"136":[75],"143":[75],"158":[75],"177":[75],"178":[75],"180":[75],"183":[75],"192":[75],"195":[75],"197":[75],"207":[75],"208":[75],"215":[75],"216":[75],"218":[75],"232":[75]},"packages/@n8n/api-types/src/schemas/folder.schema.ts":{"10":[48,104,49]},"packages/cli/src/controllers/folder.controller.ts":{"36":[48,104,49,50,51],"43":[48,104,49,50,51],"61":[48,104,49,50,51],"82":[48,104,49,50,51],"102":[48,104,49,50,51],"126":[48,104,49,50,51],"148":[48,104,49,50,51],"170":[48,104,49,50,51],"185":[48,104,49,50,51],"207":[48,104,49,50,51]},"packages/cli/src/services/folder.service.ts":{"32":[48,104,49,50,51,117],"40":[48,104,49,50,51,117],"58":[48,104,49,50,51,117],"66":[48,104,49,50,51,117],"105":[48,104,49,50,51,117],"113":[48,104,49,50,51,117],"142":[48,104,49,50,51,117],"161":[48,104,49,50,51,117],"174":[48,104,49,50,51,117],"197":[48,104,49,50,51,117],"205":[48,104,49,50,51,117],"213":[48,104,49,50,51,117],"221":[48,104,49,50,51,117],"232":[48,104,49,50,51,117],"246":[48,104,49,50,51,117],"247":[48,104,49,50,51,117],"255":[48,104,49,50,51,117],"280":[48,104,49,50,51,117],"293":[48,104,49,50,51,117],"319":[48,104,49,50,51,117],"329":[48,104,49,50,51,117],"332":[48,104,49,50,51,117],"337":[48,104,49,50,51,117]},"packages/frontend/editor-ui/src/features/core/folders/components/FolderCard.vue":{"68":[48,104,49],"72":[104],"74":[48,104,49],"98":[48,104,49],"106":[48,104,49],"119":[48,104,49],"130":[48,104,49],"134":[48,104,49],"145":[104],"165":[104],"169":[48,104,49],"180":[48,104,49],"213":[48,104,49],"220":[48,104,49],"221":[48,104,49],"230":[48,104,49],"233":[48,104,49],"240":[48,104,49],"248":[48,49],"259":[48,104,49],"271":[48,104,49],"280":[48,104,49],"286":[48,104,49],"287":[48,104,49],"299":[104],"329":[48,104,49],"339":[48,104,49],"360":[48,104,49]},"packages/frontend/editor-ui/src/features/core/folders/folders.api.ts":{"11":[104,49],"23":[48,104,49],"35":[49],"46":[49],"57":[49,50,51],"87":[49],"100":[48,49],"111":[49]},"packages/core/src/execution-engine/node-execution-context/utils/construct-execution-metadata.ts":{"8":[45,137,144],"13":[45,137,144]},"packages/frontend/editor-ui/src/app/utils/json-schema.ts":{"5":[137],"9":[137],"12":[137],"25":[137],"32":[137],"35":[137]},"packages/@n8n/permissions/src/schemas.ee.ts":{"23":[66],"50":[66],"73":[66]},"localhost-45029/assets/_plugin-vue_export-helper-Da88TEg1.js":{"2":[66]},"localhost-45029/assets/chunk-CC9Q-vWm.js":{"8":[66],"9":[66],"10":[66],"19":[66],"23":[66],"29":[66],"33":[66],"34":[66]},"packages/frontend/editor-ui/src/features/execution/executions/components/ExecutionsTime.vue":{"14":[158],"22":[158],"24":[158],"29":[158],"36":[158]},"packages/@n8n/api-types/src/dto/binary-data/binary-data-query.dto.ts":{"8":[21],"12":[21]},"packages/cli/src/controllers/binary-data.controller.ts":{"16":[21],"19":[21],"36":[21],"50":[21],"74":[21]},"packages/core/src/utils/serialized-buffer.ts":{"10":[0,21,168,46],"14":[0,21,168,46]},"packages/cli/src/metrics/prometheus/token-exchange-metrics.service.ts":{"22":[176],"27":[176],"31":[176],"72":[176],"76":[176],"81":[176],"85":[176],"90":[176],"94":[176]},"packages/cli/src/modules/token-exchange/controllers/embed-auth.controller.ts":{"22":[176],"37":[176],"55":[176],"66":[176]},"packages/cli/src/modules/token-exchange/controllers/token-exchange.controller.ts":{"22":[176],"43":[176]},"packages/cli/src/modules/token-exchange/database/repositories/token-exchange-jti.repository.ts":{"14":[176],"18":[176],"31":[176],"32":[176],"79":[176],"80":[176]},"packages/cli/src/modules/token-exchange/database/repositories/trusted-key.repository.ts":{"8":[176],"12":[176],"16":[176]},"packages/cli/src/modules/token-exchange/services/identity-resolution.service.ts":{"33":[176],"37":[176],"45":[176],"58":[176],"73":[176],"97":[176],"159":[176],"177":[176],"210":[176],"224":[176],"267":[176],"321":[176],"367":[176]},"packages/cli/src/modules/token-exchange/services/jti-store.service.ts":{"25":[176],"34":[176]},"packages/cli/src/modules/token-exchange/services/scoped-jwt.strategy.ts":{"17":[176],"22":[176],"67":[176],"74":[176],"97":[176],"104":[176]},"packages/cli/src/modules/token-exchange/services/token-exchange.service.ts":{"34":[176],"56":[176],"143":[176],"157":[176]},"packages/cli/src/modules/token-exchange/services/trusted-key.service.ts":{"79":[176],"104":[176],"117":[176],"125":[176],"129":[176],"137":[176],"143":[176],"157":[176],"208":[176],"216":[176],"220":[176],"226":[176],"252":[176],"256":[176],"281":[176],"290":[176],"291":[176],"293":[176],"294":[176],"339":[176],"354":[176],"371":[176],"397":[176],"399":[176],"418":[176],"470":[176],"489":[176],"508":[176],"524":[176],"539":[176],"548":[176],"588":[176],"634":[176]},"packages/cli/src/modules/token-exchange/token-exchange.errors.ts":{"11":[176],"24":[176]},"packages/cli/src/modules/token-exchange/token-exchange.schemas.ts":{"43":[176]},"packages/cli/src/public-api/v1/shared/services/pagination.service.ts":{"10":[176,90],"14":[176,90],"26":[176,90],"38":[176,90],"47":[176,90]},"packages/cli/src/utils/validate-redirect-url.ts":{"5":[176]},"packages/nodes-base/nodes/Jira/GenericFunctions.ts":{"20":[28],"33":[28],"45":[28],"114":[28],"165":[28],"187":[28],"197":[28],"206":[28],"211":[28],"225":[28],"296":[28],"299":[28],"304":[28],"315":[28],"342":[28],"343":[28],"347":[28],"352":[28],"360":[28],"367":[28],"406":[28]},"packages/nodes-base/nodes/Jira/JiraTrigger.node.ts":{"547":[28],"606":[28],"666":[28],"691":[28],"754":[28],"785":[28]},"packages/nodes-base/nodes/Linear/GenericFunctions.ts":{"16":[28],"66":[28],"70":[28],"100":[28],"126":[28]},"packages/nodes-base/nodes/Linear/Linear.node.ts":{"109":[28],"119":[28],"136":[28],"154":[28],"172":[28],"220":[28]},"packages/nodes-base/nodes/Linear/Queries.ts":{"2":[28],"15":[28],"28":[28],"41":[28],"95":[28],"102":[28],"138":[28],"147":[28],"189":[28],"245":[28],"255":[28]},"localhost-44339/assets/_plugin-vue_export-helper-Da88TEg1.js":{"2":[28,46,122,123,152,155]},"localhost-44339/assets/chunk-CC9Q-vWm.js":{"8":[28,46,122,123,152,155],"9":[28,46,122,123,152,155],"10":[28,46,122,123,152,155],"19":[28,46,122,123,152,155],"23":[28,46,122,123,152,155],"29":[28,46,122,123,152,155],"33":[28,46,122,123,152,155],"34":[28,46,122,123,152,155]},"localhost-44339/assets/verified-L2wNNiNu.js":{"8":[28,155]},"localhost-35417/assets/_plugin-vue_export-helper-Da88TEg1.js":{"2":[76]},"localhost-35417/assets/chunk-CC9Q-vWm.js":{"8":[76],"9":[76],"10":[76],"19":[76],"23":[76],"29":[76],"33":[76],"34":[76]},"packages/@n8n/backend-common/src/utils/path-util.ts":{"9":[46],"27":[46],"40":[46],"46":[46],"55":[46],"63":[46]},"packages/nodes-base/nodes/Files/ExtractFromFile/ExtractFromFile.node.ts":{"117":[46]},"packages/nodes-base/nodes/Files/ExtractFromFile/actions/moveTo.operation.ts":{"105":[46]},"packages/nodes-base/nodes/Files/ExtractFromFile/actions/pdf.operation.ts":{"83":[46]},"packages/nodes-base/nodes/Files/ExtractFromFile/actions/spreadsheet.operation.ts":{"8":[46],"9":[46],"18":[46],"58":[46]},"packages/nodes-base/nodes/Files/ReadWriteFile/ReadWriteFile.node.ts":{"60":[46]},"packages/nodes-base/nodes/Files/ReadWriteFile/actions/read.operation.ts":{"88":[46]},"packages/nodes-base/nodes/Files/ReadWriteFile/actions/write.operation.ts":{"72":[46]},"packages/nodes-base/nodes/Files/ReadWriteFile/helpers/utils.ts":{"5":[46],"37":[46],"44":[46]},"packages/nodes-base/nodes/SpreadsheetFile/v2/fromFile.operation.ts":{"85":[46],"126":[46],"129":[46],"136":[46],"144":[46],"154":[46]},"packages/@n8n/db/src/repositories/scheduled-task.repository.ts":{"118":[165,167,57],"136":[165,167,57],"148":[165,167,57],"191":[165,167,57],"214":[165,167,57],"219":[165,167,57],"242":[165,167,57],"247":[165,167,57],"264":[165,167,57],"271":[165,167,57],"273":[165,167,57],"298":[165,167,57],"310":[165,167,57],"311":[165,167,57],"325":[165,167,57],"327":[165,167,57],"332":[165,167,57],"335":[165,167,57],"343":[165,167,57],"346":[165,167,57],"347":[165,167,57],"358":[165,167,57],"365":[165,167,57],"366":[165,167,57],"384":[165,167,57],"400":[165,167,57],"420":[165,167,57],"457":[165,167,57],"474":[165,167,57],"479":[165,167,57],"480":[165,167,57],"481":[165,167,57],"503":[165,167,57],"508":[165,167,57],"509":[165,167,57],"524":[165,167,57],"529":[165,167,57],"541":[165,167,57],"574":[165,167,57],"610":[165,167,57],"611":[165,167,57],"632":[165,167,57],"647":[165,167,57]},"packages/@n8n/db/src/repositories/workflow-publication-outbox.repository.ts":{"16":[25,165,167,57],"27":[25,165,167,57],"32":[25,165,167,57],"48":[25,165,167,57],"61":[25,165,167,57],"79":[25,165,167,57],"107":[25,165,167,57],"116":[25,165,167,57],"140":[25,165,167,57],"173":[25,165,167,57],"184":[25,165,167,57],"210":[25,165,167,57],"213":[25,165,167,57],"243":[25,165,167,57],"266":[25,165,167,57],"278":[25,165,167,57],"286":[25,165,167,57],"328":[25,165,167,57],"331":[25,165,167,57],"349":[25,165,167,57],"356":[25,165,167,57],"387":[25,165,167,57],"400":[25,165,167,57],"411":[25,165,167,57],"427":[25,165,167,57],"443":[25,165,167,57],"463":[25,165,167,57],"488":[25,165,167,57],"497":[25,165,167,57],"519":[25,165,167,57],"528":[25,165,167,57],"540":[25,165,167,57],"550":[25,165,167,57],"562":[25,165,167,57]},"packages/@n8n/db/src/repositories/workflow-publication-trigger-status.repository.ts":{"26":[25,165,167,57],"31":[25,165,167,57],"36":[25,165,167,57],"41":[25,165,167,57],"47":[25,165,167,57],"59":[25,165,167,57],"64":[25,165,167,57]},"packages/@n8n/db/src/repositories/workflow-published-version.repository.ts":{"31":[25,165,167,57],"35":[25,165,167,57],"39":[25,165,167,57],"43":[25,165,167,57],"56":[25,165,167,57],"68":[25,165,167,57]},"packages/@n8n/scheduler/src/core/index.ts":{"18":[165,167,57],"61":[165,167,57],"66":[165,167,57],"68":[165,167,57]},"packages/@n8n/scheduler/src/core/materializer/materialize.ts":{"86":[165,167,178,57],"93":[165,167,178,57],"126":[165,167,178,57],"127":[165,167,178,57],"128":[165,167,178,57],"139":[165,167,178,57],"147":[165,167,178,57],"149":[165,167,178,57],"150":[165,167,178,57],"154":[165,167,178,57]},"packages/@n8n/scheduler/src/core/materializer/plan.ts":{"33":[165,167,57]},"packages/@n8n/scheduler/src/core/provisioning/index.ts":{"1":[165,167,57]},"packages/@n8n/scheduler/src/core/provisioning/schedule-identity.ts":{"14":[165,167,57],"49":[165,167,57],"68":[165,167,57]},"packages/@n8n/scheduler/src/core/recurrence/field.ts":{"14":[165,167,57]},"packages/@n8n/scheduler/src/core/recurrence/kinds/cron.ts":{"25":[165,167,57],"62":[165,167,57],"82":[165,167,57],"103":[165,167,57],"112":[165,167,57],"127":[165,167,57]},"packages/@n8n/scheduler/src/core/recurrence/next-run.ts":{"33":[165,167,57],"68":[165,167,57],"92":[165,167,57]},"packages/@n8n/scheduler/src/core/recurrence/resolve.ts":{"21":[165,167,57]},"packages/@n8n/scheduler/src/core/recurrence/validate.ts":{"14":[165,167,57]},"packages/cli/src/workflows/publication/publication-status-reporter.ts":{"30":[25,165,167,57],"42":[25,165,167,57],"82":[25,165,167,57],"110":[25,165,167,57],"115":[25,165,167,57],"122":[25,165,167,57],"125":[25,165,167,57],"140":[25,165,167,57],"150":[25,165,167,57],"154":[25,165,167,57],"164":[25,165,167,57],"166":[25,165,167,57],"173":[25,165,167,57],"187":[25,165,167,57],"191":[25,165,167,57],"196":[25,165,167,57],"204":[25,165,167,57],"208":[25,165,167,57],"221":[25,165,167,57]},"packages/cli/src/workflows/publication/trigger-diff.ts":{"21":[25,165,167,57]},"packages/cli/src/workflows/publication/workflow-publication-applier.ts":{"38":[25,165,167,57],"70":[25,165,167,57],"95":[25,165,167,57],"122":[25,165,167,57],"129":[25,165,167,57],"190":[25,165,167,57],"199":[25,165,167,57],"219":[25,165,167,57],"229":[25,165,167,57],"242":[25,165,167,57],"247":[25,165,167,57],"248":[25,165,167,57],"269":[25,165,167,57],"273":[25,165,167,57],"286":[25,165,167,57],"311":[25,165,167,57]},"packages/cli/src/workflows/publication/workflow-publication-lifecycle-lock.ts":{"27":[25,165,167,57],"31":[25,165,167,57],"36":[25,165,167,57],"41":[25,165,167,57],"55":[25,165,167,57],"76":[25,165,167,57],"85":[25,165,167,57],"92":[25,165,167,57],"96":[25,165,167,57],"108":[25,165,167,57],"115":[25,165,167,57],"122":[25,165,167,57],"129":[25,165,167,57]},"packages/cli/src/workflows/publication/workflow-publication-notifier.ts":{"13":[25,165,167,57],"26":[25,165,167,57],"33":[25,165,167,57],"46":[25,165,167,57]},"packages/cli/src/workflows/publication/workflow-publication-outbox-consumer.ts":{"39":[25,165,167,57],"54":[25,165,167,57],"62":[25,165,167,57],"71":[25,165,167,57],"82":[25,165,167,57],"96":[25,165,167,57],"106":[25,165,167,57],"110":[25,165,167,57],"121":[25,165,167,57],"139":[25,165,167,57],"151":[25,165,167,57],"159":[25,165,167,57],"164":[25,165,167,57],"174":[25,165,167,57],"175":[25,165,167,57],"183":[25,165,167,57],"193":[25,165,167,57],"210":[25,165,167,57],"221":[25,165,167,57],"222":[25,165,167,57],"287":[25,165,167,57]},"packages/cli/src/workflows/triggers/non-webhook-trigger-registrar.ts":{"50":[25,165,167,57],"64":[25,165,167,57],"65":[25,165,167,57],"72":[25,165,167,57],"79":[25,165,167,57],"122":[25,165,167,57],"145":[25,165,167,57],"170":[25,165,167,57],"180":[25,165,167,57]},"packages/cli/src/workflows/triggers/trigger-activation-retry.ts":{"9":[25,165,167,57],"19":[25,165,167,57]},"packages/cli/src/workflows/triggers/trigger-count.service.ts":{"13":[25,165,167,57],"14":[25,165,167,57],"17":[25,165,167,57],"27":[25,165,167,57]},"packages/cli/src/workflows/triggers/webhook-trigger-registrar.ts":{"20":[25,165,167,57],"26":[25,165,167,57],"47":[25,165,167,57],"67":[25,165,167,57],"74":[25,165,167,57],"86":[25,165,167,57],"133":[25,165,167,57],"143":[25,165,167,57],"160":[25,165,167,57],"174":[25,165,167,57],"187":[25,165,167,57],"194":[25,165,167,57],"223":[25,165,167,57],"239":[25,165,167,57],"243":[25,165,167,57],"258":[25,165,167,57]},"packages/cli/src/workflows/triggers/workflow-trigger-activator.ts":{"67":[25,165,167,57],"95":[25,165,167,57],"108":[25,165,167,57],"119":[25,165,167,57],"130":[25,165,167,57],"152":[25,165,167,57],"173":[25,165,167,57],"177":[25,165,167,57],"200":[25,165,167,57],"210":[25,165,167,57],"224":[25,165,167,57],"245":[25,165,167,57],"259":[25,165,167,57],"317":[25,165,167,57],"336":[25,165,167,57],"350":[25,165,167,57],"363":[25,165,167,57],"382":[25,165,167,57],"389":[25,165,167,57],"414":[25,165,167,57],"426":[25,165,167,57],"447":[25,165,167,57],"453":[25,165,167,57],"474":[25,165,167,57],"483":[25,165,167,57],"498":[25,165,167,57],"507":[25,165,167,57],"528":[25,165,167,57],"551":[25,165,167,57],"564":[25,165,167,57],"582":[25,165,167,57],"589":[25,165,167,57],"599":[25,165,167,57],"615":[25,165,167,57],"629":[25,165,167,57],"632":[25,165,167,57],"658":[25,165,167,57],"665":[25,165,167,57],"674":[25,165,167,57],"679":[25,165,167,57],"682":[25,165,167,57],"685":[25,165,167,57],"686":[25,165,167,57],"690":[25,165,167,57],"691":[25,165,167,57],"702":[25,165,167,57],"738":[25,165,167,57]},"packages/cli/src/workflows/workflow-published-data.service.ts":{"22":[25,165,167,57],"26":[25,165,167,57],"39":[25,165,167,57],"50":[25,165,167,57],"64":[25,165,167,57],"84":[25,165,167,57],"93":[25,165,167,57]},"packages/core/src/execution-engine/node-execution-context/poll-context.ts":{"19":[165,166],"23":[165,166],"30":[165,166],"50":[165,166],"54":[165,166]},"packages/core/src/execution-engine/poll-trigger-executor.ts":{"19":[165,166],"36":[165,166],"42":[165,166],"59":[165,166]},"packages/nodes-base/nodes/E2eTest/E2eTestPollingTrigger.node.ts":{"42":[165,166]},"packages/workflow/src/cron.ts":{"52":[165,166],"80":[165,166]},"packages/workflow/src/workflow-diff.ts":{"28":[25,165,167,57],"42":[25,165,167,57],"54":[25,165,167,57],"59":[25,165,167,57],"64":[25,165,167,57],"94":[25,165,167,57],"98":[25,165,167,57],"116":[25,165,167,57],"128":[25,165,167,57],"141":[25,165,167,57],"148":[25,165,167,57],"150":[25,165,167,57],"166":[25,165,167,57],"180":[25,165,167,57],"184":[25,165,167,57],"197":[25,165,167,57],"205":[25,165,167,57],"213":[25,165,167,57],"227":[25,165,167,57],"228":[25,165,167,57],"238":[25,165,167,57],"239":[25,165,167,57],"251":[25,165,167,57],"253":[25,165,167,57],"254":[25,165,167,57],"256":[25,165,167,57],"273":[25,165,167,57],"302":[25,165,167,57],"310":[25,165,167,57],"314":[25,165,167,57],"320":[25,165,167,57],"321":[25,165,167,57],"324":[25,165,167,57],"378":[25,165,167,57],"407":[25,165,167,57],"408":[25,165,167,57]},"localhost-34935/assets/_plugin-vue_export-helper-Da88TEg1.js":{"2":[59]},"localhost-34935/assets/chunk-CC9Q-vWm.js":{"8":[59],"9":[59],"10":[59],"19":[59],"23":[59],"29":[59],"33":[59],"34":[59]},"packages/frontend/editor-ui/src/app/views/DemoDiffView.vue":{"20":[122],"27":[122],"36":[122],"64":[122]},"packages/frontend/editor-ui/src/features/workflows/workflowDiff/HighlightedEdge.vue":{"6":[122]},"packages/frontend/editor-ui/src/features/workflows/workflowDiff/SyncedWorkflowCanvas.vue":{"16":[122],"39":[122],"49":[122],"68":[122],"71":[122],"76":[122],"90":[122],"98":[122],"102":[122],"131":[122],"146":[122],"149":[122],"152":[122]},"packages/frontend/editor-ui/src/features/workflows/workflowDiff/WorkflowDiffContent.vue":{"15":[122],"41":[122],"46":[122],"48":[122],"59":[122],"70":[122],"75":[122],"87":[122],"102":[122],"113":[122],"118":[122],"130":[122]},"packages/frontend/editor-ui/src/features/workflows/workflowDiff/WorkflowDiffView.vue":{"64":[122],"65":[122],"84":[122],"85":[122],"102":[122],"148":[122],"169":[122],"174":[122],"176":[122],"183":[122]},"packages/frontend/editor-ui/src/features/workflows/workflowDiff/useViewportSync.ts":{"30":[122],"47":[122],"53":[122],"72":[122]},"packages/frontend/editor-ui/src/features/workflows/workflowDiff/useWorkflowDiff.ts":{"37":[122],"38":[122],"48":[122],"49":[122],"53":[122],"58":[122],"77":[122],"89":[122],"100":[122],"106":[122],"123":[122],"136":[122],"152":[122],"168":[122],"173":[122],"183":[122],"226":[122],"248":[122],"276":[122]},"packages/frontend/editor-ui/src/features/workflows/workflowDiff/useWorkflowDiffUI.ts":{"25":[122],"40":[122],"94":[122],"97":[122],"98":[122],"130":[122],"167":[122],"182":[122],"221":[122],"225":[122]},"packages/cli/src/task-runners/errors/task-cancelled.error.ts":{"4":[181]},"packages/cli/src/task-runners/task-runner-module.ts":{"44":[6,181],"53":[6,181],"61":[6,181],"69":[6,181],"70":[6,181],"77":[6,181],"84":[6,181],"94":[6,181],"103":[6,181],"113":[6,181],"159":[6,181]},"packages/cli/src/utils/object-to-error.ts":{"10":[181]},"packages/workflow/src/errors/execution-cancelled.error.ts":{"9":[6,181],"19":[6,181],"26":[6,181],"33":[6,181]},"packages/frontend/editor-ui/src/app/components/WorkflowExtractionNameModal.vue":{"21":[152],"31":[152],"37":[152],"57":[152],"59":[152],"64":[152],"79":[152],"90":[152]},"packages/cli/src/errors/response-errors/conflict.error.ts":{"4":[155]},"localhost-42035/assets/_plugin-vue_export-helper-Da88TEg1.js":{"2":[69,8,97,105,117,142,13]},"localhost-42035/assets/chunk-CC9Q-vWm.js":{"8":[69,8,97,105,117,142,13],"9":[69,8,97,105,117,142,13],"10":[69,8,97,105,117,142,13],"19":[69,8,97,105,117,142,13],"23":[69,8,97,105,117,142,13],"29":[69,8,97,105,117,142,13],"33":[69,8,97,105,117,142,13],"34":[69,8,97,105,117,142,13]},"packages/frontend/@n8n/design-system/src/components/N8nIcon/custom/status-warning.svg":{"8":[69]},"packages/frontend/editor-ui/src/app/components/VersionUpdateCTA.vue":{"13":[69],"30":[69]},"localhost-39865/assets/_plugin-vue_export-helper-Da88TEg1.js":{"2":[18,83,85,99,49,107,149]},"localhost-39865/assets/chunk-CC9Q-vWm.js":{"8":[18,83,85,99,49,107,149],"9":[18,83,85,99,49,107,149],"10":[18,83,85,99,49,107,149],"19":[18,83,85,99,49,107,149],"23":[18,83,85,99,49,107,149],"29":[18,83,85,99,49,107,149],"33":[18,83,85,99,49,107,149],"34":[18,83,85,99,49,107,149]},"localhost-44967/assets/_plugin-vue_export-helper-Da88TEg1.js":{"2":[18]},"localhost-44967/assets/chunk-CC9Q-vWm.js":{"8":[18],"9":[18],"10":[18],"19":[18],"23":[18],"29":[18],"33":[18],"34":[18]},"packages/@n8n/db/src/repositories/evaluation-config.repository.ts":{"9":[83],"13":[83],"17":[83],"21":[83],"36":[83],"47":[83],"52":[83],"56":[83]},"packages/cli/src/evaluation.ee/evaluation-config.controller.ts":{"17":[83],"23":[83],"36":[83],"42":[83],"50":[83],"61":[83],"78":[83],"85":[83],"100":[83]},"packages/cli/src/evaluation.ee/evaluation-config.service.ts":{"13":[83],"19":[83],"23":[83],"27":[83],"55":[83],"71":[83],"75":[83],"79":[83]},"packages/@n8n/api-types/src/schemas/eval-collections.schema.ts":{"74":[83],"107":[83],"123":[83],"129":[83]},"packages/frontend/editor-ui/src/features/ai/evaluation.ee/components/Compare/AiInsightsCard.vue":{"9":[83],"24":[83],"25":[83],"30":[83],"36":[83],"40":[83],"48":[83],"50":[83],"64":[83],"71":[83],"77":[83],"81":[83],"92":[83],"102":[83],"124":[83],"137":[83],"144":[83],"157":[83]},"packages/frontend/editor-ui/src/features/ai/evaluation.ee/components/Compare/CasesTable.vue":{"27":[83],"44":[83],"49":[83],"56":[83],"57":[83],"62":[83],"65":[83],"70":[83],"73":[83],"81":[83],"87":[83],"91":[83],"92":[83],"97":[83],"116":[83],"144":[83],"149":[83],"162":[83],"166":[83],"173":[83],"184":[83],"188":[83]},"packages/frontend/editor-ui/src/features/ai/evaluation.ee/components/Compare/CompareHeader.vue":{"27":[83],"28":[83],"34":[83],"51":[83],"63":[83],"64":[83],"72":[83],"95":[83],"104":[83],"124":[83],"127":[83]},"packages/frontend/editor-ui/src/features/ai/evaluation.ee/components/Compare/CompareTabs.vue":{"16":[83],"36":[83],"43":[83],"46":[83],"49":[83],"52":[83],"68":[83]},"packages/frontend/editor-ui/src/features/ai/evaluation.ee/components/Compare/DatasetMismatchBanner.vue":{"8":[83],"14":[83],"15":[83],"19":[83]},"packages/frontend/editor-ui/src/features/ai/evaluation.ee/components/Compare/MetricCriteria.vue":{"19":[83],"21":[83],"44":[83],"49":[83]},"packages/frontend/editor-ui/src/features/ai/evaluation.ee/components/Compare/OutputsTab.vue":{"20":[83],"47":[83],"48":[83],"51":[83],"56":[83],"65":[83],"75":[83],"82":[83],"84":[83],"92":[83],"99":[83],"107":[83],"125":[83],"134":[83]},"packages/frontend/editor-ui/src/features/ai/evaluation.ee/components/Compare/ScoreChart.vue":{"21":[83],"25":[83],"36":[83],"37":[83],"43":[83],"50":[83],"57":[83],"63":[83]},"packages/frontend/editor-ui/src/features/ai/evaluation.ee/components/shared/GroupedMetricChart.vue":{"48":[83],"54":[83],"59":[83],"81":[83],"91":[83],"125":[83],"126":[83]},"packages/frontend/editor-ui/src/features/ai/evaluation.ee/components/shared/VersionAvatar.vue":{"10":[83],"22":[83],"24":[83],"25":[83]},"packages/frontend/editor-ui/src/features/ai/evaluation.ee/components/shared/versionPalette.ts":{"15":[83],"21":[83]},"packages/frontend/editor-ui/src/features/ai/evaluation.ee/composables/useCompareCases.ts":{"42":[83],"45":[83],"46":[83],"56":[83],"79":[83],"103":[83],"114":[83],"124":[83],"127":[83],"133":[83],"136":[83],"142":[83],"148":[83],"149":[83],"154":[83],"158":[83],"162":[83],"164":[83],"168":[83],"172":[83],"173":[83],"191":[83],"197":[83],"202":[83],"213":[83],"214":[83],"215":[83],"227":[83],"239":[83],"241":[83]},"packages/frontend/editor-ui/src/features/ai/evaluation.ee/composables/useCompareData.ts":{"47":[83],"50":[83],"52":[83],"67":[83],"68":[83],"78":[83],"79":[83],"82":[83]},"packages/frontend/editor-ui/src/features/ai/evaluation.ee/composables/useEvalCollectionsFlag.ts":{"24":[83]},"packages/frontend/editor-ui/src/features/ai/evaluation.ee/evalCollections.api.ts":{"16":[83],"34":[83],"141":[83]},"packages/frontend/editor-ui/src/features/ai/evaluation.ee/evalCollections.store.ts":{"24":[83],"27":[83],"28":[83],"30":[83],"49":[83],"64":[83],"66":[83],"85":[83],"261":[83],"339":[83]},"packages/frontend/editor-ui/src/features/ai/evaluation.ee/evaluation.utils.ts":{"21":[83],"90":[83],"94":[83],"108":[83],"115":[83],"118":[83],"130":[83],"133":[83],"136":[83],"155":[83],"165":[83],"178":[83],"180":[83],"181":[83],"187":[83],"189":[83],"203":[83],"230":[83],"244":[83],"251":[83],"282":[83],"304":[83],"336":[83],"388":[83]},"packages/frontend/editor-ui/src/features/ai/evaluation.ee/views/CompareCollectionView.vue":{"38":[83],"42":[83],"58":[83],"69":[83],"70":[83],"86":[83],"89":[83],"92":[83],"98":[83],"112":[83],"137":[83],"151":[83],"187":[83]},"packages/cli/src/modules/provisioning.ee/claims-context.builder.ts":{"3":[27],"17":[27],"29":[27]},"packages/cli/src/modules/provisioning.ee/provisioning.controller.ee.ts":{"13":[27,117],"21":[27,117],"31":[27,117]},"packages/cli/src/modules/provisioning.ee/role-mapping-rule.service.ee.ts":{"46":[27],"52":[27],"79":[27],"83":[27],"112":[27],"150":[27],"168":[27],"192":[27],"242":[27],"259":[27],"265":[27],"288":[27],"294":[27],"304":[27],"307":[27],"324":[27],"334":[27],"336":[27],"339":[27],"355":[27],"362":[27],"369":[27]},"packages/cli/src/modules/sso-oidc/oidc.controller.ee.ts":{"23":[27],"36":[27],"47":[27],"65":[27],"87":[27],"108":[27]},"packages/cli/src/modules/sso-oidc/oidc.service.ee.ts":{"72":[27],"91":[27],"105":[27],"114":[27],"120":[27],"124":[27],"132":[27],"144":[27],"179":[27],"187":[27],"220":[27],"258":[27],"358":[27],"391":[27],"431":[27],"497":[27],"498":[27],"510":[27],"535":[27],"546":[27],"556":[27],"587":[27],"620":[27],"630":[27],"692":[27],"720":[27],"754":[27]},"localhost-37107/assets/_plugin-vue_export-helper-Da88TEg1.js":{"2":[27]},"localhost-37107/assets/chunk-CC9Q-vWm.js":{"8":[27],"9":[27],"10":[27],"19":[27],"23":[27],"29":[27],"33":[27],"34":[27]},"packages/@n8n/db/src/entities/credentials-entity.ts":{"69":[180,8]},"packages/nodes-base/nodes/Pipedrive/PipedriveTrigger.node.ts":{"16":[8],"280":[8],"314":[8],"362":[8],"386":[8]},"packages/nodes-base/nodes/Pipedrive/v1/GenericFunctions.ts":{"31":[8,146],"103":[8,146],"142":[8,146],"187":[8,146],"195":[8,146],"210":[8,146],"230":[8,146],"278":[8,146],"292":[8,146],"293":[8,146],"302":[8,146],"305":[8,146]},"packages/nodes-base/nodes/Trello/GenericFunctions.ts":{"14":[8],"46":[8]},"packages/nodes-base/nodes/Trello/Trello.node.ts":{"138":[8],"161":[8],"169":[8],"192":[8],"203":[8]},"localhost-44313/assets/_plugin-vue_export-helper-Da88TEg1.js":{"2":[5,91,92]},"localhost-44313/assets/chunk-CC9Q-vWm.js":{"8":[5,91,92],"9":[5,91,92],"10":[5,91,92],"19":[5,91,92],"23":[5,91,92],"29":[5,91,92],"33":[5,91,92],"34":[5,91,92]},"packages/frontend/@n8n/design-system/src/components/N8nDropdownMenu/DropdownMenu.utils.ts":{"28":[4,5,91]},"packages/frontend/@n8n/design-system/src/components/N8nDropdownMenu/DropdownMenuSearch.vue":{"18":[4,5,91],"43":[4,5,91],"48":[4,5,91]},"packages/frontend/@n8n/design-system/src/components/N8nDropdownMenu/DropdownMenuSearchableContent.vue":{"18":[4,5,91],"60":[4,5,91],"96":[4,5,91],"123":[4,5,91],"132":[4,5,91],"137":[4,5,91],"175":[4,5,91],"181":[4,5,91],"186":[4,5,91],"188":[4,5,91],"236":[4,5,91],"237":[4,5,91],"240":[4,5,91],"251":[4,5,91],"259":[4,5,91]},"packages/frontend/editor-ui/src/features/ai/chatHub/components/ChatProviderAvatar.vue":{"13":[4,5,91],"23":[4,5,91]},"packages/frontend/editor-ui/src/features/ai/components/CredentialSelectorModal.vue":{"36":[5],"37":[5],"46":[5],"108":[5],"116":[5],"120":[5],"123":[5],"147":[5],"150":[5]},"packages/frontend/editor-ui/src/features/credentials/components/CredentialPicker/CredentialsDropdown.vue":{"48":[5,92,68],"57":[5,92],"61":[92],"66":[92],"103":[5,92,68],"108":[5,92,68],"109":[5,92,68],"114":[5,92,68]},"packages/frontend/editor-ui/src/features/ai/chatHub/ChatPersonalAgentsView.vue":{"38":[91],"39":[91],"40":[91],"42":[91],"51":[91],"66":[91],"78":[91],"79":[91],"87":[91],"110":[91],"129":[91],"133":[91],"140":[91],"145":[91],"158":[91],"159":[91]},"packages/frontend/editor-ui/src/features/ai/chatHub/components/AgentEditorModal.vue":{"116":[91],"119":[91],"125":[91],"126":[91],"127":[91],"132":[91],"138":[91],"145":[91],"152":[91],"156":[91],"162":[91],"172":[91],"195":[91],"197":[91],"210":[91],"238":[91],"243":[91],"426":[91],"435":[91],"449":[91],"471":[91],"485":[91],"520":[91],"540":[91],"555":[91],"571":[91],"581":[91],"600":[91],"687":[91],"690":[91]},"packages/frontend/editor-ui/src/features/ai/chatHub/components/SuggestedPromptsEditor.vue":{"9":[91],"51":[91],"92":[91]},"packages/cli/src/errors/credential-not-found.error.ts":{"4":[4,92]},"packages/frontend/@n8n/design-system/src/components/N8nPagination/Pagination.vue":{"10":[92,117]},"packages/frontend/@n8n/design-system/src/components/N8nSwitch/Switch.vue":{"10":[92],"14":[92],"21":[92],"25":[92],"33":[92],"43":[92]},"packages/frontend/editor-ui/src/features/ai/chatHub/SettingsChatHubView.vue":{"26":[92],"41":[92],"43":[92],"60":[92],"68":[92],"76":[92],"94":[92],"115":[92],"126":[92],"133":[92],"141":[92]},"packages/frontend/editor-ui/src/features/ai/chatHub/components/ChatProvidersTable.vue":{"25":[92],"47":[92],"56":[92],"65":[92],"75":[92],"81":[92],"89":[92],"93":[92],"116":[92],"123":[92],"136":[92],"140":[92],"163":[92],"172":[92],"176":[92],"190":[92],"193":[92],"199":[92],"204":[92]},"packages/frontend/editor-ui/src/features/ai/chatHub/components/ProviderSettingsModal.vue":{"123":[92],"127":[92],"141":[92],"156":[92],"164":[92],"184":[92],"217":[92],"227":[92],"229":[92],"240":[92],"243":[92],"250":[92],"254":[92],"265":[92],"279":[92],"308":[92],"319":[92],"372":[92],"375":[92],"398":[92],"401":[92]},"packages/frontend/editor-ui/src/features/credentials/components/CredentialEdit/CredentialSharing.ee.vue":{"37":[92,34,119,63],"59":[92,34,119,63],"67":[92,34,119,63],"68":[92,34,119,63],"80":[92,34,119,63],"84":[92,34,119,63],"88":[92,34,119,63],"94":[92,34,119,63],"100":[92,34,119,63],"101":[92,34,119,63],"114":[92,34,119,63],"120":[92,34,119,63],"126":[119,63],"137":[92,34,119,63],"163":[92,34,119,63],"192":[92,34]},"packages/@n8n/instance-ai/src/parsers/docx-parser.ts":{"17":[72]},"packages/@n8n/instance-ai/src/parsers/html-parser.ts":{"35":[72]},"packages/@n8n/instance-ai/src/parsers/ooxml-guard.ts":{"24":[72],"35":[72],"76":[72]},"packages/@n8n/instance-ai/src/parsers/pdf-parser.ts":{"19":[72]},"packages/@n8n/instance-ai/src/parsers/structured-file-parser.ts":{"18":[72],"31":[72],"35":[72],"66":[72],"150":[72],"155":[72],"177":[72],"179":[72],"216":[72],"217":[72],"220":[72],"221":[72],"225":[72],"229":[72],"232":[72],"240":[72],"261":[72],"268":[72],"299":[72],"313":[72],"366":[72],"412":[72],"416":[72],"425":[72],"452":[72],"455":[72],"460":[72],"527":[72],"528":[72],"553":[72],"583":[72],"593":[72]},"packages/@n8n/instance-ai/src/parsers/validate-attachments.ts":{"31":[72],"39":[72],"43":[72],"68":[72],"69":[72],"81":[72],"83":[72],"84":[72]},"packages/@n8n/instance-ai/src/parsers/xlsx-parser.ts":{"18":[72]},"packages/@n8n/instance-ai/src/tools/attachments/parse-file.tool.ts":{"62":[72],"111":[72],"112":[72],"115":[72],"119":[72],"129":[72],"138":[72],"156":[72],"171":[72]},"localhost-35391/assets/_plugin-vue_export-helper-Da88TEg1.js":{"2":[72,41]},"localhost-35391/assets/chunk-CC9Q-vWm.js":{"8":[72,41],"9":[72,41],"10":[72,41],"19":[72,41],"23":[72,41],"29":[72,41],"33":[72,41],"34":[72,41]},"packages/frontend/editor-ui/src/features/ai/instanceAi/components/AttachmentPreview.vue":{"7":[72],"21":[72],"24":[72],"27":[72],"31":[72],"33":[72],"35":[72],"50":[72],"68":[72],"73":[72]},"localhost-34363/assets/_plugin-vue_export-helper-Da88TEg1.js":{"2":[39]},"localhost-34363/assets/chunk-CC9Q-vWm.js":{"8":[39],"9":[39],"10":[39],"19":[39],"23":[39],"29":[39],"33":[39],"34":[39]},"packages/frontend/editor-ui/src/features/ai/instanceAi/workflowSetup/InstanceAiWorkflowSetup.vue":{"9":[39]},"packages/frontend/editor-ui/src/features/ai/instanceAi/workflowSetup/components/WorkflowSetupCard.vue":{"14":[39],"23":[39],"25":[39],"26":[39],"28":[39],"32":[39],"36":[39],"48":[39],"60":[39]},"packages/frontend/editor-ui/src/features/ai/instanceAi/workflowSetup/components/WorkflowSetupSectionBody.vue":{"26":[39],"35":[39],"37":[39],"43":[39],"69":[39],"73":[39],"76":[39],"79":[39],"103":[39],"110":[39],"112":[39],"124":[39],"133":[39],"137":[39],"140":[39],"165":[39],"188":[39]},"packages/frontend/editor-ui/src/features/ai/instanceAi/workflowSetup/components/WorkflowSetupWizard.vue":{"8":[39],"10":[39],"14":[39],"22":[39],"23":[39],"32":[39]},"packages/frontend/editor-ui/src/features/ai/instanceAi/workflowSetup/components/WorkflowSetupWizardFooter.vue":{"9":[39],"12":[39],"13":[39],"17":[39],"21":[39],"25":[39],"30":[39],"34":[39],"44":[39],"46":[39],"49":[39],"51":[39],"57":[39],"67":[39],"80":[39],"97":[39],"126":[39]},"packages/frontend/editor-ui/src/features/ai/instanceAi/workflowSetup/composables/useWorkflowSetupActions.ts":{"36":[39],"63":[39],"66":[39],"75":[39],"87":[39]},"packages/frontend/editor-ui/src/features/ai/instanceAi/workflowSetup/composables/useWorkflowSetupApply.ts":{"12":[39]},"packages/frontend/editor-ui/src/features/ai/instanceAi/workflowSetup/composables/useWorkflowSetupBootstrap.ts":{"5":[39],"14":[39]},"packages/frontend/editor-ui/src/features/ai/instanceAi/workflowSetup/composables/useWorkflowSetupContext.ts":{"70":[39],"75":[39],"92":[39],"96":[39],"97":[39],"98":[39],"153":[39],"162":[39],"202":[39]},"packages/frontend/editor-ui/src/features/ai/instanceAi/workflowSetup/composables/useWorkflowSetupInputs.ts":{"15":[39],"75":[39],"87":[39],"94":[39],"100":[39],"126":[39],"139":[39],"143":[39],"198":[39],"223":[39],"232":[39],"235":[39],"246":[39],"249":[39]},"packages/frontend/editor-ui/src/features/ai/instanceAi/workflowSetup/composables/useWorkflowSetupSections.ts":{"11":[39],"16":[39],"21":[39],"70":[39],"93":[39],"123":[39]},"packages/frontend/editor-ui/src/features/ai/instanceAi/workflowSetup/composables/useWorkflowSetupSteps.ts":{"15":[39],"19":[39]},"packages/frontend/editor-ui/src/features/ai/instanceAi/workflowSetup/composables/useWorkflowSetupTelemetry.ts":{"42":[39],"58":[39],"68":[39],"69":[39],"76":[39],"80":[39],"100":[39],"122":[39],"137":[39],"144":[39],"158":[39],"168":[39],"173":[39]},"packages/frontend/editor-ui/src/features/ai/instanceAi/workflowSetup/workflowSetup.helpers.ts":{"8":[39],"21":[39]},"packages/frontend/editor-ui/src/features/credentials/composables/useCredentialTestInBackground.ts":{"12":[19],"28":[19],"92":[39]},"packages/frontend/editor-ui/src/features/setupPanel/setupPanel.utils.ts":{"7":[19,39],"25":[19],"50":[19],"103":[19],"136":[19],"227":[19],"267":[19],"310":[19],"318":[19]},"packages/@n8n/workflow-sdk/src/mock-data/ai-root-shapes.ts":{"38":[41],"51":[41],"70":[41],"76":[41]},"localhost-42035/assets/verified-L2wNNiNu.js":{"8":[8,13]},"packages/frontend/editor-ui/src/features/credentials/components/CredentialsSelect.vue":{"21":[70,8,34],"36":[70,8,34],"37":[70,8,34],"43":[70,8,34],"44":[70,8,34],"47":[8,34],"57":[70,8,34],"84":[70,8,34],"92":[70,8],"101":[70,8,34],"102":[70,8,34],"125":[70,8,34],"139":[8,34],"143":[70,8,34],"151":[70,8,34],"173":[70,8,34]},"packages/cli/src/modules/mcp-registry/mcp-registry-node-loader.ts":{"52":[10],"57":[10],"61":[10],"71":[10],"109":[10],"115":[10],"121":[10],"129":[10],"134":[10],"138":[10],"142":[10]},"packages/cli/src/modules/mcp-registry/mcp-registry-test.controller.ts":{"17":[10],"23":[10],"29":[10],"38":[10]},"packages/cli/src/modules/mcp-registry/node-description-transform.ts":{"30":[10],"37":[10],"45":[10],"58":[10],"73":[10],"93":[10],"127":[10],"140":[10],"152":[10],"163":[10],"181":[10],"202":[10],"205":[10],"208":[10],"226":[10],"228":[10],"238":[10],"239":[10],"240":[10],"250":[10],"255":[10],"265":[10],"282":[10]},"packages/cli/src/modules/mcp-registry/registry/mcp-registry.service.ts":{"35":[10],"47":[10],"60":[10],"67":[10],"72":[10],"78":[10],"85":[10],"94":[10],"99":[10],"104":[10],"113":[10],"117":[10],"122":[10],"131":[10],"136":[10],"150":[10],"180":[10],"185":[10],"186":[10],"188":[10],"189":[10],"191":[10],"192":[10],"206":[10],"217":[10],"229":[10],"253":[10],"257":[10],"261":[10]},"packages/cli/src/modules/mcp-registry/registry/mcp-registry.types.ts":{"80":[10],"97":[10]},"localhost-42397/assets/_plugin-vue_export-helper-Da88TEg1.js":{"2":[10]},"localhost-42397/assets/chunk-CC9Q-vWm.js":{"8":[10],"9":[10],"10":[10],"19":[10],"23":[10],"29":[10],"33":[10],"34":[10]},"localhost-39865/assets/verified-L2wNNiNu.js":{"8":[99,149]},"packages/frontend/@n8n/design-system/src/components/N8nIcon/nodes/n8n-trigger.svg":{"11":[99]},"packages/core/src/utils/hitl-callback-reference.ts":{"31":[47],"40":[47],"52":[47],"56":[47],"70":[47],"89":[47],"103":[47]},"packages/core/src/utils/signature-helpers.ts":{"9":[47],"16":[47],"31":[47]},"localhost-45601/assets/_plugin-vue_export-helper-Da88TEg1.js":{"2":[47]},"localhost-45601/assets/chunk-CC9Q-vWm.js":{"8":[47],"9":[47],"10":[47],"19":[47],"23":[47],"29":[47],"33":[47],"34":[47]},"packages/@n8n/api-types/src/dto/folders/list-folder-query.dto.ts":{"46":[49,50,51],"74":[49,50,51],"75":[49,50,51],"83":[49,50,51],"84":[49,50,51],"93":[49,50,51],"102":[49,50,51]},"packages/frontend/editor-ui/src/features/core/folders/components/DeleteFolderModal.vue":{"16":[49],"41":[49],"46":[49],"50":[49],"55":[49],"74":[49],"82":[49],"105":[49],"141":[49],"143":[49],"155":[49],"162":[49],"165":[49],"168":[49],"181":[49],"197":[49],"199":[49],"227":[49]},"packages/frontend/editor-ui/src/features/core/folders/components/MoveToFolderDropdown.vue":{"40":[49,50,51],"41":[49,50,51],"42":[49,50,51],"54":[49,50,51],"64":[49],"94":[49,50,51],"95":[49,50,51],"105":[49,50,51],"107":[49],"109":[49,50,51],"128":[49,50,51],"130":[49,50,51],"132":[49,50,51],"137":[49,50,51],"141":[49,50,51],"148":[49],"151":[49]},"packages/frontend/editor-ui/src/features/core/folders/components/MoveToFolderModal.vue":{"65":[49,50,51],"81":[49,50,51],"84":[49,50,51],"90":[49,50,51],"100":[49,50,51],"101":[50,51],"105":[51],"108":[49,50,51],"109":[51],"127":[49,50,51],"130":[49,50,51],"134":[49,50,51],"143":[49,50,51],"153":[49,50,51],"167":[49,50,51],"168":[50,51],"174":[49,50,51],"175":[49,50,51],"182":[49,50,51],"186":[50],"192":[50,51],"200":[49,50,51],"354":[49],"379":[49,50,51],"381":[49,50,51],"386":[49,50,51],"390":[49,50,51],"417":[49,50,51],"427":[49,50,51],"436":[49,50,51],"438":[49,50,51],"452":[50,51],"457":[50,51],"478":[49,50,51],"497":[51],"506":[51],"532":[51],"556":[49,50,51]},"packages/nodes-base/nodes/Google/Gmail/GmailTrigger.node.ts":{"253":[149],"270":[149],"283":[149],"319":[149],"337":[149],"355":[149],"416":[149],"427":[149],"474":[149],"486":[149],"503":[149],"527":[149],"531":[149],"538":[149]},"localhost-44415/assets/_plugin-vue_export-helper-Da88TEg1.js":{"2":[70,36,43,119,135,145]},"localhost-44415/assets/chunk-CC9Q-vWm.js":{"8":[70,36,43,119,135,145],"9":[70,36,43,119,135,145],"10":[70,36,43,119,135,145],"19":[70,36,43,119,135,145],"23":[70,36,43,119,135,145],"29":[70,36,43,119,135,145],"33":[70,36,43,119,135,145],"34":[70,36,43,119,135,145]},"packages/@n8n/api-types/src/dto/data-table/delete-data-table-rows.dto.ts":{"7":[36],"52":[36]},"packages/@n8n/api-types/src/dto/data-table/update-data-table-row.dto.ts":{"10":[36],"18":[36]},"packages/frontend/editor-ui/src/app/components/DependencyPill.vue":{"37":[34,36,105,51,119,63,161],"39":[34,36,105,51,119,63,161],"45":[34,36,105,51,119,63,161],"47":[34,36,105,51,119,63,161],"51":[34,36,105,51,119,63,161],"93":[34,36,105,51,119,63,161],"219":[34,36,105,51,119,63,161],"222":[34,36,105,51,119,63,161]},"packages/frontend/editor-ui/src/app/components/common/SelectedItemsInfo.vue":{"17":[36],"24":[36],"28":[36],"32":[36]},"packages/frontend/editor-ui/src/features/core/dataTable/components/dataGrid/ElDatePickerCellEditor.vue":{"7":[36],"24":[36],"30":[36],"36":[36],"43":[36],"45":[36]},"packages/frontend/editor-ui/src/features/core/dataTable/components/dataGrid/NullEmptyCellRenderer.vue":{"2":[36]},"packages/frontend/editor-ui/src/features/core/dataTable/composables/useDatePickerCommon.ts":{"11":[36],"17":[36],"22":[36],"35":[36],"60":[36],"67":[36],"84":[36]},"packages/frontend/editor-ui/src/features/core/dataTable/utils/typeUtils.ts":{"13":[36],"49":[36]},"packages/frontend/editor-ui/src/features/core/dataTable/utils.ts":{"1":[36],"6":[36]},"packages/@n8n/decorators/src/credential-resolver/errors.ts":{"5":[163],"16":[163],"27":[163]},"packages/cli/src/modules/dynamic-credentials.ee/credential-resolvers/identifiers/oauth2-metadata-http-client.ts":{"31":[163],"51":[163],"64":[163]},"packages/cli/src/modules/dynamic-credentials.ee/credential-resolvers/identifiers/oauth2-userinfo-identifier.ts":{"46":[163],"52":[163],"75":[163],"106":[163],"117":[163],"128":[163],"137":[163]},"packages/cli/src/modules/dynamic-credentials.ee/credential-resolvers/identifiers/oauth2-utils.ts":{"11":[163]},"packages/cli/src/modules/dynamic-credentials.ee/credential-resolvers/oauth-credential-resolver.ts":{"39":[163],"125":[163],"154":[163],"175":[163],"185":[163],"189":[163],"202":[163],"207":[163],"218":[163],"226":[163]},"packages/cli/src/modules/dynamic-credentials.ee/credential-resolvers/storage/dynamic-credential-entry-storage.ts":{"10":[163],"14":[163],"31":[163],"53":[163],"66":[163]},"packages/cli/src/modules/dynamic-credentials.ee/credential-resolvers.controller.ts":{"39":[163,170],"43":[163,170],"53":[163,170],"64":[163,170],"67":[163,170],"78":[163,170],"110":[163,170],"131":[163,170],"151":[163,170],"189":[163,170]},"packages/cli/src/modules/dynamic-credentials.ee/dynamic-credentials.controller.ts":{"35":[163,9],"51":[163,9],"77":[163,9],"105":[163,9],"117":[163,9],"147":[163,9],"159":[163,9],"227":[163,9],"308":[163,9]},"packages/cli/src/modules/dynamic-credentials.ee/services/credential-resolver.service.ts":{"48":[163,170],"64":[163,170],"89":[163,170],"92":[163,170],"97":[163,170],"100":[163,170],"107":[163,170],"112":[163,170],"113":[163,170],"121":[163,170],"134":[163,170],"192":[163,170],"206":[163,170],"220":[163,170],"262":[163,170],"290":[163,170],"297":[163,170],"311":[163,170]},"packages/cli/src/modules/dynamic-credentials.ee/services/dynamic-credential-storage.service.ts":{"25":[163],"34":[163],"119":[163],"125":[163]},"packages/cli/src/modules/dynamic-credentials.ee/services/resolver-config-expression.service.ts":{"16":[163],"23":[163]},"packages/cli/src/modules/dynamic-credentials.ee/services/shared-fields.ts":{"19":[163],"38":[163],"60":[163],"67":[163]},"packages/workflow/src/node-parameters/node-parameter-value-type-guard.ts":{"13":[163],"27":[163],"38":[163],"54":[163],"66":[163],"84":[163],"120":[163]},"localhost-44777/assets/_plugin-vue_export-helper-Da88TEg1.js":{"2":[34]},"localhost-44777/assets/chunk-CC9Q-vWm.js":{"8":[34],"9":[34],"10":[34],"19":[34],"23":[34],"29":[34],"33":[34],"34":[34]},"packages/cli/src/modules/dynamic-credentials.ee/context-establishment-hooks/bearer-token-extractor.ts":{"29":[169],"37":[169],"41":[169]},"packages/cli/src/modules/dynamic-credentials.ee/context-establishment-hooks/http-header-extractor.ts":{"24":[169],"32":[169],"53":[169],"75":[169],"101":[169],"105":[169]},"packages/workflow/src/trigger-identity.ts":{"29":[169],"47":[169]},"packages/@n8n/nodes-langchain/nodes/mcp/McpTrigger/McpServer.ts":{"29":[9,11,138],"83":[9,11,138],"95":[9,11,138],"104":[9,11,138],"117":[9,11,138],"126":[9,11,138],"127":[9,11,138],"130":[9,11,138],"145":[9,11,138],"161":[9,11,138],"221":[9,11,138],"248":[9,11,138],"272":[9,11,138],"276":[9,11,138],"286":[9,11,138],"302":[9,11,138],"320":[9,11,138],"372":[9,11,138],"377":[9,11,138],"382":[9,11,138],"386":[9,11,138],"390":[9,11,138],"394":[9,11,138],"396":[9,11,138],"402":[9,11,138],"409":[9,11,138],"427":[9,11,138],"431":[9,11,138],"434":[9,11,138],"437":[9,11,138],"441":[9,11,138],"445":[9,11,138],"449":[9,11,138],"453":[9,11,138],"457":[9,11,138],"468":[9,11,138],"477":[9,11,138],"493":[9,11,138],"510":[9,11,138],"524":[9,11,138],"537":[9,11,138],"542":[9,11,138],"543":[9,11,138],"583":[9,11,138],"586":[9,11,138],"593":[9,11,138],"605":[9,11,138],"621":[9,11,138],"676":[9,11,138],"679":[9,11,138]},"packages/@n8n/nodes-langchain/nodes/mcp/McpTrigger/McpTrigger.node.ts":{"21":[9,11],"169":[9,11]},"packages/@n8n/nodes-langchain/nodes/mcp/McpTrigger/execution/DirectExecutionStrategy.ts":{"6":[9,11]},"packages/@n8n/nodes-langchain/nodes/mcp/McpTrigger/execution/ExecutionCoordinator.ts":{"10":[9,11],"14":[9,11],"22":[9,11],"26":[9,11],"30":[9,11]},"packages/@n8n/nodes-langchain/nodes/mcp/McpTrigger/execution/PendingCallsManager.ts":{"10":[9,11],"12":[9,11],"15":[9,11],"21":[9,11],"22":[9,11],"38":[9,11],"49":[9,11],"60":[9,11],"64":[9,11],"68":[9,11],"69":[9,11],"72":[9,11],"76":[9,11]},"packages/@n8n/nodes-langchain/nodes/mcp/McpTrigger/n8n-oauth2-auth.ts":{"3":[9,11],"7":[9,11],"16":[9,11],"31":[9,11]},"packages/@n8n/nodes-langchain/nodes/mcp/McpTrigger/protocol/MessageFormatter.ts":{"18":[9,11],"52":[9,11],"68":[9,11],"81":[9,11],"82":[9,11],"86":[9,11],"112":[9,11],"113":[9,11],"114":[9,11],"120":[9,11],"126":[9,11]},"packages/@n8n/nodes-langchain/nodes/mcp/McpTrigger/protocol/MessageParser.ts":{"11":[9,11],"20":[9,11],"30":[9,11],"40":[9,11],"49":[9,11]},"packages/@n8n/nodes-langchain/nodes/mcp/McpTrigger/session/InMemorySessionStore.ts":{"5":[9,11],"11":[9,11],"16":[9,11],"21":[9,11],"26":[9,11],"30":[9,11],"34":[9,11]},"packages/@n8n/nodes-langchain/nodes/mcp/McpTrigger/session/SessionManager.ts":{"18":[9,11,138],"20":[9,11,138],"35":[9,11,138],"43":[9,11,138],"45":[9,11,138],"46":[9,11,138],"49":[9,11,138],"53":[9,11,138],"56":[9,11,138],"60":[9,11,138],"64":[9,11,138],"68":[9,11,138],"72":[9,11,138],"76":[9,11,138],"80":[9,11,138],"84":[9,11,138]},"packages/@n8n/nodes-langchain/nodes/mcp/McpTrigger/transport/StreamableHttpTransport.ts":{"17":[9,11],"32":[9,11],"37":[9,11],"49":[9,11],"54":[9,11]},"packages/@n8n/nodes-langchain/nodes/mcp/McpTrigger/transport/TransportFactory.ts":{"13":[9,11],"17":[9,11],"23":[9,11],"30":[9,11],"35":[9,11]},"packages/@n8n/nodes-langchain/utils/schemaParsing.ts":{"9":[9,11],"10":[9,11],"36":[9,11],"51":[9,11],"55":[9,11]},"packages/cli/src/modules/dynamic-credentials.ee/context-establishment-hooks/n8n-oauth/n8n-oauth-extractor.ts":{"18":[9],"31":[9],"37":[9]},"packages/cli/src/modules/dynamic-credentials.ee/context-establishment-hooks/n8n-oauth/n8n-oauth-seeder.ts":{"11":[9],"18":[9]},"packages/cli/src/modules/dynamic-credentials.ee/credential-resolvers/identifiers/n8n-identifier.ts":{"47":[9],"52":[9],"56":[9]},"packages/cli/src/modules/dynamic-credentials.ee/credential-resolvers/n8n-credential-resolver.ts":{"22":[9],"40":[9],"68":[9],"88":[9],"102":[9],"106":[9],"110":[9],"121":[9],"128":[9]},"packages/cli/src/modules/dynamic-credentials.ee/credential-resolvers/storage/dynamic-credential-user-entry-storage.ts":{"14":[9],"26":[9],"52":[9],"81":[9],"100":[9]},"packages/cli/src/modules/dynamic-credentials.ee/services/authorize-intent.service.ts":{"37":[9],"39":[9],"48":[9],"52":[9]},"packages/cli/src/modules/dynamic-credentials.ee/services/credential-check-proxy.service.ts":{"21":[9],"31":[9],"49":[9],"70":[9],"82":[9]},"packages/cli/src/services/trigger-auth-identity-seeder-proxy.service.ts":{"12":[9],"14":[9],"18":[9]},"packages/frontend/editor-ui/src/features/credentials/credentials.ee.api.ts":{"6":[185,119,63],"19":[185,50]},"packages/cli/src/execution-lifecycle/save-execution-progress.ts":{"8":[135]},"packages/nodes-base/nodes/Filter/Filter.node.ts":{"8":[53,135]},"packages/nodes-base/nodes/Filter/V1/FilterV1.node.ts":{"17":[53,135],"310":[53,135]},"packages/nodes-base/nodes/Filter/V1/GenericFunctions.ts":{"5":[53,135],"8":[53,135],"13":[53,135],"14":[53,135],"15":[53,135],"17":[53,135],"19":[53,135],"21":[53,135],"23":[53,135],"24":[53,135],"25":[53,135],"26":[53,135],"28":[53,135],"30":[53,135],"32":[53,135],"34":[53,135],"36":[53,135],"42":[53,135],"50":[53,135],"64":[53,135],"81":[53,135]},"packages/nodes-base/nodes/Filter/V2/FilterV2.node.ts":{"20":[53,135],"95":[53,135],"99":[53,135]},"packages/nodes-base/nodes/Notion/NotionTrigger.node.ts":{"30":[135],"38":[135],"49":[135],"268":[135],"341":[135],"350":[135],"353":[135]},"packages/nodes-base/nodes/Telegram/GenericFunctions.ts":{"72":[19,135,68],"134":[19,135,68],"208":[19,135,68],"248":[19,135,68],"261":[19,135,68],"265":[19,135,68],"271":[19,135,68],"292":[19,135,68]},"packages/nodes-base/nodes/Telegram/Telegram.node.ts":{"2043":[19,135,68],"2049":[19,135,68]},"packages/nodes-base/nodes/Telegram/hitl/setup.ts":{"17":[19,135,68],"30":[19,135,68],"47":[19,135,68],"73":[19,135,68]},"packages/nodes-base/nodes/Telegram/hitl/tokens.ts":{"17":[19,135,68]},"packages/nodes-base/nodes/Telegram/hitl/webhook.ts":{"12":[19,135,68],"21":[19,135,68],"25":[19,135,68],"36":[19,135,68],"51":[19,135,68],"87":[19,135,68],"126":[19,135,68]},"packages/frontend/@n8n/design-system/src/components/N8nIcon/nodes/filter.svg":{"11":[53,135]},"packages/frontend/@n8n/design-system/src/components/N8nIcon/nodes/merge.svg":{"11":[22,125,135,13]},"localhost-38209/assets/_plugin-vue_export-helper-Da88TEg1.js":{"2":[20]},"localhost-38209/assets/chunk-CC9Q-vWm.js":{"8":[20],"9":[20],"10":[20],"19":[20],"23":[20],"29":[20],"33":[20],"34":[20]},"packages/frontend/@n8n/design-system/src/components/CanvasPill/CanvasPill.vue":{"8":[19,20],"9":[19,20]},"packages/frontend/@n8n/design-system/src/components/CanvasThinkingPill/CanvasThinkingPill.vue":{"17":[19,20],"23":[19,20],"27":[19,20],"30":[19,20]},"packages/frontend/editor-ui/src/features/ai/assistant/assistant.types.ts":{"422":[19,20],"439":[19],"458":[19,20],"464":[19,20],"468":[19],"492":[19],"505":[19],"511":[19],"523":[19],"529":[19],"535":[19],"545":[19],"551":[19],"555":[19],"561":[19],"573":[19,20],"579":[19]},"packages/frontend/editor-ui/src/features/ai/assistant/builder.utils.ts":{"19":[19,20],"23":[19,20],"27":[19,20],"130":[19,20],"141":[19,20]},"packages/frontend/editor-ui/src/features/ai/assistant/components/HubSwitcher.vue":{"12":[19,20],"22":[19,20],"29":[19,20],"36":[19,20]},"packages/frontend/editor-ui/src/features/workflows/canvas/components/elements/nodes/render-types/CanvasNodeChoicePrompt.vue":{"21":[19,185,20],"30":[19,185,20],"41":[19,185,20],"44":[19,185,20],"46":[19,185,20],"50":[185],"61":[185],"65":[19,185,20],"75":[185],"83":[19,185,20],"87":[19,185,20],"112":[185]},"localhost-40355/assets/_plugin-vue_export-helper-Da88TEg1.js":{"2":[64]},"localhost-40355/assets/chunk-CC9Q-vWm.js":{"8":[64],"9":[64],"10":[64],"19":[64],"23":[64],"29":[64],"33":[64],"34":[64]},"packages/nodes-base/nodes/Transform/Sort/Sort.node.ts":{"164":[144,145],"221":[144,145],"226":[144,145]},"packages/nodes-base/nodes/Transform/Sort/utils.ts":{"8":[144,145]},"packages/nodes-base/nodes/Kafka/Kafka.node.ts":{"222":[45],"230":[45],"270":[45],"298":[45]},"packages/nodes-base/nodes/Kafka/KafkaTrigger.node.ts":{"389":[45],"417":[45],"426":[45],"450":[45],"487":[45],"495":[45],"519":[45]},"packages/nodes-base/nodes/Kafka/utils.ts":{"95":[45],"119":[45],"154":[45],"157":[45],"190":[45],"241":[45],"247":[45],"283":[45],"313":[45],"337":[45],"342":[45],"345":[45],"348":[45],"353":[45],"357":[45],"360":[45],"363":[45],"366":[45],"369":[45],"398":[45],"401":[45],"416":[45],"436":[45],"440":[45],"484":[45],"539":[45],"566":[45],"587":[45],"596":[45],"621":[45],"627":[45],"628":[45],"664":[45],"686":[45],"694":[45]},"localhost-44571/assets/_plugin-vue_export-helper-Da88TEg1.js":{"2":[45]},"localhost-44571/assets/chunk-CC9Q-vWm.js":{"8":[45],"9":[45],"10":[45],"19":[45],"23":[45],"29":[45],"33":[45],"34":[45]},"localhost-40413/assets/_plugin-vue_export-helper-Da88TEg1.js":{"2":[50]},"localhost-40413/assets/chunk-CC9Q-vWm.js":{"8":[50],"9":[50],"10":[50],"19":[50],"23":[50],"29":[50],"33":[50],"34":[50]},"packages/frontend/editor-ui/src/features/collaboration/projects/components/ProjectMoveResourceModal.vue":{"42":[50],"68":[50],"75":[50],"92":[50],"96":[50],"99":[50],"100":[50],"102":[50],"108":[50],"111":[50],"121":[50],"124":[50],"129":[50],"133":[50],"182":[50],"198":[50],"201":[50],"203":[50],"211":[50],"215":[50],"216":[50],"222":[50],"223":[50],"231":[50],"249":[50],"254":[50],"346":[50],"349":[50],"357":[50]},"packages/frontend/editor-ui/src/features/collaboration/projects/components/ProjectMoveSuccessToastMessage.vue":{"19":[50],"20":[50],"21":[50],"24":[50]},"packages/frontend/editor-ui/src/features/collaboration/projects/composables/useMoveResourceToProjectToast.ts":{"26":[50]},"localhost-36211/assets/_plugin-vue_export-helper-Da88TEg1.js":{"2":[55,124,133,144,146,151]},"localhost-36211/assets/chunk-CC9Q-vWm.js":{"8":[55,124,133,144,146,151],"9":[55,124,133,144,146,151],"10":[55,124,133,144,146,151],"19":[55,124,133,144,146,151],"23":[55,124,133,144,146,151],"29":[55,124,133,144,146,151],"33":[55,124,133,144,146,151],"34":[55,124,133,144,146,151]},"localhost-36211/assets/verified-L2wNNiNu.js":{"8":[124,146]},"packages/frontend/editor-ui/src/app/components/WorkflowPreviewHost.vue":{"45":[133,161],"50":[133,161],"61":[133,161],"76":[133,161],"90":[133,161],"122":[133,161],"124":[133,161],"128":[133,161]},"packages/frontend/editor-ui/src/features/workflows/workflowHistory/components/WorkflowHistoryContent.vue":{"40":[133],"55":[133],"70":[133],"81":[133],"84":[133],"97":[133],"101":[133],"107":[133],"129":[133],"130":[133],"135":[133],"150":[133],"152":[133],"172":[133]},"packages/frontend/editor-ui/src/features/workflows/workflowHistory/components/WorkflowHistoryList.vue":{"22":[133],"49":[133],"61":[133],"73":[133],"82":[133],"86":[133],"92":[133],"119":[133],"151":[133],"179":[133],"184":[133]},"packages/frontend/editor-ui/src/features/workflows/workflowHistory/components/WorkflowHistoryListItem.vue":{"26":[133],"58":[133],"63":[133],"74":[133],"83":[133],"99":[133],"165":[133],"172":[133],"203":[133],"211":[133],"214":[133]},"packages/frontend/editor-ui/src/features/workflows/workflowHistory/views/WorkflowHistory.vue":{"61":[133],"78":[133],"81":[133],"92":[133],"93":[133],"100":[133],"101":[133],"106":[133],"112":[133],"117":[133],"127":[133],"128":[133],"143":[133],"154":[133],"169":[133],"177":[133],"181":[133],"199":[133],"228":[133],"572":[133],"587":[133],"624":[133],"643":[133]},"packages/nodes-base/nodes/N8nTrainingCustomerDatastore/N8nTrainingCustomerDatastore.node.ts":{"119":[144]},"packages/frontend/editor-ui/src/app/utils/stringUtils.ts":{"8":[142,144],"16":[142,144],"21":[142,144],"22":[142,144]},"packages/frontend/editor-ui/src/features/ndv/runData/components/RunDataPaginationBar.vue":{"12":[106,142,144],"16":[106,142,144],"28":[106],"39":[106,142,144],"40":[106,142,144]},"packages/nodes-base/nodes/Pipedrive/Pipedrive.node.ts":{"8":[146]},"packages/nodes-base/nodes/Pipedrive/v1/PipedriveV1.node.ts":{"37":[146],"2256":[146],"2393":[146],"3809":[146],"3820":[146],"3834":[146],"3862":[146],"3876":[146],"3900":[146],"3904":[146],"3908":[146],"3912":[146],"3916":[146],"3926":[146],"3930":[146],"3944":[146],"3962":[146],"3978":[146],"3994":[146],"4023":[146],"4053":[146],"4058":[146],"4063":[146],"4068":[146],"4073":[146],"4103":[146],"4949":[146]},"packages/nodes-base/nodes/Pipedrive/v2/PipedriveV2.node.ts":{"16":[146],"28":[146]},"packages/nodes-base/nodes/Pipedrive/v2/actions/activity/create.operation.ts":{"119":[146]},"packages/nodes-base/nodes/Pipedrive/v2/actions/activity/delete.operation.ts":{"31":[146]},"packages/nodes-base/nodes/Pipedrive/v2/actions/activity/get.operation.ts":{"34":[146]},"packages/nodes-base/nodes/Pipedrive/v2/actions/activity/getAll.operation.ts":{"126":[146]},"packages/nodes-base/nodes/Pipedrive/v2/actions/activity/update.operation.ts":{"140":[146]},"packages/nodes-base/nodes/Pipedrive/v2/actions/deal/create.operation.ts":{"222":[146]},"packages/nodes-base/nodes/Pipedrive/v2/actions/deal/delete.operation.ts":{"31":[146]},"packages/nodes-base/nodes/Pipedrive/v2/actions/deal/duplicate.operation.ts":{"31":[146]},"packages/nodes-base/nodes/Pipedrive/v2/actions/deal/get.operation.ts":{"34":[146]},"packages/nodes-base/nodes/Pipedrive/v2/actions/deal/getAll.operation.ts":{"124":[146]},"packages/nodes-base/nodes/Pipedrive/v2/actions/deal/search.operation.ts":{"138":[146]},"packages/nodes-base/nodes/Pipedrive/v2/actions/deal/update.operation.ts":{"179":[146]},"packages/nodes-base/nodes/Pipedrive/v2/actions/dealProduct/add.operation.ts":{"117":[146]},"packages/nodes-base/nodes/Pipedrive/v2/actions/dealProduct/getAll.operation.ts":{"54":[146]},"packages/nodes-base/nodes/Pipedrive/v2/actions/dealProduct/remove.operation.ts":{"40":[146]},"packages/nodes-base/nodes/Pipedrive/v2/actions/dealProduct/update.operation.ts":{"116":[146]},"packages/nodes-base/nodes/Pipedrive/v2/actions/file/create.operation.ts":{"87":[146]},"packages/nodes-base/nodes/Pipedrive/v2/actions/file/delete.operation.ts":{"31":[146]},"packages/nodes-base/nodes/Pipedrive/v2/actions/file/download.operation.ts":{"34":[146]},"packages/nodes-base/nodes/Pipedrive/v2/actions/file/get.operation.ts":{"31":[146]},"packages/nodes-base/nodes/Pipedrive/v2/actions/file/update.operation.ts":{"54":[146]},"packages/nodes-base/nodes/Pipedrive/v2/actions/lead/create.operation.ts":{"148":[146],"175":[146]},"packages/nodes-base/nodes/Pipedrive/v2/actions/lead/delete.operation.ts":{"31":[146]},"packages/nodes-base/nodes/Pipedrive/v2/actions/lead/get.operation.ts":{"31":[146]},"packages/nodes-base/nodes/Pipedrive/v2/actions/lead/getAll.operation.ts":{"100":[146]},"packages/nodes-base/nodes/Pipedrive/v2/actions/lead/update.operation.ts":{"105":[146],"132":[146]},"packages/nodes-base/nodes/Pipedrive/v2/actions/note/create.operation.ts":{"96":[146]},"packages/nodes-base/nodes/Pipedrive/v2/actions/note/delete.operation.ts":{"31":[146]},"packages/nodes-base/nodes/Pipedrive/v2/actions/note/get.operation.ts":{"31":[146]},"packages/nodes-base/nodes/Pipedrive/v2/actions/note/getAll.operation.ts":{"143":[146]},"packages/nodes-base/nodes/Pipedrive/v2/actions/note/update.operation.ts":{"103":[146]},"packages/nodes-base/nodes/Pipedrive/v2/actions/organization/create.operation.ts":{"71":[146]},"packages/nodes-base/nodes/Pipedrive/v2/actions/organization/delete.operation.ts":{"31":[146]},"packages/nodes-base/nodes/Pipedrive/v2/actions/organization/get.operation.ts":{"34":[146]},"packages/nodes-base/nodes/Pipedrive/v2/actions/organization/getAll.operation.ts":{"81":[146]},"packages/nodes-base/nodes/Pipedrive/v2/actions/organization/search.operation.ts":{"98":[146]},"packages/nodes-base/nodes/Pipedrive/v2/actions/organization/update.operation.ts":{"78":[146]},"packages/nodes-base/nodes/Pipedrive/v2/actions/person/create.operation.ts":{"164":[146]},"packages/nodes-base/nodes/Pipedrive/v2/actions/person/delete.operation.ts":{"31":[146]},"packages/nodes-base/nodes/Pipedrive/v2/actions/person/get.operation.ts":{"34":[146]},"packages/nodes-base/nodes/Pipedrive/v2/actions/person/getAll.operation.ts":{"80":[146]},"packages/nodes-base/nodes/Pipedrive/v2/actions/person/search.operation.ts":{"117":[146]},"packages/nodes-base/nodes/Pipedrive/v2/actions/person/update.operation.ts":{"171":[146]},"packages/nodes-base/nodes/Pipedrive/v2/actions/product/create.operation.ts":{"126":[146]},"packages/nodes-base/nodes/Pipedrive/v2/actions/product/delete.operation.ts":{"31":[146]},"packages/nodes-base/nodes/Pipedrive/v2/actions/product/get.operation.ts":{"34":[146]},"packages/nodes-base/nodes/Pipedrive/v2/actions/product/getAll.operation.ts":{"53":[146]},"packages/nodes-base/nodes/Pipedrive/v2/actions/product/search.operation.ts":{"101":[146]},"packages/nodes-base/nodes/Pipedrive/v2/actions/product/update.operation.ts":{"133":[146]},"packages/nodes-base/nodes/Pipedrive/v2/actions/router.ts":{"15":[146]},"packages/nodes-base/nodes/Pipedrive/v2/helpers/customFields.ts":{"24":[146],"36":[146],"58":[146],"59":[146],"61":[146],"69":[146],"97":[146],"130":[146],"142":[146],"143":[146]},"packages/nodes-base/nodes/Pipedrive/v2/helpers/fields.ts":{"10":[146]},"packages/nodes-base/nodes/Pipedrive/v2/helpers/index.ts":{"3":[146],"7":[146]},"packages/nodes-base/nodes/Pipedrive/v2/helpers/searchResponse.ts":{"9":[146],"14":[146],"25":[146]},"packages/nodes-base/nodes/Pipedrive/v2/helpers/typeCoercion.ts":{"4":[146],"18":[146],"32":[146],"55":[146]},"packages/nodes-base/nodes/Pipedrive/v2/methods/loadOptions.ts":{"9":[146],"34":[146],"65":[146],"83":[146],"117":[146],"120":[146],"127":[146],"130":[146],"137":[146],"143":[146],"150":[146],"162":[146],"187":[146],"193":[146],"203":[146],"206":[146],"213":[146],"223":[146],"226":[146]},"packages/nodes-base/nodes/Pipedrive/v2/transport/pipedrive.api.ts":{"30":[146],"96":[146],"126":[146],"170":[146],"229":[146],"232":[146]},"packages/@n8n/ai-utilities/src/utils/loaders/n8n-pdf-loader.ts":{"21":[0],"26":[0],"43":[0],"57":[0],"73":[0],"82":[0]},"packages/@n8n/ai-utilities/src/utils/n8n-binary-loader.ts":{"33":[0],"40":[0],"54":[0],"60":[0],"86":[0],"104":[0],"151":[0],"158":[0],"166":[0],"174":[0],"198":[0],"227":[0]},"localhost-40371/assets/_plugin-vue_export-helper-Da88TEg1.js":{"2":[0,82,88,90,98,101,125]},"localhost-40371/assets/chunk-CC9Q-vWm.js":{"8":[0,82,88,90,98,101,125],"9":[0,82,88,90,98,101,125],"10":[0,82,88,90,98,101,125],"19":[0,82,88,90,98,101,125],"23":[0,82,88,90,98,101,125],"29":[0,82,88,90,98,101,125],"33":[0,82,88,90,98,101,125],"34":[0,82,88,90,98,101,125]},"packages/@n8n/nodes-langchain/nodes/vector_store/VectorStoreZep/VectorStoreZep.node.ts":{"81":[185],"108":[185]},"packages/frontend/editor-ui/src/app/components/NodeExecutionErrorMessage.vue":{"4":[185],"5":[185]},"packages/frontend/editor-ui/src/features/agents/composables/useAgentCapabilitySummary.ts":{"19":[185],"33":[185],"48":[185],"57":[185],"63":[185],"77":[185],"88":[185]},"packages/frontend/editor-ui/src/features/workflows/canvas/components/elements/nodes/render-types/CanvasNodeAgent.vue":{"26":[185],"52":[185],"54":[185],"59":[185],"67":[185],"71":[185],"82":[185],"92":[185],"96":[185],"98":[185],"105":[185],"110":[185],"116":[185],"129":[185],"133":[185],"146":[185],"150":[185],"156":[185],"160":[185],"175":[185],"178":[185],"182":[185],"196":[185],"202":[185],"231":[185]},"packages/frontend/editor-ui/src/features/workflows/canvas/experimental/components/ExperimentalCanvasNodeSettings.vue":{"21":[185,112,127],"31":[185,112,127],"32":[185,112,127],"35":[185,112,127],"36":[185,112,127],"41":[185,112,127],"43":[185],"49":[185],"70":[185,112,127],"92":[185],"94":[185]},"packages/frontend/editor-ui/src/features/workflows/canvas/experimental/components/ExperimentalEmbeddedNdvActions.vue":{"6":[185]},"packages/frontend/editor-ui/src/features/workflows/canvas/experimental/components/ExperimentalEmbeddedNodeDetails.vue":{"25":[185],"28":[185],"34":[185],"35":[185],"43":[185],"56":[185],"62":[185],"66":[185],"70":[185],"78":[185],"81":[185],"107":[185],"119":[185],"122":[185]},"packages/frontend/editor-ui/src/features/workflows/canvas/experimental/composables/useExpressionResolveCtx.ts":{"7":[19,185,112,127],"12":[19,185,112,127],"21":[19,185,112,127]},"packages/frontend/editor-ui/src/features/workflows/canvas/experimental/experimentalNdv.utils.ts":{"5":[185],"25":[185],"26":[185]},"packages/cli/src/modules/mcp/mcp-api-key.service.ts":{"24":[164],"30":[164],"53":[164],"68":[164],"102":[164],"111":[164],"124":[164],"140":[164],"141":[164]},"packages/cli/src/modules/mcp/mcp.errors.ts":{"13":[164],"30":[164]},"packages/cli/src/modules/mcp/tools/webhook-utils.ts":{"47":[164],"55":[164],"63":[164],"69":[164],"134":[164],"140":[164],"149":[164],"155":[164],"165":[164],"171":[164],"185":[164],"197":[164],"214":[164],"251":[164],"254":[164],"259":[164],"269":[164],"285":[164],"315":[164],"330":[164],"350":[164]},"packages/cli/src/modules/mcp/tools/workflow-validation.utils.ts":{"20":[164],"51":[164]},"packages/frontend/editor-ui/src/features/shared/nodeCreator/components/ItemTypes/OpenTemplateItem.vue":{"14":[98,48],"23":[98,48],"27":[48]},"localhost-37321/assets/_plugin-vue_export-helper-Da88TEg1.js":{"2":[51]},"localhost-37321/assets/chunk-CC9Q-vWm.js":{"8":[51],"9":[51],"10":[51],"19":[51],"23":[51],"29":[51],"33":[51],"34":[51]},"localhost-37321/assets/verified-L2wNNiNu.js":{"8":[51]},"packages/frontend/@n8n/design-system/src/components/N8nIcon/svgContentDirective.ts":{"13":[51],"21":[51]},"packages/frontend/@n8n/design-system/src/components/N8nIconPicker/IconColorPicker.vue":{"15":[51],"63":[51],"65":[51],"81":[51],"92":[51],"100":[51]},"packages/frontend/@n8n/design-system/src/components/N8nIconPicker/SkinTonePicker.vue":{"10":[51],"29":[51],"39":[51],"50":[51],"58":[51]},"packages/frontend/@n8n/design-system/src/components/N8nIconPicker/useIconPickerVirtualRows.ts":{"30":[51],"40":[51],"60":[51],"79":[51]},"packages/frontend/editor-ui/src/features/collaboration/projects/components/ProjectIcon.vue":{"35":[51]},"packages/@n8n/db/src/utils/dialect-time.ts":{"13":[167,178,57],"22":[167,178,57],"36":[167,178,57]},"packages/@n8n/scheduler/src/core/factory.ts":{"113":[167,178,57],"131":[167,178,57],"188":[167,178,57],"198":[167,178,57],"231":[167,178,57],"246":[167,178,57],"253":[167,178,57],"259":[167,178,57],"265":[167,178,57],"271":[167,178,57],"272":[167,178,57],"283":[167,178,57],"284":[167,178,57],"289":[167,178,57],"314":[167,178,57],"320":[167,178,57],"326":[167,178,57],"337":[167,178,57],"353":[167,178,57],"367":[167,178,57],"368":[167,178,57],"374":[167,178,57],"379":[167,178,57],"389":[167,178,57],"394":[167,178,57],"404":[167,178,57],"407":[167,178,57],"416":[167,178,57],"425":[167,178,57],"428":[167,178,57],"434":[167,178,57],"450":[167,178,57],"456":[167,178,57],"462":[167,178,57],"499":[167,178,57],"524":[167,178,57],"538":[167,178,57],"551":[167,178,57],"554":[167,178,57],"557":[167,178,57]},"packages/@n8n/scheduler/src/observability/materializer-tracing.ts":{"17":[167,178,57],"32":[167,178,57]},"packages/cli/src/metrics/prometheus/scheduler-metrics.service.ts":{"41":[167,178,57],"48":[167,178,57],"52":[167,178,57],"58":[167,178,57],"120":[167,178,57],"132":[167,178,57],"138":[167,178,57],"147":[167,178,57],"156":[167,178,57],"165":[167,178,57],"180":[167,178,57],"186":[167,178,57],"192":[167,178,57],"198":[167,178,57],"204":[167,178,57],"211":[167,178,57],"220":[167,178,57],"226":[167,178,57]},"packages/cli/src/scheduling/durable-scheduler.ts":{"26":[167,178,57],"82":[167,178,57],"83":[167,178,57],"91":[167,178,57],"104":[167,178,57],"109":[167,178,57],"114":[167,178,57],"119":[167,178,57],"121":[167,178,57],"123":[167,178,57],"125":[167,178,57],"127":[167,178,57],"130":[167,178,57]},"packages/frontend/editor-ui/src/app/utils/formatters/textFormatter.ts":{"8":[105]},"localhost-40371/assets/verified-L2wNNiNu.js":{"8":[125]},"packages/@n8n/scheduler/src/core/executor/executor.ts":{"103":[167,57],"135":[167,57],"172":[167,57],"175":[167,57],"186":[167,57],"189":[167,57],"194":[167,57],"207":[167,57],"208":[167,57],"212":[167,57],"255":[167,57],"257":[167,57],"258":[167,57],"321":[167,57],"337":[167,57],"344":[167,57],"353":[167,57]},"packages/@n8n/scheduler/src/core/executor/precision-timer.ts":{"14":[167,57],"15":[167,57],"16":[167,57],"35":[167,57],"38":[167,57],"43":[167,57],"50":[167,57],"58":[167,57],"66":[167,57]},"packages/@n8n/scheduler/src/core/executor/task-handler.ts":{"50":[167,57],"52":[167,57],"56":[167,57],"88":[167,57],"92":[167,57],"99":[167,57],"104":[167,57]},"packages/@n8n/scheduler/src/observability/attributes.ts":{"71":[167,57]},"packages/@n8n/scheduler/src/observability/executor-tracing.ts":{"21":[167,57],"23":[167,57],"35":[167,57],"66":[167,57],"70":[167,57],"81":[167,57],"94":[167,57]},"packages/cli/src/scheduling/schedule-trigger-node/schedule-trigger-task-handler.ts":{"36":[167,57],"49":[167,57],"117":[167,57],"126":[167,57],"131":[167,57],"148":[167,57]},"packages/cli/src/scheduling/schedule-trigger-node/schedule-trigger-task.ts":{"19":[167,57],"25":[167,57],"30":[167,57],"40":[167,57]},"packages/@n8n/api-types/src/dto/user/role-change-request.dto.ts":{"9":[117]},"packages/cli/src/modules/chat-hub/chat-hub-event-relay.service.ts":{"10":[117],"19":[117],"27":[117]},"packages/cli/src/modules/data-table/data-table.module.ts":{"7":[117],"20":[117],"27":[117],"40":[117],"51":[117],"58":[117]},"packages/cli/src/services/ownership-transfer/ownership-transfer-handler.registry.ts":{"25":[117],"28":[117],"32":[117]},"packages/cli/src/services/ownership-transfer/ownership-transfer.service.ts":{"21":[117],"36":[117],"39":[117],"64":[117]},"packages/frontend/editor-ui/src/app/utils/rbac/checks/isDefaultUser.ts":{"4":[117]},"packages/frontend/editor-ui/src/features/roles/instance/instanceRoleScopes.ts":{"110":[117],"120":[117],"125":[117],"137":[117],"176":[117],"180":[117],"295":[117]},"packages/frontend/editor-ui/src/features/settings/sso/provisioning/composables/userRoleProvisioning.store.ts":{"13":[117],"18":[117]},"packages/frontend/editor-ui/src/features/settings/users/components/DeleteUserModal.vue":{"15":[117],"32":[117],"35":[117],"38":[117],"40":[117],"49":[117],"65":[117],"71":[117],"105":[117],"118":[117],"122":[117],"128":[117],"132":[117],"134":[117],"137":[117],"142":[117],"154":[117],"155":[117],"156":[117],"165":[117],"174":[117]},"packages/frontend/editor-ui/src/features/settings/users/components/SettingsUsersActionsCell.vue":{"7":[117],"16":[117],"21":[117]},"packages/frontend/editor-ui/src/features/settings/users/components/SettingsUsersLastActiveCell.vue":{"7":[117],"10":[117],"13":[117]},"packages/frontend/editor-ui/src/features/settings/users/components/SettingsUsersProjectsCell.vue":{"8":[117],"20":[117],"26":[117],"33":[117],"34":[117],"38":[117],"51":[117],"52":[117]},"packages/frontend/editor-ui/src/features/settings/users/components/SettingsUsersProjectsModal.vue":{"17":[117],"52":[117]},"packages/frontend/editor-ui/src/features/settings/users/components/SettingsUsersRoleCell.vue":{"31":[117],"33":[117],"34":[117],"36":[117],"40":[117],"41":[117],"45":[117],"48":[117],"49":[117],"50":[117],"52":[117],"56":[117],"58":[117]},"packages/frontend/editor-ui/src/features/settings/users/components/SettingsUsersTable.vue":{"19":[117],"41":[117],"47":[117],"63":[117],"74":[117],"85":[117],"96":[117],"127":[117],"135":[117],"138":[117],"143":[117],"145":[117],"160":[117],"165":[117],"176":[117],"179":[117],"182":[117],"186":[117]},"packages/frontend/editor-ui/src/features/settings/users/views/SettingsUsersView.vue":{"71":[117],"73":[117],"77":[117],"81":[117],"83":[117],"98":[117],"103":[117],"112":[117],"118":[117],"125":[117],"134":[117],"139":[117],"143":[117],"147":[117],"171":[117],"206":[117],"211":[117],"328":[117],"329":[117],"338":[117],"350":[117],"373":[117],"378":[117],"425":[117],"428":[117],"433":[117],"442":[117],"502":[117],"509":[117]},"localhost-36197/assets/_plugin-vue_export-helper-Da88TEg1.js":{"2":[19,87,48,53,112,129,141,160]},"localhost-36197/assets/chunk-CC9Q-vWm.js":{"8":[19,87,48,53,112,129,141,160],"9":[19,87,48,53,112,129,141,160],"10":[19,87,48,53,112,129,141,160],"19":[19,87,48,53,112,129,141,160],"23":[19,87,48,53,112,129,141,160],"29":[19,87,48,53,112,129,141,160],"33":[19,87,48,53,112,129,141,160],"34":[19,87,48,53,112,129,141,160]},"packages/frontend/editor-ui/src/features/ai/assistant/components/Agent/BuilderSetupCard.vue":{"30":[19],"55":[19],"59":[19],"61":[19],"84":[19],"91":[19],"97":[19],"101":[19],"105":[19],"106":[19],"107":[19],"111":[19],"112":[19],"113":[19],"114":[19],"116":[19],"122":[19],"140":[19],"153":[19],"186":[19],"191":[19],"193":[19],"209":[19],"210":[19],"213":[19],"222":[19],"223":[19]},"packages/frontend/editor-ui/src/features/ai/assistant/components/Agent/BuilderSetupWizard.vue":{"14":[19],"39":[19],"43":[19],"47":[19],"51":[19],"55":[19],"59":[19],"75":[19],"86":[19],"91":[19],"96":[19],"102":[19],"104":[19],"107":[19],"115":[19],"122":[19],"131":[19],"140":[19],"162":[19],"175":[19],"187":[19],"210":[19]},"packages/frontend/editor-ui/src/features/ai/assistant/components/Agent/CreditsSettingsDropdown.vue":{"9":[19],"32":[19],"99":[19]},"packages/frontend/editor-ui/src/features/ai/assistant/components/Agent/ExecuteMessage.vue":{"34":[19],"51":[19],"58":[19],"87":[19],"136":[19],"146":[19],"191":[19],"223":[19]},"packages/frontend/editor-ui/src/features/ai/assistant/composables/useBuilderExecution.ts":{"21":[19],"53":[19]},"packages/frontend/editor-ui/src/features/ai/assistant/composables/useBuilderSetupCards.ts":{"10":[19],"21":[19],"26":[19],"30":[19],"45":[19],"59":[19],"60":[19],"65":[19],"67":[19],"71":[19],"74":[19],"77":[19],"89":[19],"90":[19],"104":[19],"110":[19],"157":[19]},"packages/frontend/editor-ui/src/features/setupPanel/components/TriggerExecuteButton.vue":{"6":[19],"18":[19],"22":[19],"30":[19]},"packages/frontend/editor-ui/src/features/setupPanel/components/cards/SetupCardBody.vue":{"22":[19],"57":[19],"61":[19],"63":[19],"70":[19],"77":[19],"94":[19],"97":[19],"106":[19],"110":[19],"111":[19],"113":[19],"124":[19],"135":[19],"136":[19],"143":[19],"183":[19],"197":[19],"200":[19],"206":[19]},"packages/frontend/editor-ui/src/features/setupPanel/components/cards/SetupCardSection.vue":{"9":[19],"13":[19],"16":[19]},"packages/frontend/editor-ui/src/features/setupPanel/composables/groupSetupCards.ts":{"20":[19],"81":[19]},"packages/frontend/editor-ui/src/features/setupPanel/composables/useTriggerExecution.ts":{"18":[19],"38":[19],"40":[19],"46":[19],"70":[19],"77":[19],"81":[19],"87":[19],"91":[19]},"packages/frontend/editor-ui/src/features/setupPanel/composables/useWebhookUrls.ts":{"21":[19],"25":[19],"27":[19],"33":[19],"35":[19],"83":[19]},"packages/frontend/editor-ui/src/features/setupPanel/composables/useWorkflowSetupState.ts":{"50":[19],"67":[19],"74":[19],"121":[19],"124":[19],"160":[19],"198":[19],"226":[19],"231":[19],"297":[19],"299":[19],"300":[19],"306":[19],"330":[19],"353":[19],"354":[19],"361":[19],"362":[19],"365":[19],"366":[19],"376":[19],"390":[19],"404":[19],"417":[19],"434":[19],"435":[19],"437":[19],"441":[19],"449":[19],"468":[19],"471":[19],"477":[19],"478":[19],"513":[19],"622":[19],"638":[19],"641":[19],"664":[19],"665":[19],"667":[19],"669":[19],"700":[19],"703":[19],"714":[19],"718":[19],"730":[19],"800":[19],"811":[19],"837":[19]},"packages/frontend/editor-ui/src/features/workflows/canvas/experimental/components/ExperimentalEmbeddedNdvMapper.vue":{"19":[19,112,127],"39":[19,112,127],"43":[19,112,127],"45":[127],"54":[127],"80":[19,112,127],"87":[112,127],"96":[19,112,127]},"packages/frontend/@n8n/chat/src/components/GetStarted.vue":{"5":[48]},"packages/frontend/@n8n/chat/src/utils/streamingHandlers.ts":{"11":[48],"55":[48],"64":[48],"78":[48],"98":[48]},"packages/frontend/editor-ui/src/app/components/ConsumedTokensDetails.vue":{"7":[48],"13":[48],"24":[48]},"packages/frontend/editor-ui/src/app/components/DuplicateWorkflowDialog.vue":{"20":[48],"58":[48],"64":[48],"72":[48],"76":[48],"150":[48],"159":[48],"164":[48],"175":[48],"195":[48]},"packages/frontend/editor-ui/src/app/components/MainHeader/utils.ts":{"1":[48]},"packages/frontend/editor-ui/src/app/composables/usePushConnection/handlers/builderCreditsUpdated.ts":{"4":[48]},"packages/frontend/editor-ui/src/app/composables/usePushConnection/handlers/executionRecovered.ts":{"14":[48]},"packages/frontend/editor-ui/src/app/composables/usePushConnection/handlers/nodeDescriptionUpdated.ts":{"10":[48]},"packages/frontend/editor-ui/src/app/composables/usePushConnection/handlers/reloadNodeType.ts":{"9":[48]},"packages/frontend/editor-ui/src/app/composables/usePushConnection/handlers/removeNodeType.ts":{"10":[48],"17":[48]},"packages/frontend/editor-ui/src/app/composables/usePushConnection/handlers/sendWorkerStatusMessage.ts":{"8":[48]},"packages/frontend/editor-ui/src/app/composables/usePushConnection/handlers/testWebhookDeleted.ts":{"8":[48]},"packages/frontend/editor-ui/src/app/composables/usePushConnection/handlers/workflowActivated.ts":{"10":[48]},"packages/frontend/editor-ui/src/app/composables/usePushConnection/handlers/workflowAutoDeactivated.ts":{"10":[48]},"packages/frontend/editor-ui/src/app/composables/usePushConnection/handlers/workflowDeactivated.ts":{"9":[48]},"packages/frontend/editor-ui/src/app/composables/usePushConnection/handlers/workflowFailedToActivate.ts":{"10":[48],"46":[48]},"packages/frontend/editor-ui/src/app/composables/usePushConnection/handlers/workflowPartiallyActivated.ts":{"12":[48],"54":[48]},"packages/frontend/editor-ui/src/app/composables/usePushConnection/handlers/workflowSettingsUpdated.ts":{"8":[48]},"packages/frontend/editor-ui/src/features/ai/chatHub/components/CanvasChatFloatingMenu.vue":{"10":[48],"26":[48],"27":[48],"32":[48],"34":[48],"43":[48],"75":[48],"108":[48],"120":[48],"133":[48],"148":[48],"149":[48],"154":[48]},"packages/frontend/editor-ui/src/features/ai/chatHub/components/CanvasChatFloatingWindow.vue":{"23":[48],"28":[48],"37":[48],"46":[48],"58":[48],"59":[48],"62":[48],"66":[48],"70":[48],"78":[48],"90":[48],"92":[48],"100":[48],"105":[48],"122":[48],"125":[48],"130":[48],"140":[48]},"packages/frontend/editor-ui/src/features/ai/chatHub/components/CanvasChatHubPanel.vue":{"30":[48],"50":[48],"51":[48],"61":[48],"62":[48],"63":[48],"66":[48],"72":[48],"100":[48],"101":[48],"136":[48],"138":[48],"144":[48],"150":[48],"159":[48],"164":[48],"174":[48],"175":[48],"181":[48],"196":[48],"200":[48],"214":[48],"218":[48],"222":[48],"243":[48],"254":[48],"266":[48],"278":[48],"279":[48],"280":[48],"281":[48],"282":[48],"294":[48],"308":[48],"312":[48],"322":[48],"324":[48],"334":[48],"336":[48],"361":[48],"372":[48],"382":[48]},"packages/frontend/editor-ui/src/features/ai/chatHub/components/CanvasChatOverlay.vue":{"9":[48],"18":[48],"19":[48],"26":[48],"31":[48],"39":[48],"42":[48],"51":[48],"59":[48],"60":[48],"68":[48],"72":[48],"76":[48],"81":[48]},"packages/frontend/editor-ui/src/features/ai/chatHub/components/CanvasChatSessionDropdown.vue":{"19":[48],"32":[48],"33":[48],"40":[48],"42":[48],"51":[48],"52":[48],"54":[48],"67":[48],"72":[48],"77":[48],"78":[48],"91":[48],"99":[48],"100":[48],"103":[48],"112":[48],"117":[48],"129":[48],"131":[48],"137":[48],"150":[48],"156":[48],"161":[48],"164":[48],"167":[48],"168":[48]},"packages/frontend/editor-ui/src/features/ai/chatHub/components/ChatButtons.vue":{"11":[48],"14":[48],"27":[48],"33":[48],"40":[48],"41":[48]},"packages/frontend/editor-ui/src/features/ai/chatHub/components/ChatMarkdownChunk.vue":{"33":[48],"38":[48],"47":[48],"84":[48],"91":[48]},"packages/frontend/editor-ui/src/features/settings/communityNodes/components/nodeCreator/CommunityNodeInstallHint.vue":{"10":[48],"11":[48]},"packages/frontend/editor-ui/src/features/shared/nodeCreator/components/ItemTypes/AgentItem.vue":{"15":[48]},"packages/frontend/editor-ui/src/features/shared/nodeCreator/components/Modes/AgentsMode.vue":{"28":[48],"53":[48],"57":[48],"61":[48],"65":[48],"86":[48],"87":[48],"100":[48],"106":[48],"117":[48],"121":[48],"134":[48],"146":[48],"180":[48],"181":[48],"189":[48],"195":[48],"196":[48],"199":[48],"203":[48],"206":[48],"226":[48],"229":[48],"235":[48],"242":[48],"254":[48],"257":[48]},"packages/frontend/editor-ui/src/features/shared/nodeCreator/components/SectionHeaderCreditsTag.vue":{"7":[48],"10":[48],"22":[48],"25":[48]},"packages/frontend/editor-ui/src/features/workflow-reviews/components/WorkflowPublishChoiceDialog.vue":{"17":[48],"26":[48],"31":[48],"34":[48],"42":[48],"43":[48],"45":[48],"48":[48],"53":[48],"60":[48],"64":[48]},"packages/frontend/editor-ui/src/features/workflow-reviews/components/WorkflowReviewRequiredToggle.vue":{"9":[48],"17":[48],"20":[48],"21":[48],"26":[48],"39":[48],"41":[48],"43":[48],"46":[48],"58":[48]},"packages/frontend/editor-ui/src/features/workflow-reviews/components/WorkflowReviewSubmittedDialog.vue":{"18":[48],"25":[48],"29":[48],"36":[48],"37":[48],"41":[48],"45":[48],"50":[48],"54":[48],"55":[48]},"packages/frontend/editor-ui/src/features/workflow-reviews/components/WorkflowSubmitForReviewDialog.vue":{"28":[48],"55":[48],"59":[48],"60":[48],"66":[48],"80":[48],"81":[48],"93":[48],"98":[48],"100":[48],"103":[48],"143":[48],"155":[48],"165":[48],"177":[48],"191":[48],"202":[48],"214":[48],"222":[48]},"packages/frontend/editor-ui/src/features/workflow-reviews/components/WorkflowUpdateReviewDialog.vue":{"11":[48],"29":[48],"35":[48],"44":[48],"82":[48],"92":[48],"94":[48],"97":[48],"103":[48],"112":[48]},"packages/nodes-base/nodes/Discourse/Discourse.node.ts":{"93":[53],"107":[53]},"packages/nodes-base/nodes/Discourse/GenericFunctions.ts":{"11":[53],"40":[53]},"packages/frontend/editor-ui/src/app/components/FocusPanel.vue":{"57":[112,127],"89":[112,127],"93":[112,127],"113":[112,127],"129":[112,127],"137":[112,127],"140":[112,127],"142":[112,127],"196":[112,127],"202":[112,127],"213":[112,127],"215":[112,127],"233":[112,127],"235":[127],"241":[127],"369":[112,127],"373":[112,127],"374":[112,127],"395":[112,127],"601":[127],"604":[127]},"packages/frontend/editor-ui/src/features/workflows/canvas/experimental/components/ExperimentalFocusPanelHeader.vue":{"18":[112,127],"27":[112,127],"28":[112,127],"32":[112,127]},"packages/frontend/editor-ui/src/features/workflows/canvas/experimental/components/ExperimentalNodeDetailsDrawer.vue":{"21":[112,127],"26":[112,127],"27":[112],"33":[112,127],"36":[112,127],"37":[112],"46":[112,127],"51":[112],"53":[112],"58":[112]},"localhost-34587/assets/_plugin-vue_export-helper-Da88TEg1.js":{"2":[79]},"localhost-34587/assets/chunk-CC9Q-vWm.js":{"8":[79],"9":[79],"10":[79],"19":[79],"23":[79],"29":[79],"33":[79],"34":[79]},"packages/cli/src/middlewares/list-query/dtos/base.select.dto.ts":{"7":[160],"12":[160]},"packages/cli/src/middlewares/list-query/dtos/workflow.select.dto.ts":{"4":[160],"22":[160]},"packages/frontend/@n8n/design-system/src/components/N8nCommandBar/CommandBarItem.vue":{"6":[157,160],"17":[160],"21":[160],"27":[157,160]},"packages/frontend/@n8n/design-system/src/components/N8nTags/Tags.vue":{"21":[160,68],"37":[160,68],"45":[160],"50":[160,68],"56":[160,68]},"packages/frontend/editor-ui/src/features/shared/commandBar/components/CommandBarItemTitle.vue":{"23":[160]},"packages/frontend/editor-ui/src/features/workflows/workflowHistory/components/WorkflowHistoryVersionUnpublishModal.vue":{"15":[160],"36":[160],"42":[160],"45":[160],"49":[160],"51":[160],"58":[160],"62":[160],"70":[160],"73":[160],"76":[160]},"packages/@n8n/imap/src/errors.ts":{"5":[68],"18":[68],"24":[68]},"packages/@n8n/imap/src/helpers/get-message.ts":{"13":[68],"17":[68],"21":[68],"24":[68],"29":[68],"41":[68],"45":[68]},"packages/@n8n/imap/src/imap-simple.ts":{"12":[68],"16":[68],"21":[68],"27":[68],"35":[68],"39":[68],"57":[68],"65":[68],"66":[68],"87":[68],"94":[68],"98":[68],"104":[68],"111":[68],"122":[68],"128":[68],"134":[68],"148":[68],"154":[68],"159":[68],"169":[68],"175":[68],"176":[68],"181":[68],"182":[68],"183":[68],"188":[68],"192":[68],"193":[68],"198":[68],"202":[68],"203":[68]},"packages/@n8n/imap/src/index.ts":{"12":[68],"18":[68],"19":[68],"26":[68],"31":[68],"40":[68],"45":[68],"81":[68]},"packages/@n8n/imap/src/part-data.ts":{"8":[68],"10":[68],"14":[68],"41":[68],"47":[68],"55":[68],"59":[68],"65":[68],"72":[68],"78":[68]},"packages/@n8n/imap/src/uudecode.ts":{"9":[68]},"packages/cli/src/controllers/oauth/oauth1-credential.controller.ts":{"13":[68],"22":[68],"37":[68]},"packages/nodes-base/nodes/EmailReadImap/EmailReadImap.node.ts":{"8":[68]},"packages/nodes-base/nodes/EmailReadImap/v1/EmailReadImapV1.node.ts":{"24":[68],"222":[68],"231":[68],"271":[68],"285":[68],"290":[68],"310":[68],"320":[68],"329":[68],"344":[68],"441":[68],"500":[68],"510":[68],"529":[68],"561":[68],"585":[68],"586":[68],"617":[68],"628":[68]},"packages/nodes-base/nodes/EmailReadImap/v2/EmailReadImapV2.node.ts":{"200":[68],"209":[68],"261":[68],"291":[68],"300":[68],"320":[68],"331":[68],"334":[68],"347":[68],"365":[68],"384":[68],"430":[68],"442":[68],"448":[68],"469":[68],"472":[68],"488":[68],"507":[68],"530":[68]},"packages/nodes-base/nodes/EmailReadImap/v2/utils.ts":{"20":[68],"61":[68],"109":[68],"198":[68]},"packages/nodes-base/nodes/NextCloud/GenericFunctions.ts":{"14":[68]},"packages/nodes-base/nodes/NextCloud/NextCloud.node.ts":{"872":[68],"1103":[68],"1190":[68],"1198":[68],"1284":[68],"1292":[68],"1326":[68],"1334":[68],"1364":[68],"1372":[68],"1392":[68],"1397":[68],"1405":[68]},"packages/nodes-base/nodes/Shopify/GenericFunctions.ts":{"12":[68],"85":[68],"124":[68]},"packages/nodes-base/nodes/Shopify/ShopifyTrigger.node.ts":{"335":[68],"350":[68],"373":[68],"389":[68]},"packages/nodes-base/nodes/Twitter/Twitter.node.ts":{"8":[68]},"packages/nodes-base/nodes/Twitter/V1/GenericFunctions.ts":{"12":[68],"44":[68],"67":[68],"79":[68]},"packages/nodes-base/nodes/Twitter/V1/TwitterV1.node.ts":{"27":[68],"76":[68],"92":[68],"123":[68],"164":[68],"171":[68]},"packages/nodes-base/nodes/Twitter/V2/GenericFunctions.ts":{"13":[68],"60":[68],"83":[68],"105":[68]},"packages/nodes-base/nodes/Twitter/V2/TwitterV2.node.ts":{"31":[68],"100":[68],"116":[68]},"localhost-40825/assets/_plugin-vue_export-helper-Da88TEg1.js":{"2":[68]},"localhost-40825/assets/chunk-CC9Q-vWm.js":{"8":[68],"9":[68],"10":[68],"19":[68],"23":[68],"29":[68],"33":[68],"34":[68]},"packages/frontend/@n8n/design-system/src/components/N8nIcon/nodes/email-trigger.svg":{"11":[68]},"packages/frontend/editor-ui/src/app/components/GoBackButton.vue":{"11":[68]},"packages/frontend/editor-ui/src/app/components/NodeList.vue":{"19":[68,161],"23":[161],"27":[68,161],"31":[68,161],"36":[68,161],"42":[68,161]},"packages/frontend/editor-ui/src/features/workflows/templates/components/TemplateCard.vue":{"12":[68,161],"37":[68],"41":[161],"43":[68,161],"65":[68,161],"71":[68,161],"75":[68,161]},"packages/frontend/editor-ui/src/features/workflows/templates/components/TemplateDetails.vue":{"21":[68],"32":[68],"34":[68],"49":[68],"59":[68],"62":[68],"79":[68],"86":[68],"89":[68]},"packages/frontend/editor-ui/src/features/workflows/templates/components/TemplateList.vue":{"38":[68,161],"47":[68,161],"71":[161],"74":[68],"83":[68,161],"90":[68,161],"97":[161],"98":[68]},"packages/frontend/editor-ui/src/features/workflows/templates/setupTemplate.store.ts":{"39":[68],"56":[68],"60":[68],"80":[68],"98":[68],"115":[68],"128":[68],"149":[68],"176":[68]},"packages/frontend/editor-ui/src/features/workflows/templates/utils/templateActions.ts":{"30":[68],"72":[68],"100":[161],"128":[68,161],"132":[68,161],"137":[68,161]},"packages/frontend/editor-ui/src/features/workflows/templates/views/SetupWorkflowFromTemplateView.vue":{"22":[68],"25":[68],"26":[68],"28":[68],"49":[68],"57":[68],"79":[68],"89":[68],"109":[68],"135":[68],"143":[68],"151":[68]},"packages/frontend/editor-ui/src/features/workflows/templates/views/TemplatesCollectionView.vue":{"32":[68],"37":[68],"39":[68],"44":[68],"45":[68],"48":[68],"49":[68],"64":[68],"87":[68],"88":[68],"98":[68],"117":[68],"121":[68],"124":[68]},"localhost-41291/assets/_plugin-vue_export-helper-Da88TEg1.js":{"2":[15,86,106,110,62,130,138,157,161]},"localhost-41291/assets/chunk-CC9Q-vWm.js":{"8":[15,86,106,110,62,130,138,157,161],"9":[15,86,106,110,62,130,138,157,161],"10":[15,86,106,110,62,130,138,157,161],"19":[15,86,106,110,62,130,138,157,161],"23":[15,86,106,110,62,130,138,157,161],"29":[15,86,106,110,62,130,138,157,161],"33":[15,86,106,110,62,130,138,157,161],"34":[15,86,106,110,62,130,138,157,161]},"packages/frontend/@n8n/design-system/src/components/AskAssistantChat/messages/EventMessage.vue":{"24":[15],"40":[15],"44":[15]},"packages/frontend/editor-ui/src/features/ai/assistant/components/Chat/NewAssistantSessionModal.vue":{"14":[15],"27":[15],"31":[15],"44":[15],"56":[15],"62":[15],"65":[15],"68":[15],"72":[15]},"packages/@n8n/nodes-langchain/nodes/mcp/McpTrigger/transport/SSETransport.ts":{"10":[11],"17":[11],"22":[11]},"localhost-41291/assets/verified-L2wNNiNu.js":{"8":[62,130]},"packages/cli/src/modules/instance-registry/instance-registry.controller.ts":{"9":[130],"16":[130],"24":[130],"39":[130]},"packages/frontend/editor-ui/src/app/components/AboutModal.vue":{"16":[130],"24":[130],"28":[130],"57":[130],"68":[130],"72":[130],"75":[130],"80":[130],"83":[130],"88":[130],"91":[130],"92":[130],"98":[130],"101":[130],"102":[130],"108":[130],"111":[130],"116":[130],"119":[130],"120":[130],"127":[130]},"packages/core/src/execution-engine/node-execution-context/hook-context.ts":{"21":[161],"34":[161],"38":[161],"42":[161],"54":[161],"61":[161]},"localhost-36281/assets/_plugin-vue_export-helper-Da88TEg1.js":{"2":[4]},"localhost-36281/assets/chunk-CC9Q-vWm.js":{"8":[4],"9":[4],"10":[4],"19":[4],"23":[4],"29":[4],"33":[4],"34":[4]},"packages/frontend/@n8n/design-system/src/utils/markdown.ts":{"36":[161]},"packages/frontend/editor-ui/src/app/components/CollectionWorkflowCard.vue":{"12":[161],"13":[161],"17":[161]},"packages/frontend/editor-ui/src/app/views/WorkflowOnboardingView.vue":{"17":[161],"55":[161]},"packages/frontend/editor-ui/src/features/workflows/templates/components/TemplateFilters.vue":{"15":[161],"34":[161],"38":[161],"43":[161],"44":[161],"52":[161],"60":[161],"68":[161],"69":[161],"80":[161],"81":[161],"91":[161],"108":[161],"118":[161]},"packages/frontend/editor-ui/src/features/workflows/templates/components/TemplatesInfoCard.vue":{"21":[161],"29":[161]},"packages/frontend/editor-ui/src/features/workflows/templates/components/TemplatesInfoCarousel.vue":{"11":[161],"37":[161],"64":[161],"65":[161],"67":[161],"73":[161],"74":[161],"76":[161],"82":[161],"95":[161],"101":[161],"112":[161],"113":[161],"115":[161]},"packages/frontend/editor-ui/src/features/workflows/templates/recommendations/components/RecommendedTemplateCard.vue":{"17":[161],"35":[161],"38":[161],"43":[161],"46":[161],"71":[161],"82":[161],"101":[161],"109":[161],"121":[161],"127":[161],"137":[161],"148":[161],"177":[161],"187":[161]},"packages/frontend/editor-ui/src/features/workflows/templates/recommendations/recommendedTemplates.store.ts":{"5":[161]},"packages/frontend/editor-ui/src/features/workflows/templates/views/TemplatesSearchView.vue":{"51":[161],"54":[161],"61":[161],"65":[161],"69":[161],"73":[161],"87":[161],"105":[161],"106":[161],"110":[161],"115":[161],"125":[161],"129":[161],"137":[161],"149":[161],"156":[161],"166":[161],"173":[161],"178":[161],"192":[161],"196":[161],"206":[161],"220":[161],"258":[161],"277":[161],"285":[161],"297":[161],"310":[161],"319":[161],"330":[161],"379":[161],"386":[161],"410":[161]},"packages/frontend/editor-ui/src/features/workflows/templates/views/TemplatesWorkflowView.vue":{"43":[161],"47":[161],"49":[161],"80":[161],"90":[161],"91":[161],"102":[161],"124":[161],"148":[161],"155":[161],"168":[161],"194":[161],"200":[161]},"localhost-42751/assets/_plugin-vue_export-helper-Da88TEg1.js":{"2":[22,29,89]},"localhost-42751/assets/chunk-CC9Q-vWm.js":{"8":[22,29,89],"9":[22,29,89],"10":[22,29,89],"19":[22,29,89],"23":[22,29,89],"29":[22,29,89],"33":[22,29,89],"34":[22,29,89]},"packages/cli/src/controllers/password-reset.controller.ts":{"44":[175],"70":[175],"114":[175],"170":[175],"198":[175],"229":[175]},"packages/cli/src/middlewares/jitter.ts":{"14":[175],"18":[175],"21":[175]},"packages/cli/src/user-management/email/node-mailer.ts":{"19":[175],"48":[175],"84":[175]},"localhost-42751/assets/verified-L2wNNiNu.js":{"8":[29,89]},"packages/nodes-base/nodes/EmailSend/EmailSend.node.ts":{"8":[42]},"packages/nodes-base/nodes/EmailSend/utils.ts":{"1":[42]},"packages/nodes-base/nodes/EmailSend/v1/EmailSendV1.node.ts":{"136":[42],"143":[42]},"packages/nodes-base/nodes/EmailSend/v2/EmailSendV2.node.ts":{"88":[42],"101":[42]},"packages/nodes-base/nodes/EmailSend/v2/send.operation.ts":{"186":[42]},"packages/nodes-base/nodes/EmailSend/v2/sendAndWait.operation.ts":{"28":[42]},"packages/nodes-base/nodes/EmailSend/v2/utils.ts":{"20":[42],"54":[42]},"localhost-37679/assets/_plugin-vue_export-helper-Da88TEg1.js":{"2":[42]},"localhost-37679/assets/chunk-CC9Q-vWm.js":{"8":[42],"9":[42],"10":[42],"19":[42],"23":[42],"29":[42],"33":[42],"34":[42]},"packages/@n8n/scheduler/src/core/reaper/reap.ts":{"97":[167]},"packages/cli/src/workflows/publication/workflow-publication-reconciler.service.ts":{"54":[167],"72":[167],"89":[167],"95":[167],"103":[167],"109":[167],"120":[167],"125":[167],"167":[167],"173":[167],"178":[167],"181":[167],"201":[167],"209":[167],"216":[167],"229":[167]},"packages/cli/src/modules/external-secrets.ee/external-secrets-provider-connection-manager.ee.ts":{"15":[179,58],"24":[179,58],"40":[179,58],"57":[179,58],"60":[179,58],"64":[179,58],"73":[179,58],"101":[179,58],"131":[179,58],"149":[179,58],"167":[179,58],"187":[179,58]},"packages/cli/src/modules/external-secrets.ee/external-secrets.controller.ee.ts":{"13":[179],"22":[179],"34":[179],"40":[179],"47":[179],"60":[179],"68":[179],"76":[179],"90":[179]},"packages/cli/src/modules/external-secrets.ee/external-secrets.service.ee.ts":{"10":[179],"15":[179],"30":[179],"31":[179],"44":[179],"50":[179],"54":[179],"61":[179],"66":[179],"70":[179],"77":[179]},"packages/cli/src/modules/external-secrets.ee/provider-lifecycle.service.ts":{"25":[179,58],"36":[179,58],"78":[179,58],"112":[179,58],"127":[179,58]},"packages/cli/src/modules/external-secrets.ee/redaction.service.ee.ts":{"15":[179],"19":[179],"20":[179],"42":[179],"53":[179],"62":[179]},"packages/cli/src/modules/external-secrets.ee/retry-manager.service.ts":{"27":[179,58],"30":[179,58],"31":[179,58],"42":[179,58],"62":[179,58],"70":[179,58],"97":[179,58],"112":[179,58],"123":[179,58],"130":[179,58]},"packages/cli/src/modules/external-secrets.ee/secrets-cache.service.ts":{"16":[179,58],"26":[179,58],"30":[179,58],"39":[179,58],"55":[179,58],"57":[179,58],"58":[179,58],"79":[179,58],"87":[179,58],"95":[179,58],"103":[179,58]},"packages/cli/src/modules/external-secrets.ee/settings-store.service.ts":{"18":[179],"26":[179],"38":[179],"54":[179],"61":[179],"80":[179],"108":[179],"116":[179],"127":[179],"142":[179]},"packages/cli/src/modules/external-secrets.ee/errors/secrets-provider-errors.ts":{"24":[58],"59":[58],"79":[58],"96":[58]},"packages/cli/src/modules/external-secrets.ee/secret-provider-access-check.service.ee.ts":{"17":[58],"24":[58],"34":[58],"77":[58],"98":[58],"100":[58]},"localhost-37257/assets/_plugin-vue_export-helper-Da88TEg1.js":{"2":[58]},"localhost-37257/assets/chunk-CC9Q-vWm.js":{"8":[58],"9":[58],"10":[58],"19":[58],"23":[58],"29":[58],"33":[58],"34":[58]},"packages/frontend/editor-ui/src/features/integrations/externalSecrets.ee/assets/images/aws-secrets-manager.svg":{"8":[58]},"packages/frontend/editor-ui/src/features/integrations/secretsProviders.ee/components/DeleteSecretsProviderModal.ee.vue":{"28":[58],"41":[58],"43":[58],"47":[58],"54":[58],"64":[58],"74":[58],"90":[58],"128":[58],"142":[58],"145":[58],"157":[58],"162":[58],"163":[58],"179":[58],"190":[58],"195":[58],"198":[58],"206":[58]},"packages/frontend/editor-ui/src/features/integrations/secretsProviders.ee/components/SecretsProviderConnectionCard.ee.vue":{"49":[58],"57":[58],"61":[58],"63":[58],"74":[58],"85":[58],"87":[58],"95":[58],"115":[58],"156":[58],"167":[58],"180":[58],"183":[58],"205":[58],"231":[58],"237":[58]},"packages/frontend/editor-ui/src/features/integrations/secretsProviders.ee/components/SecretsProviderConnectionModal.ee.vue":{"43":[58],"75":[58],"76":[58],"77":[58],"78":[58],"92":[58],"93":[58],"103":[58],"120":[58],"130":[58],"146":[58],"150":[58],"151":[58],"157":[58],"166":[58],"171":[58],"175":[58],"179":[58],"183":[58],"208":[58],"228":[58],"235":[58],"248":[58],"264":[58],"270":[58],"303":[58],"308":[58],"313":[58],"344":[58],"418":[58],"439":[58],"441":[58],"451":[58],"460":[58],"477":[58],"481":[58],"490":[58],"502":[58],"505":[58],"510":[58]},"packages/frontend/editor-ui/src/features/integrations/secretsProviders.ee/components/SecretsProviderImage.ee.vue":{"17":[58]},"packages/frontend/editor-ui/src/features/integrations/secretsProviders.ee/components/SecretsProvidersEmptyState.ee.vue":{"8":[58],"16":[58],"29":[58],"31":[58],"45":[58],"48":[58],"56":[58],"62":[58],"64":[58],"73":[58],"95":[58],"101":[58],"108":[58],"112":[58]},"packages/frontend/editor-ui/src/features/integrations/secretsProviders.ee/composables/useConnectionModal.ee.ts":{"31":[58],"35":[58],"60":[58],"66":[58],"92":[58],"118":[58],"138":[58],"142":[58],"151":[58],"159":[58],"164":[58],"166":[58],"167":[58],"173":[58],"174":[58],"181":[58],"187":[58],"194":[58],"199":[58],"200":[58],"207":[58],"208":[58],"212":[58],"213":[58],"215":[58],"225":[58],"233":[58],"239":[58],"257":[58],"276":[58],"281":[58],"283":[58],"291":[58],"298":[58],"306":[58],"307":[58],"357":[58],"401":[58],"442":[58],"450":[58],"478":[58],"516":[58]},"packages/frontend/editor-ui/src/features/integrations/secretsProviders.ee/composables/useSecretsProviderConnection.ee.ts":{"34":[58],"39":[58],"62":[58],"79":[58],"100":[58]},"packages/frontend/editor-ui/src/features/integrations/secretsProviders.ee/views/SettingsSecretsProviders.ee.vue":{"47":[58],"49":[58],"52":[58],"97":[58],"101":[58],"105":[58],"137":[58],"138":[58],"141":[58],"154":[58],"193":[58],"194":[58],"205":[58],"212":[58],"237":[58],"243":[58],"251":[58],"266":[58],"269":[58],"285":[58],"298":[58],"310":[58]},"localhost-43685/assets/_plugin-vue_export-helper-Da88TEg1.js":{"2":[63,127]},"localhost-43685/assets/chunk-CC9Q-vWm.js":{"8":[63,127],"9":[63,127],"10":[63,127],"19":[63,127],"23":[63,127],"29":[63,127],"33":[63,127],"34":[63,127]},"localhost-43685/assets/verified-L2wNNiNu.js":{"8":[63]}}} \ No newline at end of file +{"specs":["tests/e2e/ai/ai-2505-pdf-embed.spec.ts","tests/e2e/ai/langchain-agents.spec.ts","tests/e2e/ai/langchain-vectorstores.spec.ts","tests/e2e/chat-hub/chat-hub-attachment.spec.ts","tests/e2e/chat-hub/chat-hub-basic.spec.ts","tests/e2e/chat-hub/chat-hub-chat-user.spec.ts","tests/e2e/workflows/editor/execution/logs.spec.ts","tests/e2e/ai/langchain-chains.spec.ts","tests/e2e/credentials/crud.spec.ts","tests/e2e/dynamic-credentials/mcp-trigger-credential-gate.spec.ts","tests/e2e/mcp-registry/mcp-registry.spec.ts","tests/e2e/nodes/mcp-trigger.spec.ts","tests/e2e/regression/AI-1401-sub-nodes-input-panel.spec.ts","tests/e2e/workflows/editor/ndv/ndv-floating-nodes.spec.ts","tests/e2e/workflows/editor/ndv/ndv-parameters.spec.ts","tests/e2e/ai/assistant-basic.spec.ts","tests/e2e/ai/assistant-credential-help.spec.ts","tests/e2e/ai/assistant-support-chat.spec.ts","tests/e2e/ai/builder-setup-wizard.spec.ts","tests/e2e/ai/chat-session.spec.ts","tests/e2e/ai/workflow-builder.spec.ts","tests/e2e/api/binary-data-xss.spec.ts","tests/e2e/api/waiting-endpoint-security.spec.ts","tests/e2e/api/workflow-publication-service.spec.ts","tests/e2e/app-config/security-notifications.spec.ts","tests/e2e/auth/oidc.spec.ts","tests/e2e/building-blocks/canvas-actions.spec.ts","tests/e2e/building-blocks/credential-mismatch.spec.ts","tests/e2e/building-blocks/credentials.spec.ts","tests/e2e/building-blocks/workflow-entry-points.spec.ts","tests/e2e/capabilities/proxy-server.spec.ts","tests/e2e/capabilities/task-runner.spec.ts","tests/e2e/chat-hub/chat-hub-workflow-agent.spec.ts","tests/e2e/credentials/global.spec.ts","tests/e2e/data-tables/details.spec.ts","tests/e2e/data-tables/tables.spec.ts","tests/e2e/instance-ai/instance-ai-artifacts.spec.ts","tests/e2e/instance-ai/instance-ai-confirmations.spec.ts","tests/e2e/instance-ai/instance-ai-remediation-guard.spec.ts","tests/e2e/instance-ai/instance-ai-workflow-execution.spec.ts","tests/e2e/instance-ai/instance-ai-workflow-preview.spec.ts","tests/e2e/journeys/admin-views-executions-list.spec.ts","tests/e2e/mcp/mcp-service.spec.ts","tests/e2e/node-creator/actions.spec.ts","tests/e2e/node-creator/special-nodes.spec.ts","tests/e2e/node-creator/vector-stores.spec.ts","tests/e2e/node-creator/workflows.spec.ts","tests/e2e/nodes/email-send-node.spec.ts","tests/e2e/nodes/if-node.spec.ts","tests/e2e/nodes/kafka-nodes.spec.ts","tests/e2e/nodes/send-and-wait.spec.ts","tests/e2e/nodes/webhook.spec.ts","tests/e2e/projects/folders-advanced.spec.ts","tests/e2e/projects/folders-operations.spec.ts","tests/e2e/projects/projects-move-resources.spec.ts","tests/e2e/projects/projects.spec.ts","tests/e2e/redaction-enforcement/redaction-enforcement.spec.ts","tests/e2e/regression/AI-812-partial-execs-broken-when-using-chat-trigger.spec.ts","tests/e2e/regression/CAT-726-canvas-node-connectors-not-rendered-when-nodes-inserted.spec.ts","tests/e2e/scheduling/schedule-trigger-timezone.spec.ts","tests/e2e/settings/external-secrets/secret-providers-connections-ui.spec.ts","tests/e2e/settings/log-streaming/log-streaming-delivery.spec.ts","tests/e2e/settings/log-streaming/log-streaming-observability.spec.ts","tests/e2e/settings/log-streaming/log-streaming.spec.ts","tests/e2e/sharing/credential-sharing.spec.ts","tests/e2e/workflows/editor/canvas/actions.spec.ts","tests/e2e/workflows/editor/canvas/node-groups.spec.ts","tests/e2e/workflows/editor/code/code-node.spec.ts","tests/e2e/workflows/editor/execution/execution.spec.ts","tests/e2e/workflows/editor/execution/executions-list-infinite-scroll.spec.ts","tests/e2e/workflows/editor/ndv/paired-item.spec.ts","tests/e2e/workflows/editor/routing.spec.ts","tests/e2e/workflows/editor/viewer-permissions.spec.ts","tests/e2e/workflows/templates/credentials-setup.spec.ts","tests/e2e/ai/rag-callout.spec.ts","tests/e2e/app-config/versions.spec.ts","tests/e2e/auth/signin.spec.ts","tests/e2e/cloud/cloud.spec.ts","tests/e2e/instance-ai/instance-ai-attachments.spec.ts","tests/e2e/instance-ai/instance-ai-background-lifecycle.spec.ts","tests/e2e/instance-ai/instance-ai-chat-basics.spec.ts","tests/e2e/instance-ai/instance-ai-sidebar.spec.ts","tests/e2e/instance-ai/thread-launcher.spec.ts","tests/e2e/regression/ADO-4462-template-setup-experiment.spec.ts","tests/e2e/sentry/sentry-baseline.spec.ts","tests/e2e/settings/environments/source-control.spec.ts","tests/e2e/settings/personal/two-factor-authentication.spec.ts","tests/e2e/ai/assistant-code-help.spec.ts","tests/e2e/ai/eval-collections-compare.spec.ts","tests/e2e/api/wait-form-resume.spec.ts","tests/e2e/app-config/demo-reimport.spec.ts","tests/e2e/app-config/demo.spec.ts","tests/e2e/auth/admin-smoke.spec.ts","tests/e2e/auth/authenticated.spec.ts","tests/e2e/building-blocks/node-details-configuration.spec.ts","tests/e2e/building-blocks/user-service.spec.ts","tests/e2e/chat-hub/chat-hub-personal-agent.spec.ts","tests/e2e/chat-hub/chat-hub-settings.spec.ts","tests/e2e/chat-hub/chat-hub-tools.spec.ts","tests/e2e/credentials/oauth.spec.ts","tests/e2e/instance-ai/instance-ai-out-of-credits.spec.ts","tests/e2e/node-creator/navigation.spec.ts","tests/e2e/nodes/community-nodes.spec.ts","tests/e2e/nodes/form-trigger-node.spec.ts","tests/e2e/nodes/http-request-node.spec.ts","tests/e2e/nodes/pdf-node.spec.ts","tests/e2e/nodes/schedule-trigger-node.spec.ts","tests/e2e/projects/folders-basic.spec.ts","tests/e2e/projects/project-settings.spec.ts","tests/e2e/regression/ADO-1338-ndv-missing-input-panel.spec.ts","tests/e2e/regression/ADO-2230-ndv-reset-data-pagination.spec.ts","tests/e2e/regression/ADO-2372-prevent-clipping-params.spec.ts","tests/e2e/regression/ADO-2929-can-load-old-switch-node-workflows.spec.ts","tests/e2e/regression/AI-716-correctly-set-up-agent-model-shows-error.spec.ts","tests/e2e/regression/CAT-2395-node-spacing.spec.ts","tests/e2e/regression/PAY-4367-node-shifting-cyclic.spec.ts","tests/e2e/regression/SUG-121-fields-reset-after-closing-ndv.spec.ts","tests/e2e/regression/SUG-38-inline-expression-preview.spec.ts","tests/e2e/settings/community-nodes/community-nodes.spec.ts","tests/e2e/settings/environments/variables.spec.ts","tests/e2e/settings/log-streaming/log-streaming-ui-e2e.spec.ts","tests/e2e/settings/personal/personal.spec.ts","tests/e2e/settings/users/users.spec.ts","tests/e2e/settings/workers/workers.spec.ts","tests/e2e/sharing/access-control.spec.ts","tests/e2e/sharing/credential-visibility.spec.ts","tests/e2e/sharing/workflow-sharing.spec.ts","tests/e2e/workflows/checklist/production-checklist.spec.ts","tests/e2e/workflows/demo-diff.spec.ts","tests/e2e/workflows/demo-executable-chat-trigger.spec.ts","tests/e2e/workflows/editor/canvas/canvas-nodes.spec.ts","tests/e2e/workflows/editor/canvas/canvas-zoom.spec.ts","tests/e2e/workflows/editor/canvas/focus-panel.spec.ts","tests/e2e/workflows/editor/canvas/stickies.spec.ts","tests/e2e/workflows/editor/canvas/undo-redo.spec.ts","tests/e2e/workflows/editor/code/editors.spec.ts","tests/e2e/workflows/editor/editor-after-route-changes.spec.ts","tests/e2e/workflows/editor/execution/debug.spec.ts","tests/e2e/workflows/editor/execution/inject-previous.spec.ts","tests/e2e/workflows/editor/execution/partial.spec.ts","tests/e2e/workflows/editor/expressions/inline.spec.ts","tests/e2e/workflows/editor/expressions/mapping.spec.ts","tests/e2e/workflows/editor/expressions/modal.spec.ts","tests/e2e/workflows/editor/expressions/transformation.spec.ts","tests/e2e/workflows/editor/ndv/io-filter.spec.ts","tests/e2e/workflows/editor/ndv/ndv-core.spec.ts","tests/e2e/workflows/editor/ndv/ndv-data-display.spec.ts","tests/e2e/workflows/editor/ndv/pinning.spec.ts","tests/e2e/workflows/editor/ndv/resource-locator.spec.ts","tests/e2e/workflows/editor/ndv/resource-mapper.spec.ts","tests/e2e/workflows/editor/ndv/schema-preview.spec.ts","tests/e2e/workflows/editor/subworkflows/debugging.spec.ts","tests/e2e/workflows/editor/subworkflows/extraction.spec.ts","tests/e2e/workflows/editor/subworkflows/workflow-selector.spec.ts","tests/e2e/workflows/editor/tags.spec.ts","tests/e2e/workflows/editor/workflow-actions/archive.spec.ts","tests/e2e/workflows/editor/workflow-actions/publish.spec.ts","tests/e2e/workflows/executions/filter.spec.ts","tests/e2e/workflows/executions/list.spec.ts","tests/e2e/workflows/executions/preview-isolation.spec.ts","tests/e2e/workflows/list/import.spec.ts","tests/e2e/workflows/list/workflows.spec.ts","tests/e2e/workflows/templates/templates.spec.ts","tests/e2e/api/form-endpoint-isolation.spec.ts","tests/e2e/api/webhook-external.spec.ts","tests/e2e/dynamic-credentials/execution-status.spec.ts","tests/e2e/scheduling/poll-trigger-durable.spec.ts","tests/e2e/scheduling/poll-trigger-legacy.spec.ts","tests/e2e/scheduling/poll-trigger-misfire-skip.spec.ts","tests/e2e/scheduling/schedule-trigger-durable.spec.ts","tests/e2e/scheduling/schedule-trigger-misfire-coalesce.spec.ts","tests/e2e/dynamic-credentials/resolver-deletion.spec.ts","tests/e2e/mcp/mcp-oauth.spec.ts","tests/e2e/credentials/api-operations.spec.ts","tests/e2e/app-config/env-feature-flags.spec.ts","tests/e2e/auth/password-reset.spec.ts","tests/e2e/auth/token-exchange.spec.ts","tests/e2e/instance-ai/instance-ai-multimain.spec.ts","tests/e2e/scheduling/schedule-trigger-multimain.spec.ts","tests/e2e/settings/external-secrets/aws-secrets-manager.spec.ts","tests/e2e/api/webhook-isolation.spec.ts","tests/e2e/workflows/editor/execution/subworkflow-stop.spec.ts","tests/e2e/dynamic-credentials/external-user-trigger.spec.ts","tests/e2e/nodes/n8n-trigger.spec.ts","tests/e2e/workflows/editor/subworkflows/subworkflow-version-resolution.spec.ts","tests/e2e/scheduling/schedule-trigger-legacy.spec.ts","tests/e2e/api/discovery.spec.ts","tests/e2e/workflows/editor/subworkflows/wait.spec.ts","tests/e2e/nodes/code-node-api.spec.ts"],"files":{"packages/@n8n/ai-utilities/src/guards.ts":{"7":[0,1,2,3,4,5,6],"9":[0,1,2,3,4,5,6],"17":[0,1,2,3,4,5,6],"21":[0,1,2,3,4,5,6],"25":[0,1,2,3,4,5,6],"31":[0,1,2,3,4,5,6]},"packages/@n8n/ai-utilities/src/index.ts":{"7":[0,1,7,2,3,4,5,8,9,10,11,12,6,13,14],"12":[0,1,7,2,3,4,5,8,9,10,11,12,6,13,14],"13":[0,1,7,2,3,4,5,8,9,10,11,12,6,13,14],"14":[0,1,7,2,3,4,5,8,9,10,11,12,6,13,14],"16":[0,1,7,2,3,4,5,8,9,10,11,12,6,13,14],"51":[0,1,7,2,3,4,5,8,9,10,11,12,6,13,14],"81":[0,1,7,2,3,4,5,8,9,10,11,12,6,13,14],"89":[0,1,7,2,3,4,5,8,9,10,11,12,6,13,14],"90":[0,1,7,2,3,4,5,8,9,10,11,12,6,13,14],"119":[0,1,7,2,3,4,5,8,9,10,11,12,6,13,14],"120":[0,1,7,2,3,4,5,8,9,10,11,12,6,13,14],"124":[0,1,7,2,3,4,5,8,9,10,11,12,6,13,14]},"packages/@n8n/ai-utilities/src/utils/embeddings-input-validation.ts":{"13":[0,2],"32":[0,2],"40":[0,2]},"packages/@n8n/ai-utilities/src/utils/helpers.ts":{"3":[0,1,2,3,4,5,6],"17":[0,1,2,3,4,5,6],"37":[0,1,2,3,4,5,6]},"packages/@n8n/ai-utilities/src/utils/log-ai-event.ts":{"4":[0,1,2,3,4,5,6]},"packages/@n8n/ai-utilities/src/utils/log-wrapper.ts":{"38":[0,2,3,4,5],"80":[0,2,3,4,5],"109":[0,2,3,4,5],"127":[0,2,3,4,5],"132":[0,2,3,4,5],"155":[0,2,3,4,5],"184":[0,2,3,4,5],"205":[0,2,3,4,5],"227":[0,2,3,4,5],"271":[0,2,3,4,5],"298":[0,2,3,4,5],"325":[0,2,3,4,5],"355":[0,2,3,4,5],"374":[0,2,3,4,5],"398":[0,2,3,4,5],"422":[0,2,3,4,5],"456":[0,2,3,4,5]},"packages/@n8n/ai-utilities/src/utils/n8n-json-loader.ts":{"15":[2],"21":[2],"35":[2],"45":[2],"81":[2]},"packages/@n8n/ai-utilities/src/utils/shared-fields.ts":{"37":[0,1,7,2,3,4,5,8,9,10,11,12,6,13,14],"46":[0,1,7,2,3,4,5,8,9,10,11,12,6,13,14],"110":[0,1,7,2,3,4,5,8,9,10,11,12,6,13,14],"115":[0,1,7,2,3,4,5,8,9,10,11,12,6,13,14],"120":[0,1,7,2,3,4,5,8,9,10,11,12,6,13,14],"125":[0,1,7,2,3,4,5,8,9,10,11,12,6,13,14],"132":[0,1,7,2,3,4,5,8,9,10,11,12,6,13,14],"158":[0,1,7,2,3,4,5,8,9,10,11,12,6,13,14],"164":[0,1,7,2,3,4,5,8,9,10,11,12,6,13,14]},"packages/@n8n/ai-utilities/src/utils/vector-store/MemoryManager/MemoryCalculator.ts":{"21":[0,2],"61":[0,2]},"packages/@n8n/ai-utilities/src/utils/vector-store/MemoryManager/MemoryVectorStoreManager.ts":{"40":[0,2],"69":[0,2],"80":[0,2],"91":[0,2],"106":[0,2],"114":[0,2],"131":[0,2],"138":[0,2],"163":[0,2],"175":[0,2],"182":[0,2],"190":[0,2],"211":[0,2],"268":[0,2]},"packages/@n8n/ai-utilities/src/utils/vector-store/MemoryManager/StoreCleanupService.ts":{"16":[0,2],"31":[0,2],"45":[0,2],"79":[0,2],"117":[0,2],"120":[0,2],"153":[0,2]},"packages/@n8n/ai-utilities/src/utils/vector-store/MemoryManager/config.ts":{"10":[0,2],"38":[0,2],"47":[0,2]},"packages/@n8n/ai-utilities/src/utils/vector-store/createVectorStoreNode/createVectorStoreNode.ts":{"50":[0,2,14],"53":[0,2,14],"417":[0,2,14],"479":[0,2,14]},"packages/@n8n/ai-utilities/src/utils/vector-store/createVectorStoreNode/operations/insertOperation.ts":{"17":[0,2]},"packages/@n8n/ai-utilities/src/utils/vector-store/createVectorStoreNode/utils.ts":{"12":[0,2,14],"16":[0,2,14],"26":[0,2,14],"36":[0,2,14],"41":[0,2,14],"47":[0,2,14]},"packages/@n8n/ai-utilities/src/utils/vector-store/processDocuments.ts":{"7":[0,2],"19":[0,2],"28":[0,2],"41":[0,2]},"packages/@n8n/api-types/src/dto/index.ts":{"1":"b:R6f/////////AwAAAAAAAAAAAAAAAAAA","3":"b:R6f/////////AwAAAAAAAAAAAAAAAAAA","4":"b:R6f/////////AwAAAAAAAAAAAAAAAAAA","12":"b:R6f/////////AwAAAAAAAAAAAAAAAAAA","16":"b:R6f/////////AwAAAAAAAAAAAAAAAAAA","36":"b:R6f/////////AwAAAAAAAAAAAAAAAAAA","37":"b:R6f/////////AwAAAAAAAAAAAAAAAAAA","40":"b:R6f/////////AwAAAAAAAAAAAAAAAAAA","44":"b:R6f/////////AwAAAAAAAAAAAAAAAAAA","51":"b:R6f/////////AwAAAAAAAAAAAAAAAAAA","53":"b:R6f/////////AwAAAAAAAAAAAAAAAAAA","55":"b:R6f/////////AwAAAAAAAAAAAAAAAAAA","59":"b:R6f/////////AwAAAAAAAAAAAAAAAAAA","61":"b:R6f/////////AwAAAAAAAAAAAAAAAAAA","64":"b:R6f/////////AwAAAAAAAAAAAAAAAAAA","66":"b:R6f/////////AwAAAAAAAAAAAAAAAAAA","72":"b:R6f/////////AwAAAAAAAAAAAAAAAAAA","73":"b:R6f/////////AwAAAAAAAAAAAAAAAAAA","89":"b:R6f/////////AwAAAAAAAAAAAAAAAAAA","92":"b:R6f/////////AwAAAAAAAAAAAAAAAAAA","98":"b:R6f/////////AwAAAAAAAAAAAAAAAAAA","107":"b:R6f/////////AwAAAAAAAAAAAAAAAAAA","108":"b:R6f/////////AwAAAAAAAAAAAAAAAAAA","110":"b:R6f/////////AwAAAAAAAAAAAAAAAAAA","118":"b:R6f/////////AwAAAAAAAAAAAAAAAAAA","119":"b:R6f/////////AwAAAAAAAAAAAAAAAAAA","127":"b:R6f/////////AwAAAAAAAAAAAAAAAAAA","128":"b:R6f/////////AwAAAAAAAAAAAAAAAAAA","129":"b:R6f/////////AwAAAAAAAAAAAAAAAAAA","131":"b:R6f/////////AwAAAAAAAAAAAAAAAAAA","133":"b:R6f/////////AwAAAAAAAAAAAAAAAAAA","137":"b:R6f/////////AwAAAAAAAAAAAAAAAAAA","138":"b:R6f/////////AwAAAAAAAAAAAAAAAAAA","151":"b:R6f/////////AwAAAAAAAAAAAAAAAAAA","157":"b:R6f/////////AwAAAAAAAAAAAAAAAAAA","164":"b:R6f/////////AwAAAAAAAAAAAAAAAAAA","165":"b:R6f/////////AwAAAAAAAAAAAAAAAAAA","180":"b:R6f/////////AwAAAAAAAAAAAAAAAAAA","181":"b:R6f/////////AwAAAAAAAAAAAAAAAAAA","211":"b:R6f/////////AwAAAAAAAAAAAAAAAAAA","216":"b:R6f/////////AwAAAAAAAAAAAAAAAAAA","218":"b:R6f/////////AwAAAAAAAAAAAAAAAAAA","228":"b:R6f/////////AwAAAAAAAAAAAAAAAAAA","231":"b:R6f/////////AwAAAAAAAAAAAAAAAAAA","232":"b:R6f/////////AwAAAAAAAAAAAAAAAAAA","238":"b:R6f/////////AwAAAAAAAAAAAAAAAAAA","272":"b:R6f/////////AwAAAAAAAAAAAAAAAAAA","284":"b:R6f/////////AwAAAAAAAAAAAAAAAAAA","285":"b:R6f/////////AwAAAAAAAAAAAAAAAAAA","287":"b:R6f/////////AwAAAAAAAAAAAAAAAAAA","288":"b:R6f/////////AwAAAAAAAAAAAAAAAAAA","297":"b:R6f/////////AwAAAAAAAAAAAAAAAAAA","298":"b:R6f/////////AwAAAAAAAAAAAAAAAAAA","311":"b:R6f/////////AwAAAAAAAAAAAAAAAAAA","312":"b:R6f/////////AwAAAAAAAAAAAAAAAAAA","316":"b:R6f/////////AwAAAAAAAAAAAAAAAAAA"},"packages/@n8n/api-types/src/dto/pagination/pagination.dto.ts":{"10":"b:R4Re3lOo1hEo/38AAAAAAAAAAAAAAAAA","11":"b:R4Re3lOo1hEo/38AAAAAAAAAAAAAAAAA","14":"b:R4Re3lOo1hEo/38AAAAAAAAAAAAAAAAA","18":"b://Vf///5/7f///////////////8HAAAA","22":"b:R4Re3lOo1hEo/38AAAAAAAAAAAAAAAAA","23":"b:R4Re3lOo1hEo/38AAAAAAAAAAAAAAAAA","27":"b:R4Re3lOo1hEo/38AAAAAAAAAAAAAAAAA","35":"b:R4Re3lOo1hEo/38AAAAAAAAAAAAAAAAA","47":"b:R4Re3lOo1hEo/38AAAAAAAAAAAAAAAAA","48":"b:R4Re3lOo1hEo/38AAAAAAAAAAAAAAAAA","51":"b:R4Re3lOo1hEo/38AAAAAAAAAAAAAAAAA","55":"b://Vf///5/7f///////////////8HAAAA","59":"b:R4Re3lOo1hEo/38AAAAAAAAAAAAAAAAA","60":"b:R4Re3lOo1hEo/38AAAAAAAAAAAAAAAAA","63":"b:R4Re3lOo1hEo/38AAAAAAAAAAAAAAAAA","66":"b:R4Re3lOo1hEo/38AAAAAAAAAAAAAAAAA"},"packages/@n8n/api-types/src/dto/user/users-list-filter.dto.ts":{"51":"b:R4Re3lOo1hEo/38AAAAAAAAAAAAAAAAA"},"packages/@n8n/api-types/src/dto/workflows/base-workflow.dto.ts":{"26":"b:R4f+7KP+x2moAwACAAAAAAAAAOD4BwAA","31":"b:R4f+7KP+x2moAwACAAAAAAAAAOD4BwAA","42":"b:R4f+7KP+x2moAwACAAAAAAAAAOD4BwAA","54":"b:R4f+7KP+x2moAwACAAAAAAAAAOD4BwAA","55":"b:R4f+7KP+x2moAwACAAAAAAAAAOD4BwAA","69":"b:R4f+7KP+x2moAwACAAAAAAAAAOD4BwAA","81":"b:R4f+7KP+x2moAwACAAAAAAAAAOD4BwAA","90":"b:R4f+7KP+x2moAwACAAAAAAAAAOD4BwAA","141":"b:R4f+7KP+x2moAwACAAAAAAAAAOD4BwAA","144":"b:R4f+7KP+x2moAwACAAAAAAAAAOD4BwAA"},"packages/@n8n/api-types/src/dto/workflows/manual-run.dto.ts":{"5":"b:R4BqwIKDDid6AAAAAAAAAAAAAAAAAAAA","64":"b:R4BqwIKDDid6AAAAAAAAAAAAAAAAAAAA","69":"b:R4BqwIKDDid6AAAAAAAAAAAAAAAAAAAA","86":"b:R4BqwIKDDid6AAAAAAAAAAAAAAAAAAAA"},"packages/@n8n/api-types/src/index.ts":{"95":"b:f4Ve3vP/1xEp/38gFBAARAAAAAAgGAAA","116":"b:f4Ve3vP/1xEp/38gFBAARAAAAAAgGAAA","124":"b:f4Ve3vP/1xEp/38gFBAARAAAAAAgGAAA","254":"b:f4Ve3vP/1xEp/38gFBAARAAAAAAgGAAA","628":"b:f4Ve3vP/1xEp/38gFBAARAAAAAAgGAAA","639":"b:f4Ve3vP/1xEp/38gFBAARAAAAAAgGAAA"},"packages/@n8n/api-types/src/schemas/boolean-from-string.ts":{"3":"b:f4Re3lKg1jko/38AAAAAAQAAAAAAMAAA"},"packages/@n8n/api-types/src/zod-class.ts":{"32":"b://Xf///9///////////////////v9w8A","37":"b://Vf///5/7f/+/////////////8HAAAA","39":"b:f4Xf918k9nmp738MAAAAAAAAAADo9w8A","44":"b:f4Xf918k9nmp738MAAAAAAAAAADo9w8A","48":"b:f4Xf918k9nmp738MAAAAAAAAAADo9w8A","52":"b:f4Xf918k9nmp738MAAAAAAAAAADo9w8A"},"packages/@n8n/backend-common/src/index.ts":{"5":"b:f4Dv+pKF3z/4G3AAAAICAAIAAAAgIDAA","6":"b:f4Dv+pKF3z/4G3AAAAICAAIAAAAgIDAA","7":"b:f4Dv+pKF3z/4G3AAAAICAAIAAAAgIDAA","9":"b:f4Dv+pKF3z/4G3AAAAICAAIAAAAgIDAA","20":"b:f4Dv+pKF3z/4G3AAAAICAAIAAAAgIDAA"},"packages/@n8n/backend-common/src/license-state.ts":{"9":"b:f4X+/9v//nmo/38giRiABQAAAALoPwEA","15":"b:f4X+/9v//nmo/38giRiABQAAAALoPwEA","18":"b:f4X+/9v//nmo/38giRiABQAAAALoPwEA","22":"b:f4X+/9v//nmo/38giRiABQAAAALoPwEA","33":"b:f4X+/9v//nmo/38giRiABQAAAALoPwEA","47":"b:f4X+/9v//nmo/38giRiABQAAAALoPwEA","57":"b:f4X+/9v//nmo/38giRiABQAAAALoPwEA","61":"b:f4X+/9v//nmo/38giRiABQAAAALoPwEA","65":"b:f4X+/9v//nmo/38giRiABQAAAALoPwEA","69":"b:f4X+/9v//nmo/38giRiABQAAAALoPwEA","73":"b:f4X+/9v//nmo/38giRiABQAAAALoPwEA","77":"b:f4X+/9v//nmo/38giRiABQAAAALoPwEA","81":"b:f4X+/9v//nmo/38giRiABQAAAALoPwEA","85":"b:f4X+/9v//nmo/38giRiABQAAAALoPwEA","89":"b:f4X+/9v//nmo/38giRiABQAAAALoPwEA","93":"b:f4X+/9v//nmo/38giRiABQAAAALoPwEA","97":"b:f4X+/9v//nmo/38giRiABQAAAALoPwEA","101":"b:f4X+/9v//nmo/38giRiABQAAAALoPwEA","105":"b:f4X+/9v//nmo/38giRiABQAAAALoPwEA","109":"b:f4X+/9v//nmo/38giRiABQAAAALoPwEA","113":"b:f4X+/9v//nmo/38giRiABQAAAALoPwEA","117":"b:f4X+/9v//nmo/38giRiABQAAAALoPwEA","121":"b:f4X+/9v//nmo/38giRiABQAAAALoPwEA","125":"b:f4X+/9v//nmo/38giRiABQAAAALoPwEA","129":"b:f4X+/9v//nmo/38giRiABQAAAALoPwEA","133":"b:f4X+/9v//nmo/38giRiABQAAAALoPwEA","137":"b:f4X+/9v//nmo/38giRiABQAAAALoPwEA","141":"b:f4X+/9v//nmo/38giRiABQAAAALoPwEA","145":"b:f4X+/9v//nmo/38giRiABQAAAALoPwEA","149":"b:f4X+/9v//nmo/38giRiABQAAAALoPwEA","153":"b:f4X+/9v//nmo/38giRiABQAAAALoPwEA","157":"b:f4X+/9v//nmo/38giRiABQAAAALoPwEA","161":"b:f4X+/9v//nmo/38giRiABQAAAALoPwEA","165":"b:f4X+/9v//nmo/38giRiABQAAAALoPwEA","169":"b:f4X+/9v//nmo/38giRiABQAAAALoPwEA","173":"b:f4X+/9v//nmo/38giRiABQAAAALoPwEA","177":"b:f4X+/9v//nmo/38giRiABQAAAALoPwEA","181":"b:f4X+/9v//nmo/38giRiABQAAAALoPwEA","185":"b:f4X+/9v//nmo/38giRiABQAAAALoPwEA","189":"b:f4X+/9v//nmo/38giRiABQAAAALoPwEA","193":"b:f4X+/9v//nmo/38giRiABQAAAALoPwEA","197":"b:f4X+/9v//nmo/38giRiABQAAAALoPwEA","201":"b:f4X+/9v//nmo/38giRiABQAAAALoPwEA","205":"b:f4X+/9v//nmo/38giRiABQAAAALoPwEA","209":"b:f4X+/9v//nmo/38giRiABQAAAALoPwEA","213":"b:f4X+/9v//nmo/38giRiABQAAAALoPwEA","221":"b:f4X+/9v//nmo/38giRiABQAAAALoPwEA","225":"b:f4X+/9v//nmo/38giRiABQAAAALoPwEA","229":"b:f4X+/9v//nmo/38giRiABQAAAALoPwEA","233":"b:f4X+/9v//nmo/38giRiABQAAAALoPwEA","237":"b:f4X+/9v//nmo/38giRiABQAAAALoPwEA","241":"b:f4X+/9v//nmo/38giRiABQAAAALoPwEA","245":"b:f4X+/9v//nmo/38giRiABQAAAALoPwEA","249":"b:f4X+/9v//nmo/38giRiABQAAAALoPwEA","253":"b:f4X+/9v//nmo/38giRiABQAAAALoPwEA","257":"b:f4X+/9v//nmo/38giRiABQAAAALoPwEA","262":"b:f4X+/9v//nmo/38giRiABQAAAALoPwEA","275":"b:f4X+/9v//nmo/38giRiABQAAAALoPwEA"},"packages/@n8n/backend-common/src/utils/parse-flatted.ts":{"15":"b:f4DqwJKFDw94AAAAAAAAAAAAAAAAAAAA"},"packages/@n8n/backend-network/src/http/axios/config.ts":{"16":"b:B4RezFLoliEoZw8MEAAAAAAAAADgAQAA","27":"b:B4RezFLoliEoZw8MEAAAAAAAAADgAQAA","38":"b:B4RezFLoliEoZw8MEAAAAAAAAADgAQAA","52":"b:B4RezFLoliEoZw8MEAAAAAAAAADgAQAA","53":"b:B4RezFLoliEoZw8MEAAAAAAAAADgAQAA"},"packages/@n8n/backend-network/src/http/axios/invoke.ts":{"6":"b:B4RezALoliEoJwgMAAAAAAAAAADgAQAA"},"packages/@n8n/backend-network/src/http/axios/redirect.ts":{"49":"b:B4RezALoliEoJwgMAAAAAAAAAADgAQAA","67":"b:B4RezALoliEoJwgMAAAAAAAAAADgAQAA","72":"b:B4RezALoliEoJwgMAAAAAAAAAADgAQAA","80":"b:B4RezALoliEoJwgMAAAAAAAAAADgAQAA","95":"b:B4RezALoliEoJwgMAAAAAAAAAADgAQAA","107":"b:B4RezALoliEoJwgMAAAAAAAAAADgAQAA","113":"b:B4RezALoliEoJwgMAAAAAAAAAADgAQAA","119":"b:B4RezALoliEoJwgMAAAAAAAAAADgAQAA","135":"b:B4RezALoliEoJwgMAAAAAAAAAADgAQAA","151":"b:B4RezALoliEoJwgMAAAAAAAAAADgAQAA","183":"b:B4RezALoliEoJwgMAAAAAAAAAADgAQAA","206":"b:B4RezALoliEoJwgMAAAAAAAAAADgAQAA","219":"b:B4RezALoliEoJwgMAAAAAAAAAADgAQAA","227":"b:B4RezALoliEoJwgMAAAAAAAAAADgAQAA","234":"b:B4RezALoliEoJwgMAAAAAAAAAADgAQAA","238":"b:B4RezALoliEoJwgMAAAAAAAAAADgAQAA"},"packages/@n8n/backend-network/src/http/axios/request.ts":{"27":"b:B4RezALoliEoJwgMAAAAAAAAAADgAQAA","77":"b:B4RezALoliEoJwgMAAAAAAAAAADgAQAA","128":"b:B4RezALoliEoJwgMAAAAAAAAAADgAQAA","132":"b:B4RezALoliEoJwgMAAAAAAAAAADgAQAA","144":"b:B4RezALoliEoJwgMAAAAAAAAAADgAQAA","158":"b:B4RezALoliEoJwgMAAAAAAAAAADgAQAA","182":"b:B4RezALoliEoJwgMAAAAAAAAAADgAQAA"},"packages/@n8n/backend-network/src/http/axios/user-agent.ts":{"11":"b:B4RezALoliEoJwgMAAAAAAAAAADgAQAA","28":"b:B4RezALoliEoJwgMAAAAAAAAAADgAQAA","40":"b:B4RezALoliEoJwgMAAAAAAAAAADgAQAA","54":"b:B4RezALoliEoJwgMAAAAAAAAAADgAQAA"},"packages/@n8n/backend-network/src/http/axios/utils.ts":{"20":"b:B4RezALoliEoJwgMAAEAAAAKAADgAUAA","29":"b:B4RezALoliEoJwgMAAEAAAAKAADgAUAA","38":"b:B4RezALoliEoJwgMAAEAAAAKAADgAUAA","50":"b:B4RezALoliEoJwgMAAEAAAAKAADgAUAA","57":"b:B4RezALoliEoJwgMAAEAAAAKAADgAUAA","61":"b:B4RezALoliEoJwgMAAEAAAAKAADgAUAA","81":"b:B4RezALoliEoJwgMAAEAAAAKAADgAUAA","100":"b:B4RezALoliEoJwgMAAEAAAAKAADgAUAA","108":"b:B4RezALoliEoJwgMAAEAAAAKAADgAUAA","150":"b:B4RezALoliEoJwgMAAEAAAAKAADgAUAA","157":"b:B4RezALoliEoJwgMAAEAAAAKAADgAUAA","162":"b:B4RezALoliEoJwgMAAEAAAAKAADgAUAA","165":"b:B4RezALoliEoJwgMAAEAAAAKAADgAUAA","168":"b:B4RezALoliEoJwgMAAEAAAAKAADgAUAA","201":"b:B4RezALoliEoJwgMAAEAAAAKAADgAUAA","211":"b:B4RezALoliEoJwgMAAEAAAAKAADgAUAA","214":"b:B4RezALoliEoJwgMAAEAAAAKAADgAUAA","218":"b:B4RezALoliEoJwgMAAEAAAAKAADgAUAA","234":"b:B4RezALoliEoJwgMAAEAAAAKAADgAUAA","250":"b:B4RezALoliEoJwgMAAEAAAAKAADgAUAA","270":"b:B4RezALoliEoJwgMAAEAAAAKAADgAUAA","277":"b:B4RezALoliEoJwgMAAEAAAAKAADgAUAA","287":"b:B4RezALoliEoJwgMAAEAAAAKAADgAUAA","295":"b:B4RezALoliEoJwgMAAEAAAAKAADgAUAA","302":"b:B4RezALoliEoJwgMAAEAAAAKAADgAUAA","303":"b:B4RezALoliEoJwgMAAEAAAAKAADgAUAA","318":"b:B4RezALoliEoJwgMAAEAAAAKAADgAUAA","349":"b:B4RezALoliEoJwgMAAEAAAAKAADgAUAA","360":"b:B4RezALoliEoJwgMAAEAAAAKAADgAUAA","383":"b:B4RezALoliEoJwgMAAEAAAAKAADgAUAA","398":"b:B4RezALoliEoJwgMAAEAAAAKAADgAUAA"},"packages/@n8n/backend-network/src/http/client-default-headers.ts":{"18":"b:B4RezALoliEoJwgMAAAAAAAAAAAgAAAA"},"packages/@n8n/backend-network/src/http/env-proxy-https-agent.ts":{"17":"b:B4RezALolgEoJwgMAAAAAAAAAAAgAAgA","22":"b:B4RezALolgEoJwgMAAAAAAAAAAAgAAgA","26":"b:B4RezALolgEoJwgMAAAAAAAAAAAgAAgA"},"packages/@n8n/backend-network/src/http/env-proxy-router.ts":{"22":"b:B4RezFLoliEoZw8MEAAAAAAAAADgAQgA","31":"b:B4RezFLoliEoZw8MEAAAAAAAAADgAQgA","51":"b:B4RezFLoliEoZw8MEAAAAAAAAADgAQgA","64":"b:B4RezFLoliEoZw8MEAAAAAAAAADgAQgA"},"packages/@n8n/backend-network/src/http/index.ts":{"9":"b:f4DqxJKHBm94AAAAAAEAAAAAEgDIB/AA","10":"b:f4DqxJKHBm94AAAAAAEAAAAAEgDIB/AA","11":"b:f4DqxJKHBm94AAAAAAEAAAAAEgDIB/AA","12":"b:f4DqxJKHBm94AAAAAAEAAAAAEgDIB/AA"},"packages/@n8n/backend-network/src/http/node-agents.ts":{"23":"b:B4RezALoliEoJwgMAAAAAAAAAADgAQgA","67":"b:B4RezALoliEoJwgMAAAAAAAAAADgAQgA","122":"b:B4RezALoliEoJwgMAAAAAAAAAADgAQgA","137":"b:B4RezALoliEoJwgMAAAAAAAAAADgAQgA","142":"b:B4RezALoliEoJwgMAAAAAAAAAADgAQgA"},"packages/@n8n/backend-network/src/http/outbound-http.ts":{"161":"b:B4RezlLoliEoZw8MEAEAAAAKAAAgAEgA","170":"b:B4RezlLoliEoZw8MEAEAAAAKAAAgAEgA","174":"b:B4RezlLoliEoZw8MEAEAAAAKAAAgAEgA","186":"b:B4RezlLoliEoZw8MEAEAAAAKAAAgAEgA","197":"b:B4RezlLoliEoZw8MEAEAAAAKAAAgAEgA","210":"b:B4RezlLoliEoZw8MEAEAAAAKAAAgAEgA","220":"b:B4RezlLoliEoZw8MEAEAAAAKAAAgAEgA","223":"b:B4RezlLoliEoZw8MEAEAAAAKAAAgAEgA","224":"b:B4RezlLoliEoZw8MEAEAAAAKAAAgAEgA","225":"b:B4RezlLoliEoZw8MEAEAAAAKAAAgAEgA","231":"b:B4RezlLoliEoZw8MEAEAAAAKAAAgAEgA","233":"b:B4RezlLoliEoZw8MEAEAAAAKAAAgAEgA"},"packages/@n8n/config/src/index.ts":{"58":"b:f69ezALoliMoJwgMAAAAAAIAAADgAQAA","60":"b:f69ezALoliMoJwgMAAAAAAIAAADgAQAA","61":"b:f69ezALoliMoJwgMAAAAAAIAAADgAQAA","63":"b:f69ezALoliMoJwgMAAAAAAIAAADgAQAA","64":"b:f69ezALoliMoJwgMAAAAAAIAAADgAQAA","69":"b:f69ezALoliMoJwgMAAAAAAIAAADgAQAA","71":"b:f69ezALoliMoJwgMAAAAAAIAAADgAQAA","81":"b:f69ezALoliMoJwgMAAAAAAIAAADgAQAA","83":"b:f69ezALoliMoJwgMAAAAAAIAAADgAQAA","84":"b:f69ezALoliMoJwgMAAAAAAIAAADgAQAA","85":"b:f69ezALoliMoJwgMAAAAAAIAAADgAQAA","89":"b:f69ezALoliMoJwgMAAAAAAIAAADgAQAA","90":"b:f69ezALoliMoJwgMAAAAAAIAAADgAQAA","101":"b:f69ezALoliMoJwgMAAAAAAIAAADgAQAA"},"packages/@n8n/db/src/entities/index.ts":{"5":[19,1,2,3,4,5,57,129,6],"9":[19,1,2,3,4,5,57,129,6],"17":[19,1,2,3,4,5,57,129,6],"20":[19,1,2,3,4,5,57,129,6],"21":[19,1,2,3,4,5,57,129,6],"27":[19,1,2,3,4,5,57,129,6],"29":[19,1,2,3,4,5,57,129,6],"32":[19,1,2,3,4,5,57,129,6],"46":[19,1,2,3,4,5,57,129,6],"49":[19,1,2,3,4,5,57,129,6],"53":[19,1,2,3,4,5,57,129,6],"55":[19,1,2,3,4,5,57,129,6],"58":[19,1,2,3,4,5,57,129,6],"59":[19,1,2,3,4,5,57,129,6],"61":[19,1,2,3,4,5,57,129,6]},"packages/@n8n/db/src/entities/types-db.ts":{"327":"b:f4Te3lKkxmko/38AAAAAAAAAAADoNwgA","339":"b:f4Te3lKkxmko/38AAAAAAAAAAADoNwgA","477":"b:f4Te3lKkxmko/38AAAAAAAAAAADoNwgA"},"packages/@n8n/db/src/entities/user.ts":{"60":"b:R4VeztL5lwEoAwAAAAAAAAAAAAAAAAAA","84":"b:R4VeztL5lwEoAwAAAAAAAAAAAAAAAAAA","115":"b:R4VeztL5lwEoAwAAAAAAAAAAAAAAAAAA","117":"b:R4VeztL5lwEoAwAAAAAAAAAAAAAAAAAA","124":"b:R4VeztL5lwEoAwAAAAAAAAAAAAAAAAAA","129":"b:R4VeztL5lwEoAwAAAAAAAAAAAAAAAAAA","139":"b:R4VeztL5lwEoAwAAAAAAAAAAAAAAAAAA"},"packages/@n8n/db/src/index.ts":{"14":"b:f4Hr/7+H33/5G3AsAhCACAAIAgDqJ9EB","16":"b:f4Hr/7+H33/5G3AsAhCACAAIAgDqJ9EB","19":"b:f4Hr/7+H33/5G3AsAhCACAAIAgDqJ9EB","20":"b:f4Hr/7+H33/5G3AsAhCACAAIAgDqJ9EB","21":"b:f4Hr/7+H33/5G3AsAhCACAAIAgDqJ9EB","36":"b:f4Hr/7+H33/5G3AsAhCACAAIAgDqJ9EB","46":"b:f4Hr/7+H33/5G3AsAhCACAAIAgDqJ9EB","56":"b:f4Hr/7+H33/5G3AsAhCACAAIAgDqJ9EB"},"packages/@n8n/db/src/repositories/credentials.repository.ts":{"19":"b:f+V//vP5/3ctP3ggy4gAVAAAEAIAIEAA","26":"b:f+V//vP5/3ctP3ggy4gAVAAAEAIAIEAA","33":"b:f+V//vP5/3ctP3ggy4gAVAAAEAIAIEAA","40":"b:f+V//vP5/3ctP3ggy4gAVAAAEAIAIEAA","48":"b:f+V//vP5/3ctP3ggy4gAVAAAEAIAIEAA","58":"b:f+V//vP5/3ctP3ggy4gAVAAAEAIAIEAA","65":"b:f+V//vP5/3ctP3ggy4gAVAAAEAIAIEAA","78":"b:f+V//vP5/3ctP3ggy4gAVAAAEAIAIEAA","117":"b:f+V//vP5/3ctP3ggy4gAVAAAEAIAIEAA","135":"b:f+V//vP5/3ctP3ggy4gAVAAAEAIAIEAA","153":"b:f+V//vP5/3ctP3ggy4gAVAAAEAIAIEAA","160":"b:f+V//vP5/3ctP3ggy4gAVAAAEAIAIEAA","231":"b:f+V//vP5/3ctP3ggy4gAVAAAEAIAIEAA","271":"b:f+V//vP5/3ctP3ggy4gAVAAAEAIAIEAA","290":"b:f+V//vP5/3ctP3ggy4gAVAAAEAIAIEAA","320":"b:f+V//vP5/3ctP3ggy4gAVAAAEAIAIEAA","346":"b:f+V//vP5/3ctP3ggy4gAVAAAEAIAIEAA","362":"b:f+V//vP5/3ctP3ggy4gAVAAAEAIAIEAA","376":"b:f+V//vP5/3ctP3ggy4gAVAAAEAIAIEAA","389":"b:f+V//vP5/3ctP3ggy4gAVAAAEAIAIEAA","397":"b:f+V//vP5/3ctP3ggy4gAVAAAEAIAIEAA","409":"b:f+V//vP5/3ctP3ggy4gAVAAAEAIAIEAA","449":"b:f+V//vP5/3ctP3ggy4gAVAAAEAIAIEAA","528":"b:f+V//vP5/3ctP3ggy4gAVAAAEAIAIEAA","530":"b:f+V//vP5/3ctP3ggy4gAVAAAEAIAIEAA","534":"b:f+V//vP5/3ctP3ggy4gAVAAAEAIAIEAA","552":"b:f+V//vP5/3ctP3ggy4gAVAAAEAIAIEAA"},"packages/@n8n/db/src/repositories/execution-metadata.repository.ts":{"8":[19,1,2,3,4,5,57,129,6]},"packages/@n8n/db/src/repositories/execution.repository.ts":{"63":"b:f4DqwJOHBm94AAACAAAAAAAAACDYB/AD","98":"b:f4DqwJOHBm94AAACAAAAAAAAACDYB/AD","141":"b:f4DqwJOHBm94AAACAAAAAAAAACDYB/AD","145":"b:f4DqwJOHBm94AAACAAAAAAAAACDYB/AD","157":"b:f4DqwJOHBm94AAACAAAAAAAAACDYB/AD","189":"b:f4DqwJOHBm94AAACAAAAAAAAACDYB/AD","209":"b:f4DqwJOHBm94AAACAAAAAAAAACDYB/AD","214":"b:f4DqwJOHBm94AAACAAAAAAAAACDYB/AD","221":"b:f4DqwJOHBm94AAACAAAAAAAAACDYB/AD","228":"b:f4DqwJOHBm94AAACAAAAAAAAACDYB/AD","234":"b:f4DqwJOHBm94AAACAAAAAAAAACDYB/AD","240":"b:f4DqwJOHBm94AAACAAAAAAAAACDYB/AD","246":"b:f4DqwJOHBm94AAACAAAAAAAAACDYB/AD","253":"b:f4DqwJOHBm94AAACAAAAAAAAACDYB/AD","284":"b:f4DqwJOHBm94AAACAAAAAAAAACDYB/AD","347":"b:f4DqwJOHBm94AAACAAAAAAAAACDYB/AD","353":"b:f4DqwJOHBm94AAACAAAAAAAAACDYB/AD","372":"b:f4DqwJOHBm94AAACAAAAAAAAACDYB/AD","375":"b:f4DqwJOHBm94AAACAAAAAAAAACDYB/AD","404":"b:f4DqwJOHBm94AAACAAAAAAAAACDYB/AD","428":"b:f4DqwJOHBm94AAACAAAAAAAAACDYB/AD","455":"b:f4DqwJOHBm94AAACAAAAAAAAACDYB/AD","495":"b:f4DqwJOHBm94AAACAAAAAAAAACDYB/AD","506":"b:f4DqwJOHBm94AAACAAAAAAAAACDYB/AD","507":"b:f4DqwJOHBm94AAACAAAAAAAAACDYB/AD","514":"b:f4DqwJOHBm94AAACAAAAAAAAACDYB/AD","520":"b:f4DqwJOHBm94AAACAAAAAAAAACDYB/AD","523":"b:f4DqwJOHBm94AAACAAAAAAAAACDYB/AD","569":"b:f4DqwJOHBm94AAACAAAAAAAAACDYB/AD","578":"b:f4DqwJOHBm94AAACAAAAAAAAACDYB/AD","596":"b:f4DqwJOHBm94AAACAAAAAAAAACDYB/AD","603":"b:f4DqwJOHBm94AAACAAAAAAAAACDYB/AD","607":"b:f4DqwJOHBm94AAACAAAAAAAAACDYB/AD","631":"b:f4DqwJOHBm94AAACAAAAAAAAACDYB/AD","646":"b:f4DqwJOHBm94AAACAAAAAAAAACDYB/AD","656":"b:f4DqwJOHBm94AAACAAAAAAAAACDYB/AD","670":"b:f4DqwJOHBm94AAACAAAAAAAAACDYB/AD","688":"b:f4DqwJOHBm94AAACAAAAAAAAACDYB/AD","699":"b:f4DqwJOHBm94AAACAAAAAAAAACDYB/AD","705":"b:f4DqwJOHBm94AAACAAAAAAAAACDYB/AD","718":"b:f4DqwJOHBm94AAACAAAAAAAAACDYB/AD","756":"b:f4DqwJOHBm94AAACAAAAAAAAACDYB/AD","797":"b:f4DqwJOHBm94AAACAAAAAAAAACDYB/AD","813":"b:f4DqwJOHBm94AAACAAAAAAAAACDYB/AD","817":"b:f4DqwJOHBm94AAACAAAAAAAAACDYB/AD","844":"b:f4DqwJOHBm94AAACAAAAAAAAACDYB/AD","880":"b:f4DqwJOHBm94AAACAAAAAAAAACDYB/AD","884":"b:f4DqwJOHBm94AAACAAAAAAAAACDYB/AD","904":"b:f4DqwJOHBm94AAACAAAAAAAAACDYB/AD","924":"b:f4DqwJOHBm94AAACAAAAAAAAACDYB/AD","1043":"b:f4DqwJOHBm94AAACAAAAAAAAACDYB/AD","1045":"b:f4DqwJOHBm94AAACAAAAAAAAACDYB/AD","1053":"b:f4DqwJOHBm94AAACAAAAAAAAACDYB/AD","1091":"b:f4DqwJOHBm94AAACAAAAAAAAACDYB/AD","1099":"b:f4DqwJOHBm94AAACAAAAAAAAACDYB/AD","1102":"b:f4DqwJOHBm94AAACAAAAAAAAACDYB/AD","1110":"b:f4DqwJOHBm94AAACAAAAAAAAACDYB/AD","1113":"b:f4DqwJOHBm94AAACAAAAAAAAACDYB/AD","1116":"b:f4DqwJOHBm94AAACAAAAAAAAACDYB/AD","1122":"b:f4DqwJOHBm94AAACAAAAAAAAACDYB/AD","1130":"b:f4DqwJOHBm94AAACAAAAAAAAACDYB/AD","1137":"b:f4DqwJOHBm94AAACAAAAAAAAACDYB/AD","1145":"b:f4DqwJOHBm94AAACAAAAAAAAACDYB/AD","1162":"b:f4DqwJOHBm94AAACAAAAAAAAACDYB/AD"},"packages/@n8n/db/src/repositories/index.ts":{"3":"b:R4T+zNP/xmkoAwAAAAAAAAAAAADoBwEA","4":"b:R4T+zNP/xmkoAwAAAAAAAAAAAADoBwEA","7":"b:R4T+zNP/xmkoAwAAAAAAAAAAAADoBwEA","10":"b:R4T+zNP/xmkoAwAAAAAAAAAAAADoBwEA","11":"b:R4T+zNP/xmkoAwAAAAAAAAAAAADoBwEA","12":"b:R4T+zNP/xmkoAwAAAAAAAAAAAADoBwEA","13":"b:R4T+zNP/xmkoAwAAAAAAAAAAAADoBwEA","26":"b:R4T+zNP/xmkoAwAAAAAAAAAAAADoBwEA","28":"b:R4T+zNP/xmkoAwAAAAAAAAAAAADoBwEA","35":"b:R4T+zNP/xmkoAwAAAAAAAAAAAADoBwEA","37":"b:R4T+zNP/xmkoAwAAAAAAAAAAAADoBwEA","38":"b:R4T+zNP/xmkoAwAAAAAAAAAAAADoBwEA","42":"b:R4T+zNP/xmkoAwAAAAAAAAAAAADoBwEA","59":"b:R4T+zNP/xmkoAwAAAAAAAAAAAADoBwEA","61":"b:R4T+zNP/xmkoAwAAAAAAAAAAAADoBwEA","64":"b:R4T+zNP/xmkoAwAAAAAAAAAAAADoBwEA","65":"b:R4T+zNP/xmkoAwAAAAAAAAAAAADoBwEA","67":"b:R4T+zNP/xmkoAwAAAAAAAAAAAADoBwEA","69":"b:R4T+zNP/xmkoAwAAAAAAAAAAAADoBwEA","89":"b:R4T+zNP/xmkoAwAAAAAAAAAAAADoBwEA","90":"b:R4T+zNP/xmkoAwAAAAAAAAAAAADoBwEA","96":"b:R4T+zNP/xmkoAwAAAAAAAAAAAADoBwEA","97":"b:R4T+zNP/xmkoAwAAAAAAAAAAAADoBwEA"},"packages/@n8n/db/src/repositories/project-relation.repository.ts":{"9":"b:fgDqQoOChksxAQCiggAARAIIAADYB/AD","13":"b:fgDqQoOChksxAQCiggAARAIIAADYB/AD","27":"b:fgDqQoOChksxAQCiggAARAIIAADYB/AD","35":"b:fgDqQoOChksxAQCiggAARAIIAADYB/AD","38":"b:fgDqQoOChksxAQCiggAARAIIAADYB/AD","43":"b:fgDqQoOChksxAQCiggAARAIIAADYB/AD","49":"b:fgDqQoOChksxAQCiggAARAIIAADYB/AD","56":"b:fgDqQoOChksxAQCiggAARAIIAADYB/AD","62":"b:fgDqQoOChksxAQCiggAARAIIAADYB/AD","70":"b:fgDqQoOChksxAQCiggAARAIIAADYB/AD","76":"b:fgDqQoOChksxAQCiggAARAIIAADYB/AD","79":"b:fgDqQoOChksxAQCiggAARAIIAADYB/AD"},"packages/@n8n/db/src/repositories/project.repository.ts":{"9":"b:f4T+3/v893sp/38AAhAAVQAAAADqNwEA","13":"b:f4T+3/v893sp/38AAhAAVQAAAADqNwEA","25":"b:f4T+3/v893sp/38AAhAAVQAAAADqNwEA","36":"b:f4T+3/v893sp/38AAhAAVQAAAADqNwEA","46":"b:f4T+3/v893sp/38AAhAAVQAAAADqNwEA","70":"b:f4T+3/v893sp/38AAhAAVQAAAADqNwEA","85":"b:f4T+3/v893sp/38AAhAAVQAAAADqNwEA","102":"b:f4T+3/v893sp/38AAhAAVQAAAADqNwEA","112":"b:f4T+3/v893sp/38AAhAAVQAAAADqNwEA","124":"b:f4T+3/v893sp/38AAhAAVQAAAADqNwEA","140":"b:f4T+3/v893sp/38AAhAAVQAAAADqNwEA","149":"b:f4T+3/v893sp/38AAhAAVQAAAADqNwEA","165":"b:f4T+3/v893sp/38AAhAAVQAAAADqNwEA","178":"b:f4T+3/v893sp/38AAhAAVQAAAADqNwEA","192":"b:f4T+3/v893sp/38AAhAAVQAAAADqNwEA","202":"b:f4T+3/v893sp/38AAhAAVQAAAADqNwEA","209":"b:f4T+3/v893sp/38AAhAAVQAAAADqNwEA"},"packages/@n8n/db/src/repositories/role.repository.ts":{"9":"b:f4Te3lOu1nkp/38AAAAARQAAAADAF0EE","13":"b:f4Te3lOu1nkp/38AAAAARQAAAADAF0EE","18":"b:f4Te3lOu1nkp/38AAAAARQAAAADAF0EE","36":"b:f4Te3lOu1nkp/38AAAAARQAAAADAF0EE","52":"b:f4Te3lOu1nkp/38AAAAARQAAAADAF0EE","63":"b:f4Te3lOu1nkp/38AAAAARQAAAADAF0EE","72":"b:f4Te3lOu1nkp/38AAAAARQAAAADAF0EE","80":"b:f4Te3lOu1nkp/38AAAAARQAAAADAF0EE","106":"b:f4Te3lOu1nkp/38AAAAARQAAAADAF0EE","109":"b:f4Te3lOu1nkp/38AAAAARQAAAADAF0EE","112":"b:f4Te3lOu1nkp/38AAAAARQAAAADAF0EE","126":"b:f4Te3lOu1nkp/38AAAAARQAAAADAF0EE","129":"b:f4Te3lOu1nkp/38AAAAARQAAAADAF0EE","149":"b:f4Te3lOu1nkp/38AAAAARQAAAADAF0EE","178":"b:f4Te3lOu1nkp/38AAAAARQAAAADAF0EE","185":"b:f4Te3lOu1nkp/38AAAAARQAAAADAF0EE","192":"b:f4Te3lOu1nkp/38AAAAARQAAAADAF0EE","203":"b:f4Te3lOu1nkp/38AAAAARQAAAADAF0EE","204":"b:f4Te3lOu1nkp/38AAAAARQAAAADAF0EE","228":"b:f4Te3lOu1nkp/38AAAAARQAAAADAF0EE","261":"b:f4Te3lOu1nkp/38AAAAARQAAAADAF0EE","265":"b:f4Te3lOu1nkp/38AAAAARQAAAADAF0EE"},"packages/@n8n/db/src/repositories/shared-workflow.repository.ts":{"21":"b:d4T+7/Pv9mm5G3AuAAgAZAAAIgD6B+ED","25":"b:d4T+7/Pv9mm5G3AuAAgAZAAAIgD6B+ED","32":"b:d4T+7/Pv9mm5G3AuAAgAZAAAIgD6B+ED","35":"b:d4T+7/Pv9mm5G3AuAAgAZAAAIgD6B+ED","46":"b:d4T+7/Pv9mm5G3AuAAgAZAAAIgD6B+ED","52":"b:d4T+7/Pv9mm5G3AuAAgAZAAAIgD6B+ED","55":"b:d4T+7/Pv9mm5G3AuAAgAZAAAIgD6B+ED","76":"b:d4T+7/Pv9mm5G3AuAAgAZAAAIgD6B+ED","86":"b:d4T+7/Pv9mm5G3AuAAgAZAAAIgD6B+ED","92":"b:d4T+7/Pv9mm5G3AuAAgAZAAAIgD6B+ED","104":"b:d4T+7/Pv9mm5G3AuAAgAZAAAIgD6B+ED","116":"b:d4T+7/Pv9mm5G3AuAAgAZAAAIgD6B+ED","128":"b:d4T+7/Pv9mm5G3AuAAgAZAAAIgD6B+ED","131":"b:d4T+7/Pv9mm5G3AuAAgAZAAAIgD6B+ED","139":"b:d4T+7/Pv9mm5G3AuAAgAZAAAIgD6B+ED","148":"b:d4T+7/Pv9mm5G3AuAAgAZAAAIgD6B+ED","157":"b:d4T+7/Pv9mm5G3AuAAgAZAAAIgD6B+ED","166":"b:d4T+7/Pv9mm5G3AuAAgAZAAAIgD6B+ED","171":"b:d4T+7/Pv9mm5G3AuAAgAZAAAIgD6B+ED","184":"b:d4T+7/Pv9mm5G3AuAAgAZAAAIgD6B+ED","222":"b:d4T+7/Pv9mm5G3AuAAgAZAAAIgD6B+ED"},"packages/@n8n/db/src/repositories/user.repository.ts":{"16":"b:f6V+3tP71xUt/39AABkQRAAAAAAAoIAE","20":"b:f6V+3tP71xUt/39AABkQRAAAAAAAoIAE","32":"b:f6V+3tP71xUt/39AABkQRAAAAAAAoIAE","56":"b:f6V+3tP71xUt/39AABkQRAAAAAAAoIAE","60":"b:f6V+3tP71xUt/39AABkQRAAAAAAAoIAE","64":"b:f6V+3tP71xUt/39AABkQRAAAAAAAoIAE","68":"b:f6V+3tP71xUt/39AABkQRAAAAAAAoIAE","75":"b:f6V+3tP71xUt/39AABkQRAAAAAAAoIAE","79":"b:f6V+3tP71xUt/39AABkQRAAAAAAAoIAE","90":"b:f6V+3tP71xUt/39AABkQRAAAAAAAoIAE","97":"b:f6V+3tP71xUt/39AABkQRAAAAAAAoIAE","108":"b:f6V+3tP71xUt/39AABkQRAAAAAAAoIAE","115":"b:f6V+3tP71xUt/39AABkQRAAAAAAAoIAE","119":"b:f6V+3tP71xUt/39AABkQRAAAAAAAoIAE","166":"b:f6V+3tP71xUt/39AABkQRAAAAAAAoIAE","200":"b:f6V+3tP71xUt/39AABkQRAAAAAAAoIAE","221":"b:f6V+3tP71xUt/39AABkQRAAAAAAAoIAE","234":"b:f6V+3tP71xUt/39AABkQRAAAAAAAoIAE","242":"b:f6V+3tP71xUt/39AABkQRAAAAAAAoIAE","247":"b:f6V+3tP71xUt/39AABkQRAAAAAAAoIAE","306":"b:f6V+3tP71xUt/39AABkQRAAAAAAAoIAE","332":"b:f6V+3tP71xUt/39AABkQRAAAAAAAoIAE","351":"b:f6V+3tP71xUt/39AABkQRAAAAAAAoIAE","373":"b:f6V+3tP71xUt/39AABkQRAAAAAAAoIAE","384":"b:f6V+3tP71xUt/39AABkQRAAAAAAAoIAE"},"packages/@n8n/db/src/repositories/workflow-dependency.repository.ts":{"15":"b:R4T+zNP/xmkoAwAAAAAAAAAAAAjqBwEA","21":"b:R4T+zNP/xmkoAwAAAAAAAAAAAAjqBwEA","40":"b:R4T+zNP/xmkoAwAAAAAAAAAAAAjqBwEA","53":"b:R4T+zNP/xmkoAwAAAAAAAAAAAAjqBwEA","57":"b:R4T+zNP/xmkoAwAAAAAAAAAAAAjqBwEA","62":"b:R4T+zNP/xmkoAwAAAAAAAAAAAAjqBwEA","95":"b:R4T+zNP/xmkoAwAAAAAAAAAAAAjqBwEA","120":"b:R4T+zNP/xmkoAwAAAAAAAAAAAAjqBwEA","121":"b:R4T+zNP/xmkoAwAAAAAAAAAAAAjqBwEA","132":"b:R4T+zNP/xmkoAwAAAAAAAAAAAAjqBwEA","158":"b:R4T+zNP/xmkoAwAAAAAAAAAAAAjqBwEA"},"packages/@n8n/db/src/repositories/workflow-statistics.repository.ts":{"43":"b:R4DqwJOHDm94AAACgAAAAAIICADIB9AA","50":"b:R4DqwJOHDm94AAACgAAAAAIICADIB9AA","83":"b:R4DqwJOHDm94AAACgAAAAAIICADIB9AA","126":"b:R4DqwJOHDm94AAACgAAAAAIICADIB9AA","131":"b:R4DqwJOHDm94AAACgAAAAAIICADIB9AA","144":"b:R4DqwJOHDm94AAACgAAAAAIICADIB9AA","197":"b:R4DqwJOHDm94AAACgAAAAAIICADIB9AA","202":"b:R4DqwJOHDm94AAACgAAAAAIICADIB9AA","212":"b:R4DqwJOHDm94AAACgAAAAAIICADIB9AA"},"packages/@n8n/db/src/repositories/workflow.repository.ts":{"58":"b:f6b+7/Pv/m/+P3huABlA0AQgAAPbD+AL","68":"b:f6b+7/Pv/m/+P3huABlA0AQgAAPbD+AL","78":"b:f6b+7/Pv/m/+P3huABlA0AQgAAPbD+AL","84":"b:f6b+7/Pv/m/+P3huABlA0AQgAAPbD+AL","87":"b:f6b+7/Pv/m/+P3huABlA0AQgAAPbD+AL","94":"b:f6b+7/Pv/m/+P3huABlA0AQgAAPbD+AL","97":"b:f6b+7/Pv/m/+P3huABlA0AQgAAPbD+AL","103":"b:f6b+7/Pv/m/+P3huABlA0AQgAAPbD+AL","112":"b:f6b+7/Pv/m/+P3huABlA0AQgAAPbD+AL","118":"b:f6b+7/Pv/m/+P3huABlA0AQgAAPbD+AL","128":"b:f6b+7/Pv/m/+P3huABlA0AQgAAPbD+AL","143":"b:f6b+7/Pv/m/+P3huABlA0AQgAAPbD+AL","154":"b:f6b+7/Pv/m/+P3huABlA0AQgAAPbD+AL","160":"b:f6b+7/Pv/m/+P3huABlA0AQgAAPbD+AL","163":"b:f6b+7/Pv/m/+P3huABlA0AQgAAPbD+AL","179":"b:f6b+7/Pv/m/+P3huABlA0AQgAAPbD+AL","188":"b:f6b+7/Pv/m/+P3huABlA0AQgAAPbD+AL","195":"b:f6b+7/Pv/m/+P3huABlA0AQgAAPbD+AL","204":"b:f6b+7/Pv/m/+P3huABlA0AQgAAPbD+AL","211":"b:f6b+7/Pv/m/+P3huABlA0AQgAAPbD+AL","225":"b:f6b+7/Pv/m/+P3huABlA0AQgAAPbD+AL","238":"b:f6b+7/Pv/m/+P3huABlA0AQgAAPbD+AL","245":"b:f6b+7/Pv/m/+P3huABlA0AQgAAPbD+AL","251":"b:f6b+7/Pv/m/+P3huABlA0AQgAAPbD+AL","257":"b:f6b+7/Pv/m/+P3huABlA0AQgAAPbD+AL","267":"b:f6b+7/Pv/m/+P3huABlA0AQgAAPbD+AL","356":"b:f6b+7/Pv/m/+P3huABlA0AQgAAPbD+AL","380":"b:f6b+7/Pv/m/+P3huABlA0AQgAAPbD+AL","402":"b:f6b+7/Pv/m/+P3huABlA0AQgAAPbD+AL","428":"b:f6b+7/Pv/m/+P3huABlA0AQgAAPbD+AL","439":"b:f6b+7/Pv/m/+P3huABlA0AQgAAPbD+AL","440":"b:f6b+7/Pv/m/+P3huABlA0AQgAAPbD+AL","443":"b:f6b+7/Pv/m/+P3huABlA0AQgAAPbD+AL","465":"b:f6b+7/Pv/m/+P3huABlA0AQgAAPbD+AL","509":"b:f6b+7/Pv/m/+P3huABlA0AQgAAPbD+AL","567":"b:f6b+7/Pv/m/+P3huABlA0AQgAAPbD+AL","601":"b:f6b+7/Pv/m/+P3huABlA0AQgAAPbD+AL","631":"b:f6b+7/Pv/m/+P3huABlA0AQgAAPbD+AL","710":"b:f6b+7/Pv/m/+P3huABlA0AQgAAPbD+AL","721":"b:f6b+7/Pv/m/+P3huABlA0AQgAAPbD+AL","726":"b:f6b+7/Pv/m/+P3huABlA0AQgAAPbD+AL","727":"b:f6b+7/Pv/m/+P3huABlA0AQgAAPbD+AL","730":"b:f6b+7/Pv/m/+P3huABlA0AQgAAPbD+AL","732":"b:f6b+7/Pv/m/+P3huABlA0AQgAAPbD+AL","733":"b:f6b+7/Pv/m/+P3huABlA0AQgAAPbD+AL","736":"b:f6b+7/Pv/m/+P3huABlA0AQgAAPbD+AL","751":"b:f6b+7/Pv/m/+P3huABlA0AQgAAPbD+AL","758":"b:f6b+7/Pv/m/+P3huABlA0AQgAAPbD+AL","759":"b:f6b+7/Pv/m/+P3huABlA0AQgAAPbD+AL","761":"b:f6b+7/Pv/m/+P3huABlA0AQgAAPbD+AL","770":"b:f6b+7/Pv/m/+P3huABlA0AQgAAPbD+AL","784":"b:f6b+7/Pv/m/+P3huABlA0AQgAAPbD+AL","803":"b:f6b+7/Pv/m/+P3huABlA0AQgAAPbD+AL","834":"b:f6b+7/Pv/m/+P3huABlA0AQgAAPbD+AL","904":"b:f6b+7/Pv/m/+P3huABlA0AQgAAPbD+AL","927":"b:f6b+7/Pv/m/+P3huABlA0AQgAAPbD+AL","966":"b:f6b+7/Pv/m/+P3huABlA0AQgAAPbD+AL","973":"b:f6b+7/Pv/m/+P3huABlA0AQgAAPbD+AL","974":"b:f6b+7/Pv/m/+P3huABlA0AQgAAPbD+AL","981":"b:f6b+7/Pv/m/+P3huABlA0AQgAAPbD+AL","997":"b:f6b+7/Pv/m/+P3huABlA0AQgAAPbD+AL","1010":"b:f6b+7/Pv/m/+P3huABlA0AQgAAPbD+AL","1020":"b:f6b+7/Pv/m/+P3huABlA0AQgAAPbD+AL","1034":"b:f6b+7/Pv/m/+P3huABlA0AQgAAPbD+AL","1051":"b:f6b+7/Pv/m/+P3huABlA0AQgAAPbD+AL","1063":"b:f6b+7/Pv/m/+P3huABlA0AQgAAPbD+AL","1071":"b:f6b+7/Pv/m/+P3huABlA0AQgAAPbD+AL","1094":"b:f6b+7/Pv/m/+P3huABlA0AQgAAPbD+AL","1102":"b:f6b+7/Pv/m/+P3huABlA0AQgAAPbD+AL","1108":"b:f6b+7/Pv/m/+P3huABlA0AQgAAPbD+AL","1119":"b:f6b+7/Pv/m/+P3huABlA0AQgAAPbD+AL","1125":"b:f6b+7/Pv/m/+P3huABlA0AQgAAPbD+AL","1130":"b:f6b+7/Pv/m/+P3huABlA0AQgAAPbD+AL","1141":"b:f6b+7/Pv/m/+P3huABlA0AQgAAPbD+AL","1153":"b:f6b+7/Pv/m/+P3huABlA0AQgAAPbD+AL","1174":"b:f6b+7/Pv/m/+P3huABlA0AQgAAPbD+AL","1187":"b:f6b+7/Pv/m/+P3huABlA0AQgAAPbD+AL","1196":"b:f6b+7/Pv/m/+P3huABlA0AQgAAPbD+AL","1217":"b:f6b+7/Pv/m/+P3huABlA0AQgAAPbD+AL","1228":"b:f6b+7/Pv/m/+P3huABlA0AQgAAPbD+AL","1241":"b:f6b+7/Pv/m/+P3huABlA0AQgAAPbD+AL","1251":"b:f6b+7/Pv/m/+P3huABlA0AQgAAPbD+AL","1253":"b:f6b+7/Pv/m/+P3huABlA0AQgAAPbD+AL","1276":"b:f6b+7/Pv/m/+P3huABlA0AQgAAPbD+AL","1303":"b:f6b+7/Pv/m/+P3huABlA0AQgAAPbD+AL","1307":"b:f6b+7/Pv/m/+P3huABlA0AQgAAPbD+AL","1317":"b:f6b+7/Pv/m/+P3huABlA0AQgAAPbD+AL","1349":"b:f6b+7/Pv/m/+P3huABlA0AQgAAPbD+AL","1357":"b:f6b+7/Pv/m/+P3huABlA0AQgAAPbD+AL","1363":"b:f6b+7/Pv/m/+P3huABlA0AQgAAPbD+AL","1373":"b:f6b+7/Pv/m/+P3huABlA0AQgAAPbD+AL","1378":"b:f6b+7/Pv/m/+P3huABlA0AQgAAPbD+AL","1394":"b:f6b+7/Pv/m/+P3huABlA0AQgAAPbD+AL","1403":"b:f6b+7/Pv/m/+P3huABlA0AQgAAPbD+AL","1410":"b:f6b+7/Pv/m/+P3huABlA0AQgAAPbD+AL","1417":"b:f6b+7/Pv/m/+P3huABlA0AQgAAPbD+AL","1424":"b:f6b+7/Pv/m/+P3huABlA0AQgAAPbD+AL","1434":"b:f6b+7/Pv/m/+P3huABlA0AQgAAPbD+AL","1449":"b:f6b+7/Pv/m/+P3huABlA0AQgAAPbD+AL","1483":"b:f6b+7/Pv/m/+P3huABlA0AQgAAPbD+AL","1490":"b:f6b+7/Pv/m/+P3huABlA0AQgAAPbD+AL","1494":"b:f6b+7/Pv/m/+P3huABlA0AQgAAPbD+AL","1509":"b:f6b+7/Pv/m/+P3huABlA0AQgAAPbD+AL","1516":"b:f6b+7/Pv/m/+P3huABlA0AQgAAPbD+AL","1549":"b:f6b+7/Pv/m/+P3huABlA0AQgAAPbD+AL","1565":"b:f6b+7/Pv/m/+P3huABlA0AQgAAPbD+AL","1601":"b:f6b+7/Pv/m/+P3huABlA0AQgAAPbD+AL","1616":"b:f6b+7/Pv/m/+P3huABlA0AQgAAPbD+AL","1652":"b:f6b+7/Pv/m/+P3huABlA0AQgAAPbD+AL"},"packages/@n8n/db/src/utils/transformers.ts":{"7":"b:f4T+zNL/xm94AwAAAAAAAAAAAADoB9EA","8":"b:f4T+zNL/xm94AwAAAAAAAAAAAADoB9EA","15":"b:f4T+zNL/xm94AwAAAAAAAAAAAADoB9EA","16":"b:f4T+zNL/xm94AwAAAAAAAAAAAADoB9EA","23":"b:f4T+zNL/xm94AwAAAAAAAAAAAADoB9EA","24":"b:f4T+zNL/xm94AwAAAAAAAAAAAADoB9EA","32":"b:f4T+zNL/xm94AwAAAAAAAAAAAADoB9EA","34":"b:f4T+zNL/xm94AwAAAAAAAAAAAADoB9EA","44":"b:f4T+zNL/xm94AwAAAAAAAAAAAADoB9EA","45":"b:f4T+zNL/xm94AwAAAAAAAAAAAADoB9EA"},"packages/@n8n/decorators/src/execution-lifecycle/lifecycle-metadata.ts":{"82":"b:f4DqwJKHBm94AAAAAAAAAAAAAADIB9AA","85":"b:f4DqwJKHBm94AAAAAAAAAAAAAADIB9AA","89":"b:f4DqwJKHBm94AAAAAAAAAAAAAADIB9AA"},"packages/@n8n/decorators/src/redactable.ts":{"14":"b:f4TexlAkxmko7n8MAAAAAAAAAADo9w8A","21":"b:f4TexlAkxmko7n8MAAAAAAAAAADo9w8A","46":"b:f4TexlAkxmko7n8MAAAAAAAAAADo9w8A","47":"b:f4TexlAkxmko7n8MAAAAAAAAAADo9w8A","53":"b:f4TexlAkxmko7n8MAAAAAAAAAADo9w8A","54":"b:f4TexlAkxmko7n8MAAAAAAAAAADo9w8A"},"packages/@n8n/errors/src/isolate.error.ts":{"2":"b:f4DqwIKHBm94AgAAAAAAAAAAAADoB4AA"},"packages/@n8n/expression-runtime/src/bridge/isolated-vm-bridge.ts":{"15":"b:f4Dq5JKHD294AgACgAAgAAKgAgHoB9AA","26":"b:f4Dq5JKHD294AgACgAAgAAKgAgHoB9AA","42":"b:f4Dq5JKHD294AgACgAAgAAKgAgHoB9AA","45":"b:f4Dq5JKHD294AgACgAAgAAKgAgHoB9AA","66":"b:f4Dq5JKHD294AgACgAAgAAKgAgHoB9AA","98":"b:f4Dq5JKHD294AgACgAAgAAKgAgHoB9AA","121":"b:f4Dq5JKHD294AgACgAAgAAKgAgHoB9AA","163":"b:f4Dq5JKHD294AgACgAAgAAKgAgHoB9AA","204":"b:f4Dq5JKHD294AgACgAAgAAKgAgHoB9AA","255":"b:f4Dq5JKHD294AgACgAAgAAKgAgHoB9AA","294":"b:f4Dq5JKHD294AgACgAAgAAKgAgHoB9AA","295":"b:f4Dq5JKHD294AgACgAAgAAKgAgHoB9AA","371":"b:f4Dq5JKHD294AgACgAAgAAKgAgHoB9AA","372":"b:f4Dq5JKHD294AgACgAAgAAKgAgHoB9AA","468":"b:f4Dq5JKHD294AgACgAAgAAKgAgHoB9AA","469":"b:f4Dq5JKHD294AgACgAAgAAKgAgHoB9AA","531":"b:f4Dq5JKHD294AgACgAAgAAKgAgHoB9AA","538":"b:f4Dq5JKHD294AgACgAAgAAKgAgHoB9AA","545":"b:f4Dq5JKHD294AgACgAAgAAKgAgHoB9AA","563":"b:f4Dq5JKHD294AgACgAAgAAKgAgHoB9AA","567":"b:f4Dq5JKHD294AgACgAAgAAKgAgHoB9AA","571":"b:f4Dq5JKHD294AgACgAAgAAKgAgHoB9AA","584":"b:f4Dq5JKHD294AgACgAAgAAKgAgHoB9AA","607":"b:f4Dq5JKHD294AgACgAAgAAKgAgHoB9AA","629":"b:f4Dq5JKHD294AgACgAAgAAKgAgHoB9AA","636":"b:f4Dq5JKHD294AgACgAAgAAKgAgHoB9AA","643":"b:f4Dq5JKHD294AgACgAAgAAKgAgHoB9AA","662":"b:f4Dq5JKHD294AgACgAAgAAKgAgHoB9AA","683":"b:f4Dq5JKHD294AgACgAAgAAKgAgHoB9AA","713":"b:f4Dq5JKHD294AgACgAAgAAKgAgHoB9AA","812":"b:f4Dq5JKHD294AgACgAAgAAKgAgHoB9AA","832":"b:f4Dq5JKHD294AgACgAAgAAKgAgHoB9AA","853":"b:f4Dq5JKHD294AgACgAAgAAKgAgHoB9AA"},"packages/@n8n/expression-runtime/src/evaluator/expression-evaluator.ts":{"19":"b:f4Dq5JKHD294AgACgAAgAAIAAADoB8AA","52":"b:f4Dq5JKHD294AgACgAAgAAIAAADoB8AA","54":"b:f4Dq5JKHD294AgACgAAgAAIAAADoB8AA","58":"b:f4Dq5JKHD294AgACgAAgAAIAAADoB8AA","64":"b:f4Dq5JKHD294AgACgAAgAAIAAADoB8AA","82":"b:f4Dq5JKHD294AgACgAAgAAIAAADoB8AA","92":"b:f4Dq5JKHD294AgACgAAgAAIAAADoB8AA","107":"b:f4Dq5JKHD294AgACgAAgAAIAAADoB8AA","135":"b:f4Dq5JKHD294AgACgAAgAAIAAADoB8AA","150":"b:f4Dq5JKHD294AgACgAAgAAIAAADoB8AA","157":"b:f4Dq5JKHD294AgACgAAgAAIAAADoB8AA","170":"b:f4Dq5JKHD294AgACgAAgAAIAAADoB8AA","184":"b:f4Dq5JKHD294AgACgAAgAAIAAADoB8AA","200":"b:f4Dq5JKHD294AgACgAAgAAIAAADoB8AA","207":"b:f4Dq5JKHD294AgACgAAgAAIAAADoB8AA"},"packages/@n8n/expression-runtime/src/evaluator/lru-cache.ts":{"4":"b:f4DIIAAEDUNQAAACgAAgAAIAAAAIAEAA","13":"b:f4DIIAAEDUNQAAACgAAgAAIAAAAIAEAA","21":"b:f4DIIAAEDUNQAAACgAAgAAIAAAAIAEAA","31":"b:f4DIIAAEDUNQAAACgAAgAAIAAAAIAEAA","35":"b:f4DIIAAEDUNQAAACgAAgAAIAAAAIAEAA"},"packages/@n8n/expression-runtime/src/pool/isolate-pool.ts":{"6":"b:f4DqxJKHBm94AgAAAAAAAAAAAADoB4AA","13":"b:f4DqxJKHBm94AgAAAAAAAAAAAADoB4AA","36":"b:f4DqxJKHBm94AgAAAAAAAAAAAADoB4AA","43":"b:f4DqxJKHBm94AgAAAAAAAAAAAADoB4AA","45":"b:f4DqxJKHBm94AgAAAAAAAAAAAADoB4AA","67":"b:f4DqxJKHBm94AgAAAAAAAAAAAADoB4AA","75":"b:f4DqxJKHBm94AgAAAAAAAAAAAADoB4AA","80":"b:f4DqxJKHBm94AgAAAAAAAAAAAADoB4AA","83":"b:f4DqxJKHBm94AgAAAAAAAAAAAADoB4AA","90":"b:f4DqxJKHBm94AgAAAAAAAAAAAADoB4AA","96":"b:f4DqxJKHBm94AgAAAAAAAAAAAADoB4AA","103":"b:f4DqxJKHBm94AgAAAAAAAAAAAADoB4AA","112":"b:f4DqxJKHBm94AgAAAAAAAAAAAADoB4AA","119":"b:f4DqxJKHBm94AgAAAAAAAAAAAADoB4AA"},"packages/@n8n/nodes-langchain/nodes/chains/ChainRetrievalQA/ChainRetrievalQa.node.ts":{"21":[2],"186":[2],"202":[2],"208":[2]},"packages/@n8n/nodes-langchain/nodes/chains/ChainRetrievalQA/processItem.ts":{"19":[2]},"packages/@n8n/nodes-langchain/nodes/code/Code.node.ts":{"67":[0,2],"122":[0,2],"126":[0,2],"136":[0,2],"146":[0,2],"150":[0,2],"151":[0,2],"152":[0,2]},"packages/@n8n/nodes-langchain/nodes/document_loaders/DocumentDefaultDataLoader/DocumentDefaultDataLoader.node.ts":{"21":[0,2],"38":[0,2],"329":[0,2]},"packages/@n8n/nodes-langchain/nodes/retrievers/RetrieverVectorStore/RetrieverVectorStore.node.ts":{"14":[2],"73":[2]},"packages/@n8n/nodes-langchain/nodes/text_splitters/TextSplitterCharacterTextSplitter/TextSplitterCharacterTextSplitter.node.ts":{"13":[0,2],"68":[0,2]},"packages/@n8n/nodes-langchain/nodes/trigger/ChatTrigger/ChatTrigger.node.ts":{"35":[19,1,2,3,4,5,8,10,57,129,6,13],"229":[19,1,2,3,4,5,8,10,57,129,6,13],"750":[19,1,2,3,4,5,8,10,57,129,6,13],"813":[19,1,2,3,4,5,8,10,57,129,6,13],"939":[19,1,2,3,4,5,8,10,57,129,6,13],"940":[19,1,2,3,4,5,8,10,57,129,6,13]},"packages/@n8n/nodes-langchain/nodes/trigger/ChatTrigger/GenericFunctions.ts":{"7":[19,1,2,3,4,5,8,10,57,129,6,13],"41":[19,1,2,3,4,5,8,10,57,129,6,13]},"packages/@n8n/nodes-langchain/nodes/trigger/ChatTrigger/error.ts":{"4":[19,1,2,3,4,5,8,10,57,129,6,13]},"packages/@n8n/nodes-langchain/nodes/trigger/ChatTrigger/templates.ts":{"5":[19,1,2,3,4,5,8,10,57,129,6,13],"21":[19,1,2,3,4,5,8,10,57,129,6,13],"26":[19,1,2,3,4,5,8,10,57,129,6,13],"27":[19,1,2,3,4,5,8,10,57,129,6,13],"42":[19,1,2,3,4,5,8,10,57,129,6,13],"43":[19,1,2,3,4,5,8,10,57,129,6,13],"46":[19,1,2,3,4,5,8,10,57,129,6,13],"55":[19,1,2,3,4,5,8,10,57,129,6,13],"62":[19,1,2,3,4,5,8,10,57,129,6,13]},"packages/@n8n/nodes-langchain/nodes/trigger/ChatTrigger/types.ts":{"8":[19,1,2,3,4,5,8,10,57,129,6,13],"12":[19,1,2,3,4,5,8,10,57,129,6,13]},"packages/@n8n/nodes-langchain/nodes/vector_store/VectorStoreInMemory/VectorStoreInMemory.node.ts":{"37":[0,2],"132":[0,2],"143":[0,2],"152":[0,2],"161":[0,2],"186":[0,2],"192":[0,2]},"packages/@n8n/nodes-langchain/utils/N8nTool.ts":{"11":[1,2,3,4,5,8,9,10,11,12,127,6,13,14],"25":[1,2,3,4,5,8,9,10,11,12,127,6,13,14],"28":[1,2,3,4,5,8,9,10,11,12,127,6,13,14],"33":[1,2,3,4,5,8,9,10,11,12,127,6,13,14],"50":[1,2,3,4,5,8,9,10,11,12,127,6,13,14],"57":[1,2,3,4,5,8,9,10,11,12,127,6,13,14],"62":[1,2,3,4,5,8,9,10,11,12,127,6,13,14]},"packages/@n8n/nodes-langchain/utils/descriptions.ts":{"28":[1,2,3,4,5,8,9,10,11,12,127,6,13,14],"54":[1,2,3,4,5,8,9,10,11,12,127,6,13,14],"72":[1,2,3,4,5,8,9,10,11,12,127,6,13,14]},"packages/@n8n/nodes-langchain/utils/helpers.ts":{"17":"b:fn8AAAAAAAAAAAAABAAAgAAAAAAAAAAA","64":"b:fn8AAAAAAAAAAAAABAAAgAAAAAAAAAAA","77":"b:fn8AAAAAAAAAAAAABAAAgAAAAAAAAAAA","81":"b:fn8AAAAAAAAAAAAABAAAgAAAAAAAAAAA","145":"b:fn8AAAAAAAAAAAAABAAAgAAAAAAAAAAA","147":"b:fn8AAAAAAAAAAAAABAAAgAAAAAAAAAAA","159":"b:fn8AAAAAAAAAAAAABAAAgAAAAAAAAAAA","180":"b:fn8AAAAAAAAAAAAABAAAgAAAAAAAAAAA","192":"b:fn8AAAAAAAAAAAAABAAAgAAAAAAAAAAA","213":"b:fn8AAAAAAAAAAAAABAAAgAAAAAAAAAAA","217":"b:fn8AAAAAAAAAAAAABAAAgAAAAAAAAAAA","221":"b:fn8AAAAAAAAAAAAABAAAgAAAAAAAAAAA","271":"b:fn8AAAAAAAAAAAAABAAAgAAAAAAAAAAA","289":"b:fn8AAAAAAAAAAAAABAAAgAAAAAAAAAAA","307":"b:fn8AAAAAAAAAAAAABAAAgAAAAAAAAAAA"},"packages/@n8n/nodes-langchain/utils/tracing.ts":{"19":[1,2,3,4,5,8,10,12,127,6,13],"83":[1,2,3,4,5,8,10,12,127,6,13]},"packages/@n8n/permissions/src/index.ts":{"20":"b:f4T+3tL1xnko/38MAAAAAAAAAADo9w8A","21":"b:f4T+3tL1xnko/38MAAAAAAAAAADo9w8A","25":"b:f4T+3tL1xnko/38MAAAAAAAAAADo9w8A","27":"b:f4T+3tL1xnko/38MAAAAAAAAAADo9w8A"},"packages/@n8n/permissions/src/roles/all-roles.ts":{"58":"b://Vf///5/7f///////////////8HEAEA","62":"b://Vf///5/7f///////////////8HEAEA","83":"b:f4Re3lKo1jEo/n8AAAAAAQAAAAAAEAEA"},"packages/@n8n/permissions/src/utilities/combine-scopes.ee.ts":{"16":"b://X////9///////////////////vNwkA","18":"b://X////9///////////////////vNwkA","23":"b:f4T+3tL1xnko738MAAAAAAAAAADoNwkA","26":"b:f4T+3tL1xnko738MAAAAAAAAAADoNwkA"},"packages/@n8n/permissions/src/utilities/get-global-scopes.ee.ts":{"8":"b:f4TexlAkxmko7n8MAAAAAAAAAADo9w8A","9":"b:f4TexlAkxmko7n8MAAAAAAAAAADo9w8A"},"packages/@n8n/permissions/src/utilities/get-role-scopes.ee.ts":{"5":"b://X////9///////////////////vNwkA","16":"b://X////9///////////////////vNwkA","19":"b:f4T+3tL1xnko738MAAAAAAAAAADoNwkA","30":"b:f4T+3tL1xnko738MAAAAAAAAAADoNwkA","36":"b:f4T+3tL1xnko738MAAAAAAAAAADoNwkA","38":"b:f4T+3tL1xnko738MAAAAAAAAAADoNwkA"},"packages/@n8n/permissions/src/utilities/has-global-scope.ee.ts":{"10":"b:f4TezlKkxmko7n8MAAAAAAAAAADoNwkA"},"packages/@n8n/permissions/src/utilities/has-scope.ee.ts":{"11":"b://Xf///9///////////////////vNwkA","20":"b://Xf///9///////P//////z////vNwkA","21":"b://Xf///9///////////////////vNwkA"},"packages/@n8n/tournament/src/ExpressionBuilder.ts":{"25":"b:///PfPJdj1fWA4hKgOEvsN7//xMfAHAB","29":"b:///PfPJdj1fWA4hKgOEvMN7//xMeAHAB","33":"b:///MJAAMjUNSAABKgCAjgEaovgMfAHAB","37":"b:f4rIIAAEDUNQAAACgCAgAAbQiAAYAHAB","50":"b:///PfPJdj1fWA4hKgOEvsN7//xMfAHAB","54":"b:f4rIIAAEDUNQAAACgCAhAAaAjAAYAHAB","58":"b:f4rIIAAEDUNQAAACgCAgAAaAiAAYAHAB","62":"b:///IJAAEDUNSAABKgCAjgEaojgAfAHAB","71":"b:///PfPJdj1fWA4hKgOEvsN7//xMfAHAB","75":"b:f7rIIAAEDUNQAAACgCAggAaAiAAYAHAB","88":"b:///PfPJdj1fWA4hKgOEvsN7//xMfAHAB","103":"b:///PfPJdj1fWA4hKgOEvsN7//xMfAHAB","110":"b:f4rMIAAEDUNQAAACgKAhAAagjgAYAHAB","136":"b:///PfPJdj1fWA4hKgOEvsN7//xMfAHAB","137":"b:f7rIIAAEDUNQAAACgCAggAaAiAAYAHAB","141":"b:f7rIIAAEDUNQAAACgCAggAaAiAAYAHAB","157":"b:///PfPJdj1fWA4hKgOEvsN7//xMfAHAB","158":"b:///PfPJdj1fWA4hKgOEvsN7//xMfAHAB","171":"b:///PfPJdj1fWA4hKgOEvsN7//xMfAHAB","188":"b:///PfPJdj1fWA4hKgOEvsN7//xMfAHAB","197":"b:///PfPJdj1fWA4hKgOEvsN7//xMfAHAB","198":"b:///PfPJdj1fWA4hKgOEvsN7//xMfAHAB","263":"b:f4rMIAAEDUNQAAACgKAhAAagjgAYAHAB"},"packages/@n8n/tournament/src/ExpressionSplitter.ts":{"19":"b://XPfPJdj1fWA4hIgOEvsN7//xMPAEAA","23":"b://XPfPJdj1fWA4hIgOEvsN7//xMPAEAA","27":"b://XPfPJdj1fWA4hIgOEvsN7//xMPAEAA","101":"b:f4DIIAAEDUNQAAAAgAAgAAIAAAAIAEAA","105":"b:f4DIIAAEDUNQAAAAgAAgAAIAAAAIAEAA","107":"b:f4DIIAAEDUNQAAAAgAAgAAIAAAAIAEAA"},"packages/@n8n/tournament/src/Parser.ts":{"5":"b://XPfPJdj1fWA4hIgOEvsN7//xMPAEAA"},"packages/@n8n/tournament/src/VariablePolyfill.ts":{"12":"b:/zXIIAAEDUNSAAACgAAiAAIACAAYAHAB","21":"b://XPfPJdj1fWA4hKgOEvsN7//xMfAHAB","32":"b://XPfPJdj1fWA4hKgOEvsN7//xMfAHAB","45":"b://XPfPJdj1fWA4hKgOEvsN7//xMfAHAB","72":"b://XPfPJdj1fWA4hKgOEvsN7//xMfAHAB","77":"b:fwDIIAAEDUNQAAACgAAgAAIACAAYAHAB","82":"b:/3XIJAAEDUNSAABKgAAjgEZ4DgAfAHAB","101":"b:fwDIIAAEDUNQAAACgAAgAAIACAAYAHAB","106":"b:/3XIJAAEDUNSAABKgAAjgEYoDgAfAHAB","111":"b:/3XIJAAEDUNSAABKgAAjgEYoDgAfAHAB","122":"b://XPfPJdj1fWA4hKgOEvsN7//xMfAHAB","127":"b:fwDIIAAEDUNQAAACgAAgAAIACAAYAHAB","130":"b://XPfPJdj1fWA4hKgOEvsN7//xMfAHAB"},"packages/@n8n/tournament/src/index.ts":{"16":"b://Xf///9//f/+/////////////8PAEAA","17":"b:f4DIIAAEDUNQAAAAgAAgAAIAAAAIAEAA","25":"b://Xf///9//f/+/////////////8PAEAA","29":"b://XPfPJdj1fWA4hIgOEvsN7//xMPAEAA","33":"b://XPfPJdj1fWA4hIgOEvsN7//xMPAEAA"},"packages/@n8n/utils/src/promise/deferred-promise.ts":{"10":"b:f4DqwJKHBm94AAAAAAAAAAAAAADIB9AA","12":"b:f4DqwJKHBm94AAAAAAAAAAAAAADIB9AA"},"packages/cli/src/abstract-server.ts":{"80":"b:RygAANoB0QEAAAKABQAAAgBAAAIAAAAA","113":"b:RygAANoB0QEAAAKABQAAAgBAAAIAAAAA","117":"b:RygAANoB0QEAAAKABQAAAgBAAAIAAAAA","125":"b:RygAANoB0QEAAAKABQAAAgBAAAIAAAAA","133":"b:RygAANoB0QEAAAKABQAAAgBAAAIAAAAA","137":"b:RygAANoB0QEAAAKABQAAAgBAAAIAAAAA","140":"b:RygAANoB0QEAAAKABQAAAgBAAAIAAAAA","144":"b:RygAANoB0QEAAAKABQAAAgBAAAIAAAAA","151":"b:RygAANoB0QEAAAKABQAAAgBAAAIAAAAA","157":"b:RygAANoB0QEAAAKABQAAAgBAAAIAAAAA","166":"b:RygAANoB0QEAAAKABQAAAgBAAAIAAAAA","174":"b:RygAANoB0QEAAAKABQAAAgBAAAIAAAAA","194":"b:RygAANoB0QEAAAKABQAAAgBAAAIAAAAA","224":"b:RygAANoB0QEAAAKABQAAAgBAAAIAAAAA","233":"b:RygAANoB0QEAAAKABQAAAgBAAAIAAAAA","306":"b:RygAANoB0QEAAAKABQAAAgBAAAIAAAAA","310":"b:RygAANoB0QEAAAKABQAAAgBAAAIAAAAA","349":"b:RygAANoB0QEAAAKABQAAAgBAAAIAAAAA","358":"b:RygAANoB0QEAAAKABQAAAgBAAAIAAAAA"},"packages/cli/src/active-executions.ts":{"46":"b:f4Dq4JKHDm94AAACgAAAAAIACADIB/AJ","55":"b:f4Dq4JKHDm94AAACgAAAAAIACADIB/AJ","62":"b:f4Dq4JKHDm94AAACgAAAAAIACADIB/AJ","165":"b:f4Dq4JKHDm94AAACgAAAAAIACADIB/AJ","169":"b:f4Dq4JKHDm94AAACgAAAAAIACADIB/AJ","190":"b:f4Dq4JKHDm94AAACgAAAAAIACADIB/AJ","194":"b:f4Dq4JKHDm94AAACgAAAAAIACADIB/AJ","201":"b:f4Dq4JKHDm94AAACgAAAAAIACADIB/AJ","207":"b:f4Dq4JKHDm94AAACgAAAAAIACADIB/AJ","216":"b:f4Dq4JKHDm94AAACgAAAAAIACADIB/AJ","246":"b:f4Dq4JKHDm94AAACgAAAAAIACADIB/AJ","268":"b:f4Dq4JKHDm94AAACgAAAAAIACADIB/AJ","286":"b:f4Dq4JKHDm94AAACgAAAAAIACADIB/AJ","293":"b:f4Dq4JKHDm94AAACgAAAAAIACADIB/AJ","313":"b:f4Dq4JKHDm94AAACgAAAAAIACADIB/AJ","317":"b:f4Dq4JKHDm94AAACgAAAAAIACADIB/AJ","321":"b:f4Dq4JKHDm94AAACgAAAAAIACADIB/AJ","325":"b:f4Dq4JKHDm94AAACgAAAAAIACADIB/AJ","330":"b:f4Dq4JKHDm94AAACgAAAAAIACADIB/AJ","365":"b:f4Dq4JKHDm94AAACgAAAAAIACADIB/AJ"},"packages/cli/src/auth/auth-handler.registry.ts":{"7":"b:f4TexlAkxmko7n8MAAAAAAAAAADo9w8A","20":"b:f4TexlAkxmko7n8MAAAAAAAAAADo9w8A","30":"b:f4TexlAkxmko7n8MAAAAAAAAAADo9w8A","82":"b:f4TexlAkxmko7n8MAAAAAAAAAADo9w8A","94":"b:f4TexlAkxmko7n8MAAAAAAAAAADo9w8A","112":"b:f4TexlAkxmko7n8MAAAAAAAAAADo9w8A"},"packages/cli/src/auth/auth.service.ts":{"42":"b:f4T+ztKn3m96738MAAAAAAAAAADo9w8A","73":"b:f4T+ztKn3m96738MAAAAAAAAAADo9w8A","122":"b:f4T+ztKn3m96738MAAAAAAAAAADo9w8A","127":"b:f4T+ztKn3m96738MAAAAAAAAAADo9w8A","185":"b:f4T+ztKn3m96738MAAAAAAAAAADo9w8A","194":"b:f4T+ztKn3m96738MAAAAAAAAAADo9w8A","202":"b:f4T+ztKn3m96738MAAAAAAAAAADo9w8A","206":"b:f4T+ztKn3m96738MAAAAAAAAAADo9w8A","210":"b:f4T+ztKn3m96738MAAAAAAAAAADo9w8A","214":"b:f4T+ztKn3m96738MAAAAAAAAAADo9w8A","230":"b:f4T+ztKn3m96738MAAAAAAAAAADo9w8A","255":"b:f4T+ztKn3m96738MAAAAAAAAAADo9w8A","276":"b:f4T+ztKn3m96738MAAAAAAAAAADo9w8A","283":"b:f4T+ztKn3m96738MAAAAAAAAAADo9w8A","309":"b:f4T+ztKn3m96738MAAAAAAAAAADo9w8A","319":"b:f4T+ztKn3m96738MAAAAAAAAAADo9w8A","335":"b:f4T+ztKn3m96738MAAAAAAAAAADo9w8A","336":"b:f4T+ztKn3m96738MAAAAAAAAAADo9w8A","341":"b:f4T+ztKn3m96738MAAAAAAAAAADo9w8A","358":"b:f4T+ztKn3m96738MAAAAAAAAAADo9w8A","389":"b:f4T+ztKn3m96738MAAAAAAAAAADo9w8A","419":"b:f4T+ztKn3m96738MAAAAAAAAAADo9w8A","424":"b:f4T+ztKn3m96738MAAAAAAAAAADo9w8A","434":"b:f4T+ztKn3m96738MAAAAAAAAAADo9w8A","468":"b:f4T+ztKn3m96738MAAAAAAAAAADo9w8A","476":"b:f4T+ztKn3m96738MAAAAAAAAAADo9w8A","481":"b:f4T+ztKn3m96738MAAAAAAAAAADo9w8A","491":"b:f4T+ztKn3m96738MAAAAAAAAAADo9w8A"},"packages/cli/src/auth/handlers/email.auth-handler.ts":{"16":"b:f4TexlAkxmko7n8MAAAAAAAAAADo9w8A","23":"b:f4TexlAkxmko7n8MAAAAAAAAAADo9w8A","35":"b:f4TexlAkxmko7n8MAAAAAAAAAADo9w8A"},"packages/cli/src/chat/chat-server.ts":{"21":"b:f4Re3FL4lgEo5w8AAAAAAAAAAAAAAAAA","23":"b:f4Re3FL4lgEo5w8AAAAAAAAAAAAAAAAA","24":"b:f4Re3FL4lgEo5w8AAAAAAAAAAAAAAAAA","28":"b:f4Re3FL4lgEo5w8AAAAAAAAAAAAAAAAA","34":"b:f4Re3FL4lgEo5w8AAAAAAAAAAAAAAAAA","39":"b:f4Re3FL4lgEo5w8AAAAAAAAAAAAAAAAA","42":"b:f4Re3FL4lgEo5w8AAAAAAAAAAAAAAAAA","51":"b:f4Re3FL4lgEo5w8AAAAAAAAAAAAAAAAA"},"packages/cli/src/chat/chat-service.ts":{"44":"b:X4AWjlop1HEoIm+dFQAAAAAAAAAAAgAA","47":"b:X4AWjlop1HEoIm+dFQAAAAAAAAAAAgAA","51":"b:X4AWjlop1HEoIm+dFQAAAAAAAAAAAgAA","63":"b:X4AWjlop1HEoIm+dFQAAAAAAAAAAAgAA","69":"b:X4AWjlop1HEoIm+dFQAAAAAAAAAAAgAA","74":"b:X4AWjlop1HEoIm+dFQAAAAAAAAAAAgAA","121":"b:X4AWjlop1HEoIm+dFQAAAAAAAAAAAgAA","123":"b:X4AWjlop1HEoIm+dFQAAAAAAAAAAAgAA","146":"b:X4AWjlop1HEoIm+dFQAAAAAAAAAAAgAA","176":"b:X4AWjlop1HEoIm+dFQAAAAAAAAAAAgAA","181":"b:X4AWjlop1HEoIm+dFQAAAAAAAAAAAgAA","190":"b:X4AWjlop1HEoIm+dFQAAAAAAAAAAAgAA","191":"b:X4AWjlop1HEoIm+dFQAAAAAAAAAAAgAA","237":"b:X4AWjlop1HEoIm+dFQAAAAAAAAAAAgAA","238":"b:X4AWjlop1HEoIm+dFQAAAAAAAAAAAgAA","266":"b:X4AWjlop1HEoIm+dFQAAAAAAAAAAAgAA","272":"b:X4AWjlop1HEoIm+dFQAAAAAAAAAAAgAA","298":"b:X4AWjlop1HEoIm+dFQAAAAAAAAAAAgAA","300":"b:X4AWjlop1HEoIm+dFQAAAAAAAAAAAgAA","307":"b:X4AWjlop1HEoIm+dFQAAAAAAAAAAAgAA","313":"b:X4AWjlop1HEoIm+dFQAAAAAAAAAAAgAA","318":"b:X4AWjlop1HEoIm+dFQAAAAAAAAAAAgAA","328":"b:X4AWjlop1HEoIm+dFQAAAAAAAAAAAgAA","335":"b:X4AWjlop1HEoIm+dFQAAAAAAAAAAAgAA","363":"b:X4AWjlop1HEoIm+dFQAAAAAAAAAAAgAA","379":"b:X4AWjlop1HEoIm+dFQAAAAAAAAAAAgAA"},"packages/cli/src/collaboration/collaboration.message.ts":{"71":"b:R4VezNL5lwEoAwAAAAAAAAAAAAAAAAAA"},"packages/cli/src/collaboration/collaboration.service.ts":{"34":"b:R6XezPP9n03sA4AAgAAAIAxNKAnaB8AC","43":"b:R6XezPP9n03sA4AAgAAAIAxNKAnaB8AC","44":"b:R6XezPP9n03sA4AAgAAAIAxNKAnaB8AC","69":"b:R6XezPP9n03sA4AAgAAAIAxNKAnaB8AC","78":"b:R6XezPP9n03sA4AAgAAAIAxNKAnaB8AC","94":"b:R6XezPP9n03sA4AAgAAAIAxNKAnaB8AC","110":"b:R6XezPP9n03sA4AAgAAAIAxNKAnaB8AC","133":"b:R6XezPP9n03sA4AAgAAAIAxNKAnaB8AC","137":"b:R6XezPP9n03sA4AAgAAAIAxNKAnaB8AC","143":"b:R6XezPP9n03sA4AAgAAAIAxNKAnaB8AC","145":"b:R6XezPP9n03sA4AAgAAAIAxNKAnaB8AC","155":"b:R6XezPP9n03sA4AAgAAAIAxNKAnaB8AC","183":"b:R6XezPP9n03sA4AAgAAAIAxNKAnaB8AC","200":"b:R6XezPP9n03sA4AAgAAAIAxNKAnaB8AC","211":"b:R6XezPP9n03sA4AAgAAAIAxNKAnaB8AC","217":"b:R6XezPP9n03sA4AAgAAAIAxNKAnaB8AC","232":"b:R6XezPP9n03sA4AAgAAAIAxNKAnaB8AC","234":"b:R6XezPP9n03sA4AAgAAAIAxNKAnaB8AC","247":"b:R6XezPP9n03sA4AAgAAAIAxNKAnaB8AC","249":"b:R6XezPP9n03sA4AAgAAAIAxNKAnaB8AC","263":"b:R6XezPP9n03sA4AAgAAAIAxNKAnaB8AC","270":"b:R6XezPP9n03sA4AAgAAAIAxNKAnaB8AC","301":"b:R6XezPP9n03sA4AAgAAAIAxNKAnaB8AC","307":"b:R6XezPP9n03sA4AAgAAAIAxNKAnaB8AC","327":"b:R6XezPP9n03sA4AAgAAAIAxNKAnaB8AC","329":"b:R6XezPP9n03sA4AAgAAAIAxNKAnaB8AC","347":"b:R6XezPP9n03sA4AAgAAAIAxNKAnaB8AC","361":"b:R6XezPP9n03sA4AAgAAAIAxNKAnaB8AC","375":"b:R6XezPP9n03sA4AAgAAAIAxNKAnaB8AC"},"packages/cli/src/collaboration/collaboration.state.ts":{"35":"b:R6XezNP9n02sA4AAgAAAAAxNKAnKB0AA","40":"b:R6XezNP9n02sA4AAgAAAAAxNKAnKB0AA","52":"b:R6XezNP9n02sA4AAgAAAAAxNKAnKB0AA","58":"b:R6XezNP9n02sA4AAgAAAAAxNKAnKB0AA","86":"b:R6XezNP9n02sA4AAgAAAAAxNKAnKB0AA","90":"b:R6XezNP9n02sA4AAgAAAAAxNKAnKB0AA","105":"b:R6XezNP9n02sA4AAgAAAAAxNKAnKB0AA","112":"b:R6XezNP9n02sA4AAgAAAAAxNKAnKB0AA","117":"b:R6XezNP9n02sA4AAgAAAAAxNKAnKB0AA","149":"b:R6XezNP9n02sA4AAgAAAAAxNKAnKB0AA","160":"b:R6XezNP9n02sA4AAgAAAAAxNKAnKB0AA","166":"b:R6XezNP9n02sA4AAgAAAAAxNKAnKB0AA","177":"b:R6XezNP9n02sA4AAgAAAAAxNKAnKB0AA","198":"b:R6XezNP9n02sA4AAgAAAAAxNKAnKB0AA","203":"b:R6XezNP9n02sA4AAgAAAAAxNKAnKB0AA","212":"b:R6XezNP9n02sA4AAgAAAAAxNKAnKB0AA"},"packages/cli/src/concurrency/concurrency-capacity-reservation.ts":{"21":"b:f4DqwJKHBm94AAAAAAAAAAAAAADIB9AA","23":"b:f4DqwJKHBm94AAAAAAAAAAAAAADIB9AA","30":"b:f4DqwJKHBm94AAAAAAAAAAAAAADIB9AA"},"packages/cli/src/concurrency/concurrency-control.service.ts":{"35":"b:f4DqwJKHBm94AAAAAAAAAAAAAADIB/AA","45":"b:f4DqwJKHBm94AAAAAAAAAAAAAADIB/AA","90":"b:f4DqwJKHBm94AAAAAAAAAAAAAADIB/AA","100":"b:f4DqwJKHBm94AAAAAAAAAAAAAADIB/AA","113":"b:f4DqwJKHBm94AAAAAAAAAAAAAADIB/AA","135":"b:f4DqwJKHBm94AAAAAAAAAAAAAADIB/AA","136":"b:f4DqwJKHBm94AAAAAAAAAAAAAADIB/AA","145":"b:f4DqwJKHBm94AAAAAAAAAAAAAADIB/AA","150":"b:f4DqwJKHBm94AAAAAAAAAAAAAADIB/AA","158":"b:f4DqwJKHBm94AAAAAAAAAAAAAADIB/AA","173":"b:f4DqwJKHBm94AAAAAAAAAAAAAADIB/AA","183":"b:f4DqwJKHBm94AAAAAAAAAAAAAADIB/AA","193":"b:f4DqwJKHBm94AAAAAAAAAAAAAADIB/AA","205":"b:f4DqwJKHBm94AAAAAAAAAAAAAADIB/AA","208":"b:f4DqwJKHBm94AAAAAAAAAAAAAADIB/AA","225":"b:f4DqwJKHBm94AAAAAAAAAAAAAADIB/AA","233":"b:f4DqwJKHBm94AAAAAAAAAAAAAADIB/AA","237":"b:f4DqwJKHBm94AAAAAAAAAAAAAADIB/AA","244":"b:f4DqwJKHBm94AAAAAAAAAAAAAADIB/AA"},"packages/cli/src/controller.registry.ts":{"37":"b:f4Xe/1qk9nkp/38ACQiAAAAAAALoNwgA","46":"b:f4Xe/1qk9nkp/38ACQiAAAAAAALoNwgA","54":"b:f4Xe/1qk9nkp/38ACQiAAAAAAALoNwgA","66":"b:f4Xe/1qk9nkp/38ACQiAAAAAAALoNwgA","92":"b:f4Xe/1qk9nkp/38ACQiAAAAAAALoNwgA","118":"b:f4Xe/1qk9nkp/38ACQiAAAAAAALoNwgA","123":"b:f4Xe/1qk9nkp/38ACQiAAAAAAALoNwgA","146":"b:f4Xe/1qk9nkp/38ACQiAAAAAAALoNwgA","226":"b:f4Xe/1qk9nkp/38ACQiAAAAAAALoNwgA","227":"b:f4Xe/1qk9nkp/38ACQiAAAAAAALoNwgA","236":"b:f4Xe/1qk9nkp/38ACQiAAAAAAALoNwgA","237":"b:f4Xe/1qk9nkp/38ACQiAAAAAAALoNwgA"},"packages/cli/src/controllers/active-workflows.controller.ts":{"8":"b:R4Re3tKp1gEoP3gAAAAAAAAAAAAAAAAA","11":"b:R4Re3tKp1gEoP3gAAAAAAAAAAAAAAAAA","16":"b:R4Re3tKp1gEoP3gAAAAAAAAAAAAAAAAA"},"packages/cli/src/controllers/auth.controller.ts":{"40":"b:f4Te3las1nko738MAAAAAQAAAADo9w8A","67":"b:f4Te3las1nko738MAAAAAQAAAADo9w8A","109":"b:f4Te3las1nko738MAAAAAQAAAADo9w8A","115":"b:f4Te3las1nko738MAAAAAQAAAADo9w8A","131":"b:f4Te3las1nko738MAAAAAQAAAADo9w8A","164":"b:f4Te3las1nko738MAAAAAQAAAADo9w8A","192":"b:f4Te3las1nko738MAAAAAQAAAADo9w8A","205":"b:f4Te3las1nko738MAAAAAQAAAADo9w8A","250":"b:f4Te3las1nko738MAAAAAQAAAADo9w8A","259":"b:f4Te3las1nko738MAAAAAQAAAADo9w8A","278":"b:f4Te3las1nko738MAAAAAQAAAADo9w8A"},"packages/cli/src/controllers/e2e.controller.ts":{"186":"b:f4TezlKs1nko738MAAAACQAAAADo9x8A","200":"b:f4TezlKs1nko738MAAAACQAAAADo9x8A","203":"b:f4TezlKs1nko738MAAAACQAAAADo9x8A","214":"b:f4TezlKs1nko738MAAAACQAAAADo9x8A","218":"b:f4TezlKs1nko738MAAAACQAAAADo9x8A","229":"b:f4TezlKs1nko738MAAAACQAAAADo9x8A","235":"b:f4TezlKs1nko738MAAAACQAAAADo9x8A","241":"b:f4TezlKs1nko738MAAAACQAAAADo9x8A","247":"b:f4TezlKs1nko738MAAAACQAAAADo9x8A","258":"b:f4TezlKs1nko738MAAAACQAAAADo9x8A","266":"b:f4TezlKs1nko738MAAAACQAAAADo9x8A","277":"b:f4TezlKs1nko738MAAAACQAAAADo9x8A","286":"b:f4TezlKs1nko738MAAAACQAAAADo9x8A","291":"b:f4TezlKs1nko738MAAAACQAAAADo9x8A","330":"b:f4TezlKs1nko738MAAAACQAAAADo9x8A","349":"b:f4TezlKs1nko738MAAAACQAAAADo9x8A","383":"b:f4TezlKs1nko738MAAAACQAAAADo9x8A","418":"b:f4TezlKs1nko738MAAAACQAAAADo9x8A","431":"b:f4TezlKs1nko738MAAAACQAAAADo9x8A","435":"b:f4TezlKs1nko738MAAAACQAAAADo9x8A","436":"b:f4TezlKs1nko738MAAAACQAAAADo9x8A","439":"b:f4TezlKs1nko738MAAAACQAAAADo9x8A","450":"b:f4TezlKs1nko738MAAAACQAAAADo9x8A","457":"b:f4TezlKs1nko738MAAAACQAAAADo9x8A","468":"b:f4TezlKs1nko738MAAAACQAAAADo9x8A","478":"b:f4TezlKs1nko738MAAAACQAAAADo9x8A","490":"b:f4TezlKs1nko738MAAAACQAAAADo9x8A","570":"b:f4TezlKs1nko738MAAAACQAAAADo9x8A","576":"b:f4TezlKs1nko738MAAAACQAAAADo9x8A","577":"b:f4TezlKs1nko738MAAAACQAAAADo9x8A","585":"b:f4TezlKs1nko738MAAAACQAAAADo9x8A","596":"b:f4TezlKs1nko738MAAAACQAAAADo9x8A","601":"b:f4TezlKs1nko738MAAAACQAAAADo9x8A","610":"b:f4TezlKs1nko738MAAAACQAAAADo9x8A","631":"b:f4TezlKs1nko738MAAAACQAAAADo9x8A","638":"b:f4TezlKs1nko738MAAAACQAAAADo9x8A","706":"b:f4TezlKs1nko738MAAAACQAAAADo9x8A"},"packages/cli/src/controllers/module-settings.controller.ts":{"7":"b:f4Re3lKo1jEo/38AAAAAAQAAAAAAEAAA","13":"b:f4Re3lKo1jEo/38AAAAAAQAAAAAAEAAA"},"packages/cli/src/controllers/project.controller.ts":{"43":"b:f4Vf/1+q9jGp/38ACxgAdQAAAAICMAAA","52":"b:f4Vf/1+q9jGp/38ACxgAdQAAAAICMAAA","73":"b:f4Vf/1+q9jGp/38ACxgAdQAAAAICMAAA","84":"b:f4Vf/1+q9jGp/38ACxgAdQAAAAICMAAA","101":"b:f4Vf/1+q9jGp/38ACxgAdQAAAAICMAAA","122":"b:f4Vf/1+q9jGp/38ACxgAdQAAAAICMAAA","135":"b:f4Vf/1+q9jGp/38ACxgAdQAAAAICMAAA","143":"b:f4Vf/1+q9jGp/38ACxgAdQAAAAICMAAA","159":"b:f4Vf/1+q9jGp/38ACxgAdQAAAAICMAAA","197":"b:f4Vf/1+q9jGp/38ACxgAdQAAAAICMAAA","210":"b:f4Vf/1+q9jGp/38ACxgAdQAAAAICMAAA","223":"b:f4Vf/1+q9jGp/38ACxgAdQAAAAICMAAA","234":"b:f4Vf/1+q9jGp/38ACxgAdQAAAAICMAAA","243":"b:f4Vf/1+q9jGp/38ACxgAdQAAAAICMAAA","253":"b:f4Vf/1+q9jGp/38ACxgAdQAAAAICMAAA","262":"b:f4Vf/1+q9jGp/38ACxgAdQAAAAICMAAA","280":"b:f4Vf/1+q9jGp/38ACxgAdQAAAAICMAAA","290":"b:f4Vf/1+q9jGp/38ACxgAdQAAAAICMAAA","313":"b:f4Vf/1+q9jGp/38ACxgAdQAAAAICMAAA","335":"b:f4Vf/1+q9jGp/38ACxgAdQAAAAICMAAA","350":"b:f4Vf/1+q9jGp/38ACxgAdQAAAAICMAAA","364":"b:f4Vf/1+q9jGp/38ACxgAdQAAAAICMAAA","376":"b:f4Vf/1+q9jGp/38ACxgAdQAAAAICMAAA","384":"b:f4Vf/1+q9jGp/38ACxgAdQAAAAICMAAA"},"packages/cli/src/controllers/role.controller.ts":{"39":"b:f4Re3lKo1jEo/38AAAAAAQAAAAAAEAAA","44":"b:f4Re3lKo1jEo/38AAAAAAQAAAAAAEAAA","51":"b:f4Re3lKo1jEo/38AAAAAAQAAAAAAEAAA","58":"b:f4Re3lKo1jEo/38AAAAAAQAAAAAAEAAA","59":"b:f4Re3lKo1jEo/38AAAAAAQAAAAAAEAAA","60":"b:f4Re3lKo1jEo/38AAAAAAQAAAAAAEAAA","61":"b:f4Re3lKo1jEo/38AAAAAAQAAAAAAEAAA","66":"b:f4Re3lKo1jEo/38AAAAAAQAAAAAAEAAA","79":"b:f4Re3lKo1jEo/38AAAAAAQAAAAAAEAAA","92":"b:f4Re3lKo1jEo/38AAAAAAQAAAAAAEAAA","102":"b:f4Re3lKo1jEo/38AAAAAAQAAAAAAEAAA","113":"b:f4Re3lKo1jEo/38AAAAAAQAAAAAAEAAA","132":"b:f4Re3lKo1jEo/38AAAAAAQAAAAAAEAAA","150":"b:f4Re3lKo1jEo/38AAAAAAQAAAAAAEAAA"},"packages/cli/src/controllers/tags.controller.ts":{"20":"b:X4Re3lKo1jGo/38AAQAQAQAAAAQBEAAA","24":"b:X4Re3lKo1jGo/38AAQAQAQAAAAQBEAAA","30":"b:X4Re3lKo1jGo/38AAQAQAQAAAAQBEAAA","43":"b:X4Re3lKo1jGo/38AAQAQAQAAAAQBEAAA","56":"b:X4Re3lKo1jGo/38AAQAQAQAAAAQBEAAA"},"packages/cli/src/controllers/users.controller.ts":{"55":"b:R4Re3lOo1hEo/38AAAAABAAAAAAAAAAA","82":"b:R4Re3lOo1hEo/38AAAAABAAAAAAAAAAA","106":"b:R4Re3lOo1hEo/38AAAAABAAAAAAAAAAA","115":"b:R4Re3lOo1hEo/38AAAAABAAAAAAAAAAA","128":"b:R4Re3lOo1hEo/38AAAAABAAAAAAAAAAA","135":"b:R4Re3lOo1hEo/38AAAAABAAAAAAAAAAA","153":"b:R4Re3lOo1hEo/38AAAAABAAAAAAAAAAA","175":"b:R4Re3lOo1hEo/38AAAAABAAAAAAAAAAA","203":"b:R4Re3lOo1hEo/38AAAAABAAAAAAAAAAA","224":"b:R4Re3lOo1hEo/38AAAAABAAAAAAAAAAA","301":"b:R4Re3lOo1hEo/38AAAAABAAAAAAAAAAA","318":"b:R4Re3lOo1hEo/38AAAAABAAAAAAAAAAA","341":"b:R4Re3lOo1hEo/38AAAAABAAAAAAAAAAA"},"packages/cli/src/credentials/credentials-finder.service.ts":{"13":"b:f8de3NLx1gEpJwgACAAAEAAAEAAgIAAA","22":"b:f8de3NLx1gEpJwgACAAAEAAAEAAgIAAA","34":"b:f8de3NLx1gEpJwgACAAAEAAAEAAgIAAA","41":"b:f8de3NLx1gEpJwgACAAAEAAAEAAgIAAA","55":"b:f8de3NLx1gEpJwgACAAAEAAAEAAgIAAA","71":"b:f8de3NLx1gEpJwgACAAAEAAAEAAgIAAA","76":"b:f8de3NLx1gEpJwgACAAAEAAAEAAgIAAA","78":"b:f8de3NLx1gEpJwgACAAAEAAAEAAgIAAA","80":"b:f8de3NLx1gEpJwgACAAAEAAAEAAgIAAA","92":"b:f8de3NLx1gEpJwgACAAAEAAAEAAgIAAA","132":"b:f8de3NLx1gEpJwgACAAAEAAAEAAgIAAA","191":"b:f8de3NLx1gEpJwgACAAAEAAAEAAgIAAA","223":"b:f8de3NLx1gEpJwgACAAAEAAAEAAgIAAA","234":"b:f8de3NLx1gEpJwgACAAAEAAAEAAgIAAA","236":"b:f8de3NLx1gEpJwgACAAAEAAAEAAgIAAA","253":"b:f8de3NLx1gEpJwgACAAAEAAAEAAgIAAA","287":"b:f8de3NLx1gEpJwgACAAAEAAAEAAgIAAA","301":"b:f8de3NLx1gEpJwgACAAAEAAAEAAgIAAA","324":"b:f8de3NLx1gEpJwgACAAAEAAAEAAgIAAA"},"packages/cli/src/credentials/credentials.controller.ts":{"54":"b:f8Vf/9Px/jGpP3ggSgACMQAAEAAgIAAA","71":"b:f8Vf/9Px/jGpP3ggSgACMQAAEAAgIAAA","86":"b:f8Vf/9Px/jGpP3ggSgACMQAAEAAgIAAA","96":"b:f8Vf/9Px/jGpP3ggSgACMQAAEAAgIAAA","104":"b:f8Vf/9Px/jGpP3ggSgACMQAAEAAgIAAA","118":"b:f8Vf/9Px/jGpP3ggSgACMQAAEAAgIAAA","147":"b:f8Vf/9Px/jGpP3ggSgACMQAAEAAgIAAA","160":"b:f8Vf/9Px/jGpP3ggSgACMQAAEAAgIAAA","204":"b:f8Vf/9Px/jGpP3ggSgACMQAAEAAgIAAA","372":"b:f8Vf/9Px/jGpP3ggSgACMQAAEAAgIAAA","406":"b:f8Vf/9Px/jGpP3ggSgACMQAAEAAgIAAA","449":"b:f8Vf/9Px/jGpP3ggSgACMQAAEAAgIAAA","455":"b:f8Vf/9Px/jGpP3ggSgACMQAAEAAgIAAA","471":"b:f8Vf/9Px/jGpP3ggSgACMQAAEAAgIAAA","472":"b:f8Vf/9Px/jGpP3ggSgACMQAAEAAgIAAA","475":"b:f8Vf/9Px/jGpP3ggSgACMQAAEAAgIAAA","476":"b:f8Vf/9Px/jGpP3ggSgACMQAAEAAgIAAA","500":"b:f8Vf/9Px/jGpP3ggSgACMQAAEAAgIAAA","540":"b:f8Vf/9Px/jGpP3ggSgACMQAAEAAgIAAA","547":"b:f8Vf/9Px/jGpP3ggSgACMQAAEAAgIAAA"},"packages/cli/src/credentials/credentials.service.ts":{"161":"b:f+V///P5/3WtP3hgiwkSdUAAAAAgIIAA","193":"b:f+V///P5/3WtP3hgiwkSdUAAAAAgIIAA","197":"b:f+V///P5/3WtP3hgiwkSdUAAAAAgIIAA","201":"b:f+V///P5/3WtP3hgiwkSdUAAAAAgIIAA","206":"b:f+V///P5/3WtP3hgiwkSdUAAAAAgIIAA","214":"b:f+V///P5/3WtP3hgiwkSdUAAAAAgIIAA","227":"b:f+V///P5/3WtP3hgiwkSdUAAAAAgIIAA","228":"b:f+V///P5/3WtP3hgiwkSdUAAAAAgIIAA","237":"b:f+V///P5/3WtP3hgiwkSdUAAAAAgIIAA","268":"b:f+V///P5/3WtP3hgiwkSdUAAAAAgIIAA","337":"b:f+V///P5/3WtP3hgiwkSdUAAAAAgIIAA","398":"b:f+V///P5/3WtP3hgiwkSdUAAAAAgIIAA","481":"b:f+V///P5/3WtP3hgiwkSdUAAAAAgIIAA","522":"b:f+V///P5/3WtP3hgiwkSdUAAAAAgIIAA","558":"b:f+V///P5/3WtP3hgiwkSdUAAAAAgIIAA","572":"b:f+V///P5/3WtP3hgiwkSdUAAAAAgIIAA","574":"b:f+V///P5/3WtP3hgiwkSdUAAAAAgIIAA","575":"b:f+V///P5/3WtP3hgiwkSdUAAAAAgIIAA","579":"b:f+V///P5/3WtP3hgiwkSdUAAAAAgIIAA","582":"b:f+V///P5/3WtP3hgiwkSdUAAAAAgIIAA","587":"b:f+V///P5/3WtP3hgiwkSdUAAAAAgIIAA","590":"b:f+V///P5/3WtP3hgiwkSdUAAAAAgIIAA","594":"b:f+V///P5/3WtP3hgiwkSdUAAAAAgIIAA","619":"b:f+V///P5/3WtP3hgiwkSdUAAAAAgIIAA","634":"b:f+V///P5/3WtP3hgiwkSdUAAAAAgIIAA","635":"b:f+V///P5/3WtP3hgiwkSdUAAAAAgIIAA","640":"b:f+V///P5/3WtP3hgiwkSdUAAAAAgIIAA","645":"b:f+V///P5/3WtP3hgiwkSdUAAAAAgIIAA","647":"b:f+V///P5/3WtP3hgiwkSdUAAAAAgIIAA","648":"b:f+V///P5/3WtP3hgiwkSdUAAAAAgIIAA","653":"b:f+V///P5/3WtP3hgiwkSdUAAAAAgIIAA","654":"b:f+V///P5/3WtP3hgiwkSdUAAAAAgIIAA","658":"b:f+V///P5/3WtP3hgiwkSdUAAAAAgIIAA","677":"b:f+V///P5/3WtP3hgiwkSdUAAAAAgIIAA","684":"b:f+V///P5/3WtP3hgiwkSdUAAAAAgIIAA","697":"b:f+V///P5/3WtP3hgiwkSdUAAAAAgIIAA","714":"b:f+V///P5/3WtP3hgiwkSdUAAAAAgIIAA","744":"b:f+V///P5/3WtP3hgiwkSdUAAAAAgIIAA","769":"b:f+V///P5/3WtP3hgiwkSdUAAAAAgIIAA","813":"b:f+V///P5/3WtP3hgiwkSdUAAAAAgIIAA","840":"b:f+V///P5/3WtP3hgiwkSdUAAAAAgIIAA","863":"b:f+V///P5/3WtP3hgiwkSdUAAAAAgIIAA","884":"b:f+V///P5/3WtP3hgiwkSdUAAAAAgIIAA","892":"b:f+V///P5/3WtP3hgiwkSdUAAAAAgIIAA","925":"b:f+V///P5/3WtP3hgiwkSdUAAAAAgIIAA","951":"b:f+V///P5/3WtP3hgiwkSdUAAAAAgIIAA","965":"b:f+V///P5/3WtP3hgiwkSdUAAAAAgIIAA","1021":"b:f+V///P5/3WtP3hgiwkSdUAAAAAgIIAA","1038":"b:f+V///P5/3WtP3hgiwkSdUAAAAAgIIAA","1052":"b:f+V///P5/3WtP3hgiwkSdUAAAAAgIIAA","1106":"b:f+V///P5/3WtP3hgiwkSdUAAAAAgIIAA","1144":"b:f+V///P5/3WtP3hgiwkSdUAAAAAgIIAA","1148":"b:f+V///P5/3WtP3hgiwkSdUAAAAAgIIAA","1160":"b:f+V///P5/3WtP3hgiwkSdUAAAAAgIIAA","1187":"b:f+V///P5/3WtP3hgiwkSdUAAAAAgIIAA","1201":"b:f+V///P5/3WtP3hgiwkSdUAAAAAgIIAA","1215":"b:f+V///P5/3WtP3hgiwkSdUAAAAAgIIAA","1230":"b:f+V///P5/3WtP3hgiwkSdUAAAAAgIIAA","1274":"b:f+V///P5/3WtP3hgiwkSdUAAAAAgIIAA","1289":"b:f+V///P5/3WtP3hgiwkSdUAAAAAgIIAA","1290":"b:f+V///P5/3WtP3hgiwkSdUAAAAAgIIAA","1293":"b:f+V///P5/3WtP3hgiwkSdUAAAAAgIIAA","1302":"b:f+V///P5/3WtP3hgiwkSdUAAAAAgIIAA","1305":"b:f+V///P5/3WtP3hgiwkSdUAAAAAgIIAA","1318":"b:f+V///P5/3WtP3hgiwkSdUAAAAAgIIAA","1327":"b:f+V///P5/3WtP3hgiwkSdUAAAAAgIIAA","1352":"b:f+V///P5/3WtP3hgiwkSdUAAAAAgIIAA","1364":"b:f+V///P5/3WtP3hgiwkSdUAAAAAgIIAA","1381":"b:f+V///P5/3WtP3hgiwkSdUAAAAAgIIAA","1406":"b:f+V///P5/3WtP3hgiwkSdUAAAAAgIIAA","1491":"b:f+V///P5/3WtP3hgiwkSdUAAAAAgIIAA","1493":"b:f+V///P5/3WtP3hgiwkSdUAAAAAgIIAA","1512":"b:f+V///P5/3WtP3hgiwkSdUAAAAAgIIAA","1521":"b:f+V///P5/3WtP3hgiwkSdUAAAAAgIIAA","1529":"b:f+V///P5/3WtP3hgiwkSdUAAAAAgIIAA","1530":"b:f+V///P5/3WtP3hgiwkSdUAAAAAgIIAA","1541":"b:f+V///P5/3WtP3hgiwkSdUAAAAAgIIAA","1542":"b:f+V///P5/3WtP3hgiwkSdUAAAAAgIIAA","1547":"b:f+V///P5/3WtP3hgiwkSdUAAAAAgIIAA","1554":"b:f+V///P5/3WtP3hgiwkSdUAAAAAgIIAA","1562":"b:f+V///P5/3WtP3hgiwkSdUAAAAAgIIAA","1583":"b:f+V///P5/3WtP3hgiwkSdUAAAAAgIIAA","1587":"b:f+V///P5/3WtP3hgiwkSdUAAAAAgIIAA","1604":"b:f+V///P5/3WtP3hgiwkSdUAAAAAgIIAA","1629":"b:f+V///P5/3WtP3hgiwkSdUAAAAAgIIAA","1639":"b:f+V///P5/3WtP3hgiwkSdUAAAAAgIIAA","1671":"b:f+V///P5/3WtP3hgiwkSdUAAAAAgIIAA","1685":"b:f+V///P5/3WtP3hgiwkSdUAAAAAgIIAA","1713":"b:f+V///P5/3WtP3hgiwkSdUAAAAAgIIAA","1725":"b:f+V///P5/3WtP3hgiwkSdUAAAAAgIIAA","1736":"b:f+V///P5/3WtP3hgiwkSdUAAAAAgIIAA","1802":"b:f+V///P5/3WtP3hgiwkSdUAAAAAgIIAA","1860":"b:f+V///P5/3WtP3hgiwkSdUAAAAAgIIAA","1892":"b:f+V///P5/3WtP3hgiwkSdUAAAAAgIIAA","1901":"b:f+V///P5/3WtP3hgiwkSdUAAAAAgIIAA","1931":"b:f+V///P5/3WtP3hgiwkSdUAAAAAgIIAA"},"packages/cli/src/environments.ee/variables/variables.service.ee.ts":{"32":"b:f4DqxJKHhm84AgAAAACAAAAAAADoB4AA","40":"b:f4DqxJKHhm84AgAAAACAAAAAAADoB4AA","47":"b:f4DqxJKHhm84AgAAAACAAAAAAADoB4AA","70":"b:f4DqxJKHhm84AgAAAACAAAAAAADoB4AA","75":"b:f4DqxJKHhm84AgAAAACAAAAAAADoB4AA","79":"b:f4DqxJKHhm84AgAAAACAAAAAAADoB4AA","85":"b:f4DqxJKHhm84AgAAAACAAAAAAADoB4AA","87":"b:f4DqxJKHhm84AgAAAACAAAAAAADoB4AA","94":"b:f4DqxJKHhm84AgAAAACAAAAAAADoB4AA","100":"b:f4DqxJKHhm84AgAAAACAAAAAAADoB4AA","110":"b:f4DqxJKHhm84AgAAAACAAAAAAADoB4AA","117":"b:f4DqxJKHhm84AgAAAACAAAAAAADoB4AA","125":"b:f4DqxJKHhm84AgAAAACAAAAAAADoB4AA","129":"b:f4DqxJKHhm84AgAAAACAAAAAAADoB4AA","147":"b:f4DqxJKHhm84AgAAAACAAAAAAADoB4AA","175":"b:f4DqxJKHhm84AgAAAACAAAAAAADoB4AA","180":"b:f4DqxJKHhm84AgAAAACAAAAAAADoB4AA","185":"b:f4DqxJKHhm84AgAAAACAAAAAAADoB4AA","193":"b:f4DqxJKHhm84AgAAAACAAAAAAADoB4AA","204":"b:f4DqxJKHhm84AgAAAACAAAAAAADoB4AA","206":"b:f4DqxJKHhm84AgAAAACAAAAAAADoB4AA","209":"b:f4DqxJKHhm84AgAAAACAAAAAAADoB4AA","216":"b:f4DqxJKHhm84AgAAAACAAAAAAADoB4AA","223":"b:f4DqxJKHhm84AgAAAACAAAAAAADoB4AA","265":"b:f4DqxJKHhm84AgAAAACAAAAAAADoB4AA"},"packages/cli/src/eventbus/event-message-classes/abstract-event-message.ts":{"10":"b:f4TexlAkxuko7n8MAAAAAAAAAADo9w8A","12":"b:f4TexlAkxuko7n8MAAAAAAAAAADo9w8A","16":"b:f4TexlAkxuko7n8MAAAAAAAAAADo9w8A","28":"b:f4TexlAkxuko7n8MAAAAAAAAAADo9w8A","48":"b:f4TexlAkxuko7n8MAAAAAAAAAADo9w8A","59":"b:f4TexlAkxuko7n8MAAAAAAAAAADo9w8A","72":"b:f4TexlAkxuko7n8MAAAAAAAAAADo9w8A","101":"b:f4TexlAkxuko7n8MAAAAAAAAAADo9w8A","108":"b:f4TexlAkxuko7n8MAAAAAAAAAADo9w8A","113":"b:f4TexlAkxuko7n8MAAAAAAAAAADo9w8A","124":"b:f4TexlAkxuko7n8MAAAAAAAAAADo9w8A","135":"b:f4TexlAkxuko7n8MAAAAAAAAAADo9w8A","139":"b:f4TexlAkxuko7n8MAAAAAAAAAADo9w8A"},"packages/cli/src/eventbus/event-message-classes/event-message-ai-node.ts":{"34":[0,1,2,3,4,5,6],"42":[0,1,2,3,4,5,6],"47":[0,1,2,3,4,5,6]},"packages/cli/src/eventbus/event-message-classes/event-message-confirm.ts":{"19":"b:f4TexlAkxmko7n8MAAAAAAAAAADo9w8A","25":"b:f4TexlAkxmko7n8MAAAAAAAAAADo9w8A","36":"b:f4TexlAkxmko7n8MAAAAAAAAAADo9w8A"},"packages/cli/src/eventbus/event-message-classes/event-message-node.ts":{"35":"b:f4DqwJKHBm94AAAAAAAAAAAAAADIB9AA","43":"b:f4DqwJKHBm94AAAAAAAAAAAAAADIB9AA","48":"b:f4DqwJKHBm94AAAAAAAAAAAAAADIB9AA"},"packages/cli/src/eventbus/event-message-classes/event-message-runner.ts":{"24":"b:RQIggAACAAB4AIAAAAAAAAgABoAAABAA","32":"b:RQIggAACAAB4AIAAAAAAAAgABoAAABAA","37":"b:RQIggAACAAB4AIAAAAAAAAgABoAAABAA"},"packages/cli/src/eventbus/event-message-classes/event-message-workflow.ts":{"36":"b:f4DqwJKHBm94AAAAAAAAAAAAAADIB9AA","44":"b:f4DqwJKHBm94AAAAAAAAAAAAAADIB9AA","49":"b:f4DqwJKHBm94AAAAAAAAAAAAAADIB9AA"},"packages/cli/src/eventbus/message-event-bus/message-event-bus.ts":{"58":"b:f4LqwJKHBm94AIAAAAAAAAgABoDIB9AA","77":"b:f4LqwJKHBm94AIAAAAAAAAgABoDIB9AA","117":"b:f4LqwJKHBm94AIAAAAAAAAgABoDIB9AA","126":"b:f4LqwJKHBm94AIAAAAAAAAgABoDIB9AA","137":"b:f4LqwJKHBm94AIAAAAAAAAgABoDIB9AA","140":"b:f4LqwJKHBm94AIAAAAAAAAgABoDIB9AA","150":"b:f4LqwJKHBm94AIAAAAAAAAgABoDIB9AA","161":"b:f4LqwJKHBm94AIAAAAAAAAgABoDIB9AA","168":"b:f4LqwJKHBm94AIAAAAAAAAgABoDIB9AA","191":"b:f4LqwJKHBm94AIAAAAAAAAgABoDIB9AA","195":"b:f4LqwJKHBm94AIAAAAAAAAgABoDIB9AA","196":"b:f4LqwJKHBm94AIAAAAAAAAgABoDIB9AA","201":"b:f4LqwJKHBm94AIAAAAAAAAgABoDIB9AA","207":"b:f4LqwJKHBm94AIAAAAAAAAgABoDIB9AA","213":"b:f4LqwJKHBm94AIAAAAAAAAgABoDIB9AA","219":"b:f4LqwJKHBm94AIAAAAAAAAgABoDIB9AA","224":"b:f4LqwJKHBm94AIAAAAAAAAgABoDIB9AA","238":"b:f4LqwJKHBm94AIAAAAAAAAgABoDIB9AA","249":"b:f4LqwJKHBm94AIAAAAAAAAgABoDIB9AA","253":"b:f4LqwJKHBm94AIAAAAAAAAgABoDIB9AA","257":"b:f4LqwJKHBm94AIAAAAAAAAgABoDIB9AA","261":"b:f4LqwJKHBm94AIAAAAAAAAgABoDIB9AA","265":"b:f4LqwJKHBm94AIAAAAAAAAgABoDIB9AA","269":"b:f4LqwJKHBm94AIAAAAAAAAgABoDIB9AA","273":"b:f4LqwJKHBm94AIAAAAAAAAgABoDIB9AA","284":"b:f4LqwJKHBm94AIAAAAAAAAgABoDIB9AA","350":"b:f4LqwJKHBm94AIAAAAAAAAgABoDIB9AA","369":"b:f4LqwJKHBm94AIAAAAAAAAgABoDIB9AA","388":"b:f4LqwJKHBm94AIAAAAAAAAgABoDIB9AA"},"packages/cli/src/eventbus/message-event-bus-writer/message-event-bus-log-writer.ts":{"67":"b:f4TexlAkxmko7n8MAAAAAAAAAADo9w8A","72":"b:f4TexlAkxmko7n8MAAAAAAAAAADo9w8A","82":"b:f4TexlAkxmko7n8MAAAAAAAAAADo9w8A","110":"b:f4TexlAkxmko7n8MAAAAAAAAAADo9w8A","116":"b:f4TexlAkxmko7n8MAAAAAAAAAADo9w8A","122":"b:f4TexlAkxmko7n8MAAAAAAAAAADo9w8A","126":"b:f4TexlAkxmko7n8MAAAAAAAAAADo9w8A","132":"b:f4TexlAkxmko7n8MAAAAAAAAAADo9w8A","142":"b:f4TexlAkxmko7n8MAAAAAAAAAADo9w8A","153":"b:f4TexlAkxmko7n8MAAAAAAAAAADo9w8A","162":"b:f4TexlAkxmko7n8MAAAAAAAAAADo9w8A","169":"b:f4TexlAkxmko7n8MAAAAAAAAAADo9w8A","175":"b:f4TexlAkxmko7n8MAAAAAAAAAADo9w8A","184":"b:f4TexlAkxmko7n8MAAAAAAAAAADo9w8A","205":"b:f4TexlAkxmko7n8MAAAAAAAAAADo9w8A","213":"b:f4TexlAkxmko7n8MAAAAAAAAAADo9w8A","241":"b:f4TexlAkxmko7n8MAAAAAAAAAADo9w8A","291":"b:f4TexlAkxmko7n8MAAAAAAAAAADo9w8A","326":"b:f4TexlAkxmko7n8MAAAAAAAAAADo9w8A","333":"b:f4TexlAkxmko7n8MAAAAAAAAAADo9w8A","341":"b:f4TexlAkxmko7n8MAAAAAAAAAADo9w8A","345":"b:f4TexlAkxmko7n8MAAAAAAAAAADo9w8A","353":"b:f4TexlAkxmko7n8MAAAAAAAAAADo9w8A","366":"b:f4TexlAkxmko7n8MAAAAAAAAAADo9w8A","369":"b:f4TexlAkxmko7n8MAAAAAAAAAADo9w8A","380":"b:f4TexlAkxmko7n8MAAAAAAAAAADo9w8A","406":"b:f4TexlAkxmko7n8MAAAAAAAAAADo9w8A","410":"b:f4TexlAkxmko7n8MAAAAAAAAAADo9w8A","414":"b:f4TexlAkxmko7n8MAAAAAAAAAADo9w8A","418":"b:f4TexlAkxmko7n8MAAAAAAAAAADo9w8A","422":"b:f4TexlAkxmko7n8MAAAAAAAAAADo9w8A","433":"b:f4TexlAkxmko7n8MAAAAAAAAAADo9w8A"},"packages/cli/src/events/relays/event-relay.ts":{"8":"b:f4TexlAkxmko7n8MAAAAAAAAAADo9w8A","10":"b:f4TexlAkxmko7n8MAAAAAAAAAADo9w8A","18":"b:f4TexlAkxmko7n8MAAAAAAAAAADo9w8A"},"packages/cli/src/events/relays/log-streaming.event-relay.ts":{"15":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","19":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","34":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","42":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","44":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","45":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","46":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","47":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","48":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","49":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","50":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","51":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","52":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","53":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","54":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","55":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","56":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","57":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","58":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","59":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","60":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","61":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","62":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","63":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","64":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","65":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","66":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","67":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","68":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","69":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","70":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","71":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","72":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","73":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","74":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","75":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","76":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","77":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","78":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","79":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","80":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","81":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","82":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","83":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","85":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","86":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","87":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","88":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","90":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","91":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","93":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","95":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","97":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","98":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","100":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","101":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","102":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","103":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","104":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","105":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","106":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","107":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","108":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","109":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","110":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","111":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","112":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","113":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","114":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","115":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","116":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","117":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","118":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","119":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","120":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","121":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","122":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","123":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","124":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","125":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","126":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","127":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","128":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","129":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","130":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","131":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","132":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","133":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","134":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","135":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","136":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","137":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","138":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","139":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","140":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","141":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","142":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","143":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","144":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","151":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","159":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","167":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","175":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","183":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","195":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","203":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","211":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","219":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","232":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","250":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","263":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","284":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","320":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","384":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","407":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","428":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","440":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","460":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","485":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","493":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","501":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","509":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","517":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","525":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","537":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","545":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","552":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","567":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","575":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","583":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","597":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","605":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","613":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","625":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","637":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","645":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","653":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","664":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","672":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","679":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","688":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","702":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","710":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","718":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","729":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","738":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","747":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","756":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","765":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","774":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","783":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","797":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","808":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","816":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","827":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","834":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","843":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","861":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","876":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","886":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","904":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","919":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","946":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","955":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","962":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","969":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","976":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","983":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","990":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","997":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","1004":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","1011":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","1018":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","1025":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","1032":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","1039":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","1050":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","1057":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","1068":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","1075":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","1082":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","1094":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","1137":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","1152":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","1159":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","1166":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","1173":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","1180":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","1187":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","1194":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","1201":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","1212":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","1230":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","1245":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","1259":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P","1272":"b:f/f+/vP//n9/7/8OjgGC9g4QLtv69/8P"},"packages/cli/src/execution-lifecycle/execution-lifecycle-hooks.ts":{"54":"b:f4DqwJKHD296AAACgAEAAAAKAAHIB/AJ","62":"b:f4DqwJKHD296AAACgAEAAAAKAAHIB/AJ","77":"b:f4DqwJKHD296AAACgAEAAAAKAAHIB/AJ","91":"b:f4DqwJKHD296AAACgAEAAAAKAAHIB/AJ","106":"b:f4DqwJKHD296AAACgAEAAAAKAAHIB/AJ","120":"b:f4DqwJKHD296AAACgAEAAAAKAAHIB/AJ","145":"b:f4DqwJKHD296AAACgAEAAAAKAAHIB/AJ","154":"b:f4DqwJKHD296AAACgAEAAAAKAAHIB/AJ","164":"b:f4DqwJKHD296AAACgAEAAAAKAAHIB/AJ","206":"b:f4DqwJKHD296AAACgAEAAAAKAAHIB/AJ","208":"b:f4DqwJKHD296AAACgAEAAAAKAAHIB/AJ","210":"b:f4DqwJKHD296AAACgAEAAAAKAAHIB/AJ","220":"b:f4DqwJKHD296AAACgAEAAAAKAAHIB/AJ","222":"b:f4DqwJKHD296AAACgAEAAAAKAAHIB/AJ","238":"b:f4DqwJKHD296AAACgAEAAAAKAAHIB/AJ","261":"b:f4DqwJKHD296AAACgAEAAAAKAAHIB/AJ","275":"b:f4DqwJKHD296AAACgAEAAAAKAAHIB/AJ","292":"b:f4DqwJKHD296AAACgAEAAAAKAAHIB/AJ","310":"b:f4DqwJKHD296AAACgAEAAAAKAAHIB/AJ","388":"b:f4DqwJKHD296AAACgAEAAAAKAAHIB/AJ","448":"b:f4DqwJKHD296AAACgAEAAAAKAAHIB/AJ","469":"b:f4DqwJKHD296AAACgAEAAAAKAAHIB/AJ","472":"b:f4DqwJKHD296AAACgAEAAAAKAAHIB/AJ","475":"b:f4DqwJKHD296AAACgAEAAAAKAAHIB/AJ","485":"b:f4DqwJKHD296AAACgAEAAAAKAAHIB/AJ","490":"b:f4DqwJKHD296AAACgAEAAAAKAAHIB/AJ","502":"b:f4DqwJKHD296AAACgAEAAAAKAAHIB/AJ","503":"b:f4DqwJKHD296AAACgAEAAAAKAAHIB/AJ","508":"b:f4DqwJKHD296AAACgAEAAAAKAAHIB/AJ","513":"b:f4DqwJKHD296AAACgAEAAAAKAAHIB/AJ","524":"b:f4DqwJKHD296AAACgAEAAAAKAAHIB/AJ","543":"b:f4DqwJKHD296AAACgAEAAAAKAAHIB/AJ","554":"b:f4DqwJKHD296AAACgAEAAAAKAAHIB/AJ","674":"b:f4DqwJKHD296AAACgAEAAAAKAAHIB/AJ","682":"b:f4DqwJKHD296AAACgAEAAAAKAAHIB/AJ","760":"b:f4DqwJKHD296AAACgAEAAAAKAAHIB/AJ","785":"b:f4DqwJKHD296AAACgAEAAAAKAAHIB/AJ","817":"b:f4DqwJKHD296AAACgAEAAAAKAAHIB/AJ","848":"b:f4DqwJKHD296AAACgAEAAAAKAAHIB/AJ","905":"b:f4DqwJKHD296AAACgAEAAAAKAAHIB/AJ"},"packages/cli/src/execution-lifecycle/restore-binary-data-id.ts":{"30":"b:f4DqwJOHBm94AAACAAAAAAAAAADYB/AJ","64":"b:f4DqwJOHBm94AAACAAAAAAAAAADYB/AJ","78":"b:f4DqwJOHBm94AAACAAAAAAAAAADYB/AJ"},"packages/cli/src/execution-lifecycle/shared/shared-hook-functions.ts":{"13":"b:f4DqwJKHBm94AAAAAAAAAAAAAADIB9AA","29":"b:f4DqwJKHBm94AAAAAAAAAAAAAADIB9AA","78":"b:f4DqwJKHBm94AAAAAAAAAAAAAADIB9AA","94":"b:f4DqwJKHBm94AAAAAAAAAAAAAADIB9AA"},"packages/cli/src/execution-lifecycle/to-save-settings.ts":{"20":"b:f4DqwJKHBm94AAAAAAAAAAAAAADIB9AA"},"packages/cli/src/executions/execution-data/db-store.ts":{"14":"b:f4DqwJKHD294AAAAAAAAAAwEAADIB9AA","16":"b:f4DqwJKHD294AAAAAAAAAAwEAADIB9AA","32":"b:f4DqwJKHD294AAAAAAAAAAwEAADIB9AA","50":"b:f4DqwJKHD294AAAAAAAAAAwEAADIB9AA","58":"b:f4DqwJKHD294AAAAAAAAAAwEAADIB9AA","71":"b:f4DqwJKHD294AAAAAAAAAAwEAADIB9AA","82":"b:f4DqwJKHD294AAAAAAAAAAwEAADIB9AA","96":"b:f4DqwJKHD294AAAAAAAAAAwEAADIB9AA","100":"b:f4DqwJKHD294AAAAAAAAAAwEAADIB9AA"},"packages/cli/src/executions/execution-persistence.ts":{"76":"b:f4DqwJOHD296AAACgAAAAA4EAAjKB/AA","93":"b:f4DqwJOHD296AAACgAAAAA4EAAjKB/AA","110":"b:f4DqwJOHD296AAACgAAAAA4EAAjKB/AA","116":"b:f4DqwJOHD296AAACgAAAAA4EAAjKB/AA","171":"b:f4DqwJOHD296AAACgAAAAA4EAAjKB/AA","208":"b:f4DqwJOHD296AAACgAAAAA4EAAjKB/AA","228":"b:f4DqwJOHD296AAACgAAAAA4EAAjKB/AA","300":"b:f4DqwJOHD296AAACgAAAAA4EAAjKB/AA","398":"b:f4DqwJOHD296AAACgAAAAA4EAAjKB/AA","452":"b:f4DqwJOHD296AAACgAAAAA4EAAjKB/AA","453":"b:f4DqwJOHD296AAACgAAAAA4EAAjKB/AA","461":"b:f4DqwJOHD296AAACgAAAAA4EAAjKB/AA","462":"b:f4DqwJOHD296AAACgAAAAA4EAAjKB/AA","467":"b:f4DqwJOHD296AAACgAAAAA4EAAjKB/AA","476":"b:f4DqwJOHD296AAACgAAAAA4EAAjKB/AA","507":"b:f4DqwJOHD296AAACgAAAAA4EAAjKB/AA","508":"b:f4DqwJOHD296AAACgAAAAA4EAAjKB/AA","515":"b:f4DqwJOHD296AAACgAAAAA4EAAjKB/AA","525":"b:f4DqwJOHD296AAACgAAAAA4EAAjKB/AA","540":"b:f4DqwJOHD296AAACgAAAAA4EAAjKB/AA","555":"b:f4DqwJOHD296AAACgAAAAA4EAAjKB/AA","596":"b:f4DqwJOHD296AAACgAAAAA4EAAjKB/AA","606":"b:f4DqwJOHD296AAACgAAAAA4EAAjKB/AA","611":"b:f4DqwJOHD296AAACgAAAAA4EAAjKB/AA","612":"b:f4DqwJOHD296AAACgAAAAA4EAAjKB/AA","617":"b:f4DqwJOHD296AAACgAAAAA4EAAjKB/AA","633":"b:f4DqwJOHD296AAACgAAAAA4EAAjKB/AA","647":"b:f4DqwJOHD296AAACgAAAAA4EAAjKB/AA","677":"b:f4DqwJOHD296AAACgAAAAA4EAAjKB/AA","691":"b:f4DqwJOHD296AAACgAAAAA4EAAjKB/AA","710":"b:f4DqwJOHD296AAACgAAAAA4EAAjKB/AA","711":"b:f4DqwJOHD296AAACgAAAAA4EAAjKB/AA","714":"b:f4DqwJOHD296AAACgAAAAA4EAAjKB/AA","727":"b:f4DqwJOHD296AAACgAAAAA4EAAjKB/AA","737":"b:f4DqwJOHD296AAACgAAAAA4EAAjKB/AA","765":"b:f4DqwJOHD296AAACgAAAAA4EAAjKB/AA","787":"b:f4DqwJOHD296AAACgAAAAA4EAAjKB/AA","790":"b:f4DqwJOHD296AAACgAAAAA4EAAjKB/AA","828":"b:f4DqwJOHD296AAACgAAAAA4EAAjKB/AA","847":"b:f4DqwJOHD296AAACgAAAAA4EAAjKB/AA","865":"b:f4DqwJOHD296AAACgAAAAA4EAAjKB/AA","891":"b:f4DqwJOHD296AAACgAAAAA4EAAjKB/AA","915":"b:f4DqwJOHD296AAACgAAAAA4EAAjKB/AA","927":"b:f4DqwJOHD296AAACgAAAAA4EAAjKB/AA","937":"b:f4DqwJOHD296AAACgAAAAA4EAAjKB/AA","944":"b:f4DqwJOHD296AAACgAAAAA4EAAjKB/AA","965":"b:f4DqwJOHD296AAACgAAAAA4EAAjKB/AA","978":"b:f4DqwJOHD296AAACgAAAAA4EAAjKB/AA","983":"b:f4DqwJOHD296AAACgAAAAA4EAAjKB/AA","992":"b:f4DqwJOHD296AAACgAAAAA4EAAjKB/AA","1008":"b:f4DqwJOHD296AAACgAAAAA4EAAjKB/AA","1028":"b:f4DqwJOHD296AAACgAAAAA4EAAjKB/AA","1041":"b:f4DqwJOHD296AAACgAAAAA4EAAjKB/AA","1055":"b:f4DqwJOHD296AAACgAAAAA4EAAjKB/AA","1067":"b:f4DqwJOHD296AAACgAAAAA4EAAjKB/AA","1083":"b:f4DqwJOHD296AAACgAAAAA4EAAjKB/AA","1099":"b:f4DqwJOHD296AAACgAAAAA4EAAjKB/AA","1105":"b:f4DqwJOHD296AAACgAAAAA4EAAjKB/AA","1115":"b:f4DqwJOHD296AAACgAAAAA4EAAjKB/AA"},"packages/cli/src/executions/execution-redaction-proxy.service.ts":{"13":"b:R4DqwJKBDw96AAAAAAAAAAAAAAAAAAAA","17":"b:R4DqwJKBDw96AAAAAAAAAAAAAAAAAAAA","27":"b:R4DqwJKBDw96AAAAAAAAAAAAAAAAAAAA"},"packages/cli/src/executions/execution.service.ee.ts":{"13":"b:R4BqwJKBDwd6AAAAAAAAAAAAAAAAAAAA","19":"b:R4BqwJKBDwd6AAAAAAAAAAAAAAAAAAAA"},"packages/cli/src/executions/execution.service.ts":{"115":"b:R7Dq6JKDn09+AwACgAAAAAgMAGDQB+AD","143":"b:R7Dq6JKDn09+AwACgAAAAAgMAGDQB+AD","151":"b:R7Dq6JKDn09+AwACgAAAAAgMAGDQB+AD","205":"b:R7Dq6JKDn09+AwACgAAAAAgMAGDQB+AD","237":"b:R7Dq6JKDn09+AwACgAAAAAgMAGDQB+AD","403":"b:R7Dq6JKDn09+AwACgAAAAAgMAGDQB+AD","408":"b:R7Dq6JKDn09+AwACgAAAAAgMAGDQB+AD","442":"b:R7Dq6JKDn09+AwACgAAAAAgMAGDQB+AD","479":"b:R7Dq6JKDn09+AwACgAAAAAgMAGDQB+AD","499":"b:R7Dq6JKDn09+AwACgAAAAAgMAGDQB+AD","502":"b:R7Dq6JKDn09+AwACgAAAAAgMAGDQB+AD","538":"b:R7Dq6JKDn09+AwACgAAAAAgMAGDQB+AD","546":"b:R7Dq6JKDn09+AwACgAAAAAgMAGDQB+AD","563":"b:R7Dq6JKDn09+AwACgAAAAAgMAGDQB+AD","580":"b:R7Dq6JKDn09+AwACgAAAAAgMAGDQB+AD","591":"b:R7Dq6JKDn09+AwACgAAAAAgMAGDQB+AD","624":"b:R7Dq6JKDn09+AwACgAAAAAgMAGDQB+AD","645":"b:R7Dq6JKDn09+AwACgAAAAAgMAGDQB+AD","655":"b:R7Dq6JKDn09+AwACgAAAAAgMAGDQB+AD","675":"b:R7Dq6JKDn09+AwACgAAAAAgMAGDQB+AD","697":"b:R7Dq6JKDn09+AwACgAAAAAgMAGDQB+AD","715":"b:R7Dq6JKDn09+AwACgAAAAAgMAGDQB+AD","716":"b:R7Dq6JKDn09+AwACgAAAAAgMAGDQB+AD","727":"b:R7Dq6JKDn09+AwACgAAAAAgMAGDQB+AD","765":"b:R7Dq6JKDn09+AwACgAAAAAgMAGDQB+AD","777":"b:R7Dq6JKDn09+AwACgAAAAAgMAGDQB+AD"},"packages/cli/src/executions/executions.controller.ts":{"19":"b:R4DqwJKDD096AAACAAAAAAAAACDQB+AD","26":"b:R4DqwJKDD096AAACAAAAAAAAACDQB+AD","31":"b:R4DqwJKDD096AAACAAAAAAAAACDQB+AD","75":"b:R4DqwJKDD096AAACAAAAAAAAACDQB+AD","86":"b:R4DqwJKDD096AAACAAAAAAAAACDQB+AD","101":"b:R4DqwJKDD096AAACAAAAAAAAACDQB+AD","117":"b:R4DqwJKDD096AAACAAAAAAAAACDQB+AD","128":"b:R4DqwJKDD096AAACAAAAAAAAACDQB+AD","137":"b:R4DqwJKDD096AAACAAAAAAAAACDQB+AD","146":"b:R4DqwJKDD096AAACAAAAAAAAACDQB+AD"},"packages/cli/src/executions/pre-execution-checks/credentials-permission-checker.ts":{"16":"b:f4DqwJOHDm94AAAAgAAAAAAAgADIB9AA","25":"b:f4DqwJOHDm94AAAAgAAAAAAAgADIB9AA","36":"b:f4DqwJOHDm94AAAAgAAAAAAAgADIB9AA","46":"b:f4DqwJOHDm94AAAAgAAAAAAAgADIB9AA","63":"b:f4DqwJOHDm94AAAAgAAAAAAAgADIB9AA","90":"b:f4DqwJOHDm94AAAAgAAAAAAAgADIB9AA","102":"b:f4DqwJOHDm94AAAAgAAAAAAAgADIB9AA","141":"b:f4DqwJOHDm94AAAAgAAAAAAAgADIB9AA","157":"b:f4DqwJOHDm94AAAAgAAAAAAAgADIB9AA","171":"b:f4DqwJOHDm94AAAAgAAAAAAAgADIB9AA","172":"b:f4DqwJOHDm94AAAAgAAAAAAAgADIB9AA","199":"b:f4DqwJOHDm94AAAAgAAAAAAAgADIB9AA"},"packages/cli/src/executions/sum-binary-data-bytes.ts":{"13":"b:f4DqwJKHBm94AAAAAAAAAAAAAADIB9AA","37":"b:f4DqwJKHBm94AAAAAAAAAAAAAADIB9AA","64":"b:f4DqwJKHBm94AAAAAAAAAAAAAADIB9AA"},"packages/cli/src/expression-observability/expression-observability.provider.ts":{"46":"b:f4Dq5JKHD294AgACgAAgAAIAAgDoB8AA","49":"b:f4Dq5JKHD294AgACgAAgAAIAAgDoB8AA","70":"b:f4Dq5JKHD294AgACgAAgAAIAAgDoB8AA","71":"b:f4Dq5JKHD294AgACgAAgAAIAAgDoB8AA","72":"b:f4Dq5JKHD294AgACgAAgAAIAAgDoB8AA","76":"b:f4Dq5JKHD294AgACgAAgAAIAAgDoB8AA","80":"b:f4Dq5JKHD294AgACgAAgAAIAAgDoB8AA","81":"b:f4Dq5JKHD294AgACgAAgAAIAAgDoB8AA","82":"b:f4Dq5JKHD294AgACgAAgAAIAAgDoB8AA","83":"b:f4Dq5JKHD294AgACgAAgAAIAAgDoB8AA","87":"b:f4Dq5JKHD294AgACgAAgAAIAAgDoB8AA","119":"b:f4Dq5JKHD294AgACgAAgAAIAAgDoB8AA","126":"b:f4Dq5JKHD294AgACgAAgAAIAAgDoB8AA","134":"b:f4Dq5JKHD294AgACgAAgAAIAAgDoB8AA","142":"b:f4Dq5JKHD294AgACgAAgAAIAAgDoB8AA","152":"b:f4Dq5JKHD294AgACgAAgAAIAAgDoB8AA","183":"b:f4Dq5JKHD294AgACgAAgAAIAAgDoB8AA","191":"b:f4Dq5JKHD294AgACgAAgAAIAAgDoB8AA","200":"b:f4Dq5JKHD294AgACgAAgAAIAAgDoB8AA","204":"b:f4Dq5JKHD294AgACgAAgAAIAAgDoB8AA","208":"b:f4Dq5JKHD294AgACgAAgAAIAAgDoB8AA","209":"b:f4Dq5JKHD294AgACgAAgAAIAAgDoB8AA","213":"b:f4Dq5JKHD294AgACgAAgAAIAAgDoB8AA"},"packages/cli/src/external-hooks.ts":{"51":"b:R4T+zNP/xmkoAwAAAAAAAAAAAADoBwEA","172":"b:R4T+zNP/xmkoAwAAAAAAAAAAAADoBwEA","189":"b:R4T+zNP/xmkoAwAAAAAAAAAAAADoBwEA","209":"b:R4T+zNP/xmkoAwAAAAAAAAAAAADoBwEA","220":"b:R4T+zNP/xmkoAwAAAAAAAAAAAADoBwEA","224":"b:R4T+zNP/xmkoAwAAAAAAAAAAAADoBwEA"},"packages/cli/src/generic-helpers.ts":{"13":"b:R4T+zNP3xmkoAwAAAgAAAAAAAADoJwEA","25":"b:R4T+zNP3xmkoAwAAAgAAAAAAAADoJwEA"},"packages/cli/src/license/license.controller.ts":{"15":"b:R4ReztKp1gEoL3gMAAAAAAAAAAAAAAAA","22":"b:R4ReztKp1gEoL3gMAAAAAAAAAAAAAAAA","28":"b:R4ReztKp1gEoL3gMAAAAAAAAAAAAAAAA","45":"b:R4ReztKp1gEoL3gMAAAAAAAAAAAAAAAA","61":"b:R4ReztKp1gEoL3gMAAAAAAAAAAAAAAAA","73":"b:R4ReztKp1gEoL3gMAAAAAAAAAAAAAAAA","78":"b:R4ReztKp1gEoL3gMAAAAAAAAAAAAAAAA"},"packages/cli/src/license/license.service.ts":{"30":"b:R4ReztKp1gEoL3gMAAAAAAAAAAAAAAAA","45":"b:R4ReztKp1gEoL3gMAAAAAAAAAAAAAAAA","70":"b:R4ReztKp1gEoL3gMAAAAAAAAAAAAAAAA","85":"b:R4ReztKp1gEoL3gMAAAAAAAAAAAAAAAA","128":"b:R4ReztKp1gEoL3gMAAAAAAAAAAAAAAAA","136":"b:R4ReztKp1gEoL3gMAAAAAAAAAAAAAAAA","162":"b:R4ReztKp1gEoL3gMAAAAAAAAAAAAAAAA","180":"b:R4ReztKp1gEoL3gMAAAAAAAAAAAAAAAA","195":"b:R4ReztKp1gEoL3gMAAAAAAAAAAAAAAAA","209":"b:R4ReztKp1gEoL3gMAAAAAAAAAAAAAAAA"},"packages/cli/src/license.ts":{"48":"b:f4TeztKt1mko738MAAAAAAAAAADo9w8A","58":"b:f4TeztKt1mko738MAAAAAAAAAADo9w8A","77":"b:f4TeztKt1mko738MAAAAAAAAAADo9w8A","78":"b:f4TeztKt1mko738MAAAAAAAAAADo9w8A","80":"b:f4TeztKt1mko738MAAAAAAAAAADo9w8A","81":"b:f4TeztKt1mko738MAAAAAAAAAADo9w8A","83":"b:f4TeztKt1mko738MAAAAAAAAAADo9w8A","84":"b:f4TeztKt1mko738MAAAAAAAAAADo9w8A","86":"b:f4TeztKt1mko738MAAAAAAAAAADo9w8A","87":"b:f4TeztKt1mko738MAAAAAAAAAADo9w8A","89":"b:f4TeztKt1mko738MAAAAAAAAAADo9w8A","90":"b:f4TeztKt1mko738MAAAAAAAAAADo9w8A","91":"b:f4TeztKt1mko738MAAAAAAAAAADo9w8A","115":"b:f4TeztKt1mko738MAAAAAAAAAADo9w8A","117":"b:f4TeztKt1mko738MAAAAAAAAAADo9w8A","143":"b:f4TeztKt1mko738MAAAAAAAAAADo9w8A","157":"b:f4TeztKt1mko738MAAAAAAAAAADo9w8A","172":"b:f4TeztKt1mko738MAAAAAAAAAADo9w8A","177":"b:f4TeztKt1mko738MAAAAAAAAAADo9w8A","182":"b:f4TeztKt1mko738MAAAAAAAAAADo9w8A","189":"b:f4TeztKt1mko738MAAAAAAAAAADo9w8A","206":"b:f4TeztKt1mko738MAAAAAAAAAADo9w8A","208":"b:f4TeztKt1mko738MAAAAAAAAAADo9w8A","216":"b:f4TeztKt1mko738MAAAAAAAAAADo9w8A","229":"b:f4TeztKt1mko738MAAAAAAAAAADo9w8A","239":"b:f4TeztKt1mko738MAAAAAAAAAADo9w8A","248":"b:f4TeztKt1mko738MAAAAAAAAAADo9w8A","257":"b:f4TeztKt1mko738MAAAAAAAAAADo9w8A","267":"b:f4TeztKt1mko738MAAAAAAAAAADo9w8A","280":"b:f4TeztKt1mko738MAAAAAAAAAADo9w8A","284":"b:f4TeztKt1mko738MAAAAAAAAAADo9w8A","288":"b:f4TeztKt1mko738MAAAAAAAAAADo9w8A","293":"b:f4TeztKt1mko738MAAAAAAAAAADo9w8A","298":"b:f4TeztKt1mko738MAAAAAAAAAADo9w8A","303":"b:f4TeztKt1mko738MAAAAAAAAAADo9w8A","308":"b:f4TeztKt1mko738MAAAAAAAAAADo9w8A","313":"b:f4TeztKt1mko738MAAAAAAAAAADo9w8A","318":"b:f4TeztKt1mko738MAAAAAAAAAADo9w8A","323":"b:f4TeztKt1mko738MAAAAAAAAAADo9w8A","328":"b:f4TeztKt1mko738MAAAAAAAAAADo9w8A","333":"b:f4TeztKt1mko738MAAAAAAAAAADo9w8A","338":"b:f4TeztKt1mko738MAAAAAAAAAADo9w8A","343":"b:f4TeztKt1mko738MAAAAAAAAAADo9w8A","348":"b:f4TeztKt1mko738MAAAAAAAAAADo9w8A","353":"b:f4TeztKt1mko738MAAAAAAAAAADo9w8A","358":"b:f4TeztKt1mko738MAAAAAAAAAADo9w8A","363":"b:f4TeztKt1mko738MAAAAAAAAAADo9w8A","368":"b:f4TeztKt1mko738MAAAAAAAAAADo9w8A","373":"b:f4TeztKt1mko738MAAAAAAAAAADo9w8A","378":"b:f4TeztKt1mko738MAAAAAAAAAADo9w8A","383":"b:f4TeztKt1mko738MAAAAAAAAAADo9w8A","388":"b:f4TeztKt1mko738MAAAAAAAAAADo9w8A","393":"b:f4TeztKt1mko738MAAAAAAAAAADo9w8A","398":"b:f4TeztKt1mko738MAAAAAAAAAADo9w8A","403":"b:f4TeztKt1mko738MAAAAAAAAAADo9w8A","407":"b:f4TeztKt1mko738MAAAAAAAAAADo9w8A","411":"b:f4TeztKt1mko738MAAAAAAAAAADo9w8A","415":"b:f4TeztKt1mko738MAAAAAAAAAADo9w8A","425":"b:f4TeztKt1mko738MAAAAAAAAAADo9w8A","435":"b:f4TeztKt1mko738MAAAAAAAAAADo9w8A","438":"b:f4TeztKt1mko738MAAAAAAAAAADo9w8A","442":"b:f4TeztKt1mko738MAAAAAAAAAADo9w8A","449":"b:f4TeztKt1mko738MAAAAAAAAAADo9w8A","454":"b:f4TeztKt1mko738MAAAAAAAAAADo9w8A","459":"b:f4TeztKt1mko738MAAAAAAAAAADo9w8A","464":"b:f4TeztKt1mko738MAAAAAAAAAADo9w8A","469":"b:f4TeztKt1mko738MAAAAAAAAAADo9w8A","477":"b:f4TeztKt1mko738MAAAAAAAAAADo9w8A","481":"b:f4TeztKt1mko738MAAAAAAAAAADo9w8A","485":"b:f4TeztKt1mko738MAAAAAAAAAADo9w8A","493":"b:f4TeztKt1mko738MAAAAAAAAAADo9w8A","501":"b:f4TeztKt1mko738MAAAAAAAAAADo9w8A","516":"b:f4TeztKt1mko738MAAAAAAAAAADo9w8A","531":"b:f4TeztKt1mko738MAAAAAAAAAADo9w8A","540":"b:f4TeztKt1mko738MAAAAAAAAAADo9w8A","545":"b:f4TeztKt1mko738MAAAAAAAAAADo9w8A","550":"b:f4TeztKt1mko738MAAAAAAAAAADo9w8A","554":"b:f4TeztKt1mko738MAAAAAAAAAADo9w8A","560":"b:f4TeztKt1mko738MAAAAAAAAAADo9w8A","563":"b:f4TeztKt1mko738MAAAAAAAAAADo9w8A"},"packages/cli/src/load-nodes-and-credentials.ts":{"63":"b:f7Xu3NL/zn14Rw8AHOEIQIBIAgDoJwEA","72":"b:f7Xu3NL/zn14Rw8AHOEIQIBIAgDoJwEA","133":"b:f7Xu3NL/zn14Rw8AHOEIQIBIAgDoJwEA","137":"b:f7Xu3NL/zn14Rw8AHOEIQIBIAgDoJwEA","153":"b:f7Xu3NL/zn14Rw8AHOEIQIBIAgDoJwEA","168":"b:f7Xu3NL/zn14Rw8AHOEIQIBIAgDoJwEA","172":"b:f7Xu3NL/zn14Rw8AHOEIQIBIAgDoJwEA","176":"b:f7Xu3NL/zn14Rw8AHOEIQIBIAgDoJwEA","180":"b:f7Xu3NL/zn14Rw8AHOEIQIBIAgDoJwEA","184":"b:f7Xu3NL/zn14Rw8AHOEIQIBIAgDoJwEA","188":"b:f7Xu3NL/zn14Rw8AHOEIQIBIAgDoJwEA","229":"b:f7Xu3NL/zn14Rw8AHOEIQIBIAgDoJwEA","236":"b:f7Xu3NL/zn14Rw8AHOEIQIBIAgDoJwEA","240":"b:f7Xu3NL/zn14Rw8AHOEIQIBIAgDoJwEA","258":"b:f7Xu3NL/zn14Rw8AHOEIQIBIAgDoJwEA","288":"b:f7Xu3NL/zn14Rw8AHOEIQIBIAgDoJwEA","289":"b:f7Xu3NL/zn14Rw8AHOEIQIBIAgDoJwEA","304":"b:f7Xu3NL/zn14Rw8AHOEIQIBIAgDoJwEA","315":"b:f7Xu3NL/zn14Rw8AHOEIQIBIAgDoJwEA","321":"b:f7Xu3NL/zn14Rw8AHOEIQIBIAgDoJwEA","330":"b:f7Xu3NL/zn14Rw8AHOEIQIBIAgDoJwEA","341":"b:f7Xu3NL/zn14Rw8AHOEIQIBIAgDoJwEA","344":"b:f7Xu3NL/zn14Rw8AHOEIQIBIAgDoJwEA","345":"b:f7Xu3NL/zn14Rw8AHOEIQIBIAgDoJwEA","356":"b:f7Xu3NL/zn14Rw8AHOEIQIBIAgDoJwEA","367":"b:f7Xu3NL/zn14Rw8AHOEIQIBIAgDoJwEA","368":"b:f7Xu3NL/zn14Rw8AHOEIQIBIAgDoJwEA","375":"b:f7Xu3NL/zn14Rw8AHOEIQIBIAgDoJwEA","391":"b:f7Xu3NL/zn14Rw8AHOEIQIBIAgDoJwEA","395":"b:f7Xu3NL/zn14Rw8AHOEIQIBIAgDoJwEA","404":"b:f7Xu3NL/zn14Rw8AHOEIQIBIAgDoJwEA","415":"b:f7Xu3NL/zn14Rw8AHOEIQIBIAgDoJwEA","426":"b:f7Xu3NL/zn14Rw8AHOEIQIBIAgDoJwEA","436":"b:f7Xu3NL/zn14Rw8AHOEIQIBIAgDoJwEA","513":"b:f7Xu3NL/zn14Rw8AHOEIQIBIAgDoJwEA","526":"b:f7Xu3NL/zn14Rw8AHOEIQIBIAgDoJwEA","543":"b:f7Xu3NL/zn14Rw8AHOEIQIBIAgDoJwEA","555":"b:f7Xu3NL/zn14Rw8AHOEIQIBIAgDoJwEA","561":"b:f7Xu3NL/zn14Rw8AHOEIQIBIAgDoJwEA","566":"b:f7Xu3NL/zn14Rw8AHOEIQIBIAgDoJwEA","598":"b:f7Xu3NL/zn14Rw8AHOEIQIBIAgDoJwEA","617":"b:f7Xu3NL/zn14Rw8AHOEIQIBIAgDoJwEA","624":"b:f7Xu3NL/zn14Rw8AHOEIQIBIAgDoJwEA","641":"b:f7Xu3NL/zn14Rw8AHOEIQIBIAgDoJwEA","658":"b:f7Xu3NL/zn14Rw8AHOEIQIBIAgDoJwEA","676":"b:f7Xu3NL/zn14Rw8AHOEIQIBIAgDoJwEA","701":"b:f7Xu3NL/zn14Rw8AHOEIQIBIAgDoJwEA","705":"b:f7Xu3NL/zn14Rw8AHOEIQIBIAgDoJwEA","706":"b:f7Xu3NL/zn14Rw8AHOEIQIBIAgDoJwEA","712":"b:f7Xu3NL/zn14Rw8AHOEIQIBIAgDoJwEA","722":"b:f7Xu3NL/zn14Rw8AHOEIQIBIAgDoJwEA","723":"b:f7Xu3NL/zn14Rw8AHOEIQIBIAgDoJwEA","724":"b:f7Xu3NL/zn14Rw8AHOEIQIBIAgDoJwEA"},"packages/cli/src/manual-execution.service.ts":{"27":"b:R5AiwIKDBiV6AAAAAEEAAAgYAAAAAAAA","29":"b:R5AiwIKDBiV6AAAAAEEAAAgYAAAAAAAA","49":"b:R5AiwIKDBiV6AAAAAEEAAAgYAAAAAAAA","61":"b:R5AiwIKDBiV6AAAAAEEAAAgYAAAAAAAA"},"packages/cli/src/metrics/prometheus/webhook-and-form-metrics.service.ts":{"18":"b:RgDIIAAADEMQAAACgAAAAAIACAAIAHAA","20":"b:RgDIIAAADEMQAAACgAAAAAIACAAIAHAA","24":"b:RgDIIAAADEMQAAACgAAAAAIACAAIAHAA","46":"b:RgDIIAAADEMQAAACgAAAAAIACAAIAHAA","67":"b:RgDIIAAADEMQAAACgAAAAAIACAAIAHAA"},"packages/cli/src/metrics/prometheus/workflow-execution-duration-metrics.service.ts":{"19":"b:f4DqwJKHBm94AAAAAAAAAAAAAADIB9AA","24":"b:f4DqwJKHBm94AAAAAAAAAAAAAADIB9AA","28":"b:f4DqwJKHBm94AAAAAAAAAAAAAADIB9AA","41":"b:f4DqwJKHBm94AAAAAAAAAAAAAADIB9AA"},"packages/cli/src/modules/chat-hub/chat-hub-execution-store.service.ts":{"58":"b:f4DqwJKHBm94AAAAAAAAAAAAAADIB8AA","79":"b:f4DqwJKHBm94AAAAAAAAAAAAAADIB8AA","93":"b:f4DqwJKHBm94AAAAAAAAAAAAAADIB8AA","103":"b:f4DqwJKHBm94AAAAAAAAAAAAAADIB8AA","127":"b:f4DqwJKHBm94AAAAAAAAAAAAAADIB8AA","140":"b:f4DqwJKHBm94AAAAAAAAAAAAAADIB8AA","154":"b:f4DqwJKHBm94AAAAAAAAAAAAAADIB8AA","158":"b:f4DqwJKHBm94AAAAAAAAAAAAAADIB8AA","171":"b:f4DqwJKHBm94AAAAAAAAAAAAAADIB8AA","189":"b:f4DqwJKHBm94AAAAAAAAAAAAAADIB8AA","199":"b:f4DqwJKHBm94AAAAAAAAAAAAAADIB8AA","202":"b:f4DqwJKHBm94AAAAAAAAAAAAAADIB8AA","211":"b:f4DqwJKHBm94AAAAAAAAAAAAAADIB8AA"},"packages/cli/src/modules/chat-hub/chat-hub-execution-watcher.service.ts":{"32":"b:f4DqwJKHBm94AAACgAAAAAAAAADIB9AI","54":"b:f4DqwJKHBm94AAACgAAAAAAAAADIB9AI","117":"b:f4DqwJKHBm94AAACgAAAAAAAAADIB9AI","160":"b:f4DqwJKHBm94AAACgAAAAAAAAADIB9AI","203":"b:f4DqwJKHBm94AAACgAAAAAAAAADIB9AI","235":"b:f4DqwJKHBm94AAACgAAAAAAAAADIB9AI","257":"b:f4DqwJKHBm94AAACgAAAAAAAAADIB9AI","280":"b:f4DqwJKHBm94AAACgAAAAAAAAADIB9AI"},"packages/cli/src/modules/data-table/data-table-aggregate.controller.ts":{"10":"b:X4Re/1Ko1jGo/38gARAAAQAAAAAAEAAA","17":"b:X4Re/1Ko1jGo/38gARAAAQAAAAAAEAAA","27":"b:X4Re/1Ko1jGo/38gARAAAQAAAAAAEAAA"},"packages/cli/src/modules/data-table/data-table-size-validator.service.ts":{"16":"b:X4Re3l6o1jEo/38AAQAAAQAAAAAAEAAA","21":"b:X4Re3l6o1jEo/38AAQAAAQAAAAAAEAAA","33":"b:X4Re3l6o1jEo/38AAQAAAQAAAAAAEAAA","56":"b:X4Re3l6o1jEo/38AAQAAAQAAAAAAEAAA","73":"b:X4Re3l6o1jEo/38AAQAAAQAAAAAAEAAA","86":"b:X4Re3l6o1jEo/38AAQAAAQAAAAAAEAAA","91":"b:X4Re3l6o1jEo/38AAQAAAQAAAAAAEAAA"},"packages/cli/src/modules/data-table/data-table.repository.ts":{"29":"b:X4Re/16o1jGo/38gARAABQAAAAAAEAAA","45":"b:X4Re/16o1jGo/38gARAABQAAAAAAEAAA","46":"b:X4Re/16o1jGo/38gARAABQAAAAAAEAAA","48":"b:X4Re/16o1jGo/38gARAABQAAAAAAEAAA","53":"b:X4Re/16o1jGo/38gARAABQAAAAAAEAAA","60":"b:X4Re/16o1jGo/38gARAABQAAAAAAEAAA","61":"b:X4Re/16o1jGo/38gARAABQAAAAAAEAAA","67":"b:X4Re/16o1jGo/38gARAABQAAAAAAEAAA","86":"b:X4Re/16o1jGo/38gARAABQAAAAAAEAAA","115":"b:X4Re/16o1jGo/38gARAABQAAAAAAEAAA","116":"b:X4Re/16o1jGo/38gARAABQAAAAAAEAAA","123":"b:X4Re/16o1jGo/38gARAABQAAAAAAEAAA","130":"b:X4Re/16o1jGo/38gARAABQAAAAAAEAAA","165":"b:X4Re/16o1jGo/38gARAABQAAAAAAEAAA","166":"b:X4Re/16o1jGo/38gARAABQAAAAAAEAAA","179":"b:X4Re/16o1jGo/38gARAABQAAAAAAEAAA","180":"b:X4Re/16o1jGo/38gARAABQAAAAAAEAAA","194":"b:X4Re/16o1jGo/38gARAABQAAAAAAEAAA","200":"b:X4Re/16o1jGo/38gARAABQAAAAAAEAAA","205":"b:X4Re/16o1jGo/38gARAABQAAAAAAEAAA","216":"b:X4Re/16o1jGo/38gARAABQAAAAAAEAAA","220":"b:X4Re/16o1jGo/38gARAABQAAAAAAEAAA","225":"b:X4Re/16o1jGo/38gARAABQAAAAAAEAAA","240":"b:X4Re/16o1jGo/38gARAABQAAAAAAEAAA","248":"b:X4Re/16o1jGo/38gARAABQAAAAAAEAAA","258":"b:X4Re/16o1jGo/38gARAABQAAAAAAEAAA","263":"b:X4Re/16o1jGo/38gARAABQAAAAAAEAAA","286":"b:X4Re/16o1jGo/38gARAABQAAAAAAEAAA","294":"b:X4Re/16o1jGo/38gARAABQAAAAAAEAAA","305":"b:X4Re/16o1jGo/38gARAABQAAAAAAEAAA","316":"b:X4Re/16o1jGo/38gARAABQAAAAAAEAAA","320":"b:X4Re/16o1jGo/38gARAABQAAAAAAEAAA","323":"b:X4Re/16o1jGo/38gARAABQAAAAAAEAAA","327":"b:X4Re/16o1jGo/38gARAABQAAAAAAEAAA","355":"b:X4Re/16o1jGo/38gARAABQAAAAAAEAAA","393":"b:X4Re/16o1jGo/38gARAABQAAAAAAEAAA"},"packages/cli/src/modules/data-table/data-table.service.ts":{"53":"b:X4Re3l6o1jEo/38AAQAABQAAAAAAEAAA","67":"b:X4Re3l6o1jEo/38AAQAABQAAAAAAEAAA","68":"b:X4Re3l6o1jEo/38AAQAABQAAAAAAEAAA","70":"b:X4Re3l6o1jEo/38AAQAABQAAAAAAEAAA","88":"b:X4Re3l6o1jEo/38AAQAABQAAAAAAEAAA","136":"b:X4Re3l6o1jEo/38AAQAABQAAAAAAEAAA","146":"b:X4Re3l6o1jEo/38AAQAABQAAAAAAEAAA","158":"b:X4Re3l6o1jEo/38AAQAABQAAAAAAEAAA","185":"b:X4Re3l6o1jEo/38AAQAABQAAAAAAEAAA","194":"b:X4Re3l6o1jEo/38AAQAABQAAAAAAEAAA","206":"b:X4Re3l6o1jEo/38AAQAABQAAAAAAEAAA","221":"b:X4Re3l6o1jEo/38AAQAABQAAAAAAEAAA","231":"b:X4Re3l6o1jEo/38AAQAABQAAAAAAEAAA","242":"b:X4Re3l6o1jEo/38AAQAABQAAAAAAEAAA","252":"b:X4Re3l6o1jEo/38AAQAABQAAAAAAEAAA","266":"b:X4Re3l6o1jEo/38AAQAABQAAAAAAEAAA","277":"b:X4Re3l6o1jEo/38AAQAABQAAAAAAEAAA","289":"b:X4Re3l6o1jEo/38AAQAABQAAAAAAEAAA","293":"b:X4Re3l6o1jEo/38AAQAABQAAAAAAEAAA","300":"b:X4Re3l6o1jEo/38AAQAABQAAAAAAEAAA","318":"b:X4Re3l6o1jEo/38AAQAABQAAAAAAEAAA","324":"b:X4Re3l6o1jEo/38AAQAABQAAAAAAEAAA","344":"b:X4Re3l6o1jEo/38AAQAABQAAAAAAEAAA","353":"b:X4Re3l6o1jEo/38AAQAABQAAAAAAEAAA","394":"b:X4Re3l6o1jEo/38AAQAABQAAAAAAEAAA","404":"b:X4Re3l6o1jEo/38AAQAABQAAAAAAEAAA","451":"b:X4Re3l6o1jEo/38AAQAABQAAAAAAEAAA","495":"b:X4Re3l6o1jEo/38AAQAABQAAAAAAEAAA","505":"b:X4Re3l6o1jEo/38AAQAABQAAAAAAEAAA","559":"b:X4Re3l6o1jEo/38AAQAABQAAAAAAEAAA","568":"b:X4Re3l6o1jEo/38AAQAABQAAAAAAEAAA","598":"b:X4Re3l6o1jEo/38AAQAABQAAAAAAEAAA","601":"b:X4Re3l6o1jEo/38AAQAABQAAAAAAEAAA","611":"b:X4Re3l6o1jEo/38AAQAABQAAAAAAEAAA","620":"b:X4Re3l6o1jEo/38AAQAABQAAAAAAEAAA","627":"b:X4Re3l6o1jEo/38AAQAABQAAAAAAEAAA","628":"b:X4Re3l6o1jEo/38AAQAABQAAAAAAEAAA","630":"b:X4Re3l6o1jEo/38AAQAABQAAAAAAEAAA","648":"b:X4Re3l6o1jEo/38AAQAABQAAAAAAEAAA","694":"b:X4Re3l6o1jEo/38AAQAABQAAAAAAEAAA","709":"b:X4Re3l6o1jEo/38AAQAABQAAAAAAEAAA","722":"b:X4Re3l6o1jEo/38AAQAABQAAAAAAEAAA","733":"b:X4Re3l6o1jEo/38AAQAABQAAAAAAEAAA","739":"b:X4Re3l6o1jEo/38AAQAABQAAAAAAEAAA","749":"b:X4Re3l6o1jEo/38AAQAABQAAAAAAEAAA","785":"b:X4Re3l6o1jEo/38AAQAABQAAAAAAEAAA","787":"b:X4Re3l6o1jEo/38AAQAABQAAAAAAEAAA","791":"b:X4Re3l6o1jEo/38AAQAABQAAAAAAEAAA","793":"b:X4Re3l6o1jEo/38AAQAABQAAAAAAEAAA","809":"b:X4Re3l6o1jEo/38AAQAABQAAAAAAEAAA","823":"b:X4Re3l6o1jEo/38AAQAABQAAAAAAEAAA","851":"b:X4Re3l6o1jEo/38AAQAABQAAAAAAEAAA","876":"b:X4Re3l6o1jEo/38AAQAABQAAAAAAEAAA","881":"b:X4Re3l6o1jEo/38AAQAABQAAAAAAEAAA","883":"b:X4Re3l6o1jEo/38AAQAABQAAAAAAEAAA","888":"b:X4Re3l6o1jEo/38AAQAABQAAAAAAEAAA","913":"b:X4Re3l6o1jEo/38AAQAABQAAAAAAEAAA","941":"b:X4Re3l6o1jEo/38AAQAABQAAAAAAEAAA","950":"b:X4Re3l6o1jEo/38AAQAABQAAAAAAEAAA"},"packages/cli/src/modules/data-table/utils/sql-utils.ts":{"25":"b:X4Re3l6o1jEo/38AAQAAAQAAAAAAEAAA","26":"b:X4Re3l6o1jEo/38AAQAAAQAAAAAAEAAA","44":"b:X4Re3l6o1jEo/38AAQAAAQAAAAAAEAAA","72":"b:X4Re3l6o1jEo/38AAQAAAQAAAAAAEAAA","79":"b:X4Re3l6o1jEo/38AAQAAAQAAAAAAEAAA","83":"b:X4Re3l6o1jEo/38AAQAAAQAAAAAAEAAA","87":"b:X4Re3l6o1jEo/38AAQAAAQAAAAAAEAAA","102":"b:X4Re3l6o1jEo/38AAQAAAQAAAAAAEAAA","111":"b:X4Re3l6o1jEo/38AAQAAAQAAAAAAEAAA","128":"b:X4Re3l6o1jEo/38AAQAAAQAAAAAAEAAA","137":"b:X4Re3l6o1jEo/38AAQAAAQAAAAAAEAAA","146":"b:X4Re3l6o1jEo/38AAQAAAQAAAAAAEAAA","149":"b:X4Re3l6o1jEo/38AAQAAAQAAAAAAEAAA","153":"b:X4Re3l6o1jEo/38AAQAAAQAAAAAAEAAA","157":"b:X4Re3l6o1jEo/38AAQAAAQAAAAAAEAAA","170":"b:X4Re3l6o1jEo/38AAQAAAQAAAAAAEAAA","174":"b:X4Re3l6o1jEo/38AAQAAAQAAAAAAEAAA","184":"b:X4Re3l6o1jEo/38AAQAAAQAAAAAAEAAA","192":"b:X4Re3l6o1jEo/38AAQAAAQAAAAAAEAAA","205":"b:X4Re3l6o1jEo/38AAQAAAQAAAAAAEAAA","212":"b:X4Re3l6o1jEo/38AAQAAAQAAAAAAEAAA","230":"b:X4Re3l6o1jEo/38AAQAAAQAAAAAAEAAA","251":"b:X4Re3l6o1jEo/38AAQAAAQAAAAAAEAAA","256":"b:X4Re3l6o1jEo/38AAQAAAQAAAAAAEAAA","260":"b:X4Re3l6o1jEo/38AAQAAAQAAAAAAEAAA","289":"b:X4Re3l6o1jEo/38AAQAAAQAAAAAAEAAA","322":"b:X4Re3l6o1jEo/38AAQAAAQAAAAAAEAAA","340":"b:X4Re3l6o1jEo/38AAQAAAQAAAAAAEAAA","363":"b:X4Re3l6o1jEo/38AAQAAAQAAAAAAEAAA","380":"b:X4Re3l6o1jEo/38AAQAAAQAAAAAAEAAA","386":"b:X4Re3l6o1jEo/38AAQAAAQAAAAAAEAAA","391":"b:X4Re3l6o1jEo/38AAQAAAQAAAAAAEAAA"},"packages/cli/src/modules/favorites/database/repositories/user-favorite.repository.ts":{"9":"b:f4Re3lqo9jEo/38AAQAAAQAAAAgCEAAA","13":"b:f4Re3lqo9jEo/38AAQAAAQAAAAgCEAAA","20":"b:f4Re3lqo9jEo/38AAQAAAQAAAAgCEAAA","24":"b:f4Re3lqo9jEo/38AAQAAAQAAAAgCEAAA"},"packages/cli/src/modules/favorites/favorites.controller.ts":{"10":"b:f4Re3lKo1jEo/38AAAAAAQAAAAAAEAAA","16":"b:f4Re3lKo1jEo/38AAAAAAQAAAAAAEAAA","19":"b:f4Re3lKo1jEo/38AAAAAAQAAAAAAEAAA","24":"b:f4Re3lKo1jEo/38AAAAAAQAAAAAAEAAA","29":"b:f4Re3lKo1jEo/38AAAAAAQAAAAAAEAAA"},"packages/cli/src/modules/favorites/favorites.service.ts":{"26":"b:f4Re3lqo9jEo/38AAQAAAQAAAAgCEAAA","27":"b:f4Re3lqo9jEo/38AAQAAAQAAAAgCEAAA","29":"b:f4Re3lqo9jEo/38AAQAAAQAAAAgCEAAA","35":"b:f4Re3lqo9jEo/38AAQAAAQAAAAgCEAAA","46":"b:f4Re3lqo9jEo/38AAQAAAQAAAAgCEAAA","56":"b:f4Re3lqo9jEo/38AAQAAAQAAAAgCEAAA","69":"b:f4Re3lqo9jEo/38AAQAAAQAAAAgCEAAA","79":"b:f4Re3lqo9jEo/38AAQAAAQAAAAgCEAAA","97":"b:f4Re3lqo9jEo/38AAQAAAQAAAAgCEAAA","119":"b:f4Re3lqo9jEo/38AAQAAAQAAAAgCEAAA","120":"b:f4Re3lqo9jEo/38AAQAAAQAAAAgCEAAA","128":"b:f4Re3lqo9jEo/38AAQAAAQAAAAgCEAAA","133":"b:f4Re3lqo9jEo/38AAQAAAQAAAAgCEAAA","138":"b:f4Re3lqo9jEo/38AAQAAAQAAAAgCEAAA","151":"b:f4Re3lqo9jEo/38AAQAAAQAAAAgCEAAA","170":"b:f4Re3lqo9jEo/38AAQAAAQAAAAgCEAAA","193":"b:f4Re3lqo9jEo/38AAQAAAQAAAAgCEAAA","214":"b:f4Re3lqo9jEo/38AAQAAAQAAAAgCEAAA","232":"b:f4Re3lqo9jEo/38AAQAAAQAAAAgCEAAA","261":"b:f4Re3lqo9jEo/38AAQAAAQAAAAgCEAAA","273":"b:f4Re3lqo9jEo/38AAQAAAQAAAAgCEAAA","277":"b:f4Re3lqo9jEo/38AAQAAAQAAAAgCEAAA"},"packages/cli/src/modules/insights/insights-collection.service.ts":{"65":"b:f4j+ztuv3n94M2+ZFQQAAAQgAAjZB8AL","95":"b:f4j+ztuv3n94M2+ZFQQAAAQgAAjZB8AL","105":"b:f4j+ztuv3n94M2+ZFQQAAAQgAAjZB8AL","113":"b:f4j+ztuv3n94M2+ZFQQAAAQgAAjZB8AL","119":"b:f4j+ztuv3n94M2+ZFQQAAAQgAAjZB8AL","124":"b:f4j+ztuv3n94M2+ZFQQAAAQgAAjZB8AL","131":"b:f4j+ztuv3n94M2+ZFQQAAAQgAAjZB8AL","136":"b:f4j+ztuv3n94M2+ZFQQAAAQgAAjZB8AL","152":"b:f4j+ztuv3n94M2+ZFQQAAAQgAAjZB8AL","217":"b:f4j+ztuv3n94M2+ZFQQAAAQgAAjZB8AL","232":"b:f4j+ztuv3n94M2+ZFQQAAAQgAAjZB8AL","255":"b:f4j+ztuv3n94M2+ZFQQAAAQgAAjZB8AL","283":"b:f4j+ztuv3n94M2+ZFQQAAAQgAAjZB8AL","305":"b:f4j+ztuv3n94M2+ZFQQAAAQgAAjZB8AL","328":"b:f4j+ztuv3n94M2+ZFQQAAAQgAAjZB8AL","345":"b:f4j+ztuv3n94M2+ZFQQAAAQgAAjZB8AL"},"packages/cli/src/modules/instance-registry/instance-registry.service.ts":{"29":"b:V6EWjlo59FFuMW+bFSAAAIAQggAAAAAA","37":"b:V6EWjlo59FFuMW+bFSAAAIAQggAAAAAA","52":"b:V6EWjlo59FFuMW+bFSAAAIAQggAAAAAA","72":"b:V6EWjlo59FFuMW+bFSAAAIAQggAAAAAA","76":"b:V6EWjlo59FFuMW+bFSAAAIAQggAAAAAA","80":"b:V6EWjlo59FFuMW+bFSAAAIAQggAAAAAA","84":"b:V6EWjlo59FFuMW+bFSAAAIAQggAAAAAA","88":"b:V6EWjlo59FFuMW+bFSAAAIAQggAAAAAA","92":"b:V6EWjlo59FFuMW+bFSAAAIAQggAAAAAA","96":"b:V6EWjlo59FFuMW+bFSAAAIAQggAAAAAA","109":"b:V6EWjlo59FFuMW+bFSAAAIAQggAAAAAA","122":"b:V6EWjlo59FFuMW+bFSAAAIAQggAAAAAA","123":"b:V6EWjlo59FFuMW+bFSAAAIAQggAAAAAA","133":"b:V6EWjlo59FFuMW+bFSAAAIAQggAAAAAA"},"packages/cli/src/modules/instance-registry/storage/memory-storage.ts":{"9":"b:V6EWjlo59FFuMW+bFSAAAIAQggAAAAAA","16":"b:V6EWjlo59FFuMW+bFSAAAIAQggAAAAAA","20":"b:V6EWjlo59FFuMW+bFSAAAIAQggAAAAAA","24":"b:V6EWjlo59FFuMW+bFSAAAIAQggAAAAAA","30":"b:V6EWjlo59FFuMW+bFSAAAIAQggAAAAAA","34":"b:V6EWjlo59FFuMW+bFSAAAIAQggAAAAAA","41":"b:V6EWjlo59FFuMW+bFSAAAIAQggAAAAAA","45":"b:V6EWjlo59FFuMW+bFSAAAIAQggAAAAAA","49":"b:V6EWjlo59FFuMW+bFSAAAIAQggAAAAAA","53":"b:V6EWjlo59FFuMW+bFSAAAIAQggAAAAAA"},"packages/cli/src/modules/otel/execution-level-tracer.ts":{"22":"b:f4DqwJKHBm94AAAAAAEAAAAKAADIB8AA","39":"b:f4DqwJKHBm94AAAAAAEAAAAKAADIB8AA","43":"b:f4DqwJKHBm94AAAAAAEAAAAKAADIB8AA","48":"b:f4DqwJKHBm94AAAAAAEAAAAKAADIB8AA","87":"b:f4DqwJKHBm94AAAAAAEAAAAKAADIB8AA","123":"b:f4DqwJKHBm94AAAAAAEAAAAKAADIB8AA","168":"b:f4DqwJKHBm94AAAAAAEAAAAKAADIB8AA","199":"b:f4DqwJKHBm94AAAAAAEAAAAKAADIB8AA","220":"b:f4DqwJKHBm94AAAAAAEAAAAKAADIB8AA","226":"b:f4DqwJKHBm94AAAAAAEAAAAKAADIB8AA","239":"b:f4DqwJKHBm94AAAAAAEAAAAKAADIB8AA","244":"b:f4DqwJKHBm94AAAAAAEAAAAKAADIB8AA","252":"b:f4DqwJKHBm94AAAAAAEAAAAKAADIB8AA","264":"b:f4DqwJKHBm94AAAAAAEAAAAKAADIB8AA","276":"b:f4DqwJKHBm94AAAAAAEAAAAKAADIB8AA","285":"b:f4DqwJKHBm94AAAAAAEAAAAKAADIB8AA","291":"b:f4DqwJKHBm94AAAAAAEAAAAKAADIB8AA","297":"b:f4DqwJKHBm94AAAAAAEAAAAKAADIB8AA","316":"b:f4DqwJKHBm94AAAAAAEAAAAKAADIB8AA","329":"b:f4DqwJKHBm94AAAAAAEAAAAKAADIB8AA","336":"b:f4DqwJKHBm94AAAAAAEAAAAKAADIB8AA","344":"b:f4DqwJKHBm94AAAAAAEAAAAKAADIB8AA"},"packages/cli/src/modules/otel/otel-lifecycle-handler.ts":{"20":"b:f4DqwJKHBm94AAACgAAAAAAAAADIB9AI","29":"b:f4DqwJKHBm94AAACgAAAAAAAAADIB9AI","41":"b:f4DqwJKHBm94AAACgAAAAAAAAADIB9AI","52":"b:f4DqwJKHBm94AAACgAAAAAAAAADIB9AI","57":"b:f4DqwJKHBm94AAACgAAAAAAAAADIB9AI","61":"b:f4DqwJKHBm94AAACgAAAAAAAAADIB9AI","72":"b:f4DqwJKHBm94AAACgAAAAAAAAADIB9AI","84":"b:f4DqwJKHBm94AAACgAAAAAAAAADIB9AI","117":"b:f4DqwJKHBm94AAACgAAAAAAAAADIB9AI","124":"b:f4DqwJKHBm94AAACgAAAAAAAAADIB9AI","153":"b:f4DqwJKHBm94AAACgAAAAAAAAADIB9AI","165":"b:f4DqwJKHBm94AAACgAAAAAAAAADIB9AI","168":"b:f4DqwJKHBm94AAACgAAAAAAAAADIB9AI","178":"b:f4DqwJKHBm94AAACgAAAAAAAAADIB9AI","181":"b:f4DqwJKHBm94AAACgAAAAAAAAADIB9AI","194":"b:f4DqwJKHBm94AAACgAAAAAAAAADIB9AI","198":"b:f4DqwJKHBm94AAACgAAAAAAAAADIB9AI","219":"b:f4DqwJKHBm94AAACgAAAAAAAAADIB9AI","232":"b:f4DqwJKHBm94AAACgAAAAAAAAADIB9AI","237":"b:f4DqwJKHBm94AAACgAAAAAAAAADIB9AI","242":"b:f4DqwJKHBm94AAACgAAAAAAAAADIB9AI","244":"b:f4DqwJKHBm94AAACgAAAAAAAAADIB9AI","247":"b:f4DqwJKHBm94AAACgAAAAAAAAADIB9AI"},"packages/cli/src/modules/redaction/executions/execution-redaction.service.ts":{"42":"b:R4DqwJKBDw96AAAAAAAAAAAAAAAAAAAA","50":"b:R4DqwJKBDw96AAAAAAAAAAAAAAAAAAAA","62":"b:R4DqwJKBDw96AAAAAAAAAAAAAAAAAAAA","79":"b:R4DqwJKBDw96AAAAAAAAAAAAAAAAAAAA","94":"b:R4DqwJKBDw96AAAAAAAAAAAAAAAAAAAA","101":"b:R4DqwJKBDw96AAAAAAAAAAAAAAAAAAAA","104":"b:R4DqwJKBDw96AAAAAAAAAAAAAAAAAAAA","196":"b:R4DqwJKBDw96AAAAAAAAAAAAAAAAAAAA","244":"b:R4DqwJKBDw96AAAAAAAAAAAAAAAAAAAA","277":"b:R4DqwJKBDw96AAAAAAAAAAAAAAAAAAAA","294":"b:R4DqwJKBDw96AAAAAAAAAAAAAAAAAAAA","309":"b:R4DqwJKBDw96AAAAAAAAAAAAAAAAAAAA","328":"b:R4DqwJKBDw96AAAAAAAAAAAAAAAAAAAA"},"packages/cli/src/modules/redaction/instance-redaction-enforcement.service.ts":{"16":"b:f4DqwJKHBm94AAAAAAAAAAAAAADIB9AA","29":"b:f4DqwJKHBm94AAAAAAAAAAAAAADIB9AA","33":"b:f4DqwJKHBm94AAAAAAAAAAAAAADIB9AA","35":"b:f4DqwJKHBm94AAAAAAAAAAAAAADIB9AA","42":"b:f4DqwJKHBm94AAAAAAAAAAAAAADIB9AA","60":"b:f4DqwJKHBm94AAAAAAAAAAAAAADIB9AA","77":"b:f4DqwJKHBm94AAAAAAAAAAAAAADIB9AA","81":"b:f4DqwJKHBm94AAAAAAAAAAAAAADIB9AA","90":"b:f4DqwJKHBm94AAAAAAAAAAAAAADIB9AA"},"packages/cli/src/modules/redaction/redaction-context-hook.ts":{"19":"b:f4DqwJKHBm94AAAAAAAAAAAAAADIB9AA","27":"b:f4DqwJKHBm94AAAAAAAAAAAAAADIB9AA","45":"b:f4DqwJKHBm94AAAAAAAAAAAAAADIB9AA"},"packages/cli/src/modules/workflow-index/workflow-index.service.ts":{"28":"b:R6T+7PP/9mksA4ACAAAAoAQAIAv6B+ED","40":"b:R6T+7PP/9mksA4ACAAAAoAQAIAv6B+ED","41":"b:R6T+7PP/9mksA4ACAAAAoAQAIAv6B+ED","43":"b:R6T+7PP/9mksA4ACAAAAoAQAIAv6B+ED","45":"b:R6T+7PP/9mksA4ACAAAAoAQAIAv6B+ED","48":"b:R6T+7PP/9mksA4ACAAAAoAQAIAv6B+ED","51":"b:R6T+7PP/9mksA4ACAAAAoAQAIAv6B+ED","54":"b:R6T+7PP/9mksA4ACAAAAoAQAIAv6B+ED","66":"b:R6T+7PP/9mksA4ACAAAAoAQAIAv6B+ED","69":"b:R6T+7PP/9mksA4ACAAAAoAQAIAv6B+ED","71":"b:R6T+7PP/9mksA4ACAAAAoAQAIAv6B+ED","77":"b:R6T+7PP/9mksA4ACAAAAoAQAIAv6B+ED","90":"b:R6T+7PP/9mksA4ACAAAAoAQAIAv6B+ED","138":"b:R6T+7PP/9mksA4ACAAAAoAQAIAv6B+ED","152":"b:R6T+7PP/9mksA4ACAAAAoAQAIAv6B+ED","170":"b:R6T+7PP/9mksA4ACAAAAoAQAIAv6B+ED","177":"b:R6T+7PP/9mksA4ACAAAAoAQAIAv6B+ED","199":"b:R6T+7PP/9mksA4ACAAAAoAQAIAv6B+ED","217":"b:R6T+7PP/9mksA4ACAAAAoAQAIAv6B+ED","218":"b:R6T+7PP/9mksA4ACAAAAoAQAIAv6B+ED","260":"b:R6T+7PP/9mksA4ACAAAAoAQAIAv6B+ED","270":"b:R6T+7PP/9mksA4ACAAAAoAQAIAv6B+ED","287":"b:R6T+7PP/9mksA4ACAAAAoAQAIAv6B+ED","309":"b:R6T+7PP/9mksA4ACAAAAoAQAIAv6B+ED","324":"b:R6T+7PP/9mksA4ACAAAAoAQAIAv6B+ED","338":"b:R6T+7PP/9mksA4ACAAAAoAQAIAv6B+ED","353":"b:R6T+7PP/9mksA4ACAAAAoAQAIAv6B+ED"},"packages/cli/src/node-types.ts":{"19":"b:f4TuzNL/hmkoA4AAAAAAAAAAAADoBwEA","32":"b:f4TuzNL/hmkoA4AAAAAAAAAAAADoBwEA","44":"b:f4TuzNL/hmkoA4AAAAAAAAAAAADoBwEA","57":"b:f4TuzNL/hmkoA4AAAAAAAAAAAADoBwEA","83":"b:f4TuzNL/hmkoA4AAAAAAAAAAAADoBwEA","87":"b:f4TuzNL/hmkoA4AAAAAAAAAAAADoBwEA","107":"b:f4TuzNL/hmkoA4AAAAAAAAAAAADoBwEA","148":"b:f4TuzNL/hmkoA4AAAAAAAAAAAADoBwEA","162":"b:f4TuzNL/hmkoA4AAAAAAAAAAAADoBwEA","175":"b:f4TuzNL/hmkoA4AAAAAAAAAAAADoBwEA","177":"b:f4TuzNL/hmkoA4AAAAAAAAAAAADoBwEA","195":"b:f4TuzNL/hmkoA4AAAAAAAAAAAADoBwEA","213":"b:f4TuzNL/hmkoA4AAAAAAAAAAAADoBwEA","216":"b:f4TuzNL/hmkoA4AAAAAAAAAAAADoBwEA","223":"b:f4TuzNL/hmkoA4AAAAAAAAAAAADoBwEA","226":"b:f4TuzNL/hmkoA4AAAAAAAAAAAADoBwEA","237":"b:f4TuzNL/hmkoA4AAAAAAAAAAAADoBwEA","238":"b:f4TuzNL/hmkoA4AAAAAAAAAAAADoBwEA","262":"b:f4TuzNL/hmkoA4AAAAAAAAAAAADoBwEA"},"packages/cli/src/permissions.ee/check-access.ts":{"35":"b:f6Te3lKmxmko/38AABAAUAAAAADoNwgA","57":"b:f6Te3lKmxmko/38AABAAUAAAAADoNwgA","91":"b:f6Te3lKmxmko/38AABAAUAAAAADoNwgA","111":"b:f6Te3lKmxmko/38AABAAUAAAAADoNwgA","146":"b:f6Te3lKmxmko/38AABAAUAAAAADoNwgA"},"packages/cli/src/posthog/index.ts":{"33":"b:f4TexlAkxmko7n8MAAAAAAAAAADo9w8A","38":"b:f4TexlAkxmko7n8MAAAAAAAAAADo9w8A","50":"b:f4TexlAkxmko7n8MAAAAAAAAAADo9w8A","56":"b:f4TexlAkxmko7n8MAAAAAAAAAADo9w8A","71":"b:f4TexlAkxmko7n8MAAAAAAAAAADo9w8A","97":"b:f4TexlAkxmko7n8MAAAAAAAAAADo9w8A","109":"b:f4TexlAkxmko7n8MAAAAAAAAAADo9w8A","123":"b:f4TexlAkxmko7n8MAAAAAAAAAADo9w8A","151":"b:f4TexlAkxmko7n8MAAAAAAAAAADo9w8A","171":"b:f4TexlAkxmko7n8MAAAAAAAAAADo9w8A"},"packages/cli/src/push/abstract.push.ts":{"34":"b:f4Xe/f75nw9652+ApgSICAAIAABABwAA","40":"b:f4Xe/f75nw9652+ApgSICAAIAABABwAA","43":"b:f4Xe/f75nw9652+ApgSICAAIAABABwAA","58":"b:f4Xe/f75nw9652+ApgSICAAIAABABwAA","66":"b:f4Xe/f75nw9652+ApgSICAAIAABABwAA","70":"b:f4Xe/f75nw9652+ApgSICAAIAABABwAA","79":"b:f4Xe/f75nw9652+ApgSICAAIAABABwAA","96":"b:f4Xe/f75nw9652+ApgSICAAIAABABwAA","102":"b:f4Xe/f75nw9652+ApgSICAAIAABABwAA","106":"b:f4Xe/f75nw9652+ApgSICAAIAABABwAA","115":"b:f4Xe/f75nw9652+ApgSICAAIAABABwAA","117":"b:f4Xe/f75nw9652+ApgSICAAIAABABwAA","124":"b:f4Xe/f75nw9652+ApgSICAAIAABABwAA","134":"b:f4Xe/f75nw9652+ApgSICAAIAABABwAA"},"packages/cli/src/push/index.ts":{"52":"b:f4Xe3NL5nw965w8AgAAAAAAAAABABwAA","62":"b:f4Xe3NL5nw965w8AgAAAAAAAAABABwAA","65":"b:f4Xe3NL5nw965w8AgAAAAAAAAABABwAA","70":"b:f4Xe3NL5nw965w8AgAAAAAAAAABABwAA","73":"b:f4Xe3NL5nw965w8AgAAAAAAAAABABwAA","75":"b:f4Xe3NL5nw965w8AgAAAAAAAAABABwAA","79":"b:f4Xe3NL5nw965w8AgAAAAAAAAABABwAA","94":"b:f4Xe3NL5nw965w8AgAAAAAAAAABABwAA","99":"b:f4Xe3NL5nw965w8AgAAAAAAAAABABwAA","111":"b:f4Xe3NL5nw965w8AgAAAAAAAAABABwAA","166":"b:f4Xe3NL5nw965w8AgAAAAAAAAABABwAA","171":"b:f4Xe3NL5nw965w8AgAAAAAAAAABABwAA","180":"b:f4Xe3NL5nw965w8AgAAAAAAAAABABwAA","189":"b:f4Xe3NL5nw965w8AgAAAAAAAAABABwAA","194":"b:f4Xe3NL5nw965w8AgAAAAAAAAABABwAA","215":"b:f4Xe3NL5nw965w8AgAAAAAAAAABABwAA","222":"b:f4Xe3NL5nw965w8AgAAAAAAAAABABwAA","237":"b:f4Xe3NL5nw965w8AgAAAAAAAAABABwAA","244":"b:f4Xe3NL5nw965w8AgAAAAAAAAABABwAA"},"packages/cli/src/push/push-helpers.ts":{"5":"b:f4Re3FL4lgEo5w8AAAAAAAAAAAAAAAAA","9":"b:f4Re3FL4lgEo5w8AAAAAAAAAAAAAAAAA","14":"b:f4Re3FL4lgEo5w8AAAAAAAAAAAAAAAAA","18":"b:f4Re3FL4lgEo5w8AAAAAAAAAAAAAAAAA"},"packages/cli/src/push/websocket.push.ts":{"9":"b:f4Ve3PL5nwE65w8AJAAAIIAgAgQAAAAA","15":"b:f4Ve3PL5nwE65w8AJAAAIIAgAgQAAAAA","21":"b:f4Ve3PL5nwE65w8AJAAAIIAgAgQAAAAA","60":"b:f4Ve3PL5nwE65w8AJAAAIIAgAgQAAAAA","71":"b:f4Ve3PL5nwE65w8AJAAAIIAgAgQAAAAA","75":"b:f4Ve3PL5nwE65w8AJAAAIIAgAgQAAAAA","79":"b:f4Ve3PL5nwE65w8AJAAAIIAgAgQAAAAA","88":"b:f4Ve3PL5nwE65w8AJAAAIIAgAgQAAAAA"},"packages/cli/src/server.ts":{"89":"b:f5Re3lL41jVo738MAOEIwYBIAAAAEAEA","101":"b:f5Re3lL41jVo738MAOEIwYBIAAAAEAEA","125":"b:f5Re3lL41jVo738MAOEIwYBIAAAAEAEA","163":"b:f5Re3lL41jVo738MAOEIwYBIAAAAEAEA","204":"b:f5Re3lL41jVo738MAOEIwYBIAAAAEAEA","249":"b:f5Re3lL41jVo738MAOEIwYBIAAAAEAEA","279":"b:f5Re3lL41jVo738MAOEIwYBIAAAAEAEA","333":"b:f5Re3lL41jVo738MAOEIwYBIAAAAEAEA","337":"b:f5Re3lL41jVo738MAOEIwYBIAAAAEAEA","352":"b:f5Re3lL41jVo738MAOEIwYBIAAAAEAEA","367":"b:f5Re3lL41jVo738MAOEIwYBIAAAAEAEA","441":"b:f5Re3lL41jVo738MAOEIwYBIAAAAEAEA","443":"b:f5Re3lL41jVo738MAOEIwYBIAAAAEAEA","456":"b:f5Re3lL41jVo738MAOEIwYBIAAAAEAEA","463":"b:f5Re3lL41jVo738MAOEIwYBIAAAAEAEA","485":"b:f5Re3lL41jVo738MAOEIwYBIAAAAEAEA","494":"b:f5Re3lL41jVo738MAOEIwYBIAAAAEAEA","503":"b:f5Re3lL41jVo738MAOEIwYBIAAAAEAEA","510":"b:f5Re3lL41jVo738MAOEIwYBIAAAAEAEA"},"packages/cli/src/services/access.service.ts":{"13":"b:R6VezNL5nwUsA4AAgAAAAARIKAEAAAAA","19":"b:R6VezNL5nwUsA4AAgAAAAARIKAEAAAAA","31":"b:R6VezNL5nwUsA4AAgAAAAARIKAEAAAAA"},"packages/cli/src/services/active-workflows.service.ts":{"13":"b:R4Re/tKp3gEoP3gCAAEAgAQgAAMBAAAA","21":"b:R4Re/tKp3gEoP3gCAAEAgAQgAAMBAAAA","24":"b:R4Re/tKp3gEoP3gCAAEAgAQgAAMBAAAA","27":"b:R4Re/tKp3gEoP3gCAAEAgAQgAAMBAAAA","33":"b:R4Re/tKp3gEoP3gCAAEAgAQgAAMBAAAA","38":"b:R4Re/tKp3gEoP3gCAAEAgAQgAAMBAAAA","41":"b:R4Re/tKp3gEoP3gCAAEAgAQgAAMBAAAA"},"packages/cli/src/services/cache/cache.service.ts":{"26":"b:f6T+7NP/zm88AwACgAAAhAIECFjoB+AD","32":"b:f6T+7NP/zm88AwACgAAAhAIECFjoB+AD","69":"b:f6T+7NP/zm88AwACgAAAhAIECFjoB+AD","79":"b:f6T+7NP/zm88AwACgAAAhAIECFjoB+AD","83":"b:f6T+7NP/zm88AwACgAAAhAIECFjoB+AD","87":"b:f6T+7NP/zm88AwACgAAAhAIECFjoB+AD","91":"b:f6T+7NP/zm88AwACgAAAhAIECFjoB+AD","103":"b:f6T+7NP/zm88AwACgAAAhAIECFjoB+AD","115":"b:f6T+7NP/zm88AwACgAAAhAIECFjoB+AD","121":"b:f6T+7NP/zm88AwACgAAAhAIECFjoB+AD","139":"b:f6T+7NP/zm88AwACgAAAhAIECFjoB+AD","160":"b:f6T+7NP/zm88AwACgAAAhAIECFjoB+AD","180":"b:f6T+7NP/zm88AwACgAAAhAIECFjoB+AD","218":"b:f6T+7NP/zm88AwACgAAAhAIECFjoB+AD","256":"b:f6T+7NP/zm88AwACgAAAhAIECFjoB+AD","300":"b:f6T+7NP/zm88AwACgAAAhAIECFjoB+AD","308":"b:f6T+7NP/zm88AwACgAAAhAIECFjoB+AD","320":"b:f6T+7NP/zm88AwACgAAAhAIECFjoB+AD","339":"b:f6T+7NP/zm88AwACgAAAhAIECFjoB+AD","355":"b:f6T+7NP/zm88AwACgAAAhAIECFjoB+AD"},"packages/cli/src/services/execution-metadata.service.ts":{"7":[19,1,2,3,4,5,57,129,6],"9":[19,1,2,3,4,5,57,129,6]},"packages/cli/src/services/frontend.service.ts":{"124":"b:f4Re3lKt1jEo/38gAAAAAQAAAAAAEAAA","145":"b:f4Re3lKt1jEo/38gAAAAAQAAAAAAEAAA","146":"b:f4Re3lKt1jEo/38gAAAAAQAAAAAAEAAA","151":"b:f4Re3lKt1jEo/38gAAAAAQAAAAAAEAAA","158":"b:f4Re3lKt1jEo/38gAAAAAQAAAAAAEAAA","170":"b:f4Re3lKt1jEo/38gAAAAAQAAAAAAEAAA","177":"b:f4Re3lKt1jEo/38gAAAAAQAAAAAAEAAA","415":"b:f4Re3lKt1jEo/38gAAAAAQAAAAAAEAAA","416":"b:f4Re3lKt1jEo/38gAAAAAQAAAAAAEAAA","421":"b:f4Re3lKt1jEo/38gAAAAAQAAAAAAEAAA","452":"b:f4Re3lKt1jEo/38gAAAAAQAAAAAAEAAA","466":"b:f4Re3lKt1jEo/38gAAAAAQAAAAAAEAAA","645":"b:f4Re3lKt1jEo/38gAAAAAQAAAAAAEAAA","656":"b:f4Re3lKt1jEo/38gAAAAAQAAAAAAEAAA","677":"b:f4Re3lKt1jEo/38gAAAAAQAAAAAAEAAA","719":"b:f4Re3lKt1jEo/38gAAAAAQAAAAAAEAAA","723":"b:f4Re3lKt1jEo/38gAAAAAQAAAAAAEAAA","739":"b:f4Re3lKt1jEo/38gAAAAAQAAAAAAEAAA","747":"b:f4Re3lKt1jEo/38gAAAAAQAAAAAAEAAA","752":"b:f4Re3lKt1jEo/38gAAAAAQAAAAAAEAAA","762":"b:f4Re3lKt1jEo/38gAAAAAQAAAAAAEAAA","775":"b:f4Re3lKt1jEo/38gAAAAAQAAAAAAEAAA","776":"b:f4Re3lKt1jEo/38gAAAAAQAAAAAAEAAA","806":"b:f4Re3lKt1jEo/38gAAAAAQAAAAAAEAAA"},"packages/cli/src/services/jwt.service.ts":{"12":"b:f4TezlKmxmkp7n8MAAAAQAAAAADo9w8E","33":"b:f4TezlKmxmkp7n8MAAAAQAAAAADo9w8E","60":"b:f4TezlKmxmkp7n8MAAAAQAAAAADo9w8E","64":"b:f4TezlKmxmkp7n8MAAAAQAAAAADo9w8E","68":"b:f4TezlKmxmkp7n8MAAAAQAAAAADo9w8E"},"packages/cli/src/services/last-active-at.service.ts":{"11":"b:f4TezlKkxmko7n8MAAAAAAAAAADoNwkA","16":"b:f4TezlKkxmko7n8MAAAAAAAAAADoNwkA","18":"b:f4TezlKkxmko7n8MAAAAAAAAAADoNwkA","25":"b:f4TezlKkxmko7n8MAAAAAAAAAADoNwkA"},"packages/cli/src/services/ownership.service.ts":{"29":"b:f6T+/dv3xn98AwACgRAAJAIIAAH6J/AC","46":"b:f6T+/dv3xn98AwACgRAAJAIIAAH6J/AC","52":"b:f6T+/dv3xn98AwACgRAAJAIIAAH6J/AC","59":"b:f6T+/dv3xn98AwACgRAAJAIIAAH6J/AC","66":"b:f6T+/dv3xn98AwACgRAAJAIIAAH6J/AC","76":"b:f6T+/dv3xn98AwACgRAAJAIIAAH6J/AC","93":"b:f6T+/dv3xn98AwACgRAAJAIIAAH6J/AC","116":"b:f6T+/dv3xn98AwACgRAAJAIIAAH6J/AC","128":"b:f6T+/dv3xn98AwACgRAAJAIIAAH6J/AC","148":"b:f6T+/dv3xn98AwACgRAAJAIIAAH6J/AC","155":"b:f6T+/dv3xn98AwACgRAAJAIIAAH6J/AC","162":"b:f6T+/dv3xn98AwACgRAAJAIIAAH6J/AC","173":"b:f6T+/dv3xn98AwACgRAAJAIIAAH6J/AC","176":"b:f6T+/dv3xn98AwACgRAAJAIIAAH6J/AC","188":"b:f6T+/dv3xn98AwACgRAAJAIIAAH6J/AC","230":"b:f6T+/dv3xn98AwACgRAAJAIIAAH6J/AC","236":"b:f6T+/dv3xn98AwACgRAAJAIIAAH6J/AC","256":"b:f6T+/dv3xn98AwACgRAAJAIIAAH6J/AC"},"packages/cli/src/services/password.utility.ts":{"8":"b:f4TexlAkxmko7n8MAAAAAAAAAADo9w8A","12":"b:f4TexlAkxmko7n8MAAAAAAAAAADo9w8A"},"packages/cli/src/services/project.service.ee.ts":{"37":"b:f4Xf/1/u9nmp/38MGxiAdQAAAALqNwEA","45":"b:f4Xf/1/u9nmp/38MGxiAdQAAAALqNwEA","51":"b:f4Xf/1/u9nmp/38MGxiAdQAAAALqNwEA","55":"b:f4Xf/1/u9nmp/38MGxiAdQAAAALqNwEA","73":"b:f4Xf/1/u9nmp/38MGxiAdQAAAALqNwEA","85":"b:f4Xf/1/u9nmp/38MGxiAdQAAAALqNwEA","86":"b:f4Xf/1/u9nmp/38MGxiAdQAAAALqNwEA","91":"b:f4Xf/1/u9nmp/38MGxiAdQAAAALqNwEA","92":"b:f4Xf/1/u9nmp/38MGxiAdQAAAALqNwEA","97":"b:f4Xf/1/u9nmp/38MGxiAdQAAAALqNwEA","98":"b:f4Xf/1/u9nmp/38MGxiAdQAAAALqNwEA","103":"b:f4Xf/1/u9nmp/38MGxiAdQAAAALqNwEA","104":"b:f4Xf/1/u9nmp/38MGxiAdQAAAALqNwEA","109":"b:f4Xf/1/u9nmp/38MGxiAdQAAAALqNwEA","111":"b:f4Xf/1/u9nmp/38MGxiAdQAAAALqNwEA","115":"b:f4Xf/1/u9nmp/38MGxiAdQAAAALqNwEA","116":"b:f4Xf/1/u9nmp/38MGxiAdQAAAALqNwEA","121":"b:f4Xf/1/u9nmp/38MGxiAdQAAAALqNwEA","122":"b:f4Xf/1/u9nmp/38MGxiAdQAAAALqNwEA","127":"b:f4Xf/1/u9nmp/38MGxiAdQAAAALqNwEA","128":"b:f4Xf/1/u9nmp/38MGxiAdQAAAALqNwEA","133":"b:f4Xf/1/u9nmp/38MGxiAdQAAAALqNwEA","135":"b:f4Xf/1/u9nmp/38MGxiAdQAAAALqNwEA","139":"b:f4Xf/1/u9nmp/38MGxiAdQAAAALqNwEA","141":"b:f4Xf/1/u9nmp/38MGxiAdQAAAALqNwEA","145":"b:f4Xf/1/u9nmp/38MGxiAdQAAAALqNwEA","191":"b:f4Xf/1/u9nmp/38MGxiAdQAAAALqNwEA","208":"b:f4Xf/1/u9nmp/38MGxiAdQAAAALqNwEA","287":"b:f4Xf/1/u9nmp/38MGxiAdQAAAALqNwEA","306":"b:f4Xf/1/u9nmp/38MGxiAdQAAAALqNwEA","315":"b:f4Xf/1/u9nmp/38MGxiAdQAAAALqNwEA","324":"b:f4Xf/1/u9nmp/38MGxiAdQAAAALqNwEA","328":"b:f4Xf/1/u9nmp/38MGxiAdQAAAALqNwEA","331":"b:f4Xf/1/u9nmp/38MGxiAdQAAAALqNwEA","333":"b:f4Xf/1/u9nmp/38MGxiAdQAAAALqNwEA","348":"b:f4Xf/1/u9nmp/38MGxiAdQAAAALqNwEA","356":"b:f4Xf/1/u9nmp/38MGxiAdQAAAALqNwEA","370":"b:f4Xf/1/u9nmp/38MGxiAdQAAAALqNwEA","380":"b:f4Xf/1/u9nmp/38MGxiAdQAAAALqNwEA","384":"b:f4Xf/1/u9nmp/38MGxiAdQAAAALqNwEA","414":"b:f4Xf/1/u9nmp/38MGxiAdQAAAALqNwEA","421":"b:f4Xf/1/u9nmp/38MGxiAdQAAAALqNwEA","426":"b:f4Xf/1/u9nmp/38MGxiAdQAAAALqNwEA","431":"b:f4Xf/1/u9nmp/38MGxiAdQAAAALqNwEA","432":"b:f4Xf/1/u9nmp/38MGxiAdQAAAALqNwEA","446":"b:f4Xf/1/u9nmp/38MGxiAdQAAAALqNwEA","450":"b:f4Xf/1/u9nmp/38MGxiAdQAAAALqNwEA","457":"b:f4Xf/1/u9nmp/38MGxiAdQAAAALqNwEA","469":"b:f4Xf/1/u9nmp/38MGxiAdQAAAALqNwEA","473":"b:f4Xf/1/u9nmp/38MGxiAdQAAAALqNwEA","476":"b:f4Xf/1/u9nmp/38MGxiAdQAAAALqNwEA","477":"b:f4Xf/1/u9nmp/38MGxiAdQAAAALqNwEA","482":"b:f4Xf/1/u9nmp/38MGxiAdQAAAALqNwEA","486":"b:f4Xf/1/u9nmp/38MGxiAdQAAAALqNwEA","492":"b:f4Xf/1/u9nmp/38MGxiAdQAAAALqNwEA","502":"b:f4Xf/1/u9nmp/38MGxiAdQAAAALqNwEA","514":"b:f4Xf/1/u9nmp/38MGxiAdQAAAALqNwEA","523":"b:f4Xf/1/u9nmp/38MGxiAdQAAAALqNwEA","531":"b:f4Xf/1/u9nmp/38MGxiAdQAAAALqNwEA","536":"b:f4Xf/1/u9nmp/38MGxiAdQAAAALqNwEA","550":"b:f4Xf/1/u9nmp/38MGxiAdQAAAALqNwEA","567":"b:f4Xf/1/u9nmp/38MGxiAdQAAAALqNwEA","571":"b:f4Xf/1/u9nmp/38MGxiAdQAAAALqNwEA","589":"b:f4Xf/1/u9nmp/38MGxiAdQAAAALqNwEA","593":"b:f4Xf/1/u9nmp/38MGxiAdQAAAALqNwEA","605":"b:f4Xf/1/u9nmp/38MGxiAdQAAAALqNwEA","615":"b:f4Xf/1/u9nmp/38MGxiAdQAAAALqNwEA","620":"b:f4Xf/1/u9nmp/38MGxiAdQAAAALqNwEA","629":"b:f4Xf/1/u9nmp/38MGxiAdQAAAALqNwEA","631":"b:f4Xf/1/u9nmp/38MGxiAdQAAAALqNwEA","635":"b:f4Xf/1/u9nmp/38MGxiAdQAAAALqNwEA","645":"b:f4Xf/1/u9nmp/38MGxiAdQAAAALqNwEA","651":"b:f4Xf/1/u9nmp/38MGxiAdQAAAALqNwEA","663":"b:f4Xf/1/u9nmp/38MGxiAdQAAAALqNwEA","669":"b:f4Xf/1/u9nmp/38MGxiAdQAAAALqNwEA","677":"b:f4Xf/1/u9nmp/38MGxiAdQAAAALqNwEA","683":"b:f4Xf/1/u9nmp/38MGxiAdQAAAALqNwEA","687":"b:f4Xf/1/u9nmp/38MGxiAdQAAAALqNwEA","694":"b:f4Xf/1/u9nmp/38MGxiAdQAAAALqNwEA","704":"b:f4Xf/1/u9nmp/38MGxiAdQAAAALqNwEA","735":"b:f4Xf/1/u9nmp/38MGxiAdQAAAALqNwEA","757":"b:f4Xf/1/u9nmp/38MGxiAdQAAAALqNwEA","760":"b:f4Xf/1/u9nmp/38MGxiAdQAAAALqNwEA","766":"b:f4Xf/1/u9nmp/38MGxiAdQAAAALqNwEA","769":"b:f4Xf/1/u9nmp/38MGxiAdQAAAALqNwEA","802":"b:f4Xf/1/u9nmp/38MGxiAdQAAAALqNwEA","815":"b:f4Xf/1/u9nmp/38MGxiAdQAAAALqNwEA","824":"b:f4Xf/1/u9nmp/38MGxiAdQAAAALqNwEA","828":"b:f4Xf/1/u9nmp/38MGxiAdQAAAALqNwEA","835":"b:f4Xf/1/u9nmp/38MGxiAdQAAAALqNwEA","845":"b:f4Xf/1/u9nmp/38MGxiAdQAAAALqNwEA","856":"b:f4Xf/1/u9nmp/38MGxiAdQAAAALqNwEA"},"packages/cli/src/services/role.service.ts":{"52":"b:f4X+/9P//nmp/38iCAgARQAAAAD4N2EE","61":"b:f4X+/9P//nmp/38iCAgARQAAAAD4N2EE","64":"b:f4X+/9P//nmp/38iCAgARQAAAAD4N2EE","71":"b:f4X+/9P//nmp/38iCAgARQAAAAD4N2EE","79":"b:f4X+/9P//nmp/38iCAgARQAAAAD4N2EE","83":"b:f4X+/9P//nmp/38iCAgARQAAAAD4N2EE","91":"b:f4X+/9P//nmp/38iCAgARQAAAAD4N2EE","98":"b:f4X+/9P//nmp/38iCAgARQAAAAD4N2EE","116":"b:f4X+/9P//nmp/38iCAgARQAAAAD4N2EE","129":"b:f4X+/9P//nmp/38iCAgARQAAAAD4N2EE","142":"b:f4X+/9P//nmp/38iCAgARQAAAAD4N2EE","176":"b:f4X+/9P//nmp/38iCAgARQAAAAD4N2EE","192":"b:f4X+/9P//nmp/38iCAgARQAAAAD4N2EE","206":"b:f4X+/9P//nmp/38iCAgARQAAAAD4N2EE","210":"b:f4X+/9P//nmp/38iCAgARQAAAAD4N2EE","212":"b:f4X+/9P//nmp/38iCAgARQAAAAD4N2EE","214":"b:f4X+/9P//nmp/38iCAgARQAAAAD4N2EE","224":"b:f4X+/9P//nmp/38iCAgARQAAAAD4N2EE","257":"b:f4X+/9P//nmp/38iCAgARQAAAAD4N2EE","286":"b:f4X+/9P//nmp/38iCAgARQAAAAD4N2EE","291":"b:f4X+/9P//nmp/38iCAgARQAAAAD4N2EE","300":"b:f4X+/9P//nmp/38iCAgARQAAAAD4N2EE","327":"b:f4X+/9P//nmp/38iCAgARQAAAAD4N2EE","375":"b:f4X+/9P//nmp/38iCAgARQAAAAD4N2EE","385":"b:f4X+/9P//nmp/38iCAgARQAAAAD4N2EE","389":"b:f4X+/9P//nmp/38iCAgARQAAAAD4N2EE","399":"b:f4X+/9P//nmp/38iCAgARQAAAAD4N2EE","408":"b:f4X+/9P//nmp/38iCAgARQAAAAD4N2EE","420":"b:f4X+/9P//nmp/38iCAgARQAAAAD4N2EE","444":"b:f4X+/9P//nmp/38iCAgARQAAAAD4N2EE","455":"b:f4X+/9P//nmp/38iCAgARQAAAAD4N2EE","456":"b:f4X+/9P//nmp/38iCAgARQAAAAD4N2EE","466":"b:f4X+/9P//nmp/38iCAgARQAAAAD4N2EE","468":"b:f4X+/9P//nmp/38iCAgARQAAAAD4N2EE","473":"b:f4X+/9P//nmp/38iCAgARQAAAAD4N2EE","479":"b:f4X+/9P//nmp/38iCAgARQAAAAD4N2EE","483":"b:f4X+/9P//nmp/38iCAgARQAAAAD4N2EE","493":"b:f4X+/9P//nmp/38iCAgARQAAAAD4N2EE"},"packages/cli/src/services/tag.service.ts":{"15":"b:X4be3lL813Go/38AAQAQQQAAAOQTEAAA","25":"b:X4be3lL813Go/38AAQAQQQAAAOQTEAAA","30":"b:X4be3lL813Go/38AAQAQQQAAAOQTEAAA","36":"b:X4be3lL813Go/38AAQAQQQAAAOQTEAAA","50":"b:X4be3lL813Go/38AAQAQQQAAAOQTEAAA","60":"b:X4be3lL813Go/38AAQAQQQAAAOQTEAAA","67":"b:X4be3lL813Go/38AAQAQQQAAAOQTEAAA","86":"b:X4be3lL813Go/38AAQAQQQAAAOQTEAAA","99":"b:X4be3lL813Go/38AAQAQQQAAAOQTEAAA","105":"b:X4be3lL813Go/38AAQAQQQAAAOQTEAAA","110":"b:X4be3lL813Go/38AAQAQQQAAAOQTEAAA","119":"b:X4be3lL813Go/38AAQAQQQAAAOQTEAAA","133":"b:X4be3lL813Go/38AAQAQQQAAAOQTEAAA","134":"b:X4be3lL813Go/38AAQAQQQAAAOQTEAAA","139":"b:X4be3lL813Go/38AAQAQQQAAAOQTEAAA","148":"b:X4be3lL813Go/38AAQAQQQAAAOQTEAAA","150":"b:X4be3lL813Go/38AAQAQQQAAAOQTEAAA","155":"b:X4be3lL813Go/38AAQAQQQAAAOQTEAAA"},"packages/cli/src/services/user.service.ts":{"45":"b:f+Te3leu1nsp/38MAAAAxwAAgAH4958G","60":"b:f+Te3leu1nsp/38MAAAAxwAAgAH4958G","70":"b:f+Te3leu1nsp/38MAAAAxwAAgAH4958G","74":"b:f+Te3leu1nsp/38MAAAAxwAAgAH4958G","86":"b:f+Te3leu1nsp/38MAAAAxwAAgAH4958G","98":"b:f+Te3leu1nsp/38MAAAAxwAAgAH4958G","109":"b:f+Te3leu1nsp/38MAAAAxwAAgAH4958G","115":"b:f+Te3leu1nsp/38MAAAAxwAAgAH4958G","120":"b:f+Te3leu1nsp/38MAAAAxwAAgAH4958G","163":"b:f+Te3leu1nsp/38MAAAAxwAAgAH4958G","166":"b:f+Te3leu1nsp/38MAAAAxwAAgAH4958G","167":"b:f+Te3leu1nsp/38MAAAAxwAAgAH4958G","172":"b:f+Te3leu1nsp/38MAAAAxwAAgAH4958G","180":"b:f+Te3leu1nsp/38MAAAAxwAAgAH4958G","190":"b:f+Te3leu1nsp/38MAAAAxwAAgAH4958G","262":"b:f+Te3leu1nsp/38MAAAAxwAAgAH4958G","263":"b:f+Te3leu1nsp/38MAAAAxwAAgAH4958G","267":"b:f+Te3leu1nsp/38MAAAAxwAAgAH4958G","269":"b:f+Te3leu1nsp/38MAAAAxwAAgAH4958G","271":"b:f+Te3leu1nsp/38MAAAAxwAAgAH4958G","283":"b:f+Te3leu1nsp/38MAAAAxwAAgAH4958G","289":"b:f+Te3leu1nsp/38MAAAAxwAAgAH4958G","291":"b:f+Te3leu1nsp/38MAAAAxwAAgAH4958G","311":"b:f+Te3leu1nsp/38MAAAAxwAAgAH4958G","319":"b:f+Te3leu1nsp/38MAAAAxwAAgAH4958G","322":"b:f+Te3leu1nsp/38MAAAAxwAAgAH4958G","338":"b:f+Te3leu1nsp/38MAAAAxwAAgAH4958G","341":"b:f+Te3leu1nsp/38MAAAAxwAAgAH4958G","431":"b:f+Te3leu1nsp/38MAAAAxwAAgAH4958G","457":"b:f+Te3leu1nsp/38MAAAAxwAAgAH4958G"},"packages/cli/src/services/workflow-statistics.service.ts":{"62":"b:f4jqwJOHDm94AAACgAEAAAIIiAHYB/AL","86":"b:f4jqwJOHDm94AAACgAEAAAIIiAHYB/AL","101":"b:f4jqwJOHDm94AAACgAEAAAIIiAHYB/AL","116":"b:f4jqwJOHDm94AAACgAEAAAIIiAHYB/AL","120":"b:f4jqwJOHDm94AAACgAEAAAIIiAHYB/AL","125":"b:f4jqwJOHDm94AAACgAEAAAIIiAHYB/AL","185":"b:f4jqwJOHDm94AAACgAEAAAIIiAHYB/AL","205":"b:f4jqwJOHDm94AAACgAEAAAIIiAHYB/AL","232":"b:f4jqwJOHDm94AAACgAEAAAIIiAHYB/AL","276":"b:f4jqwJOHDm94AAACgAEAAAIIiAHYB/AL","307":"b:f4jqwJOHDm94AAACgAEAAAIIiAHYB/AL"},"packages/cli/src/sso.ee/sso-helpers.ts":{"15":"b:f4TexlAmxmkp7n8MAAAARAAAAADo9w8E","29":"b:f4TexlAmxmkp7n8MAAAARAAAAADo9w8E","48":"b:f4TexlAmxmkp7n8MAAAARAAAAADo9w8E","52":"b:f4TexlAmxmkp7n8MAAAARAAAAADo9w8E","59":"b:f4TexlAmxmkp7n8MAAAARAAAAADo9w8E","63":"b:f4TexlAmxmkp7n8MAAAARAAAAADo9w8E","67":"b:f4TexlAmxmkp7n8MAAAARAAAAADo9w8E","71":"b:f4TexlAmxmkp7n8MAAAARAAAAADo9w8E","75":"b:f4TexlAmxmkp7n8MAAAARAAAAADo9w8E","79":"b:f4TexlAmxmkp7n8MAAAARAAAAADo9w8E","83":"b:f4TexlAmxmkp7n8MAAAARAAAAADo9w8E","91":"b:f4TexlAmxmkp7n8MAAAARAAAAADo9w8E","96":"b:f4TexlAmxmkp7n8MAAAARAAAAADo9w8E","107":"b:f4TexlAmxmkp7n8MAAAARAAAAADo9w8E","111":"b:f4TexlAmxmkp7n8MAAAARAAAAADo9w8E"},"packages/cli/src/task-runners/default-task-runner-disconnect-analyzer.ts":{"18":"b:RQIggAACAAB4AIAAAAAAAAgABoAAABAA","22":"b:RQIggAACAAB4AIAAAAAAAAgABoAAABAA"},"packages/cli/src/task-runners/errors/task-runner-disconnected-error.ts":{"7":"b:RQIggAACAAB4AIAAAAAAAAgABoAAABAA","27":"b:RQIggAACAAB4AIAAAAAAAAgABoAAABAA"},"packages/cli/src/task-runners/task-broker/errors/task-deferred.error.ts":{"4":"b:RQIggAACAAB4AIAAAAAAAAgABoAAABAA"},"packages/cli/src/task-runners/task-broker/errors/task-runner-accept-timeout.error.ts":{"4":"b:RQAAgAACAAB4AIAAAAAAAAgABoAACAAE"},"packages/cli/src/task-runners/task-broker/task-broker-ws-server.ts":{"22":"b:V4I2jlor1FF4I++VFQAAAAgABoAAAhAA","38":"b:V4I2jlor1FF4I++VFQAAAAgABoAAAhAA","47":"b:V4I2jlor1FF4I++VFQAAAAgABoAAAhAA","51":"b:V4I2jlor1FF4I++VFQAAAAgABoAAAhAA","58":"b:V4I2jlor1FF4I++VFQAAAAgABoAAAhAA","75":"b:V4I2jlor1FF4I++VFQAAAAgABoAAAhAA","84":"b:V4I2jlor1FF4I++VFQAAAAgABoAAAhAA","88":"b:V4I2jlor1FF4I++VFQAAAAgABoAAAhAA","92":"b:V4I2jlor1FF4I++VFQAAAAgABoAAAhAA","96":"b:V4I2jlor1FF4I++VFQAAAAgABoAAAhAA","102":"b:V4I2jlor1FF4I++VFQAAAAgABoAAAhAA","147":"b:V4I2jlor1FF4I++VFQAAAAgABoAAAhAA","159":"b:V4I2jlor1FF4I++VFQAAAAgABoAAAhAA","178":"b:V4I2jlor1FF4I++VFQAAAAgABoAAAhAA","182":"b:V4I2jlor1FF4I++VFQAAAAgABoAAAhAA","187":"b:V4I2jlor1FF4I++VFQAAAAgABoAAAhAA","193":"b:V4I2jlor1FF4I++VFQAAAAgABoAAAhAA"},"packages/cli/src/task-runners/task-broker/task-broker.service.ts":{"97":"b:RQIggAACAAB4AIAAAAAAAAgABqAACDAE","108":"b:RQIggAACAAB4AIAAAAAAAAgABqAACDAE","109":"b:RQIggAACAAB4AIAAAAAAAAgABqAACDAE","114":"b:RQIggAACAAB4AIAAAAAAAAgABqAACDAE","115":"b:RQIggAACAAB4AIAAAAAAAAgABqAACDAE","131":"b:RQIggAACAAB4AIAAAAAAAAgABqAACDAE","142":"b:RQIggAACAAB4AIAAAAAAAAgABqAACDAE","147":"b:RQIggAACAAB4AIAAAAAAAAgABqAACDAE","169":"b:RQIggAACAAB4AIAAAAAAAAgABqAACDAE","173":"b:RQIggAACAAB4AIAAAAAAAAgABqAACDAE","177":"b:RQIggAACAAB4AIAAAAAAAAgABqAACDAE","181":"b:RQIggAACAAB4AIAAAAAAAAgABqAACDAE","185":"b:RQIggAACAAB4AIAAAAAAAAgABqAACDAE","233":"b:RQIggAACAAB4AIAAAAAAAAgABqAACDAE","252":"b:RQIggAACAAB4AIAAAAAAAAgABqAACDAE","260":"b:RQIggAACAAB4AIAAAAAAAAgABqAACDAE","268":"b:RQIggAACAAB4AIAAAAAAAAgABqAACDAE","276":"b:RQIggAACAAB4AIAAAAAAAAgABqAACDAE","293":"b:RQIggAACAAB4AIAAAAAAAAgABqAACDAE","310":"b:RQIggAACAAB4AIAAAAAAAAgABqAACDAE","327":"b:RQIggAACAAB4AIAAAAAAAAgABqAACDAE","364":"b:RQIggAACAAB4AIAAAAAAAAgABqAACDAE","381":"b:RQIggAACAAB4AIAAAAAAAAgABqAACDAE","393":"b:RQIggAACAAB4AIAAAAAAAAgABqAACDAE","409":"b:RQIggAACAAB4AIAAAAAAAAgABqAACDAE","420":"b:RQIggAACAAB4AIAAAAAAAAgABqAACDAE","428":"b:RQIggAACAAB4AIAAAAAAAAgABqAACDAE","442":"b:RQIggAACAAB4AIAAAAAAAAgABqAACDAE","456":"b:RQIggAACAAB4AIAAAAAAAAgABqAACDAE","472":"b:RQIggAACAAB4AIAAAAAAAAgABqAACDAE","479":"b:RQIggAACAAB4AIAAAAAAAAgABqAACDAE","490":"b:RQIggAACAAB4AIAAAAAAAAgABqAACDAE","516":"b:RQIggAACAAB4AIAAAAAAAAgABqAACDAE","530":"b:RQIggAACAAB4AIAAAAAAAAgABqAACDAE","544":"b:RQIggAACAAB4AIAAAAAAAAgABqAACDAE","548":"b:RQIggAACAAB4AIAAAAAAAAgABqAACDAE","552":"b:RQIggAACAAB4AIAAAAAAAAgABqAACDAE","594":"b:RQIggAACAAB4AIAAAAAAAAgABqAACDAE","606":"b:RQIggAACAAB4AIAAAAAAAAgABqAACDAE","615":"b:RQIggAACAAB4AIAAAAAAAAgABqAACDAE","648":"b:RQIggAACAAB4AIAAAAAAAAgABqAACDAE","655":"b:RQIggAACAAB4AIAAAAAAAAgABqAACDAE","670":"b:RQIggAACAAB4AIAAAAAAAAgABqAACDAE","674":"b:RQIggAACAAB4AIAAAAAAAAgABqAACDAE","681":"b:RQIggAACAAB4AIAAAAAAAAgABqAACDAE","695":"b:RQIggAACAAB4AIAAAAAAAAgABqAACDAE","700":"b:RQIggAACAAB4AIAAAAAAAAgABqAACDAE","704":"b:RQIggAACAAB4AIAAAAAAAAgABqAACDAE","712":"b:RQIggAACAAB4AIAAAAAAAAgABqAACDAE","716":"b:RQIggAACAAB4AIAAAAAAAAgABqAACDAE","720":"b:RQIggAACAAB4AIAAAAAAAAgABqAACDAE","724":"b:RQIggAACAAB4AIAAAAAAAAgABqAACDAE","728":"b:RQIggAACAAB4AIAAAAAAAAgABqAACDAE","732":"b:RQIggAACAAB4AIAAAAAAAAgABqAACDAE","736":"b:RQIggAACAAB4AIAAAAAAAAgABqAACDAE","740":"b:RQIggAACAAB4AIAAAAAAAAgABqAACDAE","744":"b:RQIggAACAAB4AIAAAAAAAAgABqAACDAE","748":"b:RQIggAACAAB4AIAAAAAAAAgABqAACDAE","752":"b:RQIggAACAAB4AIAAAAAAAAgABqAACDAE"},"packages/cli/src/task-runners/task-managers/data-request-response-builder.ts":{"17":"b:RQIggAACAAB4AIAAAAAAAAgABoAAABAA","37":"b:RQIggAACAAB4AIAAAAAAAAgABoAAABAA","57":"b:RQIggAACAAB4AIAAAAAAAAgABoAAABAA","70":"b:RQIggAACAAB4AIAAAAAAAAgABoAAABAA"},"packages/cli/src/task-runners/task-managers/data-request-response-stripper.ts":{"18":"b:RQIggAACAAB4AIAAAAAAAAgABoAAABAQ","32":"b:RQIggAACAAB4AIAAAAAAAAgABoAAABAQ","43":"b:RQIggAACAAB4AIAAAAAAAAgABoAAABAQ","64":"b:RQIggAACAAB4AIAAAAAAAAgABoAAABAQ","68":"b:RQIggAACAAB4AIAAAAAAAAgABoAAABAQ","72":"b:RQIggAACAAB4AIAAAAAAAAgABoAAABAQ","86":"b:RQIggAACAAB4AIAAAAAAAAgABoAAABAQ","94":"b:RQIggAACAAB4AIAAAAAAAAgABoAAABAQ","118":"b:RQIggAACAAB4AIAAAAAAAAgABoAAABAQ","138":"b:RQIggAACAAB4AIAAAAAAAAgABoAAABAQ"},"packages/cli/src/task-runners/task-managers/local-task-requester.ts":{"19":"b:RQIggAACAAB4AIAAAAAAAAgABoAAABAA","30":"b:RQIggAACAAB4AIAAAAAAAAgABoAAABAA","39":"b:RQIggAACAAB4AIAAAAAAAAgABoAAABAA","40":"b:RQIggAACAAB4AIAAAAAAAAgABoAAABAA"},"packages/cli/src/task-runners/task-managers/task-requester.ts":{"70":"b:RQIggAACAAB4AIAAAAAAAAgABqAAADAA","78":"b:RQIggAACAAB4AIAAAAAAAAgABqAAADAA","82":"b:RQIggAACAAB4AIAAAAAAAAgABqAAADAA","87":"b:RQIggAACAAB4AIAAAAAAAAgABqAAADAA","135":"b:RQIggAACAAB4AIAAAAAAAAgABqAAADAA","171":"b:RQIggAACAAB4AIAAAAAAAAgABqAAADAA","195":"b:RQIggAACAAB4AIAAAAAAAAgABqAAADAA","217":"b:RQIggAACAAB4AIAAAAAAAAgABqAAADAA","223":"b:RQIggAACAAB4AIAAAAAAAAgABqAAADAA","227":"b:RQIggAACAAB4AIAAAAAAAAgABqAAADAA","246":"b:RQIggAACAAB4AIAAAAAAAAgABqAAADAA","258":"b:RQIggAACAAB4AIAAAAAAAAgABqAAADAA","260":"b:RQIggAACAAB4AIAAAAAAAAgABqAAADAA","286":"b:RQIggAACAAB4AIAAAAAAAAgABqAAADAA","300":"b:RQIggAACAAB4AIAAAAAAAAgABqAAADAA","324":"b:RQIggAACAAB4AIAAAAAAAAgABqAAADAA","332":"b:RQIggAACAAB4AIAAAAAAAAgABqAAADAA","340":"b:RQIggAACAAB4AIAAAAAAAAgABqAAADAA","348":"b:RQIggAACAAB4AIAAAAAAAAgABqAAADAA","374":"b:RQIggAACAAB4AIAAAAAAAAgABqAAADAA","389":"b:RQIggAACAAB4AIAAAAAAAAgABqAAADAA"},"packages/cli/src/utils/get-item-count-by-connection-type.ts":{"4":"b:R4BKwJKBDwZ6AAAAgAAAAAAAAAAAAAAA","15":"b:R4BKwJKBDwZ6AAAAgAAAAAAAAAAAAAAA"},"packages/cli/src/utils/strapi-utils.ts":{"54":"b:B4RezALolgEoJwgMAAAAAAAAAAAAAAAA","66":"b:B4RezALolgEoJwgMAAAAAAAAAAAAAAAA","76":"b:B4RezALolgEoJwgMAAAAAAAAAAAAAAAA","129":"b:B4RezALolgEoJwgMAAAAAAAAAAAAAAAA"},"packages/cli/src/utils.ts":{"10":"b:f/Xu/9L//3/7G3AgjBACUAIAOgHoJ/kB","19":"b:f/Xu/9L//3/7G3AgjBACUAIAOgHoJ/kB","28":"b:f/Xu/9L//3/7G3AgjBACUAIAOgHoJ/kB","32":"b:f/Xu/9L//3/7G3AgjBACUAIAOgHoJ/kB","33":"b:f/Xu/9L//3/7G3AgjBACUAIAOgHoJ/kB","35":"b:f/Xu/9L//3/7G3AgjBACUAIAOgHoJ/kB","40":"b:f/Xu/9L//3/7G3AgjBACUAIAOgHoJ/kB","60":"b:f/Xu/9L//3/7G3AgjBACUAIAOgHoJ/kB","64":"b:f/Xu/9L//3/7G3AgjBACUAIAOgHoJ/kB","66":"b:f/Xu/9L//3/7G3AgjBACUAIAOgHoJ/kB","71":"b:f/Xu/9L//3/7G3AgjBACUAIAOgHoJ/kB","76":"b:f/Xu/9L//3/7G3AgjBACUAIAOgHoJ/kB","82":"b:f/Xu/9L//3/7G3AgjBACUAIAOgHoJ/kB","95":"b:f/Xu/9L//3/7G3AgjBACUAIAOgHoJ/kB","97":"b:f/Xu/9L//3/7G3AgjBACUAIAOgHoJ/kB","102":"b:f/Xu/9L//3/7G3AgjBACUAIAOgHoJ/kB","122":"b:f/Xu/9L//3/7G3AgjBACUAIAOgHoJ/kB","129":"b:f/Xu/9L//3/7G3AgjBACUAIAOgHoJ/kB","149":"b:f/Xu/9L//3/7G3AgjBACUAIAOgHoJ/kB","161":"b:f/Xu/9L//3/7G3AgjBACUAIAOgHoJ/kB","165":"b:f/Xu/9L//3/7G3AgjBACUAIAOgHoJ/kB","173":"b:f/Xu/9L//3/7G3AgjBACUAIAOgHoJ/kB"},"packages/cli/src/webhooks/node-type-matcher.ts":{"5":"b:RgDIIAAADEMQAAACgAAAAAIACAAIAHAA"},"packages/cli/src/webhooks/test-webhook-registrations.service.ts":{"28":"b:RgBIIAAACAIQAAAAgAAAAAoACAAAAAAA","40":"b:RgBIIAAACAIQAAAAgAAAAAoACAAAAAAA","47":"b:RgBIIAAACAIQAAAAgAAAAAoACAAAAAAA","76":"b:RgBIIAAACAIQAAAAgAAAAAoACAAAAAAA","85":"b:RgBIIAAACAIQAAAAgAAAAAoACAAAAAAA","90":"b:RgBIIAAACAIQAAAAgAAAAAoACAAAAAAA","98":"b:RgBIIAAACAIQAAAAgAAAAAoACAAAAAAA","106":"b:RgBIIAAACAIQAAAAgAAAAAoACAAAAAAA","116":"b:RgBIIAAACAIQAAAAgAAAAAoACAAAAAAA"},"packages/cli/src/webhooks/test-webhooks.ts":{"56":"b:R4Bq4IKDDid6AAAAgAAAAAoACAAAAAAA","72":"b:R4Bq4IKDDid6AAAAgAAAAAoACAAAAAAA","102":"b:R4Bq4IKDDid6AAAAgAAAAAoACAAAAAAA","150":"b:R4Bq4IKDDid6AAAAgAAAAAoACAAAAAAA","164":"b:R4Bq4IKDDid6AAAAgAAAAAoACAAAAAAA","222":"b:R4Bq4IKDDid6AAAAgAAAAAoACAAAAAAA","245":"b:R4Bq4IKDDid6AAAAgAAAAAoACAAAAAAA","251":"b:R4Bq4IKDDid6AAAAgAAAAAoACAAAAAAA","274":"b:R4Bq4IKDDid6AAAAgAAAAAoACAAAAAAA","278":"b:R4Bq4IKDDid6AAAAgAAAAAoACAAAAAAA","285":"b:R4Bq4IKDDid6AAAAgAAAAAoACAAAAAAA","288":"b:R4Bq4IKDDid6AAAAgAAAAAoACAAAAAAA","301":"b:R4Bq4IKDDid6AAAAgAAAAAoACAAAAAAA","314":"b:R4Bq4IKDDid6AAAAgAAAAAoACAAAAAAA","360":"b:R4Bq4IKDDid6AAAAgAAAAAoACAAAAAAA","363":"b:R4Bq4IKDDid6AAAAgAAAAAoACAAAAAAA","371":"b:R4Bq4IKDDid6AAAAgAAAAAoACAAAAAAA","382":"b:R4Bq4IKDDid6AAAAgAAAAAoACAAAAAAA","465":"b:R4Bq4IKDDid6AAAAgAAAAAoACAAAAAAA","493":"b:R4Bq4IKDDid6AAAAgAAAAAoACAAAAAAA","500":"b:R4Bq4IKDDid6AAAAgAAAAAoACAAAAAAA","514":"b:R4Bq4IKDDid6AAAAgAAAAAoACAAAAAAA","522":"b:R4Bq4IKDDid6AAAAgAAAAAoACAAAAAAA","523":"b:R4Bq4IKDDid6AAAAgAAAAAoACAAAAAAA","532":"b:R4Bq4IKDDid6AAAAgAAAAAoACAAAAAAA","544":"b:R4Bq4IKDDid6AAAAgAAAAAoACAAAAAAA","551":"b:R4Bq4IKDDid6AAAAgAAAAAoACAAAAAAA","587":"b:R4Bq4IKDDid6AAAAgAAAAAoACAAAAAAA"},"packages/cli/src/webhooks/webhook-execution-context.ts":{"17":"b:RgDIIAAADEMQAAACgAAAAAIACAAYAHAB","28":"b:RgDIIAAADEMQAAACgAAAAAIACAAYAHAB","46":"b:RgDIIAAADEMQAAACgAAAAAIACAAYAHAB"},"packages/cli/src/webhooks/webhook-helpers.ts":{"101":"b:R4rq4IOHDm94AAACgAAAAAIACADYB/AJ","118":"b:R4rq4IOHDm94AAACgAAAAAIACADYB/AJ","130":"b:R4rq4IOHDm94AAACgAAAAAIACADYB/AJ","139":"b:R4rq4IOHDm94AAACgAAAAAIACADYB/AJ","149":"b:R4rq4IOHDm94AAACgAAAAAIACADYB/AJ","189":"b:R4rq4IOHDm94AAACgAAAAAIACADYB/AJ","207":"b:R4rq4IOHDm94AAACgAAAAAIACADYB/AJ","278":"b:R4rq4IOHDm94AAACgAAAAAIACADYB/AJ","314":"b:R4rq4IOHDm94AAACgAAAAAIACADYB/AJ","323":"b:R4rq4IOHDm94AAACgAAAAAIACADYB/AJ","358":"b:R4rq4IOHDm94AAACgAAAAAIACADYB/AJ","360":"b:R4rq4IOHDm94AAACgAAAAAIACADYB/AJ","380":"b:R4rq4IOHDm94AAACgAAAAAIACADYB/AJ","400":"b:R4rq4IOHDm94AAACgAAAAAIACADYB/AJ","417":"b:R4rq4IOHDm94AAACgAAAAAIACADYB/AJ","478":"b:R4rq4IOHDm94AAACgAAAAAIACADYB/AJ","564":"b:R4rq4IOHDm94AAACgAAAAAIACADYB/AJ","574":"b:R4rq4IOHDm94AAACgAAAAAIACADYB/AJ","586":"b:R4rq4IOHDm94AAACgAAAAAIACADYB/AJ","591":"b:R4rq4IOHDm94AAACgAAAAAIACADYB/AJ","594":"b:R4rq4IOHDm94AAACgAAAAAIACADYB/AJ","615":"b:R4rq4IOHDm94AAACgAAAAAIACADYB/AJ","629":"b:R4rq4IOHDm94AAACgAAAAAIACADYB/AJ","960":"b:R4rq4IOHDm94AAACgAAAAAIACADYB/AJ","993":"b:R4rq4IOHDm94AAACgAAAAAIACADYB/AJ","1077":"b:R4rq4IOHDm94AAACgAAAAAIACADYB/AJ","1112":"b:R4rq4IOHDm94AAACgAAAAAIACADYB/AJ","1196":"b:R4rq4IOHDm94AAACgAAAAAIACADYB/AJ","1247":"b:R4rq4IOHDm94AAACgAAAAAIACADYB/AJ","1272":"b:R4rq4IOHDm94AAACgAAAAAIACADYB/AJ"},"packages/cli/src/webhooks/webhook-last-node-response-extractor.ts":{"30":[19,1,2,180,51,57,129,6,147,187],"72":[19,1,2,180,51,57,129,6,147,187],"117":[19,1,2,180,51,57,129,6,147,187],"184":[19,1,2,180,51,57,129,6,147,187]},"packages/cli/src/webhooks/webhook-request-handler.ts":{"34":"b:RgDIIAAADEMQAAACgAAAAAIACAAIAHAB","43":"b:RgDIIAAADEMQAAACgAAAAAIACAAIAHAB","96":"b:RgDIIAAADEMQAAACgAAAAAIACAAIAHAB","112":"b:RgDIIAAADEMQAAACgAAAAAIACAAIAHAB","121":"b:RgDIIAAADEMQAAACgAAAAAIACAAIAHAB","124":"b:RgDIIAAADEMQAAACgAAAAAIACAAIAHAB","137":"b:RgDIIAAADEMQAAACgAAAAAIACAAIAHAB","143":"b:RgDIIAAADEMQAAACgAAAAAIACAAIAHAB","152":"b:RgDIIAAADEMQAAACgAAAAAIACAAIAHAB","182":"b:RgDIIAAADEMQAAACgAAAAAIACAAIAHAB","236":"b:RgDIIAAADEMQAAACgAAAAAIACAAIAHAB","244":"b:RgDIIAAADEMQAAACgAAAAAIACAAIAHAB","283":"b:RgDIIAAADEMQAAACgAAAAAIACAAIAHAB","290":"b:RgDIIAAADEMQAAACgAAAAAIACAAIAHAB"},"packages/cli/src/webhooks/webhook-response-headers.ts":{"22":"b:RgDIIAAADEMQAAACgAAAAAIACAAIAHAB","26":"b:RgDIIAAADEMQAAACgAAAAAIACAAIAHAB","33":"b:RgDIIAAADEMQAAACgAAAAAIACAAIAHAB","50":"b:RgDIIAAADEMQAAACgAAAAAIACAAIAHAB","57":"b:RgDIIAAADEMQAAACgAAAAAIACAAIAHAB","66":"b:RgDIIAAADEMQAAACgAAAAAIACAAIAHAB","74":"b:RgDIIAAADEMQAAACgAAAAAIACAAIAHAB"},"packages/cli/src/webhooks/webhook-response.ts":{"37":"b:RgDIIAAADEMQAAACgAAAAAIACAAIAHAA","41":"b:RgDIIAAADEMQAAACgAAAAAIACAAIAHAA","45":"b:RgDIIAAADEMQAAACgAAAAAIACAAIAHAA","49":"b:RgDIIAAADEMQAAACgAAAAAIACAAIAHAA","53":"b:RgDIIAAADEMQAAACgAAAAAIACAAIAHAA","59":"b:RgDIIAAADEMQAAACgAAAAAIACAAIAHAA","72":"b:RgDIIAAADEMQAAACgAAAAAIACAAIAHAA"},"packages/cli/src/webhooks/webhook.service.ts":{"33":"b:R4rq4IOHDm94AAACgAAAgAIACFvaB/AL","40":"b:R4rq4IOHDm94AAACgAAAgAIACFvaB/AL","45":"b:R4rq4IOHDm94AAACgAAAgAIACFvaB/AL","48":"b:R4rq4IOHDm94AAACgAAAgAIACFvaB/AL","52":"b:R4rq4IOHDm94AAACgAAAgAIACFvaB/AL","64":"b:R4rq4IOHDm94AAACgAAAgAIACFvaB/AL","82":"b:R4rq4IOHDm94AAACgAAAgAIACFvaB/AL","95":"b:R4rq4IOHDm94AAACgAAAgAIACFvaB/AL","104":"b:R4rq4IOHDm94AAACgAAAgAIACFvaB/AL","112":"b:R4rq4IOHDm94AAACgAAAgAIACFvaB/AL","129":"b:R4rq4IOHDm94AAACgAAAgAIACFvaB/AL","130":"b:R4rq4IOHDm94AAACgAAAgAIACFvaB/AL","148":"b:R4rq4IOHDm94AAACgAAAgAIACFvaB/AL","152":"b:R4rq4IOHDm94AAACgAAAgAIACFvaB/AL","189":"b:R4rq4IOHDm94AAACgAAAgAIACFvaB/AL","194":"b:R4rq4IOHDm94AAACgAAAgAIACFvaB/AL","198":"b:R4rq4IOHDm94AAACgAAAgAIACFvaB/AL","205":"b:R4rq4IOHDm94AAACgAAAgAIACFvaB/AL","209":"b:R4rq4IOHDm94AAACgAAAgAIACFvaB/AL","214":"b:R4rq4IOHDm94AAACgAAAgAIACFvaB/AL","215":"b:R4rq4IOHDm94AAACgAAAgAIACFvaB/AL","220":"b:R4rq4IOHDm94AAACgAAAgAIACFvaB/AL","224":"b:R4rq4IOHDm94AAACgAAAgAIACFvaB/AL","235":"b:R4rq4IOHDm94AAACgAAAgAIACFvaB/AL","246":"b:R4rq4IOHDm94AAACgAAAgAIACFvaB/AL","248":"b:R4rq4IOHDm94AAACgAAAgAIACFvaB/AL","255":"b:R4rq4IOHDm94AAACgAAAgAIACFvaB/AL","352":"b:R4rq4IOHDm94AAACgAAAgAIACFvaB/AL","369":"b:R4rq4IOHDm94AAACgAAAgAIACFvaB/AL","383":"b:R4rq4IOHDm94AAACgAAAgAIACFvaB/AL","432":"b:R4rq4IOHDm94AAACgAAAgAIACFvaB/AL","434":"b:R4rq4IOHDm94AAACgAAAgAIACFvaB/AL","438":"b:R4rq4IOHDm94AAACgAAAgAIACFvaB/AL","443":"b:R4rq4IOHDm94AAACgAAAgAIACFvaB/AL","462":"b:R4rq4IOHDm94AAACgAAAgAIACFvaB/AL","471":"b:R4rq4IOHDm94AAACgAAAgAIACFvaB/AL","503":"b:R4rq4IOHDm94AAACgAAAgAIACFvaB/AL","534":"b:R4rq4IOHDm94AAACgAAAgAIACFvaB/AL"},"packages/cli/src/workflow-execute-additional-data.ts":{"75":"b:f4LqxJKHhm94AoACgAEAAAgChoHoB7AJ","120":"b:f4LqxJKHhm94AoACgAEAAAgChoHoB7AJ","166":"b:f4LqxJKHhm94AoACgAEAAAgChoHoB7AJ","184":"b:f4LqxJKHhm94AoACgAEAAAgChoHoB7AJ","242":"b:f4LqxJKHhm94AoACgAEAAAgChoHoB7AJ","275":"b:f4LqxJKHhm94AoACgAEAAAgChoHoB7AJ","292":"b:f4LqxJKHhm94AoACgAEAAAgChoHoB7AJ","363":"b:f4LqxJKHhm94AoACgAEAAAgChoHoB7AJ","442":"b:f4LqxJKHhm94AoACgAEAAAgChoHoB7AJ","448":"b:f4LqxJKHhm94AoACgAEAAAgChoHoB7AJ","462":"b:f4LqxJKHhm94AoACgAEAAAgChoHoB7AJ","463":"b:f4LqxJKHhm94AoACgAEAAAgChoHoB7AJ","477":"b:f4LqxJKHhm94AoACgAEAAAgChoHoB7AJ","496":"b:f4LqxJKHhm94AoACgAEAAAgChoHoB7AJ","697":"b:f4LqxJKHhm94AoACgAEAAAgChoHoB7AJ","707":"b:f4LqxJKHhm94AoACgAEAAAgChoHoB7AJ","736":"b:f4LqxJKHhm94AoACgAEAAAgChoHoB7AJ","786":"b:f4LqxJKHhm94AoACgAEAAAgChoHoB7AJ","792":"b:f4LqxJKHhm94AoACgAEAAAgChoHoB7AJ","802":"b:f4LqxJKHhm94AoACgAEAAAgChoHoB7AJ","839":"b:f4LqxJKHhm94AoACgAEAAAgChoHoB7AJ","842":"b:f4LqxJKHhm94AoACgAEAAAgChoHoB7AJ","845":"b:f4LqxJKHhm94AoACgAEAAAgChoHoB7AJ"},"packages/cli/src/workflow-helpers.ts":{"41":"b:f6f+7NP/z298A4ACgAAAIAZAqAHoB/EJ","68":"b:f6f+7NP/z298A4ACgAAAIAZAqAHoB/EJ","74":"b:f6f+7NP/z298A4ACgAAAIAZAqAHoB/EJ","80":"b:f6f+7NP/z298A4ACgAAAIAZAqAHoB/EJ","99":"b:f6f+7NP/z298A4ACgAAAIAZAqAHoB/EJ","118":"b:f6f+7NP/z298A4ACgAAAIAZAqAHoB/EJ","122":"b:f6f+7NP/z298A4ACgAAAIAZAqAHoB/EJ","134":"b:f6f+7NP/z298A4ACgAAAIAZAqAHoB/EJ","159":"b:f6f+7NP/z298A4ACgAAAIAZAqAHoB/EJ","160":"b:f6f+7NP/z298A4ACgAAAIAZAqAHoB/EJ","182":"b:f6f+7NP/z298A4ACgAAAIAZAqAHoB/EJ","208":"b:f6f+7NP/z298A4ACgAAAIAZAqAHoB/EJ","246":"b:f6f+7NP/z298A4ACgAAAIAZAqAHoB/EJ","254":"b:f6f+7NP/z298A4ACgAAAIAZAqAHoB/EJ","260":"b:f6f+7NP/z298A4ACgAAAIAZAqAHoB/EJ","283":"b:f6f+7NP/z298A4ACgAAAIAZAqAHoB/EJ","319":"b:f6f+7NP/z298A4ACgAAAIAZAqAHoB/EJ","442":"b:f6f+7NP/z298A4ACgAAAIAZAqAHoB/EJ","463":"b:f6f+7NP/z298A4ACgAAAIAZAqAHoB/EJ","491":"b:f6f+7NP/z298A4ACgAAAIAZAqAHoB/EJ","587":"b:f6f+7NP/z298A4ACgAAAIAZAqAHoB/EJ","609":"b:f6f+7NP/z298A4ACgAAAIAZAqAHoB/EJ"},"packages/cli/src/workflow-runner.ts":{"68":"b:f4DqwJKHDm94AAAAgAAAAAAIAADIB9AB","78":"b:f4DqwJKHDm94AAAAgAAAAAAIAADIB9AB","97":"b:f4DqwJKHDm94AAAAgAAAAAAIAADIB9AB","165":"b:f4DqwJKHDm94AAAAgAAAAAAIAADIB9AB","186":"b:f4DqwJKHDm94AAAAgAAAAAAIAADIB9AB","264":"b:f4DqwJKHDm94AAAAgAAAAAAIAADIB9AB","300":"b:f4DqwJKHDm94AAAAgAAAAAAIAADIB9AB","319":"b:f4DqwJKHDm94AAAAgAAAAAAIAADIB9AB","329":"b:f4DqwJKHDm94AAAAgAAAAAAIAADIB9AB","394":"b:f4DqwJKHDm94AAAAgAAAAAAIAADIB9AB","399":"b:f4DqwJKHDm94AAAAgAAAAAAIAADIB9AB","454":"b:f4DqwJKHDm94AAAAgAAAAAAIAADIB9AB","464":"b:f4DqwJKHDm94AAAAgAAAAAAIAADIB9AB","474":"b:f4DqwJKHDm94AAAAgAAAAAAIAADIB9AB","501":"b:f4DqwJKHDm94AAAAgAAAAAAIAADIB9AB","555":"b:f4DqwJKHDm94AAAAgAAAAAAIAADIB9AB","557":"b:f4DqwJKHDm94AAAAgAAAAAAIAADIB9AB","670":"b:f4DqwJKHDm94AAAAgAAAAAAIAADIB9AB","690":"b:f4DqwJKHDm94AAAAgAAAAAAIAADIB9AB"},"packages/cli/src/workflows/utils.ts":{"19":"b:f5T+zKP/xm16AwAAAAEAAAAQAADoB4EA","26":"b:f5T+zKP/xm16AwAAAAEAAAAQAADoB4EA","27":"b:f5T+zKP/xm16AwAAAAEAAAAQAADoB4EA","35":"b:f5T+zKP/xm16AwAAAAEAAAAQAADoB4EA","41":"b:f5T+zKP/xm16AwAAAAEAAAAQAADoB4EA","46":"b:f5T+zKP/xm16AwAAAAEAAAAQAADoB4EA","56":"b:f5T+zKP/xm16AwAAAAEAAAAQAADoB4EA"},"packages/cli/src/workflows/workflow-creation.service.ts":{"44":"b:R4T+zKP+9mkoAwAAAAgAAAAAAADoBwEA","66":"b:R4T+zKP+9mkoAwAAAAgAAAAAAADoBwEA","187":"b:R4T+zKP+9mkoAwAAAAgAAAAAAADoBwEA","283":"b:R4T+zKP+9mkoAwAAAAgAAAAAAADoBwEA","295":"b:R4T+zKP+9mkoAwAAAAgAAAAAAADoBwEA","300":"b:R4T+zKP+9mkoAwAAAAgAAAAAAADoBwEA"},"packages/cli/src/workflows/workflow-entity-mapper.ts":{"22":"b:R4T+zKP+xmkoAwAAAAAAAAAAAADoBwEA","29":"b:R4T+zKP+xmkoAwAAAAAAAAAAAADoBwEA","30":"b:R4T+zKP+xmkoAwAAAAAAAAAAAADoBwEA"},"packages/cli/src/workflows/workflow-execution.service.ts":{"49":"b:f5BqwIKDDi96AAACAGEAAADcJgDAB4AD","67":"b:f5BqwIKDDi96AAACAGEAAADcJgDAB4AD","111":"b:f5BqwIKDDi96AAACAGEAAADcJgDAB4AD","112":"b:f5BqwIKDDi96AAACAGEAAADcJgDAB4AD","123":"b:f5BqwIKDDi96AAACAGEAAADcJgDAB4AD","295":"b:f5BqwIKDDi96AAACAGEAAADcJgDAB4AD","343":"b:f5BqwIKDDi96AAACAGEAAADcJgDAB4AD","388":"b:f5BqwIKDDi96AAACAGEAAADcJgDAB4AD","551":"b:f5BqwIKDDi96AAACAGEAAADcJgDAB4AD","569":"b:f5BqwIKDDi96AAACAGEAAADcJgDAB4AD","574":"b:f5BqwIKDDi96AAACAGEAAADcJgDAB4AD","577":"b:f5BqwIKDDi96AAACAGEAAADcJgDAB4AD","580":"b:f5BqwIKDDi96AAACAGEAAADcJgDAB4AD","583":"b:f5BqwIKDDi96AAACAGEAAADcJgDAB4AD","591":"b:f5BqwIKDDi96AAACAGEAAADcJgDAB4AD","616":"b:f5BqwIKDDi96AAACAGEAAADcJgDAB4AD","630":"b:f5BqwIKDDi96AAACAGEAAADcJgDAB4AD","643":"b:f5BqwIKDDi96AAACAGEAAADcJgDAB4AD","655":"b:f5BqwIKDDi96AAACAGEAAADcJgDAB4AD","662":"b:f5BqwIKDDi96AAACAGEAAADcJgDAB4AD"},"packages/cli/src/workflows/workflow-finder.service.ts":{"13":"b:R4b+7dPt9mmoAwAsABgAQAAAAADoBwEA","20":"b:R4b+7dPt9mmoAwAsABgAQAAAAADoBwEA","53":"b:R4b+7dPt9mmoAwAsABgAQAAAAADoBwEA","72":"b:R4b+7dPt9mmoAwAsABgAQAAAAADoBwEA","95":"b:R4b+7dPt9mmoAwAsABgAQAAAAADoBwEA","141":"b:R4b+7dPt9mmoAwAsABgAQAAAAADoBwEA","152":"b:R4b+7dPt9mmoAwAsABgAQAAAAADoBwEA","155":"b:R4b+7dPt9mmoAwAsABgAQAAAAADoBwEA","161":"b:R4b+7dPt9mmoAwAsABgAQAAAAADoBwEA","164":"b:R4b+7dPt9mmoAwAsABgAQAAAAADoBwEA","192":"b:R4b+7dPt9mmoAwAsABgAQAAAAADoBwEA","219":"b:R4b+7dPt9mmoAwAsABgAQAAAAADoBwEA","230":"b:R4b+7dPt9mmoAwAsABgAQAAAAADoBwEA","238":"b:R4b+7dPt9mmoAwAsABgAQAAAAADoBwEA","269":"b:R4b+7dPt9mmoAwAsABgAQAAAAADoBwEA","272":"b:R4b+7dPt9mmoAwAsABgAQAAAAADoBwEA","276":"b:R4b+7dPt9mmoAwAsABgAQAAAAADoBwEA","285":"b:R4b+7dPt9mmoAwAsABgAQAAAAADoBwEA","297":"b:R4b+7dPt9mmoAwAsABgAQAAAAADoBwEA","300":"b:R4b+7dPt9mmoAwAsABgAQAAAAADoBwEA","317":"b:R4b+7dPt9mmoAwAsABgAQAAAAADoBwEA"},"packages/cli/src/workflows/workflow-history/workflow-history.controller.ts":{"20":"b:RzAcKNCBlgEkAwAMgAAAAAoNAEAAAAAA","23":"b:RzAcKNCBlgEkAwAMgAAAAAoNAEAAAAAA","40":"b:RzAcKNCBlgEkAwAMgAAAAAoNAEAAAAAA","58":"b:RzAcKNCBlgEkAwAMgAAAAAoNAEAAAAAA","79":"b:RzAcKNCBlgEkAwAMgAAAAAoNAEAAAAAA","92":"b:RzAcKNCBlgEkAwAMgAAAAAoNAEAAAAAA"},"packages/cli/src/workflows/workflow-history/workflow-history.service.ts":{"27":"b:R6T+7NP/xmksAwAOgAAAgAINABjqB8EA","36":"b:R6T+7NP/xmksAwAOgAAAgAINABjqB8EA","71":"b:R6T+7NP/xmksAwAOgAAAgAINABjqB8EA","104":"b:R6T+7NP/xmksAwAOgAAAgAINABjqB8EA","126":"b:R6T+7NP/xmksAwAOgAAAgAINABjqB8EA","169":"b:R6T+7NP/xmksAwAOgAAAgAINABjqB8EA","216":"b:R6T+7NP/xmksAwAOgAAAgAINABjqB8EA","264":"b:R6T+7NP/xmksAwAOgAAAgAINABjqB8EA","284":"b:R6T+7NP/xmksAwAOgAAAgAINABjqB8EA","309":"b:R6T+7NP/xmksAwAOgAAAgAINABjqB8EA","312":"b:R6T+7NP/xmksAwAOgAAAgAINABjqB8EA","330":"b:R6T+7NP/xmksAwAOgAAAgAINABjqB8EA"},"packages/cli/src/workflows/workflow-sharing.service.ts":{"21":"b:R4DqwJKDD096AAACAAAAAAAAAADQB+AD","36":"b:R4DqwJKDD096AAACAAAAAAAAAADQB+AD","44":"b:R4DqwJKDD096AAACAAAAAAAAAADQB+AD","69":"b:R4DqwJKDD096AAACAAAAAAAAAADQB+AD","72":"b:R4DqwJKDD096AAACAAAAAAAAAADQB+AD","86":"b:R4DqwJKDD096AAACAAAAAAAAAADQB+AD","89":"b:R4DqwJKDD096AAACAAAAAAAAAADQB+AD","97":"b:R4DqwJKDD096AAACAAAAAAAAAADQB+AD","100":"b:R4DqwJKDD096AAACAAAAAAAAAADQB+AD","106":"b:R4DqwJKDD096AAACAAAAAAAAAADQB+AD","113":"b:R4DqwJKDD096AAACAAAAAAAAAADQB+AD","126":"b:R4DqwJKDD096AAACAAAAAAAAAADQB+AD"},"packages/cli/src/workflows/workflow-static-data.service.ts":{"11":"b:RgDIIAEEBksQAAACgAAAgAIACBvYB/AD","18":"b:RgDIIAEEBksQAAACgAAAgAIACBvYB/AD","27":"b:RgDIIAEEBksQAAACgAAAgAIACBvYB/AD","48":"b:RgDIIAEEBksQAAACgAAAgAIACBvYB/AD","54":"b:RgDIIAEEBksQAAACgAAAgAIACBvYB/AD"},"packages/cli/src/workflows/workflow-validation.service.ts":{"50":"b:R63+7NP/zmkoAwACAAAAoAAAAAP8B+EL","51":"b:R63+7NP/zmkoAwACAAAAoAAAAAP8B+EL","56":"b:R63+7NP/zmkoAwACAAAAoAAAAAP8B+EL","67":"b:R63+7NP/zmkoAwACAAAAoAAAAAP8B+EL","129":"b:R63+7NP/zmkoAwACAAAAoAAAAAP8B+EL","130":"b:R63+7NP/zmkoAwACAAAAoAAAAAP8B+EL","153":"b:R63+7NP/zmkoAwACAAAAoAAAAAP8B+EL","185":"b:R63+7NP/zmkoAwACAAAAoAAAAAP8B+EL","246":"b:R63+7NP/zmkoAwACAAAAoAAAAAP8B+EL","267":"b:R63+7NP/zmkoAwACAAAAoAAAAAP8B+EL","306":"b:R63+7NP/zmkoAwACAAAAoAAAAAP8B+EL","343":"b:R63+7NP/zmkoAwACAAAAoAAAAAP8B+EL","371":"b:R63+7NP/zmkoAwACAAAAoAAAAAP8B+EL","399":"b:R63+7NP/zmkoAwACAAAAoAAAAAP8B+EL","440":"b:R63+7NP/zmkoAwACAAAAoAAAAAP8B+EL","445":"b:R63+7NP/zmkoAwACAAAAoAAAAAP8B+EL","479":"b:R63+7NP/zmkoAwACAAAAoAAAAAP8B+EL","498":"b:R63+7NP/zmkoAwACAAAAoAAAAAP8B+EL","523":"b:R63+7NP/zmkoAwACAAAAoAAAAAP8B+EL","531":"b:R63+7NP/zmkoAwACAAAAoAAAAAP8B+EL","552":"b:R63+7NP/zmkoAwACAAAAoAAAAAP8B+EL"},"packages/cli/src/workflows/workflow.service.ee.ts":{"49":"b:R63+zPP//288A4AAgIgAYARAKEHoB4AB","66":"b:R63+zPP//288A4AAgIgAYARAKEHoB4AB","79":"b:R63+zPP//288A4AAgIgAYARAKEHoB4AB","81":"b:R63+zPP//288A4AAgIgAYARAKEHoB4AB","88":"b:R63+zPP//288A4AAgIgAYARAKEHoB4AB","99":"b:R63+zPP//288A4AAgIgAYARAKEHoB4AB","111":"b:R63+zPP//288A4AAgIgAYARAKEHoB4AB","121":"b:R63+zPP//288A4AAgIgAYARAKEHoB4AB","125":"b:R63+zPP//288A4AAgIgAYARAKEHoB4AB","137":"b:R63+zPP//288A4AAgIgAYARAKEHoB4AB","138":"b:R63+zPP//288A4AAgIgAYARAKEHoB4AB","152":"b:R63+zPP//288A4AAgIgAYARAKEHoB4AB","156":"b:R63+zPP//288A4AAgIgAYARAKEHoB4AB","160":"b:R63+zPP//288A4AAgIgAYARAKEHoB4AB","165":"b:R63+zPP//288A4AAgIgAYARAKEHoB4AB","173":"b:R63+zPP//288A4AAgIgAYARAKEHoB4AB","197":"b:R63+zPP//288A4AAgIgAYARAKEHoB4AB","209":"b:R63+zPP//288A4AAgIgAYARAKEHoB4AB","221":"b:R63+zPP//288A4AAgIgAYARAKEHoB4AB","225":"b:R63+zPP//288A4AAgIgAYARAKEHoB4AB","228":"b:R63+zPP//288A4AAgIgAYARAKEHoB4AB","245":"b:R63+zPP//288A4AAgIgAYARAKEHoB4AB","254":"b:R63+zPP//288A4AAgIgAYARAKEHoB4AB","267":"b:R63+zPP//288A4AAgIgAYARAKEHoB4AB","271":"b:R63+zPP//288A4AAgIgAYARAKEHoB4AB","273":"b:R63+zPP//288A4AAgIgAYARAKEHoB4AB","283":"b:R63+zPP//288A4AAgIgAYARAKEHoB4AB","306":"b:R63+zPP//288A4AAgIgAYARAKEHoB4AB","332":"b:R63+zPP//288A4AAgIgAYARAKEHoB4AB","387":"b:R63+zPP//288A4AAgIgAYARAKEHoB4AB","409":"b:R63+zPP//288A4AAgIgAYARAKEHoB4AB","478":"b:R63+zPP//288A4AAgIgAYARAKEHoB4AB","501":"b:R63+zPP//288A4AAgIgAYARAKEHoB4AB","526":"b:R63+zPP//288A4AAgIgAYARAKEHoB4AB","551":"b:R63+zPP//288A4AAgIgAYARAKEHoB4AB","566":"b:R63+zPP//288A4AAgIgAYARAKEHoB4AB","592":"b:R63+zPP//288A4AAgIgAYARAKEHoB4AB","603":"b:R63+zPP//288A4AAgIgAYARAKEHoB4AB","640":"b:R63+zPP//288A4AAgIgAYARAKEHoB4AB","644":"b:R63+zPP//288A4AAgIgAYARAKEHoB4AB","666":"b:R63+zPP//288A4AAgIgAYARAKEHoB4AB","671":"b:R63+zPP//288A4AAgIgAYARAKEHoB4AB","684":"b:R63+zPP//288A4AAgIgAYARAKEHoB4AB","698":"b:R63+zPP//288A4AAgIgAYARAKEHoB4AB","704":"b:R63+zPP//288A4AAgIgAYARAKEHoB4AB","725":"b:R63+zPP//288A4AAgIgAYARAKEHoB4AB"},"packages/cli/src/workflows/workflow.service.ts":{"69":"b:f6T+7/P//mmsG/AiABhAtARAKBv6D+AD","102":"b:f6T+7/P//mmsG/AiABhAtARAKBv6D+AD","169":"b:f6T+7/P//mmsG/AiABhAtARAKBv6D+AD","214":"b:f6T+7/P//mmsG/AiABhAtARAKBv6D+AD","235":"b:f6T+7/P//mmsG/AiABhAtARAKBv6D+AD","242":"b:f6T+7/P//mmsG/AiABhAtARAKBv6D+AD","246":"b:f6T+7/P//mmsG/AiABhAtARAKBv6D+AD","252":"b:f6T+7/P//mmsG/AiABhAtARAKBv6D+AD","264":"b:f6T+7/P//mmsG/AiABhAtARAKBv6D+AD","267":"b:f6T+7/P//mmsG/AiABhAtARAKBv6D+AD","268":"b:f6T+7/P//mmsG/AiABhAtARAKBv6D+AD","271":"b:f6T+7/P//mmsG/AiABhAtARAKBv6D+AD","272":"b:f6T+7/P//mmsG/AiABhAtARAKBv6D+AD","276":"b:f6T+7/P//mmsG/AiABhAtARAKBv6D+AD","282":"b:f6T+7/P//mmsG/AiABhAtARAKBv6D+AD","287":"b:f6T+7/P//mmsG/AiABhAtARAKBv6D+AD","293":"b:f6T+7/P//mmsG/AiABhAtARAKBv6D+AD","301":"b:f6T+7/P//mmsG/AiABhAtARAKBv6D+AD","308":"b:f6T+7/P//mmsG/AiABhAtARAKBv6D+AD","312":"b:f6T+7/P//mmsG/AiABhAtARAKBv6D+AD","314":"b:f6T+7/P//mmsG/AiABhAtARAKBv6D+AD","329":"b:f6T+7/P//mmsG/AiABhAtARAKBv6D+AD","650":"b:f6T+7/P//mmsG/AiABhAtARAKBv6D+AD","722":"b:f6T+7/P//mmsG/AiABhAtARAKBv6D+AD","746":"b:f6T+7/P//mmsG/AiABhAtARAKBv6D+AD","756":"b:f6T+7/P//mmsG/AiABhAtARAKBv6D+AD","774":"b:f6T+7/P//mmsG/AiABhAtARAKBv6D+AD","984":"b:f6T+7/P//mmsG/AiABhAtARAKBv6D+AD","1065":"b:f6T+7/P//mmsG/AiABhAtARAKBv6D+AD","1105":"b:f6T+7/P//mmsG/AiABhAtARAKBv6D+AD","1140":"b:f6T+7/P//mmsG/AiABhAtARAKBv6D+AD","1199":"b:f6T+7/P//mmsG/AiABhAtARAKBv6D+AD","1269":"b:f6T+7/P//mmsG/AiABhAtARAKBv6D+AD","1307":"b:f6T+7/P//mmsG/AiABhAtARAKBv6D+AD","1311":"b:f6T+7/P//mmsG/AiABhAtARAKBv6D+AD","1315":"b:f6T+7/P//mmsG/AiABhAtARAKBv6D+AD","1319":"b:f6T+7/P//mmsG/AiABhAtARAKBv6D+AD","1331":"b:f6T+7/P//mmsG/AiABhAtARAKBv6D+AD","1339":"b:f6T+7/P//mmsG/AiABhAtARAKBv6D+AD","1348":"b:f6T+7/P//mmsG/AiABhAtARAKBv6D+AD","1349":"b:f6T+7/P//mmsG/AiABhAtARAKBv6D+AD","1360":"b:f6T+7/P//mmsG/AiABhAtARAKBv6D+AD","1361":"b:f6T+7/P//mmsG/AiABhAtARAKBv6D+AD","1366":"b:f6T+7/P//mmsG/AiABhAtARAKBv6D+AD","1372":"b:f6T+7/P//mmsG/AiABhAtARAKBv6D+AD","1384":"b:f6T+7/P//mmsG/AiABhAtARAKBv6D+AD","1391":"b:f6T+7/P//mmsG/AiABhAtARAKBv6D+AD","1402":"b:f6T+7/P//mmsG/AiABhAtARAKBv6D+AD","1404":"b:f6T+7/P//mmsG/AiABhAtARAKBv6D+AD","1411":"b:f6T+7/P//mmsG/AiABhAtARAKBv6D+AD","1421":"b:f6T+7/P//mmsG/AiABhAtARAKBv6D+AD","1422":"b:f6T+7/P//mmsG/AiABhAtARAKBv6D+AD","1442":"b:f6T+7/P//mmsG/AiABhAtARAKBv6D+AD","1469":"b:f6T+7/P//mmsG/AiABhAtARAKBv6D+AD","1508":"b:f6T+7/P//mmsG/AiABhAtARAKBv6D+AD","1530":"b:f6T+7/P//mmsG/AiABhAtARAKBv6D+AD","1537":"b:f6T+7/P//mmsG/AiABhAtARAKBv6D+AD","1584":"b:f6T+7/P//mmsG/AiABhAtARAKBv6D+AD","1590":"b:f6T+7/P//mmsG/AiABhAtARAKBv6D+AD"},"packages/cli/src/workflows/workflows.controller.ts":{"73":"b:R7X+//P//2/+P/hugBlQ4AwMIFv6D+AD","100":"b:R7X+//P//2/+P/hugBlQ4AwMIFv6D+AD","132":"b:R7X+//P//2/+P/hugBlQ4AwMIFv6D+AD","157":"b:R7X+//P//2/+P/hugBlQ4AwMIFv6D+AD","173":"b:R7X+//P//2/+P/hugBlQ4AwMIFv6D+AD","207":"b:R7X+//P//2/+P/hugBlQ4AwMIFv6D+AD","290":"b:R7X+//P//2/+P/hugBlQ4AwMIFv6D+AD","297":"b:R7X+//P//2/+P/hugBlQ4AwMIFv6D+AD","341":"b:R7X+//P//2/+P/hugBlQ4AwMIFv6D+AD","352":"b:R7X+//P//2/+P/hugBlQ4AwMIFv6D+AD","373":"b:R7X+//P//2/+P/hugBlQ4AwMIFv6D+AD","407":"b:R7X+//P//2/+P/hugBlQ4AwMIFv6D+AD","441":"b:R7X+//P//2/+P/hugBlQ4AwMIFv6D+AD","475":"b:R7X+//P//2/+P/hugBlQ4AwMIFv6D+AD","506":"b:R7X+//P//2/+P/hugBlQ4AwMIFv6D+AD","563":"b:R7X+//P//2/+P/hugBlQ4AwMIFv6D+AD","569":"b:R7X+//P//2/+P/hugBlQ4AwMIFv6D+AD","583":"b:R7X+//P//2/+P/hugBlQ4AwMIFv6D+AD","584":"b:R7X+//P//2/+P/hugBlQ4AwMIFv6D+AD","588":"b:R7X+//P//2/+P/hugBlQ4AwMIFv6D+AD","589":"b:R7X+//P//2/+P/hugBlQ4AwMIFv6D+AD","593":"b:R7X+//P//2/+P/hugBlQ4AwMIFv6D+AD","594":"b:R7X+//P//2/+P/hugBlQ4AwMIFv6D+AD","615":"b:R7X+//P//2/+P/hugBlQ4AwMIFv6D+AD","639":"b:R7X+//P//2/+P/hugBlQ4AwMIFv6D+AD","646":"b:R7X+//P//2/+P/hugBlQ4AwMIFv6D+AD","663":"b:R7X+//P//2/+P/hugBlQ4AwMIFv6D+AD","684":"b:R7X+//P//2/+P/hugBlQ4AwMIFv6D+AD","706":"b:R7X+//P//2/+P/hugBlQ4AwMIFv6D+AD","732":"b:R7X+//P//2/+P/hugBlQ4AwMIFv6D+AD","751":"b:R7X+//P//2/+P/hugBlQ4AwMIFv6D+AD"},"packages/community-node-types-utils.ts":{"34":"b:B4RezALolgEoJwgMAAAAAAAAAAAAAAAA","40":"b:B4RezALolgEoJwgMAAAAAAAAAAAAAAAA","57":"b:B4RezALolgEoJwgMAAAAAAAAAAAAAAAA"},"packages/community-node-types.controller.ts":{"9":"b:B4RezNLplgEoJwgMAAAAAAAAAAAAAAAA","12":"b:B4RezNLplgEoJwgMAAAAAAAAAAAAAAAA","17":"b:B4RezNLplgEoJwgMAAAAAAAAAAAAAAAA"},"packages/community-node-types.service.ts":{"31":"b:B4RezNLplgEoJwgMAAAAAAAAAAAAAAAA","37":"b:B4RezNLplgEoJwgMAAAAAAAAAAAAAAAA","54":"b:B4RezNLplgEoJwgMAAAAAAAAAAAAAAAA","85":"b:B4RezNLplgEoJwgMAAAAAAAAAAAAAAAA","127":"b:B4RezNLplgEoJwgMAAAAAAAAAAAAAAAA","135":"b:B4RezNLplgEoJwgMAAAAAAAAAAAAAAAA","151":"b:B4RezNLplgEoJwgMAAAAAAAAAAAAAAAA","153":"b:B4RezNLplgEoJwgMAAAAAAAAAAAAAAAA","166":"b:B4RezNLplgEoJwgMAAAAAAAAAAAAAAAA","188":"b:B4RezNLplgEoJwgMAAAAAAAAAAAAAAAA","194":"b:B4RezNLplgEoJwgMAAAAAAAAAAAAAAAA","199":"b:B4RezNLplgEoJwgMAAAAAAAAAAAAAAAA","204":"b:B4RezNLplgEoJwgMAAAAAAAAAAAAAAAA","206":"b:B4RezNLplgEoJwgMAAAAAAAAAAAAAAAA","208":"b:B4RezNLplgEoJwgMAAAAAAAAAAAAAAAA","211":"b:B4RezNLplgEoJwgMAAAAAAAAAAAAAAAA","218":"b:B4RezNLplgEoJwgMAAAAAAAAAAAAAAAA","224":"b:B4RezNLplgEoJwgMAAAAAAAAAAAAAAAA","231":"b:B4RezNLplgEoJwgMAAAAAAAAAAAAAAAA","236":"b:B4RezNLplgEoJwgMAAAAAAAAAAAAAAAA"},"packages/core/src/execution-engine/execution-context-hook-registry.service.ts":{"18":"b:f4DqwJKHBm94AAAAAAAAAAAAAAHIB/AB","34":"b:f4DqwJKHBm94AAAAAAAAAAAAAAHIB/AB","89":"b:f4DqwJKHBm94AAAAAAAAAAAAAAHIB/AB","98":"b:f4DqwJKHBm94AAAAAAAAAAAAAAHIB/AB","111":"b:f4DqwJKHBm94AAAAAAAAAAAAAAHIB/AB","112":"b:f4DqwJKHBm94AAAAAAAAAAAAAAHIB/AB","117":"b:f4DqwJKHBm94AAAAAAAAAAAAAAHIB/AB","127":"b:f4DqwJKHBm94AAAAAAAAAAAAAAHIB/AB"},"packages/core/src/execution-engine/execution-context.service.ts":{"22":"b:f5LqwJKHBm96AAAAAAEAAAgQAAHIB/AB","28":"b:f5LqwJKHBm96AAAAAAEAAAgQAAHIB/AB","33":"b:f5LqwJKHBm96AAAAAAEAAAgQAAHIB/AB","52":"b:f5LqwJKHBm96AAAAAAEAAAgQAAHIB/AB","61":"b:f5LqwJKHBm96AAAAAAEAAAgQAAHIB/AB","70":"b:f5LqwJKHBm96AAAAAAEAAAgQAAHIB/AB","82":"b:f5LqwJKHBm96AAAAAAEAAAgQAAHIB/AB","104":"b:f5LqwJKHBm96AAAAAAEAAAgQAAHIB/AB","132":"b:f5LqwJKHBm96AAAAAAEAAAgQAAHIB/AB"},"packages/core/src/execution-engine/execution-context.ts":{"104":"b:f4DqwJKHBm94AAAAAAAAAAAAAADIB9AA"},"packages/core/src/execution-engine/execution-lifecycle-hooks.ts":{"103":"b:f4DqwJKHBm94AAAAAAAAAAAAAADIB9AA","110":"b:f4DqwJKHBm94AAAAAAAAAAAAAADIB9AA","118":"b:f4DqwJKHBm94AAAAAAAAAAAAAADIB9AA"},"packages/core/src/execution-engine/node-execution-context/base-execute-context.ts":{"48":"b:f4LqwJKHBk94AIAChAEAAAgGhoHAB/AJ","63":"b:f4LqwJKHBk94AIAChAEAAAgGhoHAB/AJ","67":"b:f4LqwJKHBk94AIAChAEAAAgGhoHAB/AJ","71":"b:f4LqwJKHBk94AIAChAEAAAgGhoHAB/AJ","72":"b:f4LqwJKHBk94AIAChAEAAAgGhoHAB/AJ","79":"b:f4LqwJKHBk94AIAChAEAAAgGhoHAB/AJ","80":"b:f4LqwJKHBk94AIAChAEAAAgGhoHAB/AJ","92":"b:f4LqwJKHBk94AIAChAEAAAgGhoHAB/AJ","96":"b:f4LqwJKHBk94AIAChAEAAAgGhoHAB/AJ","100":"b:f4LqwJKHBk94AIAChAEAAAgGhoHAB/AJ","105":"b:f4LqwJKHBk94AIAChAEAAAgGhoHAB/AJ","115":"b:f4LqwJKHBk94AIAChAEAAAgGhoHAB/AJ","127":"b:f4LqwJKHBk94AIAChAEAAAgGhoHAB/AJ","134":"b:f4LqwJKHBk94AIAChAEAAAgGhoHAB/AJ","193":"b:f4LqwJKHBk94AIAChAEAAAgGhoHAB/AJ","223":"b:f4LqwJKHBk94AIAChAEAAAgGhoHAB/AJ","237":"b:f4LqwJKHBk94AIAChAEAAAgGhoHAB/AJ","253":"b:f4LqwJKHBk94AIAChAEAAAgGhoHAB/AJ","257":"b:f4LqwJKHBk94AIAChAEAAAgGhoHAB/AJ","275":"b:f4LqwJKHBk94AIAChAEAAAgGhoHAB/AJ","283":"b:f4LqwJKHBk94AIAChAEAAAgGhoHAB/AJ","299":"b:f4LqwJKHBk94AIAChAEAAAgGhoHAB/AJ","305":"b:f4LqwJKHBk94AIAChAEAAAgGhoHAB/AJ","330":"b:f4LqwJKHBk94AIAChAEAAAgGhoHAB/AJ","341":"b:f4LqwJKHBk94AIAChAEAAAgGhoHAB/AJ","366":"b:f4LqwJKHBk94AIAChAEAAAgGhoHAB/AJ"},"packages/core/src/execution-engine/node-execution-context/execute-context.ts":{"54":"b:f4DqwJKHDk94AAAAgAIAAAAEAADAB9AB","101":"b:f4DqwJKHDk94AAAAgAIAAAAEAADAB9AB","103":"b:f4DqwJKHDk94AAAAgAIAAAAEAADAB9AB","111":"b:f4DqwJKHDk94AAAAgAIAAAAEAADAB9AB","115":"b:f4DqwJKHDk94AAAAgAIAAAAEAADAB9AB","125":"b:f4DqwJKHDk94AAAAgAIAAAAEAADAB9AB","140":"b:f4DqwJKHDk94AAAAgAIAAAAEAADAB9AB","144":"b:f4DqwJKHDk94AAAAgAIAAAAEAADAB9AB","159":"b:f4DqwJKHDk94AAAAgAIAAAAEAADAB9AB","184":"b:f4DqwJKHDk94AAAAgAIAAAAEAADAB9AB","205":"b:f4DqwJKHDk94AAAAgAIAAAAEAADAB9AB","213":"b:f4DqwJKHDk94AAAAgAIAAAAEAADAB9AB","215":"b:f4DqwJKHDk94AAAAgAIAAAAEAADAB9AB","227":"b:f4DqwJKHDk94AAAAgAIAAAAEAADAB9AB","232":"b:f4DqwJKHDk94AAAAgAIAAAAEAADAB9AB","237":"b:f4DqwJKHDk94AAAAgAIAAAAEAADAB9AB","241":"b:f4DqwJKHDk94AAAAgAIAAAAEAADAB9AB","245":"b:f4DqwJKHDk94AAAAgAIAAAAEAADAB9AB","250":"b:f4DqwJKHDk94AAAAgAIAAAAEAADAB9AB"},"packages/core/src/execution-engine/node-execution-context/node-execution-context.ts":{"52":"b:f4rq5JOHjm94AAACgAQAgAYMAgPcB/AL","64":"b:f4rq5JOHjm94AAACgAQAgAYMAgPcB/AL","69":"b:f4rq5JOHjm94AAACgAQAgAYMAgPcB/AL","81":"b:f4rq5JOHjm94AAACgAQAgAYMAgPcB/AL","85":"b:f4rq5JOHjm94AAACgAQAgAYMAgPcB/AL","89":"b:f4rq5JOHjm94AAACgAQAgAYMAgPcB/AL","93":"b:f4rq5JOHjm94AAACgAQAgAYMAgPcB/AL","98":"b:f4rq5JOHjm94AAACgAQAgAYMAgPcB/AL","102":"b:f4rq5JOHjm94AAACgAQAgAYMAgPcB/AL","106":"b:f4rq5JOHjm94AAACgAQAgAYMAgPcB/AL","128":"b:f4rq5JOHjm94AAACgAQAgAYMAgPcB/AL","166":"b:f4rq5JOHjm94AAACgAQAgAYMAgPcB/AL","177":"b:f4rq5JOHjm94AAACgAQAgAYMAgPcB/AL","181":"b:f4rq5JOHjm94AAACgAQAgAYMAgPcB/AL","186":"b:f4rq5JOHjm94AAACgAQAgAYMAgPcB/AL","197":"b:f4rq5JOHjm94AAACgAQAgAYMAgPcB/AL","207":"b:f4rq5JOHjm94AAACgAQAgAYMAgPcB/AL","212":"b:f4rq5JOHjm94AAACgAQAgAYMAgPcB/AL","214":"b:f4rq5JOHjm94AAACgAQAgAYMAgPcB/AL","218":"b:f4rq5JOHjm94AAACgAQAgAYMAgPcB/AL","223":"b:f4rq5JOHjm94AAACgAQAgAYMAgPcB/AL","225":"b:f4rq5JOHjm94AAACgAQAgAYMAgPcB/AL","229":"b:f4rq5JOHjm94AAACgAQAgAYMAgPcB/AL","232":"b:f4rq5JOHjm94AAACgAQAgAYMAgPcB/AL","233":"b:f4rq5JOHjm94AAACgAQAgAYMAgPcB/AL","234":"b:f4rq5JOHjm94AAACgAQAgAYMAgPcB/AL","237":"b:f4rq5JOHjm94AAACgAQAgAYMAgPcB/AL","241":"b:f4rq5JOHjm94AAACgAQAgAYMAgPcB/AL","245":"b:f4rq5JOHjm94AAACgAQAgAYMAgPcB/AL","249":"b:f4rq5JOHjm94AAACgAQAgAYMAgPcB/AL","253":"b:f4rq5JOHjm94AAACgAQAgAYMAgPcB/AL","257":"b:f4rq5JOHjm94AAACgAQAgAYMAgPcB/AL","265":"b:f4rq5JOHjm94AAACgAQAgAYMAgPcB/AL","269":"b:f4rq5JOHjm94AAACgAQAgAYMAgPcB/AL","294":"b:f4rq5JOHjm94AAACgAQAgAYMAgPcB/AL","298":"b:f4rq5JOHjm94AAACgAQAgAYMAgPcB/AL","307":"b:f4rq5JOHjm94AAACgAQAgAYMAgPcB/AL","318":"b:f4rq5JOHjm94AAACgAQAgAYMAgPcB/AL","369":"b:f4rq5JOHjm94AAACgAQAgAYMAgPcB/AL","474":"b:f4rq5JOHjm94AAACgAQAgAYMAgPcB/AL","479":"b:f4rq5JOHjm94AAACgAQAgAYMAgPcB/AL","489":"b:f4rq5JOHjm94AAACgAQAgAYMAgPcB/AL","588":"b:f4rq5JOHjm94AAACgAQAgAYMAgPcB/AL","603":"b:f4rq5JOHjm94AAACgAQAgAYMAgPcB/AL"},"packages/core/src/execution-engine/node-execution-context/supply-data-context.ts":{"64":[0,1,2,3,4,5,9,11,6],"109":[0,1,2,3,4,5,9,11,6],"111":[0,1,2,3,4,5,9,11,6],"119":[0,1,2,3,4,5,9,11,6],"126":[0,1,2,3,4,5,9,11,6],"141":[0,1,2,3,4,5,9,11,6],"164":[0,1,2,3,4,5,9,11,6],"185":[0,1,2,3,4,5,9,11,6],"193":[0,1,2,3,4,5,9,11,6],"199":[0,1,2,3,4,5,9,11,6],"204":[0,1,2,3,4,5,9,11,6],"220":[0,1,2,3,4,5,9,11,6],"233":[0,1,2,3,4,5,9,11,6],"249":[0,1,2,3,4,5,9,11,6],"259":[0,1,2,3,4,5,9,11,6],"395":[0,1,2,3,4,5,9,11,6],"422":[0,1,2,3,4,5,9,11,6],"424":[0,1,2,3,4,5,9,11,6],"436":[0,1,2,3,4,5,9,11,6]},"packages/core/src/execution-engine/node-execution-context/trigger-context.ts":{"21":"b:R5AiwJKHBi14AAAAAAEAAAAAAAAABoAB","25":"b:R5AiwJKHBi14AAAAAAEAAAAAAAAABoAB","29":"b:R5AiwJKHBi14AAAAAAEAAAAAAAAABoAB","36":"b:R5AiwJKHBi14AAAAAAEAAAAAAAAABoAB","63":"b:R5AiwJKHBi14AAAAAAEAAAAAAAAABoAB","67":"b:R5AiwJKHBi14AAAAAAEAAAAAAAAABoAB"},"packages/core/src/execution-engine/node-execution-context/utils/ai-tool-types.ts":{"13":[0,1,2,3,4,5,9,11,6],"17":[0,1,2,3,4,5,9,11,6],"23":[0,1,2,3,4,5,9,11,6]},"packages/core/src/execution-engine/node-execution-context/utils/binary-helper-functions.ts":{"33":"b:f4DqwJKHDm94AAAAAAIAAAAAAADIB9AA","40":"b:f4DqwJKHDm94AAAAAAIAAAAAAADIB9AA","47":"b:f4DqwJKHDm94AAAAAAIAAAAAAADIB9AA","56":"b:f4DqwJKHDm94AAAAAAIAAAAAAADIB9AA","128":"b:f4DqwJKHDm94AAAAAAIAAAAAAADIB9AA","155":"b:f4DqwJKHDm94AAAAAAIAAAAAAADIB9AA","167":"b:f4DqwJKHDm94AAAAAAIAAAAAAADIB9AA","180":"b:f4DqwJKHDm94AAAAAAIAAAAAAADIB9AA","244":"b:f4DqwJKHDm94AAAAAAIAAAAAAADIB9AA","342":"b:f4DqwJKHDm94AAAAAAIAAAAAAADIB9AA","351":"b:f4DqwJKHDm94AAAAAAIAAAAAAADIB9AA","355":"b:f4DqwJKHDm94AAAAAAIAAAAAAADIB9AA","357":"b:f4DqwJKHDm94AAAAAAIAAAAAAADIB9AA","359":"b:f4DqwJKHDm94AAAAAAIAAAAAAADIB9AA"},"packages/core/src/execution-engine/node-execution-context/utils/cleanup-parameter-data.ts":{"8":"b:f4DqxJKHj094AAAAgCAgAAIEAADIB9AA","14":"b:f4DqxJKHj094AAAAgCAgAAIEAADIB9AA","19":"b:f4DqxJKHj094AAAAgCAgAAIEAADIB9AA"},"packages/core/src/execution-engine/node-execution-context/utils/credential-check-helper-functions.ts":{"3":"b:f4DqwJKHBk94AAAAAAAAAAAEAADAB9AA","9":"b:f4DqwJKHBk94AAAAAAAAAAAEAADAB9AA"},"packages/core/src/execution-engine/node-execution-context/utils/custom-data.ts":{"15":"b:f4DqwJKHDkd4AAAAAAAAAAIEAAAAAFAA","23":"b:f4DqwJKHDkd4AAAAAAAAAAIEAAAAAFAA","34":"b:f4DqwJKHDkd4AAAAAAAAAAIEAAAAAFAA","45":"b:f4DqwJKHDkd4AAAAAAAAAAIEAAAAAFAA","48":"b:f4DqwJKHDkd4AAAAAAAAAAIEAAAAAFAA"},"packages/core/src/execution-engine/node-execution-context/utils/data-table-helper-functions.ts":{"8":"b:f4DqxJKHBk94AAAAAAAAAAAEAADAB9AA","16":"b:f4DqxJKHBk94AAAAAAAAAAAEAADAB9AA","22":"b:f4DqxJKHBk94AAAAAAAAAAAEAADAB9AA"},"packages/core/src/execution-engine/node-execution-context/utils/deduplication-helper-functions.ts":{"16":"b:f4DqwJKHBk94AAAAAAAAAAAEAADAB9AA","30":"b:f4DqwJKHBk94AAAAAAAAAAAEAADAB9AA","46":"b:f4DqwJKHBk94AAAAAAAAAAAEAADAB9AA","60":"b:f4DqwJKHBk94AAAAAAAAAAAEAADAB9AA","72":"b:f4DqwJKHBk94AAAAAAAAAAAEAADAB9AA","84":"b:f4DqwJKHBk94AAAAAAAAAAAEAADAB9AA","88":"b:f4DqwJKHBk94AAAAAAAAAAAEAADAB9AA","95":"b:f4DqwJKHBk94AAAAAAAAAAAEAADAB9AA","109":"b:f4DqwJKHBk94AAAAAAAAAAAEAADAB9AA","116":"b:f4DqwJKHBk94AAAAAAAAAAAEAADAB9AA","122":"b:f4DqwJKHBk94AAAAAAAAAAAEAADAB9AA"},"packages/core/src/execution-engine/node-execution-context/utils/execution-metadata.ts":{"8":[19,1,2,3,4,5,57,129,6],"46":[19,1,2,3,4,5,57,129,6],"51":[19,1,2,3,4,5,57,129,6],"63":[19,1,2,3,4,5,57,129,6],"70":[19,1,2,3,4,5,57,129,6]},"packages/core/src/execution-engine/node-execution-context/utils/file-system-helper-functions.ts":{"41":"b:f4DqwJKHBk94AAAAAAIAAAAEAADAB9AA","47":"b:f4DqwJKHBk94AAAAAAIAAAAEAADAB9AA","48":"b:f4DqwJKHBk94AAAAAAIAAAAEAADAB9AA","49":"b:f4DqwJKHBk94AAAAAAIAAAAEAADAB9AA","54":"b:f4DqwJKHBk94AAAAAAIAAAAEAADAB9AA","60":"b:f4DqwJKHBk94AAAAAAIAAAAEAADAB9AA","77":"b:f4DqwJKHBk94AAAAAAIAAAAEAADAB9AA","85":"b:f4DqwJKHBk94AAAAAAIAAAAEAADAB9AA","86":"b:f4DqwJKHBk94AAAAAAIAAAAEAADAB9AA","87":"b:f4DqwJKHBk94AAAAAAIAAAAEAADAB9AA","96":"b:f4DqwJKHBk94AAAAAAIAAAAEAADAB9AA","105":"b:f4DqwJKHBk94AAAAAAIAAAAEAADAB9AA","126":"b:f4DqwJKHBk94AAAAAAIAAAAEAADAB9AA","132":"b:f4DqwJKHBk94AAAAAAIAAAAEAADAB9AA","142":"b:f4DqwJKHBk94AAAAAAIAAAAEAADAB9AA","148":"b:f4DqwJKHBk94AAAAAAIAAAAEAADAB9AA","149":"b:f4DqwJKHBk94AAAAAAIAAAAEAADAB9AA","157":"b:f4DqwJKHBk94AAAAAAIAAAAEAADAB9AA","169":"b:f4DqwJKHBk94AAAAAAIAAAAEAADAB9AA","177":"b:f4DqwJKHBk94AAAAAAIAAAAEAADAB9AA","189":"b:f4DqwJKHBk94AAAAAAIAAAAEAADAB9AA","242":"b:f4DqwJKHBk94AAAAAAIAAAAEAADAB9AA","245":"b:f4DqwJKHBk94AAAAAAIAAAAEAADAB9AA","251":"b:f4DqwJKHBk94AAAAAAIAAAAEAADAB9AA","278":"b:f4DqwJKHBk94AAAAAAIAAAAEAADAB9AA","279":"b:f4DqwJKHBk94AAAAAAIAAAAEAADAB9AA","329":"b:f4DqwJKHBk94AAAAAAIAAAAEAADAB9AA","333":"b:f4DqwJKHBk94AAAAAAIAAAAEAADAB9AA","423":"b:f4DqwJKHBk94AAAAAAIAAAAEAADAB9AA","435":"b:f4DqwJKHBk94AAAAAAIAAAAEAADAB9AA","438":"b:f4DqwJKHBk94AAAAAAIAAAAEAADAB9AA","442":"b:f4DqwJKHBk94AAAAAAIAAAAEAADAB9AA","450":"b:f4DqwJKHBk94AAAAAAIAAAAEAADAB9AA"},"packages/core/src/execution-engine/node-execution-context/utils/get-additional-keys.ts":{"13":"b:f4DqxJKHjk94AgAAAAAAAAAEAADoB9AA","20":"b:f4DqxJKHjk94AgAAAAAAAAAEAADoB9AA","62":"b:f4DqxJKHjk94AgAAAAAAAAAEAADoB9AA"},"packages/core/src/execution-engine/node-execution-context/utils/get-input-connection-data.ts":{"46":[0,1,2,3,4,5,98,9,11,6],"52":[0,1,2,3,4,5,98,9,11,6],"64":[0,1,2,3,4,5,98,9,11,6],"68":[0,1,2,3,4,5,98,9,11,6],"78":[0,1,2,3,4,5,98,9,11,6],"91":[0,1,2,3,4,5,98,9,11,6],"116":[0,1,2,3,4,5,98,9,11,6],"156":[0,1,2,3,4,5,98,9,11,6],"160":[0,1,2,3,4,5,98,9,11,6],"169":[0,1,2,3,4,5,98,9,11,6],"172":[0,1,2,3,4,5,98,9,11,6],"181":[0,1,2,3,4,5,98,9,11,6],"182":[0,1,2,3,4,5,98,9,11,6],"190":[0,1,2,3,4,5,98,9,11,6],"221":[0,1,2,3,4,5,98,9,11,6],"233":[0,1,2,3,4,5,98,9,11,6],"247":[0,1,2,3,4,5,98,9,11,6],"341":[0,1,2,3,4,5,98,9,11,6],"361":[0,1,2,3,4,5,98,9,11,6],"364":[0,1,2,3,4,5,98,9,11,6],"378":[0,1,2,3,4,5,98,9,11,6],"393":[0,1,2,3,4,5,98,9,11,6],"409":[0,1,2,3,4,5,98,9,11,6],"418":[0,1,2,3,4,5,98,9,11,6],"471":[0,1,2,3,4,5,98,9,11,6],"494":[0,1,2,3,4,5,98,9,11,6],"571":[0,1,2,3,4,5,98,9,11,6]},"packages/core/src/execution-engine/node-execution-context/utils/normalize-items.ts":{"10":"b:RQAggAACAQB4AIAAAAAAAAgABoAAABAA","17":"b:RQAggAACAQB4AIAAAAAAAAgABoAAABAA","20":"b:RQAggAACAQB4AIAAAAAAAAgABoAAABAA","24":"b:RQAggAACAQB4AIAAAAAAAAgABoAAABAA","26":"b:RQAggAACAQB4AIAAAAAAAAgABoAAABAA","27":"b:RQAggAACAQB4AIAAAAAAAAgABoAAABAA","40":"b:RQAggAACAQB4AIAAAAAAAAgABoAAABAA","44":"b:RQAggAACAQB4AIAAAAAAAAgABoAAABAA"},"packages/core/src/execution-engine/node-execution-context/utils/request-helpers/factory.ts":{"27":"b:f4DqxJKHBm94AAAAAAEAAAAKEgDIB9AA","34":"b:f4DqxJKHBm94AAAAAAEAAAAKEgDIB9AA","69":"b:f4DqxJKHBm94AAAAAAEAAAAKEgDIB9AA","89":"b:f4DqxJKHBm94AAAAAAEAAAAKEgDIB9AA","90":"b:f4DqxJKHBm94AAAAAAEAAAAKEgDIB9AA","112":"b:f4DqxJKHBm94AAAAAAEAAAAKEgDIB9AA","130":"b:f4DqxJKHBm94AAAAAAEAAAAKEgDIB9AA","144":"b:f4DqxJKHBm94AAAAAAEAAAAKEgDIB9AA","169":"b:f4DqxJKHBm94AAAAAAEAAAAKEgDIB9AA","189":"b:f4DqxJKHBm94AAAAAAEAAAAKEgDIB9AA","208":"b:f4DqxJKHBm94AAAAAAEAAAAKEgDIB9AA"},"packages/core/src/execution-engine/node-execution-context/utils/resolve-source-overwrite.ts":{"7":"b:f4DqwJKHBm94AAAAAAAAAAAAAADIB9AA"},"packages/core/src/execution-engine/node-execution-context/utils/return-json-array.ts":{"8":"b:R5AqwJKDBid6AAAAAAAAAAAAAADAAYAA","15":"b:R5AqwJKDBid6AAAAAAAAAAAAAADAAYAA"},"packages/core/src/execution-engine/node-execution-context/utils/scheduling-helper-functions.ts":{"8":"b:R5AiwJKHBiV4AAAAAAEAgAAAABjAAYAD","15":"b:R5AiwJKHBiV4AAAAAAEAgAAAABjAAYAD"},"packages/core/src/execution-engine/node-execution-context/utils/ssh-tunnel-helper-functions.ts":{"7":"b:f4DqxJKHBm94AAAAAAAAAAAAAADAB9AA","10":"b:f4DqxJKHBm94AAAAAAAAAAAAAADAB9AA","12":"b:f4DqxJKHBm94AAAAAAAAAAAAAADAB9AA"},"packages/core/src/execution-engine/node-execution-context/utils/validate-value-against-schema.ts":{"20":"b:/4DqxJKHj094AAAAgAQggAAMIAPIB/AB","44":"b:/4DqxJKHj094AAAAgAQggAAMIAPIB/AB","77":"b:/4DqxJKHj094AAAAgAQggAAMIAPIB/AB","89":"b:/4DqxJKHj094AAAAgAQggAAMIAPIB/AB","151":"b:/4DqxJKHj094AAAAgAQggAAMIAPIB/AB","162":"b:/4DqxJKHj094AAAAgAQggAAMIAPIB/AB"},"packages/core/src/execution-engine/node-execution-context/webhook-context.ts":{"37":"b:RgrIIAAADEMQAAACgAAAAAIACAAYAHAB","89":"b:RgrIIAAADEMQAAACgAAAAAIACAAYAHAB","100":"b:RgrIIAAADEMQAAACgAAAAAIACAAYAHAB","104":"b:RgrIIAAADEMQAAACgAAAAAIACAAYAHAB","108":"b:RgrIIAAADEMQAAACgAAAAAIACAAYAHAB","112":"b:RgrIIAAADEMQAAACgAAAAAIACAAYAHAB","116":"b:RgrIIAAADEMQAAACgAAAAAIACAAYAHAB","120":"b:RgrIIAAADEMQAAACgAAAAAIACAAYAHAB","124":"b:RgrIIAAADEMQAAACgAAAAAIACAAYAHAB","131":"b:RgrIIAAADEMQAAACgAAAAAIACAAYAHAB","139":"b:RgrIIAAADEMQAAACgAAAAAIACAAYAHAB","158":"b:RgrIIAAADEMQAAACgAAAAAIACAAYAHAB","162":"b:RgrIIAAADEMQAAACgAAAAAIACAAYAHAB","171":"b:RgrIIAAADEMQAAACgAAAAAIACAAYAHAB","178":"b:RgrIIAAADEMQAAACgAAAAAIACAAYAHAB","188":"b:RgrIIAAADEMQAAACgAAAAAIACAAYAHAB","195":"b:RgrIIAAADEMQAAACgAAAAAIACAAYAHAB","205":"b:RgrIIAAADEMQAAACgAAAAAIACAAYAHAB","212":"b:RgrIIAAADEMQAAACgAAAAAIACAAYAHAB","219":"b:RgrIIAAADEMQAAACgAAAAAIACAAYAHAB"},"packages/core/src/execution-engine/requests-response.ts":{"31":"b:f4DqwJKHBm94AAAAAAAAAAAAAADIB9AA","55":"b:f4DqwJKHBm94AAAAAAAAAAAAAADIB9AA","89":"b:f4DqwJKHBm94AAAAAAAAAAAAAADIB9AA","187":"b:f4DqwJKHBm94AAAAAAAAAAAAAADIB9AA","203":"b:f4DqwJKHBm94AAAAAAAAAAAAAADIB9AA","238":"b:f4DqwJKHBm94AAAAAAAAAAAAAADIB9AA","285":"b:f4DqwJKHBm94AAAAAAAAAAAAAADIB9AA","291":"b:f4DqwJKHBm94AAAAAAAAAAAAAADIB9AA"},"packages/core/src/execution-engine/ssh-clients-manager.ts":{"10":"b:f4HqxbKHPm94AAAABgAIEgEYAADAB9AA","16":"b:f4HqxbKHPm94AAAABgAIEgEYAADAB9AA","17":"b:f4HqxbKHPm94AAAABgAIEgEYAADAB9AA","67":"b:f4HqxbKHPm94AAAABgAIEgEYAADAB9AA","69":"b:f4HqxbKHPm94AAAABgAIEgEYAADAB9AA","85":"b:f4HqxbKHPm94AAAABgAIEgEYAADAB9AA","88":"b:f4HqxbKHPm94AAAABgAIEgEYAADAB9AA","108":"b:f4HqxbKHPm94AAAABgAIEgEYAADAB9AA","133":"b:f4HqxbKHPm94AAAABgAIEgEYAADAB9AA","135":"b:f4HqxbKHPm94AAAABgAIEgEYAADAB9AA","157":"b:f4HqxbKHPm94AAAABgAIEgEYAADAB9AA","158":"b:f4HqxbKHPm94AAAABgAIEgEYAADAB9AA","162":"b:f4HqxbKHPm94AAAABgAIEgEYAADAB9AA","166":"b:f4HqxbKHPm94AAAABgAIEgEYAADAB9AA","170":"b:f4HqxbKHPm94AAAABgAIEgEYAADAB9AA","180":"b:f4HqxbKHPm94AAAABgAIEgEYAADAB9AA","191":"b:f4HqxbKHPm94AAAABgAIEgEYAADAB9AA","203":"b:f4HqxbKHPm94AAAABgAIEgEYAADAB9AA"},"packages/core/src/execution-engine/triggers-and-pollers.ts":{"26":"b:R5AiwJKHBi16AAAAAAEAAAgAAADAB4AB","51":"b:R5AiwJKHBi16AAAAAAEAAAgAAADAB4AB","55":"b:R5AiwJKHBi16AAAAAAEAAAgAAADAB4AB","61":"b:R5AiwJKHBi16AAAAAAEAAAgAAADAB4AB","65":"b:R5AiwJKHBi16AAAAAAEAAAgAAADAB4AB","71":"b:R5AiwJKHBi16AAAAAAEAAAgAAADAB4AB","76":"b:R5AiwJKHBi16AAAAAAEAAAgAAADAB4AB","81":"b:R5AiwJKHBi16AAAAAAEAAAgAAADAB4AB","95":"b:R5AiwJKHBi16AAAAAAEAAAgAAADAB4AB"},"packages/core/src/execution-engine/workflow-execute.ts":{"114":"b:f5DqwJKHB296AAAChGEAAAweJyDIB/AJ","132":"b:f5DqwJKHB296AAAChGEAAAweJyDIB/AJ","199":"b:f5DqwJKHB296AAAChGEAAAweJyDIB/AJ","207":"b:f5DqwJKHB296AAAChGEAAAweJyDIB/AJ","310":"b:f5DqwJKHB296AAAChGEAAAweJyDIB/AJ","342":"b:f5DqwJKHB296AAAChGEAAAweJyDIB/AJ","383":"b:f5DqwJKHB296AAAChGEAAAweJyDIB/AJ","411":"b:f5DqwJKHB296AAAChGEAAAweJyDIB/AJ","430":"b:f5DqwJKHB296AAAChGEAAAweJyDIB/AJ","633":"b:f5DqwJKHB296AAAChGEAAAweJyDIB/AJ","850":"b:f5DqwJKHB296AAAChGEAAAweJyDIB/AJ","917":"b:f5DqwJKHB296AAAChGEAAAweJyDIB/AJ","935":"b:f5DqwJKHB296AAAChGEAAAweJyDIB/AJ","946":"b:f5DqwJKHB296AAAChGEAAAweJyDIB/AJ","988":"b:f5DqwJKHB296AAAChGEAAAweJyDIB/AJ","1001":"b:f5DqwJKHB296AAAChGEAAAweJyDIB/AJ","1012":"b:f5DqwJKHB296AAAChGEAAAweJyDIB/AJ","1027":"b:f5DqwJKHB296AAAChGEAAAweJyDIB/AJ","1032":"b:f5DqwJKHB296AAAChGEAAAweJyDIB/AJ","1041":"b:f5DqwJKHB296AAAChGEAAAweJyDIB/AJ","1092":"b:f5DqwJKHB296AAAChGEAAAweJyDIB/AJ","1099":"b:f5DqwJKHB296AAAChGEAAAweJyDIB/AJ","1101":"b:f5DqwJKHB296AAAChGEAAAweJyDIB/AJ","1126":"b:f5DqwJKHB296AAAChGEAAAweJyDIB/AJ","1194":"b:f5DqwJKHB296AAAChGEAAAweJyDIB/AJ","1214":"b:f5DqwJKHB296AAAChGEAAAweJyDIB/AJ","1267":"b:f5DqwJKHB296AAAChGEAAAweJyDIB/AJ","1302":"b:f5DqwJKHB296AAAChGEAAAweJyDIB/AJ","1322":"b:f5DqwJKHB296AAAChGEAAAweJyDIB/AJ","1451":"b:f5DqwJKHB296AAAChGEAAAweJyDIB/AJ","1479":"b:f5DqwJKHB296AAAChGEAAAweJyDIB/AJ","1505":"b:f5DqwJKHB296AAAChGEAAAweJyDIB/AJ","1531":"b:f5DqwJKHB296AAAChGEAAAweJyDIB/AJ","1577":"b:f5DqwJKHB296AAAChGEAAAweJyDIB/AJ","1593":"b:f5DqwJKHB296AAAChGEAAAweJyDIB/AJ","1598":"b:f5DqwJKHB296AAAChGEAAAweJyDIB/AJ","1607":"b:f5DqwJKHB296AAAChGEAAAweJyDIB/AJ","1708":"b:f5DqwJKHB296AAAChGEAAAweJyDIB/AJ","1713":"b:f5DqwJKHB296AAAChGEAAAweJyDIB/AJ","1790":"b:f5DqwJKHB296AAAChGEAAAweJyDIB/AJ","1791":"b:f5DqwJKHB296AAAChGEAAAweJyDIB/AJ","1820":"b:f5DqwJKHB296AAAChGEAAAweJyDIB/AJ","1821":"b:f5DqwJKHB296AAAChGEAAAweJyDIB/AJ","1939":"b:f5DqwJKHB296AAAChGEAAAweJyDIB/AJ","1943":"b:f5DqwJKHB296AAAChGEAAAweJyDIB/AJ","1992":"b:f5DqwJKHB296AAAChGEAAAweJyDIB/AJ","2282":"b:f5DqwJKHB296AAAChGEAAAweJyDIB/AJ","2378":"b:f5DqwJKHB296AAAChGEAAAweJyDIB/AJ","2395":"b:f5DqwJKHB296AAAChGEAAAweJyDIB/AJ","2398":"b:f5DqwJKHB296AAAChGEAAAweJyDIB/AJ","2425":"b:f5DqwJKHB296AAAChGEAAAweJyDIB/AJ","2430":"b:f5DqwJKHB296AAAChGEAAAweJyDIB/AJ","2467":"b:f5DqwJKHB296AAAChGEAAAweJyDIB/AJ","2483":"b:f5DqwJKHB296AAAChGEAAAweJyDIB/AJ","2501":"b:f5DqwJKHB296AAAChGEAAAweJyDIB/AJ","2522":"b:f5DqwJKHB296AAAChGEAAAweJyDIB/AJ","2541":"b:f5DqwJKHB296AAAChGEAAAweJyDIB/AJ","2562":"b:f5DqwJKHB296AAAChGEAAAweJyDIB/AJ","2618":"b:f5DqwJKHB296AAAChGEAAAweJyDIB/AJ","2699":"b:f5DqwJKHB296AAAChGEAAAweJyDIB/AJ","2710":"b:f5DqwJKHB296AAAChGEAAAweJyDIB/AJ","2722":"b:f5DqwJKHB296AAAChGEAAAweJyDIB/AJ","2828":"b:f5DqwJKHB296AAAChGEAAAweJyDIB/AJ","2888":"b:f5DqwJKHB296AAAChGEAAAweJyDIB/AJ","2889":"b:f5DqwJKHB296AAAChGEAAAweJyDIB/AJ","2891":"b:f5DqwJKHB296AAAChGEAAAweJyDIB/AJ","2899":"b:f5DqwJKHB296AAAChGEAAAweJyDIB/AJ"},"packages/core/src/html-sandbox.ts":{"4":"b:RgDoIAAADEMQAAACgAAAAAIACAAIAFAA","8":"b:RgDoIAAADEMQAAACgAAAAAIACAAIAFAA","15":"b:RgDoIAAADEMQAAACgAAAAAIACAAIAFAA"},"packages/core/src/instance-settings/instance-settings.ts":{"74":"b:RgBIIAAACAIQAAAAgAAAAAIACAAAAAAA","101":"b:RgBIIAAACAIQAAAAgAAAAAIACAAAAAAA","118":"b:RgBIIAAACAIQAAAAgAAAAAIACAAAAAAA","119":"b:RgBIIAAACAIQAAAAgAAAAAIACAAAAAAA","128":"b:RgBIIAAACAIQAAAAgAAAAAIACAAAAAAA","129":"b:RgBIIAAACAIQAAAAgAAAAAIACAAAAAAA","135":"b:RgBIIAAACAIQAAAAgAAAAAIACAAAAAAA","190":"b:RgBIIAAACAIQAAAAgAAAAAIACAAAAAAA","194":"b:RgBIIAAACAIQAAAAgAAAAAIACAAAAAAA","199":"b:RgBIIAAACAIQAAAAgAAAAAIACAAAAAAA","204":"b:RgBIIAAACAIQAAAAgAAAAAIACAAAAAAA","208":"b:RgBIIAAACAIQAAAAgAAAAAIACAAAAAAA","212":"b:RgBIIAAACAIQAAAAgAAAAAIACAAAAAAA","216":"b:RgBIIAAACAIQAAAAgAAAAAIACAAAAAAA","220":"b:RgBIIAAACAIQAAAAgAAAAAIACAAAAAAA","224":"b:RgBIIAAACAIQAAAAgAAAAAIACAAAAAAA","228":"b:RgBIIAAACAIQAAAAgAAAAAIACAAAAAAA","232":"b:RgBIIAAACAIQAAAAgAAAAAIACAAAAAAA","236":"b:RgBIIAAACAIQAAAAgAAAAAIACAAAAAAA","240":"b:RgBIIAAACAIQAAAAgAAAAAIACAAAAAAA","248":"b:RgBIIAAACAIQAAAAgAAAAAIACAAAAAAA","271":"b:RgBIIAAACAIQAAAAgAAAAAIACAAAAAAA","279":"b:RgBIIAAACAIQAAAAgAAAAAIACAAAAAAA","326":"b:RgBIIAAACAIQAAAAgAAAAAIACAAAAAAA","333":"b:RgBIIAAACAIQAAAAgAAAAAIACAAAAAAA","341":"b:RgBIIAAACAIQAAAAgAAAAAIACAAAAAAA","349":"b:RgBIIAAACAIQAAAAgAAAAAIACAAAAAAA","377":"b:RgBIIAAACAIQAAAAgAAAAAIACAAAAAAA","382":"b:RgBIIAAACAIQAAAAgAAAAAIACAAAAAAA","403":"b:RgBIIAAACAIQAAAAgAAAAAIACAAAAAAA","415":"b:RgBIIAAACAIQAAAAgAAAAAIACAAAAAAA"},"packages/core/src/node-execute-functions.ts":{"17":"b:R5AiwJKDBiV6AAAAAAEAAAgAAAAAAAAB","31":"b:R5AiwJKDBiV6AAAAAAEAAAgAAAAAAAAB"},"packages/core/src/nodes-loader/directory-loader.ts":{"37":"b:f6Xu7NL/j208AwACBCAgAIAIAAD4B0EB","88":"b:f6Xu7NL/j208AwACBCAgAIAIAAD4B0EB","108":"b:f6Xu7NL/j208AwACBCAgAIAIAAD4B0EB","117":"b:f6Xu7NL/j208AwACBCAgAIAIAAD4B0EB","123":"b:f6Xu7NL/j208AwACBCAgAIAIAAD4B0EB","135":"b:f6Xu7NL/j208AwACBCAgAIAIAAD4B0EB","139":"b:f6Xu7NL/j208AwACBCAgAIAIAAD4B0EB","143":"b:f6Xu7NL/j208AwACBCAgAIAIAAD4B0EB","145":"b:f6Xu7NL/j208AwACBCAgAIAIAAD4B0EB","146":"b:f6Xu7NL/j208AwACBCAgAIAIAAD4B0EB","147":"b:f6Xu7NL/j208AwACBCAgAIAIAAD4B0EB","150":"b:f6Xu7NL/j208AwACBCAgAIAIAAD4B0EB","165":"b:f6Xu7NL/j208AwACBCAgAIAIAAD4B0EB","229":"b:f6Xu7NL/j208AwACBCAgAIAIAAD4B0EB","242":"b:f6Xu7NL/j208AwACBCAgAIAIAAD4B0EB","260":"b:f6Xu7NL/j208AwACBCAgAIAIAAD4B0EB","289":"b:f6Xu7NL/j208AwACBCAgAIAIAAD4B0EB","310":"b:f6Xu7NL/j208AwACBCAgAIAIAAD4B0EB","313":"b:f6Xu7NL/j208AwACBCAgAIAIAAD4B0EB","325":"b:f6Xu7NL/j208AwACBCAgAIAIAAD4B0EB","327":"b:f6Xu7NL/j208AwACBCAgAIAIAAD4B0EB","332":"b:f6Xu7NL/j208AwACBCAgAIAIAAD4B0EB","344":"b:f6Xu7NL/j208AwACBCAgAIAIAAD4B0EB","369":"b:f6Xu7NL/j208AwACBCAgAIAIAAD4B0EB","400":"b:f6Xu7NL/j208AwACBCAgAIAIAAD4B0EB","406":"b:f6Xu7NL/j208AwACBCAgAIAIAAD4B0EB","412":"b:f6Xu7NL/j208AwACBCAgAIAIAAD4B0EB","424":"b:f6Xu7NL/j208AwACBCAgAIAIAAD4B0EB","443":"b:f6Xu7NL/j208AwACBCAgAIAIAAD4B0EB","460":"b:f6Xu7NL/j208AwACBCAgAIAIAAD4B0EB","479":"b:f6Xu7NL/j208AwACBCAgAIAIAAD4B0EB","499":"b:f6Xu7NL/j208AwACBCAgAIAIAAD4B0EB","513":"b:f6Xu7NL/j208AwACBCAgAIAIAAD4B0EB","533":"b:f6Xu7NL/j208AwACBCAgAIAIAAD4B0EB","539":"b:f6Xu7NL/j208AwACBCAgAIAIAAD4B0EB","540":"b:f6Xu7NL/j208AwACBCAgAIAIAAD4B0EB","542":"b:f6Xu7NL/j208AwACBCAgAIAIAAD4B0EB","548":"b:f6Xu7NL/j208AwACBCAgAIAIAAD4B0EB","582":"b:f6Xu7NL/j208AwACBCAgAIAIAAD4B0EB","604":"b:f6Xu7NL/j208AwACBCAgAIAIAAD4B0EB"},"packages/core/src/observability/tracing/tracing.ts":{"38":"b:R4T+zNP/xmkoAwAAAAAAAAAAAADoBwEA","62":"b:R4T+zNP/xmkoAwAAAAAAAAAAAADoBwEA","69":"b:R4T+zNP/xmkoAwAAAAAAAAAAAADoBwEA","77":"b:R4T+zNP/xmkoAwAAAAAAAAAAAADoBwEA","86":"b:R4T+zNP/xmkoAwAAAAAAAAAAAADoBwEA","94":"b:R4T+zNP/xmkoAwAAAAAAAAAAAADoBwEA","102":"b:R4T+zNP/xmkoAwAAAAAAAAAAAADoBwEA"},"packages/core/src/storage.config.ts":{"22":"b:f4DqwJKHBm94AAAAAAAAAAAAAADIB9AA","34":"b:f4DqwJKHBm94AAAAAAAAAAAAAADIB9AA","40":"b:f4DqwJKHBm94AAAAAAAAAAAAAADIB9AA","68":"b:f4DqwJKHBm94AAAAAAAAAAAAAADIB9AA","103":"b:f4DqwJKHBm94AAAAAAAAAAAAAADIB9AA"},"packages/core/src/utils/assertions.ts":{"14":"b:f4DqwJKHBm94AAAAAAAAAAAAAADIB9AA","31":"b:f4DqwJKHBm94AAAAAAAAAAAAAADIB9AA"},"packages/core/src/utils/convert-binary-data.ts":{"13":"b:f4DqwJKHBm94AAAAAAAAAAAAAADIB9AA"},"packages/core/src/utils/deep-merge.ts":{"4":"b:f4DqwJKHBm94AAAAAAAAAAAAAADIB9AA","17":"b:f4DqwJKHBm94AAAAAAAAAAAAAADIB9AA"},"packages/nodes-base/nodes/Code/Code.node.ts":{"24":"b:RYAggAACAAB6AIAAAAAAACgABoAAABAA","192":"b:RYAggAACAAB6AIAAAAAAACgABoAAABAA"},"packages/nodes-base/nodes/Code/ExecutionError.ts":{"14":[0,2,145,73],"30":[0,2,145,73],"38":[0,2,145,73],"39":[0,2,145,73],"59":[0,2,145,73],"75":[0,2,145,73]},"packages/nodes-base/nodes/Code/JavaScriptSandbox.ts":{"30":[0,2,145,73],"52":[0,2,145,73],"55":[0,2,145,73],"65":[0,2,145,73],"85":[0,2,145,73],"93":[0,2,145,73],"103":[0,2,145,73]},"packages/nodes-base/nodes/Code/JsCodeValidator.ts":{"8":"b:R4IggAACAAA6AgAAAAAAACgAAoAAAAAQ","15":"b:R4IggAACAAA6AgAAAAAAACgAAoAAAAAQ","42":"b:R4IggAACAAA6AgAAAAAAACgAAoAAAAAQ","54":"b:R4IggAACAAA6AgAAAAAAACgAAoAAAAAQ"},"packages/nodes-base/nodes/Code/JsTaskRunnerSandbox.ts":{"20":"b:R4oggAACAAB6AIAAAAAAACgABoAAABAQ","30":"b:R4oggAACAAB6AIAAAAAAACgABoAAABAQ","56":"b:R4oggAACAAB6AIAAAAAAACgABoAAABAQ","78":"b:R4oggAACAAB6AIAAAAAAACgABoAAABAQ","123":"b:R4oggAACAAB6AIAAAAAAACgABoAAABAQ","145":"b:R4oggAACAAB6AIAAAAAAACgABoAAABAQ"},"packages/nodes-base/nodes/Code/PythonTaskRunnerSandbox.ts":{"18":"b:R4IggAACAAA6AAAAAAAAACgAAoAAAAAA","26":"b:R4IggAACAAA6AAAAAAAAACgAAoAAAAAA","42":"b:R4IggAACAAA6AAAAAAAAACgAAoAAAAAA","80":"b:R4IggAACAAA6AAAAAAAAACgAAoAAAAAA","98":"b:R4IggAACAAA6AAAAAAAAACgAAoAAAAAA"},"packages/nodes-base/nodes/Code/Sandbox.ts":{"24":[0,2,145,73],"46":[0,2,145,73],"59":[0,2,145,73],"71":[0,2,145,73]},"packages/nodes-base/nodes/Code/ValidationError.ts":{"12":"b:R4IggAACAAA6AgAAAAAAACgAAoAAAAAA"},"packages/nodes-base/nodes/Code/errors/WrappedExecutionError.ts":{"13":"b:R4IggAACAAA6AIAAAAAAACgAAqAAAAAA","22":"b:R4IggAACAAA6AIAAAAAAACgAAqAAAAAA","29":"b:R4IggAACAAA6AIAAAAAAACgAAqAAAAAA"},"packages/nodes-base/nodes/Code/python-runner-unavailable.error.ts":{"11":"b:RYAggAACAAA6AAAAAAAAACgAAoAAAAAA"},"packages/nodes-base/nodes/Code/reserved-key-found-error.ts":{"4":"b:R4IggAACAAA6AgAAAAAAACgAAoAAAAAA"},"packages/nodes-base/nodes/Code/result-validation.ts":{"16":"b:R4IggAACAAB6AoAAAAAAACgABoAAABAQ","25":"b:R4IggAACAAB6AoAAAAAAACgABoAAABAQ","31":"b:R4IggAACAAB6AoAAAAAAACgABoAAABAQ","61":"b:R4IggAACAAB6AoAAAAAAACgABoAAABAQ","86":"b:R4IggAACAAB6AoAAAAAAACgABoAAABAQ","126":"b:R4IggAACAAB6AoAAAAAAACgABoAAABAQ","159":"b:R4IggAACAAB6AoAAAAAAACgABoAAABAQ","160":"b:R4IggAACAAB6AoAAAAAAACgABoAAABAQ","172":"b:R4IggAACAAB6AoAAAAAAACgABoAAABAQ"},"packages/nodes-base/nodes/Code/throw-execution-error.ts":{"5":"b:R4IggAACAAA6AIAAAAAAACgAAqAAACAA"},"packages/nodes-base/nodes/Code/utils.ts":{"3":"b:R4IggAACAAB6AoAAAAAAACgABoAAABAA","9":"b:R4IggAACAAB6AoAAAAAAACgABoAAABAA","16":"b:R4IggAACAAB6AoAAAAAAACgABoAAABAA","17":"b:R4IggAACAAB6AoAAAAAAACgABoAAABAA","40":"b:R4IggAACAAB6AoAAAAAAACgABoAAABAA","47":"b:R4IggAACAAB6AoAAAAAAACgABoAAABAA"},"packages/nodes-base/nodes/ManualTrigger/ManualTrigger.node.ts":{"40":"b:R5AizNL7hiV6AQAAAAEAAAgAAAAAAAEB","41":"b:R5AizNL7hiV6AQAAAAEAAAgAAAAAAAEB"},"packages/nodes-base/nodes/Set/Set.node.ts":{"8":"b:RqDIIJAFjkdUAQAAAAAgABQEAAAAAAAA"},"packages/nodes-base/nodes/Set/v1/SetV1.node.ts":{"139":"b:RqDIIJAFjkdUAQAAAAAgABQMBgAAAAAA","146":"b:RqDIIJAFjkdUAQAAAAAgABQMBgAAAAAA","182":"b:RqDIIJAFjkdUAQAAAAAgABQMBgAAAAAA","193":"b:RqDIIJAFjkdUAQAAAAAgABQMBgAAAAAA","211":"b:RqDIIJAFjkdUAQAAAAAgABQMBgAAAAAA"},"packages/nodes-base/nodes/Set/v2/SetV2.node.ts":{"302":"b:RqDIIJAFjkdUAQAAAAAgABQEAAAAABAA","309":"b:RqDIIJAFjkdUAQAAAAAgABQEAAAAABAA"},"packages/nodes-base/nodes/Set/v2/helpers/utils.ts":{"28":"b:RqDIIJAFjkdUAQACAAAgABYEAAAAABAA","31":"b:RqDIIJAFjkdUAQACAAAgABYEAAAAABAA","34":"b:RqDIIJAFjkdUAQACAAAgABYEAAAAABAA","37":"b:RqDIIJAFjkdUAQACAAAgABYEAAAAABAA","43":"b:RqDIIJAFjkdUAQACAAAgABYEAAAAABAA","46":"b:RqDIIJAFjkdUAQACAAAgABYEAAAAABAA","49":"b:RqDIIJAFjkdUAQACAAAgABYEAAAAABAA","56":"b:RqDIIJAFjkdUAQACAAAgABYEAAAAABAA","89":"b:RqDIIJAFjkdUAQACAAAgABYEAAAAABAA","90":"b:RqDIIJAFjkdUAQACAAAgABYEAAAAABAA","104":"b:RqDIIJAFjkdUAQACAAAgABYEAAAAABAA","105":"b:RqDIIJAFjkdUAQACAAAgABYEAAAAABAA","130":"b:RqDIIJAFjkdUAQACAAAgABYEAAAAABAA","161":"b:RqDIIJAFjkdUAQACAAAgABYEAAAAABAA","222":"b:RqDIIJAFjkdUAQACAAAgABYEAAAAABAA","236":"b:RqDIIJAFjkdUAQACAAAgABYEAAAAABAA","238":"b:RqDIIJAFjkdUAQACAAAgABYEAAAAABAA","245":"b:RqDIIJAFjkdUAQACAAAgABYEAAAAABAA","265":"b:RqDIIJAFjkdUAQACAAAgABYEAAAAABAA"},"packages/nodes-base/nodes/Set/v2/manual.mode.ts":{"189":"b:RqDIIJAFjkdUAQAAAAAgABQEAAAAABAA"},"packages/nodes-base/nodes/Set/v2/raw.mode.ts":{"36":"b:RqDIIJAFjkdUAQAAAAAgABQEAAAAAAAA"},"packages/nodes-base/utils/utilities.ts":{"34":"b:fqHOaNKVjkdUAwAAgCIgAIAAAAEoACAB","53":"b:fqHOaNKVjkdUAwAAgCIgAIAAAAEoACAB","65":"b:fqHOaNKVjkdUAwAAgCIgAIAAAAEoACAB","70":"b:fqHOaNKVjkdUAwAAgCIgAIAAAAEoACAB","86":"b:fqHOaNKVjkdUAwAAgCIgAIAAAAEoACAB","89":"b:fqHOaNKVjkdUAwAAgCIgAIAAAAEoACAB","113":"b:fqHOaNKVjkdUAwAAgCIgAIAAAAEoACAB","136":"b:fqHOaNKVjkdUAwAAgCIgAIAAAAEoACAB","140":"b:fqHOaNKVjkdUAwAAgCIgAIAAAAEoACAB","148":"b:fqHOaNKVjkdUAwAAgCIgAIAAAAEoACAB","167":"b:fqHOaNKVjkdUAwAAgCIgAIAAAAEoACAB","174":"b:fqHOaNKVjkdUAwAAgCIgAIAAAAEoACAB","183":"b:fqHOaNKVjkdUAwAAgCIgAIAAAAEoACAB","186":"b:fqHOaNKVjkdUAwAAgCIgAIAAAAEoACAB","189":"b:fqHOaNKVjkdUAwAAgCIgAIAAAAEoACAB","267":"b:fqHOaNKVjkdUAwAAgCIgAIAAAAEoACAB","271":"b:fqHOaNKVjkdUAwAAgCIgAIAAAAEoACAB","276":"b:fqHOaNKVjkdUAwAAgCIgAIAAAAEoACAB","278":"b:fqHOaNKVjkdUAwAAgCIgAIAAAAEoACAB","287":"b:fqHOaNKVjkdUAwAAgCIgAIAAAAEoACAB","309":"b:fqHOaNKVjkdUAwAAgCIgAIAAAAEoACAB","336":"b:fqHOaNKVjkdUAwAAgCIgAIAAAAEoACAB","345":"b:fqHOaNKVjkdUAwAAgCIgAIAAAAEoACAB","346":"b:fqHOaNKVjkdUAwAAgCIgAIAAAAEoACAB","356":"b:fqHOaNKVjkdUAwAAgCIgAIAAAAEoACAB","365":"b:fqHOaNKVjkdUAwAAgCIgAIAAAAEoACAB","387":"b:fqHOaNKVjkdUAwAAgCIgAIAAAAEoACAB","389":"b:fqHOaNKVjkdUAwAAgCIgAIAAAAEoACAB","413":"b:fqHOaNKVjkdUAwAAgCIgAIAAAAEoACAB","414":"b:fqHOaNKVjkdUAwAAgCIgAIAAAAEoACAB","417":"b:fqHOaNKVjkdUAwAAgCIgAIAAAAEoACAB","441":"b:fqHOaNKVjkdUAwAAgCIgAIAAAAEoACAB","447":"b:fqHOaNKVjkdUAwAAgCIgAIAAAAEoACAB","454":"b:fqHOaNKVjkdUAwAAgCIgAIAAAAEoACAB"},"packages/workflow/src/common/get-child-nodes.ts":{"5":"b:fgDqwJKHBm9wAAACACAAAAAEAADIB9AA"},"packages/workflow/src/common/get-connected-nodes.ts":{"13":"b:f5DqwNKHBm94AAAAAEAAAACIAADIB9AA","29":"b:f5DqwNKHBm94AAAAAEAAAACIAADIB9AA","63":"b:f5DqwNKHBm94AAAAAEAAAACIAADIB9AA","77":"b:f5DqwNKHBm94AAAAAEAAAACIAADIB9AA","83":"b:f5DqwNKHBm94AAAAAEAAAACIAADIB9AA","84":"b:f5DqwNKHBm94AAAAAEAAAACIAADIB9AA"},"packages/workflow/src/common/get-parent-nodes.ts":{"11":"b:f4JIQNABCAJaAAAAgEAAAAAIAAAAABAA"},"packages/workflow/src/common/map-connections-by-destination.ts":{"5":"b:f4DqxNKHhm94AgAAAAAAAAAAAADoB4AA"},"packages/workflow/src/common/resolve-variables.ts":{"23":"b:f4DqxJKHhm94AgAAAAAAAAAAAADoB4AA","51":"b:f4DqxJKHhm94AgAAAAAAAAAAAADoB4AA"},"packages/workflow/src/credential-domain-restrictions.ts":{"55":"b:B4RezALoliEoJwgMAAAAAAAAAADgAUAA","59":"b:B4RezALoliEoJwgMAAAAAAAAAADgAUAA","66":"b:B4RezALoliEoJwgMAAAAAAAAAADgAUAA","75":"b:B4RezALoliEoJwgMAAAAAAAAAADgAUAA","87":"b:B4RezALoliEoJwgMAAAAAAAAAADgAUAA","92":"b:B4RezALoliEoJwgMAAAAAAAAAADgAUAA","99":"b:B4RezALoliEoJwgMAAAAAAAAAADgAUAA","117":"b:B4RezALoliEoJwgMAAAAAAAAAADgAUAA","134":"b:B4RezALoliEoJwgMAAAAAAAAAADgAUAA","148":"b:B4RezALoliEoJwgMAAAAAAAAAADgAUAA","182":"b:B4RezALoliEoJwgMAAAAAAAAAADgAUAA"},"packages/workflow/src/dynamic-credentials-helpers.ts":{"9":"b:f4LqwJKHBm94AAAAAAAAAAAAgAHIB/AB","10":"b:f4LqwJKHBm94AAAAAAAAAAAAgAHIB/AB","13":"b:f4LqwJKHBm94AAAAAAAAAAAAgAHIB/AB","22":"b:f4LqwJKHBm94AAAAAAAAAAAAgAHIB/AB","23":"b:f4LqwJKHBm94AAAAAAAAAAAAgAHIB/AB","24":"b:f4LqwJKHBm94AAAAAAAAAAAAgAHIB/AB","33":"b:f4LqwJKHBm94AAAAAAAAAAAAgAHIB/AB","66":"b:f4LqwJKHBm94AAAAAAAAAAAAgAHIB/AB","87":"b:f4LqwJKHBm94AAAAAAAAAAAAgAHIB/AB","99":"b:f4LqwJKHBm94AAAAAAAAAAAAgAHIB/AB"},"packages/workflow/src/errors/base/base.error.ts":{"32":"b:T4IghKIECAFgAGAMAQAAAAAAAoAYMBEB"},"packages/workflow/src/errors/base/operational.error.ts":{"16":"b:R4AAhAACAAB4AAAABAAAAAgABoAECAAE"},"packages/workflow/src/errors/base/unexpected.error.ts":{"16":"b:RQIggAACAAB4AKAAAAAAAAgABoAQABAA"},"packages/workflow/src/errors/base/user.error.ts":{"19":"b:RQIggAAECABoAIAMAAAAAAAAAoAAABAA"},"packages/workflow/src/execution-context-establishment-hooks.ts":{"41":"b:f4DqwJKHBm94AAAAAAAAAAAAAADIB9AA"},"packages/workflow/src/execution-context.ts":{"12":"b:R5AiwIKDBiV6AAAAAAEAAAgQAAAAAEAA","294":"b:R5AiwIKDBiV6AAAAAAEAAAgQAAAAAEAA","316":"b:R5AiwIKDBiV6AAAAAAEAAAgQAAAAAEAA","329":"b:R5AiwIKDBiV6AAAAAAEAAAgQAAAAAEAA","334":"b:R5AiwIKDBiV6AAAAAAEAAAgQAAAAAEAA"},"packages/workflow/src/execution-status.ts":{"16":"b:R4DqwJOHBm94AAAAAAAAAAAAAADIB8AA","29":"b:R4DqwJOHBm94AAAAAAAAAAAAAADIB8AA"},"packages/workflow/src/expression-sandboxing.ts":{"22":"b:fwDIIAAEDUNQAAACgAAgAAKACAAYAHAB","25":"b:fwDIIAAEDUNQAAACgAAgAAKACAAYAHAB","73":"b:fwDIIAAEDUNQAAACgAAgAAKACAAYAHAB","74":"b:fwDIIAAEDUNQAAACgAAgAAKACAAYAHAB","93":"b:fwDIIAAEDUNQAAACgAAgAAKACAAYAHAB","137":"b:fwDIIAAEDUNQAAACgAAgAAKACAAYAHAB","139":"b:fwDIIAAEDUNQAAACgAAgAAKACAAYAHAB","171":"b:fwDIIAAEDUNQAAACgAAgAAKACAAYAHAB","195":"b:fwDIIAAEDUNQAAACgAAgAAKACAAYAHAB","211":"b:fwDIIAAEDUNQAAACgAAgAAKACAAYAHAB","248":"b:fwDIIAAEDUNQAAACgAAgAAKACAAYAHAB","250":"b:fwDIIAAEDUNQAAACgAAgAAKACAAYAHAB","326":"b:fwDIIAAEDUNQAAACgAAgAAKACAAYAHAB","356":"b:fwDIIAAEDUNQAAACgAAgAAKACAAYAHAB","358":"b:fwDIIAAEDUNQAAACgAAgAAKACAAYAHAB","367":"b:fwDIIAAEDUNQAAACgAAgAAKACAAYAHAB","384":"b:fwDIIAAEDUNQAAACgAAgAAKACAAYAHAB","393":"b:fwDIIAAEDUNQAAACgAAgAAKACAAYAHAB","413":"b:fwDIIAAEDUNQAAACgAAgAAKACAAYAHAB","433":"b:fwDIIAAEDUNQAAACgAAgAAKACAAYAHAB","442":"b:fwDIIAAEDUNQAAACgAAgAAKACAAYAHAB","451":"b:fwDIIAAEDUNQAAACgAAgAAKACAAYAHAB","460":"b:fwDIIAAEDUNQAAACgAAgAAKACAAYAHAB","469":"b:fwDIIAAEDUNQAAACgAAgAAKACAAYAHAB","508":"b:fwDIIAAEDUNQAAACgAAgAAKACAAYAHAB","533":"b:fwDIIAAEDUNQAAACgAAgAAKACAAYAHAB","542":"b:fwDIIAAEDUNQAAACgAAgAAKACAAYAHAB","551":"b:fwDIIAAEDUNQAAACgAAgAAKACAAYAHAB","557":"b:fwDIIAAEDUNQAAACgAAgAAKACAAYAHAB"},"packages/workflow/src/expression.ts":{"35":"b:f4Dq5JKHj294AgAAgAAgAAIAAgDoB8AA","38":"b:f4Dq5JKHj294AgAAgAAgAAIAAgDoB8AA","41":"b:f4Dq5JKHj294AgAAgAAgAAIAAgDoB8AA","45":"b:f4Dq5JKHj294AgAAgAAgAAIAAgDoB8AA","59":"b:f4Dq5JKHj294AgAAgAAgAAIAAgDoB8AA","112":"b:f4Dq5JKHj294AgAAgAAgAAIAAgDoB8AA","113":"b:f4Dq5JKHj294AgAAgAAgAAIAAgDoB8AA","137":"b:f4Dq5JKHj294AgAAgAAgAAIAAgDoB8AA","151":"b:f4Dq5JKHj294AgAAgAAgAAIAAgDoB8AA","183":"b:f4Dq5JKHj294AgAAgAAgAAIAAgDoB8AA","185":"b:f4Dq5JKHj294AgAAgAAgAAIAAgDoB8AA","193":"b:f4Dq5JKHj294AgAAgAAgAAIAAgDoB8AA","197":"b:f4Dq5JKHj294AgAAgAAgAAIAAgDoB8AA","208":"b:f4Dq5JKHj294AgAAgAAgAAIAAgDoB8AA","210":"b:f4Dq5JKHj294AgAAgAAgAAIAAgDoB8AA","217":"b:f4Dq5JKHj294AgAAgAAgAAIAAgDoB8AA","220":"b:f4Dq5JKHj294AgAAgAAgAAIAAgDoB8AA","231":"b:f4Dq5JKHj294AgAAgAAgAAIAAgDoB8AA","237":"b:f4Dq5JKHj294AgAAgAAgAAIAAgDoB8AA","246":"b:f4Dq5JKHj294AgAAgAAgAAIAAgDoB8AA","262":"b:f4Dq5JKHj294AgAAgAAgAAIAAgDoB8AA","283":"b:f4Dq5JKHj294AgAAgAAgAAIAAgDoB8AA","288":"b:f4Dq5JKHj294AgAAgAAgAAIAAgDoB8AA","296":"b:f4Dq5JKHj294AgAAgAAgAAIAAgDoB8AA","307":"b:f4Dq5JKHj294AgAAgAAgAAIAAgDoB8AA","320":"b:f4Dq5JKHj294AgAAgAAgAAIAAgDoB8AA","324":"b:f4Dq5JKHj294AgAAgAAgAAIAAgDoB8AA","481":"b:f4Dq5JKHj294AgAAgAAgAAIAAgDoB8AA","490":"b:f4Dq5JKHj294AgAAgAAgAAIAAgDoB8AA","531":"b:f4Dq5JKHj294AgAAgAAgAAIAAgDoB8AA","628":"b:f4Dq5JKHj294AgAAgAAgAAIAAgDoB8AA"},"packages/workflow/src/extensions/expression-extension.ts":{"25":"b:f4DIIAAEDUNQAAAAgCAgAAKAAAEIAEAA","29":"b:f4DIIAAEDUNQAAAAgCAgAAKAAAEIAEAA","64":"b:f4DIIAAEDUNQAAAAgCAgAAKAAAEIAEAA","66":"b:f4DIIAAEDUNQAAAAgCAgAAKAAAEIAEAA","69":"b:f4DIIAAEDUNQAAAAgCAgAAKAAAEIAEAA","77":"b:f4DIIAAEDUNQAAAAgCAgAAKAAAEIAEAA","79":"b:f4DIIAAEDUNQAAAAgCAgAAKAAAEIAEAA","95":"b:f4DIIAAEDUNQAAAAgCAgAAKAAAEIAEAA","124":"b:f4DIIAAEDUNQAAAAgCAgAAKAAAEIAEAA","133":"b:f4DIIAAEDUNQAAAAgCAgAAKAAAEIAEAA","160":"b:f4DIIAAEDUNQAAAAgCAgAAKAAAEIAEAA","174":"b:f4DIIAAEDUNQAAAAgCAgAAKAAAEIAEAA","183":"b:f4DIIAAEDUNQAAAAgCAgAAKAAAEIAEAA","196":"b:f4DIIAAEDUNQAAAAgCAgAAKAAAEIAEAA","208":"b:f4DIIAAEDUNQAAAAgCAgAAKAAAEIAEAA","387":"b:f4DIIAAEDUNQAAAAgCAgAAKAAAEIAEAA","405":"b:f4DIIAAEDUNQAAAAgCAgAAKAAAEIAEAA","440":"b:f4DIIAAEDUNQAAAAgCAgAAKAAAEIAEAA","457":"b:f4DIIAAEDUNQAAAAgCAgAAKAAAEIAEAA","519":"b:f4DIIAAEDUNQAAAAgCAgAAKAAAEIAEAA","527":"b:f4DIIAAEDUNQAAAAgCAgAAKAAAEIAEAA","535":"b:f4DIIAAEDUNQAAAAgCAgAAKAAAEIAEAA","555":"b:f4DIIAAEDUNQAAAAgCAgAAKAAAEIAEAA","571":"b:f4DIIAAEDUNQAAAAgCAgAAKAAAEIAEAA","579":"b:f4DIIAAEDUNQAAAAgCAgAAKAAAEIAEAA","583":"b:f4DIIAAEDUNQAAAAgCAgAAKAAAEIAEAA","584":"b:f4DIIAAEDUNQAAAAgCAgAAKAAAEIAEAA","598":"b:f4DIIAAEDUNQAAAAgCAgAAKAAAEIAEAA"},"packages/workflow/src/extensions/expression-parser.ts":{"20":"b:f4DIIAAEDUNQAAAAgCAgAAKAAAEIAEAA","24":"b:f4DIIAAEDUNQAAAAgCAgAAKAAAEIAEAA","93":"b:f4DIIAAEDUNQAAAAgCAgAAKAAAEIAEAA","97":"b:f4DIIAAEDUNQAAAAgCAgAAKAAAEIAEAA","99":"b:f4DIIAAEDUNQAAAAgCAgAAKAAAEIAEAA"},"packages/workflow/src/global-state.ts":{"9":"b:f4DqxJKHhm94AgAAAAAAAAAAAADoB4AA","13":"b:f4DqxJKHhm94AgAAAAAAAAAAAADoB4AA"},"packages/workflow/src/highlighted-data.ts":{"3":[19,1,2,3,4,5,57,129,6],"7":[19,1,2,3,4,5,57,129,6]},"packages/workflow/src/index.ts":{"93":"b:f61/zNP9j2d6AwAAgIIAoAAAJgGoAHAB","102":"b:f61/zNP9j2d6AwAAgIIAoAAAJgGoAHAB"},"packages/workflow/src/interfaces.ts":{"128":"b:f8Hu2JKHzn94AgAACAACAAAEAADoJ9AA","2523":"b:f8Hu2JKHzn94AgAACAACAAAEAADoJ9AA","2540":"b:f8Hu2JKHzn94AgAACAACAAAEAADoJ9AA"},"packages/workflow/src/node-grouping-validation.ts":{"30":"b:R4T+zNP/xmksAwAAAAAAAAAAAADoBwEA","106":"b:R4T+zNP/xmksAwAAAAAAAAAAAADoBwEA","114":"b:R4T+zNP/xmksAwAAAAAAAAAAAADoBwEA","133":"b:R4T+zNP/xmksAwAAAAAAAAAAAADoBwEA","137":"b:R4T+zNP/xmksAwAAAAAAAAAAAADoBwEA","152":"b:R4T+zNP/xmksAwAAAAAAAAAAAADoBwEA","164":"b:R4T+zNP/xmksAwAAAAAAAAAAAADoBwEA","234":"b:R4T+zNP/xmksAwAAAAAAAAAAAADoBwEA","245":"b:R4T+zNP/xmksAwAAAAAAAAAAAADoBwEA","253":"b:R4T+zNP/xmksAwAAAAAAAAAAAADoBwEA","256":"b:R4T+zNP/xmksAwAAAAAAAAAAAADoBwEA","262":"b:R4T+zNP/xmksAwAAAAAAAAAAAADoBwEA","313":"b:R4T+zNP/xmksAwAAAAAAAAAAAADoBwEA","318":"b:R4T+zNP/xmksAwAAAAAAAAAAAADoBwEA","336":"b:R4T+zNP/xmksAwAAAAAAAAAAAADoBwEA","359":"b:R4T+zNP/xmksAwAAAAAAAAAAAADoBwEA","364":"b:R4T+zNP/xmksAwAAAAAAAAAAAADoBwEA","372":"b:R4T+zNP/xmksAwAAAAAAAAAAAADoBwEA","377":"b:R4T+zNP/xmksAwAAAAAAAAAAAADoBwEA","395":"b:R4T+zNP/xmksAwAAAAAAAAAAAADoBwEA","399":"b:R4T+zNP/xmksAwAAAAAAAAAAAADoBwEA","400":"b:R4T+zNP/xmksAwAAAAAAAAAAAADoBwEA","403":"b:R4T+zNP/xmksAwAAAAAAAAAAAADoBwEA","430":"b:R4T+zNP/xmksAwAAAAAAAAAAAADoBwEA","436":"b:R4T+zNP/xmksAwAAAAAAAAAAAADoBwEA","481":"b:R4T+zNP/xmksAwAAAAAAAAAAAADoBwEA","491":"b:R4T+zNP/xmksAwAAAAAAAAAAAADoBwEA"},"packages/workflow/src/node-helpers.ts":{"256":"b:f//+/NP/339/JwgOjmUggAIaNgP4N/EB","264":"b:f//+/NP/339/JwgOjmUggAIaNgP4N/EB","274":"b:f//+/NP/339/JwgOjmUggAIaNgP4N/EB","284":"b:f//+/NP/339/JwgOjmUggAIaNgP4N/EB","299":"b:f//+/NP/339/JwgOjmUggAIaNgP4N/EB","321":"b:f//+/NP/339/JwgOjmUggAIaNgP4N/EB","322":"b:f//+/NP/339/JwgOjmUggAIaNgP4N/EB","339":"b:f//+/NP/339/JwgOjmUggAIaNgP4N/EB","343":"b:f//+/NP/339/JwgOjmUggAIaNgP4N/EB","358":"b:f//+/NP/339/JwgOjmUggAIaNgP4N/EB","417":"b:f//+/NP/339/JwgOjmUggAIaNgP4N/EB","444":"b:f//+/NP/339/JwgOjmUggAIaNgP4N/EB","483":"b:f//+/NP/339/JwgOjmUggAIaNgP4N/EB","519":"b:f//+/NP/339/JwgOjmUggAIaNgP4N/EB","556":"b:f//+/NP/339/JwgOjmUggAIaNgP4N/EB","595":"b:f//+/NP/339/JwgOjmUggAIaNgP4N/EB","600":"b:f//+/NP/339/JwgOjmUggAIaNgP4N/EB","676":"b:f//+/NP/339/JwgOjmUggAIaNgP4N/EB","944":"b:f//+/NP/339/JwgOjmUggAIaNgP4N/EB","983":"b:f//+/NP/339/JwgOjmUggAIaNgP4N/EB","1015":"b:f//+/NP/339/JwgOjmUggAIaNgP4N/EB","1016":"b:f//+/NP/339/JwgOjmUggAIaNgP4N/EB","1064":"b:f//+/NP/339/JwgOjmUggAIaNgP4N/EB","1094":"b:f//+/NP/339/JwgOjmUggAIaNgP4N/EB","1124":"b:f//+/NP/339/JwgOjmUggAIaNgP4N/EB","1145":"b:f//+/NP/339/JwgOjmUggAIaNgP4N/EB","1154":"b:f//+/NP/339/JwgOjmUggAIaNgP4N/EB","1158":"b:f//+/NP/339/JwgOjmUggAIaNgP4N/EB","1164":"b:f//+/NP/339/JwgOjmUggAIaNgP4N/EB","1167":"b:f//+/NP/339/JwgOjmUggAIaNgP4N/EB","1190":"b:f//+/NP/339/JwgOjmUggAIaNgP4N/EB","1252":"b:f//+/NP/339/JwgOjmUggAIaNgP4N/EB","1288":"b:f//+/NP/339/JwgOjmUggAIaNgP4N/EB","1314":"b:f//+/NP/339/JwgOjmUggAIaNgP4N/EB","1344":"b:f//+/NP/339/JwgOjmUggAIaNgP4N/EB","1358":"b:f//+/NP/339/JwgOjmUggAIaNgP4N/EB","1383":"b:f//+/NP/339/JwgOjmUggAIaNgP4N/EB","1411":"b:f//+/NP/339/JwgOjmUggAIaNgP4N/EB","1448":"b:f//+/NP/339/JwgOjmUggAIaNgP4N/EB","1463":"b:f//+/NP/339/JwgOjmUggAIaNgP4N/EB","1474":"b:f//+/NP/339/JwgOjmUggAIaNgP4N/EB","1479":"b:f//+/NP/339/JwgOjmUggAIaNgP4N/EB","1505":"b:f//+/NP/339/JwgOjmUggAIaNgP4N/EB","1517":"b:f//+/NP/339/JwgOjmUggAIaNgP4N/EB","1562":"b:f//+/NP/339/JwgOjmUggAIaNgP4N/EB","1565":"b:f//+/NP/339/JwgOjmUggAIaNgP4N/EB","1730":"b:f//+/NP/339/JwgOjmUggAIaNgP4N/EB","1771":"b:f//+/NP/339/JwgOjmUggAIaNgP4N/EB","1779":"b:f//+/NP/339/JwgOjmUggAIaNgP4N/EB","1791":"b:f//+/NP/339/JwgOjmUggAIaNgP4N/EB","1801":"b:f//+/NP/339/JwgOjmUggAIaNgP4N/EB","1805":"b:f//+/NP/339/JwgOjmUggAIaNgP4N/EB","1822":"b:f//+/NP/339/JwgOjmUggAIaNgP4N/EB","1835":"b:f//+/NP/339/JwgOjmUggAIaNgP4N/EB","1841":"b:f//+/NP/339/JwgOjmUggAIaNgP4N/EB","1843":"b:f//+/NP/339/JwgOjmUggAIaNgP4N/EB","1851":"b:f//+/NP/339/JwgOjmUggAIaNgP4N/EB","1855":"b:f//+/NP/339/JwgOjmUggAIaNgP4N/EB","1879":"b:f//+/NP/339/JwgOjmUggAIaNgP4N/EB","1914":"b:f//+/NP/339/JwgOjmUggAIaNgP4N/EB","1921":"b:f//+/NP/339/JwgOjmUggAIaNgP4N/EB","1937":"b:f//+/NP/339/JwgOjmUggAIaNgP4N/EB","1942":"b:f//+/NP/339/JwgOjmUggAIaNgP4N/EB","1976":"b:f//+/NP/339/JwgOjmUggAIaNgP4N/EB","2009":"b:f//+/NP/339/JwgOjmUggAIaNgP4N/EB","2024":"b:f//+/NP/339/JwgOjmUggAIaNgP4N/EB","2056":"b:f//+/NP/339/JwgOjmUggAIaNgP4N/EB","2083":"b:f//+/NP/339/JwgOjmUggAIaNgP4N/EB","2096":"b:f//+/NP/339/JwgOjmUggAIaNgP4N/EB","2110":"b:f//+/NP/339/JwgOjmUggAIaNgP4N/EB","2124":"b:f//+/NP/339/JwgOjmUggAIaNgP4N/EB","2135":"b:f//+/NP/339/JwgOjmUggAIaNgP4N/EB"},"packages/workflow/src/node-parameters/parameter-type-validation.ts":{"15":[19,1,2,3,4,5,57,129,6],"30":[19,1,2,3,4,5,57,129,6],"39":[19,1,2,3,4,5,57,129,6],"47":[19,1,2,3,4,5,57,129,6],"55":[19,1,2,3,4,5,57,129,6],"69":[19,1,2,3,4,5,57,129,6],"75":[19,1,2,3,4,5,57,129,6],"82":[19,1,2,3,4,5,57,129,6],"103":[19,1,2,3,4,5,57,129,6],"110":[19,1,2,3,4,5,57,129,6],"121":[19,1,2,3,4,5,57,129,6],"133":[19,1,2,3,4,5,57,129,6],"141":[19,1,2,3,4,5,57,129,6],"142":[19,1,2,3,4,5,57,129,6],"148":[19,1,2,3,4,5,57,129,6],"162":[19,1,2,3,4,5,57,129,6],"175":[19,1,2,3,4,5,57,129,6],"197":[19,1,2,3,4,5,57,129,6],"246":[19,1,2,3,4,5,57,129,6],"259":[19,1,2,3,4,5,57,129,6]},"packages/workflow/src/observable-object.ts":{"9":"b:f4LqxJKHhm94AoAAAAQAgAgIAoDoB9AC","38":"b:f4LqxJKHhm94AoAAAAQAgAgIAoDoB9AC","48":"b:f4LqxJKHhm94AoAAAAQAgAgIAoDoB9AC","51":"b:f4LqxJKHhm94AoAAAAQAgAgIAoDoB9AC","54":"b:f4LqxJKHhm94AoAAAAQAgAgIAoDoB9AC"},"packages/workflow/src/redaction-channels.ts":{"23":"b:f4DqwJKHBm94AAAAAAAAAAAAAADIB9AA","37":"b:f4DqwJKHBm94AAAAAAAAAAAAAADIB9AA","52":"b:f4DqwJKHBm94AAAAAAAAAAAAAADIB9AA"},"packages/workflow/src/run-execution-data/run-execution-data.ts":{"29":"b:f4DqwJKFDw94AAAAAAAAAAAAAAAAAAAA"},"packages/workflow/src/run-execution-data-factory.ts":{"54":"b:f4Dq4JKHDm94AgACgAAAAAIAAADoB/AA","67":"b:f4Dq4JKHDm94AgACgAAAAAIAAADoB/AA","86":"b:f4Dq4JKHDm94AgACgAAAAAIAAADoB/AA","108":"b:f4Dq4JKHDm94AgACgAAAAAIAAADoB/AA","125":"b:f4Dq4JKHDm94AgACgAAAAAIAAADoB/AA"},"packages/workflow/src/type-guards.ts":{"15":"b:f4DK4NOFD0d6AAAChAIAAAIAJgEIAGAB","21":"b:f4DK4NOFD0d6AAAChAIAAAIAJgEIAGAB","25":"b:f4DK4NOFD0d6AAAChAIAAAIAJgEIAGAB","29":"b:f4DK4NOFD0d6AAAChAIAAAIAJgEIAGAB","33":"b:f4DK4NOFD0d6AAAChAIAAAIAJgEIAGAB","37":"b:f4DK4NOFD0d6AAAChAIAAAIAJgEIAGAB","41":"b:f4DK4NOFD0d6AAAChAIAAAIAJgEIAGAB","47":"b:f4DK4NOFD0d6AAAChAIAAAIAJgEIAGAB","60":"b:f4DK4NOFD0d6AAAChAIAAAIAJgEIAGAB","70":"b:f4DK4NOFD0d6AAAChAIAAAIAJgEIAGAB","83":"b:f4DK4NOFD0d6AAAChAIAAAIAJgEIAGAB","93":"b:f4DK4NOFD0d6AAAChAIAAAIAJgEIAGAB","99":"b:f4DK4NOFD0d6AAAChAIAAAIAJgEIAGAB","103":"b:f4DK4NOFD0d6AAAChAIAAAIAJgEIAGAB"},"packages/workflow/src/type-validation.ts":{"15":"b:f4rIINSFD0dQAAACgAAgAAYgJgAAABAA","24":"b:f4rIINSFD0dQAAACgAAgAAYgJgAAABAA","38":"b:f4rIINSFD0dQAAACgAAgAAYgJgAAABAA","48":"b:f4rIINSFD0dQAAACgAAgAAYgJgAAABAA","72":"b:f4rIINSFD0dQAAACgAAgAAYgJgAAABAA","114":"b:f4rIINSFD0dQAAACgAAgAAYgJgAAABAA","124":"b:f4rIINSFD0dQAAACgAAgAAYgJgAAABAA","146":"b:f4rIINSFD0dQAAACgAAgAAYgJgAAABAA","162":"b:f4rIINSFD0dQAAACgAAgAAYgJgAAABAA","206":"b:f4rIINSFD0dQAAACgAAgAAYgJgAAABAA","272":"b:f4rIINSFD0dQAAACgAAgAAYgJgAAABAA","284":"b:f4rIINSFD0dQAAACgAAgAAYgJgAAABAA","305":"b:f4rIINSFD0dQAAACgAAgAAYgJgAAABAA","333":"b:f4rIINSFD0dQAAACgAAgAAYgJgAAABAA","437":"b:f4rIINSFD0dQAAACgAAgAAYgJgAAABAA","438":"b:f4rIINSFD0dQAAACgAAgAAYgJgAAABAA"},"packages/workflow/src/utils.ts":{"17":"b:f6X/7NP/z294AwACBIAggAICAgDoB9AA","28":"b:f6X/7NP/z294AwACBIAggAICAgDoB9AA","36":"b:f6X/7NP/z294AwACBIAggAICAgDoB9AA","57":"b:f6X/7NP/z294AwACBIAggAICAgDoB9AA","96":"b:f6X/7NP/z294AwACBIAggAICAgDoB9AA","101":"b:f6X/7NP/z294AwACBIAggAICAgDoB9AA","102":"b:f6X/7NP/z294AwACBIAggAICAgDoB9AA","109":"b:f6X/7NP/z294AwACBIAggAICAgDoB9AA","127":"b:f6X/7NP/z294AwACBIAggAICAgDoB9AA","129":"b:f6X/7NP/z294AwACBIAggAICAgDoB9AA","156":"b:f6X/7NP/z294AwACBIAggAICAgDoB9AA","199":"b:f6X/7NP/z294AwACBIAggAICAgDoB9AA","205":"b:f6X/7NP/z294AwACBIAggAICAgDoB9AA","215":"b:f6X/7NP/z294AwACBIAggAICAgDoB9AA","240":"b:f6X/7NP/z294AwACBIAggAICAgDoB9AA","247":"b:f6X/7NP/z294AwACBIAggAICAgDoB9AA","249":"b:f6X/7NP/z294AwACBIAggAICAgDoB9AA","260":"b:f6X/7NP/z294AwACBIAggAICAgDoB9AA","278":"b:f6X/7NP/z294AwACBIAggAICAgDoB9AA","282":"b:f6X/7NP/z294AwACBIAggAICAgDoB9AA","284":"b:f6X/7NP/z294AwACBIAggAICAgDoB9AA","291":"b:f6X/7NP/z294AwACBIAggAICAgDoB9AA","304":"b:f6X/7NP/z294AwACBIAggAICAgDoB9AA","308":"b:f6X/7NP/z294AwACBIAggAICAgDoB9AA","325":"b:f6X/7NP/z294AwACBIAggAICAgDoB9AA","342":"b:f6X/7NP/z294AwACBIAggAICAgDoB9AA","345":"b:f6X/7NP/z294AwACBIAggAICAgDoB9AA","352":"b:f6X/7NP/z294AwACBIAggAICAgDoB9AA","390":"b:f6X/7NP/z294AwACBIAggAICAgDoB9AA","396":"b:f6X/7NP/z294AwACBIAggAICAgDoB9AA","400":"b:f6X/7NP/z294AwACBIAggAICAgDoB9AA","409":"b:f6X/7NP/z294AwACBIAggAICAgDoB9AA","421":"b:f6X/7NP/z294AwACBIAggAICAgDoB9AA","429":"b:f6X/7NP/z294AwACBIAggAICAgDoB9AA","437":"b:f6X/7NP/z294AwACBIAggAICAgDoB9AA","456":"b:f6X/7NP/z294AwACBIAggAICAgDoB9AA","475":"b:f6X/7NP/z294AwACBIAggAICAgDoB9AA","478":"b:f6X/7NP/z294AwACBIAggAICAgDoB9AA"},"packages/workflow/src/versioned-node-type.ts":{"10":"b:fqXOSNKVj0UEAwAAACAgAIAAAAAoAAAA","19":"b:fqXOSNKVj0UEAwAAACAgAIAAAAAoAAAA","23":"b:fqXOSNKVj0UEAwAAACAgAIAAAAAoAAAA"},"packages/workflow/src/workflow-checksum.ts":{"46":"b:R4T+zNP/xmkoAwAAAAAAAAAAAADoBwAA","50":"b:R4T+zNP/xmkoAwAAAAAAAAAAAADoBwAA","74":"b:R4T+zNP/xmkoAwAAAAAAAAAAAADoBwAA","99":"b:R4T+zNP/xmkoAwAAAAAAAAAAAADoBwAA"},"packages/workflow/src/workflow-data-proxy-env-provider.ts":{"13":"b:f4DooAAGDUN4AAAAgAAgAAIAAAAIAEAA","41":"b:f4DooAAGDUN4AAAAgAAgAAIAAAAIAEAA","49":"b:f4DooAAGDUN4AAAAgAAgAAIAAAAIAEAA","53":"b:f4DooAAGDUN4AAAAgAAgAAIAAAAIAEAA"},"packages/workflow/src/workflow-data-proxy.ts":{"41":"b:f4DIIAEEDUNQAAAChAAgAAIAEkEIAGAB","63":"b:f4DIIAEEDUNQAAAChAAgAAIAEkEIAGAB","84":"b:f4DIIAEEDUNQAAAChAAgAAIAEkEIAGAB","123":"b:f4DIIAEEDUNQAAAChAAgAAIAEkEIAGAB","128":"b:f4DIIAEEDUNQAAAChAAgAAIAEkEIAGAB","140":"b:f4DIIAEEDUNQAAAChAAgAAIAEkEIAGAB","162":"b:f4DIIAEEDUNQAAAChAAgAAIAEkEIAGAB","163":"b:f4DIIAEEDUNQAAAChAAgAAIAEkEIAGAB","171":"b:f4DIIAEEDUNQAAAChAAgAAIAEkEIAGAB","177":"b:f4DIIAEEDUNQAAAChAAgAAIAEkEIAGAB","189":"b:f4DIIAEEDUNQAAAChAAgAAIAEkEIAGAB","195":"b:f4DIIAEEDUNQAAAChAAgAAIAEkEIAGAB","196":"b:f4DIIAEEDUNQAAAChAAgAAIAEkEIAGAB","200":"b:f4DIIAEEDUNQAAAChAAgAAIAEkEIAGAB","234":"b:f4DIIAEEDUNQAAAChAAgAAIAEkEIAGAB","259":"b:f4DIIAEEDUNQAAAChAAgAAIAEkEIAGAB","260":"b:f4DIIAEEDUNQAAAChAAgAAIAEkEIAGAB","266":"b:f4DIIAEEDUNQAAAChAAgAAIAEkEIAGAB","268":"b:f4DIIAEEDUNQAAAChAAgAAIAEkEIAGAB","270":"b:f4DIIAEEDUNQAAAChAAgAAIAEkEIAGAB","279":"b:f4DIIAEEDUNQAAAChAAgAAIAEkEIAGAB","280":"b:f4DIIAEEDUNQAAAChAAgAAIAEkEIAGAB","282":"b:f4DIIAEEDUNQAAAChAAgAAIAEkEIAGAB","283":"b:f4DIIAEEDUNQAAAChAAgAAIAEkEIAGAB","297":"b:f4DIIAEEDUNQAAAChAAgAAIAEkEIAGAB","302":"b:f4DIIAEEDUNQAAAChAAgAAIAEkEIAGAB","303":"b:f4DIIAEEDUNQAAAChAAgAAIAEkEIAGAB","306":"b:f4DIIAEEDUNQAAAChAAgAAIAEkEIAGAB","312":"b:f4DIIAEEDUNQAAAChAAgAAIAEkEIAGAB","315":"b:f4DIIAEEDUNQAAAChAAgAAIAEkEIAGAB","320":"b:f4DIIAEEDUNQAAAChAAgAAIAEkEIAGAB","321":"b:f4DIIAEEDUNQAAAChAAgAAIAEkEIAGAB","333":"b:f4DIIAEEDUNQAAAChAAgAAIAEkEIAGAB","340":"b:f4DIIAEEDUNQAAAChAAgAAIAEkEIAGAB","341":"b:f4DIIAEEDUNQAAAChAAgAAIAEkEIAGAB","344":"b:f4DIIAEEDUNQAAAChAAgAAIAEkEIAGAB","350":"b:f4DIIAEEDUNQAAAChAAgAAIAEkEIAGAB","352":"b:f4DIIAEEDUNQAAAChAAgAAIAEkEIAGAB","414":"b:f4DIIAEEDUNQAAAChAAgAAIAEkEIAGAB","446":"b:f4DIIAEEDUNQAAAChAAgAAIAEkEIAGAB","563":"b:f4DIIAEEDUNQAAAChAAgAAIAEkEIAGAB","574":"b:f4DIIAEEDUNQAAAChAAgAAIAEkEIAGAB","575":"b:f4DIIAEEDUNQAAAChAAgAAIAEkEIAGAB","682":"b:f4DIIAEEDUNQAAAChAAgAAIAEkEIAGAB","689":"b:f4DIIAEEDUNQAAAChAAgAAIAEkEIAGAB","690":"b:f4DIIAEEDUNQAAAChAAgAAIAEkEIAGAB","693":"b:f4DIIAEEDUNQAAAChAAgAAIAEkEIAGAB","699":"b:f4DIIAEEDUNQAAAChAAgAAIAEkEIAGAB","730":"b:f4DIIAEEDUNQAAAChAAgAAIAEkEIAGAB","737":"b:f4DIIAEEDUNQAAAChAAgAAIAEkEIAGAB","738":"b:f4DIIAEEDUNQAAAChAAgAAIAEkEIAGAB","741":"b:f4DIIAEEDUNQAAAChAAgAAIAEkEIAGAB","747":"b:f4DIIAEEDUNQAAAChAAgAAIAEkEIAGAB","775":"b:f4DIIAEEDUNQAAAChAAgAAIAEkEIAGAB","780":"b:f4DIIAEEDUNQAAAChAAgAAIAEkEIAGAB","781":"b:f4DIIAEEDUNQAAAChAAgAAIAEkEIAGAB","805":"b:f4DIIAEEDUNQAAAChAAgAAIAEkEIAGAB","809":"b:f4DIIAEEDUNQAAAChAAgAAIAEkEIAGAB","836":"b:f4DIIAEEDUNQAAAChAAgAAIAEkEIAGAB","886":"b:f4DIIAEEDUNQAAAChAAgAAIAEkEIAGAB","908":"b:f4DIIAEEDUNQAAAChAAgAAIAEkEIAGAB","921":"b:f4DIIAEEDUNQAAAChAAgAAIAEkEIAGAB","932":"b:f4DIIAEEDUNQAAAChAAgAAIAEkEIAGAB","944":"b:f4DIIAEEDUNQAAAChAAgAAIAEkEIAGAB","957":"b:f4DIIAEEDUNQAAAChAAgAAIAEkEIAGAB","968":"b:f4DIIAEEDUNQAAAChAAgAAIAEkEIAGAB","979":"b:f4DIIAEEDUNQAAAChAAgAAIAEkEIAGAB","988":"b:f4DIIAEEDUNQAAAChAAgAAIAEkEIAGAB","1007":"b:f4DIIAEEDUNQAAAChAAgAAIAEkEIAGAB","1016":"b:f4DIIAEEDUNQAAAChAAgAAIAEkEIAGAB","1019":"b:f4DIIAEEDUNQAAAChAAgAAIAEkEIAGAB","1055":"b:f4DIIAEEDUNQAAAChAAgAAIAEkEIAGAB","1077":"b:f4DIIAEEDUNQAAAChAAgAAIAEkEIAGAB","1081":"b:f4DIIAEEDUNQAAAChAAgAAIAEkEIAGAB","1089":"b:f4DIIAEEDUNQAAAChAAgAAIAEkEIAGAB","1096":"b:f4DIIAEEDUNQAAAChAAgAAIAEkEIAGAB","1148":"b:f4DIIAEEDUNQAAAChAAgAAIAEkEIAGAB","1173":"b:f4DIIAEEDUNQAAAChAAgAAIAEkEIAGAB","1191":"b:f4DIIAEEDUNQAAAChAAgAAIAEkEIAGAB","1214":"b:f4DIIAEEDUNQAAAChAAgAAIAEkEIAGAB","1215":"b:f4DIIAEEDUNQAAAChAAgAAIAEkEIAGAB","1228":"b:f4DIIAEEDUNQAAAChAAgAAIAEkEIAGAB","1261":"b:f4DIIAEEDUNQAAAChAAgAAIAEkEIAGAB","1271":"b:f4DIIAEEDUNQAAAChAAgAAIAEkEIAGAB","1369":"b:f4DIIAEEDUNQAAAChAAgAAIAEkEIAGAB","1388":"b:f4DIIAEEDUNQAAAChAAgAAIAEkEIAGAB","1412":"b:f4DIIAEEDUNQAAAChAAgAAIAEkEIAGAB","1443":"b:f4DIIAEEDUNQAAAChAAgAAIAEkEIAGAB","1444":"b:f4DIIAEEDUNQAAAChAAgAAIAEkEIAGAB","1447":"b:f4DIIAEEDUNQAAAChAAgAAIAEkEIAGAB","1453":"b:f4DIIAEEDUNQAAAChAAgAAIAEkEIAGAB","1468":"b:f4DIIAEEDUNQAAAChAAgAAIAEkEIAGAB","1483":"b:f4DIIAEEDUNQAAAChAAgAAIAEkEIAGAB","1498":"b:f4DIIAEEDUNQAAAChAAgAAIAEkEIAGAB","1544":"b:f4DIIAEEDUNQAAAChAAgAAIAEkEIAGAB","1571":"b:f4DIIAEEDUNQAAAChAAgAAIAEkEIAGAB","1595":"b:f4DIIAEEDUNQAAAChAAgAAIAEkEIAGAB","1651":"b:f4DIIAEEDUNQAAAChAAgAAIAEkEIAGAB","1652":"b:f4DIIAEEDUNQAAAChAAgAAIAEkEIAGAB"},"packages/workflow/src/workflow-expression.ts":{"22":"b:f4Dq5JKHj294AgACgCAgAAIECADoB/AA","30":"b:f4Dq5JKHj294AgACgCAgAAIECADoB/AA","74":"b:f4Dq5JKHj294AgACgCAgAAIECADoB/AA","89":"b:f4Dq5JKHj294AgACgCAgAAIECADoB/AA","94":"b:f4Dq5JKHj294AgACgCAgAAIECADoB/AA","154":"b:f4Dq5JKHj294AgACgCAgAAIECADoB/AA","189":"b:f4Dq5JKHj294AgACgCAgAAIECADoB/AA","222":"b:f4Dq5JKHj294AgACgCAgAAIECADoB/AA","269":"b:f4Dq5JKHj294AgACgCAgAAIECADoB/AA","274":"b:f4Dq5JKHj294AgACgCAgAAIECADoB/AA","278":"b:f4Dq5JKHj294AgACgCAgAAIECADoB/AA"},"packages/workflow/src/workflow-structure-validation.ts":{"94":"b:R4T+zNP/xmkoAwAAAAAAAAAAAADoBwEA","97":"b:R4T+zNP/xmkoAwAAAAAAAAAAAADoBwEA","103":"b:R4T+zNP/xmkoAwAAAAAAAAAAAADoBwEA","105":"b:R4T+zNP/xmkoAwAAAAAAAAAAAADoBwEA","113":"b:R4T+zNP/xmkoAwAAAAAAAAAAAADoBwEA","119":"b:R4T+zNP/xmkoAwAAAAAAAAAAAADoBwEA","190":"b:R4T+zNP/xmkoAwAAAAAAAAAAAADoBwEA"},"packages/workflow/src/workflow.ts":{"87":"b:f5Lq5JOHjm96AgACgGUAgAIsCgP4B/AD","137":"b:f5Lq5JOHjm96AgACgGUAgAIsCgP4B/AD","144":"b:f5Lq5JOHjm96AgACgGUAgAIsCgP4B/AD","149":"b:f5Lq5JOHjm96AgACgGUAgAIsCgP4B/AD","153":"b:f5Lq5JOHjm96AgACgGUAgAIsCgP4B/AD","157":"b:f5Lq5JOHjm96AgACgGUAgAIsCgP4B/AD","164":"b:f5Lq5JOHjm96AgACgGUAgAIsCgP4B/AD","217":"b:f5Lq5JOHjm96AgACgGUAgAIsCgP4B/AD","245":"b:f5Lq5JOHjm96AgACgGUAgAIsCgP4B/AD","253":"b:f5Lq5JOHjm96AgACgGUAgAIsCgP4B/AD","254":"b:f5Lq5JOHjm96AgACgGUAgAIsCgP4B/AD","261":"b:f5Lq5JOHjm96AgACgGUAgAIsCgP4B/AD","262":"b:f5Lq5JOHjm96AgACgGUAgAIsCgP4B/AD","271":"b:f5Lq5JOHjm96AgACgGUAgAIsCgP4B/AD","300":"b:f5Lq5JOHjm96AgACgGUAgAIsCgP4B/AD","309":"b:f5Lq5JOHjm96AgACgGUAgAIsCgP4B/AD","330":"b:f5Lq5JOHjm96AgACgGUAgAIsCgP4B/AD","334":"b:f5Lq5JOHjm96AgACgGUAgAIsCgP4B/AD","390":"b:f5Lq5JOHjm96AgACgGUAgAIsCgP4B/AD","409":"b:f5Lq5JOHjm96AgACgGUAgAIsCgP4B/AD","447":"b:f5Lq5JOHjm96AgACgGUAgAIsCgP4B/AD","491":"b:f5Lq5JOHjm96AgACgGUAgAIsCgP4B/AD","544":"b:f5Lq5JOHjm96AgACgGUAgAIsCgP4B/AD","563":"b:f5Lq5JOHjm96AgACgGUAgAIsCgP4B/AD","581":"b:f5Lq5JOHjm96AgACgGUAgAIsCgP4B/AD","595":"b:f5Lq5JOHjm96AgACgGUAgAIsCgP4B/AD","610":"b:f5Lq5JOHjm96AgACgGUAgAIsCgP4B/AD","624":"b:f5Lq5JOHjm96AgACgGUAgAIsCgP4B/AD","635":"b:f5Lq5JOHjm96AgACgGUAgAIsCgP4B/AD","658":"b:f5Lq5JOHjm96AgACgGUAgAIsCgP4B/AD","676":"b:f5Lq5JOHjm96AgACgGUAgAIsCgP4B/AD","677":"b:f5Lq5JOHjm96AgACgGUAgAIsCgP4B/AD","691":"b:f5Lq5JOHjm96AgACgGUAgAIsCgP4B/AD","750":"b:f5Lq5JOHjm96AgACgGUAgAIsCgP4B/AD","821":"b:f5Lq5JOHjm96AgACgGUAgAIsCgP4B/AD","851":"b:f5Lq5JOHjm96AgACgGUAgAIsCgP4B/AD","852":"b:f5Lq5JOHjm96AgACgGUAgAIsCgP4B/AD","871":"b:f5Lq5JOHjm96AgACgGUAgAIsCgP4B/AD","896":"b:f5Lq5JOHjm96AgACgGUAgAIsCgP4B/AD"},"localhost-40925/assets/_plugin-vue_export-helper-Da88TEg1.js":{"2":[2]},"localhost-40925/assets/chunk-CC9Q-vWm.js":{"8":[2],"9":[2],"10":[2],"19":[2],"23":[2],"29":[2],"33":[2],"34":[2]},"packages/@n8n/api-types/src/agent-builder-tool-node-types.ts":{"20":"b://Vf///5/7f/+/////////////8HAAAA"},"packages/@n8n/api-types/src/agents/agent-integration.schema.ts":{"3":"b://Vf///5/7f/+/////////////8HAAAA","16":"b://Vf///5/7f/+/////////////8HAAAA","22":"b://Vf///5/7f/+/////////////8HAAAA","35":"b://Vf///5/7f/+/////////////8HAAAA"},"packages/@n8n/api-types/src/agents/dto.ts":{"115":"b://Vf///5/7f/+/////////////8HAAAA","131":"b://Vf///5/7f/+/////////////8HAAAA"},"packages/@n8n/api-types/src/dto/data-table/list-data-table-query.dto.ts":{"32":"b://Vf///5/7f/+/////////////8HAAAA","36":"b:AAAAKwAA0ACAGXAgABAAAAAAAAAAAAAA"},"packages/@n8n/api-types/src/dto/evaluations/llm-judge-providers.ts":{"120":"b://Vf///5/7f/+/////////////8HAAAA"},"packages/@n8n/api-types/src/schemas/password.schema.ts":{"13":"b://Vf///7/7f/+/////////////8HAAAE","18":[186,41,56,122,64,126],"21":[186,41,56,122,64,126]},"packages/@n8n/chat-hub/src/constants.ts":{"50":"b://Vf///5/7f/+/////////////8HAAAA"},"packages/@n8n/codemirror-lang-sql/src/sql.ts":{"19":"b://Vf///5/7f/+/////////////8HAAAA","26":[135],"61":"b://Vf///5/7f/+/////////////8HAAAA","76":[135],"80":[135],"140":"b://Vf///5/7f/+/////////////8HAAAA","157":"b://Vf///5/7f/+/////////////8HAAAA","195":[135]},"packages/@n8n/codemirror-lang-sql/src/tokens.ts":{"33":"b://Vf///5/7f/+/////////////8HAAAA","80":[135],"88":[135],"113":[135],"122":[135],"141":[135],"164":[135],"171":"b://Vf///5/7f/+/////////////8HAAAA","221":"b://Vf///5/7f/+/////////////8HAAAA","240":"b://Vf///5/7f/+/////////////8HAAAA","241":[135]},"packages/@n8n/permissions/src/roles/custom-role-scopes.ee.ts":{"154":"b://Vf///5/7f/+/////////////8HAAAA","169":"b://Vf///5/7f/+/////////////8HAAAA","170":"b://Vf///5/7f/+/////////////8HAAAA","182":"b://Vf///5/7f/+/////////////8HAAAA","183":"b://Vf///5/7f/+/////////////8HAAAA"},"packages/@n8n/permissions/src/scope-information.ts":{"4":"b://Vf///5/7f/+/////////////8HAAAA","5":"b://Vf///5/7f/+/////////////8HAAAA","6":"b://Vf///5/7f/+/////////////8HAAAA","12":"b://Vf///5/7f/+/////////////8HAAAA","13":"b://Vf///5/7f/+/////////////8HAAAA","14":"b://Vf///5/7f/+/////////////8HAAAA"},"packages/@n8n/permissions/src/utilities/get-resource-permissions.ee.ts":{"14":"b://Vf///5/7f/+//z//////7///8HAAAA","15":"b://Vf///5/7f/+//z//////7///8HAAAA","18":"b://Vf///5/7f/+//z//////7///8HAAAA"},"packages/@n8n/telemetry/src/define.ts":{"19":"b://Vf///5/7f/+/////////////8HAAAA"},"packages/@n8n/tournament/src/FunctionEvaluator.ts":{"8":"b:x/VPfPJZjxfWA4hIgOEvsN7//xMHAAAA","18":"b:x/VPfPJZjxfWA4hIgOEvsN7//xMHAAAA"},"packages/@n8n/utils/src/create-event-queue.ts":{"7":"b:x/Vf/PL5nwf+I4hP6O9/+P////8HAAAA","17":"b:x/Bf6PKBnwf+A4ADgO8g8N7/r/8GAAAA","44":"b:x/Bf6PKBnwf+A4ADgO8g8N7/r/8GAAAA"},"packages/@n8n/utils/src/event-bus.ts":{"50":"b://Vf///5/7f/+/////////////8HAAAA","60":[86,161],"61":[86,161],"68":"b:9/Vf/f75/7f/M8hO7v//9f7///8HAAAA","75":"b:9/Vf/PL5/7f/M8hP6u//9/////8HAAAA","78":"b:x/Vf/PL5/7f/M8hO6u9/9/////8HAAAA"},"packages/@n8n/utils/src/placeholder.ts":{"13":[36,78,80,100,38,81,39,40,82],"22":[36,78,80,100,38,81,39,40,82],"32":"b:1vVH3NJZjbfbQ4dK3uU+sdz+/5MCAAAA","43":[18,36,78,80,100,38,81,39,40,82],"63":"b:1vVH3NJZjbfbQ4dK3uU+sdz+/5MCAAAA","84":[18,36,78,80,100,38,81,39,40,82],"87":[18,36,78,80,100,38,81,39,40,82],"91":[18,36,78,80,100,38,81,39,40,82],"95":[18,36,78,80,100,38,81,39,40,82],"107":[18,36,78,80,100,38,81,39,40,82],"111":[18,36,78,80,100,38,81,39,40,82]},"packages/@n8n/utils/src/sort/sort-by-property.ts":{"1":"b:x/Vf///5/xf/++9D+P+/8Pz///8HAAAA","6":"b:AAEEEAhI+QMeAQAAqJoUsFjTHE8HAAAA"},"packages/frontend/@n8n/chat/src/api/generic.ts":{"1":[19,1,7,2,52,57,129,6],"5":[19,1,7,2,52,57,129,6],"38":[52],"49":[19,1,7,2,52,57,129,6],"56":[52]},"packages/frontend/@n8n/chat/src/api/message.ts":{"9":[52],"24":[19,1,7,2,52,57,129,6],"68":[52],"73":[52],"82":[52],"119":[52],"151":[52],"211":[52],"245":[52]},"packages/frontend/@n8n/chat/src/components/Chat.vue":{"14":[19,1,7,2,52,57,129,6],"20":[19,1,7,2,52,57,129,6],"25":[52],"27":[52],"28":[52],"31":[19,1,7,2,52,57,129,6],"36":[19,1,7,2,52,57,129,6],"41":[19,1,7,2,52,57,129,6],"46":[19,1,7,2,52,57,129,6],"51":[52],"55":[52],"106":[19,1,7,2,52,57,129,6],"108":[1,52,6],"116":[19,1,7,2,52,57,129,6],"122":[52],"127":[19,1,7,2,52,57,129,6],"131":[19,1,7,2,52,57,129,6],"149":[19,1,7,2,52,57,129,6]},"packages/frontend/@n8n/chat/src/components/GetStartedFooter.vue":{"5":[19,1,7,2,52,57,129,6]},"packages/frontend/@n8n/chat/src/components/Input.vue":{"23":[19,1,7,2,52,57,129,6],"48":[19,1,7,2,52,57,129,6],"54":[19,1,7,2,52,57,129,6],"55":[52],"58":[19,1,7,2,52,57,129,6],"59":[52],"61":[19,1,7,2,52,57,129,6],"77":[52],"93":[52],"97":[19,1,7,2,52,57,129,6],"104":[19,1,7,2,52,57,129,6],"117":[52],"129":[52],"135":[19,1,7,2,52,57,129],"141":[52],"146":[19,1,7,2,52,57,129,6],"157":[52],"178":[52],"222":[52],"235":[52],"238":[52],"241":[52],"244":[52],"251":[52],"261":[52],"282":[19,1,7,2,52,57,129,6],"312":[52,129],"321":[52],"334":[52,129],"345":[52],"350":[19,1,7,2,52,57,129,6],"358":[19,1,7,2,52,57,129,6],"369":[19,1,7,2,52,57,129,6],"397":[52]},"packages/frontend/@n8n/chat/src/components/Layout.vue":{"6":[19,1,7,2,52,57,129,6],"8":[19,1,7,2,52,57,129,6],"15":[19,1,7,2,52,57,129,6],"20":[52],"24":[19,1,7,2,52,57,129,6]},"packages/frontend/@n8n/chat/src/components/MarkdownRenderer.vue":{"16":[19,1,7,2,57,129,6],"22":[19,1,7,2,57,129,6],"42":[19,1,7,2,57,129,6]},"packages/frontend/@n8n/chat/src/components/Message.vue":{"20":[19,1,7,2,52,57,129,6],"25":[19,1,7,2,52,57,129,6],"29":[19,1,7,2,52,57,129,6],"37":[19,1,7,2,52,57,129,6],"49":[52],"50":[52],"52":[52],"57":[19,1,7,2,52,57,129,6],"69":[19,1,7,2,52,57,129,6]},"packages/frontend/@n8n/chat/src/components/MessageActions.vue":{"8":[19,1,7,2,52,57,129,6],"16":[52],"30":[52],"35":[19,1,7,2,52,57,129,6],"43":[52]},"packages/frontend/@n8n/chat/src/components/MessageTyping.vue":{"8":[19,1,7,2,52,57,129,6],"23":[19,1,7,2,52,57,129,6],"31":[19,1,7,2,52,57,129,6],"34":[19,1,7,2,52,57,129,6]},"packages/frontend/@n8n/chat/src/components/MessagesList.vue":{"19":[19,1,7,2,52,57,129,6],"23":[19,1,7,2,52,57,129,6],"24":[19,1,7,2,52,57,129,6],"32":[19,1,7,2,52,57,129,6],"41":[52],"48":[52],"53":[19,1,7,2,52,57,129,6],"55":[19,1,7,2,52,57,129,6]},"packages/frontend/@n8n/chat/src/components/PoweredBy.vue":{"2":[19,1,7,2,52,57,129,6]},"packages/frontend/@n8n/chat/src/composables/useChat.ts":{"6":"b://Vf///5/7f/+/////////////8HAAAA"},"packages/frontend/@n8n/chat/src/composables/useI18n.ts":{"5":[19,1,7,2,57,129,6],"9":[19,1,7,2,57,129,6],"17":[19,1,7,2,57,129,6]},"packages/frontend/@n8n/chat/src/composables/useOptions.ts":{"6":[19,1,7,2,57,129,6]},"packages/frontend/@n8n/chat/src/plugins/chat.ts":{"26":[19,1,7,2,52,57,129,6],"41":[19,1,7,2,52,57,129,6],"69":[52],"77":[52],"100":[52],"130":[52],"143":[52],"146":[52],"149":[52],"193":[19,1,7,2,52,57,129,6],"210":[19,1,7,2,52,57,129,6],"218":[19,1,7,2,52,57,129,6],"219":[52],"226":[19,1,7,2,52,57,129,6],"235":[19,1,7,2,52,57,129,6],"295":[19,1,7,2,52,57,129,6],"302":[19,1,7,2,52,57,129,6],"318":[52],"334":[19,1,7,2,52,57,129,6]},"packages/frontend/@n8n/chat/src/utils/event-bus.ts":{"11":"b://Vf///5/7f/+/////////////8HAAAA","22":[19,1,7,2,57,129,6],"36":[19,1,7,2,57,129,6],"40":[19,1,7,2,57,129,6]},"packages/frontend/@n8n/chat/src/utils/streaming.ts":{"22":[19,1,7,2,52,57,129,6],"24":[52],"31":[52],"46":[52],"51":[52],"57":[52],"66":[52],"82":[52],"88":[52],"89":[52],"92":[52],"96":[52],"100":[52],"102":[52],"103":[52],"106":[52],"113":[19,1,7,2,52,57,129,6],"122":[52],"127":[52]},"packages/frontend/@n8n/composables/src/structuralComputed.ts":{"21":"b:x/VP/PL5j6f+A4hO5Oc/8f////sHAAAA","27":"b:x/VP/PL5jwf+A4hO4Oc/8P////sHAAAA"},"packages/frontend/@n8n/composables/src/useClipboard.ts":{"25":[86,68],"48":[131,134,135,72,155],"59":[131,134,135,72],"77":"b://Vf/P/5nwf+4+9O/e+/9P7///8HAAAA","90":"b:1vVDjJpZiQb6A8ICgYyesNz/f/kGAAAA"},"packages/frontend/@n8n/composables/src/useDebounce.ts":{"8":"b://Vf///5/7f/+/////////////8HAAAA","27":"b://Vf///5/7f/+/////////////8HAAAA","39":"b:1vVH3Y54nbf7A4BK6P8+8cz+//MGAAAA","53":"b:guVBnQ54iQS6AQBC4B2O8Mz+flIGAAAA"},"packages/frontend/@n8n/composables/src/useDeviceSupport.ts":{"3":"b://Vf///5/7f/+/////////////8HAAAA","26":"b:RgAIALABAAIEAAAAAAAQAHwICIEAAAAA","28":"b:QuEGVARIIBSHA4IAADq7IO7UD/kHAAAA"},"packages/frontend/@n8n/composables/src/useDocumentTitle.ts":{"28":"b://Vf///5/7f/+/////////////8HAAAA","37":"b://Vf///5/7f/++/P//////////8HAAAA","42":[155],"47":"b:x/Vf/PL5nwf+I4hP6O8/8P////8HAAAA"},"packages/frontend/@n8n/composables/src/useExternalHooks.ts":{"48":"b://Vf///5/7f/+/////////////8HAAAA"},"packages/frontend/@n8n/composables/src/useShortKeyPress.ts":{"6":"b:x/Vf/PL5nwf+I4hO6O8/8P////8HAAAA","33":"b:AMAGAAAAAAAAAAAAAAAAAIiQOAAAAAAA"},"packages/frontend/@n8n/composables/src/useStyles.ts":{"3":"b://Vf///5/7f/+/////////////8HAAAA","4":"b://Vf///5/7f/+/////////////8HAAAA","11":"b://Vf///5/7f/+/////////////8HAAAA"},"packages/frontend/@n8n/composables/src/useTelemetry.ts":{"91":"b://Vf///5/7f/+/////////////8HAAAA"},"packages/frontend/@n8n/composables/src/useThrottleWithReactiveDelay.ts":{"7":"b:x/Vf/PL5nwf+I4hC6O8/8P7///8HAAAA","12":"b:x/Vf/PL5nwf+I4hC6O8/8P7///8HAAAA"},"packages/frontend/@n8n/composables/src/useToast.ts":{"50":"b://Vf///5/7f/+/////////////8HAAAA","59":"b:19EI8QuA/xf/A+ACj3/iZg7+v+0CAAAA","75":"b://Vf///5/7f/+/////////////8HAAAA","78":"b:woEAAAsAAAAYA4AABAAAAggKCmAAAAAA","86":"b:woEAAAsAAAAYA4AABAAAAggKCmAAAAAA","92":"b:19EI8QuA/xf/A+ACj3/iZg7+v+0CAAAA","167":"b:AAAAAQAA8AGEAEAAAAgABgACAAgCAAAA","181":[24],"223":[98,32,8,35,121,139,147,73],"260":"b:x5BK4JKBjwb6E8ACgG8gsI7/r+kGAAAA","261":[15,87,137,157,158,73]},"packages/frontend/@n8n/design-system/src/components/AskAssistantChat/AskAssistantChat.vue":{"25":"b://Vf///5/7f/++/v//////////8HAAAA","62":[15],"77":"b://Vf///5/7f/++/v//////////8HAAAA","78":[15,87,16,17,18,20],"86":"b://Vf///5/7f/++/v//////////8HAAAA","87":[15,87,16,17,18,20],"99":"b://Vf///5/7f/++/v//////////8HAAAA","133":"b://Vf///5/7f/++/v//////////8HAAAA","272":"b://Vf///5/7f/++/v//////////8HAAAA","275":"b://Vf///5/7f/++/v//////////8HAAAA","287":[15,87,16,17,18,20],"303":"b:x/Vf/AL4j6f+A4hO4Oc/8fz///sHAAAA","307":"b://Vf///5/7f/++/v//////////8HAAAA","311":"b://Vf///5/7f/++/v//////////8HAAAA","315":"b://Vf///5/7f/++/v//////////8HAAAA","321":[15,87,16,17,18,20],"327":"b://Vf///5/7f/++/v//////////8HAAAA","332":"b://Vf///5/7f/++/v//////////8HAAAA","345":[18],"360":"b:x/Vf/AL4j6f+A4hO4Oc/8fz///sHAAAA","364":[20],"378":[15],"382":[15,17,18,20],"391":[18,20],"395":[18,20],"399":[15,17,18,20],"403":"b://Vf///5/7f/++/v//////////8HAAAA","405":"b://Vf///5/7f/++/v//////////8HAAAA","422":"b://Vf///5/7f/++/v//////////8HAAAA","439":[15,87,16,17],"443":[15,87,16,17,18,20],"456":[15,87,16,17,18,20],"464":[15,87,16,17,18,20],"469":"b://Vf///5/7f/++/v//////////8HAAAA","500":[15,87,16,17,18,20],"507":[15,87,16,17,18,20],"534":[87],"535":[87],"538":[18],"554":[15,87,17],"562":[15],"563":[15,87,17],"597":"b://Vf///5/7f/++/v//////////8HAAAA","602":[18,20],"614":"b://Vf///5/7f/++/v//////////8HAAAA"},"packages/frontend/@n8n/design-system/src/components/AskAssistantIcon/AssistantIcon.vue":{"4":"b://Vf///5/7f/++/v//////////8HAAAA","22":"b://Vf///5/7f/++/v//////////8HAAAA","30":"b://Vf///5/7f/++/v//////////8HAAAA"},"packages/frontend/@n8n/design-system/src/components/AskAssistantText/AssistantText.vue":{"9":"b://Vf///5/7f/++/v//////////8HAAAA"},"packages/frontend/@n8n/design-system/src/components/ConditionalRouterLink/CondtionalRouterLink.vue":{"16":"b:x/Vf///5/xf/++9D+P+/8Pz///8HAAAA","27":"b:x/Vf///5/xf3++9D+P+/8Pz///8HAAAA","32":"b:x/Vf///5/xf/++9D+P+/8Pz///8HAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nActionDropdown/ActionDropdown.vue":{"14":"b://Vf/P/5nwf+I4pD7e8/8P7///8HAAAA","50":"b://Vf/P/5nwf+I4pD7e8/8P7///8HAAAA","55":"b://Vf/P/5nwf+I4pD7e8/8P7///8HAAAA","60":"b://Vf/P/5nwf+I4pD7e8/8P7///8HAAAA","68":"b://Vf/P/5nwf+I4pD7e8/8P7///8HAAAA","69":"b://Vf/P/5nwf+I4pD7e8/8P7///8HAAAA","87":"b:QMACQAQAEAGAAIIAASI5UEAADggBAAAA","88":"b:QMACQAQAEAGAAIIAASI5UEAADggBAAAA","98":"b://Vf/P/5nwf+I4pD7e8/8P7///8HAAAA","106":"b://Vf/P/5nwf+I4pD7e8/8P7///8HAAAA","131":"b://Vf/P/5nwf+I4pD7e8/8P7///8HAAAA","144":"b:QMACQAQAEAGAAIIAASI5UEAADggBAAAA","152":"b:QMACQAQAEAGAAIIAASI5UEAADggBAAAA","154":"b:QMACQAQAEAGAAIIAASI5UEAADggBAAAA","159":"b:QMACQAQAEAGAAIIAASI5UEAADggBAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nActionToggle/ActionToggle.vue":{"31":"b:1vVH3c9Y/bbbA4BCzv3+tdz//xsGAAAA","52":"b:1vVH3c9Y/bbbA4BCzv3+tdz//xsGAAAA","53":"b:1vVH3c9Y/bbbA4BCzv3+tdz//xsGAAAA","62":"b:AAAAAAgA8JAAAAAAAghABAAAIAACAAAA","63":"b:AAAAAAgA8JAAAAAAAghABAAAIAACAAAA","92":"b:1vVH3c9Y/bbbA4BCzv3+tdz//xsGAAAA","99":"b:AAAAAAgA8JAAAAAAAghABAAAIAACAAAA","117":"b:1vVH3c9Y/bbbA4BCzv3+tdz//xsGAAAA","130":[107],"132":[107],"139":"b:AAAAAAgA8JAAAAAAAghABAAAIAACAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nBlockUi/BlockUi.vue":{"12":"b:xvVL3LJZjQfaAYBKwOU+sNz+/5MCAAAA","13":"b:xvVL3LJZjQfSAYBKwOU+sNz+/5MCAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nButton/Button.vue":{"9":"b://Vf///5/7f/++/v//////////8HAAAA","25":"b://Vf///5/7f/++/v//////////8HAAAA","31":"b://Vf///5/7f/++/v//////////8HAAAA","37":"b://Vf///5/7f/++/v//////////8HAAAA","43":"b://Vf///5/7f/++/v//////////8HAAAA","48":"b://Vf///5/7f/++/v//////////8HAAAA","53":"b://Vf///5/7f/++/v//////////8HAAAA","55":"b://Vf///5/7f/++/v//////////8HAAAA","81":"b://Vf/f+5/7f/e+NC////9/7///8HAAAA","84":"b://Vf///5/7f/++/v//////////8HAAAA","103":"b:9/Vf///5/7f3++/v//////////8HAAAA","105":"b://Vf///5/7f/++/v//////////8HAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nChatInput/ChatInput.vue":{"41":"b://Vf///5/7f/++/v//////////8HAAAA","77":"b://Vf///5/7f/++/v//////////8HAAAA","82":"b://Vf///5/7f/++/v//////////8HAAAA","96":"b://Vf///5/7f/++/v//////////8HAAAA","97":"b://Vf///5/7f/++/v//////////8HAAAA","107":"b://Vf///5/7f/++/v//////////8HAAAA","111":"b://Vf///5/7f/++/v//////////8HAAAA","120":"b://Vf///5/7f/++/v//////////8HAAAA","122":"b://Vf///5/7f/++/v//////////8HAAAA","131":"b://Vf///5/7f/++/v//////////8HAAAA","133":"b:OIAWAPEBAAAAQAMAFQAAAAAAAAAAAAAA","144":"b://Vf///5/7f/++/v//////////8HAAAA","153":"b:OIAWAPEBAAAAQAMAFQAAAAAAAAAAAAAA","160":"b:OIAWAPEBAAAAQAMAFQAAAAAAAAAAAAAA","164":[15,17,18,20],"170":"b:OIAWAPEBAAAAQAMAFQAAAAAAAAAAAAAA","177":[20],"184":[15,17,18,20],"229":"b:OIAXAPEBAAAAQIcAFQAAAAAAAAAAAAAA","234":"b:OIAWAPEBAAAAQIMAFQAAAAAAAAAAAAAA","239":[18],"244":[18],"249":"b://Vf///5/7f/++/v//////////8HAAAA","253":"b://Vf///5/7f/++/v//////////8HAAAA","255":"b://Vf///5/7f/++/v//////////8HAAAA","265":"b://Vf///5/7f/++/v//////////8HAAAA","269":"b:9/Vf///5/7f3++/v//////////8HAAAA","290":"b:OIAGAPEBAAAAQAMAFQAAAAAAAAAAAAAA","304":"b:OIAGAPEBAAAAQAMAFQAAAAAAAAAAAAAA","314":[98],"326":"b:OIAWAPEBAAAAQAMAFQAAAAAAAAAAAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nCommandBar/CommandBar.vue":{"34":"b://Vf///5/7f/+//z//////z///8HAAAA","42":[158,161],"43":[158,161],"46":[158,161],"50":[158,161],"57":[158,161],"62":[161],"85":[158,161],"90":[158,161],"103":[158,161],"110":[158,161],"115":[158,161],"122":[161],"126":[158,161],"127":[158,161],"155":[158,161],"163":[161],"189":[161],"202":"b:QuEWVARI4BSHA8IAIHq7Iez0P/0HAAAA","252":"b://Vf/b/5/7f/K+JD7///9/z///8HAAAA","260":[161],"265":"b://Vf///5/7f/+//z//////z///8HAAAA","270":[55,86],"274":"b://Vf///5/7f/+//z//////z///8HAAAA","279":"b:9/Vf///5/7f3+//z//////z///8HAAAA","280":"b:9/Vf///5/7f3+//z//////z///8HAAAA","288":[158],"293":[161],"313":[158,161],"325":[158,161],"327":[158,161],"340":[161]},"packages/frontend/@n8n/design-system/src/components/N8nDropdownMenu/DropdownMenu.typeguards.ts":{"6":"b://Vf/f/5/7f/I4pD7///9f7///8HAAAA","8":"b://Vf/f/5/7f/I4pD7///9f7///8HAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nDropdownMenu/DropdownMenu.vue":{"26":"b://Vf/f/5/7f/I4pD7///9f7///8HAAAA","55":[4,5,96,52,107,53],"60":[4,5,96,52,107,53],"79":"b://Vf/f/5/7f/I4pD7///9f7///8HAAAA","87":"b://Vf/f/5/7f/I4pD7///9f7///8HAAAA","101":"b:cMEDQAwA+JGAAIJAAyp5VEAALggDAAAA","110":[52,107,53],"129":"b:cMEDQAwA+JGAAIJAAyp5VEAALggDAAAA","141":"b:cMEDQAwA+JGAAIJAAyp5VEAALggDAAAA","156":"b:9vVL3b5Z/ZfbA4JCh75/9Nz/f/sHAAAA","163":"b:8vVDzA4Q+ZXaAYJCg6591Mz7b5kDAAAA","170":"b:8vVDyA4A+ZXaAYJCg6p91Mz7b5gDAAAA","184":"b:cMEDQAwA+JGAAIJAAyp5VEAALggDAAAA","190":"b://Vf/f/5/7f/I4pD7///9f7///8HAAAA","191":"b://Vf/f/5/7f/I4pD7///9f7///8HAAAA","203":"b:1vVLnb5Z/ZbbAwJCh5x+tNz/f/sGAAAA","212":"b:cMEDQAwA+JGAAIJAAyp5VEAALggDAAAA","242":"b://Vf/f/5/7f/I4pD7///9f7///8HAAAA","248":"b:9/Vf/f/5/7f3I4pD7///9f7///8HAAAA","249":"b://Vf/f/5/7f/I4pD7///9f7///8HAAAA","278":"b://Vf/f/5/7f/I4pD7///9f7///8HAAAA","292":"b:cMEDQAwA+JGAAIJAAyp5VEAALggDAAAA","304":[4,5,96],"312":[4,5,96],"337":[4,5,96],"340":[4,5,96],"343":[4,5,96],"363":[107],"371":"b:QMACQAQAEAGAAIIAASI5UEAADggBAAAA","374":"b:QMACQAQAMAGAAIIAASo5UEAADggBAAAA","377":"b:QMACQAwA8JGAAIIAAyp5VEAALggDAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nFormInput/validators.ts":{"7":"b:AAAAAQkAgBAAEWCgABCAIgAAAAACAAAA","36":[119],"67":[76,95,55,121,86],"97":[85,119],"109":"b://Vf///5/7f/+/////////////8HAAAA","139":"b:AAAAAQkAgBAAEWCgABCAIgAAAAACAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nHeading/Heading.vue":{"27":"b://Vf///5/7f/+/////////////8HAAAA","34":"b://Vf///5/7f/+/////////////8HAAAA","50":"b://Vf///5/7f/+/////////////8HAAAA","52":"b://Vf///5/7f/+/////////////8HAAAA","56":"b:9/Vf///5/7f3+/////////////8HAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nIcon/Icon.vue":{"25":"b://Vf///5/7f/+/////////////8HAAAA","32":"b://Vf///5/7f/+/////////////8HAAAA","42":"b://Vf///5/7f/+/////////////8HAAAA","54":"b://Vf///5/7f/+/////////////8HAAAA","66":"b://Vf///5/7f/+/////////////8HAAAA","83":"b://Vf///5/7f/+/////////////8HAAAA","91":"b://Vf///5/7f/+/////////////8HAAAA","93":"b://Vf///5/7f/+/////////////8HAAAA","95":"b:x/VP/PL5jxf+A4BO5Oc/8N////sHAAAA","107":"b://Vf///5/7f/+/////////////8HAAAA","109":"b://Vf///5/7f/+/////////////8HAAAA","129":"b://Vf///5/7f/+/////////////8HAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nIcon/custom/bolt-filled.svg":{"9":"b:x/VP/PL5jwf+A4hO4Oc/8N////sHAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nIcon/custom/node-ellipsis.svg":{"11":"b:x/VP/PL5jwf+A4hO4Oc/8P7///sHAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nIcon/custom/node-play.svg":{"11":"b:x/VP/LL5jwf+A4hC4Oc/8N7///sHAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nIcon/custom/node-power.svg":{"11":"b:x/VP/LL5jwf+A4hC4Oc/8Nz///sHAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nIcon/custom/node-success.svg":{"11":"b:x5BK4LKBDwd6AIACgGcgAA7+r+EAAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nIcon/custom/node-trash.svg":{"11":"b:x/VP/LL5jwf+A4hC4Oc/8Pz///sHAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nIcon/custom/pop-out.svg":{"8":"b:x/Vf/PL5nwf+I4hC6O8/8P7///8HAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nIcon/custom/schema.svg":{"8":[19,1,7,2,36,37,39,40,57,6,159]},"packages/frontend/@n8n/design-system/src/components/N8nIcon/custom/spinner.svg":{"9":"b://Vf///5/7f/+/////////////8HAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nIcon/iconColor.ts":{"25":"b://Vf///5/7f/+/////////////8HAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nIcon/icons.ts":{"790":"b:x/VP/PL5jwf+A4hO4Oc/8N////sHAAAA","794":"b:x/VP/PL5jxf+A4BO5Oc/8N////sHAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nIcon/nodes/character-text-splitter.svg":{"11":[0,2]},"packages/frontend/@n8n/design-system/src/components/N8nIcon/nodes/chat-trigger.svg":{"11":"b:xuVJlAJ4iQaaAQBC4AUO8M7yelICAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nIcon/nodes/code.svg":{"11":"b:RYAAhAAAAQD6AIAAAAIYAE0BBuAAAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nIcon/nodes/default-data-loader.svg":{"11":[0,2,14]},"packages/frontend/@n8n/design-system/src/components/N8nIcon/nodes/edit-fields.svg":{"11":"b:RvAIILABjwfWAYBMAIAtAFetzwEBAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nIcon/nodes/manual-trigger.svg":{"11":"b:x/VD/PL5jwX+AYBG4Gc/8M33//MCAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nIcon/nodes/question-and-answer-chain.svg":{"11":[2,14]},"packages/frontend/@n8n/design-system/src/components/N8nIcon/nodes/simple-vector-store.svg":{"11":[0,2,45,141,14]},"packages/frontend/@n8n/design-system/src/components/N8nIcon/nodes/vector-store-retriever.svg":{"11":[2,14]},"packages/frontend/@n8n/design-system/src/components/N8nIconButton/IconButton.vue":{"6":"b://Vf///5/7f/++/v//////////8HAAAA","7":"b:9/Vf///5/7f3++/v//////////8HAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nIconPicker/iconCategories.ts":{"118":"b://Vf///5/7f/+/////////////8HAAAA","129":[55]},"packages/frontend/@n8n/design-system/src/components/N8nInfoTip/InfoTip.vue":{"14":"b:1sEBWZIB/bHZA8ACyl2iMYD+v4EGAAAA","57":"b:1sEBWZIB/bHZA8ACyl2iMYD+v4EGAAAA","63":"b:1sEBWZIB/bHZA8ACyl2iMYD+v4EGAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nInlineTextEdit/InlineTextEdit.vue":{"16":"b:1/Vf/P75v7f/I4hL7u8/8fz///8HAAAA","35":"b:1/Vf/P75v7f/I4hL7u8/8fz///8HAAAA","38":"b:1/Vf/P75v7f/I4hL7u8/8fz///8HAAAA","44":"b:1/Vf/P75v7f/I4hL7u8/8fz///8HAAAA","47":"b:1/Vf/P75v7f/I4hL7u8/8fz///8HAAAA","57":"b:1/Vf/P75v7f/I4hL7u8/8fz///8HAAAA","68":"b:QMECUAYAILCFAoAAACo5YQAADgADAAAA","74":"b:QMACQAIAgACAAIAAACo5QAAADgwDAAAA","81":"b:QMECUAYAILCFAoAAACo5YQAADgADAAAA","92":"b:QMECUAYAILCFAoAAACo5YQAADgADAAAA","96":"b:QMECUAYAoLCFAoAAACo5YQAADgwDAAAA","103":"b:1/Vf/P75v7f/I4hL7u8/8fz///8HAAAA","122":"b:1/Vf/P75v7f3I4hL7u8/8fz///8HAAAA","127":"b:1/Vf/P75v7f3I4hL7u8/8fz///8HAAAA","145":"b:QMECUAYAILCFAoAAACo5YQAADgADAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nInput/Input.vue":{"11":"b:1vVP3f9Z/bffE+Dqz/3+t/z+/78HAAAA","41":"b:1vVP3f9Z/bffE+Dqz/3+t/z+/7sHAAAA","50":"b:1vVP3f9Z/bffE+Dqz/3+t/z+/78HAAAA","60":"b:1vVP3f9Z/bffE+Dqz/3+t/z+/78HAAAA","79":"b:1vVP3f9Z/bffE+Dqz/3+t/z+/78HAAAA","82":"b:1vVP3f9Z/bffE+Dqz/3+t/z+/78HAAAA","84":"b:1vVP3f9Z/bffE+Dqz/3+t/z+/78HAAAA","98":"b:1vVP3f9Z/bffE+Dqz/3+t/z+/78HAAAA","112":"b:kkFCUQ4AibAJE2BCiRnCJwAiNwYHAAAA","119":"b:kkFCUQ4AnbAJE2BCiRHCp1AiNx8DAAAA","125":"b:kkFCUQ4AnbAJE2BiiRnCp1AiNx8HAAAA","131":"b:AEECAAQAAAAAAEAAABiAAUAgAwQCAAAA","136":"b:AEBAQAAAgAAAAABCgAkABEAgMAICAAAA","147":"b:1vVP3f9Z/bffE+Dqz/3+t/z+/78HAAAA","151":"b:1vVP3f9Z/bffE+Dqz/3+t/z+/78HAAAA","157":"b:1vVLnY9Z/ZbfE2BCh9zepNz+f58HAAAA","158":"b:1vVP3f9Z/bffE+Dqz/3+t/z+/78HAAAA","163":"b:1vVP3f9Z/bffE+Dqz/3+t/z+/78HAAAA","164":"b:0uFCXY4InbNZE2DKy9niJxguPwcHAAAA","173":"b:1vVP3f9Z/bffE+Dqz/3+t/z+/78HAAAA","183":"b:kkFCUQ4AmbABE0BiiRmCJVAiNwMDAAAA","184":[145,148],"185":"b:AAAAAQwAhAAAAQAAABAAoAAABRkCAAAA","188":"b:1vVP3f9Z/bffE+Dqz/3+t/z+/78HAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nInputLabel/InputLabel.vue":{"25":"b:1vVH3c9YjbfbE+Dqz/X+t9z+/5sCAAAA","38":"b:1vVH3c9YjbfbE+Dqz/X+t9z+/5sCAAAA","72":"b:1vVH3M5YjbfbE8Dqz+X+t9z+/5sCAAAA","76":"b:kkEBAAwABBAAAkCACwCCggAAEBgCAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nInputNumber/InputNumber.vue":{"15":"b:RuABAAAYCBXQAgBKAsQAgcT8fhAAAAAA","31":"b:RuABAAAYCBXQAgBKAsQAgcT8fhAAAAAA","32":"b:RuABAAAYCBXQAgBKAsQAgcT8fhAAAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nLink/Link.vue":{"31":"b:1/Vf/f75/7f/M+jr6v////z///8HAAAA","32":"b:1/Vf/f75/7f3M+jr6v////z///8HAAAA","34":"b:1/Vf/f75/7f3M+jr6v////z///8HAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nMenuItem/MenuItem.vue":{"15":"b://Vf///5/7f/++/D//////z///8HAAAA","29":"b://Vf///5/7f/++/D//////z///8HAAAA","45":"b:AAAAAA4AwBCBCUAAAwAAIEAAAAAAAAAA","52":"b://Vf///5/7f/++/D//////z///8HAAAA","64":"b://Vf///5/7f/++/D//////z///8HAAAA","73":"b://Vf///5/7f/++/D//////z///8HAAAA","89":"b://Vf///5/7f/++/D//////z///8HAAAA","92":"b://Vf///5/7f/++/D//////z///8HAAAA","99":"b://Vf///5/7f/++/D//////z///8HAAAA","117":"b:9/Vf///5/7f3++/D//////z///8HAAAA","125":[55],"134":"b:ukEBEgsAwLEBK2CATwBCL0AAEAAAAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nNavigationDropdown/NavigationDropdown.vue":{"42":"b:x/Vf///5/xf/++9D+P+/8Pz///8HAAAA","56":"b:x/Vf///5/xf/++9D+P+/8Pz///8HAAAA","58":"b:x/Vf///5/xf/++9D+P+/8Pz///8HAAAA","59":"b:x/Vf///5/xf/++9D+P+/8Pz///8HAAAA","60":"b:x/Vf///5/xf/++9D+P+/8Pz///8HAAAA","84":"b:x/Vf/f/5/xf/a8ND+P+/8Pz///8HAAAA","89":[24,35,108,55,125,71,72,161],"94":[24,35,108,55,125,72,161],"103":"b:x/Vf///5/xf/++9D+P+/8Pz///8HAAAA","113":[24,35,108,55,125,71,72,161],"117":"b:x/Vf///5/xf/++9D+P+/8Pz///8HAAAA","126":"b:x/Vf///5/xf/++9D+P+/8Pz///8HAAAA","128":"b:x/Vf///5/xf/++9D+P+/8Pz///8HAAAA","130":"b:x/Vf///5/xf3++9D+P+/8Pz///8HAAAA","141":"b:AEEEMQlI+RNeAQBAqPqV8HzTX28HAAAA","143":"b:AEEEMQlI+RNeAQBAqPqV8HzTX28HAAAA","161":"b:AEEEMQlI+RNeAQBAqPqV8HzTX28HAAAA","163":"b:AEEEMQlI+RNWAQBAqPqV8HzTX28HAAAA","166":"b:AEEEMQlI+RNWAQBAqPqV8HzTX28HAAAA","170":[55,161],"171":"b:AEEEMQlI+RNeAQBAqPqV8HzTX28HAAAA","172":"b:AEEEMQlI+RNeAQBAqPqV8HzTX28HAAAA","184":[55],"208":"b:x/Vf///5/xf/++9D+P+/8Pz///8HAAAA","212":"b:x/Vf///5/xf3++9D+P+/8Pz///8HAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nNodeIcon/IconContent.vue":{"21":"b://VP/PP5zxf/A4hO7+c/8N////sHAAAA","35":"b:x/VP/PL5jxf+A4BO5Oc/8N////sHAAAA","54":"b:x/VP/PL5jxf+A4BO5Oc/8N////sHAAAA","57":"b://VP/PP5zxf/A4hO7+c/8N////sHAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nNodeIcon/NodeIcon.vue":{"24":"b://VP/PP5zxf/A4hO7+c/8N////sHAAAA","28":"b://VP/PP5zxf/A4hO7+c/8N////sHAAAA","43":"b://VP/PP5zxf/A4hO7+c/8N////sHAAAA","59":"b:xvVD3IJYjQfaA4BKwOU+sMz+/5MGAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nNotice/Notice.vue":{"16":"b:xuFD3AIYiBSaAqhKgIUesMz6m5MCAAAA","30":"b:xuFD3AIYiBSaAqhKgIUesMz6m5MCAAAA","34":"b:xuFD3AIYiBSaAoBKgIUesMz6m5ACAAAA","50":[14],"66":"b:xuFD3AIYiBSaAqhKgIUesMz6m5MCAAAA","75":"b:xuFD3AIYiBSSAqhKgIUesMz6m5MCAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nOption/Option.vue":{"12":"b:9vVH3c9Y/bfbA+DKzv2+99z+/7cGAAAA","13":"b:9vVH3c9Y/bfTA+DKzv2+99z+/7cGAAAA","16":"b:9vVH3c9Y/bfTA+DKzv2+99z+/7cGAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nPopover/Popover.vue":{"84":"b://Vf///5/xf/++9D/f+/8Pz///8HAAAA","105":"b:AEAAAAQAgBBACEAAAAAAgPDwOioCAAAA","111":"b:AEAAAAQAgBAAAEAAAAAAgNDwMgoCAAAA","125":"b:AEAAAAQAgBAAAAAAAAAAgNDwOCICAAAA","133":"b://Vf///5/xf/++9D/f+/8Pz///8HAAAA","134":"b:AEAAAAQAgBBAAAAAAAAAgLDwOgoAAAAA","143":"b://Vf///5/xf/++9D/f+/8Pz///8HAAAA","146":"b:AEAAAAQAgBAACEAAAAAAgNDwOCoCAAAA","147":"b://Vf///5/xf/++9D/f+/8Pz///8HAAAA","148":"b://Vf///5/xf/++9D/f+/8Pz///8HAAAA","151":"b://Vf///5/xf/++9D/f+/8Pz///8HAAAA","167":"b:AEAAAAQAgBBACEAAAAAAgPDwOioCAAAA","174":"b:AAAAAAQAgAAACEAAAAAAgFAAACgCAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nPromptInputSuggestions/N8nPromptInputSuggestions.vue":{"10":"b://Vf///5/7f/++/v//////////8HAAAA","20":[20],"23":"b://Vf///5/7f/++/v//////////8HAAAA","41":"b://Vf///5/7f/++/v//////////8HAAAA","46":[20]},"packages/frontend/@n8n/design-system/src/components/N8nRadioButtons/RadioButton.vue":{"22":"b:1/Vf/LL5nzf/I4hL6u8/8f////8HAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nRadioButtons/RadioButtons.vue":{"20":"b:1/Vf/LL5nzf/I4hL6u8/8f////8HAAAA","33":"b:QlAAAAAAABBwAAAAAAAAAAjyP+IAAAAA","41":"b:1/Vf/LL5nzf/I4hL6u8/8f////8HAAAA","50":"b:1/Vf/LL5nzf3I4hL6u8/8f////8HAAAA","58":"b:QlAAAAAAABBwAAAAAAAAAAjyP+IAAAAA","59":"b:1/Vf/LL5nzf/I4hL6u8/8f////8HAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nResizeWrapper/ResizeWrapper.vue":{"6":"b://Vf///5/7f/++/v//////////8HAAAA","72":"b://Vf///5/7f/++/v//////////8HAAAA","91":"b://Vf///5/7f/++/v//////////8HAAAA","141":"b:xvVDjJJZiQb6A0ICgYwesMz/b/kGAAAA","167":"b:xvVDjJJZiQb6A0ICgYwesMz/b/kGAAAA","195":"b://Vf///5/7f/++/v//////////8HAAAA","201":"b://Vf///5/7f/++/v//////////8HAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nRoute/Route.vue":{"13":"b://Vf///5/7f/++/r//////z///8HAAAA","15":"b://Vf///5/7f/++/r//////z///8HAAAA","28":"b://Vf///5/7f/++/L//////z///8HAAAA","29":"b://Vf///5/7f/++/r//////z///8HAAAA","38":"b://Vf///5/7f/++/j//////z///8HAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nScrollArea/N8nScrollArea.vue":{"50":"b://Vf///5/xf/++9D/f+/8Pz///8HAAAA","68":"b://Vf///5/xf/++9D/f+/8Pz///8HAAAA","82":"b:x/Vf///5/xf/++9D+P+/8Pz///8HAAAA","104":[18,20],"144":"b:x/Vf///5/xf/++9D+P+/8Pz///8HAAAA","163":"b://Vf///5/xf/++9D/f+/8Pz///8HAAAA","172":"b:9/Vf///5/xf3++9D/f+/8Pz///8HAAAA","174":"b://Vf///5/xf/++9D/f+/8Pz///8HAAAA","181":[3,4]},"packages/frontend/@n8n/design-system/src/components/N8nSelect/Select.vue":{"11":"b:9vVH3c9Y/bfbA+DKzv2+99z+/7cGAAAA","63":"b:9vVH3c9Y/bfbA+DKzv2+99z+/7cGAAAA","64":"b:9vVH3c9Y/bfbA+DKzv2+99z+/7cGAAAA","72":"b:9vVH3c9Y/bfbA+DKzv2+99z+/7cGAAAA","85":"b:9vVH3c9Y/bfbA+DKzv2+99z+/7cGAAAA","89":[16,28,8,33,99,55,60,64,125],"93":[1,7,97],"97":"b:AEEAAAIQCCAIAABCxAAAAAAAAAQAAAAA","113":"b:9vVH3c9Y/bfbA+DKzv2+99z+/7cGAAAA","134":"b:9vVH3c9Y/bfbA+DKzv2+99z+/7cGAAAA","136":"b:wFEBEQsA4BBBAwAAChCCZAAgFyACAAAA","142":"b:4mEFHEJYiABAAgBAxqECsIgAWhAAAAAA","145":"b:4mEFHEJYiABAAgBAxqECsIgAWhAAAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nSendStopButton/N8nSendStopButton.vue":{"5":"b://Vf///5/7f/++/v//////////8HAAAA","30":"b:OIAGAPEBAAAAQAMAFQAAAAAAAAAAAAAA","34":[20],"36":"b://Vf///5/7f/++/v//////////8HAAAA","62":"b:9/Vf///5/7f3++/v//////////8HAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nSpinner/Spinner.vue":{"20":"b://Vf///5/7f/+/////////////8HAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nSuggestedActions/SuggestedActions.vue":{"39":"b:x/Bf6PCBnwf+A4ADgO8g8Nz/r/8GAAAA","50":"b:x/Bf6PCBnwf+A4ADgO8g8Nz/r/8GAAAA","52":[127],"58":[127],"60":[127],"64":"b:x/Bf6PCBnwf+A4ADgO8g8Nz/r/8GAAAA","74":[127,155],"76":"b:x/Bf6PCBnwf+A4ADgO8g8Nz/r/8GAAAA","84":[127,155],"86":[127,155],"89":[127,155],"100":[127],"108":[127,155],"112":[127,155],"121":[127,155],"131":[127],"132":[127,155],"142":[127],"143":[127,155]},"packages/frontend/@n8n/design-system/src/components/N8nTabs/Tabs.vue":{"23":"b:xvVD3Y9Y/RfbAYBLzP2+sNz//5sGAAAA","36":"b:xvVD3Y9Y/RfbAYBLzP2+sNz//5sGAAAA","44":"b:xvVD3Y9Y/RfbAYBLzP2+sNz//5sGAAAA","54":"b:xvVDjQ5YyQbaAQAChJwesNz/b5kCAAAA","64":"b:xvVD3Y9Y/RfbAYBLzP2+sNz//5sGAAAA","66":"b:AAABEQ4AwABBAQAAABAAIAAAAAACAAAA","79":[55,145,70],"89":"b:xvVD3Y9Y/RfbAYBLzP2+sNz//5sGAAAA","109":"b:xvVD3Y9Y/RfTAYBLzP2+sNz//5sGAAAA","118":"b:xvVD3Y9Y/RfbAYBLzP2+sNz//5sGAAAA","142":"b:AAEBGQ8A8BCBAQAACBiAMAAAAAgGAAAA","162":[55,145,70]},"packages/frontend/@n8n/design-system/src/components/N8nTag/Tag.vue":{"15":"b:x/Bf6PCBn7f+I+CDgu9w/9z/r/8HAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nText/Text.vue":{"18":"b://Vf///5/7f/++/r//////////8HAAAA","26":"b://Vf///5/7f/++/r//////////8HAAAA","46":"b://Vf///5/7f/++/r//////////8HAAAA","48":"b://Vf///5/7f/++/r//////////8HAAAA","52":"b:9/Vf///5/7f3++/r//////////8HAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nTooltip/Tooltip.vue":{"21":"b://Vf///5/7f/++/v//////////8HAAAA","37":"b://Vf///5/7f/++/v//////////8HAAAA","45":"b://Vf///5/7f/++/v//////////8HAAAA","46":"b://Vf///5/7f/++/v//////////8HAAAA","48":"b://Vf///5/7f/++/v//////////8HAAAA","57":"b://Vf///5/7f/++/v//////////8HAAAA","62":"b://Vf///5/7f/++/v//////////8HAAAA","63":"b://Vf///5/7f/++/v//////////8HAAAA","73":"b://Vf///5/7f/++/v//////////8HAAAA","75":"b:1/VL/I/5zwf+KUJC5Kd/9P7///MDAAAA","77":"b://Vf///5/7f/++/v//////////8HAAAA","88":"b:9/Vf///5/7f3++/v//////////8HAAAA","94":"b:xgAIALEBAAIAAAAAABAABAIAAIAAAAAA","98":"b://Vf///5/7f/++/v//////////8HAAAA","101":"b://Vf///5/7f/++/v//////////8HAAAA","109":"b:xgBIIAAACAMSAAAAgAAAAMwACgAEAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nTree/Tree.vue":{"20":"b:RAAAAAAACQBAAAAIAAAAAACiBQAAAAAA","28":"b:RAAAAAAACQBAAAAIAAAAAACiBQAAAAAA","32":"b:RAAAAAAACQBAAAAIAAAAAACiBQAAAAAA","36":"b:RAAAAAAACQBAAAAIAAAAAACiBQAAAAAA","52":"b:RAAAAAAACQBAAAAIAAAAAACiBQAAAAAA","61":"b:RAAAAAAACQBAAAAIAAAAAACiBQAAAAAA","68":"b:RAAAAAAACQBAAAAIAAAAAACiBQAAAAAA","87":[6,141,144,70],"95":[6,141,144,70]},"packages/frontend/@n8n/design-system/src/composables/useAutosizeTextarea.ts":{"41":"b://Vf///5/7f/++/v//////////8HAAAA","57":"b://Vf///5/7f/++/v//////////8HAAAA","70":"b://Vf///5/7f/++/v//////////8HAAAA","116":"b://Vf///5/7f/++/v//////////8HAAAA","127":"b://Vf///5/7f/++/v//////////8HAAAA","141":"b://Vf///5/7f/++/v//////////8HAAAA","149":"b:1/Vf/f/5/7f/U+tO9///9fz///8HAAAA","159":"b://Vf///5/7f/++/v//////////8HAAAA","165":"b://Vf///5/7f/++/v//////////8HAAAA"},"packages/frontend/@n8n/design-system/src/composables/useCharacterLimit.ts":{"15":"b://Vf///5/7f/++/v//////////8HAAAA","19":"b://Vf///5/7f/++/v//////////8HAAAA","23":[15,17,18,20],"25":"b://Vf///5/7f/++/v//////////8HAAAA"},"packages/frontend/@n8n/design-system/src/composables/useI18n.ts":{"4":"b://Vf///5/7f/++/v//////////8HAAAA","6":"b://Vf///5/7f/++/v//////////8HAAAA"},"packages/frontend/@n8n/design-system/src/composables/useIconBodyLoader.ts":{"20":"b://Vf///5/7f/+/////////////8HAAAA"},"packages/frontend/@n8n/design-system/src/composables/useMessage.ts":{"14":"b://Vf///5/7f/+/////////////8HAAAA","15":[63,137,148,71,73],"49":"b:AAEAAAwAAICQAiAAAQCAgAACEAgCAAAA","64":"b:AAEAAAwAAICQAiAAAQCAgAACEAgCAAAA","111":[107,53,131]},"packages/frontend/@n8n/design-system/src/composables/useTooltipAppendTo.ts":{"7":"b:x/Vf/PL5nwf+I4hC6O8/8P7///8HAAAA","11":"b://Vf///5/7f/++/v//////////8HAAAA","14":"b://Vf///5/7f/++/v//////////8HAAAA"},"packages/frontend/@n8n/design-system/src/directives/n8n-html.ts":{"19":"b:1vVf3b5Z7bfbI4hKzu3+sdz+/5MGAAAA","31":"b:1vVf3b5Z7bfbI4hKzu3+sdz+/5MGAAAA"},"packages/frontend/@n8n/design-system/src/icons/lucide/bucket.ts":{"16":"b:x/VP/PL5jxf+A4BO5Oc/8N////sHAAAA"},"packages/frontend/@n8n/design-system/src/icons/lucide/index.ts":{"9":"b:x/VP/PL5jxf+A4BO5Oc/8N////sHAAAA","15":"b:x/VP/PL5jxf+A4BO5Oc/8N////sHAAAA"},"packages/frontend/@n8n/design-system/src/locale/format.ts":{"8":"b://Vf///5/7f/+/////////////8HAAAA","12":"b://Vf///5/7f/++/v//////////8HAAAA"},"packages/frontend/@n8n/design-system/src/locale/index.ts":{"16":"b://Vf///5/7f/++/v//////////8HAAAA","36":"b://Vf///5/7f/+/////////////8HAAAA","38":"b://Vf///5/7f/+/////////////8HAAAA"},"packages/frontend/@n8n/design-system/src/plugin.ts":{"8":"b://Vf///5/7f/+/////////////8HAAAA"},"packages/frontend/@n8n/design-system/src/types/binary.ts":{"24":"b:RNAEQIAADQVYAIAKAGEoAJT+74EAAAAA"},"packages/frontend/@n8n/design-system/src/utils/cn.ts":{"4":"b://Vf///5/7f/++/v//////////8HAAAA"},"packages/frontend/@n8n/design-system/src/utils/typeguards.ts":{"1":"b:9vVH3c9Y/bfbA+DKzv2+99z+/7cGAAAA"},"packages/frontend/@n8n/design-system/src/utils/uid.ts":{"6":"b://Vf///5/7f/+/////////////8HAAAA"},"packages/frontend/@n8n/design-system/src/v2/components/Select/Select.vue":{"45":"b://Vf///5/7f/++/v//////////8HAAAA","63":"b://Vc///5/7f/+2/v//////////8HAAAA","69":"b://Vf///5/7f/++/v//////////8HAAAA","84":"b://Vf///5/7f/++/v//////////8HAAAA","91":"b://Vf///5/7f/++/v//////////8HAAAA","99":"b://Vf///5/7f/++/v//////////8HAAAA","106":[32,108,122],"108":"b://Vc///5/7f/+2/v//////////8HAAAA","110":"b://Vc///5/7f/+2/v//////////8HAAAA","126":"b://Vf///5/7f/++/v//////////8HAAAA","137":"b:9/Vf///5/7f3++/v//////////8HAAAA","144":"b://Vf///5/7f/++/v//////////8HAAAA","147":"b://Vf///5/7f/++/v//////////8HAAAA","158":"b://Vc///5/7f/+2/v//////////8HAAAA","166":"b://Vc///5/7f/+2/v//////////8HAAAA","167":"b:9/Vc///5/7f3+2/v//////////8HAAAA","169":[32,108,122],"181":"b://Vc///5/7f/+2/v//////////8HAAAA","182":"b://Vc///5/7f/+2/v//////////8HAAAA","185":"b://Vc///5/7f/+2/v//////////8HAAAA","188":"b://Vc///5/7f/+2/v//////////8HAAAA"},"packages/frontend/@n8n/design-system/src/v2/components/Select/SelectItem.vue":{"10":"b://Vc///5/7f/+2/v//////////8HAAAA","13":"b://Vc///5/7f/+2/v//////////8HAAAA","17":"b://Vc///5/7f/+2/v//////////8HAAAA","21":"b://Vc///5/7f/+2/v//////////8HAAAA","25":"b://Vc///5/7f/+2/v//////////8HAAAA","32":[32,56,122],"33":"b://Vc///5/7f/+2/v//////////8HAAAA","35":"b://Vc///5/7f/+2/v//////////8HAAAA","39":"b://Vc///5/7f/+2/v//////////8HAAAA","46":"b://Vc///5/7f/+2/v//////////8HAAAA"},"packages/frontend/@n8n/design-system/src/v2/utils/index.ts":{"6":"b://Vc///5/7f/+2/v//////////8HAAAA","12":"b://Vc///5/7f/+2/v//////////8HAAAA"},"packages/frontend/@n8n/frontend-module-sdk/src/registries/modalRegistry.ts":{"6":"b://Vf///5/7f/+/////////////8HAAAA","10":"b://Vf///5/7f/+//j//////z///8HAAAA","11":"b://Vf///5/7f/+//j//////z///8HAAAA","14":"b://Vf///5/7f/+//j//////z///8HAAAA","42":"b://Vf///5/7f/+/////////////8HAAAA"},"packages/frontend/@n8n/frontend-module-sdk/src/registries/resourceRegistry.ts":{"25":"b://Vf///5/7f/+//j//////z///8HAAAA"},"packages/frontend/@n8n/i18n/src/index.ts":{"41":"b://Vf///5/7f/+/////////////8HAAAA","53":"b:x/VP/PL5j6f6AYBK5Oc+8d7+//MCAAAA","68":"b://Vf///5/7f/+/////////////8HAAAA","98":"b://Vf///5/7f/+/////////////8HAAAA","106":"b:1/VP/PL5j7f7A4BK7uc+8d7+//MCAAAA","110":"b:xgAIALABAAMwAAAAAAAAAAoCAOAAAAAA","143":"b:guVBlAJ4iQSaAQBC4AUO8Mz2flICAAAA","150":"b:kkEBEAIAgBABAgAASgACIAAAEAAAAAAA","158":"b:kkEBEAIAgBABAgAASgACIAAAEAAAAAAA","175":"b:kkEBEAIAgBABAgAASgACIAAAEAAAAAAA","185":"b:kkEBEAIAgBABAgAASgACIAAAEAAAAAAA","195":"b:kkEBEAIAgBABAgAASgACIAAAEAAAAAAA","208":"b:kkEBEAIAgBABAgAASgACIAAAEAAAAAAA","221":"b:kkEBEAIAgBABAgAASgACIAAAEAAAAAAA","234":"b:1/VP/PLZj7f7A4BKzuc+sd7+//MCAAAA","243":"b:1vVH3MJYjbfbA4BKzuU+sdz+/5MCAAAA","255":"b:xvVH3MJYjafaAYBKxOU+sdz+/5MCAAAA","267":"b:xvVH3MJYjafaAYBKxOU+sdz+/5MCAAAA","282":"b:xvVH3IJYjafaAYBKxOU+sdz+/5MCAAAA","300":"b:xvVH3IJYjafaAYBKxOU+sdz+/5MCAAAA","322":"b:xvVD3IJYjQfaAYBKxOU+sNz+/5MCAAAA","345":[139,141,145,146,70],"378":[22,103],"385":"b:x5BK4LKBDwZ6AIACgGcgAA4uruAAAAAA","394":"b:wuVH1AJ4iQSaA4hC4Cc/8Oz2flMHAAAA","412":"b://Vf///5/7f/+/////////////8HAAAA","495":"b://Vf///5/7f/+/////////////8HAAAA"},"packages/frontend/@n8n/i18n/src/utils.ts":{"9":"b:1vVH3MJYjbfbA4BKzuU+sdz+/5MCAAAA","29":"b:1vVH3MJYjbfbA4BKzuU+sdz+/5MCAAAA","31":"b:1vVH3MJYjbfbA4BKzuU+sdz+/5MCAAAA","34":"b:1vVH3MJYjbfbA4BKzuU+sdz+/5MCAAAA","41":"b:1vVH3MJYjbfbA4BKzuU+sdz+/5MCAAAA","49":"b:QsBDSIAIDQPQAABCgIQggFT47hAAAAAA","57":"b:QsBDSIAIDQPQAABCgIQggFT47hAAAAAA","58":"b:QsBDSIAIDQPQAABCgIQggFT47hAAAAAA"},"packages/frontend/@n8n/stores/src/cloudPlan.store.ts":{"31":"b://Vf///5/7f/+/////////////8HAAAA","45":"b://Vf///5/7f/+//j//////z///8HAAAA","49":[55,86],"55":[77],"57":[77],"60":[77],"63":[77],"72":[77],"79":[77],"85":[77],"87":[77],"97":[77],"99":[77],"101":"b:x/Vf///5/xf/++9D+P+/8Pz///8HAAAA","103":[77],"105":"b:AAAACwwA8ACAGWAAABgAMAAAAAgGAAAA","125":[77],"135":[95,77,121,86],"141":[77],"156":[77],"172":[77],"197":[77],"218":[77],"233":[77],"244":[77],"252":[77]},"packages/frontend/@n8n/stores/src/metaTagConfig.ts":{"1":"b://Vf///5/7f/+/////////////8HAAAA","8":"b://Vf///5/7f/+/////////////8HAAAA","34":"b://Vf///5/7f/+/////////////8HAAAA"},"packages/frontend/@n8n/stores/src/notifications.store.ts":{"29":"b:1/Vf/Qv4/xf/I+hP7///9v////8HAAAA","33":[90,91,129]},"packages/frontend/@n8n/stores/src/rbac.store.ts":{"9":"b://Vf///5/7f/+/////////////8HAAAA","80":"b://Vf///5/7f/+//j//////z///8HAAAA","116":"b://Vf///5/7f/+/////////////8HAAAA"},"packages/frontend/@n8n/stores/src/roles.store.ts":{"23":[24,32,35,108,55,60,122,125,72,161],"24":[24,32,35,108,55,60,122,125,72,161],"35":"b://Vf///5/7f/+//j//////z///8HAAAA","51":[24,32,35,108,55,60,125,72,161],"52":[24,32,35,108,55,60,125,72,161],"56":[122],"57":[122],"60":[122],"62":[122],"70":[122],"71":[122],"74":[24,32,35,108,55,60,125,72,161],"76":[24,32,35,108,55,60,125,72,161],"84":[97,33,64,125],"85":[97,33,64,125],"88":[124,126,161],"89":[124,126,161],"92":"b://Vf///5/7f/+//j//////z///8HAAAA"},"packages/frontend/@n8n/stores/src/settings.store.ts":{"44":"b://Vf///5/7f/+/////////////8HAAAA","73":"b:x/Vf///5/xf/++9D+P+/8Pz///8HAAAA","75":"b:x/Vf///5/xf/++9D+P+/8Pz///8HAAAA","77":"b:x/Vf///5/xf/++9D+P+/8Pz///8HAAAA","81":"b:x/Vf///5/xf/++9D+P+/8Pz///8HAAAA","83":"b:x/Vf///5/xf/++9D+P+/8Pz///8HAAAA","85":"b:x/Vf///5/xf/++9D+P+/8Pz///8HAAAA","90":"b://Vf///5/7f/+/////////////8HAAAA","92":"b:x/Vf///5/xf/++9D+P+/8Pz///8HAAAA","94":"b:x/Vf///5/xf/++9D+P+/8Pz///8HAAAA","96":"b://Vf///5/7f/+/////////////8HAAAA","98":"b://Vf///5/7f/+/////////////8HAAAA","100":"b://Vf///5/7f/++/D//////z///8HAAAA","102":[77],"104":"b://Vf///5/7f/+/////////////8HAAAA","106":"b:x/Vf/PL5jwf+I4hD6O8/8Pz///8HAAAA","112":[77],"114":[77],"116":"b://Vf///5/7f/+/////////////8HAAAA","120":"b:guVBlAJ4iQSaAYBC4AUe8Mz2fpIAAAAA","122":"b://Vf///5/7f/++/v//////////8HAAAA","126":[15,87,16,17,18,20],"130":"b://Vf///5/7f/+//v//////////8HAAAA","132":"b:x/Vf///5/xf/++9D+P+/8Pz///8HAAAA","134":"b://Vf///5/7f/+//v//////////8HAAAA","136":"b://Vf///5/7f/+/////////////8HAAAA","138":"b://Vf///5/7f/+/////////////8HAAAA","156":"b:/vVD389Y/RfbG+BKz/2+sNz+/5sGAAAA","160":[3,4,5,96,98,32],"166":"b://Vf///5/7f/++/D//////z///8HAAAA","172":"b:x/Vf//75/wf+O+hO6P8/8P7///8HAAAA","176":"b://Vf///5/7f/+/////////////8HAAAA","180":"b:wnBAAAAAOAECAAAAAIVcgUzT/P0HAAAA","186":[95,121,86],"188":"b:AAEBGw4A8BCBGWAACBiAMAAAAAgGAAAA","190":"b://Vf///5/7f/+//j//////z///8HAAAA","192":"b://Vf///5/7f/+/////////7///8HAAAA","196":"b:xvVD3YtYjRfaAYBKxPU+sNz+/5MCAAAA","212":[7],"216":[32,108,122],"220":"b:x/Vf/fL5/wf+I4hD6P8/8Pz///8HAAAA","224":"b:x/Vf/PL5nwf+I4hD6O8/8Pz///8HAAAA","230":"b://Vf///5/7f/+/////////////8HAAAA","232":"b:x/Vf///5/xf/++9D+P+/8Pz///8HAAAA","234":[162],"236":"b:x/Vf///5/xf/++9D+P+/8Pz///8HAAAA","238":"b://Vf///5/7f/+/////////////8HAAAA","240":"b:3/Vf/f/5/7f/++/P//////////8HAAAA","242":[118],"248":"b://Vf///5/7f/++/D//////z///8HAAAA","249":"b:x/Vf/LL5jwf+I4hC6O8/8Pz///8HAAAA","251":[123],"253":[56,127],"257":[77],"259":"b:x/Vf///5/xf/++9D+P+/8Pz///8HAAAA","263":"b://Vf///5/7f/+/////////////8HAAAA","265":"b:x/Vf/PL5nwf+I4hD6O8/8Pz///8HAAAA","269":"b://Vf///5/7f/+/////////////8HAAAA","301":"b://Vf///5/7f/+/////////////8HAAAA","309":"b://Vf///5/7f/+/////////////8HAAAA","313":"b://Vf///5/7f/+/////////////8HAAAA","317":"b://Vf///5/7f/+/////////////8HAAAA","321":"b://Vf///5/7f/+/////////////8HAAAA","325":"b://Vf///5/7f/+/////////////8HAAAA","376":"b://Vf///5/7f/+/////////////8HAAAA","400":[162],"401":[162],"408":[56,127],"417":"b://Vf///5/7f/+//j//////z///8HAAAA"},"packages/frontend/@n8n/stores/src/useAgentRequestStore.ts":{"18":"b://Vf///5/7f/+/////////////8HAAAA","64":[0,2,39,12,57,65,68,139,140,142],"78":"b:xZAAQIAACAJaAIAAAEUAAAD4LgAAAAAA"},"packages/frontend/@n8n/stores/src/useRootStore.ts":{"39":"b://Vf///5/7f/+/////////////8HAAAA","71":"b://Vf///5/7f/+/////////////8HAAAA","73":"b:AAFABAAICAAQAABIgAAAAAAIGgACAAAA","75":"b:AAFABAAICAAQAABIgAAAAAAIGgACAAAA","79":[6],"83":"b:AAFABAAICAAQAABIgAAAAAAIGgACAAAA","85":"b:xgFIBAAICAIQAABIgAAAAAIIGgACAAAA","93":"b:AAFABAAICAAQAABIgAAAAAAIGgACAAAA","95":"b:AAFABAAICAAQAABIgAAAAAAIGgACAAAA","99":"b://Vf///5/7f/+/////////////8HAAAA","101":"b:x5BK4IKBDwZ6AIACgGcgAA7+r+EEAAAA","103":"b://Vf///5/7f/+/////////////8HAAAA","105":[15,87,1,7,99,137,6,145,148],"109":"b://Vf///5/7f/+//j//////z///8HAAAA","111":"b://Vf///5/7f/+//v//////////8HAAAA","113":[16,8,99,148,73],"117":"b://Vf///5/7f/+/////////////8HAAAA","119":[56,127],"121":[56,127],"123":[56,127],"125":"b://Vf///5/7f/+/////////////8HAAAA","136":"b://Vf///5/7f/+/////////////8HAAAA","141":"b://Vf///5/7f/+/////////////8HAAAA","146":"b://Vf///5/7f/+/////////////8HAAAA","151":"b://Vf///5/7f/+/////////////8HAAAA","155":"b://Vf///5/7f/+/////////////8HAAAA","159":"b://Vf///5/7f/+/////////////8HAAAA","163":"b://Vf///5/7f/+/////////////8HAAAA","167":"b://Vf///5/7f/+/////////////8HAAAA","171":"b://Vf///5/7f/+/////////////8HAAAA","175":"b://Vf///5/7f/+/////////////8HAAAA","179":"b://Vf///5/7f/+/////////////8HAAAA","183":"b://Vf///5/7f/+/////////////8HAAAA","188":"b://Vf///5/7f/+/////////////8HAAAA","192":"b://Vf///5/7f/+/////////////8HAAAA","196":"b://Vf///5/7f/+/////////////8HAAAA","200":"b://Vf///5/7f/+/////////////8HAAAA","204":"b://Vf///5/7f/+/////////////8HAAAA","212":"b://Vf///5/7f/+/////////////8HAAAA","216":"b://Vf///5/7f/+/////////////8HAAAA","220":"b://Vf///5/7f/+/////////////8HAAAA"},"packages/frontend/@n8n/stores/src/users.store.ts":{"36":"b://Vf///5/7f/+//z//////z///8HAAAA","37":"b://Vf///5/7f/+/////////////8HAAAA","38":"b://Vf///5/7f/+//z//////z///8HAAAA","40":[33,55,72],"45":"b://Vf///5/7f/+/////////////8HAAAA","70":"b://Vf///5/7f/+//j//////z///8HAAAA","80":"b://Vf///5/7f/+//j//////z///8HAAAA","91":"b:x/Vf///5/xf/++9D+P+/8Pz///8HAAAA","93":"b://Vf///5/7f/+/////////////8HAAAA","97":"b:AEFBlABoiAAKAAAAgAAAsMxQfhIAAAAA","101":"b://Vf///5/7f/++/v//////////8HAAAA","103":[33,55,72],"105":"b:guVBlAJ4iQSaAQBC5AVO8Mz2flICAAAA","107":[95,121,86],"109":[95,121,86],"113":[1,7,57,127,6,13,14],"117":[75],"121":[57,127,6,13,14],"127":[1,7,8,10,113,65,68,13,14],"138":[122],"144":"b://Vf///5/7f/+//z//////z///8HAAAA","145":"b://Vf///5/7f/+//z//////z///8HAAAA","167":"b://Vf///5/7f/+//z//////z///8HAAAA","180":"b://Vf///5/7f/+//z//////z///8HAAAA","189":"b://Vf///5/7f/+//z//////z///8HAAAA","203":[55,86],"207":[122],"226":[76,55,86],"235":"b://Vf///5/7f/+/////////////8HAAAA","239":"b://Vf///5/7f/+/////////////8HAAAA","243":[55,86],"315":[121,86],"351":[122],"356":"b:x/Vf///5/xf/++9D+P+/9Pz///8HAAAA","408":"b:x/Vf//75/wf+O+hO6P8/8P7///8HAAAA","415":[86],"419":[86],"427":[86],"434":[86],"451":[122],"469":[122],"478":"b://Vf///5/7f/+//j//////z///8HAAAA"},"packages/frontend/@n8n/stores/src/versions.store.ts":{"32":"b://Vf///5/7f/+/////////////8HAAAA","71":"b://Vf///5/7f/+//j//////z///8HAAAA","81":"b:x/Vf///5/xf/++9D+P+/8Pz///8HAAAA","112":"b://Vf///5/7f/+//j//////z///8HAAAA","116":[24],"132":"b:x/Vf///5/xf/++9D+P+/8Pz///8HAAAA","142":"b://Vf///5/7f/+//j//////z///8HAAAA","157":"b://Vf///5/7f/+//j//////z///8HAAAA","158":"b://Vf///5/7f/+//j//////z///8HAAAA","159":"b://Vf///5/7f/+//j//////z///8HAAAA","209":"b://Vf///5/7f/+//j//////z///8HAAAA","251":"b://Vf///5/7f/+//j//////z///8HAAAA","255":"b://Vf///5/7f/+//j//////z///8HAAAA","278":[24]},"packages/frontend/editor-ui/src/app/App.vue":{"35":"b://Vf///5/7f/+/////////////8HAAAA","49":"b://Vf///5/7f/+/////////////8HAAAA","69":"b://Vf///5/7f/+/////////////8HAAAA","74":"b:huFBAAIAiABYAIBCgAACAAzyHwIAAAAA","81":"b://Vf///5/7f/+/////////////8HAAAA","87":"b://Vf///5/7f/+/////////////8HAAAA","93":"b://Vf///5/7f/++/v//////////8HAAAA","94":"b://Vf///5/7f/++/v//////////8HAAAA","101":"b://Vf///5/7f/+/////////////8HAAAA","113":"b://Vf///5/7f/++/v//////////8HAAAA","120":"b://Vf///5/7f/+/////////////8HAAAA","125":"b://Vf///5/7f/+/////////////8HAAAA","133":"b://Vf///5/7f/+/////////////8HAAAA","136":"b://Vf///5/7f/+/////////////8HAAAA"},"packages/frontend/editor-ui/src/app/api/favorites.ts":{"16":"b://Vf///5/7f/+//j//////z///8HAAAA"},"packages/frontend/editor-ui/src/app/api/workflows.ts":{"26":"b:wuVX9AJ4iQSaI4hO6C8/8O/yfl4HAAAA","39":"b:wuVX9AJ4iQSaI4hO6C8/8O/yfl4HAAAA","65":"b:R3AIKPCB5wP0AwABgMgAsBwth+sGAAAA","69":"b:wuVX9AJ4iQSaIYBC6C8/8Ozyfl4DAAAA","73":[56,127,161],"87":[118],"98":"b:AAAACwwA8ACAGWAAABgAMAAAAAsGAAAA","114":"b:x/Vf//75/wf+O+hD6P8/8Pz///8HAAAA","136":"b:x5AI4LKBDwZ6AIACgGcgAA7+r+EAAAAA","149":"b:R3AIKACAhwP0AwAAgMAAMBwth+kGAAAA","160":"b:x/Bf6PKBjwf+A4ADgO8g8Nz/r/8GAAAA"},"packages/frontend/editor-ui/src/app/components/BottomMenu.vue":{"23":"b://Vf///5/xf/++9D/f+/8Pz///8HAAAA","33":[75,134],"78":[75,55,86,134],"82":[55,86],"84":"b://Vf///5/xf/++9D/f+/8Pz///8HAAAA","104":[75,134],"106":[75,134],"118":[75],"129":"b:x/Vf///5/xf/++9D+P+/8Pz///8HAAAA","134":[75,134],"146":[55,86],"148":[55,86],"168":"b://Vf///5/xf/++9D/f+/8Pz///8HAAAA","173":[55,86]},"packages/frontend/editor-ui/src/app/components/BreakpointsObserver.vue":{"30":"b:x/Vf/PL5nwf+I4hD6O8/8Pz///8HAAAA","37":"b:x/Vf/PL5nwf+I4hD6O8/8Pz///8HAAAA","53":"b:x/Vf/PL5nwf+I4hD6O8/8Pz///8HAAAA","72":[52,69],"76":[52,69],"84":"b:x/Vf/PL5nwf+I4hD6O8/8Pz///8HAAAA","88":[36,39,40,52,55,136,71,155],"91":"b:x/Vf/PL5nwf+I4hD6O8/8Pz///8HAAAA"},"packages/frontend/editor-ui/src/app/components/ConnectionTracker.vue":{"26":"b:x/Vf/PL5nwf+I4hD6O8/+Pz///8HAAAA"},"packages/frontend/editor-ui/src/app/components/Draggable.vue":{"40":"b:xvVL3aJY/QfaAYBKwP0+sNz+/5MGAAAA","44":"b:AAAAAAAA8ABAAQAAAAgAAAAgBQACAAAA","78":[52,141],"105":[52,141],"115":[52,107,53,54,55,141,161],"127":[52,107,53,54,55,141,161],"135":"b:xvVKzYJY+QbaAQACgN0egNz+b5EGAAAA","149":"b:xvVL3aJY/QfaAYBKwP0+sNz+/5MGAAAA"},"packages/frontend/editor-ui/src/app/components/DraggableTarget.vue":{"15":"b:xvVH3MJYjbfaAYBKxOU+sdz+/5MCAAAA","31":"b:xvVH3MJYjTbaAYBCxOU+sdz+/xMCAAAA","32":[141],"34":"b:xvVH3MJYjbfaAYBKxOU+sdz+/5MCAAAA","37":"b:xvVH3MJYjbfaAYBKxOU+sdz+/5MCAAAA","39":[141],"49":"b:wsFFzIIQjDTaAQBKxIEMAMz8vgIAAAAA","53":"b:wsFBzIIQjDTaAQBKxIEMAMz8vgIAAAAA","57":"b:AkFASAIQiDBIAABCxAEAAID4OgIAAAAA","64":[141],"79":"b:xvVH3MJYjbfaAYBKxOU+sdz+/5MCAAAA"},"packages/frontend/editor-ui/src/app/components/DynamicModalLoader.vue":{"9":"b://Vf///5/7f/+/////////////8HAAAA","17":"b://Vf///5/7f/+//j//////z///8HAAAA","23":"b://Vf///5/7f/+/////////////8HAAAA","29":"b://Vf///5/7f/+//j//////z///8HAAAA","44":"b://Vf///5/7f/+/////////////8HAAAA","46":"b://Vf///5/7f/+//j//////z///8HAAAA","54":"b://Vf///5/7f/+/////////////8HAAAA","58":"b:9/Vf///5/7f3+//j//////z///8HAAAA","60":[5,96,97,98,34,35]},"packages/frontend/editor-ui/src/app/components/FocusSidebar.vue":{"32":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","51":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","52":[116,132],"53":"b:x/Vf/PL5nwf+I4hK4O8/8P7///8HAAAA","55":[116,132],"60":[116,132],"65":[116,132],"72":[116,132],"83":[116,132],"87":"b:x/Vf/PL5nwf+I4hK4O8/8P7///8HAAAA","95":[116,132],"111":[116,132],"115":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","119":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","120":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","131":"b:x/Vf/PL5nwf+I4hK4O8/8P7///8HAAAA","159":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","163":"b:QgAAAJABgACwAAAAAAAAgAgDAOgEAAAA","166":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","188":[116,132]},"packages/frontend/editor-ui/src/app/components/FreeAiCreditsCallout.vue":{"21":"b:1vVD3MJYjRfbA4BKyuU+sNz+/5MCAAAA","55":"b:1vVD3MJYjRfbA4BKyuU+sNz+/5MCAAAA","57":"b:1vVD3MJYjRfbA4BKyuU+sNz+/5MCAAAA","63":"b:1vVD3MJYjRfbA4BKyuU+sNz+/5MCAAAA"},"packages/frontend/editor-ui/src/app/components/KeyboardShortcutTooltip.vue":{"21":"b://Vf///5/xf/++9P/f+/8P////8HAAAA"},"packages/frontend/editor-ui/src/app/components/MainHeader/ActionsDropdownMenu.vue":{"44":"b:x/Vf/PL5nwf+I4hD6O8/8Pz///8HAAAA","74":"b:x/Vf/PL5nwf+I4hD6O8/8Pz///8HAAAA","82":"b:x/Vf/PL5nwf+I4hD6O8/8Pz///8HAAAA","84":"b:x/Vf/PL5nwf+I4hD6O8/8Pz///8HAAAA","88":"b:QMACQAAAEACAAIAAACI5AAAADgABAAAA","92":"b:QMACQAAAEACAAIAAACI5AAAADgABAAAA","114":"b:x/Vf/PL5nwf+I4hD6O8/8Pz///8HAAAA","239":"b:QMACQAAAEAGAAIAAACI5UEAADggBAAAA","413":"b:x/Vf/PL5nwf+I4hD6O8/8Pz///8HAAAA","421":"b:QMACQAAAEACAAIAAACI5AAAADgABAAAA"},"packages/frontend/editor-ui/src/app/components/MainHeader/MainHeader.vue":{"62":"b:x/Vf/AL4nwf+I4hD6O8/8Pz///8HAAAA","70":"b:x/Vf/PL5nwf+I4hD6O8/8Pz///8HAAAA","71":"b:x/Vf/PL5nwf+I4hD6O8/8Pz///8HAAAA","76":"b:x/Vf/PL5nwf+I4hD6O8/8Pz///8HAAAA","77":"b:x/Vf/PL5nwf+I4hD6O8/8Pz///8HAAAA","78":"b:x/Vf/PL5nwf+I4hD6O8/8Pz///8HAAAA","79":"b:x/Vf/PL5nwf+I4hD6O8/8Pz///8HAAAA","80":"b:x/Vf/PL5nwf+I4hD6O8/8Pz///8HAAAA","82":"b:x/Vf/PL5nwf+I4hD6O8/8Pz///8HAAAA","85":[52],"88":"b:x/Vf/PL5nwf+I4hD6O8/8Pz///8HAAAA","96":"b:x/Vf/PL5nwf+I4hD6O8/8Pz///8HAAAA","106":"b:xvVX3AJYnQb6A4hCwO8+8Mz///8GAAAA","110":"b:x/Vf/PL5nwf+I4hD6O8/8Pz///8HAAAA","114":[36,39,40,52,136,71,155],"118":"b:x/Vf/PL5nwf+I4hD6O8/8Pz///8HAAAA","123":"b:x/Vf/PL5nwf+I4hD6O8/8Pz///8HAAAA","130":"b:x/Vf/PL5nwf+I4hD6O8/8Pz///8HAAAA","140":"b:x/Vf/AL4nwf+I4hC6O8/8Pz///8HAAAA","160":[1,52,131,137,68,69,6,157,158,159],"181":[1,52,131,158,159],"212":[1,52,131,137,68,69,6,157,158,159],"238":[52],"256":[52],"260":[52]},"packages/frontend/editor-ui/src/app/components/MainHeader/TabBar.vue":{"22":[1,52,131,137,68,69,6,157,158,159],"24":"b:x/Vf/AL4nwf+I4hD6O8/8Pz///8HAAAA"},"packages/frontend/editor-ui/src/app/components/MainHeader/WorkflowDetails.vue":{"47":"b:x/Vf/PL5nwf+I4hD6O8/8Pz///8HAAAA","93":[52,154],"99":[52],"102":"b:x/Vf/PL5nwf+I4hD6O8/8Pz///8HAAAA","106":"b:x/Vf/PL5nwf+I4hD6O8/8Pz///8HAAAA","110":"b:x/Vf/PL5nwf+I4hD6O8/8Pz///8HAAAA","115":"b:x/Vf/PL5nwf+I4hD6O8/8Pz///8HAAAA","120":[52,116,71,154,72,155,160,162],"122":"b:x/Vf/PL5nwf+I4hD6O8/8Pz///8HAAAA","134":"b:x/Vf/PL5nwf+I4hD6O8/8Pz///8HAAAA","135":[52,55,71,155],"142":[52,154],"150":[52,154],"157":[52,154],"186":[52],"191":[52],"197":"b:QMACQAIAEACAAIAAACo5QAAADgADAAAA","223":[52,155],"267":[52],"288":[52],"332":[52,155],"342":[52,155],"393":[52],"395":[52],"401":"b:QMACQAAAEACAAIAAACI5AAAADgABAAAA","407":"b:x/Vf/PL5nwf+I4hD6O8/8Pz///8HAAAA","416":[36,39,40,52,55,136,71,155],"424":"b:x/Vf/PL5nwf+I4hD6O8/8Pz///8HAAAA","435":"b:x/Vf/PL5nwf+I4hD6O8/8Pz///8HAAAA","441":"b:x/Vf/PL5nwf2I4hD6O8/8Pz///8HAAAA","500":[155],"507":"b:x/Vf/PL5nwf+I4hD6O8/8Pz///8HAAAA"},"packages/frontend/editor-ui/src/app/components/MainHeader/WorkflowHeaderDraftPublishActions.vue":{"77":"b:x/Vf/PL5nwf+I4hD6O8/8Pz///8HAAAA","83":"b:x/Vf/PL5nwf+I4hD6O8/8Pz///8HAAAA","84":"b:x/Vf/PL5nwf+I4hD6O8/8Pz///8HAAAA","101":[52],"105":"b:x/Vf/PL5nwf+I4hD6O8/8Pz///8HAAAA","109":"b:x/Vf/PL5nwf+I4hD6O8/8Pz///8HAAAA","120":"b:x/Vf/PL5nwf+I4hD6O8/8Pz///8HAAAA","121":[52,55,71,155],"131":"b:x/Vf/PL5nwf+I4hD6O8/8Pz///8HAAAA","133":[52],"142":"b:QMACQAAAEACAAIAAACI5AAAADgABAAAA","144":"b:x/Vf/PL5nwf+I4hD6O8/8Pz///8HAAAA","148":"b:x/Vf/PL5nwf+I4hD6O8/8Pz///8HAAAA","152":"b:x/Vf6PKJnwf+A4ADgO8i8Nz/7/8HAAAA","156":"b:xuVNJAAIHAOQAghAgIQCgFT4WhgHAAAA","169":[52],"170":[52],"171":"b:x/Vf/PL5nwf+I4hD6O8/8Pz///8HAAAA","173":"b:x/Vf/PL5nwf+I4hD6O8/8Pz///8HAAAA","204":"b:x/Bf6PKBnwf+A4ADgO8g8Nz/r/8GAAAA","205":"b:x/Bf6PKBnwf+A4ADgO8g8Nz/r/8GAAAA","206":"b:x/Vf/PL5nwf+I4hD6O8/8Pz///8HAAAA","207":[29,50,52,56,127,155,156,161],"209":"b:wuVX9AJ4mQSaI4hC6C8/8Ozyfl4HAAAA","215":[52,127,161],"240":[50,52,127,155,156,161],"245":[50,52,127,155,156,161],"252":[52],"258":[52],"269":[52],"277":[52],"281":[50,52,127,155,156,161],"309":"b:x/Vf/PL5nwf+I4hD6O8/8Pz///8HAAAA","455":"b:x/Vf/PL5nwf+I4hD6O8/8Pz///8HAAAA","460":"b:x/Vf/PL5nwf+I4hD6O8/8Pz///8HAAAA","469":"b:x/Vf/PL5nwf+I4hD6O8/8Pz///8HAAAA","471":[52],"478":[52],"483":"b:x/Vf/PL5nwf+449D+O8/8Pz///8HAAAA","488":"b:x/Vf/PL5nwf+I4hD6O8/8Pz///8HAAAA","490":"b:x/Vf/PL5nwf+I4hD6O8/8Pz///8HAAAA","526":"b:x/Vf/PL5nwf+I4hD6O8/8Pz///8HAAAA","531":"b:x/Vf/PL5nwf+I4hD6O8/8Pz///8HAAAA","534":[52],"553":[52],"588":[52],"598":[52],"618":[52],"643":[52],"644":[52],"649":[52],"653":[52],"658":[52],"664":"b:x/Vf/PL5nwf+I4hD6O8/8Pz///8HAAAA","669":[36,39,40,52,55,136,71,155],"677":"b:x/Vf/PL5nwf+I4hD6O8/8Pz///8HAAAA","721":"b:x/Vf/PL5nwf+I4hD6O8/8Pz///8HAAAA","732":"b:x/Vf/PL5nwf2I4hD6O8/8Pz///8HAAAA","768":"b:x/Vf/PL5nwf+I4hD6O8/8Pz///8HAAAA"},"packages/frontend/editor-ui/src/app/components/MainSidebar.vue":{"36":"b:x/Vf///5/xf/++9D+P+/8Pz///8HAAAA","83":"b:x/Vf///5/xf/++9D+P+/8Pz///8HAAAA","91":"b:x/Vf///5/xf/++9D+P+/8Pz///8HAAAA","93":"b:x/Vf///5/xf/++9D+P+/8Pz///8HAAAA","216":"b:x/Vf///5/xf/++9D+P+/8Pz///8HAAAA","217":"b:x/Vf///5/xf/++9D+P+/8Pz///8HAAAA","220":"b:x/Vf///5/xf/++9D+P+/8Pz///8HAAAA","232":[75,77,35,54,55,86,72],"233":[75,77,35,54,55,86,72],"238":"b:x/Vf///5/xf/++9D+P+/8Pz///8HAAAA","242":"b:x/Vf///5/xf/++9D+P+/8Pz///8HAAAA","247":"b:x/Vf///5/xf/++9D+P+/8Pz///8HAAAA","258":"b:AAAAAAAAgACAA0AAAAgAMAABAAgGAAAA","267":[134],"274":[161],"277":[161],"289":[75,55,86,134],"332":[55,86],"340":"b:x/Vf///5/xf/++9D+P+/8Pz///8HAAAA","373":"b:x/Vf///5/xf/++9D+P+/8Pz///8HAAAA"},"packages/frontend/editor-ui/src/app/components/MainSidebarHeader.vue":{"37":"b:x/Vf/f/5/xf/a8ND+P+/8Pz///8HAAAA","41":[75,77,35,54,55,86,72],"45":[161],"69":"b:OAAAAgkAwAAAKUAABQAAAAAAAAAAAAAA","114":"b:x/Vf///5/xf/++9D+P+/8Pz///8HAAAA","123":"b:x/Vf///5/xf/++9D+P+/8Pz///8HAAAA","132":"b:x/Vf///5/xf/++9D+P+/8Pz///8HAAAA","137":[85],"144":[25,33,55,72],"168":"b://Vf///5/xf/++9D/f+/8Pz///8HAAAA"},"packages/frontend/editor-ui/src/app/components/MainSidebarSourceControl.vue":{"27":[85],"37":[85],"41":"b:x/Vf///5/xf/++9D+P+/8Pz///8HAAAA","47":[85],"53":[85],"62":[85],"84":"b:x/Vf///5/xf/++9D+P+/8Pz///8HAAAA","111":[85],"144":[85],"170":[85]},"packages/frontend/editor-ui/src/app/components/ModalDrawer.vue":{"7":"b://Vf///5/7f/+/////////////8HAAAA","40":"b:QOECVAQAIBCHA4IAADq5IG5AD0kHAAAA","61":"b://Vf///5/7f/+/////////////8HAAAA","75":"b://Vf///5/7f/+/////////////8HAAAA","89":"b://Vf///5/7f/+/////////////8HAAAA","91":[24]},"packages/frontend/editor-ui/src/app/components/ModalRoot.vue":{"20":"b://Vf///5/7f/+/////////////8HAAAA","21":"b:9/Vf///5/7f3+/////////////8HAAAA"},"packages/frontend/editor-ui/src/app/components/Modals.vue":{"151":"b://Vf///5/7f/+/////////////8HAAAA","153":"b:kkEBEAIAgBABAgAASgACIAAAEAAAAAAA","182":[134],"190":[16,28,8,33,99,55,60,64,125],"194":[52],"200":[134,160],"208":[154],"216":"b://Vf///5/7f/+/////////////8HAAAA","220":"b://Vf///5/7f/+/////////////8HAAAA","226":[56,127],"244":[122],"250":[50,127,155,156,161],"254":[86],"258":[86],"262":[124,126,161],"268":[118],"276":[118],"287":[61,120,63],"341":[60],"347":[60],"373":[161],"402":[83,73],"411":[54],"420":[15],"432":[53],"438":[53,54,55],"468":[152],"493":[119],"516":[50,127,155,156,161]},"packages/frontend/editor-ui/src/app/components/NodeExecuteButton.vue":{"15":"b:xvVD3IJYjQbaAYBCwOU+sNz+/xMCAAAA","59":"b:xvVD3IJYjQbaAYBCwOU+sNz+/xMCAAAA","74":"b:hIAAQAAACAJIAIAAAEUAAACoLgAAAAAA","81":"b:xvVD3IJYjQbaAYBCwOU+sNz+/xMCAAAA","93":"b:xvVD3IJYjQbaAYBCwOU+sNz+/xMCAAAA","99":"b:xvVD3IJYjQbaAYBCwOU+sNz+/xMCAAAA","105":"b:xvVD3IJYjQbaAYBCwOU+sNz+/xMCAAAA","116":"b:xuVD3AJYiQbKAYBCwMUesNz6fhIAAAAA","120":"b:hMBAQAAACAJIAIAAgEUAAACoLgAAAAAA","132":"b:hIAAQAAACAJIAIAAAEUAAACoLgAAAAAA","136":"b:xvVD3IJYjQbaAYBCwOU+sNz+/xMCAAAA","148":"b:xvVD3IJYjQbaAYBCwOU+sNz+/xMCAAAA"},"packages/frontend/editor-ui/src/app/components/NodeIcon.vue":{"43":"b:xvVP3PJ5jQfaA4hK5OU+8N7+/9MGAAAA","44":"b:xvVP3PJ5jQfaA4hK5OU+8N7+/9MGAAAA","48":"b:x/VP/PL5jwf+A4hO5Oc/8N////sHAAAA","50":"b:x/VP/PL5jwf+A4hO5Oc/8N////sHAAAA","51":"b:x/VP/PL5jwf+A4hO5Oc/8N////sHAAAA","56":"b:x/VP/PL5jwf+A4hO5Oc/8N////sHAAAA","61":"b:x/VP/PL5jwf+A4hO5Oc/8N////sHAAAA","66":"b:x/VP/PL5jwf+A4hO5Oc/8N////sHAAAA","68":"b:x/VP/PL5jwf+A4hO5Oc/8N////sHAAAA"},"packages/frontend/editor-ui/src/app/components/NpsSurvey.vue":{"14":"b://Vf///5/7f/+/////////////8HAAAA","40":"b://Vf///5/7f/+/////////////8HAAAA","117":"b://Vf///5/7f/+/////////////8HAAAA","127":"b://Vf///5/7f/+/////////////8HAAAA","144":"b://Vf///5/7f/+/////////////8HAAAA","146":"b:9/Vf///5/7f3+/////////////8HAAAA"},"packages/frontend/editor-ui/src/app/components/TidyUpIcon.vue":{"2":"b:x/Vf/LL5nwf+I4hC6O8/8Pz///8HAAAA"},"packages/frontend/editor-ui/src/app/components/UpdatesPanel.vue":{"11":"b://Vf///5/7f/+/////////////8HAAAA","15":"b:9/Vf///5/7f3+/////////////8HAAAA","24":"b://Vf///5/7f/+/////////////8HAAAA","29":[24],"61":[24],"70":[24]},"packages/frontend/editor-ui/src/app/components/WorkflowProductionChecklist.vue":{"32":"b:x/Bf6PKBnwf+A4ADgO8g8Nz/r/8GAAAA","49":[29,50,52,56,127,155,156,161],"50":[29,50,52,56,127,155,156,161],"51":[29,50,52,56,127,155,156,161],"57":[52,127],"61":[29,50,52,56,127,155,156,161],"66":[29,50,52,56,127,155,156,161],"67":[29,50,52,56,127,155,156,161],"72":[29,50,52,56,127,155,156,161],"73":[29,50,52,56,127,155,156,161],"76":[29,50,52,56,127,155,156,161],"81":[52,127,155],"85":[50,52,127,155,156,161],"87":"b:x/Bf6PCBnwf+A4ADgO8g8Nz/r/8GAAAA","91":"b:x/Bf6PKBnwf8A4ADgG8g8Nx+o/8GAAAA","92":[33,52,55,72],"94":"b:x/Bf6PKBnwf+A4ADgO8g8Nz/r/8GAAAA","98":"b:x/Bf6PKBnwf+A4ADgO8g8Nz/r/8GAAAA","102":"b:x/Bf6PKBnwf8A4ADgG8g8Nx+o/8GAAAA","104":"b:x/Bf6PKBnwf+A4ADgO8g8Nz/r/8GAAAA","169":"b:x/Bf6PKBnwf2A4ADgO8g8Nz/r/8GAAAA","215":"b:x/Bf6PKBnwf+A4ADgO8g8Nz/r/8GAAAA","224":[52,127],"249":[52,127],"259":[52,127],"275":[52,127],"286":[52,127],"294":[52,127,155],"298":[52,127],"302":[52,127,155],"312":"b:x/Bf6PKBnwf+A4ADgO8g8Nz/r/8GAAAA","314":[50,52,127,155,156,161],"321":[52,127,155],"328":[52],"340":"b:x/Bf6PKBnwf+A4ADgO8g8Nz/r/8GAAAA","343":"b:x/Bf6PKBnwf+A4ADgO8g8Nz/r/8GAAAA"},"packages/frontend/editor-ui/src/app/components/app/AppBanners.vue":{"7":"b://Vf///5/7f/+/////////////8HAAAA","9":"b://Vf///5/7f/+/////////////8HAAAA","10":"b://Vf///5/7f/+/////////////8HAAAA"},"packages/frontend/editor-ui/src/app/components/app/AppChatPanel.vue":{"9":"b://Vf///5/7f/++/v//////////8HAAAA","24":"b://Vf///5/7f/++/v//////////8HAAAA","26":"b://Vf///5/7f/++/v//////////8HAAAA","34":"b://Vf///5/7f/++/v//////////8HAAAA","49":"b://Vf///5/7f/++/v//////////8HAAAA"},"packages/frontend/editor-ui/src/app/components/app/AppCommandBar.vue":{"11":"b://Vf///5/7f/+/////////////8HAAAA","24":"b://Vf///5/7f/+//z//////z///8HAAAA","26":"b://Vf///5/7f/+/////////////8HAAAA","28":"b://Vf///5/7f/+//z//////z///8HAAAA","34":[158,161],"37":"b://Vf///5/7f/+/////////////8HAAAA"},"packages/frontend/editor-ui/src/app/components/app/AppHeader.vue":{"6":"b:x/Vf/AL4nwf+I4hD6O8/8Pz///8HAAAA"},"packages/frontend/editor-ui/src/app/components/app/AppLayout.vue":{"5":"b://Vf///5/7f/+/////////////8HAAAA","6":"b:AAEBOw8A8BCBG2AgCBiANAABAAgGAAAA","9":"b:AAAAAAAAALEAIGCAAgBADwAAAAAAAAAA","12":"b:x/Vf/AL4nwf+I4hD6O8/8Pz///8HAAAA","14":[93,76,55,86],"15":[90,91,128,129],"16":[3,4,5,96,98,32],"18":[36,78,79,80,37,100,38,81,39,40,82],"30":"b://Vf///5/7f/++/v//////////8HAAAA","35":"b://Vf///5/7f/++/v//////////8HAAAA","37":"b://Vf///5/7f/+/////////////8HAAAA"},"packages/frontend/editor-ui/src/app/components/app/AppModals.vue":{"7":"b://Vf///5/7f/+/////////////8HAAAA"},"packages/frontend/editor-ui/src/app/components/transitions/SlideTransition.vue":{"3":"b:x/Vf/LL5nwf+I4hC6O8/8Pz///8HAAAA"},"packages/frontend/editor-ui/src/app/composables/useActivityDetection.ts":{"7":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","10":"b:AmAGCAAAiALEAYAAgEAAoEx+KPkCAAAA","16":"b:QmAGCAAAjAbMAYAAgEAAoMx+KPkCAAAA","17":"b:QmAGCAAAjAbMAYAAgEAAoMx+KPkCAAAA","22":"b:QgAACJABgAC0AwAAAAgAsAwDAOgGAAAA","23":"b:QgAACJABgAC0AwAAAAgAsAwDAOgGAAAA","29":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","30":"b:QmAGCAAAjAbMAYAAgEAAoMx+KPkCAAAA","40":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","47":"b:QgAAAJABgACwAwAAAAgAsAgDAOgGAAAA"},"packages/frontend/editor-ui/src/app/composables/useAiGateway.ts":{"14":"b://Vf///5/7f/++/D//////z///8HAAAA","18":"b:x/Vf///5/7f/++/D/v////z///8HAAAA","37":"b:AFFBFABYiABAAAAAwKAAsIhQfhAAAAAA","43":"b:xvVH3MJYjafaAYBKxOU+sdz+/5MCAAAA","46":"b:x/Vf/PL5nwf+I4hC7O8/8Pz///8HAAAA"},"packages/frontend/editor-ui/src/app/composables/useBackendStatus.ts":{"10":"b://Vf///5/7f/+/////////////8HAAAA","19":"b://Vf///5/7f/+/////////////8HAAAA","49":"b://Vf///5/7f/+/////////////8HAAAA","67":"b:AwAAAPkBmAAAAAAABgAAIAwDAEIAAAAA","72":"b://Vf///5/7f/+/////////////8HAAAA"},"packages/frontend/editor-ui/src/app/composables/useBasePageRedirectionHelper.ts":{"21":"b://Vf///5/7f/+/////////////8HAAAA"},"packages/frontend/editor-ui/src/app/composables/useBeforeUnload.ts":{"15":"b://Vf///5/7f/+/////////////8HAAAA","22":"b:AGAACAAAgQGEAQAAiAgAAAwUDgECAAAA","27":"b:AGAACAAAgQGEAQAAiAgAAAwUDgECAAAA","32":"b:AAAACAAAgAGEAQAAAAAAAAwEAAECAAAA","43":"b://Vf///5/7f/+/////////////8HAAAA","47":"b:x/Vf/PL5jwf+I4hP6O8/8P7///8HAAAA","51":"b:QgAAAJABgACwAwAAAAgAsAgDAOgGAAAA"},"packages/frontend/editor-ui/src/app/composables/useBrowserNotifications.ts":{"57":"b://Vf///5/7f/++/v//////////8HAAAA","66":[18,20],"80":[18,20]},"packages/frontend/editor-ui/src/app/composables/useBugReporting.ts":{"30":"b:x/Vf///5/xf/++9D+P+/8Pz///8HAAAA"},"packages/frontend/editor-ui/src/app/composables/useCanvasOperations.ts":{"206":"b:wuVH1AJ4iQSaA4hC4Cc/8Oz2flMHAAAA","216":"b:QsFH1AJYiQSKA4hCwCM9MMz2fhMFAAAA","248":"b:xvVD3IJYjQfaAYBKwOU+sMz+/5MCAAAA","284":"b:guVBlAJ4iQSaAQBC4AUG8MzyelICAAAA","288":"b:wuVH1AJ4iQSaA4hC4Cc/8Oz2flMHAAAA","298":[18,114],"311":[18,114],"320":[114],"328":[18,57,114,65,130,66,134,72],"336":[18,57,114,65,130,66,134,72],"345":[18,57,114,58,115,65,130,66,134,72],"367":[134],"376":[115,152],"404":[152],"416":[131,14,153],"477":[112,65,130,66,134,6,152],"556":[112,65,130,66,134,6,152],"620":[65,130,66,134,152],"625":[65,130,66,134,152],"632":[134,152],"637":[112,65,130,66,134,6,152],"656":[152],"677":[152],"853":"b:xvVD3IJYjQfaAYBKwOU+sMz+/5MCAAAA","863":"b:xvVD3IJYjQfaAYBKwOU+sMz+/5MCAAAA","886":"b:x/VL3IJ4jQfeA4BK4Oc/8Pz+//sDAAAA","902":"b:QgAAAAAAAAACAAAAAAAAgEgIAPAAAAAA","915":[134],"958":"b:wuVH1AJ4iQSaA4hC4Cc/8Oz2flMHAAAA","966":"b:wuVH1AJ4iQSaA4hC4Cc/8Oz2flMHAAAA","1050":"b:wuVH1AJ4iQSaA4hC4Cc/8Oz2flMHAAAA","1069":"b:wuVH1AJ4iQSaA4hC4Cc/8Oz2flMHAAAA","1083":[131,72],"1094":"b:wuVH1AJ4iQSaA4hC4Cc/8Oz2flMHAAAA","1124":"b:wuVH1AJ4iQSaA4hC4Cc/8Oz2flMHAAAA","1179":"b:QMACQAAAAACAAYAAACI5AEgADgABAAAA","1180":"b:QMACQAAAAACAAYAAACI5AEgADgABAAAA","1208":[134,152],"1217":[58,115,65],"1228":"b:wuVH1AJ4iQSaA4hC4Cc/8Oz2flMHAAAA","1301":"b:guVBlAJ4iQSaAQBC4AUO8Oz2elICAAAA","1311":[133],"1317":"b:guVBlAJ4iQSaAQBC4AUO8Mz2elICAAAA","1347":"b:wuVH1AJ4iQSaA4hC4Cc/8Oz2flMHAAAA","1390":"b:wuVH1AJ4iQSaA4hC4Cc/8Oz2flMHAAAA","1396":"b:wuVH1AJ4iQSaA4hC4Cc/8Oz2flMHAAAA","1401":"b:wuVH1AJ4iQSaA4hC4Cc/8Oz2flMHAAAA","1421":"b:guVBlAJ4iQSaAQBC4AUO8Oz2elICAAAA","1433":"b:wuVH1AJ4iQSaA4hC4Cc/8Oz2flMHAAAA","1559":"b:giUAAAAACACKAAAAAAAOAMxUaAAAAAAA","1572":"b:gmVBlAIQiASKAAACwAEOsMz2ehIAAAAA","1593":[58,115,65],"1649":"b:gmVBlAIQiASKAAACwAEOsMz2ehIAAAAA","1654":[1,7,8,10,113,13],"1681":"b:AgAAAAAACACKAAAAAAAMAMxUaAAAAAAA","1732":"b:wuVH1AJ4iQSaA4hC4Cc/8Oz2flMHAAAA","1741":[58,115,65],"1763":[58,115,65],"1800":[58,115,65],"1815":[115],"1820":[58,115,65],"1822":[58,115,65],"1828":[115],"1842":[115],"1854":[115],"1868":[115],"1875":[115],"1882":[58,115,65],"1889":[58,115,65],"1897":[115,65],"1906":[115,65],"1910":[58,115,65],"1922":[58,115,65],"1925":[58,115,65],"1928":[58,115,65],"2029":[115],"2076":[58,115,65],"2100":[58,115,65],"2147":"b:wuVH1AJYiQSOAYBCwCM/sMz2fhMBAAAA","2185":"b:wuVH1AJYiQSOAYBCwCM/sOz2fhMBAAAA","2237":"b:wuVH1AJYiQSOAYBCwCM/sMz2fhMBAAAA","2247":[134,152],"2262":[112,65,130,66,134,6,152],"2328":[18,112,58,115,65,130,66,134,6,152],"2363":[18,112,58,115,65,130,66,134,6,152],"2377":[134,152],"2391":[1,94,51,130,152],"2408":[1,130,152],"2435":[1,152],"2439":[94,51,130,152],"2443":"b:wuVH1AJYiQSOAYBCwCM/sOz2fhMBAAAA","2451":"b:wuVH1AJYiQSOAYBCwCM/sOz2fhMBAAAA","2457":"b:wuVH1AJYiQSOAYBCwCM/sMz2fhMBAAAA","2464":"b:wuVH1AJYiQSOAYBCwCM/sMz2fhMBAAAA","2468":"b:wuVH1AJYiQSOAYBCwCM/sMz2fhMBAAAA","2473":"b:wuVH1AJYiQSOAYBCwCM/sMz2fhMBAAAA","2552":"b:wuVH1AJYiQSOAYBCwCM/sMz2fhMBAAAA","2583":"b:wuVH1AJ4iQSaAYBC4Cc/8Oz2flIDAAAA","2610":"b:x/Vf/PL5nwf+I4hP6O8/8P////8HAAAA","2661":"b:R3AIKPCBhwP0AwANgMAAMB4th+kGAAAA","2698":"b:QMACQAAAAACAAYAAACI5AEgADgABAAAA","2715":"b:QMACQAAAAACAAYAAACI5AEgADgABAAAA","2733":"b:QMACQAAAAACAAYAAACI5AEgADgABAAAA","2750":"b:QMACQAAAAACAAYAAACI5AEgADgABAAAA","2753":"b:QMACQAAAAACAAYAAACI5AEgADgABAAAA","2796":"b:QMACQAAAAACAAYAAACI5AEgADgABAAAA","2918":"b:QMACQAAAAACAAYAAACI5AEgADgABAAAA","2952":"b:QMACQAAAAACAAYAAACI5AEgADgABAAAA","2967":"b:QMACQAAAAACAAYAAACI5AEgADgABAAAA","2974":"b:QMACQAAAAACAAYAAACI5AEgADgABAAAA","2984":"b:QMACQAAAAACAAYAAACI5AEgADgABAAAA","2985":"b:QMACQAAAAACAAYAAACI5AEgADgABAAAA","3110":"b:QMACQAAAAACAAYAAACI5AEgADgABAAAA","3140":[105,115,116,146,14,147,71,160],"3142":[146,147,160],"3145":[116,71,160],"3151":[116,71,160],"3160":[116,71,160],"3168":[116,71,160],"3169":[116,71,160],"3180":[160],"3203":[65,131,134,147,72,73],"3217":"b:QMACQAAAAACAAYAAACI5AEgADgABAAAA","3254":[65,131,134,147,72,73],"3266":[65,131,134,147,72,73],"3308":[73],"3313":[73],"3322":[65,131,134,147,72,73],"3343":[65,131,72,73],"3350":[65,131,72,73],"3351":[65,131,72,73],"3362":[131,134,147],"3369":[131,134,147],"3372":[65,72,73],"3384":[65,72,73],"3444":[1,7,2,57,129],"3459":[1,7,2,57,129],"3464":[83,73,162],"3561":"b:guVBlAJ4iQSaAQBC4AUO8Oz2elICAAAA","3587":"b:gmUAAABYAQQAAQBAQAAGAAAAMgAAAAAA","3637":"b:x/Vf/PL5jwf+I4hP6O8/8P////8HAAAA","3638":"b:x/Vf/PL5jwf+I4hP6O8/8P////8HAAAA","3641":[83,73,162],"3649":[83,73,162],"3656":[83,73,162]},"packages/frontend/editor-ui/src/app/composables/useCollectionOverhaul.ts":{"5":"b:1vVH3MJYjbfbA4BKzuU+sdz+/5MCAAAA","8":"b:1vVD3IJYjafaA4BKxuU+sdz+/5MCAAAA"},"packages/frontend/editor-ui/src/app/composables/useDataSchema.ts":{"31":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","35":"b:RNAEQIAABQVYAIACAGEoAJT+74EAAAAA","65":[48,109,132,6,141,143,144,14,70],"75":"b:RNAAQIAABQVYAIACAGEoAJT+74EAAAAA","100":"b:RNAEQIAABQVYAIACAGEoAJT+74EAAAAA","110":[115,139,150],"130":[115,139,150],"151":[115,139,150],"163":"b:RJBAQIAADQVYAIACgEUgAAT6r4EAAAAA","179":"b:xvVH3IJYjQfeAYBKwOU+sNz+/5MCAAAA","207":"b:xvVH3IJYjQfeAYBKwOU+sNz+/5MCAAAA","216":"b:AEAAAAAAAABAAAAIAAAAAJD6DQEAAAAA","221":"b:AEAAAAAAAABAAAAIAAAAAJD6DQEAAAAA","228":[146],"236":"b:RNAAQIAABQVYAIACAGEoAJT+74EAAAAA","241":[146],"365":"b:RNAAQIAABQVYAIACAGEoAJT+74EAAAAA","367":"b:QJAAQIAABQVYAIACAGEgAARO7oAAAAAA","378":[139,150],"385":"b:RNAAQIAABQVYAIACAGEoAJT+74EAAAAA","394":"b:RNAAQIAABQVYAIACAGEoAJT+74EAAAAA","396":"b:RNAAQIAABQVYAIACAGEoAJT+74EAAAAA","399":[141,146],"407":"b:RNAAQIAABQVYAIACAGEoAJT+74EAAAAA","468":"b:RNAAQIAABQVYAIACAGEoAJT+74EAAAAA","509":"b:QNAAQIAABQVYAIACAGEoAJT+74EAAAAA","514":"b:QNAAQIAABQVYAIACAGEgAJT+74EAAAAA"},"packages/frontend/editor-ui/src/app/composables/useDebugInfo.ts":{"72":"b://Vf///5/7f/+/////////////8HAAAA","78":"b:x/Vf///5/xf/++9D+P+/8Pz///8HAAAA","115":"b:x/Vf///5/xf/++9D+P+/8Pz///8HAAAA","126":"b:x/Vf///5/xf/++9D+P+/8Pz///8HAAAA","136":"b:x/Vf///5/xf/++9D+P+/8Pz///8HAAAA","147":"b:x/Vf///5/xf/++9D+P+/8Pz///8HAAAA","154":"b:x/Vf///5/xf/++9D+P+/8Pz///8HAAAA","158":[134],"166":[134],"169":[134],"174":[134],"184":"b:x/Vf///5/xf/++9D+P+/8Pz///8HAAAA","201":"b:x/Vf///5/xf/++9D+P+/8Pz///8HAAAA","219":[134],"233":"b:x/Vf///5/xf/++9D+P+/8Pz///8HAAAA","237":"b:x/Vf///5/xf/++9D+P+/8Pz///8HAAAA"},"packages/frontend/editor-ui/src/app/composables/useDocumentVisibility.ts":{"17":"b:GGAACQkA8bGEgWWAiBgAIAwUDgkGAAAA","28":"b://Vf///5/7f/++/v//////////8HAAAA","36":"b://Vf///5/7f/++/v//////////8HAAAA","40":"b:AIADAJABgACAAIAAAAAAAAABMAsAAAAA"},"packages/frontend/editor-ui/src/app/composables/useEditorContext.ts":{"27":"b:AIAXhIAAAAAKAIAAAAAQAEwAAoAAAAAA","30":"b:x/Vf/IL4nwf+I4hC6O8/8Pz///8HAAAA","51":"b://Vf///5/7f/+/////////////8HAAAA","52":"b:x/Vf/IL4nwf+I4hC6O8/8Pz///8HAAAA","59":"b:x/Vf/PL5nwf+I4hC6O8/8Pz///8HAAAA","60":"b:x/Vf/PL5nwf+I4hC6O8/8Pz///8HAAAA","61":"b:R/FH/LL5jwX+A4hC4Oc98Nz///sHAAAA","62":"b:x/Bf6PKBnwf+A4ADgO8g8N7/r/8GAAAA","65":"b:x/Bf6PKBnwf+A4ADgO8g8N7/r/8GAAAA"},"packages/frontend/editor-ui/src/app/composables/useErrorHandler.ts":{"26":"b://Vf///5/7f/++/v//////////8HAAAA"},"packages/frontend/editor-ui/src/app/composables/useExecutingNode.ts":{"19":"b://Vf///5/7f/+/////////////8HAAAA","25":"b:x5BK4LKBDwZ6AIACgGcgAA7+r+EAAAAA","37":"b:x5BK4LKBDwZ6AIACgGcgAA7+r+EAAAAA","45":"b:x/Vf/PL5nwf+I4hP6O8/8P////8HAAAA","51":"b:x/VP/PL5jwf+A4hC4Oc/8P////sHAAAA"},"packages/frontend/editor-ui/src/app/composables/useExposeCssVar.ts":{"7":"b://Vf///5/7f/+/////////////8HAAAA","11":"b://Vf///5/7f/+/////////////8HAAAA","12":"b://Vf///5/7f/+/////////////8HAAAA"},"packages/frontend/editor-ui/src/app/composables/useExternalHooks.ts":{"16":"b://Vf///5/7f/+//v//////////8HAAAA","38":"b://Vf///5/7f/+//v//////////8HAAAA"},"packages/frontend/editor-ui/src/app/composables/useFloatingUiOffsets.ts":{"16":"b://Vf///5/7f/+/////////////8HAAAA","21":"b://Vf///5/7f/+/////////////8HAAAA","25":"b:xvVD3AJYjQbaAYBKwOU+sMz+/xMCAAAA","26":"b://Vf///5/7f/+/////////////8HAAAA","29":"b://Vf///5/7f/+/////////////8HAAAA","30":"b://Vf///5/7f/+/////////////8HAAAA","39":"b://Vf///5/7f/+/////////////8HAAAA"},"packages/frontend/editor-ui/src/app/composables/useFreeAiCredits.ts":{"35":"b:/vVD3MNYjRfbA4BKz+U+sNz+/5MCAAAA","37":[3,4,5,96,98,32],"53":"b:/vVD3MNYjRfbA4BKz+U+sNz+/5MCAAAA"},"packages/frontend/editor-ui/src/app/composables/useGlobalEntityCreation.ts":{"28":"b:AEEEMQlI+RMeAQBAqPoVsFzTX28HAAAA","31":"b:AEEEMQlI+RMeAQBAqPoVsFzTX28HAAAA","76":"b:x/Vf///5/xf/++9D+P+/8Pz///8HAAAA","79":"b:x/Vf///5/xf/++9D+P+/8Pz///8HAAAA","83":"b:x/Vf///5/xf/++9D+P+/8Pz///8HAAAA","87":"b:x/Vf///5/xf/++9D+P+/8Pz///8HAAAA","94":"b:x/Vf///5/xf/++9D+P+/8Pz///8HAAAA","96":"b:x/Vf///5/xf/++9D+P+/8Pz///8HAAAA","103":"b:x/Vf///5/xf/++9D+P+/8Pz///8HAAAA","113":"b:x/Vf///5/xf/++9D+P+/8Pz///8HAAAA","117":[119,126,133,70],"146":"b:x/Vf///5/xf/++9D+P+/8Pz///8HAAAA","157":"b:AEEEMQlI+RMeAQBAqPoVsFzTX28HAAAA","169":"b:AEEEMQlI+RMeAQBAqPoVsFzTX28HAAAA","181":"b:x/Vf///5/xf/++9D+P+/8Pz///8HAAAA","203":"b:x/Vf///5/xf/++9D+P+/8Pz///8HAAAA","327":"b:AEEEMQlI+RMeAQBAqPoVsFzTX28HAAAA","361":"b:AEEEMQlI+RMeAQBAqPoVsFzTX28HAAAA","417":[24,35,108,55,125,72,161],"440":[24,35,108,55,125,71,72,161],"468":[25,33,55,72],"493":"b:x/Vf///5/xf/++9D+P+/8Pz///8HAAAA","494":"b:x/Vf///5/xf/++9D+P+/8Pz///8HAAAA","495":"b:x/Vf///5/xf/++9D+P+/8Pz///8HAAAA"},"packages/frontend/editor-ui/src/app/composables/useGlobalLinkActions.ts":{"14":"b:x/Vf/PL5jwf+I4hO6O8/8P7///8HAAAA","15":"b:x/Vf/PL5jwf+I4hO6O8/8P7///8HAAAA","18":"b:QgAAAJABgAGwAwAAAAgAsAgDAOgGAAAA","22":[14],"34":"b:x/Vf/IL5nwf+I4BK4O8/8P7///8DAAAA","55":[14],"60":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","69":"b:QgAAAJABgAGwAwAAAAgAsAgDAOgGAAAA"},"packages/frontend/editor-ui/src/app/composables/useHistoryHelper.ts":{"18":"b://Vf///5/7f/+/////////////8HAAAA","24":"b:QuEGVARIIBSHA4IAADq7IG5ED/kHAAAA","32":[134,152],"60":[134],"88":[134,152],"94":[134,152],"99":[134],"110":"b:QuEGRARIIASGA4IAADq7AG5ED/kHAAAA","117":"b:QuEGVARIIBSHA4IAADq7IG5ED/kHAAAA","147":"b://Vf///5/7f/+/////////////8HAAAA"},"packages/frontend/editor-ui/src/app/composables/useInvalidNodeGroupCleanup.ts":{"24":"b://Vf///5/7f/+/////////////8HAAAA","31":[66],"50":[66],"51":[66],"77":"b:wuBXyAIAiAbeAYACgG8gwMz6LvcGAAAA","81":[66]},"packages/frontend/editor-ui/src/app/composables/useKeybindings.ts":{"60":"b://Vf///5/xf/++9P/f+/8P////8HAAAA","81":"b:AiAEBARIAASGAwAAAAAKAGwEAPkGAAAA","83":"b:QuEGVARIIBSHA4IAADq7IG5ED/kHAAAA","87":"b:AiAEBARIAASGAwAAAAAKAGwEAPkGAAAA","89":"b:AiAEBARIAASGAwAAAAAKAGwEAPkGAAAA","91":"b:AiAEBARIAASGAwAAAAAKAGwEAPkGAAAA","96":"b:AiAEBARIAASGAwAAAAAKAGwEAPkGAAAA","98":"b:AiAEBARIAASGAwAAAAAKAGwEAPkGAAAA","99":"b:AiAEBARIAASGAwAAAAAKAGwEAPkGAAAA","103":"b:AiAEBARIAASGAwAAAAAKAGwEAPkGAAAA","109":"b:AiAEBARIAASGAwAAAAAKAGwEAPkGAAAA","127":"b:AiAEBARIAASGAwAAAAAKAGwEAPkGAAAA","145":"b:AiAEBARIAASGAwAAAAAKAGwEAPkGAAAA","199":"b:QuEGVARIIBSHA4IAADq7IG5ED/kHAAAA"},"packages/frontend/editor-ui/src/app/composables/useLayoutProps.ts":{"4":"b:x/Vf/AL4nwf+I4hD6O8/8Pz///8HAAAA","7":"b:x/Vf/AL4nwf+I4hD6O8/8Pz///8HAAAA"},"packages/frontend/editor-ui/src/app/composables/useNodeConnections.ts":{"12":"b:x/Vf/PL5nwf+I4hO6O8/8P////8HAAAA","25":"b:x/Vf/PL5nwf+I4hO6O8/8P////8HAAAA","26":"b:x/VP/PLZjwf+A4hOwOM/sN////MFAAAA","29":"b:x/Vf/PL5nwf+I4hO6O8/8P////8HAAAA","30":"b:x/VP/PLZjwf+A4hOwOM/sN////MFAAAA","33":"b:x/Vf/PL5nwf+I4hO6O8/8P////8HAAAA","34":[0,1,7,2,8,10,12,113,127,65,6,13,14],"37":"b:x/VP/PLZjwf+A4hOwOM/sN////MFAAAA","45":"b:x/Vf/PL5nwf+I4hO6O8/8P////8HAAAA","46":"b:x/VP/PL5jwf+A4hO4Oc/8N////sHAAAA","49":"b:x/Vf/PL5nwf+I4hO6O8/8P////8HAAAA","50":"b:x/VP/PL5jwf+A4hO4Oc/8N////sHAAAA","53":"b:x/VP/PL5jwf+A4hO4Oc/8N////sHAAAA","61":[130,66,152]},"packages/frontend/editor-ui/src/app/composables/useNodeDirtiness.ts":{"27":[58,130,131,6,139,152,157,158,159],"35":[58,130,131,6,139,152,157,158,159],"58":[6,152],"66":[58,130,131,6,139,152,157,158,159],"67":[58,130,131,6,139,152,157,158,159],"77":[157,158,159],"89":"b:QAAAAAAACAQIAIAAAEAAAAAKKAEAAAAA","138":"b://Vf///5/7f/+/////////////8HAAAA","141":"b:x5BK4LKBDwd6AIACgGcgAA7+r+EAAAAA","144":"b:x/VP/PL5jwf+A4hO4Oc/8N////sHAAAA","148":"b:x5BK4LKBDwd6AIACgGcgAA7+r+EAAAAA","152":[58,157,158,159],"156":"b:x5BK4LKBDwd6AIACgGcgAA7+r+EAAAAA","158":"b:x5BIwLKBDwd4AIACgGMgAA7+r+EAAAAA","159":[0,1,7,2,6],"162":"b:x5BK4LKBDwd6AIACgGcgAA7+r+EAAAAA","179":"b:x5BK4LKBDwd6AIACgGcgAA7+r+EAAAAA","215":[58],"218":[58],"258":"b:x/VP/PL5jwf+A4hO4Oc/8N////sHAAAA","262":"b:QAAAAAAACAQIAIAAAEAAAAAKKAEAAAAA","271":[58],"308":"b:x5BIwLKBDwd4AIACgGMgAA7+r+EAAAAA","309":"b:x5BIwLKBDwd4AIACgGMgAA7+r+EAAAAA"},"packages/frontend/editor-ui/src/app/composables/useNodeDocsUrl.ts":{"5":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","8":[102],"10":[8,102,146,13],"18":"b:xvVD3IJYjQfaAYBKwOU+sNz+/5MCAAAA"},"packages/frontend/editor-ui/src/app/composables/useNodeExecution.ts":{"81":"b:xvVH3IJYjQbaAYBCwOU+sNz+/xMCAAAA","104":"b:hIAAQAAACAJIAIAAAEUAAACoLgAAAAAA","112":"b:xvVH3IJYjQbaAYBCwOU+sNz+/xMCAAAA","116":"b:xvVH3IJYjQbaAYBCwOU+sMz+/xMCAAAA","122":"b:xvVH3IJYjQbaAYBCwOU+sNz+/xMCAAAA","126":[22,103,51],"128":"b:xvVH3IJYjQbaAYBCwOU+sMz+/xMCAAAA","130":"b:hIAAQAAACAJIAIAAAEUAAACoLgAAAAAA","136":"b:xvVH3IJYjQbaAYBCwOU+sMz+/xMAAAAA","138":"b:xvVH3IJYjQbaAYBCwOU+sMz+/xMAAAAA","140":[22,103,106,51,147],"142":"b:xvVH3IJYjQbaAYBCwOU+sMz+/xMCAAAA","144":"b:xvVH3IJYjQbaAYBCwOU+sNz+/xMCAAAA","154":"b:xvVH3IJYjQbaAYBCwOU+sNz+/xMCAAAA","167":"b:xvVH3IJYjQbaAYBCwOU+sMz+/xMCAAAA","175":"b:xvVH3IJYjQbaAYBCwOU+sNz+/xMCAAAA","181":"b:gqFFBAAICACQAABAgIQAgMT4WxICAAAA","188":"b:xvVD3IJYjQbaAYBCwOU+sNz+/xMCAAAA","212":"b:xvVH3IJYjQbaAYBCwOU+sMz+/xMCAAAA","240":"b:xvVH3IJYjQbaAYBCwOU+sMz+/xMCAAAA","246":"b:xvVH3IJYjQbaAYBCwOU+sNz+/xMCAAAA","346":[7,2,57],"355":"b:hIAAQAAACAJIAIAAAEUAAACoLgAAAAAA"},"packages/frontend/editor-ui/src/app/composables/useNodeHelpers.ts":{"82":"b:xvVH3MJYjafaAYBKxOU+sdz+/5MCAAAA","112":"b:xvVH3IJYjQfaAYBKwOU+sNz+/5MCAAAA","145":"b:xvVD3IJYjQfaAYBKwOU+sNz+/5MCAAAA","158":"b:wkEAGAIAiAAAAAAAgAACMAAAEAAAAAAA","159":"b:wkEAGAIAiAAAAAAAgAACMAAAEAAAAAAA","160":"b:wkEAGAIAiAAAAAAAgAACMAAAEAAAAAAA","164":"b:1vVP/MLZj7fbA4hOzuU+sd7+/5MHAAAA","184":[7,98,13],"245":"b:x7BK4LKBDwZ6AIAChGcgAA7+r+EEAAAA","261":[27,8,33,104],"275":"b:x/VP/PL5jwf+A4hC4Oc/8Pz///sHAAAA","295":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","303":"b:x/Vf/LL5nwf+I4hC6O8/8P7///8HAAAA","315":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","323":"b:AmFC1AIAiQAKAoBCwEEQMMz6OgMAAAAA","331":"b:wuVH3AJ4iQSaA4hC4Gc/8Oz+fvMHAAAA","346":"b:AmFG1AIAiQAKAIBCwEEQMMz6OgMAAAAA","354":"b:x/VP/PL5jwf+A4hK4Oc/8Pz///sHAAAA","381":"b:x/VP/PL5jwf+A4hC5Oc/8Pz///sHAAAA","394":"b:x/VP/PLZjwf+A4hCwOM/sNz///MFAAAA","430":"b:xnVPfELZjgNSAghOxOEOsMoO2hAHAAAA","431":"b:xnVPfELZjgNSAghOwOEOsMoO2hAHAAAA","440":"b:xnVPfELZjgNSAghOwOEOsMoO2hAHAAAA","458":"b:xnVPfELZjgNSAghOxOEOsMoO2hAHAAAA","492":"b:x/VP/PL5jwf+A4hO5Oc/8P7///sHAAAA","600":"b:wkEEEACAjgAAAgAAhAACMAAAEAAAAAAA","655":[27,8,33,104],"663":[27,8],"674":[27,8],"677":"b:x/Vf/PL5nwf/I4hO6u8/8P7///8HAAAA","692":"b:xvVL3LJZjQfaAYBKwOU+sNz+/5MCAAAA","696":"b:xvVL3LJZjQfaAYBKwOU+sNz+/5MCAAAA","703":"b:xvVD3IJYjQfaAYBCwOE+sMz+/5MAAAAA","705":"b:QJAAQIAABQVYAIACAGEgAAT6r4EAAAAA","711":"b:QNAAQIAABQVYAIACAGEoAJT+74EAAAAA","719":"b:QJAAQIAABQVYAIACAGEgAAT+r4EAAAAA","725":"b:xvVL3LJZjQfaAYBKwOU+sNz+/5MCAAAA","751":"b:xpBIQLABDQdYAIACgGUgAAT+r4EAAAAA","759":"b:xvVL3LJZjQfaAYBKwOU+sNz+/5MCAAAA","790":"b:QgAAAAAAAAACAAAAAAAAgEgIAPAAAAAA","797":"b:QgAAAAAAAAACAAAAAAAAgEgIAPAAAAAA","839":"b:x/VP/PL5jwf+A4hO4Oc/8P7///sHAAAA","843":[27,47,103,49,50,55,6,73],"859":[27,47,103,49,50,6,73],"894":"b:QMACQAAAAACAAYAAACI5AEgADgABAAAA","898":"b:QMACQAAAAACAAYAAACI5AEgADgABAAAA","901":"b:QMACQAAAAACAAYAAACI5AEgADgABAAAA","919":[134],"953":[2,48,141,145,70,147],"969":"b:xvVL3LJZjQfaAYBKwOU+sNz+/5MCAAAA","1038":"b:guVBlAJ4iQSaAQBC4AUO8Oz2elICAAAA"},"packages/frontend/editor-ui/src/app/composables/useNodeIconSource.ts":{"6":"b:x/Vf/PL5jwf+I4hO7O8/8P////8HAAAA","12":"b:xvVP3PJ5jQfaA4hK5OU+8N7+/9MGAAAA"},"packages/frontend/editor-ui/src/app/composables/useNodeType.ts":{"8":"b:x/Vf/PL5jwf+I4hO6O8/8P7///8HAAAA","16":"b:xvVD3IJ4jQfaAYBK4OU+8Nz+/9MCAAAA","29":"b:xvVD3IJ4jQfaAYBK4OU+8Nz+/9MCAAAA","31":"b:xuVD3IJYjQfaAYBKwOU+sMz+/5MCAAAA"},"packages/frontend/editor-ui/src/app/composables/usePinnedData.ts":{"70":"b:xvVL3LJZjQfaAYBKwOU+sNz+/5MDAAAA","78":"b:xvVL3LJZjQfaAYBKwOU+sNz+/5MCAAAA","83":"b:xvVD3IJYjQfaAYBKwOU+sMz+/5MCAAAA","93":"b:xvVD3IJYjQfeAYBKwOU+sNz+/5MCAAAA","104":"b:xvVD3IJYjQfaAYBKwOU+sNz+/5MCAAAA","109":"b:xvVD3IJYjQfaAYBKwOU+sNz+/5MCAAAA","171":"b:AEAAAAAAAABAAAAAACAAAMDyDgABAAAA","175":"b:AEAAAAAAAABAAAAAACAAAMDyDgABAAAA","225":"b:AEAAAAAAAABAAAAAACAAAMDyDgABAAAA","244":[147],"266":"b:AEAAAAAAAABAAAAAACAAAMDyDgABAAAA"},"packages/frontend/editor-ui/src/app/composables/usePostMessageHandler.ts":{"40":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","44":"b:x/Vf/AL4nwf+I4hP6O8/8P////8HAAAA","66":"b:x/Vf/AL4nwf+I4hP6O8/8P////8HAAAA","79":[52],"85":[90,91,52,129],"132":[52],"161":[52],"184":[52],"233":"b:x/Vf/AL4nwf+I4hP6O8/8P////8HAAAA","296":"b:x/Vf/AL4nwf+I4hP6O8/8P////8HAAAA","302":[52,136,71,155]},"packages/frontend/editor-ui/src/app/composables/usePushConnection/handlers/executionFinished.ts":{"62":"b:x5AI4LKBHwZ6AIACgGcgAA7+r+EAAAAA","205":"b:x5AI4LKBHwZ6AIACgGcgAAz+r+EAAAAA","211":"b:RgAI4LCBFwZwAAACgCIgAAwOj+EAAAAA","232":[52],"246":"b:x5AI4LKBHwZ6AIACgGcgAA7+r+EAAAAA","276":"b:x5AI4LKBHwZ6AIACgGcgAAz+r+EAAAAA","289":"b:x5AI4LKBHwZ6AIACgGcgAAz+r+EAAAAA","315":[52],"333":[52],"350":"b:woAAAAIAEAAQAIAAAAAAAAAKAmAAAAAA","433":"b:RxAI4LCBHwZ6AAACgGcgAA7+r+EAAAAA","454":"b:RxAI4LCBHwZ6AAACgGcgAA7+r+EAAAAA","524":"b:x5AI4LKBHwZ6AIACgGcgAAz+r+EAAAAA"},"packages/frontend/editor-ui/src/app/composables/usePushConnection/handlers/executionStarted.ts":{"13":"b:x5BK4LKBHwZ6AIACgGcgAA7+r+EAAAAA"},"packages/frontend/editor-ui/src/app/composables/usePushConnection/handlers/nodeExecuteAfter.ts":{"16":"b:x5BK4LKBHwZ6AIACgGcgAA7+r+EAAAAA","49":"b:x5BK4LKBHwZ6AIACgGcgAA7+r+EAAAAA","50":"b:x5BK4LKBHwZ6AIACgGcgAA7+r+EAAAAA"},"packages/frontend/editor-ui/src/app/composables/usePushConnection/handlers/nodeExecuteAfterData.ts":{"11":"b:x5BK4LKBHwZ6AIACgGcgAA7+r+EAAAAA"},"packages/frontend/editor-ui/src/app/composables/usePushConnection/handlers/nodeExecuteBefore.ts":{"9":"b:x5BK4LKBHwZ6AIACgGcgAA7+r+EAAAAA"},"packages/frontend/editor-ui/src/app/composables/usePushConnection/handlers/testWebhookReceived.ts":{"8":"b:xgBIIAAAGAIQAAAAgAAAAAIACAAAAAAA"},"packages/frontend/editor-ui/src/app/composables/usePushConnection/handlers/trackNodeExecution.ts":{"11":"b:x5BK4LKBHwZ6AIACgGcgAA7+r+EAAAAA"},"packages/frontend/editor-ui/src/app/composables/usePushConnection/usePushConnection.ts":{"45":"b:x/Vf/PL5nwf+I4hP6O9/+P////8HAAAA","46":"b:x/Bf6PKBnwf+A4ADgO8g8N7/r/8GAAAA","51":[36,39,40,52,136,71,155],"60":"b:x/Bf6PKBnwf+A4ADgO8g8N7/r/8GAAAA"},"packages/frontend/editor-ui/src/app/composables/useResizablePanel.ts":{"51":[52],"61":[52],"62":[52],"63":"b://Vf/PP5nwf+I4hC7e8/8P7///8HAAAA","92":[52],"96":"b://Vf/PP5nwf+I4hC7e8/8P7///8HAAAA","100":[52],"104":"b://Vf/PP5nwf+I4hC7e8/8P7///8HAAAA","108":[52],"125":[52],"138":"b://Vf/PP5nwf+I4hC7e8/8P7///8HAAAA","139":"b://Vf/PP5nwf+I4hC7e8/8P7///8HAAAA","145":"b://Vf/PP5nwf+I4hC7e8/8P7///8HAAAA","153":"b:RgAIAJABAAKwAAAAAQAAgAgDAOgAAAAA","159":"b://Vf/PP5nwf+I4hC7e8/8P7///8HAAAA","160":"b:xgAIALABEAIAAAAAAAAAAAIAAIAAAAAA","161":[19,1,7,2,36,37,39,40,52,57,6,159]},"packages/frontend/editor-ui/src/app/composables/useResolvedExpression.ts":{"26":"b:1vVH3MJYjbfbA4BKzuU+sdz+/5MCAAAA","41":"b:1vVH3MJYjbfbA4BKzuU+sdz+/5MCAAAA","47":"b:1vVD3IJYjbfbA4BKyuU+sdz+/5MCAAAA","53":"b:1vVH3MJYjbfbA4BKzuU+sdz+/5MCAAAA","54":"b:QIAAAAAABABQAAAAACAgAABwCgAAAAAA","55":"b:wuUAAAAABBJSAAAAAKAiAADwOgAAAAAA","60":"b:1vVH3MJYjbfbA4BKzuU+sdz+/5MCAAAA","62":"b:wuUAAAAABBJSAAAAAKAiAADwOgAAAAAA","101":"b:1vVH3MJYjbfbA4BKzuU+sdz+/5MCAAAA","127":"b:1vVH3MJYjbfbA4BKzuU+sdz+/5MCAAAA","128":"b:1vVH3MJYjbfbA4BKzuU+sdz+/5MCAAAA","129":"b:1vVH3MJYjbfbA4BKzuU+sdz+/5MCAAAA"},"packages/frontend/editor-ui/src/app/composables/useRunWorkflow.ts":{"95":"b:x5BK4IKBDwZ6AIACgGcgAA7+r+EEAAAA","109":"b:QIAAAAAACABYAIAAAEAAAAAIJAAAAAAA","113":"b:x5BK4IKBDwZ6AIACgGcgAA7+r+EEAAAA","128":"b:x5BK4IKBDwZ6AIACgGcgAA7+r+EEAAAA","160":"b:x5BK4IKBDwZ6AIACgGcgAA7+r+EEAAAA","306":"b:x5BK4IKBDwZ6AIACgGcgAA7+r+EEAAAA","309":"b:x5BK4IKBBwZ6AIACgGcgAA7+r+EEAAAA","341":[19,1,2,57,129,6],"352":[106,51,65,68,147],"376":[106,51,65,68,147],"419":[87,51,110,67,139,149],"472":[22,103],"512":"b:x5BK4IKBDwZ6AIACgGcgAA7+r+EEAAAA","563":[6],"610":[6],"638":"b:QgBC4IKBDwRwAAACgCIgAAwOj+EEAAAA"},"packages/frontend/editor-ui/src/app/composables/useSelectionValidation.ts":{"26":"b://Vf///5/7f/+/////////////8HAAAA","38":"b:xvVL3IJ4jQbeA4BC4Oc/8Pz+//MDAAAA","55":"b:x/Vf/LL5jwf+I4hC6O8/8Pz///8HAAAA","62":"b:wuQCQAAAAACGA4AAACI5AEwADgEBAAAA","69":[66],"96":"b:x/Vf/LL5nwf+I4hC6O8/8Pz///8HAAAA","98":"b:xvVL3IJ4jQbeA4BC4Oc/8Pz+//MDAAAA","102":"b:xvVL3IJ4jQbeA4BC4Oc/8Pz+//MDAAAA","110":"b:x/Vf/LL5nwf+I4hC6O8/8Pz///8HAAAA","116":"b:xvVL3IJ4jQbeA4BC4Oc/8Pz+//MDAAAA","118":"b:xvVL3IJ4jQbeA4BC4Oc/8Pz+//MDAAAA","125":"b:xuVD3IJYjQbeA4BCwOE+sNz+//MAAAAA","127":"b:BmAAAIAQCAZOAAAAAKAMANx47eEAAAAA"},"packages/frontend/editor-ui/src/app/composables/useSettingsItems.ts":{"23":"b://Vf///5/7f/++/D//////z///8HAAAA","203":"b://Vf///5/7f/++/D//////z///8HAAAA","206":"b://Vf///5/7f/++/D//////z///8HAAAA"},"packages/frontend/editor-ui/src/app/composables/useSidebarLayout.ts":{"11":"b://Vf///5/xf/++9D/f+/8Pz///8HAAAA","17":"b://Vf///5/xf/++9D/f+/8Pz///8HAAAA","27":[75,77,35,54,55,86,72]},"packages/frontend/editor-ui/src/app/composables/useTelemetryInitializer.ts":{"13":"b://Vf///5/7f/+/////////////8HAAAA","27":"b://Vf///5/7f/+/////////////8HAAAA","32":"b://Vf///5/7f/+/////////////8HAAAA","36":"b://Vf///5/7f/+/////////////8HAAAA","40":"b://Vf///5/7f/+/////////////8HAAAA","59":"b://Vf///5/7f/+/////////////8HAAAA","69":"b://Vf///5/7f/+/////////////8HAAAA"},"packages/frontend/editor-ui/src/app/composables/useToast.ts":{"21":"b:19EI8QuA/xf/A+ACj3/iZg7+v+0CAAAA"},"packages/frontend/editor-ui/src/app/composables/useUniqueNodeName.ts":{"4":"b://Vf///5/7f/+/////////////8HAAAA","11":"b:AgAAAAAAAAAIAQAAAAAAAOwAaAAAAAAA","12":"b:AgAAAAAAAAAIAQAAAAAAAOwAaAAAAAAA","27":"b:wuVH1AJ4iQSaA4hC4Cc/8Oz2flMHAAAA","34":"b:AgAAAAAAAAAIAQAAAAAAAOwAaAAAAAAA"},"packages/frontend/editor-ui/src/app/composables/useUserHelpers.ts":{"5":"b://Vf///5/7f/++/D//////z///8HAAAA","6":"b://Vf///5/7f/++/D//////z///8HAAAA","12":"b://Vf///5/7f/++/D//////z///8HAAAA"},"packages/frontend/editor-ui/src/app/composables/useWorkflowDiffRouting.ts":{"18":"b://Vf///5/7f/+/////////////8HAAAA","27":"b://Vf///5/7f/+/////////////8HAAAA","50":"b://Vf///5/7f/+/////////////8HAAAA","73":"b://Vf///5/7f/+/////////////8HAAAA","111":"b://Vf///5/7f/+/////////////8HAAAA","133":"b://Vf///5/7f/+/////////////8HAAAA"},"packages/frontend/editor-ui/src/app/composables/useWorkflowExtraction.ts":{"45":"b:x/Vf/PL5jwf+I4hO6O8/8P7///8HAAAA","94":[152],"112":[152],"114":[152],"145":[152],"146":[152],"172":[152],"183":[152],"191":[152],"192":[152],"198":[152],"199":[152],"253":[152],"278":[152],"306":[152],"311":[152],"312":[152],"319":[152],"337":[152],"341":[152],"343":[152],"350":[152],"379":[152],"382":[152],"416":[152],"435":[152],"454":[152],"459":[152],"475":[152],"478":[152],"479":[152],"513":[152],"567":[152],"574":[152],"577":[152],"582":[152],"591":[152],"592":[152],"598":[152],"599":[152],"614":[152],"643":[152],"644":[152],"652":[152],"659":[152],"671":[152],"691":[152]},"packages/frontend/editor-ui/src/app/composables/useWorkflowHelpers.ts":{"76":"b:wvVABAAIjRJSAABIhKAiAIT4OgMCAAAA","177":"b:QJAAAAAABABQAAAAACAgAABwKgEAAAAA","280":[148,149],"311":[1,7,26,55,145,14,148,149,152,153],"321":[1,7,26,55,145,14,148,149,152,153],"347":"b:wsBC4IKBDwTwAYACgCI5AEwOj+EFAAAA","353":"b:wvVABAAIjQJSAABIgKAiAIT4OgMCAAAA","384":"b:QNAAAAAABABQAAAAAAAgAIDwKgEAAAAA","400":[106,51,65,68,147],"426":"b:wvVABAAIjQJSAABIgKQiAIT4OgMCAAAA","515":"b://Vf///5/7f/+/////////////8HAAAA","529":"b:QMACQAAAAACAAYAAACI5AEgADgABAAAA","555":"b:wuUD1AJ4gQSaAYBCYCM/cMwiPlIAAAAA","569":"b:gAFABAAICAAQAABIgAAAAAAIGgACAAAA","590":"b:AAFABAAICAAQAABIgAAAAAAIGgACAAAA","631":[15,87],"634":[15,87],"665":"b:wvVABAAIDBJSAABIhKAiAID4OgACAAAA","742":"b:QMACQAAAAACAAYAAACI5AEgADgABAAAA","773":[52,65,131,134,147,72,73],"777":[65,131,134,147,72,73],"782":[73],"784":[73],"798":[52],"816":"b:R3AIKPCBlwP0AwANgMAAMB4th+kGAAAA","854":"b:R3AIKPCBlwP0AwANgMAAMB4th+kGAAAA"},"packages/frontend/editor-ui/src/app/composables/useWorkflowId.ts":{"17":"b://Vf///5/7f/+/////////////8HAAAA","20":"b://Vf///5/7f/+/////////////8HAAAA","40":"b://Vf///5/7f/+/////////////8HAAAA"},"packages/frontend/editor-ui/src/app/composables/useWorkflowImport.ts":{"11":"b:x/Vf/AL4nwf+I4hP6O8/8P////8HAAAA","17":[90,91,52,129],"19":[90,91,52,129]},"packages/frontend/editor-ui/src/app/composables/useWorkflowInitialization.ts":{"81":"b:x/Vf/PL5nwf+I4hP6O8/8P////8HAAAA","92":"b:x/Vf/PL5nwf+I4BD6O8/8Pz///8HAAAA","93":"b:x/Vf/PL5nwf+I4BP6O8/8P////8HAAAA","94":"b:x/Vf/PL5nwf+I4hP6O8/8P////8HAAAA","95":[52,83,73,162],"96":"b:x/Vf/PL5nwf+I4hD6O8/8Pz///8HAAAA","98":"b:x/Vf/PL5nwf+I4hD6O8/8Pz///8HAAAA","123":"b:x/Vf/PL5nwf+I4hP6O8/8P////8HAAAA","188":"b:R3AIKPCBlwP0AwABgMAAMBwvh+kGAAAA","205":"b:x/Vf/PL5nwf+I4hP6O8/8P////8HAAAA","207":"b:x/Vf/PL5nwf+I4hP6O8/8P////8HAAAA","243":"b:R3AIKPCBlwP0AwABgMAAMBwth+kGAAAA","298":"b:wuVX9AJ4mQSaIYBO6C8/8O/yfl4DAAAA","313":"b:wuVX9AJ4mQSaIYBO6C8/8O/yfl4DAAAA","344":"b:R3AIKPCBlwP0AwABgMAAMBwth+kGAAAA","399":"b:x/Vf/PL5nwf+I4hP6O8/8P////8HAAAA","469":[36,40,52,136,71,155]},"packages/frontend/editor-ui/src/app/composables/useWorkflowNormalization.ts":{"22":"b://Vf///5/7f/+/////////////8HAAAA","26":"b:x/VP/PL5jwf+A4hO4Oc/8P7///sHAAAA","46":"b:x/VP/PL5jwf+A4hO4Oc/8P7///sHAAAA","58":"b:x/VP/PL5jwf+A4hO4Oc/8P7///sHAAAA","76":"b:R3AIKPCBhwP0AwANgMAAMB4vh+kGAAAA","81":"b:R3AIKPCBhwP0AwAMgMAAIB4vh+kGAAAA","82":"b:R3AIKPCBhwP0AwAMgMAAIB4vh+kGAAAA","83":"b:R3AIKPCBhwP0AwAMgMAAIB4vh+kGAAAA","85":"b:R3AIKPCBhwP0AwAMgMAAIB4vh+kGAAAA"},"packages/frontend/editor-ui/src/app/composables/useWorkflowPublicationStatusSync.ts":{"26":"b:x/Vf/PL5nwf+I4hD6O8/8Pz///8HAAAA","35":[52],"38":[52],"41":"b:x/Vf/PL5nwf+I4hD6O8/8Pz///8HAAAA","81":"b:x/Vf/PL5nwf+I4hD6O8/8Pz///8HAAAA","82":[52,55,71,155],"88":"b:x/Vf/PL5nwf+I4hD6O8/8Pz///8HAAAA","89":[52],"95":"b:x/Vf/PL5nwf+I4hD6O8/8Pz///8HAAAA","96":[52],"97":[36,39,40,52,55,136,71,155]},"packages/frontend/editor-ui/src/app/composables/useWorkflowSaving.ts":{"45":"b://Vf///5/7f/+/////////////8HAAAA","73":[55,131,136,137,71,155,158,159,162],"77":[71],"149":"b:wuBXyAIAiAbeAYACgG8ggMz6LvcGAAAA","156":"b:wuBXyAIAiAbeAYACgG8ggMz6LvcGAAAA","186":"b:wuBXyAIAiAbeAYACgG8ggMz6LvcGAAAA","373":"b:wuBXwAIAmASaAYACgC8gwMzSLlYGAAAA","577":"b:wuAHyAIAiATeAYAAgCogwMxyDvcCAAAA","591":"b:AmAHiAAAiACOAQAAgAgAgMxSCNYCAAAA","610":"b:wuVH3AJ4iQbeA4BC4G8/8Oz+fvcDAAAA","636":"b:woBWwAIACAZaAIACgGcgwAz6LmEGAAAA","644":"b://Vf///5/7f/+/////////////8HAAAA"},"packages/frontend/editor-ui/src/app/composables/useWorkflowUpdate.ts":{"39":"b://Vf///5/7f/++/v//////////8HAAAA","58":[18],"60":[18],"63":[18],"70":[18],"93":[18],"106":[18],"183":[18],"192":[18],"196":[18],"211":[18],"236":[18],"248":[18],"249":[18],"253":[18],"255":[18],"279":[18],"313":[18],"325":[18],"340":[18],"348":[18],"367":[18]},"packages/frontend/editor-ui/src/app/composables/useWorkflowsCache.ts":{"27":"b:x/Bf6PKBnwf+A4ADgO8g8Nz/r/8GAAAA","30":"b:x/Bf6PKBnwf+A4ADgO8g8Nz/r/8GAAAA","35":"b:x/Bf6PKBnwf+A4ADgO8g8Nz/r/8GAAAA","39":"b:x/Bf6PKBnwf+A4ADgO8g8Nz/r/8GAAAA","46":"b:x/Bf6PKBnwf+A4ADgO8g8Nz/r/8GAAAA","62":[50,52,127,155,156,161],"85":[50,52,127,155,156,161],"96":[52,127],"104":[52],"113":[52],"120":[52,127],"123":[52,127]},"packages/frontend/editor-ui/src/app/constants/actions.ts":{"1":"b://Vf///5/7f/+/////////////8HAAAA"},"packages/frontend/editor-ui/src/app/constants/experiments.ts":{"6":"b://Vf///5/7f/+/////////////8HAAAA"},"packages/frontend/editor-ui/src/app/constants/localStorage.ts":{"20":[3,4,5,96,98,32],"22":[3,4,5,96,98,32],"28":[3,4,5,96,98,32],"34":"b:x/Vf/PL5jwf+I4hD6O8/8Pz///8HAAAA","36":"b:x/Vf/PL5jwf+I4hD6O8/8Pz///8HAAAA","38":"b:x/Vf/PL5jwf+I4hD6O8/8Pz///8HAAAA"},"packages/frontend/editor-ui/src/app/constants/navigation.ts":{"9":"b://Vf///5/7f/+/////////////8HAAAA"},"packages/frontend/editor-ui/src/app/constants/nodeTypes.ts":{"136":"b://Vf///5/7f/+/////////////8HAAAA"},"packages/frontend/editor-ui/src/app/constants/workflows.ts":{"16":"b://Vf///5/7f/+/////////////8HAAAA"},"packages/frontend/editor-ui/src/app/dev/render-tracker.ts":{"38":"b://Vf///5/7f/+/////////////8HAAAA","67":"b://Vf///5/7f/+/////////////8HAAAA"},"packages/frontend/editor-ui/src/app/init.ts":{"46":"b://Vf///5/7f/+/////////////8HAAAA","99":"b://Vf///5/7f/+//v//////////8HAAAA","119":[95,77,121,86],"140":"b:x/Vf///5/xf/++9D+P+/9Pz///8HAAAA","180":[77],"202":"b:3/Vf///5/7f/+//j//////z///8HAAAA","247":"b://Vf///5/7f/+/////////////8HAAAA","260":"b://Vf///5/7f/+//z//////z///8HAAAA","296":[55,86]},"packages/frontend/editor-ui/src/app/layouts/BaseLayout.vue":{"10":"b://Vf///5/7f/+/////////////8HAAAA"},"packages/frontend/editor-ui/src/app/layouts/WorkflowLayout.vue":{"48":"b:x/Vf/AL4nwf+I4hD6O8/8Pz///8HAAAA","57":[52,55,83,71,155,73,162],"70":[52,137],"78":[52,136,71,155],"87":"b:x/Vf/AL4nwf+I4hD6O8/8Pz///8HAAAA","89":"b:x/Vf/AL4nwf+I4hD6O8/8Pz///8HAAAA","91":"b:x/Vf/AL4nwf+I4hD6O8/8Pz///8HAAAA","101":"b:x/Vf/AL4nwf+I4hC6O8/8Pz///8HAAAA","104":"b:x/Vf/AL4nwf+I4hD6O8/8Pz///8HAAAA"},"packages/frontend/editor-ui/src/app/models/history.ts":{"8":"b://Vf///5/7f/+/////////////8HAAAA","38":"b:wuVH1AJ4iQaeAYBC4Cc/8Oz+fvMDAAAA","55":"b:wuVX1AJ4iQaeAYBC4Cc/8Oz+fvMDAAAA","60":"b:woBCwAIACAYaAIACgCcgAAzaLuEAAAAA","61":"b:woBCwAIACAYaAIACgCcgAAzaLuEAAAAA","72":[18,57,114,58,115,65,130,66,134,72],"84":[134],"88":[18,114,58,115,65,66],"99":[134],"100":[134],"113":"b:wuVH1AJ4iQSaAYBC4Cc/8Oz2flMDAAAA","118":[134,152],"122":"b:wuVH1AJYiQSKAYBCwCM/sMz2fhMBAAAA","126":[134,152],"127":[134,152],"137":[112,65,130,66,134,6,152],"142":[134,152],"146":[130,134,152],"150":[134,152],"151":[134,152],"161":"b:wuVH1AJYiQSOAYBCwCM/sMz2fhMBAAAA","166":[134,152],"170":"b:wMAEAAAQAAQGAIAAACIpAEQADgEBAAAA","180":[134,152],"181":[134,152],"191":[18,112,58,115,65,130,66,134,6,152],"196":[134,152],"200":[18,112,58,115,65,130,66,134,6,152],"210":[134,152],"211":[134,152],"212":[134,152],"227":"b:QgAAAAAAAAACAAAAAAAAgEgIAPAAAAAA","234":[134],"238":[131,134],"244":[134],"245":[134],"260":[131],"290":[115,152],"299":[152],"308":[115],"317":[152],"318":[152],"329":[66],"346":[66],"372":[66],"381":[66],"400":[66],"410":[66]},"packages/frontend/editor-ui/src/app/moduleInitializer/moduleInitializer.ts":{"15":"b://Vf///5/7f/+//j//////z///8HAAAA","16":"b://Vf///5/7f/+//j//////z///8HAAAA","17":"b://Vf///5/7f/+//j//////z///8HAAAA","26":"b://Vf///5/7f/+//j//////z///8HAAAA","28":"b://Vf///5/7f/+//j//////z///8HAAAA","46":"b://Vf///5/7f/+//j//////z///8HAAAA","48":"b://Vf///5/7f/+//j//////z///8HAAAA","59":"b:AAAAAPwBAAAAwAcAEAAAAAAAAAAAAAAA","86":"b://Vf///5/7f/+//j//////z///8HAAAA","87":"b://Vf///5/7f/+//j//////z///8HAAAA","88":"b://Vf///5/7f/+//j//////z///8HAAAA","99":"b://Vf///5/7f/+/////////////8HAAAA","100":"b://Vf///5/7f/+/////////////8HAAAA","101":"b://Vf///5/7f/+/////////////8HAAAA"},"packages/frontend/editor-ui/src/app/plugins/cache.ts":{"3":"b:x/Bf6PKBnwf+A4ADgO8g8Nz/r/8GAAAA","8":"b:x/Bf6PKBnwf+A4ADgO8g8Nz/r/8GAAAA","9":"b:x/Bf6PKBnwf+A4ADgO8g8Nz/r/8GAAAA","10":"b:x/Bf6PKBnwf+A4ADgO8g8Nz/r/8GAAAA","13":"b:x/Bf6PKBnwf+A4ADgO8g8Nz/r/8GAAAA","25":[52],"33":"b:x/Bf6PKBnwf+A4ADgO8g8Nz/r/8GAAAA","34":"b:x/Bf6PKBnwf+A4ADgO8g8Nz/r/8GAAAA","36":"b:x/Bf6PKBnwf+A4ADgO8g8Nz/r/8GAAAA","39":"b:x/Bf6PKBnwf+A4ADgO8g8Nz/r/8GAAAA","40":[52],"44":[50,52,127,155,156,161],"49":"b:x/Bf6PKBnwf+A4ADgO8g8Nz/r/8GAAAA","53":[52],"58":[52],"63":[52],"67":[52],"68":[52],"71":[52],"82":[52],"89":"b:x/Bf6PKBnwf+A4ADgO8g8Nz/r/8GAAAA","99":"b:x/Bf6PKBnwf+A4ADgO8g8Nz/r/8GAAAA","100":"b:x/Bf6PKBnwf+A4ADgO8g8Nz/r/8GAAAA","104":[52],"111":[50,52,127,155,156,161],"112":[50,52,127,155,156,161],"117":[52],"118":[52],"123":[52],"124":[52]},"packages/frontend/editor-ui/src/app/plugins/chartjs.ts":{"15":"b://Vf///5/7f/+/////////////8HAAAA"},"packages/frontend/editor-ui/src/app/plugins/components.ts":{"10":"b://Vf///5/7f/+/////////////8HAAAA"},"packages/frontend/editor-ui/src/app/plugins/directives.ts":{"5":"b://Vf///5/7f/+/////////////8HAAAA"},"packages/frontend/editor-ui/src/app/plugins/sentry.ts":{"33":[84],"36":[84],"60":"b://Vf///5/7f/+/////////////8HAAAA"},"packages/frontend/editor-ui/src/app/plugins/telemetry/index.ts":{"28":"b://Vf///5/7f/+/////////////8HAAAA","40":"b://Vf///5/7f/+//v//////////8HAAAA","44":"b://Vf///5/7f/+/////////////8HAAAA","79":"b://Vf///5/7f/+//j//////z///8HAAAA","113":"b://Vf/P/5/7f/+89D////+/7///8HAAAA","143":"b://Vf///5/7f/++/v//////////8HAAAA","173":[55,86],"185":[67],"215":"b:AmFC1AIAiQAKAIBCwEEQMMz6OgMAAAAA","303":"b://Vf///5/7f/+/////////////8HAAAA"},"packages/frontend/editor-ui/src/app/plugins/vitePreloadError.ts":{"4":"b://Vf///5/7f/+/////////////8HAAAA"},"packages/frontend/editor-ui/src/app/push-connection/useEventSourceClient.ts":{"13":"b://Vf///5/7f/+/////////////8HAAAA"},"packages/frontend/editor-ui/src/app/push-connection/useHeartbeat.ts":{"12":"b://Vf///5/7f/+/////////////8HAAAA","17":"b://Vf///5/7f/+/////////////8HAAAA","21":[96,136]},"packages/frontend/editor-ui/src/app/push-connection/useReconnectTimer.ts":{"14":"b://Vf///5/7f/+/////////////8HAAAA","33":[96,136],"40":"b://Vf/PP5jwf+449D/e9/+P7///8HAAAA"},"packages/frontend/editor-ui/src/app/push-connection/useWebSocketClient.ts":{"24":"b://Vf///5/7f/+/////////////8HAAAA","35":[0,98,38,55],"40":"b://Vf/PP5jwf+449D/e9/+P7///8HAAAA","53":"b://Bf6POBjwf+A4ADhe8g8N7/r/8GAAAA","61":"b://Vf/PP5nwf+449D/e9/+P7///8HAAAA","75":"b://Vf/PP5jwf+449D/e9/+P7///8HAAAA","94":"b:x/Bf6PKBnwf+A4ADgO8g8Nz/r/8GAAAA"},"packages/frontend/editor-ui/src/app/router.ts":{"40":"b:x/Vf/AL4nwf+I4hO6O8/8P7///8HAAAA","41":[1,56,131,137,68,69,6,157,158,159],"42":[1,56,131,137,68,69,6,157,158,159],"43":[1,131,137,68,69,6,157,158,159],"44":[1,131,137,68,69,6,157,158,159],"47":[56,131,137,68,69,6,157,158,159],"48":[56,131,137,68,69,6,157,158,159],"51":[95,121,86],"52":[95,121,86],"53":[122],"54":[122],"56":[118],"57":[118],"58":[77],"59":[77],"60":[61,120,63],"61":[61,120,63],"63":[93,76,55,86],"65":[73],"66":[73],"67":[162],"68":[162],"69":[73],"70":[73],"71":[162],"72":[162],"78":[55,86],"80":[85],"81":[85],"82":[60],"87":[60],"96":[123],"97":[123],"98":[136],"99":[136],"100":[162],"101":[127],"102":[127],"105":[88],"106":[88],"107":[88,127],"108":[88,127],"115":[56],"116":[56],"176":"b:IAAAK/wBwACASAcgEBAAAAAAAAACAAAA","255":[83,73],"284":[162],"289":[162],"454":"b:wuVX9AJ4iQSaI4hC6C8/8Ozyfl4HAAAA","474":[90,91,129],"485":[128],"491":[128],"601":[92],"620":"b://Vf///5/7f/++/D//////z///8HAAAA","742":[15,87,16,17,18,20],"796":"b:3/Vf/f/5/7f/++/D//////z///8HAAAA","1090":"b:3/Vf/f/5/7f/++/D//////z///8HAAAA","1166":"b://Vf///5/7f/+/////////////8HAAAA","1181":"b://Vf///5/7f/++/v//////////8HAAAA","1191":"b://Vf///5/7f/+/////////////8HAAAA","1223":[93,76,122],"1255":"b://Vf///5/7f/++/v//////////8HAAAA"},"packages/frontend/editor-ui/src/app/stores/aiGateway.store.ts":{"25":"b://Vf///5/7f/++/L//////z///8HAAAA","123":"b:xvVH3MJYjafaAYBKxOU+sdz+/5MCAAAA","125":"b:wkEEGAIAiAAAAAAAhAACMAAAEAAAAAAA","128":"b:xvVH3MJYjafaAYBKxOU+sdz+/5MCAAAA","145":"b:AFFBFABYiABAAAAAwKAAsIhQfhAAAAAA"},"packages/frontend/editor-ui/src/app/stores/backendConnection.store.ts":{"7":"b://Vf///5/7f/+/////////////8HAAAA","10":"b://Vf///5/7f/+/////////////8HAAAA"},"packages/frontend/editor-ui/src/app/stores/canvas.store.ts":{"6":"b://Vf///5/7f/+/////////////8HAAAA","13":"b:RQAIBBABCAIGAAAAAAAAADwABgEAAAAA","20":"b:x/Vf/PL5jwf+I4hO6O8/8P7///8HAAAA","27":"b:x/Vf/PL5jwf+I4hC6O8/8P7///8HAAAA","28":"b:x/Vf/PL5jwf+I4hC6O8/8P7///8HAAAA"},"packages/frontend/editor-ui/src/app/stores/executionData.store.ts":{"59":"b:x/Vf/PL5nwf+I4hP6O8/8P////8HAAAA","66":"b:x/Vf/PL5nwf+I4hP6O8/8P////8HAAAA","75":[158,159],"85":"b:x/Vf/PL5nwf+I4hP6O8/8P////8HAAAA","86":"b:x/Vf/PL5nwf+I4hP6O8/8P////8HAAAA","95":"b:x/Vf/PL5nwf+I4hP6O8/8P////8HAAAA","99":"b:QJBAQIAADQRYAIACgGUgAAT6rwEAAAAA","101":"b:x5AI4LKBDwd6AIACgGcgAA7+r+EAAAAA","116":"b:x5BK4LKBDwd6AIACgGcgAA7+r+EAAAAA","133":"b:x5BK4LKBDwd6AIACgGcgAA7+r+EAAAAA","136":"b:x5BK4LKBDwd6AIACgGcgAA7+r+EAAAAA","139":"b:x5BK4LKBDwd6AIACgGcgAA7+r+EAAAAA","142":"b:QIAAAAAACABQAIAAAEAAAAgIJMAAAAAA","145":"b:QIAAAAAACABQAIAAAEAAAAgIJMAAAAAA","151":"b:x/Vf/PL5nwf+I4hP6O8/8P////8HAAAA","159":"b:x/Vf/PL5nwf+I4hP6O8/8P////8HAAAA","190":"b:x/Vf/PL5nwf+I4hP6O8/8P////8HAAAA","197":"b:x5BK4LKBDwd6AIACgGcgAA7+r+EEAAAA","201":"b:x/Vf/PL5nwf+I4hP6O8/8P////8HAAAA","216":"b:x5BK4LKBDwd6AIACgGcgAA7+r+EEAAAA","229":"b:x5BK4LKBDwd6AIACgGcgAA7+r+EEAAAA","236":"b:x5BK4LKBDwd6AIACgGcgAA7+r+EEAAAA","272":"b:x5BK4LKBDwd6AIACgGcgAA7+r+EEAAAA","275":"b:x5BK4LKBDwd6AIACgGcgAA7+r+EEAAAA","278":"b:x5BK4LKBDwd6AIACgGcgAA7+r+EEAAAA","287":"b:x5BK4LKBDwd6AIACgGcgAA7+r+EEAAAA","291":"b:x5BK4LKBDwd6AIACgGcgAA7+r+EEAAAA","294":[152],"297":[152],"305":"b:x/Vf/PL5nwf+I4hP6O8/8P////8HAAAA","313":"b:x/Vf/PL5nwf+I4hP6O8/8P////8HAAAA","329":"b:x/Vf/PL5nwf+I4hP6O8/8P////8HAAAA","339":"b:x5BI4LKBDwd4AIACgGMgAAx+r+EAAAAA","430":"b:x5BK4LKBDwd6AIACgGcgAA7+r+EEAAAA","450":"b:x5BK4LKBDwZ6AIACgGcgAA7+r+EAAAAA","472":"b:x5AI4LKBDwZ6AIACgGcgAAz+r+EAAAAA","476":"b:x5BK4LKBDwd6AIACgGcgAA7+r+EEAAAA","488":"b:x5BK4LKBDwZ6AIACgGcgAA7+r+EAAAAA","496":"b:x5BK4LKBDwZ6AIACgGcgAA7+r+EAAAAA","511":[6],"516":"b:x5BK4LKBDwZ6AIACgGcgAA7+r+EAAAAA","535":[2,89,103,50,6,146,70,151,152],"540":[2,89,103,50,6,146,70,151,152],"559":"b:x5BK4LKBDwZ6AIACgGcgAA7+r+EAAAAA","577":"b:x5BK4LKBDwZ6AIACgGcgAA7+r+EAAAAA","580":"b:x5BK4LKBDwZ6AIACgGcgAA7+r+EAAAAA","588":[158,159],"595":[131],"632":[131],"633":[131],"641":[131],"685":[6],"693":[6],"754":"b:x/Vf/PL5nwf+I4hP6O8/8P////8HAAAA"},"packages/frontend/editor-ui/src/app/stores/favorites.store.ts":{"9":"b://Vf///5/7f/+/////////////8HAAAA","36":"b://Vf///5/7f/+//j//////z///8HAAAA","46":"b:x/Vf/f/5/xf+I4hD6P8/8Pz///8HAAAA","52":[24,34,35,53,108,55,125,72,161],"86":[155,161],"92":[55,86]},"packages/frontend/editor-ui/src/app/stores/focusPanel.store.ts":{"47":"b://Vf///5/7f/+/////////////8HAAAA","54":"b://Vf///5/7f/+/////////////8HAAAA","64":"b://Vf///5/7f/+/////////////8HAAAA","71":"b:x/Vf/PL5jwf+I4hO6O8/8P7///8HAAAA","72":[116,132],"73":"b://Vf///5/7f/+/////////////8HAAAA","76":"b://Vf///5/7f/+/////////////8HAAAA","98":"b://Vf///5/7f/+/////////////8HAAAA","104":[52,116,132],"139":"b:wuBXwAIAmASaAYACgC8gwMzSLlYGAAAA","175":[116,132],"193":[116,132],"204":"b://Vf///5/7f/+/////////////8HAAAA","205":[116,132],"217":"b://Vf///5/7f/+/////////////8HAAAA"},"packages/frontend/editor-ui/src/app/stores/history.store.ts":{"10":"b://Vf///5/7f/+/////////////8HAAAA","17":[134,152],"24":"b:wuVH1AJ4iQaeAYBC4Cc/8Oz+fvMDAAAA","28":"b:wuVH1AJYiQSOAYBCwCM/sMz2fhMBAAAA","41":[134],"48":"b:wuVH1AJ4iQaeAYBC4Cc/8Oz+fvMDAAAA","53":[134,152],"58":"b:x/Vf/PL5jwf+I4BP6O8/8P////8HAAAA","61":"b:x/Vf/PL5jwf+I4BP6O8/8P////8HAAAA","64":"b:x/Vf/PL5jwf+I4BP6O8/8P////8HAAAA","68":[134],"74":[134,152],"78":"b:wuVX1AJ4iQaeAYBC4Cc/8Oz+fvMDAAAA","81":"b:wuVX1AJ4iQaeAYBC4Cc/8Oz+fvMDAAAA"},"packages/frontend/editor-ui/src/app/stores/logs.store.ts":{"19":"b://Vf///5/7f/+/////////////8HAAAA","22":"b:x/Vf/PL5nwf+I4hC6O8/8P7///8HAAAA","50":"b:x/Vf/PL5jwf+I4hC6O8/8P7///8HAAAA","54":"b://Vf///5/7f/+/////////////8HAAAA","58":[19,1,2,55,129],"62":[19,1,2,55,129],"66":[19,1,7,2,36,37,39,40,57,129,6,159],"74":[19,1,7,2,36,37,39,40,57,6,159],"78":[6],"124":[19,1,7,2,57,129,6],"130":"b:x/Vf/PL5jwf+I4hC6O8/8P7///8HAAAA","131":[19,1,7,2,36,37,39,40,57,6,159],"134":"b://Vf///5/7f/+/////////////8HAAAA","135":"b:x/Vf/PL5jwf+I4hC6O8/8P7///8HAAAA","136":[19,1,7,2,8,10,113,57,129,6,13],"137":[19,1,7,2,57,129,6]},"packages/frontend/editor-ui/src/app/stores/nodeTypes.store.ts":{"43":"b://Vf///5/7f/+/////////////8HAAAA","60":"b:guVBlAJ4iQSaAQBC4AUO8Mz2flICAAAA","61":"b:guVBlAJ4iQSSAQBC4AUO8Mz2flICAAAA","79":"b:x/Vf/LL5nwf+I4hC7O9/8Pz///8HAAAA","81":"b:h/VX/AJ4nQf+I4hC6O9/8Pz///8HAAAA","82":"b:h/VX/AJ4nQf+I4hC6O9/8Pz///8HAAAA","85":"b:guVBlAJ4iQSaAQBC4AUO8Mz2flICAAAA","87":"b:guVBlAJ4iQSaAQBC4AUO8Mz2flICAAAA","88":"b:guVBlAJ4iQSaAQBC4AUO8Mz2flICAAAA","91":"b:guVBlAJ4iQSaAQBC4AUO8Mz2flICAAAA","95":"b:x/Vf/PL5nwf+I4hP6O8/8P////8HAAAA","96":"b:x/Vf/PL5jwfeI4hD6K8/8Oz///8HAAAA","97":"b:x/Vf/PL5jwfeI4hD6K8/8Oz///8HAAAA","101":"b:x/Vf/LL5nwf+I4hC7O9/8Pz///8HAAAA","103":"b:x/Vf/LL5nwf+I4hC7O9/8Pz///8HAAAA","111":"b:x/Vf/LL5nwf+I4hC7O9/8Pz///8HAAAA","112":"b:x/Vf/LL5nwf+I4hC7O9/8Pz///8HAAAA","128":"b:1/Vf/PL5n7f/I4hO7u8/8f////8HAAAA","129":"b:1/VP/PL5j7f3A4hO5uc/8f////sHAAAA","144":"b:xvVD3IJYjQfaAYBKwOU+sNz+/5MCAAAA","145":"b:xvVD3IJYjQfSAYBKwOU+sNz+/5MCAAAA","150":[104],"151":[104],"162":"b:x/VP/PL5jwf+A4hO4Oc/8N////sHAAAA","163":"b:x/VP/PL5jwf2A4hO4Oc/8N////sHAAAA","175":"b:x/VP/PL5jwf+A4hO4Oc/8N////sHAAAA","180":"b:x/VP/PL5jwf+A4hO4Oc/8P////sHAAAA","181":"b:x/VP/PL5jwf2A4hO4Oc/8P////sHAAAA","187":"b:x/VH3IJYjQfaAYBKwOU+sNz+/5MCAAAA","188":"b:x/VH3IJYjQfSAYBKwOU+sNz+/5MCAAAA","191":"b:x/VH3IJYjQfaAYBCwOU+sNz+/5MAAAAA","203":"b:xuVD3IJYjQfaAYBKwOU+sNz+/5MCAAAA","204":"b:xuVD3IJYjQfSAYBKwOU+sNz+/5MCAAAA","207":"b:xuVD3IJYjQfaAYBCwOU+sNz+/5MAAAAA","225":"b:x/Vf/LL5nwf+I4hC7O9/8Pz///8HAAAA","229":"b:x/Vf/LL5nwf+I4hC7O9/8Pz///8HAAAA","239":[1,7,98,10,13,14],"240":[1,7,98,10,13,14],"244":[1,7,98,10,13,14],"265":[1,7,98,10,13,14],"305":"b:x/VP/PL5jwf+A4hO4Oc/8N////sHAAAA","306":"b:x/VP/PL5jwf2A4hO4Oc/8N////sHAAAA","320":"b:x/VP/PLZjwf+A4hOwOM/sN////MFAAAA","331":"b://Vf///5/7f/++/v////9/////8HAAAA","422":"b://Vf///5/7f/+/////////////8HAAAA","433":"b://Vf///5/7f/+//z//////3///8HAAAA","447":[26,145,14],"451":[1,7,148],"455":[148,149],"463":[55,152,153],"475":"b:x/Vf/PL5nwf+I4hP7O9/8P////8HAAAA","485":"b:h/VX/AJ4nQf+I4hP6O9/8P////8HAAAA","492":[102,118],"507":"b:x/VP/PL5jwf+A4hO4Oc/8P7///sHAAAA","508":"b:x/VP/PL5jwf2A4hO4Oc/8P7///sHAAAA","517":"b://Vf///5/7f/+/////////////8HAAAA","521":"b:x/VO5LKJjwf6AYBKgOc7AM//v+MHAAAA"},"packages/frontend/editor-ui/src/app/stores/npsSurvey.store.ts":{"18":"b://Vf///5/7f/+/////////////8HAAAA","27":"b://Vf///5/7f/+//j//////z///8HAAAA","35":"b:wnBAAAAAOAECAAAAAIVcgUzT/P0HAAAA","75":[55,86],"79":"b:AmAHiAAAiACOAQAAgAgAgMxSCBYCAAAA"},"packages/frontend/editor-ui/src/app/stores/posthog.store.ts":{"18":"b://Vf///5/7f/+/////////////8HAAAA","33":[55,86],"48":[55,86],"56":"b://Vf///5/7f/++/v//////////8HAAAA","60":"b:x/Vf///5/xf/++9D+/+/8Pz///8HAAAA","67":"b://Vf///5/7f/++/v//////////8HAAAA","73":[88],"159":"b://Vf///5/7f/+//j//////z///8HAAAA"},"packages/frontend/editor-ui/src/app/stores/pushConnection.store.ts":{"18":"b://Vf///5/7f/+/////////////8HAAAA","36":"b://Vf/PP5jwf+449P/e9/+P////8HAAAA","39":[96,36,39,40,55,136,71,155],"47":"b://Vf///5/7f/+/////////////8HAAAA","49":"b://Vf///5/7f/+/////////////8HAAAA","67":"b://Bf6POBnwf+A4ADhe8g8N7/r/8GAAAA","81":"b://Bf6POBnwf+A4ADhe8g8N7/r/8GAAAA","86":"b://Vf///5/7f/+/////////////8HAAAA","88":"b://Vf///5/7f/+/////////////8HAAAA","94":"b:x/Bf6PKBnwf+A4ADgO8g8Nz/r/8GAAAA","106":"b://Vf/PP5jwf+449D/e9/+P7///8HAAAA","126":"b://Vf/PP5nwf+449D/e9/+P7///8HAAAA","133":"b://Vf/PP5nwf+Y49D/e9/8P7///8HAAAA","149":[96,136,71,155],"161":[96],"174":"b://Vf///5/7f/+/////////////8HAAAA","175":"b://Vf/PP5nwf+449D/e9/+P7///8HAAAA","196":[36,39,40,55,136,71,155],"200":"b://Vf/PP5jwf+449D/e8/+P7///8HAAAA"},"packages/frontend/editor-ui/src/app/stores/ui.store.ts":{"121":"b://Vf///5/7f/+/////////////8HAAAA","182":"b://Vf///5/7f/+/////////////8HAAAA","294":"b:AGAACwgA8RGEgWUAiBgAIAwUDgkGAAAA","295":"b://Vf///5/7f/+/////////////8HAAAA","356":"b://VP/PP5zxf/A4hO7+c/8N////sHAAAA","360":"b://VP/PP5zxf/A4hO7+c/8N////sHAAAA","364":[119,126,161],"422":"b://Vf///5/7f/+/////////////8HAAAA","423":"b://Vf///5/7f/+/////////////8HAAAA","429":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","431":"b://Vf///5/7f/++/D//////z///8HAAAA","433":"b://Vf///5/7f/++/D//////z///8HAAAA","435":"b://Vf///5/7f/++/D//////z///8HAAAA","441":"b:R3AIKPCBlwP0AwABgMAQMH4tj+kGAAAA","449":"b:x/Vf/PL5nwf+I4hL6O8/8Pz///8HAAAA","450":"b:wuBXyAIAmAbeAYACgG8gwMz6LvcGAAAA","456":"b:guVBlAJ4iQSaAQBC4AUO8Mz2flICAAAA","461":"b:QuEGVARIJBSHA4IAADq7oG5ED/kHAAAA","478":"b:kkEBEAIAgBABAgAASgBCIAAAEAAAAAAA","485":"b:kkEBEAIAoBABAgAASgBCIAAAEAAAAAAA","492":"b:kkEBEAIAgBABAgAASAACIAAAEAAAAAAA","499":"b:IIEBEAIA9LABAAAADwCA9QAAABkCAAAA","506":"b:ssEBEQ4A9LEBAkgATwDC9UAAEB0DAAAA","514":"b:IIEBEAIA9LABAAAADwCA9QAAABkCAAAA","519":"b:8sEVEJ4B9JGxA0AADwjC9EgDEP0HAAAA","524":"b:ssEBEA4A9JEBAkAADwDC5EAAEB0DAAAA","532":[97,8,33],"554":"b:kkEBEAIAgBABAgAASAACIAAAEAAAAAAA","589":[118],"595":[118],"605":[53],"614":[53,54,55],"631":"b:wuBXyAIAmAbeAYACgG8gwMz6LvcGAAAA","637":"b:wuBXyAIAmAbeAYACgG8gwMz6LvcGAAAA","644":[75,77,35,54,55,86,72],"651":"b:x/Vf/PL5nwf+I4hP6O8/8P////8HAAAA","658":"b://Vf///5/7f/+//j//////z///8HAAAA","669":"b://Vf///5/7f/+//j//////z///8HAAAA","677":"b:x5AI4LKBDwZ6AIACgGcgAA7+r+EAAAAA","681":"b:wuVH3AJ4iabeA4hC4G8/8ez+fvcHAAAA","689":"b:x/Vf/PL5n6f+I4hP6O8/8f////8HAAAA","697":"b://Vf///5/7f/+//j//////z///8HAAAA","719":"b://Vf///5/7f/+//j//////z///8HAAAA","720":"b://Vf///5/7f/+//j//////z///8HAAAA","726":"b://Vf///5/7f/+//j//////z///8HAAAA","728":"b://Vf///5/7f/+//j//////z///8HAAAA","752":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","753":"b://Vf///5/7f/+/////////////8HAAAA","754":[29,50,56,127,155,156,161],"768":[95,121,86],"814":"b:IAEBEAIAwBABAgAACgAAIAAAAAAAAAAA","816":"b:IAEBEAIAwBABAgAACgAAIAAAAAAAAAAA"},"packages/frontend/editor-ui/src/app/stores/ui.utils.ts":{"4":[91,128],"12":"b://Vf///5/7f/+/////////////8HAAAA","17":"b://Vf///5/7f/+/////////////8HAAAA","23":"b://Vf///5/7f/+/////////////8HAAAA"},"packages/frontend/editor-ui/src/app/stores/workflowDocument/useWorkflowDocumentActive.ts":{"14":"b://Vf///5/7f/+/////////////8HAAAA","17":"b:x/Bf7LKBjwf+AYBCgG8owN7+7/8HAAAA","21":"b:x/Bf6PKBnwf+A4APgO8g8N//r/8GAAAA","30":"b:x/Bf6PKBnwf+A4APgO8g8N//r/8GAAAA","34":"b:R3AIKPCBlwP0AwABgMAAMB4th+kGAAAA"},"packages/frontend/editor-ui/src/app/stores/workflowDocument/useWorkflowDocumentChecksum.ts":{"12":"b://Vf///5/7f/+/////////////8HAAAA","17":"b:x/Bf6PKBnwf+A4APgO8g8N//r/8GAAAA","22":"b:x/Bf6PKBnwf+A4APgO8g8N//r/8GAAAA"},"packages/frontend/editor-ui/src/app/stores/workflowDocument/useWorkflowDocumentConnections.ts":{"33":"b://Vf///5/7f/+/////////////8HAAAA","44":"b:R3AMKPCBlwP0AwgNgMAAMB8vh+sGAAAA","49":"b:wuVH1AJYiQSOAYBCwCM/sMz2fhMBAAAA","110":[18,112,58,115,65,130,66,134,6,152],"140":[112,65,130,66,134,6,152],"182":[63],"191":"b:x/Vf/PL5nxf+I4hO7O8/8P////8HAAAA","193":"b:x/VP7PKJjwf+A4hCgOcj4N7///sHAAAA","197":"b:x/VP/PLZjwf+A4hKwOc/8Nz///sHAAAA","204":"b:x/VP7PKJjwf+A4hCgOcj4N7///sHAAAA","215":"b:R3AMKPCBlwP0AwgNgMAAMB8vh+sGAAAA","219":"b:wuVH1AJYiQSOAYBCwCM/sMz2fhMBAAAA","223":[18,112,58,115,65,130,66,134,6,152],"227":[112,65,130,66,134,6,152],"240":[63]},"packages/frontend/editor-ui/src/app/stores/workflowDocument/useWorkflowDocumentDescription.ts":{"12":"b://Vf///5/7f/+/////////////8HAAAA","17":"b:R3AIKPCBlwP0AwANgMAAMB8vh+kGAAAA","25":"b:R3AIKPCBlwP0AwANgMAAMB8vh+kGAAAA"},"packages/frontend/editor-ui/src/app/stores/workflowDocument/useWorkflowDocumentExpression.ts":{"6":"b://Vf///5/7f/+/////////////8HAAAA","11":"b:x/Vf/PL5nxf+I4hO7O8/8P////8HAAAA"},"packages/frontend/editor-ui/src/app/stores/workflowDocument/useWorkflowDocumentGraph.ts":{"16":"b://Vf///5/7f/+/////////////8HAAAA","21":"b:x/VP3MJ4jafeA4BK5Oc/8fz+//MDAAAA","29":"b:x/VD3IJYjQfaAYBKwOU+sNz+/5MCAAAA","37":"b:xvVH3IJ4jQfaA4hK4Oc/8Pz+/9MHAAAA","41":[152],"48":[58,115,65],"78":[7,2,57],"82":"b:wvVABAAIjQJSAABIgKAiAIT4OgMCAAAA","86":"b:wvVABAAIjQJSAABIgKAiAIT4OgMCAAAA","94":"b:xvVD3AJYiQbKAYBCwOEesNz+/hIAAAAA","109":"b:xZAAQIAACAJaAIAAAEUAAAD4LgAAAAAA","112":"b:xZAAQIAAAAJYAIAAAEEAAAD4LgAAAAAA","120":"b:xvVH3MJYjafaAYBKxOU+sdz+/5MCAAAA","123":"b:xuVH3IJYjQfaAYBCwOE+sNz+/5MAAAAA","132":"b:RZAAQIAACABaAIAAAEUAAAD4LgAAAAAA"},"packages/frontend/editor-ui/src/app/stores/workflowDocument/useWorkflowDocumentHomeProject.ts":{"9":"b://Vf///5/7f/+/////////////8HAAAA","14":"b:x/Vf/PL5nwf+I4BP6O8/8P////8HAAAA","22":"b:x/Vf/PL5nwf+I4BP6O8/8P////8HAAAA"},"packages/frontend/editor-ui/src/app/stores/workflowDocument/useWorkflowDocumentIsArchived.ts":{"12":"b://Vf///5/7f/+/////////////8HAAAA","17":"b:R3AIKPCBlwP0AwANgMAAMB8vh+kGAAAA","22":"b:R3AIKPCBlwP0AwANgMAAMB8vh+kGAAAA"},"packages/frontend/editor-ui/src/app/stores/workflowDocument/useWorkflowDocumentMeta.ts":{"13":"b://Vf///5/7f/+/////////////8HAAAA","18":"b:x3EIOPCBnwP0AwgNgMACsB8vl/kGAAAA","26":"b:R3AIKPCBlwP0AwANgMAAMB8vh+kGAAAA","30":"b:gkEAEAAAiAAAAggAAAACsAAAEBAEAAAA"},"packages/frontend/editor-ui/src/app/stores/workflowDocument/useWorkflowDocumentName.ts":{"16":"b://Vf///5/7f/+/////////////8HAAAA","21":"b:x/Vf/PL5nwf+I4hP6O8/8P////8HAAAA","27":"b:x/Vf/PL5nwf+I4hP6O8/8P////8HAAAA"},"packages/frontend/editor-ui/src/app/stores/workflowDocument/useWorkflowDocumentNodeGroups.ts":{"41":"b://Vf///5/7f/+/////////////8HAAAA","48":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","50":"b:xvVP3IJ4jQfeA4BK4Oc/8Pz+//sDAAAA","60":"b:R3AIKPCBlwP0AwANgMAAMB8vh+kGAAAA","61":[66],"68":[66],"84":[66],"93":"b:R3AIKPCBlwP0AwANgMAAMB8vh+kGAAAA","97":[66],"113":[66],"132":[66],"136":[66],"150":[66],"161":[66],"166":[66],"188":[66],"192":"b:xvVP3IJ4jQfeA4BK4Oc/8Pz+//sDAAAA","197":[112,61,120,63,65,130,66,134,6,152],"200":[66],"209":"b:AAAAAJABgICAAAAMAAAAAAIBAAgAAAAA"},"packages/frontend/editor-ui/src/app/stores/workflowDocument/useWorkflowDocumentNodeMetadata.ts":{"20":"b://Vf///5/7f/+/////////////8HAAAA","29":"b:R3AMKPCBl4P0AwANhMAAMB8vh+sGAAAA","34":"b:x/VP/PL5j6f+A4hO5Oc/8f////sHAAAA","42":"b:xvVH3IJ4jQfaA4hK4Oc/8Pz+/9MHAAAA","52":[137],"65":[112,61,120,63,65,130,66,134,6,152],"75":[131,14,153],"89":"b:R3AMKPCBl4P0AwANhMAAMB8vh+sGAAAA","94":"b:wuVH1AJ4iaSaA4hC4Cc/8ez2flMHAAAA","100":"b:R3AMKPCBhwP0AwAMhMAAIB8vh+sGAAAA","105":[112,61,120,63,65,130,66,134,6,152],"109":[131,14,153],"113":"b:xvVH3IJ4jQfaA4hK4Oc/8Pz+/9MHAAAA","117":"b:AkFGxAIYiQAKAIBCwEEYAMz6OgMAAAAA","138":[137],"142":"b:x5BK4LKBDwd6AIACgGcgAA7+r+EAAAAA","146":"b:x5BIwLKBDwd4AIACgGMgAA7+r+EAAAAA","150":"b:x5BK4LKBDwd6AIACgGcgAA7+r+EAAAAA","154":"b:xvVD3IJYjQfaAYBKwOU+sNz+/5MCAAAA"},"packages/frontend/editor-ui/src/app/stores/workflowDocument/useWorkflowDocumentNodes.ts":{"76":"b://Vf///5/7f/+/////////////8HAAAA","92":"b:wvVH3EJYiaZeA4hCwOEescz6evMFAAAA","116":"b:R3AMKPCBlwP0AwANhMAAMB8vh+sGAAAA","143":"b:R3AMKPCBhwP0AwAMhMAAIB8vh+sGAAAA","147":"b:wuVH1AJ4iaSaA4hC4Cc/8ez2flMHAAAA","167":[61,120,63],"168":[61,120,63],"182":[112,65,130,66,134,6,152],"183":[112,65,130,66,134,6,152],"184":[112,65,130,66,134,6,152],"203":"b://Vf///5/7f/+/////////////8HAAAA","227":"b:x/VP/PL5jwf+A4hO4Oc/8P////sHAAAA","242":"b:x/VP/PL5jwf+A4hO4Oc/8P////sHAAAA","256":"b:x/VP/PL5jwf+A4hO4Oc/8P////sHAAAA","270":"b:x/VP/PL5j6f+A4hO5Oc/8f////sHAAAA","273":"b:x/VP/PL5j6f+A4hO5Oc/8f////sHAAAA","276":"b:x/VP/PL5jwf+A4hO4Oc/8P////sHAAAA","280":"b:x/VP/PL5jwf+A4hO4Oc/8P////sHAAAA","283":"b:QAAAAAAAAKAGAAAEAAABAUQAAAEAAAAA","286":"b:QAAAAAAAAKAGAAAEAAABAUQAAAEAAAAA","293":"b://Vf///5/7f/+/////////////8HAAAA","301":"b://Vf///5/7f/+/////////////8HAAAA","302":"b:R3AMKPCBhwP0AwAMhMAAIB8vh+sGAAAA","303":"b:R3AMKPCBhwP0AwAMhMAAIB8vh+sGAAAA","309":"b:x/VP/PL5n6f+A4hP5Oc/8f////sHAAAA","343":"b:x/VP/PL5n6f+A4hP5Oc/8f////sHAAAA","371":"b:wuVH1AJ4iQSaA4hC4Cc/8Oz2flMHAAAA","373":"b:x/Vf/PL5nwf+I4hP7O8/8P////8HAAAA","374":"b:x/VP/PL5jwf+A4hO4Oc/8P7///sHAAAA","380":"b:guVBlAJ4iQSaAQBC4AUO8Oz2flICAAAA","381":"b:guVBlAIwiASKAAAC4AEOsMz2fhIAAAAA","388":"b:x/Vf/PL5nwf+I4hO6O8/8P////8HAAAA","392":"b://Vf///5/7f/+/////////////8HAAAA","396":"b:xvVD3AJYjQbaAYBCwOU+sMz+/xMCAAAA","397":"b:xvVD3AJYjQbaAYBCwOU+sMz+/xMCAAAA","400":"b:QgAAAAAAAAACAwAAAAAAgEgICPAAAAAA","408":"b:R3AMKPCBlwP0AwANhMAAMB8vh+sGAAAA","412":"b:wuVH1AJ4iaSaA4hC4Cc/8ez2flMHAAAA","416":[61,120,63],"420":[112,65,130,66,134,6,152],"424":"b:gmFD1AJYiQAKAIBCwEEYsMz6ehMAAAAA","425":"b:gmFD1AJYiQAKAIBCwEEYsMz6ehMAAAAA","450":"b:gEFBlABYiAAKAAAAgAAAsMxQehIAAAAA","451":"b:gEFBlABYiAAKAAAAgAAAsMxQehIAAAAA","469":[18,57,114,58,115,65,130,66,134,72],"470":[18,57,114,58,115,65,130,66,134,72],"493":[18,57,114,58,115,65,130,66,134,72],"494":[18,57,114,58,115,65,130,66,134,72],"506":"b:wkEEGAIAiKACAgAAAAACsUgIEPAAAAAA","507":"b:wkEEGAIAiKACAgAAAAACsUgIEPAAAAAA","523":"b:x/VP/PL5jwf+A4hO4Oc/8P7///sHAAAA","524":"b:x/VP/PL5jwf+A4hO4Oc/8P7///sHAAAA","551":[63],"561":[137],"562":[137],"604":[1,7,27,28,98,8,33,51,55,113,14,148],"611":[1,7,27,28,98,8,33,51,55,113,14,148],"630":[1,7,8,113]},"packages/frontend/editor-ui/src/app/stores/workflowDocument/useWorkflowDocumentNodesIssues.ts":{"23":"b://Vf///5/7f/+/////////////8HAAAA","24":"b:x/Vf6PKJnwf+A4ADgO8i8Nz/7/8HAAAA","25":"b:x/VP6PKJjwf+A4ACgOci4Nz/7/sHAAAA","41":"b:xuVNJAAIDAOQAghAgIQCgFT4WhgHAAAA","42":"b:xuVNJAAIDAOQAghAgIQCgFT4WhgHAAAA","54":[18,20],"60":[18],"69":[18],"73":[18],"108":"b:x/VP/PL5jwf+A4hO4Oc/8P////sHAAAA","114":"b:x/VP/PL5j6f+A4hO5Oc/8f////sHAAAA","117":"b:x/VP/PL5j6f+A4hO5Oc/8f////sHAAAA","120":"b:x/VP/PL5jwf+A4hO4Oc/8P////sHAAAA","123":"b:QAAAAAAAAKAGAAAEAAABAUQAAAEAAAAA","126":"b:QAAAAAAAAKAGAAAEAAABAUQAAAEAAAAA","132":"b://Vf///5/7f/+/////////////8HAAAA","140":"b:x/VP/PL5n6f+A4hP5Oc/8f////sHAAAA"},"packages/frontend/editor-ui/src/app/stores/workflowDocument/useWorkflowDocumentParentFolder.ts":{"9":"b://Vf///5/7f/+/////////////8HAAAA","14":"b:x/Vf/PL5nwf+I4BP6O8/8P////8HAAAA","22":"b:x/Vf/PL5nwf+I4BP6O8/8P////8HAAAA"},"packages/frontend/editor-ui/src/app/stores/workflowDocument/useWorkflowDocumentPinData.ts":{"38":"b:AEAAAAAAAABAAAAAACAAAMDyDgABAAAA","43":"b:AEAAAAAAAABAAAAAACAAAMDyDgABAAAA","56":"b:R3AIKPCBl4P0AwANgMAAMB8vh+kGAAAA","57":"b:AEAAAAAAAABQAAAIAAAAABAoBwEEAAAA","58":"b:AEAAAAAAAABQAAAIAAAAABAoBwEEAAAA","68":"b:xvVL3LJZjQfaA4BKwOU+sNz+/5MDAAAA","72":"b:AEAAAAAAAABAAAAIACAAAND6DwEBAAAA","76":"b:AEAAAAAAAABAAAAAACAAAMDyDgABAAAA","79":"b:AEAAAAAAAABAAAAAACAAAMDyDgABAAAA","89":"b://Vf///5/7f/+/////////////8HAAAA","94":"b:R3AIKPCBl4P0AwANgMAAMB8vh+kGAAAA","99":"b:AEAAAAAAAABAAAAAACAAAMDyDgABAAAA","107":[134,137],"116":[131,14,153],"140":"b:R3AIKPCBl4P0AwANgMAAMB8vh+kGAAAA","144":"b:AEAAAAAAAABAAAAAACAAAMDyDgABAAAA","148":[137],"152":[131,14,153],"156":"b:x/Vf/PL5jxf+A4hO5O8/8P////8HAAAA","160":"b:xvVH3IJYjQfeAYBKwOU+sNz+/5MCAAAA","179":"b:x/VP/PL5jwf+A4hO4Oc/8P////sHAAAA","185":"b:x/VP/PL5j6f+A4hO5Oc/8f////sHAAAA","188":"b:x/VP/PL5j6f+A4hO5Oc/8f////sHAAAA","191":"b:x/VP/PL5jwf+A4hO4Oc/8P////sHAAAA","194":"b:QAAAAAAAAKAGAAAEAAABAUQAAAEAAAAA","197":"b:QAAAAAAAAKAGAAAEAAABAUQAAAEAAAAA","203":"b://Vf///5/7f/+/////////////8HAAAA","211":"b:x/VP/PL5n6f+A4hP5Oc/8f////sHAAAA"},"packages/frontend/editor-ui/src/app/stores/workflowDocument/useWorkflowDocumentPublicationStatus.ts":{"6":"b://Vf///5/7f/+/////////////8HAAAA","10":[90,91,36,39,40,55,129,136,71,155]},"packages/frontend/editor-ui/src/app/stores/workflowDocument/useWorkflowDocumentRenderData.ts":{"106":"b:x/VP/PL5jwf+A4hO4Oc/8P////sHAAAA","112":"b:x/VP/PL5jwf+A4hO4Oc/8P////sHAAAA","122":"b:x/VP/PL5jwf+A4hO4Oc/8N////sHAAAA","128":"b:x/VP/PL5jwf+A4hO4Oc/8P////sHAAAA","149":"b:x/VP/PL5jwf+A4hO4Oc/8N////sHAAAA","170":"b:x/VP/PL5jwf+A4hO4Oc/8N////sHAAAA","221":"b:x/VP/PL5jwf+A4hO4Oc/8P////sHAAAA","243":"b:x/VP/PL5jwf+A4hO4Oc/8P////sHAAAA","255":[109,115,66,133,68,151,160,162],"291":"b:x/VP/PL5jwf+A4hO4Oc/8N////sHAAAA","325":"b:x/VP/PL5jwf+A4hO4Oc/8P////sHAAAA","346":"b:x/VP/PL5jwf+A4hO4Oc/8P////sHAAAA","349":"b:x/VP/PL5jwf+A4hO4Oc/8P////sHAAAA","352":"b:x/VP/PL5jwf+A4hO4Oc/8N////sHAAAA","356":"b:x/VP/PL5jwf+A4hO4Oc/8N////sHAAAA","360":"b:x/VP/PL5jwf+A4hO4Oc/8P////sHAAAA","364":"b:x/VP/PL5jwf+A4hO4Oc/8N////sHAAAA","368":"b:x/VP/PL5jwf+A4hO4Oc/8N////sHAAAA","372":"b:x/VP/PL5jwf+A4hO4Oc/8P////sHAAAA","383":"b:x/VP/PL5jwf+A4hO4Oc/8N////sHAAAA","390":"b:x/VP/PL5jwf+A4hO4Oc/8P////sHAAAA","393":"b:QgAAAJABgAC2AAAEAAABgEwDAOkEAAAA","396":[90,112,65,130,66,134,6,152],"409":"b:x/Vf/PL5nwf+I4hO6O8/8P////8HAAAA","419":"b:wvVH3EJ4iQfeA4BO4Oc/8O7+fvsHAAAA","452":"b:QgAAAJABgACwAAAAAAAAgAoDAOgEAAAA","464":"b:x/Vf/PL5nwf+I4hO6O8/8P////8HAAAA","485":[115,66,133,151],"486":[109,115,66,133,68,151,160,162],"520":"b:x/Vf/PL5nwf+I4hO6O8/8P////8HAAAA","523":"b:x/Vf/PL5nwf+I4hO6O8/8P////8HAAAA","526":"b:x/VP/PL5jwf+A4hO4Oc/8N////sHAAAA","529":"b:x/VP/PLZjwb+A4hOwOM/sN////MFAAAA","532":"b:x/Vf/PL5nwf+I4hO6O8/8P////8HAAAA","551":"b:x/Vf/PL5nwf+I4hO6O8/8P////8HAAAA","554":"b:x5AI4LKBDwd6AIACgGcgAA7+r+EAAAAA","557":"b:x/VP/PL5jwf+A4hO4Oc/8N////sHAAAA","560":"b:x/Vf/PL5nwf+I4hO6O8/8P////8HAAAA","563":"b:x/Vf/PL5nwf+I4hO6O8/8P////8HAAAA","566":"b:x/Vf/PL5nwf+I4hO6O8/8P////8HAAAA","569":"b:x/Vf/PL5nwf+I4hO6O8/8P////8HAAAA","572":"b:x/Vf/PL5nwf+I4hO6O8/8P////8HAAAA","575":"b:x/Vf/PL5nwf+I4hO6O8/8P////8HAAAA"},"packages/frontend/editor-ui/src/app/stores/workflowDocument/useWorkflowDocumentScopes.ts":{"13":"b://Vf///5/7f/+/////////////8HAAAA","18":"b:x/Vf/PL5nwf+I4BP6O8/8P////8HAAAA","23":"b:x/Vf/PL5nwf+I4BP6O8/8P////8HAAAA"},"packages/frontend/editor-ui/src/app/stores/workflowDocument/useWorkflowDocumentSettings.ts":{"19":"b://Vf///5/7f/+/////////////8HAAAA","24":"b:R3AIKPCBlwP0AwANgMAAMB8vh+kGAAAA","33":"b:R3AIKPCBlwP0AwANgMAAMB8vh+kGAAAA","41":"b:x/Bf6LKBjwf+AYACgG8gwN7+7/cHAAAA"},"packages/frontend/editor-ui/src/app/stores/workflowDocument/useWorkflowDocumentSharedWithProjects.ts":{"13":"b://Vf///5/7f/+/////////////8HAAAA","18":"b:R3AIKPCBlwP0AwANgMAAcB8vh+kGAAAA","29":"b:R3AIKPCBlwP0AwANgMAAcB8vh+kGAAAA"},"packages/frontend/editor-ui/src/app/stores/workflowDocument/useWorkflowDocumentTags.ts":{"15":"b:R3AIKPCBlwP0AwANgMIYMB8vj+0HAAAA","16":[116,71,154,160],"19":"b://Vf///5/7f/+/////////////8HAAAA","24":"b:R3AIKPCBlwP0AwANgMIYMB8vj+0HAAAA","29":[154],"37":"b:R3AIKPCBlwP0AwANgMAAMB8vh+0GAAAA","41":[105,115,116,146,14,147,71,160],"46":[154]},"packages/frontend/editor-ui/src/app/stores/workflowDocument/useWorkflowDocumentTimestamps.ts":{"19":"b://Vf///5/7f/+/////////////8HAAAA","26":"b:R3AIKPCBlwP0AwANgMAAMB8vh+kGAAAA","31":"b:x/Bf6PKBnwf+A4APgO8g8N//r/8GAAAA","36":"b:R3AIKPCBlwP0AwANgMAAMB8vh+kGAAAA","40":"b:x/Bf6PKBnwf+A4APgO8g8N//r/8GAAAA"},"packages/frontend/editor-ui/src/app/stores/workflowDocument/useWorkflowDocumentUsedCredentials.ts":{"13":"b://Vf///5/7f/+/////////////8HAAAA","18":"b:R3AIKPCBlwP0AwANgMAAMB8vh+kGAAAA","26":"b:R3AIKPCBlwP0AwANgMAAMB8vh+kGAAAA","27":[27,47,103,49,50,55,6,73]},"packages/frontend/editor-ui/src/app/stores/workflowDocument/useWorkflowDocumentVersionData.ts":{"14":"b://Vf///5/7f/+/////////////8HAAAA","20":"b:x/Bf6PKBnwf+A4APgO8g8N//r/8GAAAA","32":"b:x/Bf6PKBnwf+A4APgO8g8N//r/8GAAAA"},"packages/frontend/editor-ui/src/app/stores/workflowDocument/useWorkflowDocumentViewport.ts":{"13":"b://Vf///5/7f/+/////////////8HAAAA","18":"b:QgAAAJABgACwAwAMAAgAsAoDAOgGAAAA","26":"b:QgAAAJABgACwAwAMAAgAsAoDAOgGAAAA"},"packages/frontend/editor-ui/src/app/stores/workflowDocument/useWorkflowDocumentWorkflowObject.ts":{"12":"b://Vf///5/7f/+/////////////8HAAAA","26":"b:QAAAAJABgAGwAAAMAAAAAAsDAOgEAAAA","46":"b:x/VP/PL5n6f+A4hP5Oc/8f////sHAAAA","50":"b:x/VP/PLZn4f+A4hPwOM/sN////sHAAAA","54":"b:x/Vf/PL5nwf+I4hP6O8/8P////8HAAAA","58":"b:R3AIKPCBlwP0AwANgMAAMB8vh+kGAAAA","62":"b:QMAGQAAAAACAAYAAACI5AEgADgIBAAAA","81":[18,131,14,153],"87":[18,131,14,153]},"packages/frontend/editor-ui/src/app/stores/workflowDocument.store.ts":{"115":"b://Vf///5/7f/+/////////////8HAAAA","140":[56,131,137,68,69,6,157,158,159],"149":[56,131,137,68,69,6,157,158,159],"162":"b://Vf///5/7f/+/////////////8HAAAA","176":"b://Vf///5/7f/+/////////////8HAAAA","177":"b://Vf///5/7f/+/////////////8HAAAA","186":"b:x/Vf/PL5nwf+I4hP6O8/8P////8HAAAA","200":"b:R3AIKPCBlwP0AwANgMAAMB8vh+kGAAAA","209":"b:gEFBlABYiAAKAAAAgAAAsMxQehIAAAAA","212":"b:x/VP/PL5n6f+A4hP5Oc/8f////sHAAAA","224":"b:x/VP/PLZn4f+A4hPwOM/sN////sHAAAA","250":"b:wuVH3AJ4iaaeA4hC4Gc/8ez+fvMHAAAA","251":"b:wuVH1AJYiQSOAYBCwCM/sMz2fhMBAAAA","252":[66],"254":"b:x/VP/PL5n6f+A4hP5Oc/8f////sHAAAA","260":[63],"267":"b:x/Bf6IKBjwb+AYACgG8gwM7+r/cGAAAA","268":"b:x/BP6IKBjwb+AYACgGcgwM7+r/MGAAAA","288":[66],"301":"b:QAAAAAAAAAEwAAAAAAAAAAkDAOAEAAAA","353":[90,91,36,39,40,55,129,136,71,155],"394":"b:x/VP/PL5jxf+A4hO5Oc/8P////sHAAAA","409":"b:x9Ae4LKBDwZ6AIACgGcgAMz+r+EBAAAA","489":"b:AAAAAJABgACQAAAABAAAAAgDAMgEAAAA","505":"b://Vf///5/7f/+/////////////8HAAAA","507":"b://Vf///5/7f/+/////////////8HAAAA","516":"b://Vf///5/7f/+/////////////8HAAAA","533":"b://Vf///5/7f/++/v//////////8HAAAA","537":"b://Vf///5/7f/++/v//////////8HAAAA"},"packages/frontend/editor-ui/src/app/stores/workflowExecutionState.store.ts":{"86":"b://Vf///5/7f/+/////////////8HAAAA","102":"b://Vf///5/7f/+/////////////8HAAAA","103":"b://Vf///5/7f/+/////////////8HAAAA","163":"b:x/Vf/PL5nwf+I4hP6O8/8P////8HAAAA","176":"b:x5BK4LKBDwd6AIAOgGcgAA/+r+EEAAAA","200":"b:1/Vf/PL5n7f/I4hO7u8/8f////8HAAAA","227":"b://Vf///5/7f/+/////////////8HAAAA","234":"b:x/VP/PL5jwf+A4hO4Oc/8P////sHAAAA","241":"b:1/VP/PL5j7f/A4hO7uc/8d////sHAAAA","251":"b:xvVH3IJYjQbaAYBCwOU+sNz+/xMCAAAA","257":"b:x/Vf/PL5nwf+I4hC6O8/8P7///8HAAAA","267":[141,144,146,70],"274":"b://Vf///5/7f/+/////////////8HAAAA","295":"b:x/Vf/PL5nwf+I4hO6O8/8P////8HAAAA","307":"b:x5AI4LKBDwd6AIACgGcgAA7+r+EAAAAA","320":"b:x/Vf/PL5nwf+I4hO6O8/8P////8HAAAA","326":"b:x/Vf/PL5nwf+I4hO6O8/8P////8HAAAA","332":"b:x/Vf/PL5nwf+I4hO6O8/8P////8HAAAA","339":"b:x/Vf/PL5nwf+I4hO6O8/8P////8HAAAA","345":"b:xvVL3LJZjQfaAYBKwOU+sNz+/5MCAAAA","354":"b:x/Vf/PL5nwf+I4hO6O8/8P////8HAAAA","384":"b:x/VP/PL5jwf+A4hC4Oc/8P////sHAAAA","392":"b:x/VP/PL5jwf+A4hC4Oc/8P////sHAAAA","402":"b:x/VP/PL5j6f+A4hO5Oc/8f////sHAAAA","405":"b:x/VP/PL5j6f+A4hO5Oc/8f////sHAAAA","408":"b:x/VP/PL5jwf+A4hC4Oc/8P////sHAAAA","412":"b:x/VP/PL5jwf+A4hC4Oc/8P////sHAAAA","415":"b:QAAAAJABgKCWAAAEAAABAUwDAMkEAAAA","418":[112,61,120,63,65,130,66,134,6,152],"425":"b://Vf///5/7f/+/////////////8HAAAA","439":"b:x/VP/PL5n6f+A4hP5Oc/8f7///sHAAAA","475":"b:x/Vf/PL5nwf+I4hP6O8/8P////8HAAAA","504":"b:x5BK4LKBDwd6AIAOgGcgAA/+r+EEAAAA","532":"b:x5AI4LKBDwd6AIACgGcgAAz+r+EAAAAA","541":"b:x/Vf/KL5nwf+I4hC6O8/8P7///8HAAAA","577":"b:x5BK4LKBDwZ6AIACgGcgAA7+r+EAAAAA","594":"b:x/Vf/KL5nwf+I4hC6O8/8P7///8HAAAA","617":"b:QgAAAAAAAAAGAAAAAAABgEwIAPEAAAAA","624":"b:xgBIIAAACAIQAAAAgAAAAAIACAAAAAAA","629":[137],"634":"b:x/Vf/AL4nwf+I4hC6O8/8P7///8HAAAA","647":"b:R3AIKACAlwP0AwAAgMAAMBwth+kGAAAA","678":"b:x/Vf/AL4nwf+I4hC6O8/8P7///8HAAAA","696":[83,73,162],"701":[83,73,162],"735":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","748":[131,14,153],"786":[131,14,153],"811":"b:x/Vf/PL5nwf+I4hP6O8/8P////8HAAAA","845":[6],"969":"b:x/Vf/PL5nwf+I4hP6O8/8P////8HAAAA","992":"b://Vf///5/7f/+/////////////8HAAAA","996":"b:1/Vf/PL5n7f/I4hO7u8/8f////8HAAAA"},"packages/frontend/editor-ui/src/app/stores/workflowSave.store.ts":{"15":"b://Vf///5/7f/+/////////////8HAAAA","26":"b:wuVX3AJ4iQbeA4BC4G8/8Oz+fvcHAAAA","30":"b:wuBXyAIAiAbeAYACgG8ggMz6LvcGAAAA","55":"b:AiAGiBABiALMAIAAAEAAgIx6KPECAAAA","63":[36,40]},"packages/frontend/editor-ui/src/app/stores/workflows.store.ts":{"43":"b://Vf///5/7f/+/////////////8HAAAA","60":"b:x/Vf/PL5nwf+I4hD6O8/8P7///8HAAAA","70":"b:x/Vf/PL5nwf+I4hD6O8/8P7///8HAAAA","71":"b:x/Bf6PKBnwf+A4ADgO8g8N7/r/8GAAAA","80":"b://Vf///5/7f/+//z//////z///8HAAAA","85":"b:xvVD3AJYjQbaAYBCwOU+sMz+/xMCAAAA","89":"b:xvVD3AJYjQbaAYBCwOU+sMz+/xMCAAAA","99":[83,73,162],"120":[160],"127":"b:x/Vf/PL5nwf+I4hD6O8/8P7///8HAAAA","165":"b:x/Vf/PL5nwf+I4hP6O8/8P////8HAAAA","169":"b:x/Vf/PL5nwf+I4hP6O8/8P////8HAAAA","180":"b:R3AIKPCBlwP0AwABgMAAMB4th+kGAAAA","187":[155,161],"203":[155,161],"218":[50,127,155,156,161],"230":[155,161],"234":[52],"271":[56,131,137,68,69,6,157,158,159],"285":"b:wuBXyAIAuAfeA4ACgG8gwMz6LvcGAAAA","286":"b:wuBHyAIAiAfeA4ACgGcgwMz6LvMGAAAA","294":"b:wuBXwAIAmASaA4ACgC8gwMzSLlcGAAAA","328":"b:AiAGiAAAuAPMAIAAAEAAgIx6KPECAAAA","371":[50,127,152,155,156,161],"393":[161],"472":"b:x5BK4IKBDwZ6AIACgGcgAA7+r+EEAAAA","499":"b:x5AI4LKBDwZ6AIACgGcgAA7+r+EAAAAA"},"packages/frontend/editor-ui/src/app/stores/workflowsList.store.ts":{"27":"b://Vf///5/7f/+/////////////8HAAAA","39":[152,153],"40":[152,153],"44":"b:x/Vf/PL5jwf+I4hD6O8/8P7///8HAAAA","58":"b:x/Bf6fKB/wf+A4APgP8g8N7/r/8GAAAA","68":[155,161],"73":"b:x/Vf/PL5jwf+I4BP6O8/8P////8HAAAA","98":[50,127,155,156,161],"111":[155,161],"125":"b:AAAACwwA8ACAGWAAABgAMAAAAAsGAAAA","153":"b:AAAACQAA8ACAAQAAABgAAAAAAAMGAAAA","154":"b:AAAACQAA8ACAAQAAABgAAAAAAAMGAAAA","165":"b:AAAACwwA8ACAGWAAABgAMAAAAAsGAAAA","186":[56,127,161],"213":[56,127,161],"234":"b:R3AIKPCB5wP0AwABgMgAsBwth+sGAAAA","240":[118],"244":"b:x/Vf//75/wf+O+hD6P8/8Pz///8HAAAA","250":"b:wuVX9AJ4iQSaIYBC6C8/8Ozyfl4DAAAA","256":[155,161],"262":[155,161],"283":[155,161]},"packages/frontend/editor-ui/src/app/utils/aiUtils.ts":{"57":[1,6],"82":[6],"87":[6],"236":[1,6],"248":[1,6],"263":[1,6],"264":[1,6],"274":[19,1,7,2,36,37,39,40,57,129,6,159],"286":[6],"300":"b:x/VP/PL5jwf+A4hO4Oc/8P7///sHAAAA"},"packages/frontend/editor-ui/src/app/utils/credentialOnlyNodes.ts":{"5":"b:1/VP/PL5j7f/A4hO5uc/8f////sHAAAA","9":[104],"13":"b:x/Vf/LL5nwf+I4hC6O8/8Pz///8HAAAA","17":"b:x/Vf/LL5nwf+I4hC6O8/8Pz///8HAAAA","52":"b:x/Vf/LL5nwf+I4hC6O8/8Pz///8HAAAA"},"packages/frontend/editor-ui/src/app/utils/formatters/dateFormatter.ts":{"4":"b:RIAAQIAADQFYAIACgEUgAACqr4EAAAAA","15":[56,131,137,68,69,6,157,158,159],"27":[19,1,7,2,36,37,39,40,57,129,6],"29":[19,1,7,2,36,37,39,40,57,129,6]},"packages/frontend/editor-ui/src/app/utils/injectStrict.ts":{"4":"b://Vf///5/7f/++/v//////////8HAAAA"},"packages/frontend/editor-ui/src/app/utils/mappingUtils.ts":{"11":"b:RtBIQKAADQdYAIAKgGUoAJT+74EAAAAA","15":"b:RtBIQKAADQdYAIAKgGUoAJT+74EAAAAA","16":"b:RtBIQKAADQdYAIAKgGUoAJT+74EAAAAA","29":"b:RlBIQKAADQNYAAAKgGUgAJT6r4EAAAAA","33":"b:RhBIQKAADQNYAAAKgEUgAID6r4EAAAAA","53":"b:RlBIQKAADQNYAAAKgGUgAJT+74EAAAAA","82":[141],"96":[141]},"packages/frontend/editor-ui/src/app/utils/nodeIcon.ts":{"81":"b:x/VP/PL5jxf+A4BO5Oc/8N////sHAAAA","95":"b:x/VP/PL5jxf+A4hO5Oc/8N////sHAAAA","105":"b:x/VP/PL5jxf+A4BO5Oc/8N////sHAAAA","113":"b:wvUFlEB4hwDaAggCJOUKsMz83hAFAAAA","118":"b:x/VP/PL5jxf+A4hO5Oc/8N////sHAAAA","125":"b:wvUFlEB4hwDaAggCJOUKsMz83hAFAAAA","131":"b:x/VP/PL5jxf+A4BO5Oc/8N////sHAAAA","152":"b:x/VP/PL5jxf+A4hO5Oc/8N////sHAAAA"},"packages/frontend/editor-ui/src/app/utils/nodeTypes/nodeTypeTransforms.ts":{"8":"b://Vf///5/7f/++/v////9/////8HAAAA","29":"b://Vf///5/7f/++/v////9/////8HAAAA","32":"b://Vf///5/7f/++/v////9/////8HAAAA"},"packages/frontend/editor-ui/src/app/utils/nodeTypesUtils.ts":{"43":"b:kkUBEAIAgBABAgAASgACIAAAEAAAAAAA","46":"b:kkUBEAIAgBABAgAASgACIAAAEAAAAAAA","54":"b:xnVPfELZjgNSAghOxOEOsMoO2hAHAAAA","58":[83,14,148,73],"61":[83,14,148,73],"65":[26,94,51,139,145,147,161,162],"77":"b:x/VP/PL5jwf+A4hO4Oc/8P////sHAAAA","111":[22,43,47,103,105,51,145,162],"122":[22,43,47,103,105,51,145,162],"134":"b:x/Vf/PL5nwf+I4hD6O8/8Pz///8HAAAA","135":"b:x/VP/PL5jwf+A4hC4Oc/8Nz///sHAAAA","140":[73,162],"141":[73,162],"142":[73,162],"146":[73,162],"149":"b:AEAAAAAAABAAAAAAAAAAAADwOAIAAAAA","150":[60,140,141,142,143,14,147,148,149],"153":"b:1vVH3MJYjbfbA4BKzuU+sdz+/5MCAAAA","173":"b:xvVP3LJZjQfaAYBKwOU+sNz+/5MCAAAA","174":"b:RtBIQLABDQdYAIAKgGUgAJT+r4EAAAAA","182":"b:wmEBEEAYgAAAAAAAAAACsAAAEhMAAAAA","194":"b:wmUBFABYiABAAABAxKECsIgAWhABAAAA","214":"b:xvVHnMBYjafaAYBCxOU+sdz8/5MDAAAA","221":"b:AEEFHEAYiABAAABAgIAAMAAAWhAAAAAA","224":"b:AEEFFEAQiABAAABAgIAAMAAAWhAAAAAA","241":"b:xvVFjIBYjafaAYBAxOU2gdz8/5MBAAAA","246":"b:wmUEDABICAAAAABAxCECgIgAAAABAAAA","250":[26,27,94,8,43,103,51],"255":[26,27,94,8,43,103,51],"265":[26,94,43,103,51],"282":"b:ksEBEAIAgBABAoAASgACMAAAEAAAAAAA","296":[16,28,8,55,124,125,14,148],"300":[16,28,8,55,124,125,14,148],"303":[16,28,8,55,124,125,14,148],"330":[16,28,8,55,124,125,14,148],"331":[16,28,8,55,124,125,14,148],"335":"b:AEEBFAAYgABAAAAAAIAAMAAAWhAAAAAA","341":"b:AEEBFAAYgABAAAAAAIAAMAAAWhAAAAAA","350":[16,28,8,55,124,125,14,148],"357":[16,28,8,55,124,125,14,148],"363":"b:gkEBEAAAgAAAAAAAQAACMAAAEAAAAAAA","372":[16,28,8,55,124,125,14,148],"380":"b:AEEFFEAYgABAAAAAAIAAMAAAWhAAAAAA","385":"b:AEEFFEAYgABAAAAAAIAAMAAAWhAAAAAA","397":"b:xvVFnMBYjafaAYBAxOU2sdz8/5MBAAAA","403":"b:wmUFHEBYiABAAABAxKECsIgAWhABAAAA","405":"b:AEEFHEAYiABAAABAgIAAMAAAWhAAAAAA","406":"b:AEEFHEAYiABAAABAgIAAMAAAWhAAAAAA","408":"b:AEEFHEAYiABAAABAgIAAMAAAWhAAAAAA","424":"b:AEEFHEAYiABAAABAgIAAMAAAWhAAAAAA","447":[28,8,55,124,125,14,148],"473":"b:AEEFFEAYgABAAAAAAIAAMAAAWhAAAAAA","495":"b:1vVH3MJYjbfbA4BKzuU+sdz+/5MCAAAA","502":[149],"515":[149],"524":[149],"562":[149],"574":"b://VP/PP5zxf/A4hO7+c/8N////sHAAAA"},"packages/frontend/editor-ui/src/app/utils/nodeViewUtils.ts":{"71":[90,91,83,129,73,162],"72":[90,91,83,129,73,162],"81":"b:QMACQAAAAACAAYAAACI5AEgADgABAAAA","82":"b:QMACQAAAAACAAYAAACI5AEgADgABAAAA","91":"b:QMACQAAAAACAAYAAACI5AEgADgABAAAA","92":"b:QMACQAAAAACAAYAAACI5AEgADgABAAAA","101":"b:QMACQAAAAACAAYAAACI5AEgADgABAAAA","102":"b:QMACQAAAAACAAYAAACI5AEgADgABAAAA","111":"b:QMACQAAAAACAAYAAACI5AEgADgABAAAA","112":"b:QMACQAAAAACAAYAAACI5AEgADgABAAAA","121":"b:QMACQAAAAACAAYAAACI5AEgADgABAAAA","138":"b:gmVBlAIQiASKAQACwAEOsMz2ehIAAAAA","155":[58,115,65],"170":"b:x/VP/PL5jwf+A4hO5Oc/8P////sHAAAA","189":"b:x/VP/PL5jwf+A4hO5Oc/8P////sHAAAA","196":[18,114,128],"210":"b:wuVH1AJ4iQSaA4hC4Cc/8Oz2flMHAAAA","275":"b:x/RKTIAwjQNeA4AKoOA0APx+r/kAAAAA","285":[133],"297":[133],"316":[133],"325":[90,91,83,129,73,162],"336":[90,91,83,129,73,162],"406":"b:xvVL3LJZjQfaAYBKwOU+sNz+/5MCAAAA","548":[130],"574":"b:QuEGVARIoBS3A4IAADq7oG5HD/kHAAAA","591":"b:wuVL1AJ4iQSaAYBC4Cc/8Oz2flIDAAAA","603":[19,6],"612":[19,6],"618":[19,6],"654":"b:x/VP/PL5jwf+A4hO4Oc/8N////sHAAAA"},"packages/frontend/editor-ui/src/app/utils/nodes/nodeTransforms.ts":{"34":"b:wuVD1AJ4iQSaA4hC4Cc/8Oz2flMHAAAA","43":"b:w/VH/LL5jwX+A4hC4Kc/8Pz7//sHAAAA","68":"b:wnVHfALZjgFSAghCwKEOsMgKWhAHAAAA","78":[73,162],"92":"b:wnUFFADYjgBAAghAwKEKsIgAWhAFAAAA","106":"b:w/VH/LL5jwX+A4hC4Kc/8Pz7//sHAAAA","112":"b:wnUFFADYjgBAAghAwKEKsIgAWhAFAAAA","123":"b:BZAAQIAACAJaAIAAAEUAAAD4LgAAAAAA","137":"b:1vVH3IJYjbfbA4BKzuU+sdz+/5MCAAAA","159":"b:xvVD3IJYjafaAYBKwOU+scz+/5MCAAAA","183":"b:x/BP6IKBjwb+A4ACgGcgwM7+r/MGAAAA","249":[1,7,47,103,49,50,51,55,6,73],"252":[1,7,47,103,49,50,51,55,6,73]},"packages/frontend/editor-ui/src/app/utils/objectUtils.ts":{"3":"b:x/Vf/PL5jwf+I4hP7O8/8P7///8HAAAA","7":"b:x/Vf/PL5j6f+I4hP7O8/8f7///8HAAAA","11":"b:x/Vf/PL5j6f+I4hP7O8/8f7///8HAAAA","15":[66],"16":[66],"19":"b:x/Vf/PL5jwf+I4hO6O8/8P7///8HAAAA","23":[144,146],"24":[144,146],"36":[15,87,16,17]},"packages/frontend/editor-ui/src/app/utils/pairedItemUtils.ts":{"19":"b:x5BI4LKBDwd6AIAKgGcgAIz+r+EAAAAA","23":[141,70],"48":[141,70],"58":[141,70],"61":"b:x5AI4LKBDwd6AIACgGcgAAz+r+EAAAAA","89":"b:RxAIwLCBAwd4AAAAAGMgAAz+r+EAAAAA","95":"b:x5AI4LKBDwd6AIACgGcgAAz+r+EAAAAA","124":"b:RxAIwLCBBwd4AAACgGMgAAz+r+EAAAAA","138":"b:x5AI4LKBDwd6AIACgGcgAAz+r+EAAAAA","143":"b:x5AI4LKBDwd6AIACgGcgAAz+r+EAAAAA","152":[49],"168":"b:x5BK4LKBDwd6AIACgGcgAA7+r+EEAAAA","171":"b:x5AI4LKBDwd6AIACgGcgAAz+r+EAAAAA","172":"b:x5AI4LKBDwd6AIACgGcgAAz+r+EAAAAA","173":"b:x5AI4LKBDwd6AIACgGcgAAz+r+EAAAAA","188":"b:x5BK4LKBDwd6AIACgGcgAA7+r+EEAAAA","197":"b:x5AI4LKBDwd6AIACgGcgAAz+r+EAAAAA","209":"b:x5BK4LKBDwd6AIACgGcgAA7+r+EEAAAA","229":"b:x5AI4LKBDwd6AIACgGcgAAz+r+EAAAAA","230":"b:x5AI4LKBDwd6AIACgGcgAAz+r+EAAAAA"},"packages/frontend/editor-ui/src/app/utils/rbac/checks/hasScope.ts":{"4":"b://Vf///5/7f/+/////////////8HAAAA"},"packages/frontend/editor-ui/src/app/utils/rbac/checks/isAuthenticated.ts":{"5":"b://Vf///5/7f/+/////////////8HAAAA","14":"b://Vf///5/7f/+//v//////////8HAAAA"},"packages/frontend/editor-ui/src/app/utils/rbac/checks/isValid.ts":{"3":"b://Vf///5/7f/++/D//////z///8HAAAA"},"packages/frontend/editor-ui/src/app/utils/rbac/middleware/authenticated.ts":{"6":"b://Vf///5/7f/+//v//////////8HAAAA"},"packages/frontend/editor-ui/src/app/utils/typeGuards.ts":{"35":[141,70],"39":[99,148,73],"43":"b:RNAEQIAABQVYAIACAGEoAJT+74EAAAAA","49":"b:QFFFFEBYiABQAAAAxKAAsIhw/hMAAAAA","57":"b:kkEBEAIAgBABAgAASgACIAAAEAAAAAAA","61":[149],"65":[60],"71":"b:x/VP/PL5jwf+A4hO4Oc/8P////sHAAAA","77":"b:x/VP/PL5jwf+A4hO4Oc/8P////MHAAAA","81":"b:gmQABAAwAAQCAAAAIAAMAAQEIAAAAAAA","90":[22,26,94,103,51,68,139,145,147,161],"96":[137,149],"109":[56,60,131,137,68,69,6,157,158,159],"113":[8,99,54,119,64,125],"117":[8,99,54,119,64,125],"121":[119],"125":[28,8,33,99,54,55,119,60,64,125],"135":[8,99,54,119,64,125],"142":"b:AAEAGQ4A8BCBAQAACBiAIAAAAAAGAAAA","155":[24,32,35,108,55,60,125,72,161],"167":"b:woBWwAIACAZaAIACgGcgwAz6LmEGAAAA"},"packages/frontend/editor-ui/src/app/utils/typesUtils.ts":{"14":"b:RtBIQLABDQdYAIAKgGUgANT+r4EFAAAA","23":"b:1vVf//557bf7O+hO7v8/8f/+//8HAAAA","40":[73,162],"63":"b:AAEAGQ4A8BCBAwAAChgAIAAAAAAGAAAA","67":"b:AEAAAAAAAABAAAAAACAAAMDyDgABAAAA","73":[147],"78":[56,131,137,68,69,6,157,158,159],"84":"b:RtBIQKAADQdYAIAKgGUoAJT+74EAAAAA","132":[135,140,141,142,143,70,147],"137":[135,140,141,142,143,70,147],"179":[83,73,162],"183":"b:1vVP3IJ4/bffA4BK7u8/8f3+//MDAAAA","187":"b:kkFCUAYAibBBAgBCiEECIRDyOgAAAAAA"},"packages/frontend/editor-ui/src/app/utils/workflowUtils.ts":{"11":"b:R3AIKPCBlwP0AwANgMAAMB4th+kGAAAA","26":[128],"36":[128],"49":"b:R/AKaPCBhwP0A4AMgOI5IF4vj+kHAAAA","60":"b:R/AKaPCBhwP0A4AMgOI5AF4vj+EFAAAA","80":"b:R/AKaPCBlwP0A4ANgOI5MF4vj+kHAAAA","100":"b:R/AKaPCBhwP0A4AMgOI5AF4vj+EFAAAA","103":"b:R/AKaPCBhwP0A4AMgOI5AF4vj+EFAAAA","138":[18],"145":[18],"146":[18],"158":[18],"167":[18]},"packages/frontend/editor-ui/src/app/views/NodeView.vue":{"154":"b:x/Vf/LL5nwf+I4hC6O8/8Pz///8HAAAA","161":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","165":"b:x/Vf/LL5nwf+I4hC6O8/8Pz///8HAAAA","166":"b:x/Vf/LL5nwf2I4hC6O8/8Pz///8HAAAA","181":"b:AmAHiAAAiACOAQAAgAgAgMxSCNYCAAAA","192":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","279":"b:x/Vf/PL5nwf+I4BO4O8/8P7///8HAAAA","290":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","294":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","296":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","297":"b:wuVBlAJ4iQSaAQBC4AUO8Oz+evICAAAA","298":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","301":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","311":"b:R/FH/LL5jwX+A4hC4Oc98Nz///sHAAAA","315":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","327":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","331":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","345":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","347":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","351":[19,1,7,2,8,10,113,57,129,6,13],"357":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","402":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","407":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","417":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","421":"b:x/VP/PL5jwf+A4hO4Oc/8N7///sHAAAA","422":"b:x/VP/PL5jwf+A4hO4Oc/8N7///sHAAAA","425":"b:wuVH1AJ4iQSaAYBO4Cc/8M76flIDAAAA","463":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","465":"b:x/VP/PL5jwf+A4hO4Oc/8P7///sHAAAA","471":"b:R/FH/LL5jwX+A4hC4Oc98Nz///sHAAAA","473":"b:R/FH/LL5jwX+A4hC4Oc98Nz///sHAAAA","474":"b:R/FH/LL5jwX+A4hC4Oc98Nz///sHAAAA","478":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","479":"b:x/VP/PL5jwf+A4hO4Oc/8N7///sHAAAA","481":"b:x5BK4JKBDwZ6AIACgGcgAA7+r+EEAAAA","488":[18,114],"499":[152],"503":[57,65,130,66,134,72],"511":[134],"515":[112,65,6],"524":[65,130,66,134],"528":[134,152],"544":[127,65,6],"552":[134],"556":[1,131,134,139,156,157,158,159],"564":"b:xPBCSIAAjQNeA4AKgOAwANx+j/kAAAAA","569":"b:xPBCSIAAhQNYAIAKgOAwAIh+j4EAAAAA","591":"b:x/VL3IJ4jQfeA4BK4Oc/8Pz+//sDAAAA","596":[65,72,73],"602":[131,134,72,155],"650":[131,134,72],"663":[131,134,147],"687":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","691":"b:QgAAAJABgACwAwAAAAgAsAgDAOgGAAAA","710":[131],"732":[131],"751":[131],"778":[152],"794":[1],"798":[94,51,130],"802":"b:ggQABAAwAAACAAAAIAAEAAQEIAAAAAAA","842":[130,66,152],"846":[134,152],"850":"b:ggQABAAwAAACAAAAIAAEAAQEIAAAAAAA","878":[65,66,134],"882":[134,152],"890":"b:QMACQAAAAACAAIAAACI5AAAADgABAAAA","905":"b:QMACQAAAAACAAIAAACI5AAAADgABAAAA","921":[160],"931":[160],"934":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","940":"b:QgAAAJABgACwAwAAAAgAsAgDAOgGAAAA","951":"b:guVBlAJ4iQSaAQBC4AUO8Mz2flICAAAA","955":"b:guVBlAJ4iQSaAQBC4AUO8Oz2elICAAAA","987":[134,152],"991":[67,135,13,149],"999":[13],"1012":"b:guVBlAJ4iQSaAQBC4AUO8Mz2flICAAAA","1025":[158],"1038":"b:x/VL3IJ4jQfeA4BK4Oc/8Pz+//sDAAAA","1044":[133],"1048":[58,115,65],"1113":"b:x/Vf/PL5nwf+I4hC6O8/8Pz///8HAAAA","1119":"b:wuVX9AJ4iQSaI4hC6C8/8Ozyfl4HAAAA","1121":"b:AAEEMAAAgACAAQAACAgAMAAAEAICAAAA","1132":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","1133":"b:x/Vf/LL5nwf+I4hC6O8/8P7///8HAAAA","1137":"b:R/FH/LL5jwX+A4hC4Oc98Nz///sHAAAA","1149":"b:x/Vf/LL5nwf+I4hC6O8/8P7///8HAAAA","1152":"b:x/Vf/LL5nwf+I4hC6O8/8P7///8HAAAA","1155":"b:x/Vf/LL5nwf+I4hC6O8/8P7///8HAAAA","1159":[0,2,39,12,57,65,68,139,140,142],"1229":[0,2,39,12,57,65,68,139,140,142],"1285":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","1290":"b:QgAAAJABgACwAwAAAAgAsAgDAOgGAAAA","1295":[6],"1305":"b:QgBC4IKBDwR0AAACgCIgAAwOj+EAAAAA","1309":"b:QABC4IKBDwR0AAACgCIgAAwOj+EAAAAA","1317":"b:xuVf9AJ4mQaaIYBC6C8/8O76fl4DAAAA","1318":"b:xiUIAAAAAAICAAAAAAAKAGoIAgAAAAAA","1323":"b:x/Vf/LL5nwf+I4hC6O8/8P7///8HAAAA","1326":"b:x/VP/LL5jwf+A4hC4Oc/8P7///sHAAAA","1334":"b:x/Vf/LL5nwf+I4hC6O8/8P7///8HAAAA","1335":"b:x/VP/LL5jwf+A4hC4Oc/8N7///sHAAAA","1338":"b:xuVf9AJ4mQaaIYBC6C8/8O76fl4DAAAA","1344":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","1360":[1,129],"1368":[1],"1447":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","1461":"b:QgAAAJABgACwAwAAAAgAsAgDAOgGAAAA","1499":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","1503":"b:QgAAAJABgACwAwAAAAgAsAgDAOgGAAAA","1510":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","1514":"b:QgAAAJABgACwAwAAAAgAsAgDAOgGAAAA","1522":"b:wuVBlAJ4iQSaAQBC4AUO8Oz+evICAAAA","1565":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","1583":[137],"1599":"b:wuVD1AJ4iQSaAYBC4Cc/8Oz2flIDAAAA","1603":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","1609":"b:wuVD1AJ4iQSaAYBC4Cc/8Oz2flIDAAAA","1610":"b:wuVD1AJ4iQSaAYBC4Cc/8Oz2flIDAAAA","1617":"b:RQAIBAAACAIGAAAAAAAAADwABgEAAAAA","1622":"b:RQAIBAAACAIGAAAAAAAAADwABgEAAAAA","1630":[65],"1652":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","1662":[14],"1692":"b:QgAAAJABgACwAwAAAAgAsAgDAOgGAAAA","1698":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","1712":"b:xvVD3AJYjQbaAYBCwOU+sMz+/xMCAAAA","1730":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","1735":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","1741":"b:wuVX9AJ4mQSaIYBC6C8/8Ozyfl4DAAAA","1773":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","1775":"b:xvVD3AJYjQbaAYBCwOU+sMz+/1MCAAAA","1784":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","1786":"b:xvVD3IJYjQfaAYBKwOU+sMz+/5MCAAAA","1804":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","1805":"b:wuVH3AJ4iQbeA4BC4G8/8Oz+fvcDAAAA","1823":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","1824":[18,20],"1836":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","1848":"b:RZAAQLABCABaAIAAAEUAAAD4LkAAAAAA","1849":"b:RZAAQLABCABaAIAAAEUAAAD4LkAAAAAA","1854":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","1863":"b:R9FF9LKRjwX+AYACwGctsMz+//sAAAAA","1873":"b:R9FF9LKRjwX+AYACwGctsMz+//sAAAAA","1874":"b:R9FF9LKRjwX+AYACwGctsMz+//sAAAAA","1885":"b:QgAAAAAAgACwAAAAAAAAgAgDAOgEAAAA","1934":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","1936":[56,155],"1953":"b:x/Vf/PL5nwf+I4BO4O8/8P7///8HAAAA","1974":"b:QgAAAJABgACwAwAAAAgAsAgDAOgGAAAA","2045":[162],"2055":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","2075":"b:QgBC4IKBDwRwAAACgCIgAAwOj+EAAAAA","2090":[19,1,7,2,8,10,113,57,129,6,13]},"packages/frontend/editor-ui/src/app/workers/isDataWorkerEnabled.ts":{"10":"b://Vf///5/7f/+/////////////8HAAAA"},"packages/frontend/editor-ui/src/experiments/aiTemplatesStarterCollection/stores/aiTemplatesStarterCollection.store.ts":{"20":"b:x/Vf//75/wf+O+hP6P8/8P////8HAAAA","33":"b:AAAACQwA8ACAAQAAABgAMAAAAAAGAAAA"},"packages/frontend/editor-ui/src/experiments/enhancedHitlSlack/useEnhancedHitlSlackExperiment.ts":{"6":"b:xvVH3MJYjafaAYBKxOU+sdz+/5MCAAAA","9":"b:xvVH3MJYjafaAYBKxOU+sdz+/5MCAAAA"},"packages/frontend/editor-ui/src/experiments/enhancedHitlTelegram/useEnhancedHitlTelegramExperiment.ts":{"6":"b:xvVH3MJYjafaAYBKxOU+sdz+/5MCAAAA","9":"b:xvVH3MJYjafaAYBKxOU+sdz+/5MCAAAA"},"packages/frontend/editor-ui/src/experiments/evaluationsWizardSidepanel/useEvaluationsWizardSidepanelExperiment.ts":{"7":"b:x/Vf/PL5jwf+I4hP6O8/8P7///8HAAAA","13":"b:x/Vf/LL5jwf+I4hC6O8/8Pz///8HAAAA"},"packages/frontend/editor-ui/src/experiments/n8nCreditsCredentialSelection/useN8nCreditsCredentialSelectionExperiment.ts":{"6":"b:xvVH3MJYjQfaAYBCxOU+sNz+/5MCAAAA"},"packages/frontend/editor-ui/src/experiments/readyToRunWorkflows/stores/readyToRunWorkflows.store.ts":{"21":"b:x/Vf//75/wf+O+hP6P8/8P////8HAAAA","30":"b:AAAACQwA8ACAAQAAABgAMAAAAAgGAAAA","42":"b:AAAACQwA8ACAAQAAABgAMAAAAAgGAAAA"},"packages/frontend/editor-ui/src/experiments/readyToRunWorkflowsV2/stores/readyToRunWorkflowsV2.store.ts":{"12":"b:x/Vf///5/xf/++9D+P+/8P7///8HAAAA"},"packages/frontend/editor-ui/src/experiments/resourceCenter/stores/resourceCenter.store.ts":{"19":"b:x/Vf///5/xf/++9D+P+/8Pz///8HAAAA","32":"b:x/Vf///5/xf/++9D+P+/8Pz///8HAAAA","114":"b:x/Vf///5/xf/++9D+P+/8Pz///8HAAAA"},"packages/frontend/editor-ui/src/experiments/sidebarExpanded/useSidebarExpandedExperiment.ts":{"6":"b:x/Vf///5/xf/++9D+P+/8Pz///8HAAAA","10":"b:x/Vf///5/xf/++9D+P+/8Pz///8HAAAA"},"packages/frontend/editor-ui/src/experiments/utils.ts":{"32":"b:wuVX9AJ4mQSaIYBC6C8/8Ozyfl4DAAAA","39":"b://Vf///5/7f/+/////////////8HAAAA","46":"b:x/Vf///5/xf/++9D+P+/8Pz///8HAAAA"},"packages/frontend/editor-ui/src/features/agents/agentNodeCanvasGeometry.store.ts":{"3":"b://Vf///5/7f/+/////////////8HAAAA","37":"b:QgAAAJABgACwAAAAAAAAgAgDAOgEAAAA"},"packages/frontend/editor-ui/src/features/agents/agentReturnContext.store.ts":{"20":"b:x/Vf/PL5jwf+I4hO6O8/8P7///8HAAAA"},"packages/frontend/editor-ui/src/features/agents/composables/useAgentCapabilitiesActions.ts":{"93":"b:x/Vf/PL5jwf+I4hO6O8/8P7///8HAAAA"},"packages/frontend/editor-ui/src/features/agents/composables/useAgentConfig.ts":{"18":"b:x/Vf/PL5jwf+I4hO6O8/8P7///8HAAAA","30":"b:x/Vf/PL5jwf+I4hO6O8/8P7///8HAAAA"},"packages/frontend/editor-ui/src/features/agents/composables/useAgentNavigation.ts":{"11":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA"},"packages/frontend/editor-ui/src/features/agents/composables/useAgentScopeProjectId.ts":{"19":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA"},"packages/frontend/editor-ui/src/features/agents/composables/useMessageAgentSessionLink.ts":{"20":[52],"33":[19,1,7,2,36,37,39,40,52,57,6,159],"61":[19,1,7,2,36,37,39,40,52,57,6,159],"66":[19,1,7,2,36,37,39,40,52,57,6,159],"92":[52]},"packages/frontend/editor-ui/src/features/agents/utils/agentNode.ts":{"9":"b:x/Vf/PL5nwf+I4hO7O8/8P////8HAAAA"},"packages/frontend/editor-ui/src/features/ai/assistant/assistant.store.ts":{"37":"b://Vf///5/7f/+/////////////8HAAAA","45":"b:x/Vf/AL4jwf+I4hC6O8/8Pz///8HAAAA","58":[87],"59":[87],"79":[15,87,16,17,18,20],"80":[15,87,17],"82":[15,87,16,17],"84":[15,87,16,17,18,20],"94":"b://Vf///5/7f/+/////////////8HAAAA","96":"b://Vf///5/7f/+/////////////8HAAAA","100":[15,87,16],"102":[15,16],"107":"b://Vf///5/7f/+/////////////8HAAAA","115":"b://Vf///5/7f/++/v//////////8HAAAA","119":[15,87,16,17],"123":[15,87,16,17],"136":[15,87,16,17],"138":[15,87,16,17],"142":[15,87,16,17],"199":[15,87],"210":[16],"216":[15,87,16,17],"220":[15,87,16,17],"247":[15,87,16,17],"251":[15,16,17],"277":[15,87,16,17],"297":[15,87,16,17],"301":[15,87,16,17],"305":[15,87,16,17],"317":[16],"336":[15,17],"420":[15,16,17],"480":[15,16,17],"481":[15,16,17],"493":[15,87],"554":[15,87],"555":[15,87],"562":[87],"590":[87],"591":[87],"598":"b:x5BK4LKBDwZ6AIACgGcgAA7+r+EAAAAA","629":[15,17],"679":[15,17],"680":[15,17],"692":[15,17],"706":[15,87,16,17,18,20],"746":"b://Vf///5/7f/+/////////////8HAAAA","747":"b:wuVH3AJ4iabeA4hC4G8/8ez+fvcHAAAA","753":"b://Vf///5/7f/+/////////////8HAAAA","756":"b://Vf///5/7f/+/////////////8HAAAA"},"packages/frontend/editor-ui/src/features/ai/assistant/builder.store.ts":{"166":"b://Vf///5/7f/+/////////////8HAAAA","188":"b://Vf///5/7f/++/v//////////8HAAAA","230":"b://Vf/f/5/wf/c8tP/f8/8P////8HAAAA","259":"b://Vf///5/7f/+/////////////8HAAAA","262":[18,20],"265":[18,20],"304":[18,20],"307":"b://Vf///5/7f/++/v//////////8HAAAA","323":"b://Vf///5/7f/++/v//////////8HAAAA","327":[18,20],"329":"b://Vf///5/7f/++/v//////////8HAAAA","335":"b://Vf///5/7f/++/v//////////8HAAAA","355":"b://Vf///5/7f/++/v//////////8HAAAA","367":"b://Vf///5/7f/++/v//////////8HAAAA","371":"b://Vf///5/7f/++/v//////////8HAAAA","374":"b://Vf///5/7f/++/v//////////8HAAAA","376":"b://Vf///5/7f/++/v//////////8HAAAA","388":"b://Vf///5/7f/++/v//////////8HAAAA","410":[18,20],"420":"b://Vf///5/7f/++/v//////////8HAAAA","432":"b://Vf/f/5/wf/c8tP/f8/8P////8HAAAA","462":"b:x5AI4LKBDwZ6AIACgGcgAA7+r+EAAAAA","469":"b:x/Vf/PL5nwf+I4hP6O8/8P////8HAAAA","477":[18,20],"481":[18,20],"503":[18,20],"530":[18,20],"531":[18,20],"580":[18,20],"644":[20],"655":[20],"664":[20],"695":[18,20],"726":[18,20],"739":[18,20],"816":[18,20],"832":[18,20],"864":[18],"889":[18,20],"1025":[18],"1028":[18],"1050":[18],"1057":[20],"1128":[18,20],"1397":[18],"1402":[18],"1429":[18,20],"1440":"b:AiAGiAAAiALMAIAAAEAAgIx6KPECAAAA","1448":"b:AiAGiAAAiALMAIAAAEAAgIx6KPECAAAA","1456":[18],"1460":[18,20],"1465":[18,20],"1479":"b://Vf///5/7f/+/////////////8HAAAA","1480":"b:x/Vf/AL4nwf+I4hP6O8/8P////8HAAAA","1513":"b://Vf///5/7f/+/////////////8HAAAA","1525":[18],"1546":[18],"1677":"b://Vf///5/7f/++/v//////////8HAAAA"},"packages/frontend/editor-ui/src/features/ai/assistant/chatPanel.store.ts":{"32":[15,87,16,17,18,20],"39":"b://Vf///5/7f/+/////////////8HAAAA","50":[15,87,16,17],"58":[15,87,16,17,18,20],"64":"b://Vf///5/7f/++/v//////////8HAAAA","66":"b:x/Vf/LL5jwf+I4hC6O8/8Pz///8HAAAA","69":[15,87,16,17,18,20],"103":[15,16],"110":[15,87,16,17,18,20],"119":[15],"127":[15],"142":[15,17,18,20],"150":[15,87,16,17,18,20],"182":[16],"200":[15,87],"216":"b://Vf///5/7f/+/////////////8HAAAA","217":"b://Vf///5/7f/++/v//////////8HAAAA","254":"b://Vf///5/7f/+/////////////8HAAAA","255":"b://Vf///5/7f/++/v//////////8HAAAA"},"packages/frontend/editor-ui/src/features/ai/assistant/chatPanelState.store.ts":{"14":"b://Vf///5/7f/+/////////////8HAAAA"},"packages/frontend/editor-ui/src/features/ai/assistant/components/Agent/AskAssistantBuild.vue":{"75":"b://Vf///5/7f/++/v//////////8HAAAA","88":"b://Vf///5/7f/++/v//////////8HAAAA","118":[18],"125":"b://Vf///5/7f/++/v//////////8HAAAA","132":"b://Vf///5/7f/++/v//////////8HAAAA","136":"b://Vf///5/7f/++/v//////////8HAAAA","141":"b://Vf///5/7f/++/v//////////8HAAAA","151":"b://Vf///5/7f/++/v//////////8HAAAA","156":"b://Vf///5/7f/++/v//////////8HAAAA","167":"b://Vf///5/7f/++/v//////////8HAAAA","168":[18,20],"170":[18,20],"178":[18,20],"179":[18,20],"182":[18,20],"198":"b://Vf///5/7f/++/v//////////8HAAAA","199":"b://Vf///5/7f/++/v//////////8HAAAA","200":"b://Vf///5/7f/++/v//////////8HAAAA","203":"b://Vf///5/7f/++/v//////////8HAAAA","209":"b://Vf///5/7f/++/v//////////8HAAAA","216":"b://Vf///5/7f/++/v//////////8HAAAA","223":"b://Vf///5/7f/++/v//////////8HAAAA","227":"b://Vf///5/7f/++/v//////////8HAAAA","245":[18],"279":"b://Vf///5/7f/++/v//////////8HAAAA","311":"b://Vf///5/7f/++/v//////////8HAAAA","343":"b:x3Vc/AL4j6f+AwhO4Oc/8fz///sHAAAA","345":[18,20],"392":[18,20],"408":[18],"417":"b:egAAEf8B8ACxU0sAFRgAsAgDAOgGAAAA","543":[18],"557":"b://Vf///5/7f/++/v//////////8HAAAA","559":"b:egAUEf8B8ACxU0sAFRgAsAgDAOgGAAAA","594":"b://Vf///5/7f/++/v//////////8HAAAA","595":[18,20],"607":"b://Vf///5/7f/++/v//////////8HAAAA","609":[18,20],"684":"b:egAAEf8B8ACxU0sAFRgAsAgDAOgGAAAA","691":"b://Vf///5/7f/++/v//////////8HAAAA","703":[18,20],"709":"b://Vf///5/7f/++/v//////////8HAAAA","712":[18,20],"743":[18,20],"749":[18],"756":"b://Vf///5/7f/++/v//////////8HAAAA","773":"b://Vf///5/7f/++/v//////////8HAAAA","778":[18,20],"785":"b:x3VI/AL4j6f+AwhO4Oc/8fz///sHAAAA","788":[18],"840":"b://Vf///5/7f/++/v//////////8HAAAA","870":"b://Vf///5/7f/++/v//////////8HAAAA","872":"b://Vf///5/7f/++/v//////////8HAAAA","880":"b:x3Vc/AL4j6f+AwhO4Oc/8fz///sHAAAA","910":"b:x3Vc/AL4j6f+AwhO4Oc/8fz///sHAAAA"},"packages/frontend/editor-ui/src/features/ai/assistant/components/Agent/BuildModeEmptyState.vue":{"5":"b:x3VI/AL4j6f+AwhO4Oc/8fz///sHAAAA","6":"b:x3VI/AL4j6f2AwhO4Oc/8fz///sHAAAA"},"packages/frontend/editor-ui/src/features/ai/assistant/components/Agent/PlanModeSelector.vue":{"17":"b://Vf///5/7f/++/v//////////8HAAAA","27":"b://Vf///5/7f/++/v//////////8HAAAA","46":"b://Vf///5/7f/++/v//////////8HAAAA","47":"b://Vf///5/7f/++/v//////////8HAAAA","53":"b://Vf///5/7f/++/v//////////8HAAAA","71":"b://Vf///5/7f/++/v//////////8HAAAA","74":"b://Vc///5/7f/+2/v//////////8HAAAA"},"packages/frontend/editor-ui/src/features/ai/assistant/components/AssistantsHub.vue":{"19":"b://Vf///5/7f/++/v//////////8HAAAA","39":"b://Vf///5/7f/++/v//////////8HAAAA","40":[15,87,16,17,18,20],"51":"b://Vf///5/7f/++/v//////////8HAAAA","85":[15],"89":[15,87,16,17,18,20],"98":"b:x5Bf4LKBDwZ6AIACgGcgAA7+r+EAAAAA","106":"b://Vf///5/7f/++/v//////////8HAAAA","112":"b:/vVX3f9Z/Rb7U8tC3f++8Mz///8GAAAA","124":"b://Vf///5/7f/++/v//////////8HAAAA","141":"b://Vf///5/7f/++/v//////////8HAAAA","150":"b:9/Vf///5/7f3++/v//////////8HAAAA","164":"b://Vf///5/7f/++/v//////////8HAAAA","169":[18,20]},"packages/frontend/editor-ui/src/features/ai/assistant/components/FocusedNodes/ChatInputWithMention.vue":{"24":"b://Vf///5/7f/++/v//////////8HAAAA","45":"b://Vf///5/7f/++/v//////////8HAAAA","66":"b://Vf///5/7f/++/v//////////8HAAAA","67":[18,20],"73":[18,20],"103":[18],"114":[18,20],"121":[18,20],"127":[20],"159":[18,20],"174":"b://Vf///5/7f/++/v//////////8HAAAA","283":"b://Vf///5/7f/++/v//////////8HAAAA"},"packages/frontend/editor-ui/src/features/ai/assistant/composables/useAIAssistantHelpers.ts":{"45":"b://Vf///5/7f/+/////////////8HAAAA","47":[15,87,16],"62":[15,87,16],"81":[15,87,16],"207":[15,87],"214":[15,87],"227":[15,87,16,17],"251":[16],"276":[15,87],"298":[15,87,16],"301":[15,87,16],"309":[15,87,16,18],"335":[15,17],"357":[17],"379":[17],"420":[15,17,18,20]},"packages/frontend/editor-ui/src/features/ai/assistant/composables/useAskModeCoachmark.ts":{"14":[18,20],"17":"b://Vf///5/7f/++/v//////////8HAAAA","25":"b://Vf///5/7f/++/v//////////8HAAAA","27":"b://Vf///5/7f/++/v//////////8HAAAA","35":"b://Vf///5/7f/++/v//////////8HAAAA"},"packages/frontend/editor-ui/src/features/ai/assistant/composables/useBuilderMessages.ts":{"82":"b://Vf///5/7f/+/////////////8HAAAA","88":[18,20],"89":[18,20],"208":[18],"326":"b://Vf///5/7f/++/v//////////8HAAAA","327":[18,20],"330":"b://Vf///5/7f/++/v//////////8HAAAA","342":[18],"395":[18],"419":[18],"436":[18],"460":[18],"462":[18],"471":[18,20],"517":[20],"556":"b://Vf/f/5/wf/c8tP/f8/8P////8HAAAA"},"packages/frontend/editor-ui/src/features/ai/assistant/composables/useBuilderTodos.ts":{"37":"b://Vf///5/7f/+/////////////8HAAAA","47":[18],"89":[18],"129":[18,20],"138":[18],"151":[18,20],"206":[18,20],"212":[18],"246":[18,20],"247":[18],"254":[18]},"packages/frontend/editor-ui/src/features/ai/assistant/composables/useFocusedNodesChipUI.ts":{"14":"b://Vf///5/7f/++/v//////////8HAAAA"},"packages/frontend/editor-ui/src/features/ai/assistant/composables/useNodeMention.ts":{"32":"b://Vf///5/7f/++/v//////////8HAAAA","61":"b://Vf///5/7f/++/v//////////8HAAAA","62":"b:wmVElPIRiASOAQACwAEPsO73ehsAAAAA","155":[18,20]},"packages/frontend/editor-ui/src/features/ai/assistant/composables/useReviewChanges.ts":{"34":"b://Vf///5/7f/++/v//////////8HAAAA","71":"b://Vf///5/7f/++/v//////////8HAAAA","100":[18],"107":[18],"128":[18],"145":[18],"252":[15,87,16,17],"278":[18,20],"288":[18],"299":[18,20],"309":[15,87,16,17],"311":"b://Vf///5/7f/++/v//////////8HAAAA"},"packages/frontend/editor-ui/src/features/ai/assistant/focusedNodes.store.ts":{"27":"b://Vf///5/7f/+/////////////8HAAAA","29":"b://Vf///5/7f/+/////////////8HAAAA","36":"b://Vf///5/7f/+/////////////8HAAAA","42":"b://Vf///5/7f/++/v//////////8HAAAA","52":"b:x/Vf/PL5nwf+I4hP6O8/8P////8HAAAA","247":[18,20],"273":"b://Vf///5/7f/+/////////////8HAAAA","274":"b:x/Vf/PL5nwf+I4hP6O8/8P////8HAAAA","289":"b://Vf///5/7f/+/////////////8HAAAA","290":"b:x/Vf/PL5n6f+I4hP6O8/8f////8HAAAA","292":"b:x/VP/PL5j6f+A4hO4Oc/8f7///sHAAAA","318":"b://Vf///5/7f/+/////////////8HAAAA","319":"b:x/Vf/PL5n6f+I4hP6O8/8f////8HAAAA","331":"b://Vf///5/7f/+/////////////8HAAAA","332":"b:xvVD3IJYjabaAYBKwOU+scz+/xMCAAAA","341":[18,20]},"packages/frontend/editor-ui/src/features/ai/chatHub/chat.store.ts":{"108":"b://Vf///5/7f/+/////////////8HAAAA","137":[3,4,5,96,98,32],"140":[3,4,5,96,98,32],"147":[3,4,5,96,98,32],"163":[3,4,5,96,98,32],"173":[3,4,5,96,98,32],"177":[3,4,5,96,98,32],"214":[3,4,5,96,98,32],"237":[4],"279":[3,4,5,96,98,32],"305":[3,4,5,96],"315":[3,4,5,96,98,32],"334":[3,4,5,96,98,32],"341":[98],"368":[3,4,5,96,98,32],"371":[3,4,5,96,98,32],"377":[3,4,5,96,98,32],"382":[3,4,5,96,98,32],"411":[3,4,5,96,98,32],"441":[3,4],"447":[3,4],"458":[3,4,5,96,98,32],"467":[3,4,5,96,98,32],"484":[98,32],"595":[3,4,5,96,98,32],"610":[3],"657":[98],"694":[4],"797":[4],"949":[4],"961":[96],"985":[96],"996":[96],"1031":[96],"1056":[96],"1076":[96],"1081":[96],"1091":[3,4,5,96,98,32],"1095":[3,4,5,96,98,32],"1106":[97],"1117":[97],"1133":[97],"1150":[3,4,5,96,98,32],"1196":[3,4,5,96],"1214":[3,4,5,96,98,32],"1230":[98,32],"1252":[3,4,5,96,98,32],"1294":[3,4,5,96,98,32],"1335":[3,4,5,96,98,32],"1362":[3,4,5,96,98,32],"1392":[3],"1413":[4],"1531":[3,4,5,96,98,32],"1532":[3,4,5,96,98,32],"1557":[3,4,5,96,98,32],"1558":[3,4,5,96,98,32]},"packages/frontend/editor-ui/src/features/ai/chatHub/chatHubPanel.store.ts":{"19":"b://Vf///5/7f/++/v//////////8HAAAA","29":"b:x/Vf/PL5nwf+I4hP6O8/8P7///8HAAAA","66":"b://Vf///5/7f/++/v//////////8HAAAA","67":"b:egABEf8B8ACxU0sAFRgAsAgDAOgGAAAA"},"packages/frontend/editor-ui/src/features/ai/chatHub/module.descriptor.ts":{"18":[3,4,5,96,98,32],"19":[32],"20":[32],"21":[96],"22":[96],"23":[97],"24":[97],"46":[98],"57":[96],"69":[5],"93":[97],"203":"b://Vf///5/7f/++/D//////z///8HAAAA"},"packages/frontend/editor-ui/src/features/ai/evaluation.ee/components/EvaluationsCanvasInfoCard/EvaluationsCanvasInfoCard.vue":{"25":"b:x/Vf/LL5nwf+I4hC6O8/8Pz///8HAAAA","74":"b:x/Vf/LL5nwf+I4hC6O8/8Pz///8HAAAA","85":"b:x/Vf/LL5nwf+I4hC6O8/8Pz///8HAAAA","99":"b:x/Vf/LL5nwf+I4hC6O8/8Pz///8HAAAA","103":"b:x/Vf/LL5nwf+I4hC6O8/8Pz///8HAAAA","134":"b:x/Vf/LL5nwf+I4hC6O8/8Pz///8HAAAA","159":"b:x/Vf/LL5nwf+I4hC6O8/8Pz///8HAAAA","182":"b:x/Vf/LL5nwf+I4hC6O8/8Pz///8HAAAA"},"packages/frontend/editor-ui/src/features/ai/evaluation.ee/composables/useAiRootNodes.ts":{"15":"b:x/Vf/LL5nwf+I4hC6O8/8Pz///8HAAAA"},"packages/frontend/editor-ui/src/features/ai/evaluation.ee/composables/useEvaluationsLicense.ts":{"9":[88,52],"10":[52],"12":"b:x/Vf/PL5nwf+I4hP6O8/8P7///8HAAAA"},"packages/frontend/editor-ui/src/features/ai/evaluation.ee/evaluation.store.ts":{"11":"b:x/Vf/PL5jwf+I4hD6O8/8Pz///8HAAAA","26":"b:x/Vf/LL5jwf+I4hC6O8/8Pz///8HAAAA","45":[88],"52":[88],"65":[88,127],"69":[88],"138":[88]},"packages/frontend/editor-ui/src/features/ai/evaluation.ee/wizardSidepanel.store.ts":{"29":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","32":"b:x/Vf/LL5nwf+I4hC6O8/8Pz///8HAAAA"},"packages/frontend/editor-ui/src/features/ai/instanceAi/composables/useInstanceAiAvailability.ts":{"15":"b:x/Vf///5/xf/++9D+P+/8Pz///8HAAAA"},"packages/frontend/editor-ui/src/features/ai/instanceAi/composables/useInstanceAiHandoffCapability.ts":{"33":"b:x/Vf/AL4nwf+I4hD6O8/8Pz///8HAAAA","47":[52],"66":[52],"80":[52],"130":[52],"147":[52],"168":[52]},"packages/frontend/editor-ui/src/features/ai/instanceAi/instanceAi.store.ts":{"25":"b://Vf///5/7f/+/////////////8HAAAA","49":[36,78,80,37,100,81,39,40],"52":[36,78,79,80,37,100,38,81,39,40,82],"55":[36,78,79,80,37,100,38,81,39,40,82],"64":[79,80,81,82],"68":[81],"82":[36,78,79,80,37,100,38,81,39,40,82],"93":[36,78,79,80,37,100,38,81,39,40,82],"105":[36,78,79,80,37,100,38,81,39,40,82],"113":[36,78,79,80,37,100,38,81,39,40,82],"116":[36,37,38,39,40],"139":[36,78,79,80,37,100,38,81,39,40,82],"151":[36,78,79,80,37,100,38,81,39,40,82],"159":[36,78,79,80,37,100,81,39,40,82],"160":[36,78,79,80,37,100,81,39,40,82],"161":[36,78,79,80,37,100,81,39,40,82],"176":[36,78,80,37,100,38,81,39,40,82],"197":[100,81],"215":[81],"228":[81],"234":[81],"235":[81],"246":[36,78,79,80,37,100,38,81,39,40,82],"247":[36,78,79,80,37,100,38,81,39,40,82],"305":[36,78,79,80,37,100,38,81,39,40,82]},"packages/frontend/editor-ui/src/features/ai/instanceAi/instanceAiSettings.store.ts":{"42":"b://Vf///5/7f/+/////////////8HAAAA","101":[36,78,79,80,37,100,38,81,39,40,82],"105":[36,78,79,80,37,100,38,81,39,40,82],"108":[36,78,79,80,37,100,38,81,39,40,82],"111":[36,78,79,80,37,100,38,81,39,40,82],"114":[36,78,79,80,37,100,38,81,39,40,82],"120":[36,78,79,80,37,100,38,81,39,40,82],"149":[36,78,79,80,37,100,38,81,39,40,82],"159":[36,78,79,80,37,100,38,81,39,40,82],"261":[36,78,79,80,37,100,38,81,39,40,82],"355":[36,78,79,80,37,100,38,81,39,40,82],"374":[37,39],"390":[36,78,79,80,37,100,38,81,39,40,82],"500":[36,78,79,80,37,100,38,81,39,40,82],"503":[36,37,38,39,40],"521":[36,78,79,80,37,100,38,81,39,40,82],"522":[37,39],"589":[36,78,79,80,37,100,38,81,39,40,82],"593":[36,78,79,80,37,100,38,81,39,40,82]},"packages/frontend/editor-ui/src/features/ai/mcpAccess/module.descriptor.ts":{"87":"b://Vf///5/7f/++/D//////z///8HAAAA"},"packages/frontend/editor-ui/src/features/collaboration/collaboration/collaboration.store.ts":{"32":"b://Vf///5/7f/+/////////////8HAAAA","70":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","78":"b:x/Vf/PL5jwf+I4hO6O8/8P7///8HAAAA","79":"b:x/Bf6PKBnwf+A4ACgO8g8Nz/r/8GAAAA","82":"b://Vf///5/7f/++/v//////////8HAAAA","86":"b://Vf///5/7f/++/v//////////8HAAAA","90":"b:x/Bf6PKBnwf+A4ADgO8g8Nz/r/8GAAAA","108":"b:x/Bf6PKBnwf+A4ADgO8g8Nz/r/8GAAAA","113":"b:AAAACJABgAGEAQAAAAAAAAwFAAkCAAAA","117":"b:AAAACJABgAGEAAAAAAAAAAwFAAkCAAAA","122":"b:x/Bf6PKBnwf+A4ADgO8g8Nz/r/8GAAAA","129":"b:x/Bf6PKBjwf+A4ADgO8g8Nz/r/8GAAAA","134":"b:QmAGCJABjAbMAYAAgEAAoMx/KPkCAAAA","153":"b:QmAGCAAAjAbMAYAAgEAAoMx+KPkCAAAA","161":"b:QmAGCJABjAbMAYAAgEAAoMx/KPkCAAAA","195":"b:AAAACAAAgAGEAQAAAAAAAAwEAAECAAAA","204":"b:QmAGCAAAjAbMAYAAgEAAoMx+KPkCAAAA","208":"b:wuVX3AJ4jQbeA4BC4G8/8Oz+fv8DAAAA","247":[55,71,155],"266":"b:RwAAAEAAmAAgAAAAAAAAAAgCBMEAAAAA","281":"b:x/Bf6PKBnwf+A4ADgO8g8Nz/r/8GAAAA","288":"b:x/Bf6PKBjwf+A4ADgO8g8Nz/r/8GAAAA","293":"b:x/Vf/PL5jwf+I4hO6O8/8P7///8HAAAA","297":"b:wsBHyJIBiKbeAYACgG8gwcz7Lv8GAAAA","302":[31,51,142],"312":[31,51,142],"318":"b:AiAGiAAAjAPMAIAAAEAAgIx6KPkCAAAA","345":"b://Vf///5/7f/+/////////////8HAAAA","346":"b:wuVH3AJ4iabeA4hC4G8/8ez+fvcHAAAA","355":"b:x/Bf6PKBnwf+A4ADgO8g8Nz/r/8GAAAA","367":"b:x/Bf6PKBnwf+A4ADgO8g8Nz/r/8GAAAA","389":"b:x/Bf6PKBnwf+A4ADgO8g8Nz/r/8GAAAA","445":[36,39,40,55,136,71,155]},"packages/frontend/editor-ui/src/features/collaboration/collaboration/components/CollaborationPane.vue":{"11":"b:x/Bf6PKBnwf+A4ADgO8g8Nz/r/8GAAAA","15":"b:AAAACAAAkAGEAQAAAAgAAAwEAAECAAAA","24":"b:x/Bf6PKBnwf+A4ADgO8g8Nz/r/8GAAAA","26":[52,72],"27":[52,72],"29":[52,72],"35":[52,72],"37":"b:x/Bf6PKBnwf+A4ADgO8g8Nz/r/8GAAAA","41":[36,39,40,52,55,136,71,155],"44":"b:x/Bf6PKBnwf+A4ADgO8g8Nz/r/8GAAAA"},"packages/frontend/editor-ui/src/features/collaboration/projects/components/ProjectCardBadge.vue":{"23":"b://Vf///5/7f/+/////////////8HAAAA","37":"b:AAEAGQoAwBCBAQAACBgAIAAAAAAGAAAA","46":"b:AAEAGQoAwBCBAQAACBgAIAAAAAAGAAAA","50":"b:AAEAGQoAwBCBAQAACBgAIAAAAAAGAAAA","73":"b:AAEAGQoAwBCBAQAACBgAIAAAAAAGAAAA","77":"b:AAEAGQoAwBCBAQAACBgAIAAAAAAGAAAA","89":"b:AAEAGQoAwBCBAQAACBgAIAAAAAAGAAAA","105":"b:AAEAGQoAwBCBAQAACBgAIAAAAAAGAAAA","148":"b:AAEAGQoAwBCBAQAACBgAIAAAAAAGAAAA","175":"b:AAEAGQoAwBCBAQAACBgAIAAAAAAGAAAA","178":[24,27,107,108,54,55,71,72,161,162]},"packages/frontend/editor-ui/src/features/collaboration/projects/components/ProjectNavigation.vue":{"24":"b:x/Vf///5/xf/++9D+P+/8Pz///8HAAAA","48":"b:x/Vf///5/xf/++9D+P+/8Pz///8HAAAA","50":"b:x/Vf///5/xf/++9D+P+/8Pz///8HAAAA","55":"b:x/Vf///5/xf/++9D+P+/8Pz///8HAAAA","60":"b:x/Vf///5/xf/++9D+P+/8Pz///8HAAAA","61":"b:x/Vf///5/xf/++9D+P+/8Pz///8HAAAA","64":"b:x/Vf///5/xf/++9D+P+/8Pz///8HAAAA","76":"b:x/Vf///5/xf/++9D+P+/8Pz///8HAAAA","85":"b:x/Vf///5/xf/++9D+P+/8Pz///8HAAAA","94":"b:AEEEMQlI+RMeAQBAqPoVsFzTX28HAAAA","106":"b:x/Vf///5/xf/++9D+P+/8Pz///8HAAAA","118":"b:x/Vf///5/xf/++9D+P+/8Pz///8HAAAA","120":[36,78,79,80,37,100,38,81,39,40,82],"137":[32],"151":"b:x/Vf///5/xf/++9D+P+/8Pz///8HAAAA","156":"b:AAAAAAAAgACAA0AAAAgAMAABAAgGAAAA","159":"b:x/Vf///5/xf/++9D+P+/8Pz///8HAAAA","264":[35,54,55,72],"281":"b:AEEEMQlI+RMeAQBAqPoVsFzTX28HAAAA"},"packages/frontend/editor-ui/src/features/collaboration/projects/composables/useFavoriteNavItems.ts":{"23":"b:x/Vf///5/xf/++9D+P+/8Pz///8HAAAA","155":"b:x/Vf///5/xf/++9D+P+/8Pz///8HAAAA"},"packages/frontend/editor-ui/src/features/collaboration/projects/composables/useProjectPages.ts":{"9":"b:x/Vf///5/xf/++9D+P+/8P7///8HAAAA","13":"b:AAEBEQsA8BABAQAACBgAMAAAAAACAAAA","16":"b:AAEBGw8A8BCBGWAACBiAMAAAAAgGAAAA","19":"b:AAEBGw8A8BCBGWAACBiAMAAAAAgGAAAA"},"packages/frontend/editor-ui/src/features/collaboration/projects/projects.api.ts":{"7":[24,32,35,108,55,60,125,72,161],"11":[53,54,55,161],"27":[97,33,122,124,64,125,126,161],"45":"b://Vf///5/7f/+//j//////z///8HAAAA","49":"b://Vf///5/7f/+//j//////z///8HAAAA","53":"b:AAEFMQ8A8BCBAQAACBgAMAAAEAoCAAAA","57":[24,35,108,55,125,72,161],"64":[24,35,108,55,125,72,161],"85":"b://Vf///5/7f/+//j//////z///8HAAAA","89":[32,108],"97":[32]},"packages/frontend/editor-ui/src/features/collaboration/projects/projects.routes.ts":{"10":"b:AAAAKwwA8ACAGXAgABgANAAAAAgGAAAA","11":[16,28,8,33,99,54,55,60,64,125],"12":[16,28,8,33,99,54,55,60,64,125],"13":[24,32,35,108,55,60,125,72,161],"16":[119],"18":"b:IAEBOw4A8BCBGXAgCBiANAAAAAgGAAAA","22":"b:AAEBEQ4A8BABAQAACAgAMAAAAAgCAAAA","33":"b:IAAAKwwA8ACAGXAgABgANAAAAAgGAAAA","44":[16,28,8,33,99,54,55,60,64,125],"64":[52,107,53],"74":[119],"134":"b://Vf///5/7f/+/////////////8HAAAA","146":[24,32,35,108,55,60,125,72,161],"147":[24,32,35,108,55,60,125,72,161],"169":"b:IAABOw4AwBCBGXAgABCAJAAAAAAGAAAA","184":"b://Vf///5/7f/+/////////////8HAAAA"},"packages/frontend/editor-ui/src/features/collaboration/projects/projects.store.ts":{"32":"b://Vf///5/7f/+/////////////8HAAAA","51":"b://Vf///5/7f/+//z//////z///8HAAAA","54":"b://Vf///5/7f/+//z//////z///8HAAAA","58":"b://Vf///5/7f/+/////////////8HAAAA","64":[161],"68":[97,33,60,64,125],"69":"b:3/Vf///5/7f/+//j//////z///8HAAAA","70":"b:x/Vf///5/xf/++9D+P+/8Pz///8HAAAA","71":"b:3/Vf///5/7f/+//j//////z///8HAAAA","75":"b:3/Vf///5/7f/+//j//////z///8HAAAA","81":"b://Vf///5/7f/+/////////////8HAAAA","84":"b://Vf///5/7f/+//z//////z///8HAAAA","89":"b:x/Vf///5/xf/++9D+P+/8Pz///8HAAAA","90":"b://Vf///5/7f/+/////////////8HAAAA","95":"b:OAEFO/8BwBCB2WcgHRCAJAAAEAIGAAAA","99":[24,32,35,108,55,60,125,72,161],"103":"b://Vf///5/7f/+//j//////z///8HAAAA","107":"b://Vf///5/7f/+//j//////z///8HAAAA","119":[53,54,55,161],"129":[97,33,122,124,64,125,126,161],"139":"b:AAEFMQ8A8BCBAQAACBgAMAAAEAoCAAAA","142":"b:AAEBEQ8A8BABAQAACBgAMAAAAAgCAAAA","146":[18,28,29,8,99,55,148,71,153,161],"153":"b://Vf///5/7f/+//v//////////8HAAAA","159":[24,35,108,55,125,72,161],"166":[24,35,108,55,125,72,161],"174":[24,35,108,55,125,72,161],"192":[32,108],"200":[32],"220":"b://Vf///5/7f/+//j//////z///8HAAAA","224":"b:R3AIKPCBlwP0AwABgMAAMBwth+kGAAAA","232":[125],"264":[54,55],"301":[24,32,35,108,55,60,125,72,161],"312":"b://Vf///5/7f/+/////////////8HAAAA"},"packages/frontend/editor-ui/src/features/collaboration/projects/projects.utils.ts":{"16":[97,33,122,124,64,125,126,161],"18":[97,33,122,124,64,125,126,161],"31":"b:AAEAGQIA8BCBAQAACBiAIAAAAAAGAAAA","33":[53,54,55,161],"50":"b:kkEBEAoA4BEBAwAAShgC9AAAEAAGAAAA","64":[54,55],"69":"b://Vf///5/7f/+/////////////8HAAAA"},"packages/frontend/editor-ui/src/features/core/dataTable/components/NodeStorageLimitCallout.vue":{"9":"b:xvVD3IJYjQfaAYBKwOU+sNz+/5MCAAAA","13":"b:xvVD3IJYjQfaAYBKwOU+sNz+/5MCAAAA","28":"b:xvVD3IJYjQfaAYBKwOU+sNz+/5MCAAAA"},"packages/frontend/editor-ui/src/features/core/dataTable/dataTable.api.ts":{"13":"b:AAAACwwAwACAGWAAABAAAAAAAAAGAAAA","40":[34,35],"61":[35],"77":[34],"93":[34],"109":[34],"122":[34],"156":[34,35],"176":[34],"193":[34],"214":[34],"220":[34],"238":"b:3/Vf///5/7f/+//j//////z///8HAAAA"},"packages/frontend/editor-ui/src/features/core/dataTable/dataTable.store.ts":{"37":"b://Vf///5/7f/+/////////////8HAAAA","50":[34,35],"56":"b:3/Vf///5/7f/+//j//////z///8HAAAA","64":[34,35],"72":"b://Vf///5/7f/+//z//////z///8HAAAA","76":"b:AAAACwwAwACAGWAAABAAAAAAAAAGAAAA","101":[34,35],"176":[35],"179":[35],"185":[34],"197":[34],"199":[34],"207":[34],"215":[34],"246":[34,35],"247":[34,35],"254":[34],"266":[34],"274":[34],"288":[34],"289":[34],"323":[34,35],"341":[34],"351":[34],"366":[34],"370":"b:3/Vf///5/7f/+//j//////z///8HAAAA"},"packages/frontend/editor-ui/src/features/core/folders/components/FolderBreadcrumbs.vue":{"59":"b:x/Vf/PL5/wf+I4hD6O8/8Pz///8HAAAA","65":"b:x/Vf/PL5/wf+I4hD6O8/8Pz///8HAAAA","67":"b:x/Vf/PL5/wf+I4hD6O8/8Pz///8HAAAA","76":[107],"88":"b:x/Vf/PL5/wf+I4hD6O8/8Pz///8HAAAA","92":[107,53],"96":[52,107,53,54,55,72,161],"107":"b:x/Vf/PL5/wf+I4hD6O8/8Pz///8HAAAA","111":"b:x/Vf/PL5/wf+I4hD6O8/8Pz///8HAAAA","147":[107],"162":[107],"174":[107,53],"178":[52],"185":[52,107,53],"196":[107],"208":"b:x/Vf/PL5/wf+I4hD6O8/8Pz///8HAAAA","209":"b:x/Vf/PL5/wf+I4hD6O8/8Pz///8HAAAA","212":"b:x/Vf/PL5/wf+I4hD6O8/8Pz///8HAAAA","219":"b:AAAAAJAB4ACAAQAAAAgAAAABAAgAAAAA","242":[52,107,53],"249":[52,107,53],"275":[52,107,53,54,55,72,161],"305":[107,53]},"packages/frontend/editor-ui/src/features/core/folders/components/ProjectBreadcrumb.vue":{"30":"b:x/Vf/P75/wf+I4hD6O8/8Pz///8HAAAA","52":"b:x/Vf/P75/wf+I4hD6O8/8Pz///8HAAAA","63":"b:x/Vf/P75/wf+I4hD6O8/8Pz///8HAAAA","75":[52,107,53],"79":[52],"91":[52,107,53],"100":"b:x/Vf/P75/wf+I4hD6O8/8Pz///8HAAAA"},"packages/frontend/editor-ui/src/features/core/folders/composables/useParentFolder.ts":{"5":"b:x/Vf/PL5nwf+I4hP6O8/8P////8HAAAA","11":"b:wuVX9AJ4mQSaIYBO6C8/8O/yfl4DAAAA"},"packages/frontend/editor-ui/src/features/core/folders/folders.constants.ts":{"16":"b://Vf///5/7f/+/////////////8HAAAA"},"packages/frontend/editor-ui/src/features/core/folders/folders.store.ts":{"23":"b://Vf///5/7f/+/////////////8HAAAA","40":"b:AAAACwwA8ACAGWAAABgAMAAAAAAGAAAA","41":[52,107,53],"52":[107,53],"56":[107,53],"64":[52,107,53],"75":[52,107,53],"81":[52,107,53],"99":"b:AAAACwwA8ACAGWAAABgAMAAAAAgGAAAA","115":[53],"116":[53],"121":[53],"125":[53],"134":[53,54,55],"174":[53],"183":[53],"190":[53],"206":[53],"219":[52,53],"249":[53],"262":[107],"273":[107],"342":[107]},"packages/frontend/editor-ui/src/features/credentials/components/NodeCredentials.vue":{"91":"b:xvVH3MJYjQfaAYBCxOU+sNz+/5MCAAAA","118":[16,1,7,28,8,102,55,113,14,148],"147":[16,1,7,28,8,102,55,113,14,148],"149":"b:wmEFHEJYiABAAABAxKECsIgAWhAAAAAA","164":"b:wmVHXEJYiABSAABCxKEOsIgKWhACAAAA","166":"b:xvVH3MJYjQfaAYBCxOU+sNz+/5MCAAAA","178":"b:xvVH3MJYjQfaAYBCxOU+sNz+/5MCAAAA","179":[94,8,103,51,14],"183":"b:wmUBFABYiABAAABAxKECsIgAWhAAAAAA","194":"b:wmUFHEJYiABAAABAxKECsIgAWhAAAAAA","198":"b:wkEEGAIAiAAAAAAAhAACMAAAEAAAAAAA","206":"b:wmUFHEJYiABAAABAxKECsIgAWhAAAAAA","273":"b:xvVH3MJYjQfaAYBCxOU+sNz+/5MCAAAA","274":"b:xvVH3MJYjQfaAYBCxOU+sNz+/5MCAAAA","301":"b:xvVH3MJYjQfaAYBCxOU+sNz+/5MCAAAA","351":"b:xvVH3MJYjQfaAYBCwOU+sNz+/5MCAAAA","363":"b:xvVH3MJYjQfaAYBCxOU+sNz+/5MCAAAA","364":"b:xvVH3MJYjQfaAYBCxOU+sNz+/5MCAAAA","371":[16,1,7,28,8,102,55,113,14,148],"455":"b:xvVH3IJYjQfaAYBCxOU+sNz+/5MCAAAA","460":"b:wkEEGAIAiAAAAAAAhAACMAAAEAAAAAAA","467":[27],"471":"b:wkEEGAIAiAAAAAAAhAACMAAAEAAAAAAA","479":[8],"498":[16,1,7,28,8,102,55,113,14,148],"533":[148],"543":"b:gkEEGAIAiAAAAAAABAACMAAAEAAAAAAA","635":[28,8,55,124,125,14,148],"670":"b:wkUFGAIAiAAAAAAAhAACMAAAEAAAAAAA","674":"b:wmUFHEJYiABAAABAxKECsIgAWhAAAAAA","737":"b:wkEEGAIAiAAAAAAAhAACMAAAEAAAAAAA","750":[8],"768":"b:wmUFHEJYiABAAABAxKECsIgAWhAAAAAA","787":[1,7,27,98,33,51],"791":"b:wkEEGAIAiAAAAAAAhAACMAAAEAAAAAAA","795":[1,7],"796":[1,7],"801":[16,10],"806":"b:AmUFHEBYgABAAABARKECgIgAWhAAAAAA","814":"b:AEEBFAAYgABAAAAAAIAAAAAAWhAAAAAA","819":"b:AmUFHEBYgABAAABARKECgIgAWhAAAAAA","826":"b:AmUFHEBYgABAAABARKECgIgAWhAAAAAA","830":"b:AmUFFEBYgABAAABAQKECgIgAWhAAAAAA","834":"b:wmEFHEJYiABAAABAxKECsIgAWhAAAAAA","838":[16,10],"868":"b:xvVH3MJYjQfaAYBCxOU+sNz+/5MCAAAA","884":"b:wmUFHEJYiABAAABAxKECsIgAWhAAAAAA","886":[18],"944":[16],"952":[16],"953":[16],"972":"b:AmEFFEBYgABAAABAQKECgIgAWhAAAAAA","973":"b:AmEFFEBYgABAAABAQKECgIgAWhAAAAAA","991":[16,1,28,8,102,55,113,14,148],"992":"b:AmEFFEBYgABAAABAQKECgIgAWhAAAAAA","1023":[27,98,33,51],"1030":"b:wkEEGAIAiAAAAAAAhAACMAAAEAAAAAAA","1035":"b:wkEEGAIAiAAAAAAAhAACMAAAEAAAAAAA","1056":"b:wkEEGAIAiAAAAAAAhAACMAAAEAAAAAAA","1057":"b:wkEEGAIAiAAAAAAAhAACMAAAEAAAAAAA","1063":[1,7],"1088":[27],"1107":[8]},"packages/frontend/editor-ui/src/features/credentials/composables/useCredentialOAuth.ts":{"42":"b:kmUFHEJYgBBBAgBATqECoIgAWhAAAAAA","60":"b:kmUFHEJYgBBBAgBATqECoIgAWhAAAAAA","73":"b:kkUBEAIAgBABAgAAQgACIAAAEAAAAAAA","83":"b:AmUFHEBYgABAAABARKECgIgAWhAAAAAA","100":"b:AEEBFAAQgABAAAAAAIAAAAAAWhAAAAAA","110":[16],"122":"b:AEUBFAAQgABAAAAAAIAAAAAAWhAAAAAA","129":"b:AEUBFAAQgABAAAAAAIAAAAAAWhAAAAAA","135":"b:AEUBFAAQgABAAAAAAIAAAAAAWhAAAAAA","154":[16,10],"387":"b:xvVH3IJYjQfaAYBCxOU+sNz+/5MCAAAA"},"packages/frontend/editor-ui/src/features/credentials/composables/useNodeCredentialOptions.ts":{"23":"b:xvVH3MJYjQfaAYBKxOc/sNz+/5MDAAAA","31":"b:wmUFHEBYiABAAABAxKECsIgAWhABAAAA","32":"b:wmUFHEJYiABAAABAxKECsIgAWhABAAAA","37":"b:xvVH3MJYjQfaAYBCxOc/sNz+/5MDAAAA","44":"b:xvVH3MJYjQfaAYBCxOc/sNz+/5MDAAAA","45":"b:wmUFHEJYiABAAABAxKECsIgAWhABAAAA","55":"b:wmUFHEJYiABAAABAxKECsIgAWhABAAAA","57":"b:wmUFHEJYiABAAABAxKECsIgAWhABAAAA","59":"b:wkEEGAIAiAAAAAAAhAACMAAAEAAAAAAA","68":"b:wkEEGAIAiAAAAAAAhAACMAAAEAAAAAAA","71":[27,8,33],"77":"b:wmVHXEJYiABSAABCxKEOsMgKWhADAAAA","95":"b:wmUBHAJYiABAAABAxKECsIgAWhABAAAA","106":[94,8,103,51,14],"114":"b:wmUBFABYiABAAABAxKECsIgAWhABAAAA","127":"b:wmUFHEJYiABAAABAxKECsIgAWhABAAAA","139":"b:AEEBFAAYgABAAAAAAIAAMAAAWhAAAAAA","144":"b:wmUFHEJYiABAAABAxKECsIgAWhAAAAAA","150":"b:wkEEGAIAiAAAAAAAhAACMAAAEAAAAAAA"},"packages/frontend/editor-ui/src/features/credentials/credentials.api.ts":{"15":"b://Vf///5/7f/++/j////9/z///8HAAAA","33":"b://Vf///5/7f/++/j////9/z///8HAAAA","37":"b:kkEBEAIAgBABAgAASAACIAAAEAAAAAAA","60":"b:AAEBGw4A4LCBG2AAChAAIQAAAAAGAAAA","76":"b://Vf/PP5nwf+I4hD7e8/8Pz///8HAAAA","85":"b:kkEAEAIAgBABAgAACAACIAAAEAAAAAAA","92":[8],"104":[97,8,33,64,125],"117":[18,5,97,8,33,73],"127":[73],"138":[99,148],"148":"b:kkEEEAAAgAABAgAAAgACIAAAAAAAAAAA"},"packages/frontend/editor-ui/src/features/credentials/credentials.store.ts":{"41":"b://Vf///5/7f/+/////////////8HAAAA","49":[18],"53":[18],"67":"b://Vf///5/7f/++/j////9/z///8HAAAA","68":"b://Vf/PP537f/I4hD7+9/8fz///8HAAAA","73":"b:+mUFH0/Y/hDDG2hAz7kCsMgAWhgHAAAA","74":"b:kmEACAAISABBAgAAjBAAoEgAAAAAAAAA","77":"b:+kEEGAGAjgAAAggAhwACMAAAEAAAAAAA","80":"b:+kEEGAGAjgAAAggAhwACMAAAEAAAAAAA","82":"b:+kEEGAGAjgAAAgAAhwACMAAAEAAAAAAA","91":"b:wmUFHEJYiABAAABAxKECsIgAWhABAAAA","95":"b:wmUFHEJYiABAAABAxKECsIgAWhABAAAA","97":"b:wmEEGAIIiABAAAAAhAACsAgAEAAAAAAA","107":[1,7,28,8,55,113,14,148],"108":[1,7,28,8,55,113,14,148],"112":[1,7,28,8,55,113,14,148],"116":[1,7],"124":"b:/vVH3MPYzxfbA4hKz+c/sNz+/5MHAAAA","125":"b:/vVH3MPYzxfbA4hKz+c/sNz+/5MHAAAA","128":"b:+kEFGAOAjhABAgAAzwACMAgAEAABAAAA","129":"b:+kEFGAOAjhABAgAAzwACMAgAEAABAAAA","132":[1,7,28,98,8,33,51,55,113,14,148],"133":[1,7,28,98,8,33,51,55,113,14,148],"139":"b:+kEEGAGAjgAAAggAhwACMAAAEAAAAAAA","140":"b:8kEEGAGAjgAAAggAhwACMAAAEAAAAAAA","151":"b:kkEAEAIAgAABAgAAAgACIAAAAAAAAAAA","152":"b:kkEAEAIAgAABAgAAAgACIAAAAAAAAAAA","160":"b:kkEAEAIAgAABAgAAAgACIAAAAAAAAAAA","165":[27,8],"166":[27,8],"172":[27,8],"196":"b:kkEBEAIAgBABAgAASgACIAAAEAAAAAAA","197":"b:kkEBEAIAgBABAgAASgACIAAAEAAAAAAA","214":"b:kkEBEAIAgBABAgAASgACIAAAEAAAAAAA","215":"b:kkEBEAIAgBABAgAASgACIAAAEAAAAAAA","222":"b:x/Vf/LL5nwf+I4hC6O8/8Pz///8HAAAA","223":"b:x/Vf/LL5nwf+I4hC6O8/8Pz///8HAAAA","234":"b://Vf///5/7f/++/D////9/z///8HAAAA","235":"b://Vf///5/7f/++/D////9/z///8HAAAA","253":"b://Vf///5/7f/O+hD7/8/8fz///8HAAAA","254":"b:+mEEGAOIz7BNAgAArxIE8HgCAEAGAAAA","265":"b:kkEAEAIAgBABAgAACgACIAAAEAAAAAAA","278":"b://Vf///5/7f/++/j////9/z///8HAAAA","286":"b:AAEBGw4A4LCBG2AAChAAIQAAAAAGAAAA","318":"b://Vf/PP5nwf+I4hD7e8/8Pz///8HAAAA","329":[18,5,97,8,33,73],"337":"b:xvVH3MJYjQfaAYBKxOc/sNz+/5MDAAAA","350":"b:kkEAEAIAgBABAgAACAACIAAAEAAAAAAA","388":[97,8,33,64,125],"401":[8],"429":[99,148],"433":[73],"437":"b:kkEEEAAAgAABAgAAAgACIAAAAAAAAAAA","461":"b:kkEBEAIAgBABAgAASAACIAAAEAAAAAAA","481":[64,125],"491":[64,125],"568":"b:IAAEAPABAAAAwg8AEgAAAAAAAAAAAAAA","577":[18,5,97,36,37,38,39,40,73],"579":[18,5,97,36,37,38,39,40,73]},"packages/frontend/editor-ui/src/features/credentials/quickConnect/composables/useQuickConnect.ts":{"19":"b:1vVH3MJ4jRfbA4BKzuV+sNz+/5MCAAAA","33":"b:xvVH3MJYjQfaAYBKxOU+sNz+/5MCAAAA","41":"b:gEFBlABoiAAKAAAAwABAsMxQfhIAAAAA","51":"b:gmUFHEBYgABAAABARKECgIgAWhAAAAAA","64":"b:gEFBlABoiAAKAAAAwABAsMxQfhIAAAAA","71":"b:xvVD3IJYjQfaAYBKwOU+sNz+/5MCAAAA","117":"b:1vVH3IJ4jRfbA4BCxuV+sNz+/5MCAAAA"},"packages/frontend/editor-ui/src/features/execution/executions/components/ViewSubExecution.vue":{"9":[2,89,6,151,152],"23":[2,89,6,151,152],"27":[151,152],"41":[2,89,6,151,152]},"packages/frontend/editor-ui/src/features/execution/executions/composables/useExecutionData.ts":{"8":"b:RIAAQIAADQFYAIACgEUgAACqr4EAAAAA","10":"b:xvVD3IJYjQfaAYBKwOU+sMz+/5MCAAAA","12":"b:xvVD3IJYjQfaAYBKwOU+sMz+/5MCAAAA"},"packages/frontend/editor-ui/src/features/execution/executions/composables/useExecutionRedaction.ts":{"13":"b:xvVL3LJZjQfaAYBKwOU+sNz+/5MCAAAA","22":[19,1,7,2,36,37,39,40,56,57,6,159],"26":[19,1,7,2,36,37,39,40,57,6,159],"28":[56],"30":[56]},"packages/frontend/editor-ui/src/features/execution/executions/executions.store.ts":{"31":"b://Vf///5/7f/+/////////////8HAAAA","43":[1,56,131,137,68,69,6,157,158,159],"64":[1,56,131,137,68,69,6,157,158,159],"67":[137,69,157,158,159],"74":[1,56,131,137,68,69,6,157,158,159],"75":[56,131,137,68,69,6,157,158,159],"104":[1,56,131,137,68,69,6,157,158,159],"112":[1,56,131,137,68,69,6,157,158,159],"113":[159],"124":[1,56,131,137,68,69,6,157,158,159],"134":[159],"141":[68],"146":[157],"150":[1,56,131,137,68,69,6,157,158,159],"158":[1,56,131,137,68,69,6,157,158,159],"178":[1,56,131,137,68,69,6,157,158,159],"203":[56,131,137,68,69,6,157,158,159],"217":[1,56,131,137,68,69,6,157,158,159],"223":[159],"231":[1,56,131,137,68,69,6,157,158,159],"236":[1,56,131,137,68,69,6,157,158,159],"295":[6],"317":[68],"339":[1,131,137,157,158,159],"347":[1,131,137,157,158,159]},"packages/frontend/editor-ui/src/features/execution/executions/executions.utils.ts":{"50":[1,56,131,137,68,69,6,157,158,159],"53":[56,131,137,68,69,6,157,158,159],"56":"b://Vf///5/7f/+/////////////8HAAAA","70":[1,56,131,137,68,69,6,157,158,159],"132":[89,22,103],"145":"b:x5BI4LKBDwZ6AIACgGcgAA7+r+EAAAAA","147":"b:x5BK4IKBDwZ6AIACgGcgAA7+r+EAAAAA","198":[6],"255":"b:xpBIQLABDQdYAIACgGUgAAT6r4EAAAAA","256":"b:xpBIQLABDQdYAIACgGUgAAT6r4EAAAAA","264":"b:xpBIQLABDQdYAIACgGUgAAT6r4EAAAAA","273":"b:xpBIQLABDQdYAIACgGUgAAT6r4EAAAAA","274":"b:xpBIQLABDQdYAIACgGUgAAT6r4EAAAAA","332":"b:x5AI4LKBDwZ6AIACgGcgAAz+r+EAAAAA","370":"b:woAAAAIAAAAQAIAAAAAAAAAKAmAAAAAA","419":"b:x5AI4LKBDwd6AIACgGcgAAz+r+EAAAAA","436":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","450":[18],"457":[18,50,68,139,147],"463":"b:R/FH/PL5jwX+A4hO4Oc98Nz///sHAAAA"},"packages/frontend/editor-ui/src/features/execution/logs/components/ChatMessagesPanel.vue":{"30":[19,1,7,2,8,10,52,113,57,129,6,13],"57":[19,1,7,2,52,57,129,6],"61":[19,1,7,2,8,10,52,113,57,129,6,13],"67":[19,52],"76":[19,1,7,2,52,57,129,6],"96":[19,1,7,2,52,57,129,6],"105":[19,1,7,2,8,10,52,113,57,129,6,13],"107":[19,1,7,2,52,57,129,6],"117":[19,1,7,2,8,10,52,113,57,129,6,13],"119":[52],"135":[19,1,7,2,8,10,52,113,57,129,6,13],"137":[19,1,7,2,8,10,52,113,57,129,6,13],"145":[52],"154":[19,1,7,2,8,10,52,113,57,129,6,13],"156":[52],"166":[19,1,7,2,8,10,52,113,57,129,6,13],"168":[52],"182":[19,1,7,2,8,10,52,113,57,129,6,13],"190":[19,1,2,52,6],"193":[19,1,7,2,8,10,52,113,57,129,6,13],"208":[19,1,7,2,8,10,52,113,57,129,6,13],"210":[52],"214":[19,1,7,2,8,10,52,113,57,129,6,13],"221":[19,1,7,2,8,10,52,113,57,129,6,13],"227":[19,1,7,2,8,10,52,113,57,129,6,13]},"packages/frontend/editor-ui/src/features/execution/logs/components/LogDetailsPanel.vue":{"41":[19,1,7,2,36,37,39,40,52,57,6,159],"79":[19,1,7,2,36,37,39,40,52,57,6,159],"80":[19,1,7,2,36,37,39,40,52,57,6,159],"81":[19,1,7,2,36,37,39,40,52,57,6,159],"86":[19,1,7,2,36,37,39,40,52,57,6,159],"93":[19,1,7,2,36,37,39,40,52,57,6,159],"94":[19,1,39,52,6],"100":[52],"101":[52],"102":[19,1,7,2,36,37,39,40,52,57,6,159],"103":[19,1,7,2,36,37,39,40,52,57,6,159],"104":[19,1,7,2,36,37,39,40,52,57,6,159],"108":[1,52,6],"110":[52],"114":[1,52,6],"115":[19,1,7,2,36,37,39,40,52,57,6,159],"123":[52],"133":[19,1,7,2,36,37,39,40,52,57,6,159],"144":[19,1,7,2,36,37,39,40,52,57,6,159],"158":[19,1,7,2,36,37,39,40,52,57,6,159],"166":[52],"173":[19,1,7,2,36,37,39,40,52,57,6,159],"179":[19,1,7,2,36,37,39,40,52,57,6,159],"186":[19,1,7,2,36,37,39,40,52,57,6,159],"192":[19,1,7,2,36,37,39,40,52,57,6,159],"203":[52],"221":[1,52,6],"230":[52],"243":[52],"254":[52],"274":[52],"282":[52],"313":[52],"342":[52]},"packages/frontend/editor-ui/src/features/execution/logs/components/LogsOverviewPanel.vue":{"39":"b:x/Vf/PL5nwf+I4hC6O8/8P7///8HAAAA","51":"b:xgAIALABEAIAAAAAAAAAAAIAAIAAAAAA","52":"b:xgAIALABEAIAAAAAAAAAAAIAAIAAAAAA","57":"b:xgAIALABEAIAAAAAAAAAAAIAAIAAAAAA","59":"b:xgAIALABEAIAAAAAAAAAAAIAAIAAAAAA","67":"b:xgAIALABEAIAAAAAAAAAAAIAAIAAAAAA","72":"b:xgAIALABEAIAAAAAAAAAAAIAAIAAAAAA","75":"b:xgAIALABEAIAAAAAAAAAAAIAAIAAAAAA","78":[52],"80":"b:x/Vf/PL5nwf+I4hC6O8/8P7///8HAAAA","94":"b:x/Vf/PL5nwf+I4hC6O8/8P7///8HAAAA","98":"b:x5AI4LKBDwZ6AIACgGcgAAz+r+EAAAAA","107":"b:x5AI4LKBDwZ6AIACgGcgAAz+r+EAAAAA","125":[19,1,7,2,36,37,39,40,57,129,6,159],"153":[6],"154":[19,6]},"packages/frontend/editor-ui/src/features/execution/logs/components/LogsOverviewRow.vue":{"49":"b:xgAIALABEAIAAAAAAAAAAAIAAIAAAAAA","50":"b:xgAIALABEAIAAAAAAAAAAAIAAIAAAAAA","51":"b:xgAIALABEAIAAAAAAAAAAAIAAIAAAAAA","52":"b:xgAIALABEAIAAAAAAAAAAAIAAIAAAAAA","55":"b:xgAIALABEAIAAAAAAAAAAAIAAIAAAAAA","61":[52],"64":"b:xgAIALABEAIAAAAAAAAAAAIAAIAAAAAA","67":"b:xgAIALABEAIAAAAAAAAAAAIAAIAAAAAA","70":[19,1,7,2,36,37,39,40,52,57,129,6],"71":"b:xgAIALABEAIAAAAAAAAAAAIAAIAAAAAA","74":[19,1,7,2,36,37,39,40,52,57,129,6],"79":[52],"84":[19,1,7,2,36,37,39,40,52,57,129,6],"87":[19,1,7,2,36,37,39,40,52,57,129,6],"100":[19,1,7,2,36,37,39,40,52,57,129,6],"103":[19,1,7,2,36,37,39,40,52,57,129,6],"127":[19,1,7,2,36,37,39,40,52,57,129,6],"131":"b:xgAIALABEAIAAAAAAAAAAAIAAIAAAAAA","133":"b:xgAIALABEAIAAAAAAAAAAAIAAIAAAAAA","150":"b:xgAIALABEAIAAAAAAAAAAAIAAIAAAAAA","151":"b:xgAIALABEAIAAAAAAAAAAAIAAIAAAAAA","153":"b:xgAIALABEAIAAAAAAAAAAAIAAIAAAAAA","187":[1,7,52,6],"204":[19,1,7,2,36,37,39,40,52,57,129,6],"205":[19,1,7,2,36,37,39,40,52,57,129,6],"207":[19,1,7,2,36,37,39,40,52,57,129,6],"213":[19,1,7,2,36,37,39,40,52,57,129,6],"223":[19,1,7,2,36,37,39,40,52,57,129,6],"231":[52],"251":"b:xgAIALABEAIAAAAAAAAAAAIAAIAAAAAA","274":"b:xgAIALABEAIAAAAAAAAAAAIAAIAAAAAA"},"packages/frontend/editor-ui/src/features/execution/logs/components/LogsOverviewRows.vue":{"44":"b:xgAIALABEAIAAAAAAAAAAAIAAIAAAAAA","45":"b:xgAIALABEAIAAAAAAAAAAAIAAIAAAAAA","51":"b:xgAIALABEAIAAAAAAAAAAAIAAIAAAAAA","55":"b:xgAIALABEAIAAAAAAAAAAAIAAIAAAAAA","59":[52,6],"79":"b:xgAIALABEAIAAAAAAAAAAAIAAIAAAAAA","80":"b:xgAIALABEAIAAAAAAAAAAAIAAIAAAAAA","81":"b:xgAIALABEAIAAAAAAAAAAAIAAIAAAAAA","91":"b:xgAIALABEAIAAAAAAAAAAAIAAIAAAAAA","93":"b:xgAIALABEAIAAAAAAAAAAAIAAIAAAAAA","94":"b:xgAIALABEAIAAAAAAAAAAAIAAIAAAAAA","99":[19,1,7,2,36,37,39,40,52,57,6,159],"101":[52],"110":"b:xgAIALABEAIAAAAAAAAAAAIAAIAAAAAA","126":[52],"127":[52,6],"128":[52,6],"129":[19,52,6]},"packages/frontend/editor-ui/src/features/execution/logs/components/LogsPanel.vue":{"21":"b:x/Vf/PL5nwf+I4hC6O8/8P7///8HAAAA","31":"b:x/Vf/PL5nwf+I4hC6O8/8P7///8HAAAA","70":[19,1,7,2,36,37,39,40,52,57,6,159],"73":"b:x/Vf/PL5nwf+I4hC6O8/8P7///8HAAAA","74":"b:x/Vf/PL5nwf+I4hC6O8/8P7///8HAAAA","77":"b:x/Vf/PL5nwf+I4hC6O8/8P7///8HAAAA","86":[19,1,7,2,36,37,39,40,52,57,6,159],"91":[19,1,7,2,36,37,39,40,52,57,6,159],"97":"b:x/Vf/PL5nwf+I4hC6O8/8P7///8HAAAA","115":[52],"123":[52,6],"130":[52,6],"140":[52],"147":[52],"152":"b:x/Vf/PL5nwf+I4hC6O8/8P7///8HAAAA","184":[19,1,7,2,8,10,113,57,129,6,13],"210":"b:x/Vf/PL5nwf+I4hC6O8/8P7///8HAAAA","253":[19,1,7,2,36,37,39,40,57,6,159]},"packages/frontend/editor-ui/src/features/execution/logs/components/LogsPanelActions.vue":{"20":"b:x/Vf/PL5nwf+I4hC6O8/8P7///8HAAAA","27":"b:xgAIALABEAIAAAAAAAAAAAIAAIAAAAAA","29":"b:x/Vf/PL5nwf+I4hC6O8/8P7///8HAAAA","30":"b:x/Vf/PL5nwf+I4hC6O8/8P7///8HAAAA","33":"b:xgAIALABEAIAAAAAAAAAAAIAAIAAAAAA","42":[52],"51":"b:x/Vf/PL5nwf+I4hC6O8/8P7///8HAAAA","58":"b:x/Vf/PL5nwf+I4hC6O8/8P7///8HAAAA","77":[6],"84":"b:x/Vf/PL5nwf+I4hC6O8/8P7///8HAAAA"},"packages/frontend/editor-ui/src/features/execution/logs/components/LogsPanelHeader.vue":{"8":"b:x/Vf/PL5nwf+I4hC6O8/8P7///8HAAAA","10":[19,1,52,6,159],"13":"b:x/Vf/PL5nwf+I4hC6O8/8P7///8HAAAA","20":"b:x/Vf/PL5nwf+I4hC6O8/8P7///8HAAAA"},"packages/frontend/editor-ui/src/features/execution/logs/components/LogsViewExecutionSummary.vue":{"18":"b:xgAIALABEAIAAAAAAAAAAAIAAIAAAAAA","20":"b:xgAIALABEAIAAAAAAAAAAAIAAIAAAAAA","37":"b:xgAIALABEAIAAAAAAAAAAAIAAIAAAAAA"},"packages/frontend/editor-ui/src/features/execution/logs/components/LogsViewKeyboardEventListener.vue":{"8":"b:x/Vf/PL5nwf+I4hC6O8/8P7///8HAAAA","11":"b:AiAEBABIEASGAwAAAAAKAGwEAPkGAAAA","24":[52],"27":"b:x/Vf/PL5nwf+I4hC6O8/8P7///8HAAAA"},"packages/frontend/editor-ui/src/features/execution/logs/components/LogsViewNodeName.vue":{"12":"b:xgAIALABEAIAAAAAAAAAAAIAAIAAAAAA","18":"b:xgAIALABEAIAAAAAAAAAAAIAAIAAAAAA"},"packages/frontend/editor-ui/src/features/execution/logs/components/LogsViewRunData.vue":{"43":[19,1,7,2,36,37,39,40,52,57,6,159],"55":[19,1,7,2,36,37,39,40,52,57,6,159],"58":[19,1,7,2,36,37,39,40,52,57,6,159],"81":[19,1,7,2,36,37,39,40,52,57,6,159],"88":[52],"92":[1,52,6],"94":[19,1,7,2,36,37,39,40,52,57,6,159],"122":[52],"124":[19,1,7,2,36,37,39,40,52,57,6,159],"126":[19,1,7,2,36,37,39,40,52,57,6,159],"130":[19,1,7,2,36,37,39,40,52,57,6,159],"137":[1,52,6],"141":[52,6],"143":[52,6],"152":[52],"160":[52],"166":[52],"170":[52],"173":[52]},"packages/frontend/editor-ui/src/features/execution/logs/composables/useChatState.ts":{"41":"b:x/Vf/PL5nwf+I4hC6O8/8P7///8HAAAA","59":[19,1,7,2,52,57,129,6],"60":[52,6],"63":[19,1,7,2,52,57,129,6],"65":[52],"66":[19,1,7,2,8,10,52,113,57,129,6,13],"74":[19,1,7,2,8,10,52,113,57,129,6,13],"101":[19,1,7,2,8,10,52,113,57,129,6,13],"120":[19,1,7,2,8,10,52,113,57,129,6,13],"125":[19,1,7,2,8,10,52,113,57,129,6,13],"130":[19,1,7,2,8,10,52,113,57,129,6,13],"135":[19,1,7,2,52,57,129,6],"149":[19,1,7,2,52,57,129,6],"165":[19,1,7,2,52,57,129,6],"177":[52],"178":[52],"179":[52],"183":[52],"184":[52],"230":[19,1,7,2,52,57,129,6],"276":[19,1,7,2,52,57,129,6],"290":[19,1,7,2,52,57,129,6],"321":[52,6],"329":[19,1,2,52,55,129],"342":[52],"350":"b:x/Vf/PL5nwf+I4hC6O8/8P7///8HAAAA","351":"b:x/Vf/AL4nwf+I4hC6O8/8P7///8HAAAA","362":[19,1,7,2,8,10,52,113,57,129,6,13]},"packages/frontend/editor-ui/src/features/execution/logs/composables/useClearExecutionButtonVisible.ts":{"8":"b:x/Vf/PL5nwf+I4hC6O8/8P7///8HAAAA","13":"b:x/VP/PL5nwf+A4hC4Oc/8Nz///sHAAAA","14":"b:x/Vf/PL5nwf+I4hC6O8/8Pz///8HAAAA","15":"b:x/Vf/PL5nwf+I4hC6O8/8P7///8HAAAA","17":"b:x/Vf/PL5nwf+I4hC6O8/8Pz///8HAAAA","18":"b:x/Vf/PL5nwf+I4hC6O8/8Pz///8HAAAA","20":"b:x/VP/PL5nwf+A4hC4Oc/8Pz///sHAAAA","21":"b:x/VP/PL5nwf+A4hC4Oc/8Nz///sHAAAA","24":"b:x/Vf/PL5nwf+I4hC6O8/8P7///8HAAAA"},"packages/frontend/editor-ui/src/features/execution/logs/composables/useLogsExecutionData.ts":{"36":"b:x/Vf/PL5nwf+I4hC6O8/8P7///8HAAAA","42":"b:x/Vf/PL5nwf+I4hC6O8/8P7///8HAAAA","49":"b:x/Vf/PL5nwf+I4hC6O8/8P7///8HAAAA","56":"b:x/Vf/PL5nwf+I4hC6O8/8P7///8HAAAA","63":"b:x/Vf/PL5nwf+I4hC6O8/8P7///8HAAAA","64":"b:x5BK4LKBDwd6AIACgGcgAA7+r+EEAAAA","79":"b:x/Vf/PL5nwf+I4hC6O8/8P7///8HAAAA","89":"b:x/Vf/PL5nwf+I4hC6O8/8P7///8HAAAA","94":"b:x/Vf/PL5nwf+I4hC6O8/8P7///8HAAAA","118":"b:x/Vf/AL4nwf+I4hC6O8/8P7///8HAAAA","127":[52],"157":"b:x/Vf/PL5nwf+I4hC6O8/8P7///8HAAAA","158":"b:x/Vf/PL5nwf+I4hC6O8/8P7///8HAAAA","159":"b:x/Vf/PL5nwf+I4hC6O8/8P7///8HAAAA","160":"b:x/Vf/PL5nwf+I4hC6O8/8P7///8HAAAA","161":"b:x/Vf/PL5nwf+I4hC6O8/8P7///8HAAAA","163":"b:x/Vf/PL5nwf+I4hC6O8/8P7///8HAAAA","187":"b:x/Vf/PL5nwf+I4hC6O8/8P7///8HAAAA","188":"b:x/Vf/AL4nwf+I4hC6O8/8P7///8HAAAA","197":"b:x/Vf/PL5nwf+I4hC6O8/8P7///8HAAAA","207":"b:x/Vf/PL5nwf+I4hC6O8/8P7///8HAAAA"},"packages/frontend/editor-ui/src/features/execution/logs/composables/useLogsPanelLayout.ts":{"16":"b:x/Vf/PL5nwf+I4hC6O8/8P7///8HAAAA","30":"b:x/Vf/PL5nwf+I4hC6O8/8P7///8HAAAA","32":[52],"38":[19,1,7,2,8,10,52,113,57,129,6,13],"40":[52],"45":"b:x/Vf/PL5nwf+I4hC6O8/8P7///8HAAAA","51":"b:x/Vf/PL5nwf+I4hC6O8/8P7///8HAAAA","56":[19,1,7,2,36,37,39,40,52,57,6,159],"57":"b:x/Vf/PL5nwf+I4hC6O8/8P7///8HAAAA","58":"b:x/Vf/PL5nwf+I4hC6O8/8P7///8HAAAA","67":[52],"77":[19,1,52,6,159],"91":[52],"97":[52],"110":"b:x/Vf/PL5nwf+I4hC6O8/8P7///8HAAAA","133":"b:QgAAAJABEACwAAAAAAAAgAgDAOgAAAAA"},"packages/frontend/editor-ui/src/features/execution/logs/composables/useLogsSelection.ts":{"25":"b:x/Vf/PL5nwf+I4hC6O8/8P7///8HAAAA","34":"b:x/Vf/PL5nwf+I4hC6O8/8P7///8HAAAA","35":"b:x/Vf/PL5nwf+I4hC6O8/8P7///8HAAAA","43":[19,52,6],"58":[19,52,6],"85":[52],"100":[52],"116":"b:x/Vf/PL5nwf+I4hC6O8/8P7///8HAAAA","127":"b:x/Vf/PL5nwf+I4hC6O8/8P7///8HAAAA","156":"b:x/Vf/PL5nwf+I4hC6O8/8P7///8HAAAA","162":[1,7,2,39,57,6,159],"183":"b:x/Vf/PL5nwf+I4hC6O8/8P7///8HAAAA","222":"b:x/Vf/PL5nwf+I4hC6O8/8P7///8HAAAA","224":[19,1,7,2,36,37,39,40,57,6,159],"229":"b:x/Vf/PL5nwf+I4hC6O8/8P7///8HAAAA","230":[1,7,52,6]},"packages/frontend/editor-ui/src/features/execution/logs/composables/useLogsTreeExpand.ts":{"15":"b:x/Vf/PL5nwf+I4hC6O8/8P7///8HAAAA","19":"b:x/Vf/PL5nwf+I4hC6O8/8P7///8HAAAA","23":[1,52,6]},"packages/frontend/editor-ui/src/features/execution/logs/composables/usePopOutWindow.ts":{"32":[52],"39":[52],"61":[52],"77":"b:x/Vf/PL5nwf+I4hC6O8/8P7///8HAAAA","88":"b:x/Vf/PL5nwf+I4hC6O8/8P7///8HAAAA","89":"b:x/Vf/PL5nwf+I4hC6O8/8P7///8HAAAA","90":"b:x/Vf/PL5nwf+I4hC6O8/8P7///8HAAAA","93":"b:x/Vf/PL5nwf+I4hC6O8/8P7///8HAAAA","104":[52],"121":[52],"141":[52],"168":[52],"171":"b:x/Vf/PL5nwf+I4hC6O8/8P7///8HAAAA","180":"b:x/Vf/PL5nwf+I4hC6O8/8P7///8HAAAA","185":"b:x/Vf/PL5nwf+I4hC6O8/8P7///8HAAAA","194":"b:QgAAAJABEACwAAAAAAAAgAgDAOgAAAAA","199":"b:QgAAAJABEACwAAAAAAAAgAgDAOgAAAAA"},"packages/frontend/editor-ui/src/features/execution/logs/logs.types.ts":{"52":"b:xgAIALABEAIAAAAAAAAAAAIAAIAAAAAA","56":"b:xgAIALABEAIAAAAAAAAAAAIAAIAAAAAA"},"packages/frontend/editor-ui/src/features/execution/logs/logs.utils.ts":{"44":"b:xgAIALABEAIAAAAAAAAAAAIAAIAAAAAA","45":"b:xgAIALABEAIAAAAAAAAAAAIAAIAAAAAA","59":"b:xgAIALABEAIAAAAAAAAAAAIAAIAAAAAA","67":"b:xgAIALABEAIAAAAAAAAAAAIAAIAAAAAA","92":"b:xgAIALABEAIAAAAAAAAAAAIAAIAAAAAA","123":[1,7,52,6],"131":[1,7,52,6],"132":[1,7,52,6],"136":[1,7,52,6],"157":"b:xgAIALABEAIAAAAAAAAAAAIAAIAAAAAA","175":"b:xgAIALABEAIAAAAAAAAAAAIAAIAAAAAA","179":"b:xgAIALABEAIAAAAAAAAAAAIAAIAAAAAA","185":"b:xgAIALABEAIAAAAAAAAAAAIAAIAAAAAA","204":[52],"234":[52],"254":[52],"268":[52],"273":"b:x5AI4LKBHwd6AIACgGcgAAz+r+EAAAAA","274":[19,1,7,36,37,39,40,52,57,6,159],"280":[19,1,36,37,39,40,52,57,6,159],"297":"b:xgAIALABEAIAAAAAAAAAAAIAAIAAAAAA","303":"b:xgAIALABEAIAAAAAAAAAAAIAAIAAAAAA","319":"b:xgAIALABEAIAAAAAAAAAAAIAAIAAAAAA","327":[52],"332":[1,7,52,6],"350":"b:xgAIALABEAIAAAAAAAAAAAIAAIAAAAAA","366":"b:xgAIALABEAIAAAAAAAAAAAIAAIAAAAAA","375":[52],"384":[52],"389":[52],"403":[52],"409":[52],"443":[52],"464":[52],"470":[52],"471":[52],"502":[52],"512":"b:xgAIALABEAIAAAAAAAAAAAIAAIAAAAAA","574":"b:xgAIALABEAIAAAAAAAAAAAIAAIAAAAAA","597":[52],"598":[52],"601":"b:x/VL/LL5nwf+A4BC4Oc/8Pz+//sDAAAA","620":"b:x/Vf/PL5nwf+I4hC6O8/8P7///8HAAAA","631":[19,1,7,2,39,57,6,159],"656":"b:x/Vf/PL5nwf+I4hC6O8/8P7///8HAAAA","672":[52],"677":[52],"682":"b:xgAIALABEAIAAAAAAAAAAAIAAIAAAAAA","704":"b:xgAIALABEAIAAAAAAAAAAAIAAIAAAAAA","718":"b:xgAIALABEAIAAAAAAAAAAAIAAIAAAAAA","723":[19,1,7,2,36,37,39,40,52,57,129,6],"728":[19,1,7,2,36,37,39,40,52,57,129,6],"738":[52,6],"740":[19,1,7,2,36,37,39,40,52,57,129,6],"763":"b:xgAIALABEAIAAAAAAAAAAAIAAIAAAAAA","767":"b:xgAIALABEAIAAAAAAAAAAAIAAIAAAAAA","778":"b:x/Vf/PL5nwf+I4hC6O8/8P7///8HAAAA","781":"b:x/Vf/PL5nwf+I4hC6O8/8P7///8HAAAA","801":[19,52,6],"813":[52,6],"824":[52,6],"849":[52,6],"901":[52,6],"908":[52,6],"920":[52,6],"983":[19,1,7,2,36,37,39,40,52,57,6,159],"992":[19,1,7,2,36,37,39,40,52,57,6,159],"1002":"b:x5BK4LKBHwd6AIACgGcgAA7+r+EEAAAA","1010":"b:x5BK4LKBHwd6AIACgGcgAA7+r+EAAAAA"},"packages/frontend/editor-ui/src/features/instanceRegistry/stores/instanceRegistry.store.ts":{"7":"b://Vf///5/7f/+/////////////8HAAAA","14":[134]},"packages/frontend/editor-ui/src/features/integrations/externalSecrets.ee/externalSecrets.ee.store.ts":{"21":[60],"24":[60],"28":[60],"61":"b:1vVH3MJYzbfbA4BKzuU+sdz+/5MCAAAA","67":"b:kkEBEAIAgBABAgAASgACIAAAEAAAAAAA","79":"b:1vVH3MJYjbfbA4BKzuU+sdz+/5MCAAAA","93":[60],"94":[60],"98":[60],"101":[60],"102":[60],"142":"b:kkEBEAIAgBABAgAASgACIAAAEAAAAAAA","156":"b:kkEBEAIAgBABAgAASgACIAAAEAAAAAAA","182":"b:kkEBEAIAgBABAgAASgACIAAAEAAAAAAA","217":"b:kkEBEAIAgBABAgAASgACIAAAEAAAAAAA"},"packages/frontend/editor-ui/src/features/integrations/sourceControl.ee/sourceControl.api.ts":{"22":"b://Vf///5/7f/+/////////////8HAAAA","24":[85],"44":[85],"53":[85],"87":[85]},"packages/frontend/editor-ui/src/features/integrations/sourceControl.ee/sourceControl.store.ts":{"14":"b://Vf///5/7f/+/////////////8HAAAA","18":"b://Vf///5/7f/+//j//////z///8HAAAA","24":"b://Vf///5/7f/+/////////////8HAAAA","62":[85],"66":"b://Vf///5/7f/+/////////////8HAAAA","68":[85],"73":[85],"78":[85],"87":[85]},"packages/frontend/editor-ui/src/features/ndv/agents/composables/useInlineAgentConfig.ts":{"85":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA"},"packages/frontend/editor-ui/src/features/ndv/agents/composables/useNdvAgentConfig.ts":{"28":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA"},"packages/frontend/editor-ui/src/features/ndv/agents/composables/useReferencedAgentSummary.ts":{"22":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","27":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","31":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","107":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","133":"b:QgAAAJABgACwAAAAAAAAgAgDAOgEAAAA"},"packages/frontend/editor-ui/src/features/ndv/panel/components/InputPanel.vue":{"60":"b:xvVD3IJYjQfaAYBCwOE+sNz+/5MAAAAA","110":"b:xvVD3IJYjQfaAYBCwOE+sNz+/5MAAAAA","116":"b:xvVD3IJYjQfaAYBCwOE+sNz+/5MAAAAA","124":"b:xvVD3IJYjQfaAYBCwOE+sNz+/5MAAAAA","128":"b:xvVD3AJYiQbKAYBCwOEesNz+/hIAAAAA","145":"b:xvVD3IJYjQfaAYBCwOE+sNz+/5MAAAAA","146":"b:xvVD3IJYjQfaAYBCwOE+sNz+/5MAAAAA","155":"b:xvVD3IJYjQfaAYBCwOE+sNz+/5MAAAAA","179":"b:xuVD3IJYjQfaAYBCwOE+sNz+/5MAAAAA","180":[1,14],"184":"b:xvVD3IJYjQfaAYBCwOE+sNz+/5MAAAAA","192":"b:xvVD3IJYjQfaAYBCwOE+sNz+/5MAAAAA","208":"b:AIAAQAAAAQBIAIAAAEEAAAD4rwAAAAAA","217":"b:xvVD3AJYiQbKAYBCwOEesNz+/hIAAAAA","222":"b:xvVD3IJYjQfaAYBCwOE+sNz+/5MAAAAA","237":"b:xvVD3IJYjQfaAYBCwOE+sNz+/5MAAAAA","238":"b:xuVD3IJYjQfaAYBCwOE+sNz+/5MAAAAA","241":"b:xvVD3IJYjQfaAYBCwOE+sNz+/5MAAAAA","248":"b:xuVD3IJYjQfaAYBCwOE+sNz+/5MAAAAA","252":"b:xvVD3IJYjQfaAYBCwOE+sNz+/5MAAAAA","253":"b:xuVD3IJYjQfaAYBCwOE+sNz+/5MAAAAA","259":"b:xvVD3IJYjQfaAYBCwOE+sNz+/5MAAAAA","278":"b:xvVD3IJYjQfaAYBCwOE+sNz+/5MAAAAA","280":"b:xuVD3AJYiQbKAYBCwMEesMx6/hIAAAAA","285":"b:xvVD3IJYjQfaAYBCwOE+sNz+/5MAAAAA","298":"b:AEBAAAAACABAAAAAgAAAABDwKAAAAAAA","300":"b:AEBAAAAACAAAAAAAgAAAAADwKAAAAAAA","318":"b:xuVD3IJYjQfaAYBCwOE+sNz+/5MAAAAA","338":[67,141],"351":"b:xvVD3IJYjQfaAYBCwOE+sNz+/5MAAAAA","355":[2,146],"359":"b:xvVD3AJYiQbKAYBCwOEesNz+/hIAAAAA","363":[144],"367":[141,146,70],"376":[6,141,144,145,146,70],"380":[141,70],"381":[141,70],"397":"b:RHAAAAAAAABKAAAAAAAAAAAgFwAAAAAA","403":"b:xvVD3IJYjQfaAYBCwOE+sNz+/5MAAAAA","439":[12,6,141,144,145,146,70],"442":"b:xvVD3IJYjQfaAYBCwOE+sNz+/5MAAAAA","449":"b:xvVD3IJYjQfaAYBCwOE+sNz+/5MAAAAA","461":[12,6,141,144,145,146,70],"470":[7,8,12,113,14],"483":"b:xuVD3AJYiQbKAYBCwMEesMx6/hIAAAAA","517":"b:xuVD3AJYiQbKAYBCwMEesMx6/hIAAAAA","625":"b:gEEABAAAiACAAAAAAAACIAQACgAAAAAA","670":[2],"680":[56]},"packages/frontend/editor-ui/src/features/ndv/panel/components/NDVFloatingNodes.vue":{"13":"b:x/Vf/PL5jwf+I4hO6O8/8P7///8HAAAA","18":"b:xvVD3IJYjQfaAYBKwOU+sMz+/5MCAAAA","30":"b:xvVD3IJYjQfaAYBKwOU+sMz+/5MCAAAA","32":[13],"39":"b:AmECFABIAASCAAAAAEAKAMz0PwACAAAA","53":"b:xvVD3IJYjQfaAYBKwOU+sMz+/5MCAAAA","55":"b:xvVD3IJYjQfaAYBKwOE+sMz+/5MAAAAA","65":"b:xvVD3IJYjQfaAYBKwOE+sMz+/5MAAAAA","68":"b:xvVD3IJYjQfaAYBKwOU+sMz+/5MCAAAA","97":"b:xvVD3IJYjQfaAYBKwOU+sMz+/5MCAAAA","101":"b:xvVDjAJYiQbaAQACgIQegMz+b5ECAAAA","107":"b:xvVD3IJYjQfaAYBKwOU+sMz+/5MCAAAA","125":"b:xvVD3IJYjQfaAYBKwOE+sMz+/5MAAAAA","131":[67,135,13,149]},"packages/frontend/editor-ui/src/features/ndv/panel/components/NDVHeader.vue":{"27":"b:xvVD3IJYnQfaAYBKwOU+sMz+/5MCAAAA","32":[52],"34":"b:xvVD3IJYnQfaAYBKwOU+sMz+/5MCAAAA","62":"b:xvVD3IJYnQfaAYBKwOU+sMz+/5MCAAAA","64":"b:xvVD3IJYnQfaAYBKwOU+sMz+/5MCAAAA","70":[52,65,130,131,134,135,145],"72":"b:xvVD3IJYnQfaAYBKwOU+sMz+/5MCAAAA"},"packages/frontend/editor-ui/src/features/ndv/panel/components/NDVSubConnections.vue":{"25":"b:xvVD3IJYjQfaAYBKwOU+sMz+/5MCAAAA","57":"b:xvVD3IJYjQfaAYBKwOU+sMz+/5MCAAAA","61":"b:xvVD3IJYjQfaAYBKwOU+sMz+/5MCAAAA","66":[7,13],"84":[1,7,8,10,113,127,65,13,14],"87":[1,7,8,10,113,127,65,13,14],"99":[7,13],"110":[1,7,8,10,113,127,65,13,14],"127":[1,7,8,10,113,127,65,13,14],"131":[1,7,8,10,113,127,65,13,14],"133":[1,7,8,10,113,127,65,13,14],"137":[1,7,8,10,113,127,65,13,14],"145":[1,7,8,10,113,127,65,13,14],"152":[1,7,8,10,127,13,14],"167":[1,7,8,10,113,127,65,13,14],"169":[7,13],"183":[7,13],"186":[1,7,8,10,113,127,65,13,14],"193":"b:xuVD3IJYjQfaAYBCwOE+sMz+/5MAAAAA","201":"b:xvVD3IJYjQfaAYBKwOU+sMz+/5MCAAAA","210":"b:xuVD3IJYjQfaAYBCwOE+sMz+/5MAAAAA","231":[13],"250":"b:AIAAQAAAAABIAIAAAEUAAACoLgAAAAAA","253":"b:AIAAQAAAAABIAIAAAEUAAACoLgAAAAAA","259":"b:xvVD3IJYjQfaAYBKwOU+sMz+/5MCAAAA","260":"b:xvVD3IJYjQfaAYBKwOU+sMz+/5MCAAAA","262":"b:xvVD3IJYjQfaAYBKwOU+sMz+/5MCAAAA","274":"b:xvVD3IJYjQfaAYBKwOU+sMz+/5MCAAAA","296":[1,7,8,10,127,13,14],"297":[1,7,8,10,113,127,65,13,14],"305":[13],"317":[13],"335":[1,7,8,10,113,127,65,13,14],"355":[7,13],"376":[7,13]},"packages/frontend/editor-ui/src/features/ndv/panel/components/OutputPanel.vue":{"34":"b:xvVD3IJYjQfaAYBKwOU+sMz+/5MCAAAA","85":"b:xvVD3IJYjQfaAYBKwOU+sMz+/5MCAAAA","86":"b:RIAAQIAADQFYAIACgEUgAACqr4EAAAAA","111":"b:xvVD3IJYjQfaAYBKwOU+sMz+/5MCAAAA","115":"b:xvVD3IJYjQfaAYBKwOU+sMz+/5MCAAAA","121":"b:xvVD3AJYiQbaAYBCwOUesMz+/hICAAAA","125":"b:xvVD3IJYjQfaAYBKwOU+sMz+/5MCAAAA","144":"b:xvVD3IJYjQfaAYBKwOU+sMz+/5MCAAAA","153":"b:xvVD3IJYjQfaAYBKwOU+sMz+/5MCAAAA","157":"b:xvVD3IJYjQfaAYBKwOU+sMz+/5MCAAAA","165":"b:xvVD3IJYjQfaAYBKwOU+sMz+/5MCAAAA","167":"b:RIAAQIAADQFYAIACgEUgAACqr4EAAAAA","185":"b:xvVD3IJYjQfaAYBKwOU+sMz+/5MCAAAA","203":"b:RIAAQIAADQFYAIACgEUgAACqr4EAAAAA","211":"b:xvVD3IJYjQfaAYBKwOU+sMz+/5MCAAAA","215":"b:xvVD3AJYiQbaAYBCwOUesMz+/hICAAAA","219":"b:xvVD3IJYjQfaAYBKwOU+sMz+/5MCAAAA","242":"b:xvVD3IJYjQfaAYBKwOU+sMz+/5MCAAAA","263":[146],"267":[146],"286":[2,146,70,151],"291":"b:xvVD3IJYjQfaAYBKwOU+sMz+/5MCAAAA","304":"b:xvVD3IJYjQfaAYBKwOU+sMz+/5MCAAAA","310":"b:xvVD3IJYjQfaAYBKwOU+sMz+/5MCAAAA","341":"b:RABAQIAADQFYAAAKgEUgAID6r4EAAAAA","342":[2,141,144,146,70],"343":[144,146],"344":[135,68,146,70],"347":"b:xvVD3IJYjQfaAYBKwOU+sMz+/5MCAAAA","373":"b:xvVD3AJYiQbaAYBCwOUesMz+/hICAAAA","396":"b:gqFBBAAICACQAABAgIQAgMT4WhICAAAA","404":"b:xvVD3AJYiQbKAYBCwOEesMz+/hIAAAAA","406":"b:xvVD3AJYiQbaAYBCwOUesMz+/hICAAAA","417":"b:wnUAAAAQAQRAAAAAAAACAAAAAAAAAAAA","455":"b:huVD3AJYiQbaAYBCwOUesMz+/hICAAAA","489":[6],"491":[6],"495":[2],"513":[56],"534":[2,6,146,70,151]},"packages/frontend/editor-ui/src/features/ndv/panel/components/PanelDragButtonV2.vue":{"11":"b:xvVD3IJYnQfaAYBCwOE+sMz+/5MCAAAA","17":[52],"21":[52],"25":[52],"27":"b:xvVD3IJYnQfaAYBCwOE+sMz+/5MCAAAA"},"packages/frontend/editor-ui/src/features/ndv/panel/composables/useNdvLayout.ts":{"21":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","29":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","35":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","37":"b:xvVD3IJYnQfaAYBKwOU+sMz+/5MCAAAA","41":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","45":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","46":"b:xvVD3IJYnQfaAYBKwOU+sMz+/5MCAAAA","51":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","53":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","75":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","102":[52],"106":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","119":[52],"123":[52],"157":[52],"175":"b:xvVD3IJYnQfaAYBKwOU+sMz+/5MCAAAA","182":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA"},"packages/frontend/editor-ui/src/features/ndv/parameters/components/Collection/CollectionParameter.vue":{"9":"b:xvVDTIJYjafSAQBKhIEugdz+3wMCAAAA","25":"b:xvVDTIJYjafSAQBKhIEugdz+3wMCAAAA","34":[103,51]},"packages/frontend/editor-ui/src/features/ndv/parameters/components/Collection/CollectionParameterLegacy.vue":{"25":"b:xvVDTIJYjafSAQBKhIEugdz+3wMCAAAA","46":"b:xvVDTIJYjafSAQBKhIEugdz+3wMCAAAA","50":"b:xvVDTIJYjabSAQBChIEugdz+XwMCAAAA","57":"b:xvFDTIJYjabSAQBChIEugdz+XwACAAAA","63":"b:xvFDTIJYjabSAQBChIEugdz+XwACAAAA","75":"b:xvVDTIJYjafSAQBKhIEugdz+3wMCAAAA","91":[17,30,103,51,111,146,151],"95":[17,30,103,51,111,146,151],"112":"b:xvVDTIJYjafSAQBKhIEugdz+3wMCAAAA","114":"b:xvVDTIJYjafSAQBKhIEugdz+3wMCAAAA","115":[17,30,103,51,111,146,151],"118":[17,30,103,51,111,146,151],"120":[17,30,103,51,111,146,151],"131":"b:xvVDTIJYjafSAQBKhIEugdz+3wMCAAAA","133":"b:xvVDTIJYjafSAQBKhIEugdz+3wMCAAAA","148":"b:xvVDTIJYjafSAQBKhIEugdz+3wMCAAAA","149":"b:xvVDTIJYjafSAQBKhIEugdz+3wMCAAAA","152":[103,51],"206":[103,51],"208":"b:xvVDTIJYjafSAQBKhIEugdz+3wMCAAAA","215":"b:xvVBDIJYjafSAQBKhIEugdz+XwMCAAAA","248":"b:xvFDTIJYjabSAQBChIEugdz+XwACAAAA","251":"b:xvFDTIJYjabSAQBChIEugdz+XwACAAAA"},"packages/frontend/editor-ui/src/features/ndv/parameters/components/ExpressionEditModal.vue":{"38":"b:1vVH3MJYjbfbA4BKzuU+sdz+/5MCAAAA","64":[142],"82":[142],"84":[142],"89":[142],"103":"b:1vVH3MJYjbfbA4BKzuU+sdz+/5MCAAAA","104":[142],"140":[142],"145":[142],"160":"b:1vVH3MJYjbfbA4BKzuU+sdz+/5MCAAAA","181":[142],"189":[142],"210":[142],"220":[142],"243":[142]},"packages/frontend/editor-ui/src/features/ndv/parameters/components/ExpressionEditorModal/theme.ts":{"4":"b:1vVH3MJYjbfbA4BKzuU+sdz+/5MCAAAA","33":[142],"39":"b:1vVH3MJYjbfbA4BKzuU+sdz+/5MCAAAA"},"packages/frontend/editor-ui/src/features/ndv/parameters/components/FilterConditions/constants.ts":{"299":"b://Vf///5/7f/+/////////////8HAAAA","305":"b://Vf///5/7f/+/////////////8HAAAA","311":"b://Vf///5/7f/+/////////////8HAAAA","317":"b://Vf///5/7f/+/////////////8HAAAA","323":"b://Vf///5/7f/+/////////////8HAAAA","329":"b://Vf///5/7f/+/////////////8HAAAA"},"packages/frontend/editor-ui/src/features/ndv/parameters/components/ParameterInput.vue":{"126":"b:1vVH3MJYjbfbA4BKzuU+sdz+/5MCAAAA","245":"b:1vVH3MJYjbfbA4BKzuU+sdz+/5MCAAAA","251":[26,145,14],"255":"b:1vVH3MJYjbfbA4BKzuU+sdz+/5MCAAAA","261":"b:1vVH3MJYjbfbA4BKzuU+sdz+/5MCAAAA","265":"b:1vVH3MJYjbfbA4BKzuU+sdz+/5MCAAAA","267":"b:1vVH3MJYjbfbA4BKzuU+sdz+/5MCAAAA","271":"b:1vVH3MJYjbbbA4BCzuU+sdz+/xMCAAAA","275":"b:kkFCUAIAibABAgBCiAECIQAiMgAAAAAA","289":[2,27,46,61,131,14,151],"305":[2,27,46,61,131,14,151],"309":[2,27,46,61,131,14,151],"317":"b:1vVH3MJYjbfbA4BKzuU+sdz+/5MCAAAA","321":"b:1vVH3IJYjbfbA4BKzuU+sdz+/5MCAAAA","340":"b:AFFBFABYiABAAAAAwKAAsIhQfhAAAAAA","347":"b:BkAAjABAACAKAIAAAAAQAMwAgoAAAAAA","356":"b:wmEBEEAYgAAAAAAAAAACsAAAEhMAAAAA","360":"b:1vVH3MJYjbfbA4BKzuU+sdz+/5MCAAAA","384":"b:1vVH3MJYjbfbA4BKzuU+sdz+/5MCAAAA","400":"b:1vVH3MJYjbfbA4BKzuU+sdz+/5MCAAAA","403":"b:AgAAhAAAAAAKAIAAAAAQAEwAAoAAAAAA","411":"b:1vVH3MJYjbfbA4BKzuU+sdz+/5MCAAAA","415":"b:AAAAhAAAAAAKAIAAAAAQAEwAAoAAAAAA","423":"b:AAAAhAAAAAAKAIAAAAAQAEwAAoAAAAAA","427":"b:1vVH3MJYjbfbA4BKzuU+sdz+/5MCAAAA","501":"b:wuUBEEAYhBJSAAAAAKAisADwOhMAAAAA","516":"b:1vVH3MJYjbfbA4BKzuU+sdz+/5MCAAAA","549":"b:1vVH3MJYjbfbA4BKzuU+sdz+/5MCAAAA","572":"b:1vVH3MJYjbfbA4BKzuU+sdz+/5MCAAAA","576":"b:1vVH3MJYjbfbA4BKzuU+sdz+/5MCAAAA","616":"b:xvVD3IJYjafaAYBKxOU+sdz+/5MCAAAA","647":[55,152,153],"666":"b:1vVH3MJYjbfbA4BKzuU+sdz+/5MCAAAA","680":"b:1vVH3MJYjbfbA4BKzuU+sdz+/5MCAAAA","687":"b:1vVH3MJYjbfbA4BKzuU+sdz+/5MCAAAA","695":[98,8,12,13],"699":"b:1vVH3MJYjbfbA4BKzuU+sdz+/5MCAAAA","726":"b:1vVH3MJYjbfbA4BKzuU+sdz+/5MCAAAA","745":"b:AAAAhAAAAAAKAIAAAAAQAEwAAoAAAAAA","749":"b:1vVH3IJYjbfbA4BKzuU+sdz+/5MCAAAA","753":"b:1vVH3MJYjbfbA4BKzuU+sdz+/5MCAAAA","759":"b:AAAAhAAAAAAKAIAAAAAQAEwAAoAAAAAA","763":[135],"765":"b:kkFCUAIQibBJAgBCzEECIRDyOgAAAAAA","766":"b:AEEAAAIQCCBIAABCxEAAAQBwIAAAAAAA","768":[110,137,141],"769":"b:kkFCUAIAibABAgBCiEECIRByMgAAAAAA","776":"b:1vVH3MJYjbfbA4BKzuU+sdz+/5MCAAAA","788":[1,145],"790":"b:1vVH3MJYjbfbA4BKzuU+sdz+/5MCAAAA","799":"b:1vVH3IJYjbfbA4BKzuU+sdz+/5MCAAAA","803":"b:1vVH3IJYjbfbA4BKzuU+sdz+/5MCAAAA","807":[27,8,33],"821":"b:1vVH3MJYjbfbA4BKzuU+sdz+/5MCAAAA","835":"b:1vVH3IJYjbfbA4BKzuU+sdz+/5MCAAAA","843":"b:1vVD3IJYjRfbA4BKzuU+sNz+/5MCAAAA","851":[26,145,14,148],"903":[145],"908":[145],"913":[142],"917":[142],"947":[145],"959":[142],"974":"b:kkFCUAIQibBJAgBCzEECIRDyOgIAAAAA","982":"b:kkFCUAIQibBJAgBCzEECIRDyOgIAAAAA","1043":"b:kkFCUAIAibABAgBCiEECIQAiMgAAAAAA","1066":"b:kkFC1AIQibALAoBCzEESIczyOgMAAAAA","1153":[60,140,141,142,143,14,147,148,149],"1160":"b:kkFCUAIQibAJAgBCjEECIRDyMgIAAAAA","1227":"b:kkFCUAIAibABAgBCiEECIQAiMgAAAAAA","1234":"b:AEAAAAAAABAAAAAAAAAAAADwOAIAAAAA","1314":"b:1vVH3MJYjbfbA4BKzuU+sdz+/5MCAAAA","1348":[98,8,12,13],"1377":"b:1vVDnAJYiZbbAwAChoQeoNz+f5MCAAAA","1383":"b:1vVH3MJYjbfbA4BKzuU+sdz+/5MCAAAA","1385":"b:1vVH3MJYjbfbA4BKzuU+sdz+/5MCAAAA","1413":[145,14,148],"1419":"b:1vVH3MJYjbfbA4BKzuU+sdz+/5MCAAAA","1420":"b:kkFD1AIAibALAoBCykECIUzyOgMAAAAA","1430":[26,145,14],"1435":"b:AEAAAAAAABAAAAAAAAAAAADwOAIAAAAA","1445":"b:1vVH3MJYjbfbA4BKzuU+sdz+/5MCAAAA","1448":[16,94,8],"1464":"b:1vVH3MJYjbfbA4BKzuU+sdz+/5MCAAAA","1477":"b:1vVH3MJYjbfbA4BKzuU+sdz+/5MCAAAA","1483":"b:AEAAAAAAAAAAAAAAAEAAAYCwOgAAAAAA","1622":[145],"1708":"b:AAAAhAAAAAAKAIAAAAAQAEwAAoAAAAAA","1733":[135],"1778":[135],"1801":[1,145],"1824":[2,27,46,61,131,14,151],"1854":"b:kkFCUAIAibABAgBCiAECIQAiMgAAAAAA","1873":[17,120,141,145,14],"1878":"b:1vVH3MJYjbfbA4BKzuU+sdz+/5MCAAAA","1989":[27,8,33],"2010":"b:1vVH3IJYjbfbA4BKzuU+sdz+/5MCAAAA","2018":"b:1vVH3IJYjbfbA4BKzuU+sdz+/5MCAAAA","2050":[26,43,148],"2057":[26,43,148]},"packages/frontend/editor-ui/src/features/ndv/parameters/components/ParameterInputFull.vue":{"63":"b:xvVH3MJYjafaAYBKxOU+sdz+/5MCAAAA","97":"b:xvVH3MJYjafaAYBKxOU+sdz+/5MCAAAA","108":"b:xvVH3MJYjafaAYBKxOU+sdz+/5MCAAAA","115":"b:xvVH3MJYjafaAYBKxOU+sdz+/5MCAAAA","119":"b:xvVH3MJYjafaAYBKxOU+sdz+/5MCAAAA","123":"b:xvVH3MJYjafaAYBKxOU+sdz+/5MCAAAA","129":"b:xvVH3MJYjafaAYBKxOU+sdz+/5MCAAAA","136":"b:xvVHXMJYjSbSAQBCxOUusVz+/xMCAAAA","138":"b:xvVH3MJYjafaAYBKxOU+sdz+/5MCAAAA","147":"b:xvVH3MJYjafaAYBKxOU+sdz+/5MCAAAA","165":"b:AEFCQAIQiaBIAABCxEEAARDyOgIAAAAA","173":"b:AEFCQAIQiaAIAABChEEAARDyMgIAAAAA","189":"b:wuVFzIIQiSBaAABKxIEOAcz6vgIAAAAA","193":"b:wuVBzIIQiSBaAABKxIEOAcz6vgIAAAAA","197":[140,142,143,14,147,148,149,153],"204":"b:AkFCxAIAiaAKAIBCxEEQAczyOgMAAAAA","208":"b:AEBCQAIACaAAAABCgEEAAQAiIgAAAAAA","214":[141],"222":[141],"293":"b:xvVH3MJYjafaAYBKxOU+sdz+/5MCAAAA","298":"b:xvVH3MJYjafaAYBKxOU+sdz+/5MCAAAA","299":[149],"308":"b:wuVFzIIQiSBaAABKxIEOAcz6vgIAAAAA","313":[98,8,12,13],"317":[98],"354":"b:xvVH3MJYjafaAYBKxOU+sdz+/5MCAAAA","439":"b:xvVH3MJYjafaAYBKxOU+sdz+/5MCAAAA","443":[98,8,12,13],"456":"b:xvVH3MJYjafaAYBKxOU+sdz+/5MCAAAA","477":"b:xvVH3MJYjafaAYBKxOU+sdz+/5MCAAAA","510":[98,8,12,13]},"packages/frontend/editor-ui/src/features/ndv/parameters/components/ParameterInputList.vue":{"78":"b:guFDWAAYiADAAABCgIQAsET4TxIAAAAA","81":"b:xvVDTIJYjafSAQBKhIEugdz+3wMCAAAA","139":"b:xvVD3IJYjafaAYBKhIU+sdz+35MCAAAA","160":"b:xvVH3MJYjafaAYBKxOU+sdz+/5MCAAAA","165":"b:xvVH3MJYjafaAYBKxOU+sdz+/5MCAAAA","172":"b:xvVH3MJYjafaAYBKxOU+sdz+/5MCAAAA","202":"b:xvVH3MJYjafaAYBKxOU+sdz+/5MCAAAA","203":"b:xvVH3MJYjafaAYBKxOU+sdz+/5MCAAAA","209":"b:xvVH3MJYjafaAYBKxOU+sdz+/5MCAAAA","222":"b:xvVH3MJYjafaAYBKxOU+sdz+/5MCAAAA","228":"b:xvVH3MJYjafaAYBKxOU+sdz+/5MCAAAA","229":"b:xvVH3MJYjafaAYBKxOU+sdz+/5MCAAAA","270":"b:xvVH3MJYjafaAYBKxOU+sdz+/5MCAAAA","315":"b:xvVH3MJYjafaAYBKxOU+sdz+/5MCAAAA","342":"b:BGAAAAAAAIIIAAAABAAQAIAAIAIAAAAA","343":"b:BGAAAAAAAIIIAAAABAAQAIAAIAIAAAAA","349":"b:xvVH3MJYjafaAYBKxOU+sdz+/5MCAAAA","350":"b:xvVH3MJYjafaAYBKxOU+sdz+/5MCAAAA","353":"b:xvVH3MJYjafaAYBKxOU+sdz+/5MCAAAA","354":"b:xvVH3MJYjafaAYBKxOU+sdz+/5MCAAAA","357":"b:xvVH3MJYjafaAYBKxOU+sdz+/5MCAAAA","374":"b:xvVH3MJYjafaAYBKxOU+sdz+/5MCAAAA","383":[22,103],"386":[22,103],"409":[22,103],"427":[89],"430":[89],"468":[1,7,8,10,113,65,13],"475":[1,65],"479":[1,65],"496":[22,103],"499":[22,103],"503":[22,103],"508":"b:AEFCQAIQCKAIAABChEEAAQByMgIAAAAA","512":"b:xvVH3MJYjafaAYBKxOU+sdz+/5MCAAAA","519":"b:wGFHXEIYiABSAABKgIEMMAAKWhACAAAA","526":"b:xvVH3MJYjafaAYBKxOU+sdz+/5MCAAAA","530":"b:xvVH3MJYjafaAYBKxOU+sdz+/5MCAAAA","545":"b:xvVH3MJYjafaAYBKxOU+sdz+/5MCAAAA","558":[6],"573":"b:xvVH3MJYjafaAYBKxOU+sdz+/5MCAAAA","590":"b:AkFCxAIAiaAKAIBCxEEQAcz6OgMAAAAA","600":"b:xvVH3MJYjafaAYBKxOU+sdz+/5MCAAAA","615":"b:xvVH3MJYjafaAYBKxOU+sdz+/5MCAAAA","619":"b:xvVH3MJYjafaAYBKxOU+sdz+/5MCAAAA","647":"b:xvVH3MJYjafaAYBKxOU+sdz+/5MCAAAA","653":[1,7,8,10,113,65,13,14],"657":[1,7,8,10,113,65,13,14],"667":"b:xvVH3MJYjafaAYBKxOU+sdz+/5MCAAAA","672":"b:wkEEGAIAiAAAAAAAhAACMAAAEAAAAAAA","688":"b:xvVH3MJYjafaAYBKxOU+sdz+/5MCAAAA","766":"b:xvVH3MJYjafaAYBKxOU+sdz+/5MCAAAA","789":"b:xvVH3MJYjafaAYBKxOU+sdz+/5MCAAAA","856":[1,7,8,10,113,65,13,14],"871":[14],"877":[1,7,8,10,113,65,13,14],"919":[153],"923":[153],"929":"b:xvVDXIJYjafSAQBKhIUusdz+3xMCAAAA","952":"b:xvVDXIJYjafSAQBKhIUusdz+3xMCAAAA","954":"b:xvVDXIJYjafSAQBKhIUusdz+3xMCAAAA","1077":"b:AEFCQAIQiKAIAABChEEAAQByMgIAAAAA"},"packages/frontend/editor-ui/src/features/ndv/parameters/components/ParameterInputWrapper.vue":{"70":"b:1vVH3MJYjbfbA4BKzuU+sdz+/5MCAAAA","74":"b:1vVH3MJYjbfbA4BKzuU+sdz+/5MCAAAA","85":"b:wmEBEEAYgAAAAAAAAAACsAAAEhAAAAAA","91":"b:1vVH3MJYjbfbA4BKzuU+sdz+/5MCAAAA","102":[111,60,140,141,142,145,70,148,149],"104":[70],"108":"b:1vVH3MJYjbfbA4BKzuU+sdz+/5MCAAAA","113":"b:1vVH3MJYjbfbA4BKzuU+sdz+/5MCAAAA","130":"b:1vVH3MJYjbfbA4BKzuU+sdz+/5MCAAAA","134":"b:1vVH3MJYjbfbA4BKzuU+sdz+/5MCAAAA","141":"b:kkFCUAIQibBJAgBCzEECIRDyOgIAAAAA","145":"b:kkFCUAIQibAJAgBCjEECIRDyMgIAAAAA","153":"b:kkFC1AIAibALAoBCzEESIczyOgMAAAAA","157":"b:kkFCUAIAibABAgBCiEECIQAiMgAAAAAA","162":[98,8,12,13],"163":[60],"177":"b:1vVH3MJYjbfbA4BKzuU+sdz+/5MCAAAA","208":"b:1vVH3MJYjbfbA4BKzuU+sdz+/5MCAAAA"},"packages/frontend/editor-ui/src/features/ndv/parameters/components/ParameterOptions.vue":{"40":"b:1vVH3MJYjbfbA4BKzuU+sdz+/5MCAAAA","61":"b:1vVH3MJYjbbbA4BCzuU+sdz+/xMCAAAA","62":"b:1vVH3MJYjbbbA4BCzuU+sdz+/xMCAAAA","63":"b:1vVDXIJYjTbTAwBCyuUusdz+/xMCAAAA","64":"b:1vVH3MJYjbbbA4BCzuU+sdz+/xMCAAAA","67":"b:1vVH3MJYjbfbA4BKzuU+sdz+/5MCAAAA","77":"b:1vVH3MJYjbfbA4BKzuU+sdz+/5MCAAAA","96":"b:1vVH3MJYjbfbA4BKzuU+sdz+/5MCAAAA","115":"b:1vVDXIJYjTbTAwBCyuUusVz+/xMCAAAA","116":"b:1vVH3MJYjbbbA4BCzuU+sdz+/xMCAAAA","120":"b:1vVH3MJYjbbbA4BCzuU+sdz+/xMCAAAA","128":"b:1vVH3MJYjbbbA4BCzuU+sdz+/xMCAAAA","173":[149],"174":[60,140,142,143,14,147,148,149,153],"181":"b:1vVH3MJYjbfbA4BKzuU+sdz+/5MCAAAA","189":[149],"215":[149]},"packages/frontend/editor-ui/src/features/ndv/parameters/components/TextEdit.vue":{"10":"b:1vVH3MJYjbfbA4BKzuU+sdz+/5MCAAAA","31":"b:1vVH3MJYjbfbA4BKzuU+sdz+/5MCAAAA","41":"b:1vVH3MJYjbfbA4BKzuU+sdz+/5MCAAAA","42":"b:kkFD1AIAibALAoBCygECIUwiMgAAAAAA","48":"b:1vVH3MJYjbfbA4BKzuU+sdz+/5MCAAAA","66":"b:1vVH3MJYjbfbA4BKzuU+sdz+/5MCAAAA"},"packages/frontend/editor-ui/src/features/ndv/parameters/utils/fromAIOverride.utils.ts":{"42":[98],"83":[12],"90":[98,8,12,13],"106":[98],"117":"b:xvVH3MJYjafaAYBKxOU+sdz+/5MCAAAA","121":[98],"132":[98],"138":[98],"153":[98],"157":[98],"210":[8],"246":[98,8,12,13],"268":"b:xvVH3MJYjafaAYBKxOU+sdz+/5MCAAAA","285":"b:xvVH3MJYjafaAYBKxOU+sdz+/5MCAAAA","286":"b:xvVH3MJYjafaAYBKxOU+sdz+/5MCAAAA","302":"b:xvVH3MJYjafaAYBKxOU+sdz+/5MCAAAA","309":"b:xvVH3MJYjafaAYBKxOU+sdz+/5MCAAAA","321":"b:xvVH3MJYjafaAYBKxOU+sdz+/5MCAAAA"},"packages/frontend/editor-ui/src/features/ndv/runData/components/RunData.vue":{"103":"b:RgBIQLABDQNYAAAKgEUgAID6r4EAAAAA","104":[12,68,6,141,146],"106":"b:RNAAQIAABQVYAIACAGEoAJT+74EAAAAA","109":[1,6],"111":"b:RtBIQLABDQdYAIAKgGUgAJT6r4EAAAAA","236":"b:xvVL3IJYjQfaAYBKwOU+sNz+/5MCAAAA","237":"b:xvVL3LJZjQfaAYBKwOU+sNz+/5MCAAAA","266":"b:QIBIQIAADQBYAIACgEUgAID6rwEAAAAA","267":"b:xuVL3IJYjQfaAYBKwOU+sMz+/5MCAAAA","268":"b:xvVL3LJZjQfaAYBKwOU+sNz+/5MCAAAA","276":"b:QhAAAAAAAABQAAAAAEAAAID2DwAAAAAA","277":"b:xvVL3LJZjQfaAYBKwOU+sNz+/5MCAAAA","283":"b:xvVL3LJZjQfaAYBKwOU+sNz+/5MCAAAA","284":"b:RtAAQIAABQVYAIACAGEgAJT6r4EAAAAA","286":"b:xvVL3LJZjQfaAYBKwOU+sNz+/5MCAAAA","287":"b:RNBAQIAADQVYAIAKgGUgAIT6r4EAAAAA","288":"b:xvVL3LJZjQfaAYBKwOU+sNz+/5MCAAAA","289":"b:xvVL3LJZjQfaAYBKwOU+sNz+/5MCAAAA","290":"b:xvVL3LJZjQfaAYBKwOU+sMz+/5MCAAAA","293":"b:xvVD3IJYjQfaAYBCwME+sMz+/xMAAAAA","297":"b:xvVL3LJZjQfaAYBKwOU+sMz+/5MCAAAA","305":"b:RtAAQIAABQVYAIACAGEoAJT+74EAAAAA","306":"b:xvVD3IJYjQfaAYBKwOU+sNz+/5MCAAAA","307":"b:xvVD3IJYjQfaAYBKwOU+sNz+/5MCAAAA","308":"b:QABAQIAADQFYAAAKgEUgAID6r4EAAAAA","316":"b:RNAAQIAABQVYAIACAGEoAJT+74EAAAAA","326":"b:xvVL3LJZjQfaAYBKwOU+sNz+/5MCAAAA","332":"b:xvVL3LJZjQfaAYBKwOU+sNz+/5MCAAAA","336":"b:xtBIQLABDQdYAIAKgGUoAJT+74EAAAAA","340":"b:RtBIQLABDQdYAIAKgGUoAJT+74EAAAAA","349":"b:RtBIQLABDQdYAIAKgGUoAJT+74EAAAAA","353":"b:RtBIQLABDQdYAIAKgGUoAJT+74EAAAAA","363":"b:xvVL3LJZjQfaAYBKwOU+sMz+/5MCAAAA","365":"b:xvVD3IJYjQfaAYBKwOU+sNz+/5MCAAAA","374":"b:xvVL3LJZjQfaAYBKwOU+sNz+/5MCAAAA","383":"b:xtBIQLABDQdYAIAKgGUgAJT6r4EAAAAA","387":"b:xvVL3LJZjQfaAYBKwOU+sNz+/5MCAAAA","392":"b:xvVL3LJZjQfaAYBKwOU+sNz+/5MCAAAA","403":"b:xvVL3LJZjQfaAYBKwOU+sMz+/5MCAAAA","409":"b:xvVL3LJZjQfaAYBKwOU+sNz+/5MCAAAA","416":"b:xvVL3LJZjQfaAYBKwOU+sNz+/5MCAAAA","426":"b:xvVL3LJZjQfaAYBKwOU+sNz+/5MCAAAA","429":"b:xvVL3LJZjQfaAYBKwOU+sNz+/5MCAAAA","439":"b:xvVL3LJZjQfaAYBKwOU+sNz+/5MCAAAA","443":"b:xvVL3LJZjQfaAYBKwOU+sNz+/5MCAAAA","447":"b:AIBIQAAACQBIAIAAgEUAAAD4rwAAAAAA","451":"b:xvVL3LJZjQfaAYBKwOU+sNz+/5MCAAAA","459":"b:xvVL3LJZjQfaAYBKwOU+sNz+/5MCAAAA","462":"b:QtAIQLABBQdQAAAKACAgAJT6j4EAAAAA","463":"b:xvVL3LJZjQfaAYBKwOU+sNz+/5MCAAAA","471":"b:RgBIQLABDQNYAAAKgEUgAID6r4EAAAAA","472":"b:xvVL3LJZjQfaAYBKwOU+sNz+/5MCAAAA","476":"b:xvVL3LJZjQfaAYBKwOU+sNz+/5MCAAAA","481":[2,6,146,70,151],"491":[2,6,146,70,151],"497":"b:xvVL3LJZjQfaAYBKwOU+sNz+/5MCAAAA","501":"b:xvVL3LJZjQfaAYBKwOU+sNz+/5MCAAAA","502":"b:xvVL3LJZjQfaAYBKwOU+sNz+/5MCAAAA","503":"b:xvVL3LJZjQfaAYBKwOU+sNz+/5MCAAAA","507":"b:xvVL3LJZjQfaAYBKwOU+sNz+/5MCAAAA","508":"b:xvVL3LJZjQfaAYBKwOU+sNz+/5MCAAAA","518":"b:xvVL3LJZjQfaAYBKwOU+sNz+/5MCAAAA","528":[1,2,48,6,144,145,146,70],"529":[1,2,48,6,144,145,146,70],"569":"b:xvVL3LJZjQfaAYBKwOU+sNz+/5MCAAAA","573":"b:xuVD3IJYjQbaAYBCwOU+sMz+/xMCAAAA","574":"b:xvVL3LJZjQfaAYBKwOU+sNz+/5MCAAAA","580":"b:xvVL3LJZjQfaAYBKwOU+sNz+/5MCAAAA","591":"b:RtBIQLABDQdYAIAKgGUoAJT+74EAAAAA","595":"b:xvVL3LJZjQfaAYBKwOU+sMz+/5MCAAAA","607":"b:xvVL3LJZjQfaAYBKwOU+sNz+/5MCAAAA","624":"b:xvVL3LJZjQfaAYBKwOU+sNz+/5MCAAAA","629":"b:xvVL3LJZjQfaAYBKwOU+sNz+/5MCAAAA","638":"b:QABAQIAADQFYAAAKgEUgAID6r4EAAAAA","642":"b:xvVL3LJZjQfaAYBKwOU+sNz+/5MCAAAA","660":[2],"666":"b:xvVL3LJZjQfaAYBKwOU+sNz+/5MCAAAA","669":"b:xvVD3IJYjQfaAYBCwOE+sNz+/5MAAAAA","672":"b:xvVL3LJZjQfaAYBKwOU+sNz+/5MCAAAA","674":"b:xvVL3LJZjQfaAYBKwOU+sNz+/5MCAAAA","677":"b:AAAAAAAABAAAAAAAAIAIAIAMzAAAAAAA","678":"b:AAAAAAAABAAAAAAAAIAIAIAMzAAAAAAA","695":"b:xvVL3LJZjQfaAYBKwOU+sNz+/5MCAAAA","702":"b:xvVL3LJZjQfaAYBKwOU+sNz+/5MCAAAA","706":"b:xvVL3LJZjQfaAYBKwOU+sNz+/5MCAAAA","707":[1,6],"710":"b:xvVL3LJZjQfaAYBKwOU+sNz+/5MCAAAA","714":[6],"720":"b:QmAIAKAAAABIAAAAAAAAAIAgIAIAAAAA","725":"b:xoBIQAAACQJIAIAAgEUAAID4rwAAAAAA","735":"b:xvVL3LJZjQfaAYBKwOU+sNz+/5MCAAAA","740":"b:RtBIQLABDQdYAIAKgEUgAJT6r4EAAAAA","747":"b:xvVIQKAACQJIAIAAgEUCAID6rwIAAAAA","756":"b:xuBIQKAACQJIAIAAgEUAAID4rwIAAAAA","764":[15,87,6,145,70],"771":[144,146],"777":"b:xvVL3LJZjQfaAYBKwOU+sNz+/5MCAAAA","786":"b:xvVL3LJZjQfaAYBKwOU+sNz+/5MCAAAA","807":[137,6],"818":"b:xvVLjIJZiQbaAQACgIQegNz+b5ECAAAA","823":"b:xvVL3LJZjQfaAYBKwOU+sNz+/5MCAAAA","839":[44,109,111,58,68,146,150,153],"869":"b:xvVL3LJZjQfaAYBKwOU+sNz+/5MCAAAA","908":[2,141,144,146,70],"930":"b:xvVL3LJZjQfaAYBKwOU+sMz+/5MCAAAA","946":"b:AIAIAAAACAAIAIAAgEEAAID4KQAAAAAA","954":"b:xvVLjIJZiQbaAQACgIQegNz+b5ECAAAA","962":"b:QIBIQAAACAAIAIAAgEUAAID6KwAAAAAA","967":[135,138,140,141,142,143,70,147],"1003":[135,138,140,141,142,143,70,147],"1027":[135,140,141,142,143,70,147],"1054":[135,140,141,142,143,70,147],"1064":[137,147],"1118":[70],"1142":[2,146],"1150":[2,146],"1154":[2,146],"1158":[110],"1171":"b:RoBIQAAACQJIAIAAgEUAAIDwrwAAAAAA","1194":[1,12,68,6,141,144,145,146,70],"1225":[2,6,146,70,151],"1253":"b:xvVL3LJZjQfaAYBKwOU+sNz+/5MCAAAA","1278":"b:xvVL3LJZjQfaAYBKwOU+sNz+/5MCAAAA","1281":"b:AEAAAAAAAABAAAAIAAAAAJD6DQEAAAAA","1293":"b:xvVL3LJZjQfaAYBKwOU+sNz+/5MCAAAA","1299":[144,146],"1302":"b:xvVL3LJZjQfaAYBKwOU+sNz+/5MCAAAA","1320":"b:xvVL3LJZjQfaAYBKwOU+sNz+/5MCAAAA","1330":"b:xvVL3LJZjQfaAYBKwOU+sNz+/5MCAAAA","1357":"b:xvVL3LJZjQfaAYBKwOU+sNz+/5MCAAAA","1384":[1,2,48,6,144,145,146,70],"1402":"b:xvVL3LJZjQfaAYBKwOU+sNz+/5MCAAAA","1405":"b:RtBIQLABDQdYAIAKgEUgAJT6r4EAAAAA","1411":"b:xvVL3LJZjQfaAYBKwOU+sNz+/5MCAAAA","1418":[2,146,70,151],"1435":"b:xvVL3LJZjQfaAYBKwOU+sMz+/5MCAAAA","1441":"b:xvVL3LJZjQfaAYBKwOU+sMz+/5MCAAAA","1447":"b:xvVL3LJZjQfaAYBKwOU+sMz+/5MCAAAA","1500":"b:AAAAAAAAAABAAAAAAAAAAIDyDQAAAAAA","1504":"b:AAAAAAAAAABAAAAIAAAAAIDyDQAAAAAA","1512":"b:AAAAAAAAAABAAAAIAAAAAIDyDQAAAAAA","1527":[135,137,138,140,141,142,143,70,147],"1578":[135,138,140,141,142,143,70,147],"1591":[137,147],"1637":[2,146,70,151],"1639":[2,6,146,70,151],"1640":[2,6,146,70,151],"1641":[2,6,146,70,151],"1652":[2,146,70],"1686":[44,111,58,150,153],"1691":[44,111,58,150,153],"1763":"b:xoBIQAAACQJIAIAAgEUAAAD4rgAAAAAA","1801":[138],"1807":[135,140,141,142,143,70,147],"1812":[135,138,140,141,142,143,70,147],"1814":[135,138,140,141,142,143,70,147],"1931":[56],"2073":"b:QtAIQLABBQdQAAAKACAgAJT6j4EAAAAA","2116":[144],"2118":[144],"2141":"b:RgBIQKAADQNYAAAKgEUgAID6r4EAAAAA"},"packages/frontend/editor-ui/src/features/ndv/runData/components/RunDataDisplayModeSelect.vue":{"18":"b:xvVL3LJZjQfaAYBKwOU+sMz+/5MCAAAA","21":"b:xvVL3LJZjQfaAYBKwOU+sMz+/5MCAAAA","45":"b:xvVL3LJZjQfaAYBKwOU+sMz+/5MCAAAA","47":"b:xvVL3LJZjQfaAYBKwOU+sMz+/5MCAAAA","53":"b:xvVL3LJZjQfaAYBKwOU+sMz+/5MCAAAA","62":[12,68,6,141,144,145,146,70],"64":[19,1,7,2,36,37,39,40,57,6,159]},"packages/frontend/editor-ui/src/features/ndv/runData/components/RunDataItemCount.vue":{"16":"b:xpBIQLABDQNYAIAKgEUgAID6r4EAAAAA","17":"b:xpBIQLABDQNQAIAKgEUgAID6r4EAAAAA","21":[144,146],"29":"b:xpBIQLABDQNYAIAKgEUgAID6r4EAAAAA"},"packages/frontend/editor-ui/src/features/ndv/runData/components/RunDataSearch.vue":{"44":"b:RtBIQLABDQdYAIAKgGUgAJT6r4EAAAAA","50":"b:RtBIQLABDQdYAIAKgGUgAJT6r4EAAAAA","68":"b:RtBIQLABDQdYAIAKgGUgAJT6r4EAAAAA","72":"b:AEAAAAAAAAQAAAAAAEAAAADwDQAAAAAA","99":[144,146],"104":[144,146],"109":[144,146],"115":[144,146],"121":[144,146],"134":"b:AEAAAAAAAAQAAAAAAEAAAADwDQAAAAAA","152":"b:RtBIQLABDQdYAIAKgGUgAJT6r4EAAAAA","153":[144,146]},"packages/frontend/editor-ui/src/features/ndv/runData/components/RunDataTable.vue":{"87":"b:RABAQIAADQFYAAAKgEUgAID6r4EAAAAA","88":"b:RgBIQLABDQNYAAAKgEUgAID6r4EAAAAA","89":"b:RgBIQLABDQNYAAAKgEUgAID6r4EAAAAA","91":"b:RgBIQKAADQNYAAAKgEUgAID6r4EAAAAA","92":"b:RgBIQKAADQNYAAAKgEUgAID6r4EAAAAA","93":[141,144,146,70],"96":"b:RgBIQLABDQNYAAAKgEUgAID6r4EAAAAA","97":"b:RgBIQLABDQNYAAAKgEUgAID6r4EAAAAA","105":"b:RgBIQLABDQNYAAAKgEUgAID6r4EAAAAA","116":"b:RgBIQLABDQNYAAAKgEUgAID6r4EAAAAA","153":[151],"165":[2,141,144,146,70],"187":[2,141,146,70],"194":[141],"198":[141],"202":[141,144],"208":"b:RgBIQKAADQNYAAAKgEUgAID6r4EAAAAA","221":"b:RgBIQKAADQNYAAAKgEUgAID6r4EAAAAA","228":"b:RAAAAAAACQBAAAAIAAAAAACiBQAAAAAA","241":"b:RAAAAAAACQBAAAAIAAAAAACiBQAAAAAA","254":"b:RgBIQKAADQNYAAAKgEUgAID6r4EAAAAA","264":"b:RgBIQKAADQNYAAAKgEUgAID6r4EAAAAA","286":[141],"296":[141],"304":[141],"310":"b:RAAAAAAACQBAAAAIAAAAAACiBQAAAAAA","318":[141],"320":[141],"342":"b:RgBIQKAADQNYAAAKgEUgAID6r4EAAAAA","351":"b:RAAAAAAACQBAAAAIAAAAAACiBQAAAAAA","355":"b:RgBIQKAADQNYAAAKgEUgAID6r4EAAAAA","359":"b:RgBIQLABDQNYAAAKgEUgAID6r4EAAAAA","371":"b:RgBIQLABDQNYAAAKgEUgAID6r4EAAAAA","396":"b:QAAAAAAAAQBQAAAAAEAAAAAgjwAAAAAA","414":"b:RgBIQKAADQNYAAAKgEUgAID6r4EAAAAA","430":"b:RgBIQLABDQNYAAAKgEUgAID6r4EAAAAA","458":[51,110,141,70],"459":[51,110,141,70],"468":"b:RgBIQLABDQNYAAAKgEUgAID6r4EAAAAA","513":[36,40,6,145,146,147],"570":"b:RgBIQKAADQNYAAAKgEUgAID6r4EAAAAA","582":"b:RgBIQKAADQNYAAAKgEUgAID6r4EAAAAA","591":[141],"593":"b:RgBIQKAADQNYAAAKgEUgAID6r4EAAAAA","596":"b:RgBIQKAADQNYAAAKgEUgAID6r4EAAAAA","613":"b:RgBIQKAADQNYAAAKgEUgAID6r4EAAAAA","669":"b:RgBIQKAADQNYAAAKgEUgAID6r4EAAAAA","699":[151],"713":"b:RgBIQKAADQNYAAAKgEUgAID6r4EAAAAA","731":"b:RAAAAAAACQBAAAAIAAAAAACiBQAAAAAA","745":[141],"754":"b:RAAAAAAACQBAAAAIAAAAAACiBQAAAAAA"},"packages/frontend/editor-ui/src/features/ndv/runData/components/RunInfo.vue":{"7":"b:RIAAQIAADQFYAIACgEUgAACqr4EAAAAA","15":"b:RIAAQIAADQFYAIACgEUgAACqr4EAAAAA","19":"b:RIAAQIAADQFYAIACgEUgAACqr4EAAAAA","23":"b:RIAAQIAADQFYAIACgEUgAACqr4EAAAAA","33":"b:RIAAQIAADQFYAIACgEUgAACqr4EAAAAA"},"packages/frontend/editor-ui/src/features/ndv/runData/components/TextWithHighlights.vue":{"11":"b:RtBIQKAADQdYAIAKgGUoAJT+74EAAAAA","20":[144,146],"29":"b:RtBIQKAADQdYAIAKgGUoAJT+74EAAAAA"},"packages/frontend/editor-ui/src/features/ndv/runData/components/VirtualSchema.vue":{"107":"b:RNAAQIAABQVYAIACAGEoAJT+74EAAAAA","108":"b:RNAAQIAABQVYAIACAGEoAJT+74EAAAAA","114":[141],"117":[141],"123":"b:QNAAQIAABQVYAIACAGEoAJT+74EAAAAA","134":"b:QNAAQIAABQVYAIACAGEoAJT+74EAAAAA","143":"b:QNAAQIAABQVYAIACAGEoAJT+74EAAAAA","159":[6,145,70],"165":"b:QJAAQIAABQVYAIACAGEgAAT+r4EAAAAA","176":"b:QJAAQIAABQVYAIACAGEgAAT+r4EAAAAA","205":"b:QNAAQIAABQVYAIACAGEoAJT+74EAAAAA","209":"b:QNAAQIAABQVYAIACAGEoAJT+74EAAAAA","231":"b:QNAAQIAABQVYAIACAGEoAJT+74EAAAAA","254":"b:QNAAQIAABQVYAIACAGEoAJT+74EAAAAA","286":"b:RNAAQIAABQVYAIACAGEoAJT+74EAAAAA","298":"b:QJAAQIAABQVYAIACAGEoAARO7oAAAAAA","320":"b:RNAAQIAABQVYAIACAGEoAJT+74EAAAAA","366":"b:QNAAQIAABQVYAIACAGEgAJT+74EAAAAA","384":"b:QNAAQIAABQVYAIACAGEoAJT+74EAAAAA","388":[2,6,146],"403":"b:RNAAQIAABQVYAIACAGEoAJT+74EAAAAA","405":"b:RNAAQIAABQVYAIACAGEoAJT+74EAAAAA","431":"b:RNAAQIAABQVYAIACAGEoAJT+74EAAAAA","439":"b:RNAAQIAABQVYAIACAGEoAJT+74EAAAAA","443":"b:RNAAQIAABQVYAIACAGEoAJT+74EAAAAA","444":[146],"456":"b:RNAAQIAABQVYAIACAGEgAJT+74EAAAAA","461":"b:QNAAQIAABQVYAIACAGEgAJT+74EAAAAA","463":"b:QNAAQIAABQVYAIACAGEgAJT+74EAAAAA","466":"b:RNAAQIAABQVYAIACAGEgAJT+74EAAAAA","473":[141],"482":[141],"484":[141],"486":[141],"523":[146],"524":[146],"543":"b:RNAAQIAABQVYAIACAGEoAJT+74EAAAAA","564":[141],"574":[146],"579":[139,150],"602":[68],"628":[68,138,142]},"packages/frontend/editor-ui/src/features/ndv/runData/components/VirtualSchemaItem.vue":{"37":"b:RNAAQIAABQVYAIACAGEoAJT+74EAAAAA","70":"b:RNAAQIAABQVYAIACAGEoAJT+74EAAAAA","107":"b:QNAAQIAABQUYAIACAGEoAIT+7oEAAAAA"},"packages/frontend/editor-ui/src/features/ndv/runData/schemaPreview.store.ts":{"12":"b:x/VL/LLZjwf6AYBKwOc+sN7+//MCAAAA","22":"b:x5BK4LKBDwZ6AIACgOcoAI7+7+EAAAAA","31":"b:AAAAAAAABARAAAAAAKAIAIBMzIAAAAAA","50":"b:x5BK4LKBDwZ6AIACgGcgAA7+r+EAAAAA"},"packages/frontend/editor-ui/src/features/ndv/settings/components/NodeSettings.vue":{"139":"b:xvVD3IJYjQfaAYBCwOU+sNz+/5MCAAAA","169":"b:xvVD3IJYjQfaAYBKwOU+sNz+/5MCAAAA","172":"b:xvVD3IJYjQfaAYBKwOU+sNz+/5MCAAAA","173":[55],"176":"b:xvVD3IJYjQfaAYBKwOU+sNz+/5MCAAAA","177":[55],"180":"b:xvVD3IJYjQfaAYBKwOU+sNz+/5MCAAAA","183":"b:xvVD3IJYjQfaAYBKwOU+sNz+/5MCAAAA","185":"b:xvVD3IJYjQfaAYBKwOU+sNz+/5MCAAAA","191":"b:xvVD3IJYjQfaAYBKwOU+sNz+/5MCAAAA","194":[135,138,140,141,142,143,70,147],"196":"b:xvVD3IJYjQfaAYBKwOU+sNz+/5MCAAAA","198":"b:xuVD3IJYjQfaAYBKwOU+sNz+/5MCAAAA","200":"b:xvVD3IJYjQfaAYBKwOU+sMz+/5MCAAAA","204":"b:xvVD3IJYjQfaAYBKwOU+sNz+/5MCAAAA","209":"b:xvVD3IJYjQfaAYBKwOU+sNz+/5MCAAAA","211":"b:xvVD3IJYjQfaAYBKwOU+sNz+/5MCAAAA","215":"b:xvVD3IJYjQfaAYBKwOU+sNz+/5MCAAAA","229":"b:xvVD3IJYjQfaAYBKwOU+sNz+/5MCAAAA","243":"b:xvVD3IJYjQfaAYBKwOU+sNz+/5MCAAAA","251":"b:xvVD3IJYjQfaAYBKwOU+sNz+/5MCAAAA","255":"b:xvVD3IJYjQfaAYBKwOU+sNz+/5MCAAAA","259":"b:wmVDXAJYiABSAABKwKEOsIgKWhACAAAA","263":"b:xvVD3IJYjQfaAYBKwOU+sNz+/5MCAAAA","266":"b:wmVDXAJYiABSAABKwKEOsIgKWhACAAAA","267":"b:wmUBFABYiABAAABAwKECsIgAWhAAAAAA","271":"b:xvVD3IJYjQfaAYBKwOU+sNz+/5MCAAAA","275":"b:xvVD3IJYjQfaAYBKwOU+sNz+/5MCAAAA","284":"b:xvVD3IJYjQfaAYBKwOU+sNz+/5MCAAAA","287":"b:xvVDzIIYjQfaAYBKwMU+gNz+/5MCAAAA","290":"b:xvVD3IJYjQfaAYBKwOU+sNz+/5MCAAAA","292":[145],"313":"b:xvVD3IJYjQfaAYBKwOU+sNz+/5MCAAAA","320":"b:xvVD3IJYjQfaAYBKwOU+sNz+/5MCAAAA","329":"b:AmFC1AIAiQAKAIBCwEEQMMz6OgMAAAAA","496":[13],"504":"b:xvVD3IJYjQfaAYBKwOU+sNz+/5MCAAAA","507":"b:wuVBlAJYiQSKAQBCwAUesNz2ehICAAAA","513":"b:xvVD3IJYjQfaAYBKwOU+sNz+/5MCAAAA","528":"b:xvVD3IJYjQfaAYBKwOU+sNz+/5MCAAAA","529":"b:xvVD3IJYjQfaAYBKwOU+sNz+/5MCAAAA","534":"b:gkFCQAIQiAAIAABCgEEAAABSMgIAAAAA","535":"b:gkFAAAIQiAAIAABCgAEAAABSMgIAAAAA","543":"b:AIAAQAAAAABIAIAAAEUAAACoLgAAAAAA","549":[1,7,28,8,51,55,113,124,125,14,148],"573":"b:xvVD3IJYjQfaAYBKwOU+sNz+/5MCAAAA","598":[145],"615":[2,57,67,135,13,14,149,153],"619":"b:xvVD3IJYjQfaAYBKwOU+sNz+/5MCAAAA","630":"b:xvVDjAJYiQbaAQACgIQegNz+b5ECAAAA","636":"b:wmVDXAJYiABSAABKwKEOsIgKWhACAAAA","802":"b:xvVD3IJYjQfaAYBKwOU+sNz+/5MCAAAA"},"packages/frontend/editor-ui/src/features/ndv/settings/components/NodeSettingsHeader.vue":{"22":"b:xvVD3IJYjQfaAYBKwOU+sNz+/5MCAAAA","40":[145]},"packages/frontend/editor-ui/src/features/ndv/settings/components/NodeSettingsHint.vue":{"13":"b:xvVD3IJYjQfaAYBKwOU+sMz+/5MCAAAA","16":"b:xvVD3IJYjQfaAYBKwOU+sMz+/5MCAAAA","68":"b:xvVD3IJYjQfaAYBKwOU+sMz+/5MCAAAA","72":[145],"77":[145]},"packages/frontend/editor-ui/src/features/ndv/settings/components/NodeSettingsTabs.vue":{"29":"b:xvVD3IJYjQfaAYBKwOU+sNz+/5MCAAAA","45":[116,132],"49":"b:xvVD3IJYjQfaAYBKwOU+sNz+/5MCAAAA","53":[102],"55":"b:xvVD3IJYjQfaAYBKwOU+sNz+/5MCAAAA","63":"b:xvVD3IJYjQfaAYBKwOU+sNz+/5MCAAAA","130":[145],"161":"b:xvVD3IJYjQfaAYBKwOU+sNz+/5MCAAAA"},"packages/frontend/editor-ui/src/features/ndv/settings/components/NodeWebhooks.vue":{"21":"b:xvVD3IJYjQfaAYBKwOU+sNz+/5MCAAAA","38":"b:AAFABAAICAAQAABIgAAAAAAIGgACAAAA","45":"b:AABABAAACAAQAABIgAAAAAAICgACAAAA","53":"b:xvVD3IJYjQfaAYBKwOU+sNz+/5MCAAAA","58":"b:wEFBBAAYCABQAABKgIAAAAAIGgACAAAA","70":"b:xvVD3IJYjQfaAYBKwOU+sNz+/5MCAAAA","124":"b:AAFABAAICAAQAABIgAAAAAAIGgACAAAA","192":"b:xvVD3IJYjQfaAYBKwOU+sNz+/5MCAAAA","193":[67,135,13,14,149,153],"200":"b:xvVD3IJYjQfaAYBKwOU+sNz+/5MCAAAA","218":"b:AABABAAACAAQAABIgAAAAAAICgACAAAA","224":"b:AABABAAACAAQAABIgAAAAAAICgACAAAA"},"packages/frontend/editor-ui/src/features/ndv/settings/composables/useNodeSettingsParameters.ts":{"59":"b:1vVH3MJYjbfbA4BKzuU+sdz+/5MCAAAA","71":"b:AmFC1AIAiQAKAIBCwEEQMMz6OgMAAAAA","139":[14],"194":"b:xvVH3MJYjafaAYBKxOU+sdz+/5MCAAAA"},"packages/frontend/editor-ui/src/features/ndv/shared/composables/useBinaryDataAccessTooltip.ts":{"9":"b:1vVH3MJYjbfbA4BKzuU+sdz+/5MCAAAA"},"packages/frontend/editor-ui/src/features/ndv/shared/ndv.store.ts":{"41":"b://Vf///5/7f/+/////////////8HAAAA","45":"b://Vf///5/7f/+/////////////8HAAAA","46":"b://Vf///5/7f/+/////////////8HAAAA","109":"b://Vf///5/7f/+/////////////8HAAAA","113":"b:1vVD3IJYjRfbA4BKyOU+sNz+/5MCAAAA","138":"b:1vVH3MJYjbfbA4BKzuU+sdz+/5MCAAAA","142":"b:1vVD3IJYjRfbA4BKyOU+sNz+/5MCAAAA","149":"b:kkFCUAIAiRBBAgBCiEECIJDyOgAAAAAA","153":"b:xvVH3MJYjTbaAYBCxOU+sdz+/xMCAAAA","155":[141],"157":[141],"159":"b:RtBIQKAADQdYAIAKgGUoAJT+74EAAAAA","161":"b:xvVP3LJZjQfaAYBKwOU+sNz+/5sCAAAA","163":"b:RtBIQKAADQdYAIAKgGUoAJT+74EAAAAA","165":"b:QNAAQIAABQVYAIACAGEgAJT+74EAAAAA","185":"b:QuAAAAAABABYAIAAAIAAAIDwOgAAAAAA","187":"b:QuAAAAAABABQAAAAAIAAAIDwOgAAAAAA","189":"b:AEAAAAAAABBAAAAAAEAAAIDyOAAAAAAA","193":"b:1vVH3MJYjbfbA4BKzuU+sdz+/5MCAAAA","206":"b:1vVH3MJYjbfbA4BKzuU+sdz+/5MCAAAA","214":"b:1vVH3MJYjbfbA4BKzuU+sdz+/5MCAAAA","231":"b:x/Vf/LL5nwf+I4hP6O8/8P////8HAAAA","233":"b:xvVXjAJYiYbaAQAC4IQegMz+b9kCAAAA","238":"b:xvVD3IJYjafaAYBKwOU+scz+/5MCAAAA","247":"b:xvVD3IJYjQfaAYBCwOE+sMz+/5MAAAAA","251":[2,146,70],"255":"b:xvVD3IJYjQfaAYBKwOU+sMz+/5MCAAAA","269":"b:xvVD3IJYjQfaAYBKwOU+sMz+/5MCAAAA","273":"b:wrVHjAJYiQTaAQACgIQegMz+b5kCAAAA","277":[12,135,68,6,141,144,145,146,70],"288":[135,138,140,141,142,143,70,147],"292":[135,138,140,141,142,143,70,147],"296":"b:AEFCQAIAiSBAAABCgEEAABDyOgIAAAAA","300":[141],"318":[141],"328":[141],"332":[141],"336":[141],"340":[2,141,144,146,70],"345":"b:xvVL3LJZjQfaAYBKwOU+sNz+/5MCAAAA","353":"b:xvVL3LJZjQfaAYBKwOU+sNz+/5MCAAAA","361":[141],"366":[2,141,144,146,70],"376":"b:AEBCQAIACRBAAABCgAEAAIDyOAAAAAAA","393":"b:AEFCQAIQiaBIAABCxEEAARDyOgIAAAAA","470":"b://Vf///5/7f/+/////////////8HAAAA","474":"b:AAAAAJABgACQAAAABAAAAAgDAMgEAAAA","498":"b:1/Vf/PL5n7f/I4hO7u8/8f7///8HAAAA","501":"b:1/Vf/PL5n7f/I4hO7u8/8f7///8HAAAA","525":"b:1/Vf/PL5n7f/I4hL7u8/8fz///8HAAAA","528":"b:1/Vf/PL5n7f/I4hL7u8/8fz///8HAAAA"},"packages/frontend/editor-ui/src/features/ndv/shared/ndv.utils.ts":{"41":"b:xvVD3IJYjQfaAYBKwOU+sNz+/5MCAAAA","57":"b:AmFC1AIAiQAKAIBCwEEQMMz6OgMAAAAA","132":"b:AmFC1AIAiQAKAIBCwEEQMMz6OgMAAAAA","261":"b:AkFC1AIAiQAKAIBCwEEQMMz6OgMAAAAA","267":"b:AkFC1AIAiQAKAIBCwEEQMMz6OgMAAAAA","289":[14,148],"290":[14,148],"293":[14],"307":"b:AmFC1AIAiQAKAIBCwEEQMMz6OgMAAAAA","328":"b:1vVH3MJYjbfbA4BKzuU+sdz+/5MCAAAA","335":"b:1vVH3MJYjbfbA4BKzuU+sdz+/5MCAAAA","339":"b:1vVH3IJYjbfbA4BKzuU+sdz+/5MCAAAA","361":"b:AmFC1AIAiQAKAIBCwEEQMMz6OgMAAAAA","367":"b:AEAAAAAAABAAAAAAAAAAAADwOAIAAAAA","446":"b:xvVD3IJYjafaAYBKxOU+sdz+/5MCAAAA","457":"b:xvVD3IJYjQfaAYBKxOU+sNz+/5MCAAAA","621":"b:xvVD3IJYjQfaAYBKwOU+sNz+/5MCAAAA","733":"b:xvVD3IJYjQfaAYBKxOU+sNz+/5MCAAAA"},"packages/frontend/editor-ui/src/features/ndv/shared/views/NodeDetailsViewV2.vue":{"73":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","93":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","110":"b:xvVH3IJYnQfaAYBKwOU+sMz+/5sCAAAA","112":"b:xvVD3IJYnQfaAYBKwOU+sMz+/5MCAAAA","116":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","125":[15,87,52,110,67,141,145,147,149],"127":"b:xvVD3IJYnQfaAYBKwOU+sMz+/5MCAAAA","141":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","146":"b:xuVD3IJYjQfaAYBCwOE+sMz+/5MAAAAA","153":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","166":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","172":"b:xvVD3IJYjQfaAYBKwOU+sMz+/5MCAAAA","193":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","200":"b:xvVD3IJYnQfaAYBKwOU+sMz+/5MCAAAA","202":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","206":"b:xvVD3IJYnQfaAYBKwOU+sMz+/5MCAAAA","220":[22,103,51,52,68],"226":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","231":"b:xvVD3IJYnQfaAYBKwOU+sMz+/5MCAAAA","233":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","251":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","257":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","278":"b:xvVD3IJYjQfaAYBCwOE+sMz+/5MAAAAA","293":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","304":"b:xvVD3IJYnQfaAYBKwOU+sMz+/5MCAAAA","308":"b:xvVD3IJYnQfaAYBKwOU+sMz+/5MCAAAA","310":"b:wrVHjAJYmQTaAQACgIQegMz+b5kCAAAA","312":"b:xvVD3IJYnQfaAYBKwOU+sMz+/5MCAAAA","314":"b:xvVD3IJYnQfaAYBKwOU+sMz+/5MCAAAA","318":"b:xvVD3IJYnQfaAYBKwOU+sMz+/5MCAAAA","320":"b:xvVD3IJYnQfaAYBKwOU+sMz+/5MCAAAA","324":"b:xvVD3IJYnQbaAYBCwOU+sMz+/xMCAAAA","326":"b:xvVD3IJYnQfaAYBCwOE+sMz+/5MAAAAA","328":"b:xvVD3IJYnQfaAYBKwOU+sMz+/5MCAAAA","330":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","332":"b:xvVD3IJYnQfaAYBKwOU+sMz+/5MCAAAA","336":"b:xvVD3IJYnQfaAYBKwOU+sMz+/5MCAAAA","348":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","359":"b:AIAIAAAAGAAIAIAAgEEAAID4KQAAAAAA","363":[52,6],"367":"b:AmECFABIEASCAAAAAEAKAMz0PwACAAAA","373":[52,141,146,70],"388":[52,6,141,144,145,146,70],"392":[52],"397":[2,52,141,144,146,70],"412":[52],"427":[52],"431":[52,146],"436":"b:xvVD3AJYmQbKAYBCwOEesMz+/hIAAAAA","442":"b:AIAAQAAAEABIAIAAAEUAAACoLgAAAAAA","443":"b:AIAAAAAAEAAIAIAAAEAAAAAgLgAAAAAA","451":[52],"455":"b:xvVD3AJYmQbKAYBCwOEesMz+/hIAAAAA","464":[2,52,146],"470":[52,67,135,13,149],"474":[52,13],"478":"b:wrVHjAJYmQTaAQACgIQegMz+b5kCAAAA","518":"b:xvVD3IJYnQfaAYBCwOE+sMz+/5MAAAAA","527":[2,52,146,70,151],"532":"b:xvVD3IJYnQfaAYBCwOE+sMz+/5MAAAAA","540":"b:RABAQIAAHQFYAAAKgEUgAID6r4EAAAAA","544":[52,141,70],"558":[52],"562":"b:RHAAAAAAEABKAAAAAAAAAAAgFwAAAAAA","567":"b:xvVD3IJYnQfaAYBKwOU+sMz+/5MCAAAA","572":[52,144,146],"576":"b:xvVD3IJYnQfaAYBKwOU+sMz+/5MCAAAA","580":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","584":[52],"588":[52,12,135,68,6,141,144,145,146,70],"594":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","612":"b:xvVD3IJYjQfaAYBKwOU+sMz+/5MCAAAA","622":"b:xuFBiAIAiARaAIBCgAACgMzyfxIAAAAA","660":[2,52,6,146,70,151],"664":[2,52,146,70],"668":"b:xvVD3IJYnQfaAYBCwOE+sMz+/5MAAAAA","669":"b:xvVD3IJYnQfaAYBCwOE+sMz+/5MAAAAA","674":[2,52,146,70],"675":[2,52,146,70],"680":"b:xvVD3IJYnQfaAYBKwOU+sMz+/5MCAAAA","685":"b:xvVD3IJYnQfaAYBKwOU+sMz+/5MCAAAA","688":"b:xvVD3IJYnQfaAYBKwOU+sMz+/5MCAAAA","709":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","715":"b:QgAAAJABkACwAAAAAAAAgAgDAOgEAAAA","789":"b:xvVD3AJYiQbKAYBCwOEesMz+/hIAAAAA","797":[12,6,141,144,145,146,70],"815":"b:xvVD3IJYjQfaAYBKwOU+sMz+/5MCAAAA","856":[146],"863":[135,68,146,70]},"packages/frontend/editor-ui/src/features/resolvers/composables/useDynamicCredentials.ts":{"13":"b:3/Vf/f/5/7f/++/D//////z///8HAAAA","17":"b:3/Vf/f/5/7f/++/D//////z///8HAAAA"},"packages/frontend/editor-ui/src/features/resolvers/composables/useNodePrivateCredential.ts":{"14":"b:x/VP/PL5jwf+A4hO4Oc/8N////sHAAAA","20":"b:x/VP/PL5jwf+A4hO4Oc/8N////sHAAAA","22":"b:x/VP/PL5jwf+A4hO4Oc/8N////sHAAAA","26":"b:wkEEGAKAjgAAAgAAgAACMAAAEAAAAAAA","32":"b:x/VP/PL5jwf+A4hO4Oc/8N////sHAAAA","41":"b:x/VP/PL5jwf+A4hO4Oc/8N////sHAAAA"},"packages/frontend/editor-ui/src/features/resolvers/composables/usePrivateCredentials.ts":{"15":"b:1/VP/fP5/xf/A4hO7v8/8N////sHAAAA"},"packages/frontend/editor-ui/src/features/settings/communityNodes/communityNodes.store.ts":{"12":"b:xvVD3IJYjQfaAYBKxOV+sNz+/5MCAAAA","22":[102,118],"30":[118],"36":[102,118],"37":[118],"46":[102,118],"55":[102,118],"60":[118],"74":[118],"79":[118],"84":[118],"88":[118],"103":[102]},"packages/frontend/editor-ui/src/features/settings/communityNodes/composables/useInstalledCommunityPackage.ts":{"10":"b:xvVD3IJYjQfaAYBKwOU+sNz+/5MCAAAA","16":"b:xvVD3IJYjQfaAYBKwOU+sNz+/5MCAAAA","17":"b:xvVD3IJYjQfaAYBKwOU+sNz+/5MCAAAA","25":[102],"32":"b:xvVD3IJYjQfaAYBKwOU+sNz+/5MCAAAA","34":[2,102,57,13],"41":"b:xvVD3IJYjQfaAYBKwOU+sNz+/5MCAAAA","51":"b:xvVD3IJYjQfaAYBKwOU+sNz+/5MCAAAA"},"packages/frontend/editor-ui/src/features/settings/environments.ee/environments.store.ts":{"13":"b:1/Vf///5/7f/++9P/v+/8f////8HAAAA","18":"b:1vVH3MJYjbfbA4BKzuU+sdz+/5MCAAAA","21":"b:AAAACwwA8ACAGWAAABiAMAAAAAgGAAAA","22":[119],"29":"b:1vVH3MJYjbfbA4BKzuU+sdz+/5MCAAAA","30":[70],"35":[119,126,133,70],"43":[119],"56":[119],"64":[119],"69":[119],"74":[119],"79":"b:1vVH3MJYjbfbA4BKzuU+sdz+/5MCAAAA"},"packages/frontend/editor-ui/src/features/settings/sso/sso.store.ts":{"18":"b://Vf///5/7f/+/////////////8HAAAA","25":[93,76,55,86],"38":"b://Vf///5/7f/+/////////////8HAAAA","90":[93,76,95,55,121,86,122],"153":[93,76,55,86,122],"159":[55,86],"174":[93,76,55,86]},"packages/frontend/editor-ui/src/features/settings/usage/usage.store.ts":{"38":"b:x/Vf//75/wf+O+hP6P8/8P7///8HAAAA","49":[88],"74":"b:x/Vf//75/wf+O+hP6P8/8P7///8HAAAA","79":"b:x/Vf//75/wf+O+hP6P8/8P7///8HAAAA"},"packages/frontend/editor-ui/src/features/setupPanel/setupPanel.store.ts":{"10":"b://Vf///5/7f/+/////////////8HAAAA","14":"b:x/Vf/LL5jwf+I4hC6O8/8Pz///8HAAAA","20":"b:x/Vf/PL5jwf+I4hO6O8/8P////8HAAAA","29":[18],"34":[18]},"packages/frontend/editor-ui/src/features/shared/banners/banners.store.ts":{"14":"b://Vf///5/7f/+/////////////8HAAAA","32":"b://Vf///5/7f/+//j//////z///8HAAAA","96":"b://Vf///5/7f/+/////////////8HAAAA","100":[77],"105":[55,86],"113":"b://Vf///5/7f/+//j//////z///8HAAAA"},"packages/frontend/editor-ui/src/features/shared/banners/components/BannerStack.vue":{"38":"b://Vf///5/7f/+/////////////8HAAAA","46":"b://Vf///5/7f/+/////////////8HAAAA","51":[77],"55":[77],"56":[77],"59":"b://Vf///5/7f/+/////////////8HAAAA","65":[77],"86":"b://Vf///5/7f/+/////////////8HAAAA","89":"b://Vf///5/7f/+/////////////8HAAAA"},"packages/frontend/editor-ui/src/features/shared/commandBar/composables/useChatHubCommands.ts":{"28":"b://Vf///5/7f/+/////////////8HAAAA","40":[3,4,5,96,98,32],"47":[3,4,5,96,98,32],"81":[3,4,5,96,98,32],"87":[3,4,5,96,98,32],"98":[3,4,5,96,98,32],"101":[3,4,5,96,98,32],"128":[3,4,5,96,98,32],"150":[3,4,5,96,98,32],"151":[3,4,5,96,98,32],"154":[3,4,5,96,98,32],"175":[3,4,5,96,98,32],"176":[3,4,5,96,98,32],"179":[3,4,5,96,98,32],"199":[3,4,5,96,98,32],"200":[3,4,5,96,98,32],"203":[3,4,5,96,98,32]},"packages/frontend/editor-ui/src/features/shared/commandBar/composables/useCommandBar.ts":{"31":"b://Vf///5/7f/+/////////////8HAAAA","45":"b://Vf///5/7f/+//z//////z///8HAAAA","52":"b://Vf///5/7f/+//j//////z///8HAAAA","188":"b://Vf///5/7f/+//z//////z///8HAAAA","222":"b://Vf///5/7f/+//z//////z///8HAAAA","244":[161],"253":"b://Vf///5/7f/+//z//////z///8HAAAA","259":[161],"266":"b://Vf/PP5nwf+449C/e8/8Pz///8HAAAA","284":"b://Vf///5/7f/+//z//////z///8HAAAA","285":"b://Vf///5/7f/+//z//////z///8HAAAA","289":"b://Vf///5/7f/+//z//////z///8HAAAA","290":"b://Vf///5/7f/+//z//////z///8HAAAA","293":[161],"311":"b://Vf///5/7f/+//z//////z///8HAAAA","314":"b://Vf///5/7f/++/j////9/z///8HAAAA"},"packages/frontend/editor-ui/src/features/shared/commandBar/composables/useCredentialNavigationCommands.ts":{"22":"b://Vf///5/7f/+/////////////8HAAAA","40":[161],"41":[161],"44":"b://Vf///5/7f/+//z//////z///8HAAAA","46":[161],"56":[161],"62":[161],"119":"b://Vf///5/7f/+//z//////z///8HAAAA","123":"b://Vf///5/7f/+//z//////z///8HAAAA","130":"b://Vf///5/7f/+//z//////z///8HAAAA","192":[161],"215":"b:OAEBG/8B8LGB+2+hHxjANwAAAAAGAAAA"},"packages/frontend/editor-ui/src/features/shared/commandBar/composables/useDataTableNavigationCommands.ts":{"20":"b://Vf///5/7f/+/////////////8HAAAA","44":"b://Vf///5/7f/+//z//////z///8HAAAA","50":[161],"58":[161],"124":"b:3/Vf///5/7f/+//j//////z///8HAAAA","128":"b://Vf///5/7f/+//z//////z///8HAAAA","135":"b://Vf///5/7f/+//z//////z///8HAAAA","186":[161]},"packages/frontend/editor-ui/src/features/shared/commandBar/composables/useExecutionCommands.ts":{"27":"b://Vf///5/7f/+/////////////8HAAAA","38":[56,131,137,68,69,6,157,158,159],"40":[56,131,137,68,69,6,157,158,159],"46":[56,131,137,68,69,6,157,158,159],"51":[56,131,137,68,69,6,157,158,159],"73":[56,131,137,68,69,6,157,158,159],"79":[56,131,137,68,69,6,157,158,159],"209":[56,131,137,68,69,6,157,158,159]},"packages/frontend/editor-ui/src/features/shared/commandBar/composables/useExecutionNavigationCommands.ts":{"10":"b://Vf///5/7f/+/////////////8HAAAA","28":"b://Vf///5/7f/+//z//////z///8HAAAA"},"packages/frontend/editor-ui/src/features/shared/commandBar/composables/useGenericCommands.ts":{"31":"b://Vf///5/7f/+/////////////8HAAAA","40":"b://Vf///5/7f/+//z//////z///8HAAAA"},"packages/frontend/editor-ui/src/features/shared/commandBar/composables/useInstanceAiCommands.ts":{"20":"b://Vf///5/7f/+/////////////8HAAAA","27":"b://Vf///5/7f/+//z//////z///8HAAAA","33":[36,78,79,80,37,100,38,81,39,40,82],"42":[36,78,79,80,37,100,38,81,39,40,82],"43":[36,78,79,80,37,100,38,81,39,40,82],"54":"b://Vf///5/7f/+//z//////z///8HAAAA","101":"b://Vf///5/7f/++/j////9/z///8HAAAA"},"packages/frontend/editor-ui/src/features/shared/commandBar/composables/useNodeCommands.ts":{"26":"b://Vf///5/7f/+/////////////8HAAAA","43":"b:x/Vf/AL4nwf+I4BC6O8/8Pz///8HAAAA","46":"b:x/Vf/AL4nwf+I4BC6O8/8Pz///8HAAAA","48":"b:x/Vf/AL4nwf+I4hC6O8/8Pz///8HAAAA","52":"b:x/Vf/AL4nwf+I4hC6O8/8Pz///8HAAAA","56":"b:x/Vf/AL4nwf+I4hC6O8/8Pz///8HAAAA","62":"b:x/Vf/AL4nwf+I4hC6O8/8Pz///8HAAAA","91":"b:x/Vf/AL4nwf+I4hC6O8/8Pz///8HAAAA","96":"b:x/Vf/AL4nwf+I4hC6O8/8Pz///8HAAAA","99":"b:x/Vf/AL4nwf+I4hC6O8/8Pz///8HAAAA","107":"b:x/VP/AL4jwf+A4hC4Oc/8Pz///sHAAAA","136":"b:x/Vf/AL4nwf+I4hC6O8/8Pz///8HAAAA","137":"b:x/VP/AL4jwf+A4hC4Oc/8Pz///sHAAAA","140":"b:x/Vf/AL4nwf+I4hC6O8/8Pz///8HAAAA","148":"b:x/Vf/AL4nwf+I4hC6O8/8Pz///8HAAAA"},"packages/frontend/editor-ui/src/features/shared/commandBar/composables/useProjectNavigationCommands.ts":{"16":"b://Vf///5/7f/+/////////////8HAAAA","27":[161],"72":"b:EAAAAwsAgBAAAQAgABAAJAAAAAACAAAA","78":"b://Vf///5/7f/+//z//////z///8HAAAA","84":"b://Vf///5/7f/+//z//////z///8HAAAA","125":[161]},"packages/frontend/editor-ui/src/features/shared/commandBar/composables/useRecentResources.ts":{"32":"b://Vf///5/7f/+/////////////8HAAAA","43":"b://Vf///5/7f/++/v//////////8HAAAA","58":"b:x/Bf6AKAnwf+A4ACgO8g8Nz/r/8GAAAA","59":"b:RvBCyAAAjQbUAQACgOQgoMx/j9kCAAAA","70":"b:RvBDyAAAjQbYAIACgOUgoMz+rxMCAAAA","73":"b:QrAACAAAiQDQAAAAAIAAAEx+DwAAAAAA","89":"b://Vf///5/7f/+//z//////z///8HAAAA","177":"b://Vf///5/7f/++/j////9/z///8HAAAA","181":"b:R3AIKACAlwP0AQAAgMgAMBwth+kCAAAA"},"packages/frontend/editor-ui/src/features/shared/commandBar/composables/useWorkflowCommands.ts":{"51":"b://Vf///5/7f/+/////////////8HAAAA","68":"b:x/Vf/AL4nwf+I4BC6O8/8Pz///8HAAAA","71":"b:x/Vf/AL4nwf+I4hC6O8/8Pz///8HAAAA","73":"b:x/Vf/AL4nwf+I4hC6O8/8Pz///8HAAAA","77":"b:x/Vf/AL4nwf+I4hC6O8/8Pz///8HAAAA","81":"b:x/Vf/AL4nwf+I4hC6O8/8Pz///8HAAAA","84":"b:x/VP/AL4jwf+A4hC4Oc/8Pz///sHAAAA","85":"b:wkEEGAKAjgAAAgAAgAACMAAAEAAAAAAA","97":"b:wkEEGAKAjgAAAgAAgAACMAAAEAAAAAAA","117":"b:x/Vf/AL4nwf+I4hC6O8/8Pz///8HAAAA","302":"b:x/Vf/AL4nwf+I4hC6O8/8Pz///8HAAAA","304":"b:x/VP/AL4jwf+A4hC4Oc/8Pz///sHAAAA","305":[55,151,152,153],"306":[55,151,152,153],"312":[55,152,153],"322":[55,152,153],"344":"b:x/Vf/AL4nwf+I4hC6O8/8Pz///8HAAAA","385":"b:x/Vf/AL4nwf+I4hC6O8/8Pz///8HAAAA","422":"b:x/Vf/AL4nwf+I4hC6O8/8Pz///8HAAAA","476":"b:x/Vf/AL4nwf+I4hC6O8/8Pz///8HAAAA","486":"b:x/Vf/AL4nwf+I4hC6O8/8Pz///8HAAAA"},"packages/frontend/editor-ui/src/features/shared/commandBar/composables/useWorkflowNavigationCommands.ts":{"29":"b://Vf///5/7f/+/////////////8HAAAA","55":"b://Vf///5/7f/+//z//////z///8HAAAA","57":[161],"65":[161],"74":[161],"80":[161],"161":[161],"162":[161],"167":[161],"168":[161],"206":[161],"220":[161],"242":[161],"302":[161],"312":"b://Vf///5/7f/+//z//////z///8HAAAA","313":[161],"316":"b://Vf///5/7f/+//z//////z///8HAAAA","320":[161],"323":"b://Vf///5/7f/+//z//////z///8HAAAA","377":[161],"402":"b://Vf///5/7f/++/j////9/z///8HAAAA"},"packages/frontend/editor-ui/src/features/shared/contextMenu/components/ContextMenu.vue":{"17":"b:x/Vf/PL5nwf+I4hO6O8/8P////8HAAAA","26":"b:AgAAAAAAAAIEAAAAAAAAAGwICAEAAAAA","30":"b:AgAAAAAAAAIEAAAAAAAAAGwICAEAAAAA","51":"b:AgAAAAAAAAIEAAAAAAAAAGwICAEAAAAA","59":"b:AgAAAAAAAAIEAAAAAAAAAGwICAEAAAAA","66":"b:AgAAAAAAAAIEAAAAAAAAAGwICAEAAAAA","69":"b:x/Vf/PL5nwf+I4hO6O8/8P////8HAAAA","77":"b:AgAAAAAAAAIEAAAAAAAAAGwICAEAAAAA","93":"b:AgAAAAAAAAIEAAAAAAAAAGwICAEAAAAA","100":"b:AgAAAAAAAAIEAAAAAAAAAGwICAEAAAAA","101":"b:AgAAAAAAAAIEAAAAAAAAAGwICAEAAAAA"},"packages/frontend/editor-ui/src/features/shared/contextMenu/composables/contextMenuGroupView.ts":{"20":"b:x/Vf/PL5nwf+I4hO6O8/8P////8HAAAA"},"packages/frontend/editor-ui/src/features/shared/contextMenu/composables/useContextMenu.ts":{"29":"b:x/Vf/PL5nwf+I4hO6O8/8P////8HAAAA","31":"b:x/Vf/PL5nwf+I4hO6O8/8P////8HAAAA","33":"b:AgAAAAAAAAIEAAAAAAAAAGwICAEAAAAA","42":"b:AgAAAAAAAAIEAAAAAAAAAGwICAEAAAAA","46":"b:AgAAAAAAAAIEAAAAAAAAAGwICAEAAAAA","48":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","53":"b:AgAAAAAAAAIEAAAAAAAAAGwICAEAAAAA","74":"b:x/Vf/PL5nwf+I4hO6O8/8P////8HAAAA","80":"b:AgAAAAAAAAIEAAAAAAAAAGwICAEAAAAA"},"packages/frontend/editor-ui/src/features/shared/contextMenu/composables/useContextMenuItems.ts":{"71":"b:AgAAAAAAAAIEAAAAAAAAAGwICAEAAAAA","77":"b:x/Vf/PL5nwf+I4hO6O8/8P////8HAAAA","97":"b:AgAAAAAAAAIEAAAAAAAQAGwICAEAAAAA","101":"b:AgAAAAAAAAIEAAAAAAAQAGwICAEAAAAA","121":"b:AgAAAAAAAAIEAAAAAAAQAGwICAEAAAAA","123":"b:AgAAAAAAAAIEAAAAAAAQAGwICAEAAAAA","129":"b:AgAAAAAAAAIEAAAAAAAQAGwICAEAAAAA","131":"b:AgAAAAAAAAIEAAAAAAAQAGwICAEAAAAA","132":"b:AgAAAAAAAAIEAAAAAAAQAGwICAEAAAAA","138":"b:AgAAAAAAAAIEAAAAAAAQAGwICAEAAAAA","146":"b:AgAAAAAAAAIEAAAAAAAQAEwICAEAAAAA","150":"b:AgAAAAAAAAIEAAAAAAAQAGwICAEAAAAA","165":"b:AgAAAAAAAAIEAAAAAAAAAGwICAEAAAAA","174":"b:AgAAAAAAAAIEAAAAAAAQAEwICAEAAAAA","179":"b:AgAAAAAAAAIEAAAAAAAQAGwICAEAAAAA","186":"b:AgAAAAAAAAIEAAAAAAAQAGwICAEAAAAA","189":[66],"219":"b:AgAAAAAAAAIEAAAAAAAQAGwICAEAAAAA","264":"b:AgAAAAAAAAIEAAAAAAAQAGwICAEAAAAA","352":"b:AgAAAAAAAAIEAAAAAAAQAGwICAEAAAAA","375":[66],"383":[66],"401":[66],"410":[66],"438":"b:AgAAAAAAAAIEAAAAAAAQAEwICAEAAAAA","446":"b:AgAAAAAAAAIEAAAAAAAQAEwICAEAAAAA","450":"b:AgAAAAAAAAIEAAAAAAAQAEwICAEAAAAA"},"packages/frontend/editor-ui/src/features/shared/editors/components/CodeNodeEditor/completions/js.snippets.ts":{"8":"b://Vf///5/7f/+/////////////8HAAAA"},"packages/frontend/editor-ui/src/features/shared/editors/components/CodeNodeEditor/theme.ts":{"91":"b:BkAAjABAACBKAIAAAAAQAMz0ioAAAAAA"},"packages/frontend/editor-ui/src/features/shared/editors/components/JsonEditor/JsonEditor.vue":{"33":"b:BEAACABAACBAAAAAAAAAAIj0iAAAAAAA","43":"b:BEAACABAACBAAAAAAAAAAIj0iAAAAAAA","71":"b:BEAACABAACBAAAAAAAAAAIj0iAAAAAAA","80":"b:BEAACABAACBAAAAAAAAAAIj0iAAAAAAA","84":[2,46,135,140,141,142,143,14,70,147],"89":"b:BEAACABAACBAAAAAAAAAAIj0iAAAAAAA","90":[61,135,140,141,142,143,70,147],"102":"b:BEAACABAACBAAAAAAAAAAIj0iAAAAAAA","124":"b:BEAACABAACBAAAAAAAAAAIj0iAAAAAAA"},"packages/frontend/editor-ui/src/features/shared/editors/plugins/codemirror/completions/constants.ts":{"467":"b://Vf///5/7f/+/////////////8HAAAA","471":"b://Vf///5/7f/+/////////////8HAAAA"},"packages/frontend/editor-ui/src/features/shared/editors/plugins/codemirror/completions/datatype.completions.ts":{"75":"b:AEAAAAAAABBAAAAAAAAAAIDwOAAAAAAA","163":[140,141,14,147],"179":[140,141,143,14,147],"181":[140,141,143,14,147],"187":[140,141,143,14],"188":[140,141,143,14],"205":[140,141,143,14,147],"208":[140,141,143,14,147],"210":[140,141,143,14,147],"211":[140,141,143,14,147],"215":[143],"240":[140,141,143,14,147],"261":[140,141,143,14,147],"285":[140,141,143,14],"292":[140,141,143,14],"315":[140,141,143,14],"324":[140,141,143,14],"330":[140,141,143,14],"342":[140,141,14],"348":[140,141,14],"353":[140,141,14],"362":[140,141,14],"368":[140,141,143,14],"380":[140,141,143,14],"381":[140,141,143,14],"382":[140,141,143,14],"392":[140,141,143,14,147],"414":[140,141,143,14],"422":[140,141,14,147],"453":[140,141,14,147],"472":[147],"476":[140,141,14],"477":[140,141,14],"527":[140,141,143,14,147],"545":[140,141,143,14,147],"553":[140,141,143,14,147],"561":[140,141,143,14,147],"562":[140,141,143,14,147],"573":[143],"574":[143],"578":[143],"583":[143],"633":[143],"700":[143],"731":[143],"763":[143],"776":[143],"784":[140,141,14],"985":[147],"1077":[147],"1078":[147],"1560":"b://Vf///5/7f/+/////////////8HAAAA"},"packages/frontend/editor-ui/src/features/shared/editors/plugins/codemirror/completions/infoBoxRenderer.ts":{"6":[143],"18":[143],"31":[143],"64":[147],"76":[143,147],"117":[143],"166":[143],"170":[143],"177":[143],"189":[143],"217":[143],"238":"b://Vf///5/7f/+/////////////8HAAAA","239":[143,147]},"packages/frontend/editor-ui/src/features/shared/editors/plugins/codemirror/completions/utils.ts":{"27":[135,140,141,142,143,14,147],"83":[140,141,143,14,147],"119":[140,141,14,147],"122":[140,141,14,147],"131":[60,135,140,141,142,143,14,147,149],"134":[140,141,14],"143":[140,141,14],"169":[135,140,141,142,143,14,147,149],"183":[147],"207":[60,135,140,141,142,143,14,147,149],"232":"b:wuUAAAAABBJSAAAAAAACAIDwOgAAAAAA","234":[60,135,140,141,142,143,14,147,149],"252":[135,140,141,142,143,14,147,149],"269":[140,141,14,147],"271":[140,141,14,147],"274":"b:AEAAhAAAAAAKAIAAAAAQAMzwKoAAAAAA","298":"b:AEAAhAAAAAAKAIAAAAAQAMzwKoAAAAAA","302":"b:AEAAhAAAAAAKAIAAAAAQAMzwKoAAAAAA","303":"b:AEAAhAAAAAAKAIAAAAAQAMzwKoAAAAAA","310":[60,135,140,141,142,143,14,147,149],"314":[60,135,140,141,142,143,14,147,149],"315":[60,135,140,141,142,143,14,147,149],"319":[60,135,140,141,142,143,14,147,149],"328":[60,135,140,141,142,143,14,147,149],"353":[140,141,143,14,147],"356":[140,141,143,14,147],"357":[140,141,143,14,147],"358":[140,141,143,14,147],"376":[135,140,141,142,143,14,147,149],"432":[140,141,143,14,147],"434":[140,141,143,14,147],"438":[140,141,143,14],"439":[140,141,143,14],"442":[140,141,14,147],"453":"b://Vf///5/7f/+/////////////8HAAAA","464":[140,141,14],"481":[140,141,143,14,147]},"packages/frontend/editor-ui/src/features/shared/editors/plugins/codemirror/dragAndDrop.ts":{"15":"b:xuUAjABABDJaAIAAAKAyAMz0uoIAAAAA","18":"b:wuUAgAAABDJaAIAAAKAyAIDwugIAAAAA","20":"b:wuUAgAAABBJaAIAAAKAiAIDwugIAAAAA","35":"b:xuUAjABABDJaAIAAAKAyAMz0uoIAAAAA","42":"b:xuUAjABABDJaAIAAAKAyAMz0uoIAAAAA","46":"b:xuUAjABABDJaAIAAAKAyAMz0uoIAAAAA","93":"b:xuUAhABAABJaAAAAAIASAMzwCoAAAAAA","97":"b:gEAAgAAAADBAAAAAAAAAAIDwugAAAAAA","104":"b:AEAAAAAAADBIAAAAAAAAAIDwOgAAAAAA","112":"b:gEAAgAAAADBAAAAAAAAAAIDwuAAAAAAA","115":"b:AEAAAAAAADBAAAAAAAAAAIDwOgAAAAAA","123":[141],"127":[141],"139":[141],"143":[141],"163":"b:xuUAjABABDJaAIAAAKAyAMz0uoIAAAAA"},"packages/frontend/editor-ui/src/features/shared/editors/plugins/codemirror/format.ts":{"10":"b://Vf///5/7f/+/////////////8HAAAA"},"packages/frontend/editor-ui/src/features/shared/editors/plugins/codemirror/keymap.ts":{"83":"b://Vf///5/7f/+/////////////8HAAAA","100":[141],"261":"b:AEAAAAAAAAAAAAAAAAAAAIDwOgAAAAAA"},"packages/frontend/editor-ui/src/features/shared/editors/plugins/codemirror/multiCursor.ts":{"4":"b://Vf///5/7f/+/////////////8HAAAA"},"packages/frontend/editor-ui/src/features/shared/editors/plugins/codemirror/n8nLang.ts":{"11":[111,141,142,143],"14":"b:wuUAAAAABBJSAAAAAKAiAADwOgIAAAAA","25":"b:wuUAAAAABBJSAAAAAKAiAADwOgIAAAAA","28":"b:wuUAAAAABBJSAAAAAKAiAADwOgIAAAAA","32":"b:xuUACABABDJSAAAAAKAiAIj0ugIAAAAA","37":[140,141,14,147]},"packages/frontend/editor-ui/src/features/shared/editors/plugins/codemirror/resolvableHighlighter.ts":{"61":"b://Vf///5/7f/+/////////////8HAAAA","62":"b:QuAAAAAABBBQAAAAAIAAAIDwOgAAAAAA","65":"b:QuAAAAAABBBQAAAAAIAAAIDwOgAAAAAA","75":[135,141,143],"81":[141,145,14,70,149],"101":"b:QuAAAAAABBBQAAAAAIAAAIDwOgAAAAAA","102":"b:QuAAAAAABBBQAAAAAAAAAIDwOgAAAAAA","115":"b:QuAAAAAABBBQAAAAAIAAAIDwOgAAAAAA","116":[111,135,68,141,142,143,14]},"packages/frontend/editor-ui/src/features/shared/editors/plugins/codemirror/tooltips/InfoBoxTooltip.ts":{"27":"b://Vf///5/7f/+/////////////8HAAAA","28":"b:wuUAAAAABAJSAAAAAKAiAADwOgAAAAAA","44":[140,143,147,149],"55":[143],"74":[140,143,147,149],"96":[140,142,143,147,149],"116":[140,142,143,147,149],"119":"b:wuUAAAAABBJSAAAAAKAiAADwOgAAAAAA","123":"b:wuUAAAAABAJSAAAAAKAiAADwOgAAAAAA","127":"b:wuUAAAAABBJSAAAAAKAiAADwOgIAAAAA","142":[140,143,147,149],"143":[140,142,143,147,149],"166":[140,143,147,149],"188":[143],"202":"b:wuUAAAAABBJSAAAAAKAiAADwOgIAAAAA","256":[140,143,147,149],"262":[143],"274":"b:wuUAAAAABBJSAAAAAKAiAADwOgIAAAAA","282":"b:wuUAAAAABBJSAAAAAKAiAADwOgIAAAAA","291":"b:wuUAAAAABBJSAAAAAKAiAADwOgIAAAAA","315":"b://Vf///5/7f/+/////////////8HAAAA","319":"b:wuUAAAAABBJSAAAAAKAiAADwOgIAAAAA","322":"b:wuUAAAAABBJSAAAAAKAiAADwOgIAAAAA","352":"b:wuUAAAAABBJSAAAAAKAiAADwOgIAAAAA","360":[143,147],"378":[143],"381":[143],"423":"b:guUAhAAAABBKAIAAAAASAMzwMgAAAAAA","435":"b:wuUAAAAABBJSAAAAAKAiAADwOgIAAAAA","443":"b:wqUAAAAAABJSAAAAAIACAABQAAAAAAAA","449":"b:wuUAAAAABBJSAAAAAKAiAADwOgIAAAAA"},"packages/frontend/editor-ui/src/features/shared/editors/plugins/codemirror/typescript/client/facet.ts":{"9":"b://Vf///5/7f/+/////////////8HAAAA"},"packages/frontend/editor-ui/src/features/shared/envFeatureFlag/useEnvFeatureFlag.ts":{"5":"b://Vf///5/7f/++/L//////z///8HAAAA","8":"b://Vf///5/7f/++/L//////z///8HAAAA"},"packages/frontend/editor-ui/src/features/shared/nodeCreator/components/NodeCreator.vue":{"54":"b:x/Vf/LL5nwf+I4hC6O8/8Pz///8HAAAA","56":"b:guVBlAJ4mQSaAQBC4AUO8Mz2flICAAAA","64":"b:guVBlAJ4mQSaAQBC4AUO8Mz2flICAAAA","72":[52,65],"83":"b:wuVVlJJ4mQS6AQBC4AUO8Mz3fvoCAAAA","87":"b:guVBlAJ4mQSaAQBC4AUO8Mz2flICAAAA","91":"b:guVBlAJ4mQSaAQBC4AUO8Mz2flICAAAA","96":[52],"99":[52],"119":"b:x/Vf/LL5nwf+I4hC6O8/8Pz///8HAAAA","123":"b:x/Vf/LL5nwf+I4hC6O8/8Pz///8HAAAA","124":"b:guVBlAJ4mQSaAQBC4AUO8Mz2flICAAAA","134":"b:guVBlAJ4mQSaAQBC4AUO8Mz2flICAAAA","142":[26,101,52,14],"145":"b:x/Vf/LL5nwf+I4hC6O8/8Pz///8HAAAA","149":"b:x/Vf/LL5nwf+I4hC6O8/8Pz///8HAAAA","160":"b:QgAUAJAAkACwAAAAAAAAgAgDAOgAAAAA","165":[52,146]},"packages/frontend/editor-ui/src/features/shared/nodeCreator/components/NodeCreatorShortcutCoachmark.vue":{"14":"b:x/Vf/LL5nwf+I4hC6O8/8Pz///8HAAAA","28":"b:x/Vf/LL5nwf+I4hC6O8/8Pz///8HAAAA","31":[52],"45":[52]},"packages/frontend/editor-ui/src/features/shared/nodeCreator/composables/useActionsGeneration.ts":{"32":"b:x/Vf/LL5nwf+I4hC6O8/8Pz///8HAAAA","38":"b:x/Vf/LL5nwf+I4hC6O8/8Pz///8HAAAA","46":"b:x/Vf/LL5nwf+I4hC6O8/8Pz///8HAAAA","49":"b:x/Vf/LL5nwf+I4hC6O8/8Pz///8HAAAA","62":"b:x/Vf/LL5nwf+I4hC6O8/8Pz///8HAAAA","68":"b:x/Vf/LL5nwf+I4hC6O8/8Pz///8HAAAA","83":"b:x/Vf/LL5nwf+I4hC6O8/8Pz///8HAAAA","104":"b:x/Vf/LL5nwf+I4hC6O8/8Pz///8HAAAA","114":"b:x/Vf/LL5nwf+I4hC6O8/8Pz///8HAAAA","120":"b:x/Vf/LL5nwf+I4hC6O8/8Pz///8HAAAA","126":"b:x/Vf/LL5nwf+I4hC6O8/8Pz///8HAAAA","133":"b:x/Vf/LL5nwf+I4hC6O8/8Pz///8HAAAA","148":"b:x/Vf/LL5nwf+I4hC6O8/8Pz///8HAAAA","153":"b:x/Vf/LL5nwf+I4hC6O8/8Pz///8HAAAA","157":"b:x/Vf/LL5nwf+I4hC6O8/8Pz///8HAAAA","161":"b:x/Vf/LL5nwf+I4hC6O8/8Pz///8HAAAA","179":"b:x/Vf/LL5nwf+I4hC6O8/8Pz///8HAAAA","184":"b:x/Vf/LL5nwf+I4hC6O8/8Pz///8HAAAA","192":"b:x/Vf/LL5nwf+I4hC6O8/8Pz///8HAAAA","210":"b:x/Vf/LL5nwf+I4hC6O8/8Pz///8HAAAA","213":"b:x/Vf/LL5nwf+I4hC6O8/8Pz///8HAAAA","234":"b:x/Vf/LL5nwf+I4hC6O8/8Pz///8HAAAA","245":"b:x/Vf/LL5nwf+I4hC6O8/8Pz///8HAAAA","264":"b:x/Vf/LL5nwf+I4hC6O8/8Pz///8HAAAA","267":"b:x/Vf/LL5nwf+I4hC6O8/8Pz///8HAAAA","271":"b:x/Vf/LL5nwf+I4hC6O8/8Pz///8HAAAA","273":"b:x/Vf/LL5nwf+I4hC6O8/8Pz///8HAAAA","274":"b:x/Vf/LL5nwf+I4hC6O8/8Pz///8HAAAA","278":"b:x/Vf/LL5nwf+I4hC6O8/8Pz///8HAAAA","291":"b:x/Vf/LL5nwf+I4hC6O8/8Pz///8HAAAA","329":"b://Vf///5/7f/+/////////////8HAAAA","330":"b:x/Vf/LL5nwf+I4hC6O8/8Pz///8HAAAA","348":"b:x/Vf/LL5nwf+I4hC6O8/8Pz///8HAAAA","351":"b:x/Vf/LL5nwf+I4hC6O8/8Pz///8HAAAA","362":"b:x/Vf/LL5nwf+I4hC6O8/8Pz///8HAAAA","392":"b:x/Vf/LL5nwf+I4hC6O8/8Pz///8HAAAA","399":"b:x/Vf/LL5nwf+I4hC6O8/8Pz///8HAAAA","421":"b:x/Vf/LL5nwf+I4hC6O8/8Pz///8HAAAA","422":"b:x/Vf/LL5nwf+I4hC6O8/8Pz///8HAAAA","427":"b:x/Vf/LL5nwf+I4hC6O8/8Pz///8HAAAA","433":"b:x/Vf/LL5nwf+I4hC6O8/8Pz///8HAAAA","444":"b:x/Vf/LL5nwf+I4hC6O8/8Pz///8HAAAA","445":"b:x/Vf/LL5nwf+I4hC6O8/8Pz///8HAAAA","449":"b:x/Vf/LL5nwf+I4hC6O8/8Pz///8HAAAA"},"packages/frontend/editor-ui/src/features/shared/nodeCreator/composables/useGetNodeCreatorFilter.ts":{"9":"b://Vf///5/7f/+/////////////8HAAAA","13":[1,7,10,13,14],"30":[1,7,10,13,14],"49":[1,10,13]},"packages/frontend/editor-ui/src/features/shared/nodeCreator/composables/useKeyboardNavigation.ts":{"14":"b:x/Vf/LL5jwf+I4hC6O8/8Pz///8HAAAA","20":[26,101,65,14],"43":[26,65],"46":"b:guVBlAJ4iQSaAQBC4AUO8Mz2flICAAAA","51":"b:guVBlAJ4iQSaAQBC4AUO8Mz2flICAAAA","52":"b:guVBlAJ4iQSaAQBC4AUO8Mz2flICAAAA","55":"b:guVBlAJ4iQSaAQBC4AUO8Mz2flICAAAA","59":"b:guVBlAJ4iQSaAQBC4AUO8Mz2flICAAAA","66":[26,101,65,14],"68":[26,101,65,14],"70":[26,101,65,14],"83":[15,26,43,101,65,131,134,14],"106":[26,101,65,14],"131":"b:guVBlAJ4iQSaAQBC4AUO8Mz2flICAAAA","135":"b:guVBlAJ4iQSaAQBC4AUO8Mz2flICAAAA","145":"b:guVBlAJ4iQSaAQBC4AUO8Mz2flICAAAA","151":"b:guVBlAJ4iQSaAQBC4AUO8Mz2flICAAAA","155":"b:guVBlAJ4iQSaAQBC4AUO8Mz2flICAAAA","164":"b:x/Vf/LL5jwf+I4hC6O8/8Pz///8HAAAA","165":"b:x/Vf/LL5nwf+I4hC6O8/8Pz///8HAAAA","174":"b:guVBlAJ4iQSaAQBC4AUO8Mz2flICAAAA","179":"b:gkVBlAB4iAAKAAAA4AAAsMxQfhIAAAAA","180":"b:gkVBlAB4iAAKAAAA4AAAsMxQfhIAAAAA"},"packages/frontend/editor-ui/src/features/shared/nodeCreator/composables/useNodeCreatorShortcutCoachmark.ts":{"12":"b:x/Vf/LL5nwf+I4hC6O8/8Pz///8HAAAA","22":"b:QgAUAJAAgACwAAAAAAAAgAgDAOgAAAAA"},"packages/frontend/editor-ui/src/features/shared/nodeCreator/composables/useViewStacks.ts":{"108":"b://Vf///5/7f/+/////////////8HAAAA","115":"b:x/Vf/LL5jwf+I4hC6O8/8Pz///8HAAAA","124":"b:guVBlAJ4iQSaAQBC4AUO8Mz2flICAAAA","156":"b:guVBlAJ4iQSaAQBC4AUO8Mz2fhIAAAAA","172":"b:guVBlAJ4iQSaAQBC4AUO8Mz2flICAAAA","185":"b:guVBlAJ4iQSaAQBC4AUO8Mz2flICAAAA","189":"b:guVBlAJ4iQSaAQBC4AUO8Mz2fhIAAAAA","193":"b:guVBlAJ4iQSaAQBC4AUO8Mz2fhIAAAAA","196":"b:guVBlAJ4iQSaAQBC4AUO8Mz2flICAAAA","200":"b:guVBlAJ4iQSaAQBC4AUO8Mz2flICAAAA","204":"b:guVBlAJ4iQSaAQBC4AUO8Mz2flICAAAA","208":"b:guVBlAJ4iQSaAQBC4AUO8Mz2fhIAAAAA","209":"b:guVBlAJ4iQSaAQBC4AUO8Mz2fhIAAAAA","215":"b:guVBlAJ4iQSaAQBC4AUO8Mz2flICAAAA","233":"b:guVBlAJ4iQSaAQBC4AUO8Mz2fhIAAAAA","234":"b:guVBlAJ4iQSaAQBC4AUO8Mz2fhIAAAAA","247":[31,101],"262":[101,44],"264":"b:guVBlAJ4iQSaAQBC4AUO8Mz2fhIAAAAA","268":[1,7,8,10,113,13,14],"269":[1,7,8,10,113,13,14],"280":[1,7,10,13,14],"281":[1,7,10,13],"295":[1,10,13],"303":[1,10,13],"304":[1,10,13],"307":[1,10,13],"321":"b:guVBlAJ4iQSaAQBC4AUO8Mz2flICAAAA","326":"b:guVBlAJ4iQSaAQBC4AUO8Mz2flICAAAA","338":[1,7,8,10,13,14],"379":[1,10,13],"411":[1,10,13],"426":"b:guVBlAJ4iQSaAQBC4AUO8Mz2fhIAAAAA","427":"b:guVBlAJ4iQSaAQBC4AUO8Mz2fhIAAAAA","443":[1,7,10,13,14],"462":[1,7,10,13,14],"492":[1,7,10,13,14],"508":[1,10,13],"515":[1,7,10,13,14],"529":"b:guVBlAJ4iQSaAQBC4AUO8Mz2flICAAAA","558":[101],"559":[101],"580":"b:guVBlAJ4iQSaAQBC4AUO8Mz2flICAAAA","602":[101,45],"609":"b:guVBlAJ4iQSaAQBC4AUO8Mz2flICAAAA","611":"b:guVBlAJ4iQSaAQBC4AUO8Mz2flICAAAA","615":"b:guVBlAJ4iQSaAQBC4AUO8Mz2flICAAAA","624":"b:guVBlAJ4iQSaAQBC4AUO8Mz2flICAAAA"},"packages/frontend/editor-ui/src/features/shared/nodeCreator/nodeCreator.store.ts":{"51":"b://Vf///5/7f/+/////////////8HAAAA","53":"b:AOABBABAgAAQAAAAYAAAAMhAAEACAAAA","73":[1,7,10,13,14],"74":[1,7,10,13,14],"77":"b:x/Vf/LL5nwf+I4hC6O8/8Pz///8HAAAA","85":"b:x/Vf/LL5nwf+I4hC6O8/8Pz///8HAAAA","89":"b:x/Vf/PL5jwf+I4hP6O8/8P////8HAAAA","93":"b:guVBlAJ4iQSaAQBC4AUO8Mz2flICAAAA","97":[13,14],"118":[13,14],"143":"b:x/Vf/PL5nwf+I4hP6O8/8P////8HAAAA","182":"b:gmQABAAwAAQCAAAAIAAMAAQEIAAAAAAA","271":"b:AIABBABAgAAQAAAAYAAAAMhAAEACAAAA","331":"b:guVBlAJ4iQSaAQBC4AUO8Mz2flICAAAA","337":"b:guVBlAJ4iQSaAQBC4AUO8Mz2flICAAAA","341":"b:guVBlAJ4iQSaAQBC4AUO8Mz2flICAAAA","348":"b:guVBlAJ4iQSaAQBC4AUO8Mz2flICAAAA","375":"b:gmVBlAIQiASKAQBCwAEG4MzyehIAAAAA","391":"b:gmVBlAIQiASKAQBCwAEG4MzyehIAAAAA","431":[46],"435":"b:gEFBlABoiAAKAAAAgAAAsMxQfhIAAAAA","448":[1,10,101,44],"459":"b:guVBlAJ4iQSaAQBC4AUO8Mz2elICAAAA","475":"b:x/Vf/PL5nwf+I4hP6O8/8P////8HAAAA"},"packages/frontend/editor-ui/src/features/shared/nodeCreator/nodeCreator.utils.ts":{"55":"b:guVBlAJ4iQSaAQBC4AUO8Mz2fhIAAAAA","76":[101,44],"82":[101,44],"86":[101,44],"98":[101,44],"109":"b:gmVBlAB4iAAKAAAAoAACsMxQfhIAAAAA","110":"b:gmVBlAB4iAAKAAAAoAAAsMxQfhAAAAAA","122":"b:guVBlAJ4iQSaAQBC4AUO8Mz2fhIAAAAA","127":"b:AsEBlAIwgACaAAAAYAUA8MzyGhIAAAAA","166":"b:guVBlAJ4iQSaAQBC4AUO8Mz2flICAAAA","204":"b:guVBlAJ4iQSaAQBC4AUO8Mz2fhIAAAAA","223":"b:guVBlAJ4iQSaAQBC4AUO8Mz2fhIAAAAA","243":"b:guVBlAJ4iQSaAQBC4AUO8Mz2fhIAAAAA","252":"b:guVBlAJ4iQSaAQBC4AUO8Mz2fhIAAAAA","255":"b:guVBlAJ4iQSaAQBC4AUO8Mz2flICAAAA","256":"b:guVBlAJ4iQSaAQBC4AUO8Mz2flICAAAA","258":"b:guVBlAJ4iQSaAQBC4AUO8Mz2flICAAAA","272":[1,10,13],"277":[1,10,13],"291":[1,10,13],"306":[1,7,8,10,44,113,13,14],"311":[1,7,8,10,44,13,14],"315":[1,7,8,10,44,113,13,14],"316":[1,7,8,10,44,13,14],"317":[1,7,8,10,44,13,14],"326":[1,7,8,10,44,113,13,14],"330":[1,7,8,10,44,13,14],"341":[1,7,8,10,44,13,14],"342":[1,7,8,10,44,13,14],"358":[1,7,8,10,44,113,13,14],"360":[1,7,13,14],"374":[1,7,8,10,44,113,13,14],"382":"b:x/Vf/LL5nwf+I4hC6O8/8Pz///8HAAAA","390":"b:x/VP/PL5jwf+A4hO4Od/8P7///sHAAAA","393":"b:x/VP/PL5jxf+A4hO5Oc/8N////sHAAAA","399":"b:guVBlAJ4iQSaAQBC4AUO8Mz2flICAAAA","416":[1,7,13],"440":"b:guVBlAJ4iQSaAQBC4AUO8Mz2flICAAAA","478":"b:guVBlAJ4iQSaAQBC4AUO8Mz2flICAAAA","480":"b:guVBlAJ4iQSaAQBC4AUO8Mz2flICAAAA","498":"b:guVBlAJ4iQSaAQBC4AUO8Mz2flICAAAA","528":"b:guFBlAJ4iQSaAQBC4AUO8Mz2fhIAAAAA","533":[102],"582":[45],"600":[14],"621":"b:gsFBlAIwiASKAAAC4AEOsMz2fhIAAAAA","623":"b:gsFBlAIwiASKAAAC4AEOsMz2fhIAAAAA","624":"b:gsFBlAIwiASKAAAC4AEOsMz2fhIAAAAA","648":"b:guVBlAJ4iQSaAQBC4AUO8Mz2flICAAAA","657":"b:guVBlAJ4iQSaAQBC4AUO8Mz2flICAAAA","663":"b:guVBlAJ4iQSaAQBC4AUO8Mz2flICAAAA","670":"b:guVBlAJ4iQSaAQBC4AUO8Mz2flICAAAA","678":[44],"679":[44]},"packages/frontend/editor-ui/src/features/shared/nodeCreator/views/NodeCreation.vue":{"41":"b:x/Vf/LL5nwf+I4hC6O8/8Pz///8HAAAA","66":"b:x/Vf/LL5nwf+I4hC6O8/8Pz///8HAAAA","73":"b:guVBlAI4mQSKAQBC4AUG8MzyfhIAAAAA","80":[52,133],"94":"b:guVBlAJ4mQSaAQBC4AUO8Mz2flICAAAA","101":"b:guVBlAJ4mQSaAQBC4AUO8Mz2elICAAAA","106":[52,116,132],"124":[52],"128":[15,17,52],"145":[52],"148":[52],"164":"b:x/Vf/LL5nwf+I4hC6O8/8Pz///8HAAAA","168":"b:x/Vf/LL5nwf2I4hC6O8/8Pz///8HAAAA","183":"b:x/Vf/LL5nwf+I4hC6O8/8Pz///8HAAAA","197":"b:x/Vf/LL5nwf+I4hC6O8/8Pz///8HAAAA","211":"b:x/Vf/LL5nwf+I4hC6O8/8Pz///8HAAAA","249":[15,87,16,17,18,20],"258":[15,87,16,17,18,20]},"packages/frontend/editor-ui/src/features/shared/tags/tags.api.ts":{"8":"b://Vf///5/7f/+/////////////8HAAAA","10":"b:3/Vf///5/7f/++/j////9/z///8HAAAA","13":[116,71,154,160],"26":[154]},"packages/frontend/editor-ui/src/features/shared/tags/tags.store.ts":{"34":"b://Vf///5/7f/+/////////////8HAAAA","39":"b://Vf///5/7f/+/////////////8HAAAA","51":[52,154,161],"52":[154,161],"55":[52,154,161],"61":"b:3/Vf///5/7f/++/D////9/z///8HAAAA","62":"b:AAAAAAAAAACCAAAAAAAAAACBDAYHAAAA","70":"b:R3AIKPCBlwP0AwANgMAQMB4th+0HAAAA","71":[116,71,154,160],"92":[154],"97":"b://Vf///5/7f/++/j////9/z///8HAAAA","116":[116,71,154,160],"149":[154]},"packages/frontend/editor-ui/src/features/workflow-reviews/composables/useWorkflowReviewDialogPreferences.ts":{"10":"b:x/Vf/PL5nwf+I4hD6O8/8Pz///8HAAAA"},"packages/frontend/editor-ui/src/features/workflow-reviews/composables/useWorkflowReviewStatusSync.ts":{"14":"b:x/Vf/PL5nwf+I4hD6O8/8Pz///8HAAAA","20":"b:x/Vf/PL5nwf+I4hD6O8/8Pz///8HAAAA","30":"b:x/Bf6PKBnwf+A4ADgO8g8Nz/r/8GAAAA","42":"b:x/Vf/PL5nwf+I4hD6O8/8Pz///8HAAAA","43":"b:wuBXwAIAmASaAYACgC8gwMzSLl4GAAAA","48":"b:x/Vf/PL5nwf+I4hD6O8/8Pz///8HAAAA","49":[52],"55":"b:x/Vf/PL5nwf+I4hD6O8/8Pz///8HAAAA","56":[52],"57":[36,39,40,52,55,136,71,155]},"packages/frontend/editor-ui/src/features/workflow-reviews/composables/useWorkflowReviewsFeature.ts":{"11":"b:x/Vf///5/xf/++9D+P+/8Pz///8HAAAA","19":"b:x/Vf///5/xf/++9D+P+/8Pz///8HAAAA"},"packages/frontend/editor-ui/src/features/workflow-reviews/reviewRequired.store.ts":{"7":"b:x/Vf/PL5nwf+I4hD6O8/8Pz///8HAAAA"},"packages/frontend/editor-ui/src/features/workflow-reviews/reviewStatus.store.ts":{"13":"b:x/Vf/PL5nwf+I4hD6O8/8Pz///8HAAAA","20":[52],"25":[52],"30":[52],"33":[52],"61":[52]},"packages/frontend/editor-ui/src/features/workflows/canvas/canvas.types.ts":{"27":"b://Vf///5/7f/+/////////////8HAAAA","54":"b://Vf///5/7f/+/////////////8HAAAA","169":[66],"173":"b:xvVL3IJ4jQfeA4BK4Oc/8Pz+//sDAAAA","218":"b:x/Vf/PL5nwf+I4hO6O8/8P////8HAAAA"},"packages/frontend/editor-ui/src/features/workflows/canvas/canvas.utils.ts":{"37":"b:x/Vf/PL5nwf+I4hO6O8/8P////8HAAAA","51":"b:x/VP/PL5jwf+A4hO4Oc/8N7///sHAAAA","60":"b:x/VP/PLZjwf+A4hOwOM/sN7///MFAAAA","61":"b:x/VP/PL5jwf+A4hO4Oc/8N7///sHAAAA","63":"b:x/VP/PLZjwf+A4hOwOM/sN7///MFAAAA","64":"b:x/VP/PL5jwf+A4hO4Oc/8N7///sHAAAA","80":"b:x/Vf/PL5nwf+I4hO6O8/8P////8HAAAA","97":"b:x/Vf/PL5nwf+I4hO6O8/8P////8HAAAA","130":"b:x/Vf/PL5nwf+I4hO6O8/8P////8HAAAA","135":"b:x/Vf/PL5nwf+I4hO6O8/8P////8HAAAA","137":"b:x/VP/PLZjwf+A4hOwOM/sN////MFAAAA","143":"b:x/VP/PLZjwf+A4hOwOM/sN////MFAAAA","145":"b:x/VP/PLZjwf+A4hOwOM/sN////MFAAAA","146":"b:x/VP/PLZjwf+A4hOwOM/sN////MFAAAA","203":[134,152],"235":"b:x/VP/PL5jwf+A4hO4Oc/8P////MHAAAA","255":"b:x/VP/PL5jwf+A4hO4Oc/8P////sHAAAA","270":"b:x/VP/PLZjwf+A4hOwOM/sN////MFAAAA","281":[58,115,65,66,134],"298":"b:wuVH1AJYiQSOAYBCwCM/sOz2fhMBAAAA","332":"b:x/VP/PL5jwf+A4hO4Oc/8P////sHAAAA","341":"b:x/VP/PL5jwf+A4hO4Oc/8N////sHAAAA","349":"b:x/VP/PL5jwf+A4hO4Oc/8N////sHAAAA","366":[115,66,133,151],"384":"b:x/Vf/PL5nwf+I4hO6O8/8P////8HAAAA","401":"b:x/VP/LL5jwf+A4hC4Oc/8N3///sHAAAA","411":"b:x/VP/LL5jwf+A4hC4Oc/8N3///sHAAAA","415":"b:x/VP/LL5jwf+A4hC4Oc/8N3///sHAAAA","429":"b:QuEGVARIIBSHA4IAADq7IG5ED/kHAAAA"},"packages/frontend/editor-ui/src/features/workflows/canvas/components/Canvas.vue":{"221":"b:x/Vf/PL5nwf+I4hO6O8/8P////8HAAAA","223":"b:x/Vf/PL5nwf+I4hO6O8/8P////8HAAAA","224":[128],"261":"b:x/Vf/PL5nwf+I4hO6O8/8P////8HAAAA","275":"b:x/Vf/PL5nwf+I4hO6O8/8P////8HAAAA","276":"b:xvVL3IJ4jQfeA4BK4Oc/8Pz+//sDAAAA","278":[65,130,131,134,72,73],"279":[65,130,131,134,72,73],"287":"b:x/Vf/PL5nwf+I4hO6O8/8P////8HAAAA","304":"b:AAAAAAAAAAAGAwAAAAAAAGwAAgkEAAAA","310":"b:AMAGAAAAAAAGAwAAAAAAAOzQOgkEAAAA","348":"b:AiAEBABIAASGAwAAAAAKAGwEAPkGAAAA","350":[65],"358":[65],"388":[152],"390":[152],"393":[65],"395":[65],"410":[66],"417":[66],"433":[66],"439":[66],"449":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","459":[66],"464":[66],"476":"b:AAAEAAAAAAAGAwAAAAAAAGwAAPkEAAAA","480":[65,72,73],"481":[65,72,73],"523":[131],"524":[131,134,156,157,158,159],"526":[131],"534":[133],"537":[162],"574":"b:x/Vf/PL5nwf+I4hO6O8/8P////8HAAAA","580":"b:AAAAAAAAAAACAwAAAAAAAGwAAPEAAAAA","592":"b:x/RfTIBwjQdeA4AK4OA9gPx/r/kDAAAA","593":"b:x/RfTIBwjQdeA4AK4OA9gPx/r/kDAAAA","602":"b:x/RKTIAwjQdeA4AKoOA9gNx/r/kBAAAA","609":"b:x/Vf/PL5nwf+I4hO6O8/8P////8HAAAA","610":"b:xvVL3AIQiQfeA4BCwOc/sPz6//MAAAAA","615":"b:x/Vf/PL5nwf+I4hO6O8/8P////8HAAAA","622":"b:x/Vf/PL5nwf+I4hO6O8/8P////8HAAAA","623":"b:xvVL3IJ4jQfeA4BK4Oc/8Pz+//sDAAAA","625":"b:x/Vf/PL5nwf+I4hO6O8/8P////8HAAAA","628":"b:x/Vf/PL5nwf+I4hO6O8/8P////8HAAAA","629":[15,87,16,17,18,20],"637":"b:ggQABAAwAAACAAAAIAAEAAQEIAAAAAAA","649":[57,65,130,66,134,72],"659":[66],"674":[57,65,130,66,134,72],"675":[66],"676":[66],"677":[57,65,130,66,134,72],"678":[57,65,130,66,134,72],"679":[66],"688":"b:x/Vf/PL5nwf+I4hO6O8/8P////8HAAAA","689":[66],"690":"b:xvVL3IJ4jQfeA4BK4Oc/8Pz+//sDAAAA","691":[66],"697":"b:x/Vf/PL5nwf+I4hO6O8/8P////8HAAAA","708":[57,65,130,66,134,72],"712":[57,65,130,66,134,72],"716":[57,65,130,66,134,72],"733":[66],"757":[66],"758":[66],"773":[66],"774":[66],"779":[66],"883":[66],"998":[65,130,134],"1007":"b:xPBCSIAAjQNeA4AKgOAwANx+j/kAAAAA","1046":"b:RQAIBAAACAIGAAAAAAAAADwABgEAAAAA","1056":"b:xPBCSIAAhQNYAIAKgOAwAIh+j4EAAAAA","1065":"b:wuVL1AJ4iQaeAYBC4Cc/8Oz+flMDAAAA","1069":[65,130,131,134,72,73],"1073":"b:xvVL3IJ4jQfeA4BK4Oc/8Pz+//sDAAAA","1077":"b:wuVL1AJ4iQaeAYBC4Cc/8Oz+flMDAAAA","1082":"b:wuVL1AJ4iQaeAYBC4Cc/8Oz+flMDAAAA","1107":[127,65,6],"1111":[112,65,6],"1119":[1],"1123":[1],"1126":[94,51,130],"1130":[94,51,130],"1171":"b:ggQABAAwAAAGAAAAIAAEAAQEIAEAAAAA","1178":[130,66,152],"1185":"b:ggQABAAwAAAGAAAAIAAEAAQEIAEAAAAA","1196":[65,66,134],"1200":[58,115,65],"1213":"b:QAAAABABgAQGAAAAAAAYAEgAAAEAAAAA","1219":[58,115,116,65,66,134],"1237":"b:QAAAABABgAQGAAAAAAAYAEgAAAAAAAAA","1242":[55,58,115,116,65,66,134,6],"1249":"b:x/RXTIBwjQNeA4AK4OA9gPx+r/kDAAAA","1253":"b:xvRXTIBwjQNeAYAK4OA8gPx+r/kDAAAA","1261":[0,2,39,12,65,68,139,140,142],"1269":"b:AAAEAAAAAAAGAwAAAAAAAGwAAPkEAAAA","1277":"b:AAAEAAAAAAAGAwAAAAAAAGwAAPkEAAAA","1294":"b:x/RKTIAwjQNeA4AKoOA0APx+r/kAAAAA","1304":"b:RQAIBAAACAIGAAAAAAAAADwABgEAAAAA","1312":"b:x/Vf/PL5nwf+I4hO6O8/8P////8HAAAA","1320":[131],"1324":[131,136],"1328":[131],"1332":[131],"1336":"b:x/Vf/PL5nwf+I4hO6O8/8P////8HAAAA","1349":"b:x/Vf/PL5nwf+I4hO6O8/8P////8HAAAA","1355":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","1356":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","1382":"b:AgAAAAAAAAIEAAAAAAAAAGwICAEAAAAA","1384":[130,131,133,152],"1402":"b:AgAAAAAAAAIEAAAAAAAAAGwICAEAAAAA","1447":"b:AgAAAAAAAAIEAAAAAAAAAGwICAEAAAAA","1531":[18,114,128],"1562":[65],"1566":[65],"1576":"b:x/Vf/PL5nwf+I4hO6O8/8P////8HAAAA","1580":"b:x/Vf/PL5nwf+I4hO6O8/8P////8HAAAA","1639":"b:AGAACAAAgQGEAQAAiAgAAAwUDgECAAAA","1646":[90,91,129],"1654":[90,91,129],"1662":"b:x/Vf/PL5nwf+I4hO6O8/8P////8HAAAA","1673":"b:QgAAAJABgACwAAAAAAAAgAgDAOgEAAAA","1684":"b:x/Vf/PL5nwf+I4hO6O8/8P////8HAAAA","1693":"b:x/Vf/PL5nwf+I4hO6O8/8P////8HAAAA","1709":"b:x/Vf/PL5nwf+I4hO6O8/8P////8HAAAA","1719":"b:RQAIBAAACAIGAAAAAAAAADwABgEAAAAA","1723":"b:x/Vf/PL5nwf+I4hO6O8/8P////8HAAAA","1760":"b:x/Vf/PL5nwf+I4hO6O8/8P////8HAAAA","1804":[66],"1821":"b:x/Vf/PL5nwf+I4hO6O8/8P////8HAAAA","1856":"b:x/VP/PLZjwf+A4hOwOM/sN////MFAAAA","1866":[55,58,115,116,65,66,134,6],"1871":"b:ggQABAAwAAAGAAAAIAAEAAQEIAEAAAAA","1873":"b:x/Vf/PL5nwf+I4hO6O8/8P////8HAAAA","1877":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","1893":"b:x/Vf/PL5nwf+I4hO6O8/8P////8HAAAA"},"packages/frontend/editor-ui/src/features/workflows/canvas/components/WorkflowCanvas.vue":{"82":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","83":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","87":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","93":"b:QgAAAJABgACwAAAAAAAAgAgDAOgEAAAA","97":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","102":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","105":"b:x/Vf/PL5nwf+I4hC6O8/8Pz///8HAAAA","106":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","107":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","108":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","112":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","113":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","114":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","118":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","119":[129],"127":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","128":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","129":"b:x/Vf/LL5nwf+I4hC6O8/8Pz///8HAAAA","132":"b:x/VP/PL5jwf+A4hO4Oc/8N7///sHAAAA","148":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","163":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","171":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","178":[66],"179":[66],"180":[66],"182":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","188":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","191":[66],"192":[66],"193":[66],"194":[66],"200":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","210":[66],"215":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","231":[131,134,147,72,160],"239":[131,134,147,72,160],"245":[131,134,147,72,160],"296":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA"},"packages/frontend/editor-ui/src/features/workflows/canvas/components/elements/background/CanvasBackground.vue":{"13":"b:x/Vf/PL5nwf+I4hO6O8/8P////8HAAAA"},"packages/frontend/editor-ui/src/features/workflows/canvas/components/elements/buttons/CanvasChatButton.vue":{"9":[19,1,7,2,8,10,113,57,129,6,13]},"packages/frontend/editor-ui/src/features/workflows/canvas/components/elements/buttons/CanvasControlButtons.vue":{"10":"b:x/Vf/PL5nwf+I4hO6O8/8P////8HAAAA","36":"b:x/Vf/PL5nwf+I4hO6O8/8P////8HAAAA","38":"b:x/Vf/PL5nwf+I4hO6O8/8P////8HAAAA","40":"b:x/Vf/PL5nwf+I4hO6O8/8P////8HAAAA","42":[131],"46":[131,136],"50":[131],"54":"b:RwAIAAAAAAJWAAAAAAAwAFwMhgEBAAAA","58":[114],"64":"b:x/Vf/PL5nwf+I4hO6O8/8P////8HAAAA","71":"b:x/Vf/PL5nwf+I4hO6O8/8P////8HAAAA","82":"b:x/Vf/PL5nwf+I4hO6O8/8P////8HAAAA","92":"b:x/Vf/PL5nwf+I4hO6O8/8P////8HAAAA","126":"b:RCAIALABAABQAAAAAAAAAAgBhAAAAAAA","140":"b:x/Vf/LL5nwf+I4hC6O8/8Pz///8HAAAA","149":"b:x/Vf/LL5nwf+I4hC6O8/8Pz///8HAAAA"},"packages/frontend/editor-ui/src/features/workflows/canvas/components/elements/buttons/CanvasRunWorkflowButton.vue":{"40":"b:R/FH/LL5jwX+A4hC4Oc98Nz///sHAAAA","44":"b:R/FH/LL5jwX+A4hC4Oc98Nz///sHAAAA","45":"b:R/FH/LL5jwX+A4hC4Oc98Nz///sHAAAA","49":"b:R/FH/LL5jwX+A4hC4Oc98Nz///sHAAAA","60":[18,50,134,68,139,147],"62":[18,50,134,68,139,147],"63":[18,50,134,68,139,147],"69":[18,50,134,68,139,147],"76":"b:R/FH/LL5jwX+A4hC4Oc98Nz///sHAAAA","97":"b:R/FH/LL5jwX+A4hC4Oc98Nz///sHAAAA","108":"b:QgBC4IKBDwR0AAACgCIgAAwOj+EAAAAA","109":"b:QABC4IKBDwR0AAACgCIgAAwOj+EAAAAA","111":"b:R/FH/LL5jwX+A4hC4Oc98Nz///sHAAAA","118":[18,50,134,68,139,147],"136":[18,50,134,68,139,147]},"packages/frontend/editor-ui/src/features/workflows/canvas/components/elements/buttons/CanvasStopCurrentExecutionButton.vue":{"6":"b:x5BK4JKBDwZ6AIACgGcgAA7+r+EEAAAA","13":"b:x5BK4JKBDwZ6AIACgGcgAA7+r+EEAAAA","18":"b:x5BK4JKBDwZ6AIACgGcgAA7+r+EEAAAA"},"packages/frontend/editor-ui/src/features/workflows/canvas/components/elements/buttons/CanvasStopWaitingForWebhookButton.vue":{"5":"b:xgBIIAAACAIQAAAAgAAAAAIACAAAAAAA","9":"b:xgBIIAAACAIQAAAAgAAAAAIACAAAAAAA"},"packages/frontend/editor-ui/src/features/workflows/canvas/components/elements/edges/CanvasArrowHeadMarker.vue":{"6":"b:x/Vf/PL5nwf+I4hO6O8/8P////8HAAAA"},"packages/frontend/editor-ui/src/features/workflows/canvas/components/elements/edges/CanvasEdge.vue":{"36":"b:x/VP/PLZjwf+A4hOwOM/sN////MFAAAA","44":"b:x/VP/PLZjwf+A4hOwOM/sN////MFAAAA","46":"b:x/VP/PLZjwf+A4hOwOM/sN////MFAAAA","47":"b:x/VP/PLZjwf+A4hOwOM/sN////MFAAAA","53":"b:x/VP+PKRjwf+A4BCwOM+gNz///MEAAAA","61":"b:x/VP/PLZjwf+A4hOwOM/sN////MFAAAA","63":"b:x/VP/PLZjwf+A4hOwOM/sN////MFAAAA","65":"b:x/VP/PLZjwf+A4hOwOM/sN////MFAAAA","67":"b:x/VP/PLZjwf+A4hOwOM/sN////MFAAAA","72":"b:x/VP/PLZjwf+A4hOwOM/sN////MFAAAA","78":"b:x/VP/PLZjwf+A4hOwOM/sN////MFAAAA","83":"b:x/VP/PLZjwf+A4hOwOM/sN////MFAAAA","90":"b:x/VP/PLZjwf+A4hOwOM/sN////MFAAAA","96":"b:x/VP/PLZjwf+A4hOwOM/sN////MFAAAA","98":"b:x/VP/PLZjwf+A4hOwOM/sN////MFAAAA","100":[58,115,65,66,134],"110":"b:x/VP/PLZjwf+A4hOwOM/sN////MFAAAA","120":"b:x/VP/PLZjwf+A4hOwOM/sN////MFAAAA","121":"b:x/VP/PLZjwf+A4hOwOM/sN////MFAAAA","125":"b:x/VP/PLZjwf+A4hOwOM/sN////MFAAAA","136":[58,115,65],"140":[65,66,134],"144":[55,58,115,116,65,66,134,6],"148":[55,58,115,116,65],"150":"b:x/VP/PLZjwf+A4hOwOM/sN////MFAAAA","164":"b:x/VP/PLZjwf+A4hOwOM/sN////MFAAAA"},"packages/frontend/editor-ui/src/features/workflows/canvas/components/elements/edges/utils/getEdgeRenderData.ts":{"11":"b:x/VP/PL5jwf+A4hO4OM/sN////MFAAAA","13":"b:x/VP/PL5jwf+A4hO4OM/sN////MFAAAA"},"packages/frontend/editor-ui/src/features/workflows/canvas/components/elements/handles/CanvasHandleRenderer.vue":{"15":"b:x/VP/PL5jwf+A4hO4Oc/8N////sHAAAA","43":"b:x/VP/PL5jwf+A4hO4Oc/8N////sHAAAA","47":"b:x/VP/PL5jwf+A4hO4Oc/8N////sHAAAA","53":"b:x/VP/PL5jwf+A4hO4Oc/8N////sHAAAA","57":"b:x/VP/PL5jwf+A4hO4Oc/8N////sHAAAA","63":"b:x/VP/PL5jwf+A4hO4Oc/8N////sHAAAA","69":"b:x/VP/PL5jwf+A4hO4Oc/8N////sHAAAA","77":"b:x/VP/PL5jwf+A4hO4Oc/8N////sHAAAA","87":"b:x/VP/PL5jwf+A4hO4Oc/8N////sHAAAA","89":"b:x/VP/PL5jwf+A4hO4Oc/8N////sHAAAA","113":"b:ggQABAAwAAACAAAAIAAEAAQEIAAAAAAA","142":"b:x/VP/PL5jwf+A4hO4Oc/8N////sHAAAA"},"packages/frontend/editor-ui/src/features/workflows/canvas/components/elements/handles/render-types/CanvasHandleMainInput.vue":{"14":"b:x/VP/PLZjwf+A4hOwOM/sN////MFAAAA","22":"b:x/VP/PLZjwf+A4hOwOM/sN////MFAAAA","26":"b:x/VP/PLZjwf+A4hOwOM/sN////MFAAAA"},"packages/frontend/editor-ui/src/features/workflows/canvas/components/elements/handles/render-types/CanvasHandleMainOutput.vue":{"22":"b:x/VP/LL5jwf+A4hC4Oc/8N3///sHAAAA","27":"b:x/VP/PL5jwf+A4hO4Oc/8N////sHAAAA","29":"b:x/VP/PL5jwf+A4hO4Oc/8N////sHAAAA","38":"b:x/VP/LL5jwf+A4hC4Oc/8N3///sHAAAA","40":"b:x5AA4LKBDwV4AIACgGMgAAx+r+EAAAAA","49":"b:x/VP/LL5jwf+A4hC4Oc/8N3///sHAAAA","51":"b:x/VP/LL5jwf+A4hC4Oc/8N3///sHAAAA","53":"b:x/VP/LL5jwf+A4hC4Oc/8N3///sHAAAA","58":"b:QAAEJAAQDQVUAABIACAJAEQpDwAHAAAA","63":"b:x5AA4LKBDwV4AIACgGMgAAx+r+EAAAAA","70":"b:x/VP/PL5jwf+A4hO4Oc/8N////sHAAAA","75":"b:AAAABAAwAAACAAAAIAAEAAwEIAAAAAAA","79":[26,44,45,114,65,130,138,149],"83":[26,101,44,45,114,65,130,138,149],"85":"b:x/VP/PL5jwf+A4hO4Oc/8N////sHAAAA"},"packages/frontend/editor-ui/src/features/workflows/canvas/components/elements/handles/render-types/CanvasHandleNonMainInput.vue":{"19":[0,1,7,2,8,10,12,113,127,65,6,13,14],"21":[0,1,7,2,8,10,12,113,127,65,6,13,14],"27":[0,1,7,2,8,10,12,113,127,65,6,13,14],"33":[1,7,8,10,12,113,127,65,6,13,14],"39":[0,1,7,2,8,10,12,113,127,65,6,13,14],"44":[1,7,10],"48":[1,7,10],"52":[1,7,10],"54":[0,1,7,2,8,10,12,113,127,65,6,13,14]},"packages/frontend/editor-ui/src/features/workflows/canvas/components/elements/handles/render-types/CanvasHandleNonMainOutput.vue":{"13":[0,1,7,2,8,10,12,113,6,13,14],"15":[0,1,7,2,8,10,12,113,6,13,14],"23":[0,1,7,2,8,10,12,113,6,13,14],"27":[0,1,7,2,8,10,12,113,6,13,14]},"packages/frontend/editor-ui/src/features/workflows/canvas/components/elements/handles/render-types/parts/CanvasHandleDiamond.vue":{"13":[0,1,7,2,8,10,12,113,127,65,6,13,14]},"packages/frontend/editor-ui/src/features/workflows/canvas/components/elements/handles/render-types/parts/CanvasHandleDot.vue":{"4":"b:x/VP/PL5jwf+A4hO4Oc/8N////sHAAAA","13":"b:x/VP/PL5jwf+A4hO4Oc/8N////sHAAAA","14":"b:x/VP/PL5jwf+A4hO4Oc/8N////sHAAAA"},"packages/frontend/editor-ui/src/features/workflows/canvas/components/elements/handles/render-types/parts/CanvasHandlePlus.vue":{"6":"b:x/VP/LL5jwf+A4hC4Oc/8N3///sHAAAA","34":"b:x/VP/LL5jwf+A4hC4Oc/8N3///sHAAAA","41":"b:x/VP/LL5jwf+A4hC4Oc/8N3///sHAAAA","57":"b:x/VP/LL5jwf+A4hC4Oc/8N3///sHAAAA","64":"b:x/VP/LL5jwf+A4hC4Oc/8N3///sHAAAA","89":"b:x/VP/LL5jwf+A4hC4Oc/8N3///sHAAAA","102":"b:ggQABAAwAAACAAAAIAAEAAQEIAAAAAAA","104":"b:x/VP/LL5jwf+A4hC4Oc/8N3///sHAAAA"},"packages/frontend/editor-ui/src/features/workflows/canvas/components/elements/nodes/CanvasNode.vue":{"49":"b:x/Vf/PL5nwf+I4hO6O8/8P////8HAAAA","89":"b:x/Vf/PL5nwf+I4hO6O8/8P////8HAAAA","90":"b:x/Vf/PL5nwf+I4hO6O8/8P////8HAAAA","91":"b:x/VP/PL5jwf+A4hO4Oc/8N////sHAAAA","105":"b:x/VP/PL5jwf+A4hO4Oc/8N////sHAAAA","107":"b:x/Vf/PL5nwf+I4hO6O8/8P////8HAAAA","118":"b:x/Vf/PL5nwf+I4hO6O8/8P////8HAAAA","120":"b:x/Vf/PL5nwf+I4hO6O8/8P////8HAAAA","136":"b:xPBCSIAAhQNYAIAKgOAwAKh+j4EAAAAA","146":"b:x/Vf/PL5nwf+I4hO6O8/8P////8HAAAA","150":"b:x/Vf/PL5nwf+I4hO6O8/8P////8HAAAA","154":"b:x/Vf/PL5nwf+I4hO6O8/8P////8HAAAA","161":"b:x/Vf/PL5nwf+I4hO6O8/8P////8HAAAA","165":"b:x/VP/PL5jwf+A4hO4Oc/8N////sHAAAA","172":"b:x/Vf/PL5nwf+I4hO6O8/8P////8HAAAA","249":"b:ggQABAAwAAACAAAAIAAEAAQEIAAAAAAA","253":[112,65,6],"257":[0,2,39,12,65,68,139,140,142],"261":[127,65,6],"265":"b:xPBCSIAAhQNYAIAKgOAwAIB+j4EAAAAA","273":[131],"277":"b:AgAAAAAAAAIEAAAAAAAAAGwICAEAAAAA","326":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","331":"b:x/Vf/PL5nwf+I4hO6O8/8P////8HAAAA","340":"b:x/Vf/PL5nwf+I4hO6O8/8P////8HAAAA","341":"b:xvVL3IJ4jQfeA4BK4Oc/8Pz+//sDAAAA","347":[1],"353":[94,51,130],"359":"b:x/Vf/PL5nwf+I4hO6O8/8P////8HAAAA","364":"b:wuVX1JJ5iQS+AYBG4Cc/8OzzfvsHAAAA","368":"b:x/Vf/PL5nwf+I4hO6O8/8P////8HAAAA","378":"b:x/VP/PL5jwf+A4hO4Oc/8N////sHAAAA","395":"b:x/VP/PLZjwf+A4hOwOM/sN////MFAAAA"},"packages/frontend/editor-ui/src/features/workflows/canvas/components/elements/nodes/CanvasNodeRenderer.vue":{"11":"b:x/Vf/PL5nwf+I4hO6O8/8P////8HAAAA","13":"b:x/Vf/PL5nwf+I4hO6O8/8P////8HAAAA","37":"b:x/Vf/PL5nwf+I4hO6O8/8P////8HAAAA"},"packages/frontend/editor-ui/src/features/workflows/canvas/components/elements/nodes/CanvasNodeToolbar.vue":{"17":"b:x/VP/PL5jwf+A4hO4Oc/8P7///sHAAAA","54":[0,1,7,2,8,10,12,113,6,13,14],"57":[0,1,7,2,8,10,12,113,6,13,14],"59":"b:x/VP/LL5jwf+A4hC4Oc/8Nz///sHAAAA","66":"b:x/VP/PL5jwf+A4hO4Oc/8P7///sHAAAA","73":"b:x/VP/PL5jwf+A4hO4Oc/8P7///sHAAAA","91":"b:x/VP/PL5jwf+A4hO4Oc/8P7///sHAAAA","98":"b:x/VP/PL5jwf+A4hO4Oc/8P7///sHAAAA","100":"b:x/VP/PL5jwf+A4hO4Oc/8P7///sHAAAA","105":"b:x/VP/PL5jwf+A4hO4Oc/8P7///sHAAAA","113":"b:x/VP/PL5jwf+A4hO4Oc/8P7///sHAAAA","117":[0,2,39,12,65,68,139,140,142],"121":[127,65,6],"125":[112,65,6],"135":[131],"139":"b:RxAAAAAAAAAWAAAAAAABgAhYAAEAAAAA","143":"b:RhAAAAAAAAAUAAAAAAAAgAhYAAEAAAAA","156":"b:x/VP/PL5jwf+A4hO4Oc/8P7///sHAAAA","166":"b:RRAAAAAAAAASAAAAAAABgAhYAAAAAAAA","174":"b:x/VP/LL5jwf+A4hC4Oc/8N7///sHAAAA","188":"b:x/VP/LL5jwf+A4hC4Oc/8Nz///sHAAAA","201":"b:x/VP/LL5jwf+A4hC4Oc/8Pz///sHAAAA","236":"b:x/VP/PL5jwf+A4hO4Oc/8P7///sHAAAA"},"packages/frontend/editor-ui/src/features/workflows/canvas/components/elements/nodes/render-types/CanvasNodeDefault.vue":{"23":"b:x/VP/PL5jwf+A4hO4Oc/8N////sHAAAA","55":"b:x/VP/PL5jwf+A4hO4Oc/8N////sHAAAA","56":"b:x/VP/PL5jwf+A4hO4Oc/8N////sHAAAA","57":"b:x/VP/PL5jwf+A4hO4Oc/8N////sHAAAA","60":"b:x/VP/PL5jwf+A4hO4Oc/8N////sHAAAA","65":"b:x/VP/PL5jwf+A4hO4Oc/8N////sHAAAA","70":"b:x/VP/PL5jwf+A4hO4Oc/8N////sHAAAA","79":"b:x/VP/PL5jwf+A4hO4Oc/8N////sHAAAA","82":"b:x/VP/PL5jwf+A4hO4Oc/8N////sHAAAA","107":"b:x/VP/PL5jwf+A4hO4Oc/8N////sHAAAA","113":"b:x/VP/PL5jwf+A4hO4Oc/8N////sHAAAA","126":"b:x/VP/PL5jwf+A4hO4Oc/8N////sHAAAA","133":"b:x/VP/PL5jwf+A4hO4Oc/8N////sHAAAA","146":"b:x/VP/PL5jwf+A4hO4Oc/8N////sHAAAA","155":"b:x/VP/PL5jwf+A4hO4Oc/8N////sHAAAA","180":"b:R3AIKPCBhwP0AwgMgMAAIB8vh+kGAAAA","186":"b:x/VP/PLZjwf+A4hOwOc/sN////sHAAAA","188":"b:x/VP/PLZjwf+A4hOwOc/sN////sHAAAA","193":[1,57,130,131,66,134,139,147,152],"197":"b:xPBCSIAAhQNYAIAKgOAwAIB+j4EAAAAA","204":"b:x/VP/PL5jwf+A4hO4Oc/8N////sHAAAA"},"packages/frontend/editor-ui/src/features/workflows/canvas/components/elements/nodes/render-types/parts/CanvasNodeSettingsIcons.vue":{"12":"b:x/VP/PL5jwf+A4hO4Oc/8N////sHAAAA","13":"b:x/VP/PL5jwf+A4hO4Oc/8N////sHAAAA","28":[145]},"packages/frontend/editor-ui/src/features/workflows/canvas/components/elements/nodes/render-types/parts/CanvasNodeStatusIcons.vue":{"24":"b:x/VP/PL5jwf+A4hO4Oc/8N////sHAAAA","40":"b:x/VP/PL5jwf+A4hO4Oc/8N////sHAAAA","43":"b:x/VP/PL5jwf+A4hO4Oc/8N////sHAAAA","44":"b:x/VP/PL5jwf+A4BO4Oc/8N////sHAAAA","49":"b:x/VP/PL5jwf+A4BO4Oc/8N////sHAAAA","54":"b:x/VP/PL5jwf+A4BO4Oc/8N////sHAAAA","57":"b:wvVHHEJYiABSAohCwKEOsIgiWnIFAAAA","59":"b:x/VP/PL5jwf+A4BO4Oc/8N////sHAAAA","63":"b:x/VP/PLZjwd6AohKwOcusN7+//MFAAAA","82":"b:x/VP/PL5jwf+A4hO4Oc/8N////sHAAAA","106":[15,87,1,7,33,137,68,6,145,157,158],"118":"b:wvVHHEJYiABSAohCwKEOsIgiWjIFAAAA","139":[51,110,58,67,139,147,149,152]},"packages/frontend/editor-ui/src/features/workflows/canvas/components/elements/nodes/render-types/parts/CanvasNodeStatusMark.vue":{"5":"b:x5BK4LKBDwd6AIACgGcgAA7+r+EAAAAA"},"packages/frontend/editor-ui/src/features/workflows/canvas/components/elements/nodes/render-types/parts/CanvasNodeTooltip.vue":{"11":"b:xgBIIAAACAIQAAAAgAAAAAAACAAEAAAA","13":"b:xgBIIAAACAIQAAAAgAAAAAAACAAEAAAA","14":"b:xgBIIAAACAIQAAAAgAAAAAAACAAEAAAA","26":"b:xgBIIAAACAIQAAAAgAAAAAAACAAEAAAA","28":"b:xgBIIAAACAIQAAAAgAAAAAAACAAEAAAA"},"packages/frontend/editor-ui/src/features/workflows/canvas/components/elements/nodes/render-types/parts/CanvasNodeTrigger.vue":{"33":"b:x/VP/PL5jwf+A4hO4Oc/8N////sHAAAA","34":"b:x/VP/PL5jwf+A4hO4Oc/8N////sHAAAA","45":"b:x/VP/LL5jwf+A4hC4Oc/8N3///sHAAAA","58":[19,1,7,2,8,10,113,57,6,13],"64":[19,1,7,2,8,10,113,57,6,13],"67":"b:x/VP/LL5jwf+A4hC4Oc/8N3///sHAAAA","68":"b:x/VP/LL5jwf+A4hC4Oc/8N3///sHAAAA","86":[50,68],"89":"b:x/VP/PL5jwf+A4hO4Oc/8N////sHAAAA","94":[50,68],"116":[19,1,7,2,8,10,113,57,6,13]},"packages/frontend/editor-ui/src/features/workflows/canvas/components/elements/selection/CanvasSelectionToolbar.vue":{"15":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","38":"b:x/Vf/LL5nwf+I4hC6O8/8Pz///8HAAAA","39":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","47":"b:x/Vf/LL5nwf+I4hC6O8/8Pz///8HAAAA","49":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","53":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","57":[66,152],"69":[115,66,152],"77":[66],"84":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","96":[66],"103":[115,66,152],"119":[66,152]},"packages/frontend/editor-ui/src/features/workflows/canvas/composables/useCanvas.ts":{"4":"b:x/Vf/PL5nwf+I4hO6O8/8P////8HAAAA"},"packages/frontend/editor-ui/src/features/workflows/canvas/composables/useCanvasAgentNodeGeometry.ts":{"18":"b:x/Vf/PL5nwf+I4hO6O8/8P////8HAAAA","21":"b:x/Vf/PL5nwf+I4hO6O8/8P////8HAAAA","45":"b:QgAAAJABgACwAAAAAAAAgAgDAOgEAAAA","50":[57,65,130,66,134,72],"55":[57,65,130,66,134,72],"58":[57,65,130,66,134,72]},"packages/frontend/editor-ui/src/features/workflows/canvas/composables/useCanvasGroupIdStorage.ts":{"10":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","11":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","22":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","26":[66]},"packages/frontend/editor-ui/src/features/workflows/canvas/composables/useCanvasLayout.ts":{"65":"b:x/Vf/PL5nwf+I4hO6O8/8P////8HAAAA","78":[18,114,128],"82":[18,114,128],"91":[18,114,128],"101":[18,114,128],"106":[18,114,128],"107":[18,114,128],"108":[18,114,128],"115":[18,114,128],"119":[18,114,128],"123":[18,114,128],"163":[18,114,128],"165":[18,114,128],"168":[18,114,128],"172":[18,114,128],"174":[18,114,128],"181":[18,114,128],"183":[18,114,128],"185":[18,114,128],"190":[18,114,128],"209":[18,114,128],"210":[18,114,128],"216":[18,114,128],"217":[18,114,128],"222":[18,114,128],"231":[18],"233":[18,114,128],"237":[18,114,128],"278":[18,114,128],"279":[18,114,128],"300":[18,114,128],"310":[18,114,128],"319":[18,114,128],"321":[18,114,128],"325":[18,114,128],"368":[18,114,128],"398":[18,114,128],"404":[18,114,128],"405":[18,114,128],"410":[18,114,128],"417":[18,114,128],"421":[18,114,128],"459":[18,114,128],"468":[18,114,128],"477":[18,114,128],"520":[18,114,128],"532":[18,114,128],"583":[18,114,128],"594":[18,114,128],"637":[18,114,128]},"packages/frontend/editor-ui/src/features/workflows/canvas/composables/useCanvasMapping.groups.ts":{"45":[66],"53":[66],"70":[66],"92":[66],"99":[66],"117":[66],"123":[66],"171":[66],"186":[66],"216":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","229":[66],"236":[66],"241":[66],"248":[66],"277":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA"},"packages/frontend/editor-ui/src/features/workflows/canvas/composables/useCanvasMapping.ts":{"48":"b:x/Vf/PL5nwf+I4hO6O8/8P////8HAAAA","66":"b:x/Vf/PL5nwf+I4hO6O8/8P////8HAAAA","72":"b:x/Vf/PL5nwf+I4hO6O8/8P////8HAAAA","82":"b:x/Vf/PL5nwf+I4hO6O8/8P////8HAAAA","111":"b:x/Vf/PL5nwf+I4hO6O8/8P////8HAAAA","113":[66],"119":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","138":"b:x/VP/PLZjwf+A4hOwOM/sN////MFAAAA","147":"b:x/Vf/PL5nwf+I4hO6O8/8P////8HAAAA","154":"b:x/Vf/PL5nwf+I4hO6O8/8P////8HAAAA","206":"b:x/Vf/PL5nwf+I4hO6O8/8P////8HAAAA","209":"b:x/VP/PLZjwf+A4hOwOM/sN////MFAAAA","218":"b:x/VP/PLZjwf+A4hOwOM/sN////MFAAAA","246":"b:x/VP/PLZjwf+A4hOwOM/sN////MFAAAA","249":"b:x/VP/PLZjwf+A4hOwOM/sN////MFAAAA","270":"b:x/VP/PLZjwf+A4hOwOM/sN////MFAAAA","271":"b:x/VP/PLZjwf+A4hOwOM/sN////MFAAAA","283":"b:x/VP/PLZjwf+A4hOwOM/sN////MFAAAA"},"packages/frontend/editor-ui/src/features/workflows/canvas/composables/useCanvasNode.ts":{"8":"b:x/VP/PL5jwf+A4hO4Oc/8P////sHAAAA","10":"b:x/VP/PL5jwf+A4hO4Oc/8P////sHAAAA","33":"b:x/VP/PL5jwf+A4hO4Oc/8P////sHAAAA","34":"b:x/VP/PL5jwf+A4hO4Oc/8N////sHAAAA","36":"b:x/VP/PL5jwf+A4hO4Oc/8N////sHAAAA","37":"b:x/VP/PL5jwf+A4hO4Oc/8N////sHAAAA","38":"b:x/VP/PL5jwf+A4hO4Oc/8N////sHAAAA","40":"b:x/VP/PL5jwf+A4hO4Oc/8N////sHAAAA","41":[109,115,66,133,68,151,160,162],"42":"b:x/VP/PL5jwf+A4hO4Oc/8P////sHAAAA","46":"b:x/VP/PL5jwf+A4hO4Oc/8N////sHAAAA","48":"b:x/VP/PL5jwf+A4hO4Oc/8N////sHAAAA","49":"b:x/VP/PL5jwf+A4hO4Oc/8N////sHAAAA","50":"b:x/VP/PL5jwf+A4hO4Oc/8N////sHAAAA","51":"b:x/VP/PL5jwf+A4hO4Oc/8N////sHAAAA","53":"b:x/VP/PL5jwf+A4hO4Oc/8N////sHAAAA","54":"b:x5BK4LKBDwd6AIACgGcgAA7+r+EAAAAA","55":"b:x/VP/PL5jwf+A4hO4Oc/8N////sHAAAA","57":"b:x/VP/PL5jwf+A4hO4Oc/8P////sHAAAA","59":[109,115,66,133,68,151,160,162],"63":"b:x/VP/PL5jwf+A4hO4Oc/8N////sHAAAA"},"packages/frontend/editor-ui/src/features/workflows/canvas/composables/useCanvasNodeGroupActions.ts":{"20":"b:x/Vf/PL5nwf+I4hO6O8/8P////8HAAAA","29":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","31":"b:x/Vf/LL5nwf+I4hC6O8/8Pz///8HAAAA","33":"b:xvVL3IJ4jQbeA4BC4Oc/8Pz+//MDAAAA","34":"b:xvVL3IJ4jQbeA4BC4Oc/8Pz+//MDAAAA","37":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","70":[66],"82":[66],"86":[66],"112":[66]},"packages/frontend/editor-ui/src/features/workflows/canvas/composables/useCanvasNodeGroupDescriptionVisibility.ts":{"16":[66],"33":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","43":[66],"47":[66],"82":[66],"88":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","94":[90,91,56,66,155,158,159],"110":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA"},"packages/frontend/editor-ui/src/features/workflows/canvas/composables/useCanvasNodeGroupDrag.ts":{"38":"b:x/Vf/PL5nwf+I4hO6O8/8P////8HAAAA","46":[66],"52":[66],"71":[66],"82":[66],"108":[66],"113":[57,65,130,66,134,72],"118":[57,65,130,66,134,72],"130":[57,65,130,66,134,72],"131":[57,65,130,66,134,72],"142":[57,65,130,66,134,72],"190":[66],"198":[57,65,130,66,134,72],"205":[57,65,130,66,134,72],"223":[57,65,130,66,134,72],"235":[57,65,130,66,134,72],"246":[66],"255":[57,65,130,66,134,72]},"packages/frontend/editor-ui/src/features/workflows/canvas/composables/useCanvasNodeGroupLayout.ts":{"68":[66],"80":[66],"109":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","121":[66],"128":[66],"132":[66],"136":[66],"140":[66],"144":[66],"155":[66],"159":[66],"255":[66],"265":[66],"274":[66],"279":[66],"282":[66],"297":[66],"336":[66],"368":[66],"416":[66],"478":[66],"500":[66],"512":[66],"514":[66]},"packages/frontend/editor-ui/src/features/workflows/canvas/composables/useCanvasNodeGroupOperationGuards.ts":{"57":"b://Vf///5/7f/+/////////////8HAAAA","59":"b:wuVH1AJYiQSOAYBCwCM/sMz2fhMBAAAA","69":[66],"89":[66],"98":[66],"106":[66],"128":"b:wuVH1AJYiQSOAYBCwCM/sMz2fhMBAAAA","140":[66],"143":[66],"227":[66],"237":[66],"253":[66],"272":[66],"277":"b:wuVH1AJYiQSOAYBCwCM/sMz2fhMBAAAA","323":[18,65,66,134]},"packages/frontend/editor-ui/src/features/workflows/canvas/composables/useCanvasNodeGroupSelection.ts":{"36":"b:x/Vf/PL5nwf+I4hO6O8/8P////8HAAAA","51":"b:x/Vf/PL5nwf+I4hO6O8/8P////8HAAAA","53":"b:x/Vf/PL5nwf+I4hO6O8/8P////8HAAAA","56":[66],"61":"b:xvVL3IJ4jQfeA4BK4Oc/8Pz+//sDAAAA","68":"b:xvVL3IJ4jQfeA4BK4Oc/8Pz+//sDAAAA","75":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","76":"b:xvVL3IJ4jQfeA4BK4Oc/8Pz+//sDAAAA","77":"b:xvVL3AIQiQfeA4BCwOc/sPz6//MAAAAA","91":[66],"106":[66],"116":[66],"117":[66],"126":[66],"137":"b:x/VL3IJ4jQfeA4BK4Oc/8Pz+//sDAAAA","143":[66],"154":"b:RQAIBAAACAIGAAAAAAAAADwABgEAAAAA","163":"b:x/Vf/PL5nwf+I4hO6O8/8P////8HAAAA","175":"b:xvVL3IJ4jQfeA4BK4Oc/8Pz+//sDAAAA","180":"b:xvVL3IJ4jQfeA4BK4Oc/8Pz+//sDAAAA","195":"b:x/Vf/PL5nwf+I4hO6O8/8P////8HAAAA"},"packages/frontend/editor-ui/src/features/workflows/canvas/composables/useCanvasNodeGroupTelemetry.ts":{"19":"b://Vf///5/7f/+/////////////8HAAAA","24":[66],"37":[66],"40":[66],"43":[66],"46":[66]},"packages/frontend/editor-ui/src/features/workflows/canvas/composables/useCanvasNodeGroupView.ts":{"37":[66],"62":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","66":[57,65,130,66,134,72],"69":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","74":[66],"77":[66],"81":[66],"89":[66],"92":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","94":[66],"99":[66],"106":[66],"107":[66],"109":[66],"115":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","126":[66],"132":[66],"139":[66],"143":[66],"148":[66],"150":[66],"154":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","158":[66],"162":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","167":[66],"177":[66],"196":[66],"208":[66],"223":[57,65,130,66,134,72],"230":[66],"263":[66],"294":[90,91,56,66,155,158,159],"314":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA"},"packages/frontend/editor-ui/src/features/workflows/canvas/composables/useCanvasNodeHandle.ts":{"11":"b:x/VP/PL5jwf+A4hO4Oc/8N////sHAAAA","14":"b:x/VP/PL5jwf+A4hO4Oc/8N////sHAAAA","15":"b:x/VP/PL5jwf+A4hO4Oc/8N////sHAAAA","16":"b:x/VP/LL5jwf+A4hC4Oc/8N3///sHAAAA","17":"b:x/VP/PL5jwf+A4hO4Oc/8N////sHAAAA","18":"b:x/VP/PL5jwf+A4hO4Oc/8N////sHAAAA","19":[0,1,7,2,10,12,6,13],"23":"b:x/VP/PL5jwf+A4hO4Oc/8N////sHAAAA"},"packages/frontend/editor-ui/src/features/workflows/canvas/composables/useCanvasNodeHover.ts":{"10":"b:x/Vf/PL5nwf+I4hO6O8/8P////8HAAAA","17":"b:x/RfTIBwjQdeA4AK4OA9gPx/r/kDAAAA","31":"b:x/RKTIAwjQdeA4AKoOA9gNx/r/kBAAAA","73":"b:x/Vf/PL5nwf+I4hO6O8/8P////8HAAAA","77":"b:QgAAAJABgACwAAAAAAAAgAgDAOgEAAAA"},"packages/frontend/editor-ui/src/features/workflows/canvas/composables/useCanvasTraversal.ts":{"4":"b:x/Vf/PL5nwf+I4hO6O8/8P////8HAAAA","5":[65],"9":[65],"13":[65],"32":[152],"42":[152],"43":[152],"46":[65],"56":[65],"57":[65]},"packages/frontend/editor-ui/src/features/workflows/canvas/composables/useNodeGroupsSubscription.ts":{"18":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","21":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","30":"b:QgAAAJABgACwAAAAAAAAgAgDAOgEAAAA"},"packages/frontend/editor-ui/src/features/workflows/canvas/composables/useViewportAutoAdjust.ts":{"7":"b:x/Vf/PL5nwf+I4hO6O8/8P////8HAAAA","21":"b:x/Vf/PL5nwf+I4hO6O8/8P////8HAAAA","27":"b:x/Vf/PL5nwf+I4hO6O8/8P////8HAAAA","46":"b:QgAAAJABgACwAAAAAAAAgAgDAOgEAAAA","51":"b:x/Vf/PL5nwf+I4hO6O8/8P////8HAAAA"},"packages/frontend/editor-ui/src/features/workflows/canvas/composables/useVueFlowTransformPaneTeleport.ts":{"9":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","14":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA","16":"b:x/Vf/PL5nwf+I4hO6O8/8P7///8HAAAA"},"packages/frontend/editor-ui/src/features/workflows/canvas/composables/useZoomAdjustedValues.ts":{"8":"b:x/VP/PL5jwf+A4hO4Oc/8N////sHAAAA","17":"b:x/VP/PL5jwf+A4hO4Oc/8N////sHAAAA","39":"b:x/VP/PL5jwf+A4hO4Oc/8N////sHAAAA","40":"b:x/VP/PL5jwf+A4hO4Oc/8N////sHAAAA","60":"b:x/VP/PL5jwf+A4hO4Oc/8N////sHAAAA","61":"b:x/VP/PL5jwf+A4hO4Oc/8N////sHAAAA","76":"b:x/VP/PL5jwf+A4hO4Oc/8N////sHAAAA","77":"b:x/VP/PL5jwf+A4hO4Oc/8N////sHAAAA","92":"b:x/VP/PL5jwf+A4hO4Oc/8N////sHAAAA","94":"b:x/VP/PL5jwf+A4hO4Oc/8N////sHAAAA"},"packages/frontend/editor-ui/src/features/workflows/canvas/experimental/composables/useIsInExperimentalNdv.ts":{"4":"b:1vVH3MJYjbfbA4BKzuU+sdz+/5MCAAAA","8":"b:AEAAAAAAAJBAAAAAAAAQABDwOAAAAAAA"},"packages/frontend/editor-ui/src/features/workflows/canvas/experimental/experimentalNdv.store.ts":{"17":"b://Vf///5/7f/+/////////////8HAAAA","19":"b:x/Vf/PL5jwf+I4hO6O8/8P////8HAAAA","24":"b:x/Vf/PL5j6f+I4hO6O8/8f7///8HAAAA","54":"b:x/Vf/PL5nwf+I4hO6O8/8P////8HAAAA","62":[18,116,132],"140":"b:x/Vf/PL5jwf+I4hO6O8/8P////8HAAAA","141":"b:x/Vf/PL5jwf+I4hO6O8/8P7///8HAAAA"},"packages/frontend/editor-ui/src/features/workflows/readyToRun/composables/useEmptyStateDetection.ts":{"13":"b:x/Vf///5/xf/++9D+P+/8P7///8HAAAA","26":"b:AAAACwwAwACAGWAAABAAAAAAAAAGAAAA","45":"b:AAAACwwA8ACAGWAAABgAMAAAAAgGAAAA"},"packages/frontend/editor-ui/src/features/workflows/readyToRun/stores/readyToRun.store.ts":{"22":"b:x/Vf///5/xf/++9D+P+/8P7///8HAAAA","59":"b:x/Vf///5/xf/++9D+P+/8P7///8HAAAA","66":"b:x/Vf///5/xf/++9D+P+/8P7///8HAAAA","74":"b:x/Vf///5/xf/++9D+P+/8P7///8HAAAA","90":"b:x/Vf///5/xf/++9D+P+/8P7///8HAAAA","91":"b:x/Vf///5/xf/++9D+P+/8P7///8HAAAA","110":"b:AAEBGw4A8BCBGWAACBiAMAAAAAgGAAAA","208":"b:AAEBGQ4A8BCBAQAACBiAMAAAAAgGAAAA","220":"b:AAAACwwA8ACAGWAAABgAMAAAAAgGAAAA","224":[83]},"packages/frontend/editor-ui/src/features/workflows/templates/components/SetupWorkflowCredentialsButton.vue":{"28":[52,83],"32":"b:x/Vf/LL5nwf+I4hC6O8/8Pz///8HAAAA","36":"b:x/Vf/LL5nwf+I4hC6O8/8Pz///8HAAAA","43":"b:w/VH/LL5jwX+A4hC4Kc/8Pz7//sHAAAA","48":"b:w/VH/LL5jwX+A4hC4Kc/8Pz7//sHAAAA","51":"b:x/Vf/LL5nwf+I4hC6O8/8Pz///8HAAAA","57":[52,83,73,162],"61":"b:x/Vf/LL5nwf+I4hC6O8/8Pz///8HAAAA","78":[52,83,73,162],"86":"b:gmUBFABYmAAAAgBAQCEKsIgAWhABAAAA","96":[52],"101":[52,83,73],"105":[52,83,73],"113":"b:QgAUAJAAkACwAAAAAAAAgAgDAOgAAAAA","117":"b:x/Vf/LL5nwf+I4hC6O8/8Pz///8HAAAA","133":"b:x/Vf/LL5nwf+I4hC6O8/8Pz///8HAAAA"},"packages/frontend/editor-ui/src/features/workflows/templates/templates.store.ts":{"49":[162],"55":"b://Vf///5/7f/+/////////////8HAAAA","90":[162],"91":[162],"96":[73],"97":[73],"100":[73,162],"101":[73,162],"107":[73],"109":[162],"110":[162],"113":[162],"114":[162],"121":[162],"125":[162],"126":[162],"133":[162],"137":[162],"138":[162],"166":"b:x/Vf///5/xf/++9D+P+/8Pz///8HAAAA","170":"b:x/Vf///5/xf/++9D+P+/8Pz///8HAAAA","183":"b:x/Vf///5/xf/++9D+P+/8Pz///8HAAAA","191":"b:x/Vf///5/xf/++9D+P+/8Pz///8HAAAA","209":"b:x/Vf///5/xf/++9D+P+/8Pz///8HAAAA","214":[14],"221":[162],"225":[73,162],"228":[73,162],"229":[73,162],"240":[73,162],"241":[73,162],"247":[162],"251":[162],"259":[162],"264":[162],"309":"b://Vf///5/7f/++/v//////////8HAAAA","314":[83,73,162],"320":[73,162],"336":[73],"354":[162],"370":[162],"385":[162],"390":[162],"436":[83,73,162],"444":[83,73,162],"452":[83,73,162]},"packages/frontend/editor-ui/src/features/workflows/templates/utils/typeGuards.ts":{"8":[73],"14":[73],"20":"b://Vf///5/7f/+/////////////8HAAAA"},"packages/frontend/editor-ui/src/features/workflows/templates/utils/workflowSamples.ts":{"14":"b://Vf///5/7f/+/////////////8HAAAA","22":"b://Vf///5/7f/+/////////////8HAAAA","26":"b://Vf///5/7f/+/////////////8HAAAA"},"packages/frontend/editor-ui/src/features/workflows/workflowHistory/components/WorkflowHistoryButton.vue":{"12":"b:x/Vf/PL5nwf+I4hD6O8/8Pz///8HAAAA","24":"b:wqAEwAIAmARYAIAAgCIgwMxyDvMAAAAA","31":"b:x/Vf/PL5nwf+I4hD6O8/8Pz///8HAAAA","32":"b:wuBXyAIAmAbeAYACgG8gwMz6LvcGAAAA","42":"b:x/AI6PKBnwf8A4ABgOIg8Jx/h+sGAAAA","49":"b:x/Bf6PKBnwf+A4ADgO8g8Nz/r/8GAAAA","52":"b:x/Vf/PL5nwf+I4hD6O8/8Pz///8HAAAA","55":"b:x/Vf/PL5nwf+I4hD6O8/8Pz///8HAAAA","62":"b:x/Vf/PL5nwf2I4hD6O8/8Pz///8HAAAA"},"packages/frontend/editor-ui/src/features/workflows/workflowHistory/workflowHistory.store.ts":{"22":"b://Vf///5/7f/+/////////////8HAAAA","28":[136],"30":[136],"34":[136],"38":[136],"44":"b:QAAEAAAAAAEwAAAAAAAAAAgDAOAAAAAA"},"packages/vite/source-map-js-shim.ts":{"1":"b://Vf///5/7f/+/////////////8HAAAA"},"packages/@n8n/agents/src/index.ts":{"102":[36,78,80,100,38,81,82],"128":[36,78,80,100,38,81,82],"150":[36,78,80,100,38,81,82],"151":[36,78,80,100,38,81,82],"152":[36,78,80,100,38,81,82],"154":[36,78,80,100,38,81,82],"205":[36,78,80,100,38,81,82],"289":[36,78,80,100,38,81,82],"294":[36,78,80,100,38,81,82],"348":[36,78,80,100,38,81,82],"395":[36,78,80,100,38,81,82],"396":[36,78,80,100,38,81,82],"397":[36,78,80,100,38,81,82],"398":[36,78,80,100,38,81,82],"399":[36,78,80,100,38,81,82],"435":[36,78,80,100,38,81,82]},"packages/@n8n/agents/src/runtime/loop/agent-runtime.ts":{"192":[36,78,80,37,100,38,81,39,82],"212":[36,78,80,37,100,38,81,39,82],"223":[36,78,80,37,100,38,81,39,82],"231":[36,78,80,37,100,38,81,39,82],"237":[36,78,80,37,100,38,81,39,82],"242":[36,78,80,37,100,38,81,39,82],"253":[36,78,80,37,100,38,81,39,82],"267":[36,78,80,37,100,38,81,39,82],"290":[36,78,80,37,100,38,81,39,82],"298":[36,78,80,37,100,38,81,39,82],"313":[36,78,80,37,100,38,81,39,82],"319":[36,78,80,37,100,38,81,39,82],"341":[36,78,80,37,100,38,81,39,82],"361":[36,78,80,37,100,38,81,39,82],"400":[36,78,80,37,100,38,81,39,82],"432":[36,78,80,37,100,38,81,39,82],"458":[36,78,80,37,100,38,81,39,82],"473":[36,78,80,37,100,38,81,39,82],"476":[36,78,80,37,100,38,81,39,82],"490":[36,78,80,37,100,38,81,39,82],"556":[36,78,80,37,100,38,81,39,82],"565":[36,78,80,37,100,38,81,39,82],"579":[36,78,80,37,100,38,81,39,82],"606":[36,78,80,37,100,38,81,39,82],"625":[36,78,80,37,100,38,81,39,82],"631":[36,78,80,37,100,38,81,39,82],"634":[36,78,80,37,100,38,81,39,82],"649":[36,78,80,37,100,38,81,39,82],"662":[36,78,80,37,100,38,81,39,82],"670":[36,78,80,37,100,38,81,39,82],"674":[36,78,80,37,100,38,81,39,82],"675":[36,78,80,37,100,38,81,39,82],"676":[36,78,80,37,100,38,81,39,82],"677":[36,78,80,37,100,38,81,39,82],"678":[36,78,80,37,100,38,81,39,82],"679":[36,78,80,37,100,38,81,39,82],"680":[36,78,80,37,100,38,81,39,82],"681":[36,78,80,37,100,38,81,39,82],"686":[36,78,80,37,100,38,81,39,82],"719":[36,78,80,37,100,38,81,39,82],"745":[36,78,80,37,100,38,81,39,82],"753":[36,78,80,37,100,38,81,39,82],"916":[36,78,80,37,100,38,81,39,82],"923":[36,78,80,37,100,38,81,39,82],"925":[36,78,80,37,100,38,81,39,82],"941":[36,78,80,37,100,38,81,39,82],"946":[36,78,80,37,100,38,81,39,82],"951":[36,78,80,37,100,38,81,39,82],"952":[36,78,80,37,100,38,81,39,82],"953":[36,78,80,37,100,38,81,39,82],"954":[36,78,80,37,100,38,81,39,82],"964":[36,78,80,37,100,38,81,39,82],"1003":[36,78,80,37,100,38,81,39,82],"1028":[36,78,80,37,100,38,81,39,82],"1033":[36,78,80,37,100,38,81,39,82],"1034":[36,78,80,37,100,38,81,39,82],"1041":[36,78,80,37,100,38,81,39,82],"1046":[36,78,80,37,100,38,81,39,82],"1051":[36,78,80,37,100,38,81,39,82],"1062":[36,78,80,37,100,38,81,39,82],"1077":[36,78,80,37,100,38,81,39,82]},"packages/@n8n/agents/src/runtime/loop/execution-counter.ts":{"14":[36,78,80,100,38,81,39,82],"22":[36,78,80,100,38,81,39,82],"24":[36,78,80,100,38,81,39,82],"27":[36,78,80,100,38,81,39,82],"29":[36,78,80,100,38,81,39,82],"32":[36,78,80,100,38,81,39,82]},"packages/@n8n/agents/src/runtime/loop/hydrate-file-parts.ts":{"24":[36,78,80,100,38,81,82]},"packages/@n8n/agents/src/runtime/loop/runtime-context.ts":{"29":[36,78,80,100,38,81,82],"31":[36,78,80,100,38,81,82],"35":[36,78,80,100,38,81,82],"64":[36,78,80,100,38,81,82],"69":[36,78,80,100,38,81,82],"74":[36,78,80,100,38,81,82],"108":[36,78,80,100,38,81,82],"138":[36,78,80,100,38,81,82],"143":[36,78,80,100,38,81,82],"162":[36,78,80,100,38,81,82],"180":[36,78,80,100,38,81,82],"195":[36,78,80,100,38,81,82],"211":[36,78,80,100,38,81,82],"233":[36,78,80,100,38,81,82],"238":[36,78,80,100,38,81,82],"269":[36,78,80,100,38,81,82],"281":[36,78,80,100,38,81,82]},"packages/@n8n/agents/src/runtime/loop/runtime-helpers.ts":{"14":[36,78,80,100,38,81,82],"22":[36,78,80,100,38,81,82],"33":[36,78,80,100,38,81,82],"37":[36,78,80,100,38,81,82],"61":[36,78,80,100,38,81,82],"84":[36,78,80,100,38,81,82],"86":[36,78,80,100,38,81,82],"87":[36,78,80,100,38,81,82],"95":[36,78,80,100,38,81,82],"98":[36,78,80,100,38,81,82],"99":[36,78,80,100,38,81,82],"100":[36,78,80,100,38,81,82],"105":[36,78,80,100,38,81,82]},"packages/@n8n/agents/src/runtime/loop/stream-sink.ts":{"42":[36,78,80,37,100,38,81,39,82],"48":[36,78,80,37,100,38,81,39,82],"60":[36,78,80,37,100,38,81,39,82],"75":[36,78,80,37,100,38,81,39,82],"89":[36,78,80,37,100,38,81,39,82],"94":[36,78,80,37,100,38,81,39,82],"102":[36,78,80,37,100,38,81,39,82],"204":[36,78,80,37,100,38,81,39,82],"229":[36,78,80,37,100,38,81,39,82],"256":[36,78,80,37,100,38,81,39,82]},"packages/@n8n/agents/src/runtime/mcp/mcp-tool-resolver.ts":{"27":[36,78,80,100,38,81,82],"28":[36,78,80,100,38,81,82],"31":[36,78,80,100,38,81,82],"35":[36,78,80,100,38,81,82],"46":[36,78,80,100,38,81,82],"65":[36,78,80,100,38,81,82],"66":[36,78,80,100,38,81,82],"85":[36,78,80,100,38,81,82],"89":[36,78,80,100,38,81,82],"90":[36,78,80,100,38,81,82],"93":[36,78,80,100,38,81,82],"110":[36,78,80,100,38,81,82],"115":[36,78,80,100,38,81,82],"131":[36,78,80,100,38,81,82],"134":[36,78,80,100,38,81,82],"149":[36,78,80,100,38,81,82]},"packages/@n8n/agents/src/runtime/memory/episodic-memory.ts":{"79":[36,78,80,100,38,81,82],"85":[36,78,80,100,38,81,82],"100":[36,78,80,100,38,81,82],"119":[36,78,80,100,38,81,82],"133":[36,78,80,100,38,81,82],"157":[36,78,80,100,38,81,82],"177":[36,78,80,100,38,81,82],"192":[36,78,80,100,38,81,82],"206":[36,78,80,100,38,81,82],"207":[36,78,80,100,38,81,82],"215":[36,78,80,100,38,81,82],"222":[36,78,80,100,38,81,82],"225":[36,78,80,100,38,81,82],"226":[36,78,80,100,38,81,82],"229":[36,78,80,100,38,81,82],"236":[36,78,80,100,38,81,82],"239":[36,78,80,100,38,81,82],"240":[36,78,80,100,38,81,82],"243":[36,78,80,100,38,81,82],"244":[36,78,80,100,38,81,82],"270":[36,78,80,100,38,81,82],"271":[36,78,80,100,38,81,82],"279":[36,78,80,100,38,81,82],"286":[36,78,80,100,38,81,82],"290":[36,78,80,100,38,81,82],"294":[36,78,80,100,38,81,82],"301":[36,78,80,100,38,81,82],"307":[36,78,80,100,38,81,82],"329":[36,78,80,100,38,81,82],"340":[36,78,80,100,38,81,82],"357":[36,78,80,100,38,81,82],"366":[36,78,80,100,38,81,82],"376":[36,78,80,100,38,81,82],"382":[36,78,80,100,38,81,82],"390":[36,78,80,100,38,81,82],"403":[36,78,80,100,38,81,82],"418":[36,78,80,100,38,81,82],"425":[36,78,80,100,38,81,82],"426":[36,78,80,100,38,81,82],"431":[36,78,80,100,38,81,82],"439":[36,78,80,100,38,81,82],"444":[36,78,80,100,38,81,82],"453":[36,78,80,100,38,81,82],"460":[36,78,80,100,38,81,82],"482":[36,78,80,100,38,81,82],"486":[36,78,80,100,38,81,82],"490":[36,78,80,100,38,81,82],"503":[36,78,80,100,38,81,82],"505":[36,78,80,100,38,81,82],"516":[36,78,80,100,38,81,82],"526":[36,78,80,100,38,81,82],"529":[36,78,80,100,38,81,82],"533":[36,78,80,100,38,81,82],"537":[36,78,80,100,38,81,82],"541":[36,78,80,100,38,81,82],"542":[36,78,80,100,38,81,82],"545":[36,78,80,100,38,81,82],"548":[36,78,80,100,38,81,82],"552":[36,78,80,100,38,81,82],"566":[36,78,80,100,38,81,82],"570":[36,78,80,100,38,81,82],"580":[36,78,80,100,38,81,82],"589":[36,78,80,100,38,81,82],"603":[36,78,80,100,38,81,82]},"packages/@n8n/agents/src/runtime/memory/memory-orchestrator.ts":{"38":[36,78,80,37,100,38,81,39,82],"45":[36,78,80,37,100,38,81,39,82],"68":[36,78,80,37,100,38,81,39,82],"75":[36,78,80,37,100,38,81,39,82],"105":[36,78,80,37,100,38,81,39,82],"122":[36,78,80,37,100,38,81,39,82],"137":[36,78,80,37,100,38,81,39,82],"169":[36,78,80,37,100,38,81,39,82],"212":[36,78,80,37,100,38,81,39,82],"244":[36,78,80,37,100,38,81,39,82],"270":[36,78,80,37,100,38,81,39,82],"294":[36,78,80,37,100,38,81,39,82],"316":[36,78,80,37,100,38,81,39,82],"332":[36,78,80,37,100,38,81,39,82],"352":[36,78,80,37,100,38,81,39,82],"365":[36,78,80,37,100,38,81,39,82],"373":[36,78,80,37,100,38,81,39,82],"374":[36,78,80,37,100,38,81,39,82],"375":[36,78,80,37,100,38,81,39,82],"384":[36,78,80,37,100,38,81,39,82],"412":[36,78,80,37,100,38,81,39,82],"424":[36,78,80,37,100,38,81,39,82],"433":[36,78,80,37,100,38,81,39,82],"438":[36,78,80,37,100,38,81,39,82],"457":[36,78,80,37,100,38,81,39,82]},"packages/@n8n/agents/src/runtime/memory/memory-store.ts":{"53":[36,78,80,100,38,81,82],"61":[36,78,80,100,38,81,82],"65":[36,78,80,100,38,81,82],"81":[36,78,80,100,38,81,82],"107":[36,78,80,100,38,81,82],"109":[36,78,80,100,38,81,82],"111":[36,78,80,100,38,81,82],"112":[36,78,80,100,38,81,82],"114":[36,78,80,100,38,81,82],"115":[36,78,80,100,38,81,82],"117":[36,78,80,100,38,81,82],"119":[36,78,80,100,38,81,82],"124":[36,78,80,100,38,81,82],"129":[36,78,80,100,38,81,82],"144":[36,78,80,100,38,81,82],"152":[36,78,80,100,38,81,82],"153":[36,78,80,100,38,81,82],"156":[36,78,80,100,38,81,82],"160":[36,78,80,100,38,81,82],"175":[36,78,80,100,38,81,82],"185":[36,78,80,100,38,81,82],"189":[36,78,80,100,38,81,82],"191":[36,78,80,100,38,81,82],"198":[36,78,80,100,38,81,82],"208":[36,78,80,100,38,81,82],"214":[36,78,80,100,38,81,82],"234":[36,78,80,100,38,81,82],"239":[36,78,80,100,38,81,82],"244":[36,78,80,100,38,81,82],"251":[36,78,80,100,38,81,82],"275":[36,78,80,100,38,81,82],"282":[36,78,80,100,38,81,82],"284":[36,78,80,100,38,81,82],"289":[36,78,80,100,38,81,82],"292":[36,78,80,100,38,81,82],"301":[36,78,80,100,38,81,82],"314":[36,78,80,100,38,81,82],"326":[36,78,80,100,38,81,82],"335":[36,78,80,100,38,81,82],"355":[36,78,80,100,38,81,82],"361":[36,78,80,100,38,81,82],"366":[36,78,80,100,38,81,82],"375":[36,78,80,100,38,81,82],"382":[36,78,80,100,38,81,82],"386":[36,78,80,100,38,81,82],"392":[36,78,80,100,38,81,82],"396":[36,78,80,100,38,81,82],"407":[36,78,80,100,38,81,82],"428":[36,78,80,100,38,81,82],"433":[36,78,80,100,38,81,82],"444":[36,78,80,100,38,81,82],"452":[36,78,80,100,38,81,82],"483":[36,78,80,100,38,81,82],"489":[36,78,80,100,38,81,82],"512":[36,78,80,100,38,81,82],"522":[36,78,80,100,38,81,82],"533":[36,78,80,100,38,81,82],"539":[36,78,80,100,38,81,82],"545":[36,78,80,100,38,81,82],"555":[36,78,80,100,38,81,82],"560":[36,78,80,100,38,81,82],"561":[36,78,80,100,38,81,82],"565":[36,78,80,100,38,81,82],"571":[36,78,80,100,38,81,82],"572":[36,78,80,100,38,81,82],"578":[36,78,80,100,38,81,82],"594":[36,78,80,100,38,81,82],"595":[36,78,80,100,38,81,82],"604":[36,78,80,100,38,81,82],"615":[36,78,80,100,38,81,82],"623":[36,78,80,100,38,81,82],"633":[36,78,80,100,38,81,82],"652":[36,78,80,100,38,81,82],"665":[36,78,80,100,38,81,82],"675":[36,78,80,100,38,81,82],"685":[36,78,80,100,38,81,82],"689":[36,78,80,100,38,81,82]},"packages/@n8n/agents/src/runtime/memory/observation-log-defaults.ts":{"309":[36,78,80,100,38,81,82],"324":[36,78,80,100,38,81,82],"328":[36,78,80,100,38,81,82],"566":[36,78,80,100,38,81,82],"578":[36,78,80,100,38,81,82],"582":[36,78,80,100,38,81,82]},"packages/@n8n/agents/src/runtime/memory/observation-log-reflector.ts":{"64":[36,78,80,81],"79":[36,78,80,81],"80":[36,78,80,81],"81":[36,78,80,81],"106":[36,78,80,81],"110":[36,78,80,81],"111":[36,78,80,81],"120":[36,78,80,81],"122":[36,78,80,81],"126":[36,78,80,81],"127":[36,78,80,81],"130":[36,78,80,81],"131":[36,78,80,81],"132":[36,78,80,81],"133":[36,78,80,81],"143":[36,78,80,81],"159":[36,78,80,81],"166":[36,78,80,81],"198":[36,78,80,81],"226":[36,78,80,81],"228":[36,78,80,81],"238":[36,78,80,81],"251":[36,78,80,81],"265":[36,78,80,81],"274":[36,78,80,81],"283":[36,78,80,81],"295":[36,78,80,81],"300":[36,78,80,81],"317":[36,78,80,81],"336":[36,78,80,81],"342":[36,78,80,81],"345":[36,78,80,81],"352":[36,78,80,81],"356":[36,78,80,81],"359":[36,78,80,81],"364":[36,78,80,81],"370":[36,78,80,81]},"packages/@n8n/agents/src/runtime/memory/observation-log-renderer.ts":{"25":[36,78,80,100,38,81,82],"31":[36,78,80,100,38,81,82],"37":[36,78,80,100,38,81,82],"42":[36,78,80,100,38,81,82],"46":[36,78,80,100,38,81,82],"50":[36,78,80,100,38,81,82]},"packages/@n8n/agents/src/runtime/memory/observation-log-store.ts":{"21":[36,78,80,100,38,81,82],"28":[36,78,80,100,38,81,82],"31":[36,78,80,100,38,81,82],"34":[36,78,80,100,38,81,82],"37":[36,78,80,100,38,81,82]},"packages/@n8n/agents/src/runtime/memory/strip-orphaned-tool-messages.ts":{"13":[36,78,80,100,38,81,82],"22":[36,78,80,100,38,81,82]},"packages/@n8n/agents/src/runtime/memory/title-generation.ts":{"65":[36,78,80,81],"72":[36,78,80,81],"108":[36,78,80,81],"149":[36,78,80,81],"212":[36,78,80,81],"229":[36,78,80,81],"233":[36,78,80,81],"234":[36,78,80,81]},"packages/@n8n/agents/src/runtime/model/lazy-ai.ts":{"5":[36,78,80,100,38,81,82]},"packages/@n8n/agents/src/runtime/model/message-list.ts":{"28":[36,78,80,37,100,38,81,39,82],"42":[36,78,80,37,100,38,81,39,82],"82":[36,78,80,37,100,38,81,39,82],"108":[36,78,80,37,100,38,81,39,82],"131":[36,78,80,37,100,38,81,39,82],"137":[36,78,80,37,100,38,81,39,82],"138":[36,78,80,37,100,38,81,39,82],"171":[36,78,80,37,100,38,81,39,82],"177":[36,78,80,37,100,38,81,39,82],"185":[36,78,80,37,100,38,81,39,82],"193":[36,78,80,37,100,38,81,39,82],"208":[36,78,80,37,100,38,81,39,82],"242":[36,78,80,37,100,38,81,39,82],"258":[36,78,80,37,100,38,81,39,82],"265":[36,78,80,37,100,38,81,39,82],"273":[36,78,80,37,100,38,81,39,82],"276":[36,78,80,37,100,38,81,39,82],"287":[36,78,80,37,100,38,81,39,82],"308":[36,78,80,37,100,38,81,39,82],"309":[36,78,80,37,100,38,81,39,82],"316":[36,78,80,37,100,38,81,39,82],"317":[36,78,80,37,100,38,81,39,82],"324":[36,78,80,37,100,38,81,39,82],"325":[36,78,80,37,100,38,81,39,82],"329":[36,78,80,37,100,38,81,39,82],"333":[36,78,80,37,100,38,81,39,82],"334":[36,78,80,37,100,38,81,39,82],"343":[36,78,80,37,100,38,81,39,82]},"packages/@n8n/agents/src/runtime/model/messages.ts":{"33":[36,78,80,100,38,81,39,82],"37":[36,78,80,100,38,81,39,82],"41":[36,78,80,100,38,81,39,82],"45":[36,78,80,100,38,81,39,82],"49":[36,78,80,100,38,81,39,82],"53":[36,78,80,100,38,81,39,82],"61":[36,78,80,100,38,81,39,82],"67":[36,78,80,100,38,81,39,82],"96":[36,78,80,100,38,81,39,82],"100":[36,78,80,100,38,81,39,82],"104":[36,78,80,100,38,81,39,82],"108":[36,78,80,100,38,81,39,82],"112":[36,78,80,100,38,81,39,82],"117":[36,78,80,100,38,81,39,82],"119":[36,78,80,100,38,81,39,82],"126":[36,78,80,100,38,81,39,82],"130":[36,78,80,100,38,81,39,82],"145":[36,78,80,100,38,81,39,82],"167":[36,78,80,100,38,81,39,82],"178":[36,78,80,100,38,81,39,82],"184":[36,78,80,100,38,81,39,82],"193":[36,78,80,100,38,81,39,82],"251":[36,78,80,100,38,81,39,82],"283":[36,78,80,100,38,81,39,82],"362":[36,78,80,100,38,81,39,82],"367":[36,78,80,100,38,81,39,82],"376":[36,78,80,100,38,81,39,82],"450":[36,78,80,100,38,81,39,82],"465":[36,78,80,100,38,81,39,82],"509":[36,78,80,100,38,81,39,82],"530":[36,78,80,100,38,81,39,82]},"packages/@n8n/agents/src/runtime/model/model-factory.ts":{"27":[36,78,80,100,38,81,82],"39":[36,78,80,100,38,81,82],"46":[36,78,80,100,38,81,82],"75":[36,78,80,100,38,81,82],"86":[36,78,80,100,38,81,82],"99":[36,78,80,100,38,81,82],"117":[36,78,80,100,38,81,82],"123":[36,78,80,100,38,81,82],"129":[36,78,80,100,38,81,82],"135":[36,78,80,100,38,81,82],"141":[36,78,80,100,38,81,82],"147":[36,78,80,100,38,81,82],"153":[36,78,80,100,38,81,82],"159":[36,78,80,100,38,81,82],"166":[36,78,80,100,38,81,82],"179":[36,78,80,100,38,81,82],"197":[36,78,80,100,38,81,82],"217":[36,78,80,100,38,81,82],"261":[36,78,80,100,38,81,82],"300":[36,78,80,100,38,81,82]},"packages/@n8n/agents/src/runtime/model/prompt-cache.ts":{"13":[36,78,80,100,38,81,82],"16":[36,78,80,100,38,81,82],"29":[36,78,80,100,38,81,82],"39":[36,78,80,100,38,81,82],"49":[36,78,80,100,38,81,82],"58":[36,78,80,100,38,81,82],"70":[36,78,80,100,38,81,82],"84":[36,78,80,100,38,81,82],"91":[36,78,80,100,38,81,82],"103":[36,78,80,100,38,81,82],"118":[36,78,80,100,38,81,82],"143":[36,78,80,100,38,81,82],"155":[36,78,80,100,38,81,82],"195":[36,78,80,100,38,81,82]},"packages/@n8n/agents/src/runtime/model/provider-quirks.ts":{"41":[36,78,80,100,38,81,82],"69":[36,78,80,100,38,81,82],"89":[36,78,80,100,38,81,82],"102":[36,78,80,100,38,81,82],"109":[36,78,80,100,38,81,82],"113":[36,78,80,100,38,81,82],"118":[36,78,80,100,38,81,82]},"packages/@n8n/agents/src/runtime/model/raw-error.ts":{"36":[36,78,80,100,38,81,82],"44":[36,78,80,100,38,81,82],"55":[36,78,80,100,38,81,82],"63":[36,78,80,100,38,81,82]},"packages/@n8n/agents/src/runtime/model/raw-usage.ts":{"32":[36,78,80,100,38,81,82],"43":[36,78,80,100,38,81,82],"46":[36,78,80,100,38,81,82],"76":[36,78,80,100,38,81,82],"84":[36,78,80,100,38,81,82]},"packages/@n8n/agents/src/runtime/state/background-task-tracker.ts":{"1":[36,78,80,100,38,81,82],"4":[36,78,80,100,38,81,82],"8":[36,78,80,100,38,81,82],"10":[36,78,80,100,38,81,82],"16":[36,78,80,100,38,81,82]},"packages/@n8n/agents/src/runtime/state/event-bus.ts":{"1":[36,78,80,100,38,81,39,82],"18":[36,78,80,100,38,81,39,82],"29":[36,78,80,100,38,81,39,82],"31":[36,78,80,100,38,81,39,82],"34":[36,78,80,100,38,81,39,82],"38":[36,78,80,100,38,81,39,82],"42":[36,78,80,100,38,81,39,82],"46":[36,78,80,100,38,81,39,82],"70":[36,78,80,100,38,81,39,82],"79":[36,78,80,100,38,81,39,82],"88":[36,78,80,100,38,81,39,82],"92":[36,78,80,100,38,81,39,82],"100":[36,78,80,100,38,81,39,82],"107":[36,78,80,100,38,81,39,82],"108":[36,78,80,100,38,81,39,82],"124":[36,78,80,100,38,81,39,82],"133":[36,78,80,100,38,81,39,82],"135":[36,78,80,100,38,81,39,82],"141":[36,78,80,100,38,81,39,82],"145":[36,78,80,100,38,81,39,82],"153":[36,78,80,100,38,81,39,82]},"packages/@n8n/agents/src/runtime/state/run-state.ts":{"11":[36,78,80,37,100,38,81,39,82],"14":[36,78,80,37,100,38,81,39,82],"18":[36,78,80,37,100,38,81,39,82],"22":[36,78,80,37,100,38,81,39,82],"29":[36,78,80,37,100,38,81,39,82],"42":[36,78,80,37,100,38,81,39,82],"47":[36,78,80,37,100,38,81,39,82],"57":[36,78,80,37,100,38,81,39,82],"67":[36,78,80,37,100,38,81,39,82],"75":[36,78,80,37,100,38,81,39,82],"84":[36,78,80,37,100,38,81,39,82],"98":[36,78,80,37,100,38,81,39,82],"108":[36,78,80,37,100,38,81,39,82]},"packages/@n8n/agents/src/runtime/streaming/stream-session.ts":{"45":[36,78,80,100,38,81,39,82],"52":[36,78,80,100,38,81,39,82],"61":[36,78,80,100,38,81,39,82],"71":[36,78,80,100,38,81,39,82],"76":[36,78,80,100,38,81,39,82],"81":[36,78,80,100,38,81,39,82],"94":[36,78,80,100,38,81,39,82],"95":[36,78,80,100,38,81,39,82],"117":[36,78,80,100,38,81,39,82]},"packages/@n8n/agents/src/runtime/streaming/stream-writer-guard.ts":{"15":[36,78,80,100,38,81,39,82],"17":[36,78,80,100,38,81,39,82],"20":[36,78,80,100,38,81,39,82],"23":[36,78,80,100,38,81,39,82],"28":[36,78,80,100,38,81,39,82],"38":[36,78,80,100,38,81,39,82],"55":[36,78,80,100,38,81,39,82]},"packages/@n8n/agents/src/runtime/streaming/stream.ts":{"12":[36,78,80,100,38,81,82],"21":[36,78,80,100,38,81,82],"74":[36,78,80,100,38,81,82]},"packages/@n8n/agents/src/runtime/telemetry/runtime-telemetry.ts":{"32":[36,78,80,100,38,81,39,82],"40":[36,78,80,100,38,81,39,82],"48":[36,78,80,100,38,81,39,82],"56":[36,78,80,100,38,81,39,82],"67":[36,78,80,100,38,81,39,82],"79":[36,78,80,100,38,81,39,82],"90":[36,78,80,100,38,81,39,82],"97":[36,78,80,100,38,81,39,82],"101":[36,78,80,100,38,81,39,82],"102":[36,78,80,100,38,81,39,82],"112":[36,78,80,100,38,81,39,82],"136":[36,78,80,100,38,81,39,82],"144":[36,78,80,100,38,81,39,82],"155":[36,78,80,100,38,81,39,82],"160":[36,78,80,100,38,81,39,82],"168":[36,78,80,100,38,81,39,82],"194":[36,78,80,100,38,81,39,82],"208":[36,78,80,100,38,81,39,82],"239":[36,78,80,100,38,81,39,82],"262":[36,78,80,100,38,81,39,82],"299":[36,78,80,100,38,81,39,82],"304":[36,78,80,100,38,81,39,82],"308":[36,78,80,100,38,81,39,82]},"packages/@n8n/agents/src/runtime/telemetry/telemetry-options.ts":{"11":[36,78,80,100,38,81,82]},"packages/@n8n/agents/src/runtime/tools/deferred-tool-manager.ts":{"42":[36,78,80,100,38,81,82],"47":[36,78,80,100,38,81,82],"52":[36,78,80,100,38,81,82],"90":[36,78,80,100,38,81,82],"106":[36,78,80,100,38,81,82],"110":[36,78,80,100,38,81,82],"114":[36,78,80,100,38,81,82],"118":[36,78,80,100,38,81,82],"123":[36,78,80,100,38,81,82],"125":[36,78,80,100,38,81,82],"126":[36,78,80,100,38,81,82],"129":[36,78,80,100,38,81,82],"144":[36,78,80,100,38,81,82],"151":[36,78,80,100,38,81,82],"158":[36,78,80,100,38,81,82],"165":[36,78,80,100,38,81,82],"172":[36,78,80,100,38,81,82],"174":[36,78,80,100,38,81,82],"178":[36,78,80,100,38,81,82],"183":[36,78,80,100,38,81,82],"187":[36,78,80,100,38,81,82],"191":[36,78,80,100,38,81,82],"220":[36,78,80,100,38,81,82],"228":[36,78,80,100,38,81,82],"239":[36,78,80,100,38,81,82]},"packages/@n8n/agents/src/runtime/tools/delegate-sub-agent-tool.ts":{"204":[36,78,80,100,38,81,39,82],"208":[36,78,80,100,38,81,39,82],"213":[36,78,80,100,38,81,39,82],"283":[36,78,80,100,38,81,39,82],"308":[36,78,80,100,38,81,39,82],"323":[36,78,80,100,38,81,39,82],"330":[36,78,80,100,38,81,39,82],"368":[36,78,80,100,38,81,39,82],"392":[36,78,80,100,38,81,39,82],"395":[36,78,80,100,38,81,39,82],"440":[36,78,80,100,38,81,39,82],"449":[36,78,80,100,38,81,39,82],"453":[36,78,80,100,38,81,39,82],"460":[36,78,80,100,38,81,39,82],"467":[36,78,80,100,38,81,39,82],"493":[36,78,80,100,38,81,39,82],"538":[36,78,80,100,38,81,39,82],"566":[36,78,80,100,38,81,39,82],"578":[36,78,80,100,38,81,39,82],"601":[36,78,80,100,38,81,39,82],"613":[36,78,80,100,38,81,39,82],"617":[36,78,80,100,38,81,39,82],"640":[36,78,80,100,38,81,39,82],"675":[36,78,80,100,38,81,39,82],"690":[36,78,80,100,38,81,39,82],"704":[36,78,80,100,38,81,39,82],"737":[36,78,80,100,38,81,39,82],"744":[36,78,80,100,38,81,39,82],"745":[36,78,80,100,38,81,39,82]},"packages/@n8n/agents/src/runtime/tools/tool-adapter.ts":{"34":[36,78,80,37,100,38,81,39,82],"47":[36,78,80,37,100,38,81,39,82],"69":[36,78,80,37,100,38,81,39,82],"104":[36,78,80,37,100,38,81,39,82],"119":[36,78,80,37,100,38,81,39,82],"153":[36,78,80,37,100,38,81,39,82],"159":[36,78,80,37,100,38,81,39,82]},"packages/@n8n/agents/src/runtime/tools/tool-call-executor.ts":{"145":[36,78,80,37,100,38,81,39,82],"149":[36,78,80,37,100,38,81,39,82],"156":[36,78,80,37,100,38,81,39,82],"178":[36,78,80,37,100,38,81,39,82],"180":[36,78,80,37,100,38,81,39,82],"184":[36,78,80,37,100,38,81,39,82],"188":[36,78,80,37,100,38,81,39,82],"192":[36,78,80,37,100,38,81,39,82],"197":[36,78,80,37,100,38,81,39,82],"204":[36,78,80,37,100,38,81,39,82],"251":[36,78,80,37,100,38,81,39,82],"291":[36,78,80,37,100,38,81,39,82],"296":[36,78,80,37,100,38,81,39,82],"297":[36,78,80,37,100,38,81,39,82],"331":[36,78,80,37,100,38,81,39,82],"464":[36,78,80,37,100,38,81,39,82],"637":[36,78,80,37,100,38,81,39,82],"692":[36,78,80,37,100,38,81,39,82],"705":[36,78,80,37,100,38,81,39,82],"711":[36,78,80,37,100,38,81,39,82],"713":[36,78,80,37,100,38,81,39,82],"719":[36,78,80,37,100,38,81,39,82],"736":[36,78,80,37,100,38,81,39,82],"760":[36,78,80,37,100,38,81,39,82],"776":[36,78,80,37,100,38,81,39,82],"797":[36,78,80,37,100,38,81,39,82],"799":[36,78,80,37,100,38,81,39,82],"803":[36,78,80,37,100,38,81,39,82],"814":[36,78,80,37,100,38,81,39,82],"837":[36,78,80,37,100,38,81,39,82]},"packages/@n8n/agents/src/sdk/abort.ts":{"6":[36,78,80,100,38,81,39,82],"12":[36,78,80,100,38,81,39,82],"20":[36,78,80,100,38,81,39,82],"35":[36,78,80,100,38,81,39,82],"39":[36,78,80,100,38,81,39,82],"46":[36,78,80,100,38,81,39,82],"47":[36,78,80,100,38,81,39,82]},"packages/@n8n/agents/src/sdk/agent.ts":{"224":[36,78,80,37,100,38,81,39,82],"228":[36,78,80,37,100,38,81,39,82],"232":[36,78,80,37,100,38,81,39,82],"248":[36,78,80,37,100,38,81,39,82],"261":[36,78,80,37,100,38,81,39,82],"267":[36,78,80,37,100,38,81,39,82],"274":[36,78,80,37,100,38,81,39,82],"276":[36,78,80,37,100,38,81,39,82],"285":[36,78,80,37,100,38,81,39,82],"293":[36,78,80,37,100,38,81,39,82],"311":[36,78,80,37,100,38,81,39,82],"320":[36,78,80,37,100,38,81,39,82],"331":[36,78,80,37,100,38,81,39,82],"337":[36,78,80,37,100,38,81,39,82],"342":[36,78,80,37,100,38,81,39,82],"368":[36,78,80,37,100,38,81,39,82],"374":[36,78,80,37,100,38,81,39,82],"380":[36,78,80,37,100,38,81,39,82],"387":[36,78,80,37,100,38,81,39,82],"393":[36,78,80,37,100,38,81,39,82],"399":[36,78,80,37,100,38,81,39,82],"421":[36,78,80,37,100,38,81,39,82],"448":[36,78,80,37,100,38,81,39,82],"470":[36,78,80,37,100,38,81,39,82],"485":[36,78,80,37,100,38,81,39,82],"505":[36,78,80,37,100,38,81,39,82],"511":[36,78,80,37,100,38,81,39,82],"524":[36,78,80,37,100,38,81,39,82],"535":[36,78,80,37,100,38,81,39,82],"547":[36,78,80,37,100,38,81,39,82],"570":[36,78,80,37,100,38,81,39,82],"585":[36,78,80,37,100,38,81,39,82],"600":[36,78,80,37,100,38,81,39,82],"602":[36,78,80,37,100,38,81,39,82],"625":[36,78,80,37,100,38,81,39,82],"631":[36,78,80,37,100,38,81,39,82],"639":[36,78,80,37,100,38,81,39,82],"656":[36,78,80,37,100,38,81,39,82],"672":[36,78,80,37,100,38,81,39,82],"699":[36,78,80,37,100,38,81,39,82],"714":[36,78,80,37,100,38,81,39,82],"731":[36,78,80,37,100,38,81,39,82],"737":[36,78,80,37,100,38,81,39,82],"742":[36,78,80,37,100,38,81,39,82],"757":[36,78,80,37,100,38,81,39,82],"784":[36,78,80,37,100,38,81,39,82],"814":[36,78,80,37,100,38,81,39,82],"830":[36,78,80,37,100,38,81,39,82],"842":[36,78,80,37,100,38,81,39,82],"852":[36,78,80,37,100,38,81,39,82],"869":[36,78,80,37,100,38,81,39,82],"873":[36,78,80,37,100,38,81,39,82],"880":[36,78,80,37,100,38,81,39,82],"893":[36,78,80,37,100,38,81,39,82],"899":[36,78,80,37,100,38,81,39,82],"900":[36,78,80,37,100,38,81,39,82],"913":[36,78,80,37,100,38,81,39,82],"927":[36,78,80,37,100,38,81,39,82],"937":[36,78,80,37,100,38,81,39,82],"943":[36,78,80,37,100,38,81,39,82],"949":[36,78,80,37,100,38,81,39,82],"971":[36,78,80,37,100,38,81,39,82],"972":[36,78,80,37,100,38,81,39,82],"973":[36,78,80,37,100,38,81,39,82],"983":[36,78,80,37,100,38,81,39,82],"995":[36,78,80,37,100,38,81,39,82],"1020":[36,78,80,37,100,38,81,39,82],"1021":[36,78,80,37,100,38,81,39,82],"1033":[36,78,80,37,100,38,81,39,82],"1119":[36,78,80,37,100,38,81,39,82],"1129":[36,78,80,37,100,38,81,39,82],"1143":[36,78,80,37,100,38,81,39,82],"1145":[36,78,80,37,100,38,81,39,82],"1171":[36,78,80,37,100,38,81,39,82],"1185":[36,78,80,37,100,38,81,39,82],"1302":[36,78,80,37,100,38,81,39,82],"1311":[36,78,80,37,100,38,81,39,82],"1316":[36,78,80,37,100,38,81,39,82],"1322":[36,78,80,37,100,38,81,39,82],"1333":[36,78,80,37,100,38,81,39,82],"1336":[36,78,80,37,100,38,81,39,82],"1341":[36,78,80,37,100,38,81,39,82],"1356":[36,78,80,37,100,38,81,39,82],"1374":[36,78,80,37,100,38,81,39,82],"1381":[36,78,80,37,100,38,81,39,82],"1390":[36,78,80,37,100,38,81,39,82],"1394":[36,78,80,37,100,38,81,39,82],"1399":[36,78,80,37,100,38,81,39,82],"1402":[36,78,80,37,100,38,81,39,82]},"packages/@n8n/agents/src/sdk/catalog.ts":{"82":[36,78,80,100,38,81,82],"96":[36,78,80,100,38,81,82],"128":[36,78,80,100,38,81,82],"200":[36,78,80,100,38,81,82],"204":[36,78,80,100,38,81,82],"208":[36,78,80,100,38,81,82],"226":[36,78,80,100,38,81,82],"243":[36,78,80,100,38,81,82],"263":[36,78,80,100,38,81,82]},"packages/@n8n/agents/src/sdk/guardrails/patterns.ts":{"31":[36,78,80,100,38,81,82],"39":[36,78,80,100,38,81,82],"45":[36,78,80,100,38,81,82],"72":[36,78,80,100,38,81,82],"76":[36,78,80,100,38,81,82],"92":[36,78,80,100,38,81,82],"108":[36,78,80,100,38,81,82],"128":[36,78,80,100,38,81,82],"139":[36,78,80,100,38,81,82],"146":[36,78,80,100,38,81,82],"149":[36,78,80,100,38,81,82],"184":[36,78,80,100,38,81,82],"233":[36,78,80,100,38,81,82],"236":[36,78,80,100,38,81,82]},"packages/@n8n/agents/src/sdk/guardrails/redactor.ts":{"32":[36,78,80,100,38,81,82],"44":[36,78,80,100,38,81,82],"56":[36,78,80,100,38,81,82],"57":[36,78,80,100,38,81,82],"67":[36,78,80,100,38,81,82],"87":[36,78,80,100,38,81,82],"98":[36,78,80,100,38,81,82],"104":[36,78,80,100,38,81,82],"109":[36,78,80,100,38,81,82],"122":[36,78,80,100,38,81,82],"163":[36,78,80,100,38,81,82],"177":[36,78,80,100,38,81,82]},"packages/@n8n/agents/src/sdk/guardrails/streaming-redactor.ts":{"38":[36,78,80,100,38,81,82],"46":[36,78,80,100,38,81,82],"79":[36,78,80,100,38,81,82]},"packages/@n8n/agents/src/sdk/memory.ts":{"14":[36,78,80,100,38,81,82],"38":[36,78,80,100,38,81,82],"58":[36,78,80,100,38,81,82],"87":[36,78,80,100,38,81,82],"126":[36,78,80,100,38,81,82],"181":[36,78,80,100,38,81,82],"191":[36,78,80,100,38,81,82],"209":[36,78,80,100,38,81,82],"220":[36,78,80,100,38,81,82],"228":[36,78,80,100,38,81,82]},"packages/@n8n/agents/src/sdk/message.ts":{"3":[36,78,80,100,38,81,82],"19":[36,78,80,100,38,81,82],"23":[36,78,80,100,38,81,82]},"packages/@n8n/agents/src/sdk/tool.ts":{"31":[36,78,80,100,38,81,82],"45":[36,78,80,100,38,81,82],"61":[36,78,80,100,38,81,82],"73":[36,78,80,100,38,81,82],"168":[36,78,80,100,38,81,82],"173":[36,78,80,100,38,81,82],"187":[36,78,80,100,38,81,82],"193":[36,78,80,100,38,81,82],"200":[36,78,80,100,38,81,82],"207":[36,78,80,100,38,81,82],"214":[36,78,80,100,38,81,82],"224":[36,78,80,100,38,81,82],"234":[36,78,80,100,38,81,82],"246":[36,78,80,100,38,81,82],"255":[36,78,80,100,38,81,82],"261":[36,78,80,100,38,81,82],"267":[36,78,80,100,38,81,82],"278":[36,78,80,100,38,81,82],"289":[36,78,80,100,38,81,82],"354":[36,78,80,100,38,81,82],"390":[36,78,80,100,38,81,82]},"packages/@n8n/agents/src/skills/prompt.ts":{"7":[36,78,80,100,38,81,82],"14":[36,78,80,100,38,81,82],"44":[36,78,80,100,38,81,82],"56":[36,78,80,100,38,81,82],"60":[36,78,80,100,38,81,82]},"packages/@n8n/agents/src/skills/registry.ts":{"24":[36,78,80,100,38,81,82],"34":[36,78,80,100,38,81,82],"36":[36,78,80,100,38,81,82],"40":[36,78,80,100,38,81,82],"47":[36,78,80,100,38,81,82],"64":[36,78,80,100,38,81,82],"69":[36,78,80,100,38,81,82],"79":[36,78,80,100,38,81,82],"82":[36,78,80,100,38,81,82],"89":[36,78,80,100,38,81,82],"95":[36,78,80,100,38,81,82],"98":[36,78,80,100,38,81,82],"102":[36,78,80,100,38,81,82],"123":[36,78,80,100,38,81,82],"126":[36,78,80,100,38,81,82],"154":[36,78,80,100,38,81,82],"158":[36,78,80,100,38,81,82],"167":[36,78,80,100,38,81,82],"173":[36,78,80,100,38,81,82],"207":[36,78,80,100,38,81,82],"233":[36,78,80,100,38,81,82],"254":[36,78,80,100,38,81,82],"283":[36,78,80,100,38,81,82],"289":[36,78,80,100,38,81,82],"307":[36,78,80,100,38,81,82],"324":[36,78,80,100,38,81,82],"335":[36,78,80,100,38,81,82],"346":[36,78,80,100,38,81,82],"351":[36,78,80,100,38,81,82],"354":[36,78,80,100,38,81,82],"357":[36,78,80,100,38,81,82],"366":[36,78,80,100,38,81,82],"372":[36,78,80,100,38,81,82],"389":[36,78,80,100,38,81,82],"407":[36,78,80,100,38,81,82],"411":[36,78,80,100,38,81,82],"416":[36,78,80,100,38,81,82],"420":[36,78,80,100,38,81,82],"424":[36,78,80,100,38,81,82],"428":[36,78,80,100,38,81,82],"438":[36,78,80,100,38,81,82],"445":[36,78,80,100,38,81,82],"452":[36,78,80,100,38,81,82],"459":[36,78,80,100,38,81,82],"464":[36,78,80,100,38,81,82],"465":[36,78,80,100,38,81,82],"466":[36,78,80,100,38,81,82],"472":[36,78,80,100,38,81,82],"478":[36,78,80,100,38,81,82],"482":[36,78,80,100,38,81,82]},"packages/@n8n/agents/src/skills/tools.ts":{"49":[36,78,80,100,38,81,39,40,82],"63":[36,78,80,100,38,81,39,40,82],"100":[36,78,80,100,38,81,39,40,82],"104":[36,78,80,100,38,81,39,40,82],"112":[36,78,80,100,38,81,39,40,82],"124":[36,78,80,100,38,81,39,40,82],"130":[36,78,80,100,38,81,39,40,82],"147":[36,78,80,100,38,81,39,40,82],"216":[36,78,80,100,38,81,39,40,82],"221":[36,78,80,100,38,81,39,40,82],"222":[36,78,80,100,38,81,39,40,82],"238":[36,78,80,100,38,81,39,40,82],"243":[36,78,80,100,38,81,39,40,82],"248":[36,78,80,100,38,81,39,40,82],"254":[36,78,80,100,38,81,39,40,82],"266":[36,78,80,100,38,81,39,40,82],"271":[36,78,80,100,38,81,39,40,82],"277":[36,78,80,100,38,81,39,40,82],"288":[36,78,80,100,38,81,39,40,82],"292":[36,78,80,100,38,81,39,40,82],"299":[36,78,80,100,38,81,39,40,82]},"packages/@n8n/agents/src/skills/validator.ts":{"43":[36,78,80,100,38,81,82],"56":[36,78,80,100,38,81,82],"85":[36,78,80,100,38,81,82],"121":[36,78,80,100,38,81,82],"141":[36,78,80,100,38,81,82],"189":[36,78,80,100,38,81,82],"206":[36,78,80,100,38,81,82],"215":[36,78,80,100,38,81,82],"235":[36,78,80,100,38,81,82],"256":[36,78,80,100,38,81,82],"257":[36,78,80,100,38,81,82],"270":[36,78,80,100,38,81,82],"289":[36,78,80,100,38,81,82],"308":[36,78,80,100,38,81,82],"359":[36,78,80,100,38,81,82],"383":[36,78,80,100,38,81,82],"404":[36,78,80,100,38,81,82],"420":[36,78,80,100,38,81,82],"462":[36,78,80,100,38,81,82],"482":[36,78,80,100,38,81,82],"486":[36,78,80,100,38,81,82]},"packages/@n8n/agents/src/utils/zod.ts":{"6":[36,78,80,100,38,81,82],"14":[36,78,80,100,38,81,82]},"packages/@n8n/agents/src/workspace/filesystem/base-filesystem.ts":{"39":[36,78,80,100,38,81,40,82],"44":[36,78,80,100,38,81,40,82],"64":[36,78,80,100,38,81,40,82],"83":[36,78,80,100,38,81,40,82],"87":[36,78,80,100,38,81,40,82],"96":[36,78,80,100,38,81,40,82],"114":[36,78,80,100,38,81,40,82],"139":[36,78,80,100,38,81,40,82]},"packages/@n8n/agents/src/workspace/filesystem/n8n-sandbox-filesystem.ts":{"22":[36,78,80,100,38,81,39,40,82],"37":[36,78,80,100,38,81,39,40,82],"42":[36,78,80,100,38,81,39,40,82],"50":[36,78,80,100,38,81,39,40,82],"58":[36,78,80,100,38,81,39,40,82],"64":[36,78,80,100,38,81,39,40,82],"65":[36,78,80,100,38,81,39,40,82],"74":[36,78,80,100,38,81,39,40,82],"75":[36,78,80,100,38,81,39,40,82],"86":[36,78,80,100,38,81,39,40,82],"87":[36,78,80,100,38,81,39,40,82],"92":[36,78,80,100,38,81,39,40,82],"93":[36,78,80,100,38,81,39,40,82],"101":[36,78,80,100,38,81,39,40,82],"102":[36,78,80,100,38,81,39,40,82],"112":[36,78,80,100,38,81,39,40,82],"113":[36,78,80,100,38,81,39,40,82],"122":[36,78,80,100,38,81,39,40,82],"123":[36,78,80,100,38,81,39,40,82],"128":[36,78,80,100,38,81,39,40,82],"132":[36,78,80,100,38,81,39,40,82],"133":[36,78,80,100,38,81,39,40,82],"135":[36,78,80,100,38,81,39,40,82],"143":[36,78,80,100,38,81,39,40,82],"155":[36,78,80,100,38,81,39,40,82],"156":[36,78,80,100,38,81,39,40,82]},"packages/@n8n/agents/src/workspace/index.ts":{"1":[36,78,80,100,38,81,82],"7":[36,78,80,100,38,81,82],"8":[36,78,80,100,38,81,82]},"packages/@n8n/agents/src/workspace/lifecycle.ts":{"14":[36,78,80,100,38,81,82]},"packages/@n8n/agents/src/workspace/sandbox/base-sandbox.ts":{"17":[36,78,80,100,38,81,40,82],"37":[36,78,80,100,38,81,40,82],"50":[36,78,80,100,38,81,40,82],"70":[36,78,80,100,38,81,40,82],"74":[36,78,80,100,38,81,40,82],"86":[36,78,80,100,38,81,40,82],"94":[36,78,80,100,38,81,40,82],"95":[36,78,80,100,38,81,40,82],"118":[36,78,80,100,38,81,40,82],"123":[36,78,80,100,38,81,40,82],"128":[36,78,80,100,38,81,40,82],"132":[36,78,80,100,38,81,40,82],"135":[36,78,80,100,38,81,40,82],"142":[36,78,80,100,38,81,40,82],"154":[36,78,80,100,38,81,40,82],"163":[36,78,80,100,38,81,40,82],"167":[36,78,80,100,38,81,40,82],"183":[36,78,80,100,38,81,40,82],"199":[36,78,80,100,38,81,40,82]},"packages/@n8n/agents/src/workspace/sandbox/create-workspace.ts":{"12":[36,78,80,100,38,81,82],"19":[36,78,80,100,38,81,82],"73":[36,78,80,100,38,81,82]},"packages/@n8n/agents/src/workspace/sandbox/index.ts":{"21":[36,78,80,100,38,81,39,40,82]},"packages/@n8n/agents/src/workspace/sandbox/n8n-sandbox-sandbox.ts":{"16":[36,78,80,100,38,81,40,82],"20":[36,78,80,100,38,81,40,82],"22":[36,78,80,100,38,81,40,82],"47":[36,78,80,100,38,81,40,82],"57":[36,78,80,100,38,81,40,82],"61":[36,78,80,100,38,81,40,82],"75":[36,78,80,100,38,81,40,82],"85":[36,78,80,100,38,81,40,82],"90":[36,78,80,100,38,81,40,82],"107":[36,78,80,100,38,81,40,82],"115":[36,78,80,100,38,81,40,82],"144":[36,78,80,100,38,81,40,82],"149":[36,78,80,100,38,81,40,82],"159":[36,78,80,100,38,81,40,82],"165":[36,78,80,100,38,81,40,82],"170":[36,78,80,100,38,81,40,82]},"packages/@n8n/agents/src/workspace/sandbox/run-in-sandbox.ts":{"37":[36,78,80,100,38,81,82],"60":[36,78,80,100,38,81,82]},"packages/@n8n/agents/src/workspace/sandbox/workspace-root.ts":{"10":[36,78,80,100,38,81,82],"29":[36,78,80,100,38,81,82],"40":[36,78,80,100,38,81,82],"47":[36,78,80,100,38,81,82],"59":[36,78,80,100,38,81,82]},"packages/@n8n/agents/src/workspace/tools/append-file.ts":{"7":[36,78,80,100,38,81,82],"21":[36,78,80,100,38,81,82]},"packages/@n8n/agents/src/workspace/tools/batch-str-replace-file.ts":{"40":[36,78,80,100,38,81,82],"47":[36,78,80,100,38,81,82],"53":[36,78,80,100,38,81,82],"60":[36,78,80,100,38,81,82]},"packages/@n8n/agents/src/workspace/tools/copy-file.ts":{"7":[36,78,80,100,38,81,82],"25":[36,78,80,100,38,81,82]},"packages/@n8n/agents/src/workspace/tools/delete-file.ts":{"7":[36,78,80,100,38,81,82],"25":[36,78,80,100,38,81,82]},"packages/@n8n/agents/src/workspace/tools/execute-command.ts":{"7":[36,78,80,100,38,81,82],"26":[36,78,80,100,38,81,82]},"packages/@n8n/agents/src/workspace/tools/file-stat.ts":{"7":[36,78,80,100,38,81,82],"25":[36,78,80,100,38,81,82]},"packages/@n8n/agents/src/workspace/tools/list-files.ts":{"7":[36,78,80,100,38,81,82],"29":[36,78,80,100,38,81,82]},"packages/@n8n/agents/src/workspace/tools/mkdir.ts":{"7":[36,78,80,100,38,81,82],"21":[36,78,80,100,38,81,82]},"packages/@n8n/agents/src/workspace/tools/move-file.ts":{"7":[36,78,80,100,38,81,82],"25":[36,78,80,100,38,81,82]},"packages/@n8n/agents/src/workspace/tools/read-file.ts":{"7":[36,78,80,100,38,81,82],"21":[36,78,80,100,38,81,82]},"packages/@n8n/agents/src/workspace/tools/rmdir.ts":{"7":[36,78,80,100,38,81,82],"25":[36,78,80,100,38,81,82]},"packages/@n8n/agents/src/workspace/tools/str-replace-file.ts":{"23":[36,78,80,100,38,81,82],"30":[36,78,80,100,38,81,82],"37":[36,78,80,100,38,81,82]},"packages/@n8n/agents/src/workspace/tools/workspace-tools.ts":{"23":[36,78,80,100,38,81,82]},"packages/@n8n/agents/src/workspace/tools/write-file.ts":{"7":[36,78,80,100,38,81,82],"25":[36,78,80,100,38,81,82]},"packages/@n8n/agents/src/workspace/workspace.ts":{"24":[36,78,80,100,38,81,40,82],"31":[36,78,80,100,38,81,40,82],"35":[36,78,80,100,38,81,40,82],"39":[36,78,80,100,38,81,40,82],"43":[36,78,80,100,38,81,40,82],"59":[36,78,80,100,38,81,40,82],"80":[36,78,80,100,38,81,40,82],"93":[36,78,80,100,38,81,40,82],"97":[36,78,80,100,38,81,40,82],"126":[36,78,80,100,38,81,40,82]},"packages/@n8n/ai-utilities/src/node-catalog/index.ts":{"14":[36,78,80,100,38,81,40,82,172,42],"33":[36,78,80,100,38,81,40,82,172,42]},"packages/@n8n/ai-utilities/src/node-catalog/suggested.ts":{"25":[36,78,80,100,38,81,40,82,172,42],"34":[36,78,80,100,38,81,40,82,172,42],"59":[36,78,80,100,38,81,40,82,172,42],"93":[36,78,80,100,38,81,40,82,172,42]},"packages/@n8n/api-types/src/schemas/agent-run-reducer.ts":{"32":[36,78,79,80,38,81,39,82],"69":[36,78,79,80,37,100,38,81,39,40,82],"82":[36,78,79,80,37,100,38,81,39,40,82],"99":[36,78,79,80,37,100,38,81,39,40,82],"108":[36,78,79,80,37,38,81,39,40,82],"116":[36,78,79,80,37,38,81,39,40,82],"121":[36,78,79,80,37,38,81,39,40,82],"139":[36,78,79,80,37,38,81,39,40,82],"157":[36,78,79,80,37,100,38,81,39,40,82],"158":[36,78,79,80,38,81,39,82],"163":[36,78,79,80,38,81,39,82],"178":[36,78,79,80,37,100,38,81,39,40,82],"202":[36,78,79,80,37,100,38,81,39,40,82],"579":[36,78,79,80,37,100,38,81,39,40,82],"590":[36,78,79,80,37,100,38,81,39,40,82],"605":[36,78,79,80,37,100,38,81,39,40,82],"619":[36,78,79,80,37,100,38,81,39,40,82],"630":[36,78,79,80,38,81,39,82],"633":[36,78,79,80,38,81,39,82],"636":[36,78,79,80,38,81,39,82]},"packages/@n8n/api-types/src/schemas/instance-ai.schema.ts":{"35":[36,78,79,80,37,100,38,81,39,82],"45":[36,78,79,80,37,100,38,81,39,82],"66":[36,78,79,80,37,100,38,81,39,82],"189":[36,78,79,80,37,100,38,81,39,40,82],"537":[36,78,79,80,37,100,38,81,39,82],"541":[36,78,79,80,37,100,38,81,39,82],"545":[36,78,79,80,37,100,38,81,39,82],"549":[36,78,79,80,37,100,38,81,39,82],"552":[36,78,79,80,37,100,38,81,39,82],"560":[36,78,79,80,37,100,38,81,39,82],"597":[36,78,79,80,37,100,38,81,39,82],"598":[36,78,79,80,37,100,38,81,39,82],"1003":[36,78,79,80,37,100,38,81,39,82],"1397":[36,78,79,80,37,100,38,81,39,82],"1416":[36,78,79,80,37,100,38,81,39,82],"1540":[36,78,79,80,37,100,38,81,39,40,82]},"packages/@n8n/api-types/src/schemas/timezone.schema.ts":{"3":[3,4,5,36,78,80,100,38,81,82]},"packages/@n8n/backend-network/src/http/env-proxy-http-agent.ts":{"23":"b:AYROzFLokiEoZw8MEAAAAAAIAADgAQgA","28":"b:AYROzFLokiEoZw8MEAAAAAAIAADgAQgA","32":"b:AYROzFLokiEoZw8MEAAAAAAIAADgAQgA"},"packages/@n8n/backend-network/src/http/undici/transport.ts":{"86":[20,25,36,78,80,100,38,81,82,179],"101":[20,25,36,78,80,100,38,81,82,179],"123":[20,25,36,78,80,100,38,81,82,179],"131":[20,25,36,78,80,100,38,81,82,179],"148":[20,25,36,78,80,100,38,81,82,179],"156":[20,25,36,78,80,100,38,81,82,179],"159":[20,25,36,78,80,100,38,81,82,179],"161":[20,25,36,78,80,100,38,81,82,179],"165":[20,25,36,78,80,100,38,81,82,179],"167":[20,25,36,78,80,100,38,81,82,179],"180":[20,25,36,78,80,100,38,81,82,179],"181":[20,25,36,78,80,100,38,81,82,179],"189":[20,25,36,78,80,100,38,81,82,179],"196":[20,25,36,78,80,100,38,81,82,179],"216":[20,25,36,78,80,100,38,81,82,179],"219":[20,25,36,78,80,100,38,81,82,179],"224":[20,25,36,78,80,100,38,81,82,179],"225":[20,25,36,78,80,100,38,81,82,179],"248":[20,25,36,78,80,100,38,81,82,179],"259":[20,25,36,78,80,100,38,81,82,179],"268":[20,25,36,78,80,100,38,81,82,179]},"packages/@n8n/db/src/repositories/base-repository.ts":{"22":[9,36,78,79,80,38,81,82,172]},"packages/@n8n/db/src/repositories/instance-credential-assignment.repository.ts":{"10":[36,78,79,80,100,38,81,82],"14":[36,78,79,80,100,38,81,82],"28":[36,78,79,80,100,38,81,82],"53":[36,78,79,80,100,38,81,82],"54":[36,78,79,80,100,38,81,82],"63":[36,78,79,80,100,38,81,82],"68":[36,78,79,80,100,38,81,82],"77":[36,78,79,80,100,38,81,82],"83":[36,78,79,80,100,38,81,82],"86":[36,78,79,80,100,38,81,82],"101":[36,78,79,80,100,38,81,82]},"packages/@n8n/db/src/repositories/settings.repository.ts":{"11":[36,78,79,80,37,38,81,82],"15":[36,78,79,80,37,38,81,82],"20":[36,78,79,80,37,38,81,82],"24":[36,78,79,80,37,38,81,82],"33":[36,78,79,80,37,38,81,82],"37":[36,78,79,80,37,38,81,82]},"packages/@n8n/db/src/services/db-lock.service.ts":{"15":[36,78,79,80,38,81,82],"52":[36,78,79,80,38,81,82],"105":[36,78,79,80,38,81,82],"128":[36,78,79,80,38,81,82],"137":[36,78,79,80,38,81,82],"144":[36,78,79,80,38,81,82],"178":[36,78,79,80,38,81,82],"183":[36,78,79,80,38,81,82],"196":[36,78,79,80,38,81,82],"204":[36,78,79,80,38,81,82],"210":[36,78,79,80,38,81,82],"231":[36,78,79,80,38,81,82],"252":[36,78,79,80,38,81,82],"255":[36,78,79,80,38,81,82],"263":[36,78,79,80,38,81,82],"286":[36,78,79,80,38,81,82],"308":[36,78,79,80,38,81,82],"309":[36,78,79,80,38,81,82],"328":[36,78,79,80,38,81,82]},"packages/@n8n/db/src/services/typeorm-transaction.ts":{"16":[9,36,78,79,80,38,81,82,172],"21":[9,36,78,79,80,38,81,82,172],"29":[9,36,78,79,80,38,81,82,172],"36":[9,36,78,79,80,38,81,82,172],"59":[9,36,78,79,80,38,81,82,172]},"packages/@n8n/instance-ai/src/agent/apply-agent-thinking.ts":{"5":[36,78,80,100,38,81,82],"9":[36,78,80,100,38,81,82],"14":[36,78,80,100,38,81,82],"19":[36,78,80,100,38,81,82],"24":[36,78,80,100,38,81,82],"36":[36,78,80,100,38,81,82]},"packages/@n8n/instance-ai/src/agent/computer-use-prompt.ts":{"6":[36,78,80,100,38,81,82]},"packages/@n8n/instance-ai/src/agent/instance-agent.ts":{"30":[36,78,80,100,38,81,82],"51":[36,78,80,100,38,81,82],"83":[36,78,80,100,38,81,82],"92":[36,78,80,100,38,81,82],"95":[36,78,80,100,38,81,82],"158":[36,78,80,100,38,81,82]},"packages/@n8n/instance-ai/src/agent/mcp-tool-name-validation.ts":{"8":[36,78,80,100,38,81,82],"20":[36,78,80,100,38,81,82],"29":[36,78,80,100,38,81,82],"36":[36,78,80,100,38,81,82],"47":[36,78,80,100,38,81,82],"55":[36,78,80,100,38,81,82]},"packages/@n8n/instance-ai/src/agent/runtime-workspace.ts":{"10":[36,78,80,100,38,81,82]},"packages/@n8n/instance-ai/src/agent/sanitize-mcp-schemas.ts":{"33":[36,78,80,100,38,81,82],"95":[36,78,80,100,38,81,82],"115":[36,78,80,100,38,81,82],"191":[36,78,80,100,38,81,82],"213":[36,78,80,100,38,81,82],"231":[36,78,80,100,38,81,82],"249":[36,78,80,100,38,81,82],"266":[36,78,80,100,38,81,82],"283":[36,78,80,100,38,81,82],"302":[36,78,80,100,38,81,82],"338":[36,78,80,100,38,81,82],"339":[36,78,80,100,38,81,82],"342":[36,78,80,100,38,81,82],"345":[36,78,80,100,38,81,82],"352":[36,78,80,100,38,81,82],"366":[36,78,80,100,38,81,82],"379":[36,78,80,100,38,81,82],"380":[36,78,80,100,38,81,82],"385":[36,78,80,100,38,81,82],"394":[36,78,80,100,38,81,82],"402":[36,78,80,100,38,81,82],"416":[36,78,80,100,38,81,82],"450":[36,78,80,100,38,81,82],"451":[36,78,80,100,38,81,82],"473":[36,78,80,100,38,81,82],"488":[36,78,80,100,38,81,82],"490":[36,78,80,100,38,81,82],"503":[36,78,80,100,38,81,82],"524":[36,78,80,100,38,81,82],"676":[36,78,80,100,38,81,82],"697":[36,78,80,100,38,81,82],"713":[36,78,80,100,38,81,82]},"packages/@n8n/instance-ai/src/agent/shared-prompts.ts":{"22":[36,78,80,100,38,81,82]},"packages/@n8n/instance-ai/src/agent/sub-agent-briefing.ts":{"41":[36,78,80,100,38,81,82],"71":[36,78,80,100,38,81,82]},"packages/@n8n/instance-ai/src/agent/system-prompt.ts":{"28":[36,78,80,100,38,81,82],"41":[36,78,80,100,38,81,82],"48":[36,78,80,100,38,81,82],"70":[36,78,80,100,38,81,82],"84":[36,78,80,100,38,81,82],"92":[36,78,80,100,38,81,82],"96":[36,78,80,100,38,81,82],"118":[36,78,80,100,38,81,82]},"packages/@n8n/instance-ai/src/domain-access/domain-access-tracker.ts":{"60":[36,78,80,100,38,81,82],"74":[36,78,80,100,38,81,82],"85":[36,78,80,100,38,81,82],"90":[36,78,80,100,38,81,82],"95":[36,78,80,100,38,81,82],"99":[36,78,80,100,38,81,82],"108":[36,78,80,100,38,81,82],"113":[36,78,80,100,38,81,82],"119":[36,78,80,100,38,81,82],"124":[36,78,80,100,38,81,82]},"packages/@n8n/instance-ai/src/domain-access/domain-gating.ts":{"69":[36,78,80,100,38,81,82],"122":[36,78,80,100,38,81,82],"161":[36,78,80,100,38,81,82],"203":[36,78,80,100,38,81,82]},"packages/@n8n/instance-ai/src/domain-access/index.ts":{"1":[36,78,80,100,38,81,82]},"packages/@n8n/instance-ai/src/error-formatting.ts":{"20":[36,78,80,100,38,81,82],"31":[36,78,80,100,38,81,82],"46":[36,78,80,100,38,81,82],"51":[36,78,80,100,38,81,82],"64":[36,78,80,100,38,81,82],"66":[36,78,80,100,38,81,82],"69":[36,78,80,100,38,81,82],"76":[36,78,80,100,38,81,82],"90":[36,78,80,100,38,81,82],"101":[36,78,80,100,38,81,82],"110":[36,78,80,100,38,81,82],"117":[36,78,80,100,38,81,82],"121":[36,78,80,100,38,81,82],"128":[36,78,80,100,38,81,82],"132":[36,78,80,100,38,81,82],"157":[36,78,80,100,38,81,82],"171":[36,78,80,100,38,81,82]},"packages/@n8n/instance-ai/src/index.ts":{"47":[36,78,79,80,37,100,38,81,39,40,82],"49":[36,78,79,80,37,100,38,81,39,40,82],"52":[36,78,79,80,37,100,38,81,39,40,82],"53":[36,78,79,80,37,100,38,81,39,40,82],"58":[36,78,79,80,37,100,38,81,39,40,82],"60":[36,78,79,80,37,100,38,81,39,40,82],"67":[36,78,79,80,37,100,38,81,39,40,82],"77":[36,78,79,80,37,100,38,81,39,40,82],"86":[36,78,79,80,37,100,38,81,39,40,82],"89":[36,78,79,80,37,100,38,81,39,40,82],"92":[36,78,79,80,37,100,38,81,39,40,82],"95":[36,78,79,80,37,100,38,81,39,40,82],"97":[36,78,79,80,37,100,38,81,39,40,82],"99":[36,78,79,80,37,100,38,81,39,40,82],"102":[36,78,79,80,37,100,38,81,39,40,82],"104":[36,78,79,80,37,100,38,81,39,40,82],"106":[36,78,79,80,37,100,38,81,39,40,82],"108":[36,78,79,80,37,100,38,81,39,40,82],"110":[36,78,79,80,37,100,38,81,39,40,82],"113":[36,78,79,80,37,100,38,81,39,40,82],"115":[36,78,79,80,37,100,38,81,39,40,82],"116":[36,78,79,80,37,100,38,81,39,40,82],"118":[36,78,79,80,37,100,38,81,39,40,82],"121":[36,78,79,80,37,100,38,81,39,40,82],"124":[36,78,79,80,37,100,38,81,39,40,82],"126":[36,78,79,80,37,100,38,81,39,40,82],"127":[36,78,79,80,37,100,38,81,39,40,82],"129":[36,78,79,80,37,100,38,81,39,40,82],"132":[36,78,79,80,37,100,38,81,39,40,82],"135":[36,78,79,80,37,100,38,81,39,40,82],"138":[36,78,79,80,37,100,38,81,39,40,82],"141":[36,78,79,80,37,100,38,81,39,40,82],"144":[36,78,79,80,37,100,38,81,39,40,82],"147":[36,78,79,80,37,100,38,81,39,40,82],"150":[36,78,79,80,37,100,38,81,39,40,82],"153":[36,78,79,80,37,100,38,81,39,40,82],"156":[36,78,79,80,37,100,38,81,39,40,82],"159":[36,78,79,80,37,100,38,81,39,40,82],"162":[36,78,79,80,37,100,38,81,39,40,82],"165":[36,78,79,80,37,100,38,81,39,40,82],"167":[36,78,79,80,37,100,38,81,39,40,82],"169":[36,78,79,80,37,100,38,81,39,40,82],"172":[36,78,79,80,37,100,38,81,39,40,82],"175":[36,78,79,80,37,100,38,81,39,40,82],"178":[36,78,79,80,37,100,38,81,39,40,82],"181":[36,78,79,80,37,100,38,81,39,40,82],"191":[36,78,79,80,37,100,38,81,39,40,82],"192":[36,78,79,80,37,100,38,81,39,40,82],"194":[36,78,79,80,37,100,38,81,39,40,82],"211":[36,78,79,80,37,100,38,81,39,40,82],"215":[36,78,79,80,37,100,38,81,39,40,82],"220":[36,78,79,80,37,100,38,81,39,40,82],"223":[36,78,79,80,37,100,38,81,39,40,82],"226":[36,78,79,80,37,100,38,81,39,40,82],"229":[36,78,79,80,37,100,38,81,39,40,82],"232":[36,78,79,80,37,100,38,81,39,40,82],"236":[36,78,79,80,37,100,38,81,39,40,82],"239":[36,78,79,80,37,100,38,81,39,40,82],"243":[36,78,79,80,37,100,38,81,39,40,82],"247":[36,78,79,80,37,100,38,81,39,40,82],"251":[36,78,79,80,37,100,38,81,39,40,82],"264":[36,78,79,80,37,100,38,81,39,40,82],"266":[36,78,79,80,37,100,38,81,39,40,82],"287":[36,78,79,80,37,100,38,81,39,40,82],"291":[36,78,79,80,37,100,38,81,39,40,82],"295":[36,78,79,80,37,100,38,81,39,40,82],"298":[36,78,79,80,37,100,38,81,39,40,82],"301":[36,78,79,80,37,100,38,81,39,40,82],"306":[36,78,79,80,37,100,38,81,39,40,82],"310":[36,78,79,80,37,100,38,81,39,40,82],"314":[36,78,79,80,37,100,38,81,39,40,82],"318":[36,78,79,80,37,100,38,81,39,40,82],"320":[36,78,79,80,37,100,38,81,39,40,82],"323":[36,78,79,80,37,100,38,81,39,40,82],"326":[36,78,79,80,37,100,38,81,39,40,82],"330":[36,78,79,80,37,100,38,81,39,40,82],"343":[36,78,79,80,37,100,38,81,39,40,82],"346":[36,78,79,80,37,100,38,81,39,40,82],"350":[36,78,79,80,37,100,38,81,39,40,82],"353":[36,78,79,80,37,100,38,81,39,40,82],"356":[36,78,79,80,37,100,38,81,39,40,82],"359":[36,78,79,80,37,100,38,81,39,40,82],"362":[36,78,79,80,37,100,38,81,39,40,82],"365":[36,78,79,80,37,100,38,81,39,40,82],"368":[36,78,79,80,37,100,38,81,39,40,82],"371":[36,78,79,80,37,100,38,81,39,40,82],"372":[36,78,79,80,37,100,38,81,39,40,82],"375":[36,78,79,80,37,100,38,81,39,40,82],"377":[36,78,79,80,37,100,38,81,39,40,82],"378":[36,78,79,80,37,100,38,81,39,40,82],"381":[36,78,79,80,37,100,38,81,39,40,82],"385":[36,78,79,80,37,100,38,81,39,40,82],"389":[36,78,79,80,37,100,38,81,39,40,82],"393":[36,78,79,80,37,100,38,81,39,40,82],"395":[36,78,79,80,37,100,38,81,39,40,82],"396":[36,78,79,80,37,100,38,81,39,40,82],"399":[36,78,79,80,37,100,38,81,39,40,82],"403":[36,78,79,80,37,100,38,81,39,40,82],"405":[36,78,79,80,37,100,38,81,39,40,82],"408":[36,78,79,80,37,100,38,81,39,40,82],"410":[36,78,79,80,37,100,38,81,39,40,82],"411":[36,78,79,80,37,100,38,81,39,40,82],"412":[36,78,79,80,37,100,38,81,39,40,82],"415":[36,78,79,80,37,100,38,81,39,40,82],"419":[36,78,79,80,37,100,38,81,39,40,82],"422":[36,78,79,80,37,100,38,81,39,40,82],"426":[36,78,79,80,37,100,38,81,39,40,82],"429":[36,78,79,80,37,100,38,81,39,40,82],"432":[36,78,79,80,37,100,38,81,39,40,82],"435":[36,78,79,80,37,100,38,81,39,40,82],"439":[36,78,79,80,37,100,38,81,39,40,82],"442":[36,78,79,80,37,100,38,81,39,40,82],"444":[36,78,79,80,37,100,38,81,39,40,82],"450":[36,78,79,80,37,100,38,81,39,40,82],"453":[36,78,79,80,37,100,38,81,39,40,82],"457":[36,78,79,80,37,100,38,81,39,40,82],"462":[36,78,79,80,37,100,38,81,39,40,82],"464":[36,78,79,80,37,100,38,81,39,40,82],"475":[36,78,79,80,37,100,38,81,39,40,82],"477":[36,78,79,80,37,100,38,81,39,40,82],"496":[36,78,79,80,37,100,38,81,39,40,82],"503":[36,78,79,80,37,100,38,81,39,40,82],"517":[36,78,79,80,37,100,38,81,39,40,82],"519":[36,78,79,80,37,100,38,81,39,40,82],"522":[36,78,79,80,37,100,38,81,39,40,82],"525":[36,78,79,80,37,100,38,81,39,40,82],"529":[36,78,79,80,37,100,38,81,39,40,82],"544":[36,78,79,80,37,100,38,81,39,40,82],"547":[36,78,79,80,37,100,38,81,39,40,82],"549":[36,78,79,80,37,100,38,81,39,40,82],"551":[36,78,79,80,37,100,38,81,39,40,82],"553":[36,78,79,80,37,100,38,81,39,40,82],"555":[36,78,79,80,37,100,38,81,39,40,82],"557":[36,78,79,80,37,100,38,81,39,40,82],"576":[36,78,79,80,37,100,38,81,39,40,82],"580":[36,78,79,80,37,100,38,81,39,40,82],"582":[36,78,79,80,37,100,38,81,39,40,82],"672":[36,78,79,80,37,100,38,81,39,40,82],"675":[36,78,79,80,37,100,38,81,39,40,82],"677":[36,78,79,80,37,100,38,81,39,40,82],"679":[36,78,79,80,37,100,38,81,39,40,82],"688":[36,78,79,80,37,100,38,81,39,40,82],"690":[36,78,79,80,37,100,38,81,39,40,82],"692":[36,78,79,80,37,100,38,81,39,40,82],"694":[36,78,79,80,37,100,38,81,39,40,82],"697":[36,78,79,80,37,100,38,81,39,40,82]},"packages/@n8n/instance-ai/src/knowledge-base/build-templates-index.ts":{"36":[36,78,80,100,38,81,39,40,82],"40":[36,78,80,100,38,81,39,40,82],"41":[36,78,80,100,38,81,39,40,82],"49":[36,78,80,100,38,81,39,40,82],"56":[36,78,80,100,38,81,39,40,82],"60":[36,78,80,100,38,81,39,40,82],"64":[36,78,80,100,38,81,39,40,82],"87":[36,78,80,100,38,81,39,40,82],"108":[36,78,80,100,38,81,39,40,82],"118":[36,78,80,100,38,81,39,40,82],"126":[36,78,80,100,38,81,39,40,82],"129":[36,78,80,100,38,81,39,40,82],"141":[36,78,80,100,38,81,39,40,82],"151":[36,78,80,100,38,81,39,40,82],"152":[36,78,80,100,38,81,39,40,82],"161":[36,78,80,100,38,81,39,40,82]},"packages/@n8n/instance-ai/src/knowledge-base/extract-builder-templates-archive.ts":{"7":[36,78,80,100,38,81,39,40,82],"11":[36,78,80,100,38,81,39,40,82],"16":[36,78,80,100,38,81,39,40,82],"17":[36,78,80,100,38,81,39,40,82],"20":[36,78,80,100,38,81,39,40,82],"26":[36,78,80,100,38,81,39,40,82],"35":[36,78,80,100,38,81,39,40,82],"76":[36,78,80,100,38,81,39,40,82],"89":[36,78,80,100,38,81,39,40,82],"101":[36,78,80,100,38,81,39,40,82]},"packages/@n8n/instance-ai/src/knowledge-base/materialize-knowledge-base.ts":{"100":[36,78,80,100,38,81,39,40,82],"164":[36,78,80,100,38,81,39,40,82],"195":[36,78,80,100,38,81,39,40,82],"196":[36,78,80,100,38,81,39,40,82],"208":[36,78,80,100,38,81,39,40,82],"283":[36,78,80,100,38,81,39,40,82],"303":[36,78,80,100,38,81,39,40,82],"309":[36,78,80,100,38,81,39,40,82],"313":[36,78,80,100,38,81,39,40,82],"320":[36,78,80,100,38,81,39,40,82],"321":[36,78,80,100,38,81,39,40,82],"323":[36,78,80,100,38,81,39,40,82]},"packages/@n8n/instance-ai/src/mcp/mcp-client-manager.ts":{"61":[36,78,80,100,38,81,82],"74":[36,78,80,100,38,81,82],"98":[36,78,80,100,38,81,82],"102":[36,78,80,100,38,81,82],"103":[36,78,80,100,38,81,82],"117":[36,78,80,100,38,81,82],"118":[36,78,80,100,38,81,82],"127":[36,78,80,100,38,81,82],"132":[36,78,80,100,38,81,82],"157":[36,78,80,100,38,81,82],"162":[36,78,80,100,38,81,82],"177":[36,78,80,100,38,81,82],"190":[36,78,80,100,38,81,82],"195":[36,78,80,100,38,81,82],"198":[36,78,80,100,38,81,82],"210":[36,78,80,100,38,81,82],"224":[36,78,80,100,38,81,82],"252":[36,78,80,100,38,81,82],"273":[36,78,80,100,38,81,82],"275":[36,78,80,100,38,81,82]},"packages/@n8n/instance-ai/src/memory/title-utils.ts":{"13":[36,78,80,100,38,81,82],"21":[36,78,80,100,38,81,82],"35":[36,78,80,100,38,81,82]},"packages/@n8n/instance-ai/src/planned-tasks/planned-task-service.ts":{"20":[36,78,80,100,38,81,82],"26":[36,78,80,100,38,81,82],"27":[36,78,80,100,38,81,82],"30":[36,78,80,100,38,81,82],"35":[36,78,80,100,38,81,82],"36":[36,78,80,100,38,81,82],"50":[36,78,80,100,38,81,82],"63":[36,78,80,100,38,81,82],"83":[36,78,80,100,38,81,82],"87":[36,78,80,100,38,81,82],"104":[36,78,80,100,38,81,82],"109":[36,78,80,100,38,81,82],"118":[36,78,80,100,38,81,82],"120":[36,78,80,100,38,81,82],"139":[36,78,80,100,38,81,82],"156":[36,78,80,100,38,81,82],"157":[36,78,80,100,38,81,82],"172":[36,78,80,100,38,81,82],"173":[36,78,80,100,38,81,82],"181":[36,78,80,100,38,81,82],"185":[36,78,80,100,38,81,82],"190":[36,78,80,100,38,81,82],"191":[36,78,80,100,38,81,82],"202":[36,78,80,100,38,81,82],"207":[36,78,80,100,38,81,82],"208":[36,78,80,100,38,81,82],"219":[36,78,80,100,38,81,82],"224":[36,78,80,100,38,81,82],"225":[36,78,80,100,38,81,82],"232":[36,78,80,100,38,81,82],"261":[36,78,80,100,38,81,82],"268":[36,78,80,100,38,81,82],"269":[36,78,80,100,38,81,82],"283":[36,78,80,100,38,81,82],"311":[36,78,80,100,38,81,82],"318":[36,78,80,100,38,81,82],"325":[36,78,80,100,38,81,82],"332":[36,78,80,100,38,81,82],"333":[36,78,80,100,38,81,82],"347":[36,78,80,100,38,81,82],"361":[36,78,80,100,38,81,82],"374":[36,78,80,100,38,81,82],"375":[36,78,80,100,38,81,82],"393":[36,78,80,100,38,81,82],"422":[36,78,80,100,38,81,82],"427":[36,78,80,100,38,81,82],"428":[36,78,80,100,38,81,82],"437":[36,78,80,100,38,81,82],"448":[36,78,80,100,38,81,82],"454":[36,78,80,100,38,81,82],"478":[36,78,80,100,38,81,82],"501":[36,78,80,100,38,81,82],"504":[36,78,80,100,38,81,82],"514":[36,78,80,100,38,81,82],"520":[36,78,80,100,38,81,82],"534":[36,78,80,100,38,81,82],"549":[36,78,80,100,38,81,82],"550":[36,78,80,100,38,81,82]},"packages/@n8n/instance-ai/src/runtime/background-task-manager.ts":{"125":[36,78,79,80,177,100,38,81,39,40,82],"127":[36,78,79,80,177,100,38,81,39,40,82],"131":[36,78,79,80,177,100,38,81,39,40,82],"158":[36,78,79,80,177,100,38,81,39,40,82],"159":[36,78,79,80,177,100,38,81,39,40,82],"162":[36,78,79,80,177,100,38,81,39,40,82],"164":[36,78,79,80,177,100,38,81,39,40,82],"174":[36,78,79,80,177,100,38,81,39,40,82],"179":[36,78,79,80,177,100,38,81,39,40,82],"186":[36,78,79,80,177,100,38,81,39,40,82],"204":[36,78,79,80,177,100,38,81,39,40,82],"216":[36,78,79,80,177,100,38,81,39,40,82],"230":[36,78,79,80,177,100,38,81,39,40,82],"243":[36,78,79,80,177,100,38,81,39,40,82],"250":[36,78,79,80,177,100,38,81,39,40,82],"272":[36,78,79,80,177,100,38,81,39,40,82],"325":[36,78,79,80,177,100,38,81,39,40,82],"342":[36,78,79,80,177,100,38,81,39,40,82],"346":[36,78,79,80,177,100,38,81,39,40,82],"352":[36,78,79,80,177,100,38,81,39,40,82],"353":[36,78,79,80,177,100,38,81,39,40,82],"397":[36,78,79,80,177,100,38,81,39,40,82],"423":[36,78,79,80,177,100,38,81,39,40,82]},"packages/@n8n/instance-ai/src/runtime/memory-task-registry.ts":{"11":[36,78,79,80,38,81,40,82],"43":[36,78,79,80,38,81,40,82],"47":[36,78,79,80,38,81,40,82]},"packages/@n8n/instance-ai/src/runtime/orchestrator-identity.ts":{"6":[36,78,79,80,38,81,82]},"packages/@n8n/instance-ai/src/runtime/orchestrator-run-control.ts":{"20":[36,78,80,37,100,38,81,39,82],"26":[36,78,80,37,100,38,81,39,82],"29":[36,78,80,37,100,38,81,39,82],"32":[36,78,80,37,100,38,81,39,82],"38":[36,78,80,37,100,38,81,39,82],"44":[36,78,80,37,100,38,81,39,82]},"packages/@n8n/instance-ai/src/runtime/resumable-stream-executor.ts":{"92":[36,78,80,37,100,38,81,39,82],"100":[36,78,80,37,100,38,81,39,82],"108":[36,78,80,37,100,38,81,39,82],"112":[36,78,80,37,100,38,81,39,82],"116":[36,78,80,37,100,38,81,39,82],"126":[36,78,80,37,100,38,81,39,82],"136":[36,78,80,37,100,38,81,39,82],"168":[36,78,80,37,100,38,81,39,82],"181":[36,78,80,37,100,38,81,39,82],"199":[36,78,80,37,100,38,81,39,82],"219":[36,78,80,37,100,38,81,39,82],"253":[36,78,80,37,100,38,81,39,82],"317":[36,78,80,37,100,38,81,39,82],"491":[36,78,80,37,100,38,81,39,82],"603":[36,78,80,37,100,38,81,39,82],"618":[36,78,80,37,100,38,81,39,82],"628":[36,78,80,37,100,38,81,39,82],"632":[36,78,80,37,100,38,81,39,82],"647":[36,78,80,37,100,38,81,39,82],"648":[36,78,80,37,100,38,81,39,82],"669":[36,78,80,37,100,38,81,39,82],"677":[36,78,80,37,100,38,81,39,82],"688":[36,78,80,37,100,38,81,39,82],"702":[36,78,80,37,100,38,81,39,82]},"packages/@n8n/instance-ai/src/runtime/run-state-registry.ts":{"139":[36,78,79,80,37,100,38,81,39,40,82],"177":[36,78,79,80,37,100,38,81,39,40,82],"187":[36,78,79,80,37,100,38,81,39,40,82],"200":[36,78,79,80,37,100,38,81,39,40,82],"201":[36,78,79,80,37,100,38,81,39,40,82],"213":[36,78,79,80,37,100,38,81,39,40,82],"217":[36,78,79,80,37,100,38,81,39,40,82],"221":[36,78,79,80,37,100,38,81,39,40,82],"225":[36,78,79,80,37,100,38,81,39,40,82],"229":[36,78,79,80,37,100,38,81,39,40,82],"238":[36,78,79,80,37,100,38,81,39,40,82],"239":[36,78,79,80,37,100,38,81,39,40,82],"244":[36,78,79,80,37,100,38,81,39,40,82],"248":[36,78,79,80,37,100,38,81,39,40,82],"253":[36,78,79,80,37,100,38,81,39,40,82],"257":[36,78,79,80,37,100,38,81,39,40,82],"261":[36,78,79,80,37,100,38,81,39,40,82],"265":[36,78,79,80,37,100,38,81,39,40,82],"276":[36,78,79,80,37,100,38,81,39,40,82],"280":[36,78,79,80,37,100,38,81,39,40,82],"288":[36,78,79,80,37,100,38,81,39,40,82],"302":[36,78,79,80,37,100,38,81,39,40,82],"309":[36,78,79,80,37,100,38,81,39,40,82],"317":[36,78,79,80,37,100,38,81,39,40,82],"341":[36,78,79,80,37,100,38,81,39,40,82],"349":[36,78,79,80,37,100,38,81,39,40,82],"353":[36,78,79,80,37,100,38,81,39,40,82],"361":[36,78,79,80,37,100,38,81,39,40,82],"368":[36,78,79,80,37,100,38,81,39,40,82],"375":[36,78,79,80,37,100,38,81,39,40,82],"382":[36,78,79,80,37,100,38,81,39,40,82],"395":[36,78,79,80,37,100,38,81,39,40,82],"417":[36,78,79,80,37,100,38,81,39,40,82],"421":[36,78,79,80,37,100,38,81,39,40,82],"425":[36,78,79,80,37,100,38,81,39,40,82],"449":[36,78,79,80,37,100,38,81,39,40,82],"548":[36,78,79,80,37,100,38,81,39,40,82],"557":[36,78,79,80,37,100,38,81,39,40,82],"566":[36,78,79,80,37,100,38,81,39,40,82],"607":[36,78,79,80,37,100,38,81,39,40,82],"615":[36,78,79,80,37,100,38,81,39,40,82]},"packages/@n8n/instance-ai/src/runtime/stream-runner.ts":{"50":[36,78,80,37,100,38,81,39,82],"62":[36,78,80,37,100,38,81,39,82],"74":[36,78,80,37,100,38,81,39,82]},"packages/@n8n/instance-ai/src/skills/materialize-runtime-skills.ts":{"95":[36,78,80,100,38,81,39,40,82],"99":[36,78,80,100,38,81,39,40,82],"108":[36,78,80,100,38,81,39,40,82],"111":[36,78,80,100,38,81,39,40,82],"114":[36,78,80,100,38,81,39,40,82],"133":[36,78,80,100,38,81,39,40,82],"137":[36,78,80,100,38,81,39,40,82],"170":[36,78,80,100,38,81,39,40,82],"182":[36,78,80,100,38,81,39,40,82],"185":[36,78,80,100,38,81,39,40,82],"194":[36,78,80,100,38,81,39,40,82],"197":[36,78,80,100,38,81,39,40,82],"205":[36,78,80,100,38,81,39,40,82],"208":[36,78,80,100,38,81,39,40,82],"213":[36,78,80,100,38,81,39,40,82],"225":[36,78,80,100,38,81,39,40,82],"243":[36,78,80,100,38,81,39,40,82],"278":[36,78,80,100,38,81,39,40,82],"286":[36,78,80,100,38,81,39,40,82],"302":[36,78,80,100,38,81,39,40,82],"314":[36,78,80,100,38,81,39,40,82],"333":[36,78,80,100,38,81,39,40,82],"355":[36,78,80,100,38,81,39,40,82],"382":[36,78,80,100,38,81,39,40,82],"390":[36,78,80,100,38,81,39,40,82],"401":[36,78,80,100,38,81,39,40,82],"402":[36,78,80,100,38,81,39,40,82],"405":[36,78,80,100,38,81,39,40,82],"422":[36,78,80,100,38,81,39,40,82],"434":[36,78,80,100,38,81,39,40,82],"458":[36,78,80,100,38,81,39,40,82],"521":[36,78,80,100,38,81,39,40,82],"533":[36,78,80,100,38,81,39,40,82],"535":[36,78,80,100,38,81,39,40,82],"543":[36,78,80,100,38,81,39,40,82],"553":[36,78,80,100,38,81,39,40,82],"566":[36,78,80,100,38,81,39,40,82],"569":[36,78,80,100,38,81,39,40,82],"572":[36,78,80,100,38,81,39,40,82],"580":[36,78,80,100,38,81,39,40,82],"583":[36,78,80,100,38,81,39,40,82],"594":[36,78,80,100,38,81,39,40,82],"602":[36,78,80,100,38,81,39,40,82]},"packages/@n8n/instance-ai/src/skills/runtime-skills.ts":{"11":[36,78,80,100,38,81,82],"18":[36,78,80,100,38,81,82]},"packages/@n8n/instance-ai/src/skills/skill-gates.ts":{"14":[36,78,80,100,38,81,82]},"packages/@n8n/instance-ai/src/storage/index.ts":{"2":[36,78,79,80,100,38,81,82],"11":[36,78,79,80,100,38,81,82]},"packages/@n8n/instance-ai/src/storage/iteration-log.ts":{"30":[36,78,79,80,38,81,82],"33":[36,78,79,80,38,81,82]},"packages/@n8n/instance-ai/src/storage/planned-task-storage.ts":{"38":[36,78,79,80,100,38,81,82],"44":[36,78,79,80,100,38,81,82],"46":[36,78,79,80,100,38,81,82],"52":[36,78,79,80,100,38,81,82],"55":[36,78,79,80,100,38,81,82],"68":[36,78,79,80,100,38,81,82],"76":[36,78,79,80,100,38,81,82],"96":[36,78,79,80,100,38,81,82],"99":[36,78,79,80,100,38,81,82]},"packages/@n8n/instance-ai/src/storage/terminal-outcome-storage.ts":{"26":[36,78,79,80,38,81,82],"28":[36,78,79,80,38,81,82],"31":[36,78,79,80,38,81,82],"46":[36,78,79,80,38,81,82],"49":[36,78,79,80,38,81,82],"70":[36,78,79,80,38,81,82],"73":[36,78,79,80,38,81,82],"77":[36,78,79,80,38,81,82]},"packages/@n8n/instance-ai/src/storage/thread-iteration-log-storage.ts":{"12":[36,78,79,80,38,81,82],"14":[36,78,79,80,38,81,82],"17":[36,78,79,80,38,81,82],"34":[36,78,79,80,38,81,82],"42":[36,78,79,80,38,81,82],"45":[36,78,79,80,38,81,82],"53":[36,78,79,80,38,81,82]},"packages/@n8n/instance-ai/src/storage/thread-patch.ts":{"21":[36,78,79,80,38,81,82],"30":[36,78,79,80,38,81,82],"37":[36,78,79,80,38,81,82],"44":[36,78,79,80,38,81,82],"53":[36,78,79,80,38,81,82],"64":[36,78,79,80,38,81,82],"90":[36,78,79,80,38,81,82]},"packages/@n8n/instance-ai/src/storage/thread-task-storage.ts":{"10":[36,78,79,80,100,38,81,82],"12":[36,78,79,80,100,38,81,82],"19":[36,78,79,80,100,38,81,82],"22":[36,78,79,80,100,38,81,82]},"packages/@n8n/instance-ai/src/storage/workflow-loop-storage.ts":{"35":[36,78,79,80,100,38,81,39,40,82],"43":[36,78,79,80,100,38,81,39,40,82],"52":[36,78,79,80,100,38,81,39,40,82],"59":[36,78,79,80,100,38,81,39,40,82],"61":[36,78,79,80,100,38,81,39,40,82],"69":[36,78,79,80,100,38,81,39,40,82],"77":[36,78,79,80,100,38,81,39,40,82],"90":[36,78,79,80,100,38,81,39,40,82],"100":[36,78,79,80,100,38,81,39,40,82],"105":[36,78,79,80,100,38,81,39,40,82],"113":[36,78,79,80,100,38,81,39,40,82],"139":[36,78,79,80,100,38,81,39,40,82],"148":[36,78,79,80,100,38,81,39,40,82],"175":[36,78,79,80,100,38,81,39,40,82],"182":[36,78,79,80,100,38,81,39,40,82],"202":[36,78,79,80,100,38,81,39,40,82],"208":[36,78,79,80,100,38,81,39,40,82]},"packages/@n8n/instance-ai/src/stream/consume-with-hitl.ts":{"53":[36,78,80,100,38,81,82],"78":[36,78,80,100,38,81,82],"103":[36,78,80,100,38,81,82],"166":[36,78,80,100,38,81,82]},"packages/@n8n/instance-ai/src/stream/map-chunk.ts":{"22":[36,78,80,37,100,38,81,39,82],"27":[36,78,80,37,100,38,81,39,82],"35":[36,78,80,37,100,38,81,39,82],"43":[36,78,80,37,100,38,81,39,82],"47":[36,78,80,37,100,38,81,39,82],"49":[36,78,80,37,100,38,81,39,82],"59":[36,78,80,37,100,38,81,39,82],"63":[36,78,80,37,100,38,81,39,82],"110":[36,78,80,37,100,38,81,39,82],"134":[36,78,80,37,100,38,81,39,82],"139":[36,78,80,37,100,38,81,39,82],"163":[36,78,80,37,100,38,81,39,82],"171":[36,78,80,37,100,38,81,39,82],"187":[36,78,80,37,100,38,81,39,82],"238":[36,78,80,37,100,38,81,39,82],"242":[36,78,80,37,100,38,81,39,82],"245":[36,78,80,37,100,38,81,39,82],"246":[36,78,80,37,100,38,81,39,82],"247":[36,78,80,37,100,38,81,39,82],"251":[36,78,80,37,100,38,81,39,82],"257":[36,78,80,37,100,38,81,39,82],"263":[36,78,80,37,100,38,81,39,82],"278":[36,78,80,37,100,38,81,39,82],"285":[36,78,80,37,100,38,81,39,82],"294":[36,78,80,37,100,38,81,39,82],"309":[36,78,80,37,100,38,81,39,82],"328":[36,78,80,37,100,38,81,39,82],"393":[36,78,80,37,100,38,81,39,82],"406":[36,78,80,37,100,38,81,39,82],"423":[36,78,80,37,100,38,81,39,82],"434":[36,78,80,37,100,38,81,39,82],"440":[36,78,80,37,100,38,81,39,82],"445":[36,78,80,37,100,38,81,39,82],"463":[36,78,80,37,100,38,81,39,82]},"packages/@n8n/instance-ai/src/stream/output-redaction.ts":{"75":[36,78,80,100,38,81,82],"87":[36,78,80,100,38,81,82],"103":[36,78,80,100,38,81,82],"110":[36,78,80,100,38,81,82],"133":[36,78,80,100,38,81,82],"139":[36,78,80,100,38,81,82],"149":[36,78,80,100,38,81,82],"184":[36,78,80,100,38,81,82],"188":[36,78,80,100,38,81,82],"191":[36,78,80,100,38,81,82],"197":[36,78,80,100,38,81,82],"205":[36,78,80,100,38,81,82],"224":[36,78,80,100,38,81,82],"230":[36,78,80,100,38,81,82],"235":[36,78,80,100,38,81,82]},"packages/@n8n/instance-ai/src/stream/usage-accumulator.ts":{"83":[36,78,80,100,38,81,82],"119":[36,78,80,100,38,81,82],"124":[36,78,80,100,38,81,82],"150":[36,78,80,100,38,81,82]},"packages/@n8n/instance-ai/src/stream/work-summary-accumulator.ts":{"37":[36,78,80,100,38,81,39,82],"75":[36,78,80,100,38,81,39,82],"76":[36,78,80,100,38,81,39,82],"80":[36,78,80,100,38,81,39,82]},"packages/@n8n/instance-ai/src/tool-registry.ts":{"5":[36,78,80,100,38,81,82],"15":[36,78,80,100,38,81,82],"23":[36,78,80,100,38,81,82],"36":[36,78,80,100,38,81,82],"49":[36,78,80,100,38,81,82],"53":[36,78,80,100,38,81,82]},"packages/@n8n/instance-ai/src/tools/credentials.tool.ts":{"173":[36,78,80,100,38,81,82],"177":[36,78,80,100,38,81,82],"180":[36,78,80,100,38,81,82],"182":[36,78,80,100,38,81,82],"213":[36,78,80,100,38,81,82],"217":[36,78,80,100,38,81,82],"218":[36,78,80,100,38,81,82],"225":[36,78,80,100,38,81,82],"284":[36,78,80,100,38,81,82],"314":[36,78,80,100,38,81,82],"326":[36,78,80,100,38,81,82],"341":[36,78,80,100,38,81,82],"345":[36,78,80,100,38,81,82],"377":[36,78,80,100,38,81,82],"384":[36,78,80,100,38,81,82],"401":[36,78,80,100,38,81,82],"406":[36,78,80,100,38,81,82],"426":[36,78,80,100,38,81,82],"434":[36,78,80,100,38,81,82],"442":[36,78,80,100,38,81,82],"496":[36,78,80,100,38,81,82],"509":[36,78,80,100,38,81,82],"520":[36,78,80,100,38,81,82]},"packages/@n8n/instance-ai/src/tools/data-tables.tool.ts":{"66":[36,78,80,100,38,81,82],"84":[36,78,80,100,38,81,82],"261":[36,78,80,100,38,81,82],"284":[36,78,80,100,38,81,82],"292":[36,78,80,100,38,81,82],"303":[36,78,80,100,38,81,82],"329":[36,78,80,100,38,81,82],"381":[36,78,80,100,38,81,82],"413":[36,78,80,100,38,81,82],"449":[36,78,80,100,38,81,82],"483":[36,78,80,100,38,81,82],"517":[36,78,80,100,38,81,82],"550":[36,78,80,100,38,81,82],"583":[36,78,80,100,38,81,82],"600":[36,78,80,100,38,81,82],"634":[36,78,80,100,38,81,82],"649":[36,78,80,100,38,81,82]},"packages/@n8n/instance-ai/src/tools/evals/analyze-agent-input-columns.service.ts":{"12":[36,78,80,100,38,81,82],"16":[36,78,80,100,38,81,82],"22":[36,78,80,100,38,81,82]},"packages/@n8n/instance-ai/src/tools/evals/apply-pin-data.service.ts":{"14":[36,78,80,100,38,81,82]},"packages/@n8n/instance-ai/src/tools/evals/column-ref-utils.ts":{"6":[36,78,80,100,38,81,82],"7":[36,78,80,100,38,81,82],"10":[36,78,80,100,38,81,82],"14":[36,78,80,100,38,81,82],"18":[36,78,80,100,38,81,82],"29":[36,78,80,100,38,81,82],"33":[36,78,80,100,38,81,82],"37":[36,78,80,100,38,81,82],"43":[36,78,80,100,38,81,82],"54":[36,78,80,100,38,81,82],"80":[36,78,80,100,38,81,82],"106":[36,78,80,100,38,81,82],"128":[36,78,80,100,38,81,82],"136":[36,78,80,100,38,81,82],"145":[36,78,80,100,38,81,82],"152":[36,78,80,100,38,81,82],"156":[36,78,80,100,38,81,82],"160":[36,78,80,100,38,81,82],"164":[36,78,80,100,38,81,82],"204":[36,78,80,100,38,81,82],"219":[36,78,80,100,38,81,82],"235":[36,78,80,100,38,81,82],"250":[36,78,80,100,38,81,82],"262":[36,78,80,100,38,81,82],"270":[36,78,80,100,38,81,82],"286":[36,78,80,100,38,81,82],"297":[36,78,80,100,38,81,82]},"packages/@n8n/instance-ai/src/tools/evals/describe-metric-for-workflow.service.ts":{"14":[36,78,80,100,38,81,82],"34":[36,78,80,100,38,81,82],"43":[36,78,80,100,38,81,82],"62":[36,78,80,100,38,81,82],"68":[36,78,80,100,38,81,82],"75":[36,78,80,100,38,81,82],"84":[36,78,80,100,38,81,82],"121":[36,78,80,100,38,81,82]},"packages/@n8n/instance-ai/src/tools/evals/detect-agent-named-refs.service.ts":{"24":[36,78,80,100,38,81,82],"34":[36,78,80,100,38,81,82],"35":[36,78,80,100,38,81,82],"42":[36,78,80,100,38,81,82],"50":[36,78,80,100,38,81,82],"79":[36,78,80,100,38,81,82]},"packages/@n8n/instance-ai/src/tools/evals/detect-ai-nodes.ts":{"60":[36,78,80,100,38,81,82],"64":[36,78,80,100,38,81,82],"71":[36,78,80,100,38,81,82],"74":[36,78,80,100,38,81,82],"77":[36,78,80,100,38,81,82]},"packages/@n8n/instance-ai/src/tools/evals/detect-tool-refs.service.ts":{"26":[36,78,80,100,38,81,82],"57":[36,78,80,100,38,81,82],"58":[36,78,80,100,38,81,82],"67":[36,78,80,100,38,81,82],"76":[36,78,80,100,38,81,82]},"packages/@n8n/instance-ai/src/tools/evals/ensure-eval-data-table.service.ts":{"14":[36,78,80,100,38,81,82],"18":[36,78,80,100,38,81,82],"30":[36,78,80,100,38,81,82],"35":[36,78,80,100,38,81,82],"54":[36,78,80,100,38,81,82],"58":[36,78,80,100,38,81,82],"92":[36,78,80,100,38,81,82],"99":[36,78,80,100,38,81,82]},"packages/@n8n/instance-ai/src/tools/evals/eval-data-requirements.service.ts":{"38":[36,78,80,100,38,81,82],"45":[36,78,80,100,38,81,82],"55":[36,78,80,100,38,81,82],"63":[36,78,80,100,38,81,82],"65":[36,78,80,100,38,81,82],"67":[36,78,80,100,38,81,82],"76":[36,78,80,100,38,81,82],"91":[36,78,80,100,38,81,82],"92":[36,78,80,100,38,81,82],"95":[36,78,80,100,38,81,82],"99":[36,78,80,100,38,81,82],"102":[36,78,80,100,38,81,82],"106":[36,78,80,100,38,81,82],"108":[36,78,80,100,38,81,82],"118":[36,78,80,100,38,81,82],"120":[36,78,80,100,38,81,82],"128":[36,78,80,100,38,81,82],"137":[36,78,80,100,38,81,82],"148":[36,78,80,100,38,81,82],"162":[36,78,80,100,38,81,82],"168":[36,78,80,100,38,81,82],"173":[36,78,80,100,38,81,82],"178":[36,78,80,100,38,81,82],"185":[36,78,80,100,38,81,82],"190":[36,78,80,100,38,81,82],"193":[36,78,80,100,38,81,82],"195":[36,78,80,100,38,81,82],"202":[36,78,80,100,38,81,82],"208":[36,78,80,100,38,81,82],"213":[36,78,80,100,38,81,82],"215":[36,78,80,100,38,81,82]},"packages/@n8n/instance-ai/src/tools/evals/evals.tool.ts":{"159":[36,78,80,100,38,81,82],"173":[36,78,80,100,38,81,82],"174":[36,78,80,100,38,81,82],"176":[36,78,80,100,38,81,82],"188":[36,78,80,100,38,81,82],"193":[36,78,80,100,38,81,82],"197":[36,78,80,100,38,81,82],"201":[36,78,80,100,38,81,82],"208":[36,78,80,100,38,81,82],"215":[36,78,80,100,38,81,82],"219":[36,78,80,100,38,81,82],"227":[36,78,80,100,38,81,82],"235":[36,78,80,100,38,81,82],"258":[36,78,80,100,38,81,82],"262":[36,78,80,100,38,81,82],"269":[36,78,80,100,38,81,82],"298":[36,78,80,100,38,81,82],"307":[36,78,80,100,38,81,82],"311":[36,78,80,100,38,81,82],"320":[36,78,80,100,38,81,82],"330":[36,78,80,100,38,81,82],"347":[36,78,80,100,38,81,82],"380":[36,78,80,100,38,81,82],"394":[36,78,80,100,38,81,82],"433":[36,78,80,100,38,81,82],"457":[36,78,80,100,38,81,82],"466":[36,78,80,100,38,81,82],"467":[36,78,80,100,38,81,82],"491":[36,78,80,100,38,81,82],"547":[36,78,80,100,38,81,82],"549":[36,78,80,100,38,81,82],"559":[36,78,80,100,38,81,82]},"packages/@n8n/instance-ai/src/tools/evals/extract-rows-from-history.service.ts":{"27":[36,78,80,100,38,81,82],"45":[36,78,80,100,38,81,82],"72":[36,78,80,100,38,81,82],"76":[36,78,80,100,38,81,82]},"packages/@n8n/instance-ai/src/tools/evals/format-eval-setup-task.ts":{"28":[36,78,80,100,38,81,82],"32":[36,78,80,100,38,81,82],"36":[36,78,80,100,38,81,82],"60":[36,78,80,100,38,81,82],"87":[36,78,80,100,38,81,82],"99":[36,78,80,100,38,81,82],"100":[36,78,80,100,38,81,82],"117":[36,78,80,100,38,81,82],"149":[36,78,80,100,38,81,82],"158":[36,78,80,100,38,81,82],"162":[36,78,80,100,38,81,82],"173":[36,78,80,100,38,81,82],"176":[36,78,80,100,38,81,82],"183":[36,78,80,100,38,81,82],"188":[36,78,80,100,38,81,82],"189":[36,78,80,100,38,81,82],"191":[36,78,80,100,38,81,82]},"packages/@n8n/instance-ai/src/tools/evals/generate-sample-rows.service.ts":{"63":[36,78,80,100,38,81,82],"67":[36,78,80,100,38,81,82],"70":[36,78,80,100,38,81,82],"86":[36,78,80,100,38,81,82],"92":[36,78,80,100,38,81,82],"99":[36,78,80,100,38,81,82],"101":[36,78,80,100,38,81,82],"108":[36,78,80,100,38,81,82],"112":[36,78,80,100,38,81,82],"126":[36,78,80,100,38,81,82],"130":[36,78,80,100,38,81,82],"151":[36,78,80,100,38,81,82],"168":[36,78,80,100,38,81,82],"188":[36,78,80,100,38,81,82],"214":[36,78,80,100,38,81,82],"232":[36,78,80,100,38,81,82],"235":[36,78,80,100,38,81,82],"269":[36,78,80,100,38,81,82],"289":[36,78,80,100,38,81,82],"302":[36,78,80,100,38,81,82],"303":[36,78,80,100,38,81,82],"306":[36,78,80,100,38,81,82],"314":[36,78,80,100,38,81,82]},"packages/@n8n/instance-ai/src/tools/evals/generate-tool-ref-pin-data.service.ts":{"37":[36,78,80,100,38,81,82],"53":[36,78,80,100,38,81,82],"61":[36,78,80,100,38,81,82],"77":[36,78,80,100,38,81,82],"89":[36,78,80,100,38,81,82],"92":[36,78,80,100,38,81,82],"104":[36,78,80,100,38,81,82],"113":[36,78,80,100,38,81,82],"121":[36,78,80,100,38,81,82],"133":[36,78,80,100,38,81,82]},"packages/@n8n/instance-ai/src/tools/evals/metric-catalog.ts":{"70":[36,78,80,100,38,81,82],"74":[36,78,80,100,38,81,82],"75":[36,78,80,100,38,81,82],"78":[36,78,80,100,38,81,82],"94":[36,78,80,100,38,81,82]},"packages/@n8n/instance-ai/src/tools/executions.tool.ts":{"154":[36,78,80,100,38,81,82],"163":[36,78,80,100,38,81,82],"167":[36,78,80,100,38,81,82],"171":[36,78,80,100,38,81,82],"185":[36,78,80,100,38,81,82],"193":[36,78,80,100,38,81,82],"200":[36,78,80,100,38,81,82],"227":[36,78,80,100,38,81,82],"230":[36,78,80,100,38,81,82],"231":[36,78,80,100,38,81,82],"300":[36,78,80,100,38,81,82],"304":[36,78,80,100,38,81,82],"314":[36,78,80,100,38,81,82],"328":[36,78,80,100,38,81,82],"334":[36,78,80,100,38,81,82],"345":[36,78,80,100,38,81,82]},"packages/@n8n/instance-ai/src/tools/filesystem/create-tools-from-mcp-server.ts":{"23":[36,78,80,100,38,81,82],"81":[36,78,80,100,38,81,82],"87":[36,78,80,100,38,81,82],"100":[36,78,80,100,38,81,82],"112":[36,78,80,100,38,81,82],"117":[36,78,80,100,38,81,82],"118":[36,78,80,100,38,81,82],"154":[36,78,80,100,38,81,82],"178":[36,78,80,100,38,81,82],"202":[36,78,80,100,38,81,82],"206":[36,78,80,100,38,81,82],"212":[36,78,80,100,38,81,82],"224":[36,78,80,100,38,81,82],"225":[36,78,80,100,38,81,82],"239":[36,78,80,100,38,81,82],"240":[36,78,80,100,38,81,82],"266":[36,78,80,100,38,81,82],"328":[36,78,80,100,38,81,82],"374":[36,78,80,100,38,81,82],"381":[36,78,80,100,38,81,82],"393":[36,78,80,100,38,81,82]},"packages/@n8n/instance-ai/src/tools/index.ts":{"9":[36,78,80,100,38,81,82],"11":[36,78,80,100,38,81,82],"15":[36,78,80,100,38,81,82],"18":[36,78,80,100,38,81,82],"21":[36,78,80,100,38,81,82],"24":[36,78,80,100,38,81,82],"27":[36,78,80,100,38,81,82],"30":[36,78,80,100,38,81,82],"32":[36,78,80,100,38,81,82],"34":[36,78,80,100,38,81,82],"36":[36,78,80,100,38,81,82],"38":[36,78,80,100,38,81,82],"42":[36,78,80,100,38,81,82],"46":[36,78,80,100,38,81,82],"50":[36,78,80,100,38,81,82],"54":[36,78,80,100,38,81,82],"58":[36,78,80,100,38,81,82],"61":[36,78,80,100,38,81,82],"65":[36,78,80,100,38,81,82],"69":[36,78,80,100,38,81,82],"72":[36,78,80,100,38,81,82],"75":[36,78,80,100,38,81,82],"78":[36,78,80,100,38,81,82],"82":[36,78,80,100,38,81,82],"86":[36,78,80,100,38,81,82],"89":[36,78,80,100,38,81,82],"96":[36,78,80,100,38,81,82],"129":[36,78,80,100,38,81,82],"161":[36,78,80,100,38,81,82]},"packages/@n8n/instance-ai/src/tools/n8n-docs/ranking.ts":{"24":[36,78,80,100,38,81,82],"32":[36,78,80,100,38,81,82],"35":[36,78,80,100,38,81,82],"53":[36,78,80,100,38,81,82],"71":[36,78,80,100,38,81,82],"75":[36,78,80,100,38,81,82],"76":[36,78,80,100,38,81,82],"79":[36,78,80,100,38,81,82],"85":[36,78,80,100,38,81,82],"90":[36,78,80,100,38,81,82],"94":[36,78,80,100,38,81,82],"98":[36,78,80,100,38,81,82],"102":[36,78,80,100,38,81,82],"113":[36,78,80,100,38,81,82],"203":[36,78,80,100,38,81,82],"210":[36,78,80,100,38,81,82],"211":[36,78,80,100,38,81,82],"212":[36,78,80,100,38,81,82],"215":[36,78,80,100,38,81,82],"221":[36,78,80,100,38,81,82],"226":[36,78,80,100,38,81,82]},"packages/@n8n/instance-ai/src/tools/n8n-docs/registry.ts":{"52":[36,78,80,100,38,81,82],"67":[36,78,80,100,38,81,82],"71":[36,78,80,100,38,81,82],"86":[36,78,80,100,38,81,82],"91":[36,78,80,100,38,81,82],"116":[36,78,80,100,38,81,82],"136":[36,78,80,100,38,81,82],"140":[36,78,80,100,38,81,82],"169":[36,78,80,100,38,81,82],"174":[36,78,80,100,38,81,82],"217":[36,78,80,100,38,81,82],"233":[36,78,80,100,38,81,82],"269":[36,78,80,100,38,81,82],"287":[36,78,80,100,38,81,82]},"packages/@n8n/instance-ai/src/tools/n8n-docs.tool.ts":{"13":[36,78,80,100,38,81,82],"16":[36,78,80,100,38,81,82],"19":[36,78,80,100,38,81,82],"53":[36,78,80,100,38,81,82],"58":[36,78,80,100,38,81,82],"67":[36,78,80,100,38,81,82],"83":[36,78,80,100,38,81,82],"104":[36,78,80,100,38,81,82],"124":[36,78,80,100,38,81,82],"144":[36,78,80,100,38,81,82],"157":[36,78,80,100,38,81,82],"176":[36,78,80,100,38,81,82],"177":[36,78,80,100,38,81,82],"203":[36,78,80,100,38,81,82],"209":[36,78,80,100,38,81,82]},"packages/@n8n/instance-ai/src/tools/nodes/node-search-engine.ts":{"49":[36,78,80,100,38,81,82],"61":[36,78,80,100,38,81,82],"66":[36,78,80,100,38,81,82],"70":[36,78,80,100,38,81,82],"71":[36,78,80,100,38,81,82],"78":[36,78,80,100,38,81,82],"84":[36,78,80,100,38,81,82],"102":[36,78,80,100,38,81,82],"104":[36,78,80,100,38,81,82],"122":[36,78,80,100,38,81,82],"127":[36,78,80,100,38,81,82],"135":[36,78,80,100,38,81,82],"139":[36,78,80,100,38,81,82],"169":[36,78,80,100,38,81,82],"178":[36,78,80,100,38,81,82],"184":[36,78,80,100,38,81,82],"213":[36,78,80,100,38,81,82],"231":[36,78,80,100,38,81,82],"237":[36,78,80,100,38,81,82],"244":[36,78,80,100,38,81,82],"248":[36,78,80,100,38,81,82],"256":[36,78,80,100,38,81,82],"260":[36,78,80,100,38,81,82],"276":[36,78,80,100,38,81,82],"283":[36,78,80,100,38,81,82],"296":[36,78,80,100,38,81,82],"307":[36,78,80,100,38,81,82],"311":[36,78,80,100,38,81,82],"318":[36,78,80,100,38,81,82],"320":[36,78,80,100,38,81,82],"326":[36,78,80,100,38,81,82],"330":[36,78,80,100,38,81,82],"332":[36,78,80,100,38,81,82],"346":[36,78,80,100,38,81,82],"404":[36,78,80,100,38,81,82],"418":[36,78,80,100,38,81,82],"432":[36,78,80,100,38,81,82],"459":[36,78,80,100,38,81,82],"460":[36,78,80,100,38,81,82],"469":[36,78,80,100,38,81,82],"492":[36,78,80,100,38,81,82],"500":[36,78,80,100,38,81,82]},"packages/@n8n/instance-ai/src/tools/nodes/preferred-chat-model.ts":{"19":[36,78,80,100,38,81,82]},"packages/@n8n/instance-ai/src/tools/nodes.tool.ts":{"146":[36,78,80,100,38,81,40,82],"157":[36,78,80,100,38,81,40,82],"182":[36,78,80,100,38,81,40,82],"193":[36,78,80,100,38,81,40,82],"194":[36,78,80,100,38,81,40,82],"206":[36,78,80,100,38,81,40,82],"210":[36,78,80,100,38,81,40,82],"225":[36,78,80,100,38,81,40,82],"250":[36,78,80,100,38,81,40,82],"256":[36,78,80,100,38,81,40,82],"265":[36,78,80,100,38,81,40,82],"299":[36,78,80,100,38,81,40,82],"313":[36,78,80,100,38,81,40,82],"322":[36,78,80,100,38,81,40,82],"348":[36,78,80,100,38,81,40,82],"376":[36,78,80,100,38,81,40,82],"418":[36,78,80,100,38,81,40,82],"434":[36,78,80,100,38,81,40,82]},"packages/@n8n/instance-ai/src/tools/orchestration/agent-persistence.ts":{"7":[36,78,80,100,38,81,82],"11":[36,78,80,100,38,81,82],"21":[36,78,80,100,38,81,82],"31":[36,78,80,100,38,81,82]},"packages/@n8n/instance-ai/src/tools/orchestration/agent-preview-session-binding.ts":{"22":[36,78,80,100,38,81,82],"27":[36,78,80,100,38,81,82],"41":[36,78,80,100,38,81,82],"55":[36,78,80,100,38,81,82],"69":[36,78,80,100,38,81,82]},"packages/@n8n/instance-ai/src/tools/orchestration/complete-checkpoint.tool.ts":{"40":[36,78,80,100,38,81,82],"45":[36,78,80,100,38,81,82],"50":[36,78,80,100,38,81,82],"57":[36,78,80,100,38,81,82],"62":[36,78,80,100,38,81,82],"63":[36,78,80,100,38,81,82],"64":[36,78,80,100,38,81,82],"65":[36,78,80,100,38,81,82],"82":[36,78,80,100,38,81,82],"85":[36,78,80,100,38,81,82],"86":[36,78,80,100,38,81,82],"110":[36,78,80,100,38,81,82],"120":[36,78,80,100,38,81,82]},"packages/@n8n/instance-ai/src/tools/orchestration/display-utils.ts":{"2":[36,78,80,100,38,81,82]},"packages/@n8n/instance-ai/src/tools/orchestration/eval-data-agent.tool.ts":{"12":[36,78,80,100,38,81,82],"26":[36,78,80,100,38,81,82],"27":[36,78,80,100,38,81,82],"74":[36,78,80,100,38,81,82],"79":[36,78,80,100,38,81,82],"80":[36,78,80,100,38,81,82],"89":[36,78,80,100,38,81,82],"102":[36,78,80,100,38,81,82],"108":[36,78,80,100,38,81,82],"111":[36,78,80,100,38,81,82]},"packages/@n8n/instance-ai/src/tools/orchestration/eval-setup-agent.tool.ts":{"48":[36,78,80,100,38,81,82],"94":[36,78,80,100,38,81,82],"137":[36,78,80,100,38,81,82],"138":[36,78,80,100,38,81,82],"259":[36,78,80,100,38,81,82],"275":[36,78,80,100,38,81,82]},"packages/@n8n/instance-ai/src/tools/orchestration/plan.tool.ts":{"53":[36,78,80,100,38,81,82],"68":[36,78,80,100,38,81,82],"69":[36,78,80,100,38,81,82],"74":[36,78,80,100,38,81,82],"75":[36,78,80,100,38,81,82],"78":[36,78,80,100,38,81,82],"99":[36,78,80,100,38,81,82],"149":[36,78,80,100,38,81,82],"172":[36,78,80,100,38,81,82],"255":[36,78,80,100,38,81,82]},"packages/@n8n/instance-ai/src/tools/orchestration/report-verification-verdict.tool.ts":{"74":[36,78,80,100,38,81,82],"107":[36,78,80,100,38,81,82],"120":[36,78,80,100,38,81,82]},"packages/@n8n/instance-ai/src/tools/orchestration/tracing-utils.ts":{"27":[36,78,80,100,38,81,82],"50":[36,78,80,100,38,81,82],"57":[36,78,80,100,38,81,82],"61":[36,78,80,100,38,81,82],"90":[36,78,80,100,38,81,82],"137":[36,78,80,100,38,81,82],"150":[36,78,80,100,38,81,82],"162":[36,78,80,100,38,81,82],"173":[36,78,80,100,38,81,82],"182":[36,78,80,100,38,81,82]},"packages/@n8n/instance-ai/src/tools/orchestration/verification/analyze-result.ts":{"28":[36,78,80,100,38,81,40,82],"37":[36,78,80,100,38,81,40,82],"51":[36,78,80,100,38,81,40,82],"55":[36,78,80,100,38,81,40,82],"68":[36,78,80,100,38,81,40,82],"77":[36,78,80,100,38,81,40,82],"88":[36,78,80,100,38,81,40,82],"95":[36,78,80,100,38,81,40,82],"109":[36,78,80,100,38,81,40,82],"121":[36,78,80,100,38,81,40,82],"132":[36,78,80,100,38,81,40,82],"133":[36,78,80,100,38,81,40,82],"136":[36,78,80,100,38,81,40,82],"202":[36,78,80,100,38,81,40,82],"209":[36,78,80,100,38,81,40,82],"223":[36,78,80,100,38,81,40,82],"259":[36,78,80,100,38,81,40,82],"263":[36,78,80,100,38,81,40,82],"271":[36,78,80,100,38,81,40,82],"292":[36,78,80,100,38,81,40,82],"306":[36,78,80,100,38,81,40,82],"308":[36,78,80,100,38,81,40,82],"309":[36,78,80,100,38,81,40,82],"343":[36,78,80,100,38,81,40,82]},"packages/@n8n/instance-ai/src/tools/orchestration/verification/finalize-result.ts":{"21":[36,78,80,100,38,81,40,82],"84":[36,78,80,100,38,81,40,82],"135":[36,78,80,100,38,81,40,82]},"packages/@n8n/instance-ai/src/tools/orchestration/verification/prepare-run.ts":{"17":[36,78,80,100,38,81,40,82],"25":[36,78,80,100,38,81,40,82],"26":[36,78,80,100,38,81,40,82],"29":[36,78,80,100,38,81,40,82],"40":[36,78,80,100,38,81,40,82],"69":[36,78,80,100,38,81,40,82],"81":[36,78,80,100,38,81,40,82],"87":[36,78,80,100,38,81,40,82],"89":[36,78,80,100,38,81,40,82]},"packages/@n8n/instance-ai/src/tools/orchestration/verification/reconcile-plan.ts":{"15":[36,78,80,100,38,81,40,82],"36":[36,78,80,100,38,81,40,82]},"packages/@n8n/instance-ai/src/tools/orchestration/verification/resolve-target.ts":{"29":[36,78,80,100,38,81,40,82]},"packages/@n8n/instance-ai/src/tools/orchestration/verification/scripted-gate-run.ts":{"45":[36,78,80,100,38,81,82],"72":[36,78,80,100,38,81,82],"74":[36,78,80,100,38,81,82],"99":[36,78,80,100,38,81,82],"108":[36,78,80,100,38,81,82],"110":[36,78,80,100,38,81,82],"111":[36,78,80,100,38,81,82],"112":[36,78,80,100,38,81,82],"113":[36,78,80,100,38,81,82],"121":[36,78,80,100,38,81,82],"138":[36,78,80,100,38,81,82],"143":[36,78,80,100,38,81,82]},"packages/@n8n/instance-ai/src/tools/orchestration/verify-built-workflow.tool.ts":{"115":[36,78,80,100,38,81,40,82],"126":[36,78,80,100,38,81,40,82],"176":[36,78,80,100,38,81,40,82],"209":[36,78,80,100,38,81,40,82]},"packages/@n8n/instance-ai/src/tools/research.tool.ts":{"20":[36,78,80,100,38,81,82],"88":[36,78,80,100,38,81,82],"143":[36,78,80,100,38,81,82],"220":[36,78,80,100,38,81,82],"249":[36,78,80,100,38,81,82],"262":[36,78,80,100,38,81,82]},"packages/@n8n/instance-ai/src/tools/shared/ask-user.tool.ts":{"5":[36,78,80,100,38,81,82],"41":[36,78,80,100,38,81,82],"85":[36,78,80,100,38,81,82],"106":[36,78,80,100,38,81,82],"108":[36,78,80,100,38,81,82]},"packages/@n8n/instance-ai/src/tools/task-control.tool.ts":{"43":[36,78,80,100,38,81,82],"58":[36,78,80,100,38,81,82],"69":[36,78,80,100,38,81,82],"96":[36,78,80,100,38,81,82],"102":[36,78,80,100,38,81,82]},"packages/@n8n/instance-ai/src/tools/web-research/sanitize-web-content.ts":{"12":[36,78,80,100,38,81,40,82],"27":[36,78,80,100,38,81,40,82],"32":[36,78,80,100,38,81,40,82],"47":[36,78,80,100,38,81,40,82]},"packages/@n8n/instance-ai/src/tools/workflows/apply-workflow-credentials.tool.ts":{"24":[36,78,80,100,38,81,82],"38":[36,78,80,100,38,81,82]},"packages/@n8n/instance-ai/src/tools/workflows/build-workflow.tool.ts":{"98":[36,78,80,100,38,81,39,40,82],"188":[36,78,80,100,38,81,39,40,82],"206":[36,78,80,100,38,81,39,40,82],"239":[36,78,80,100,38,81,39,40,82],"249":[36,78,80,100,38,81,39,40,82],"265":[36,78,80,100,38,81,39,40,82],"280":[36,78,80,100,38,81,39,40,82],"322":[36,78,80,100,38,81,39,40,82],"549":[36,78,80,100,38,81,39,40,82],"648":[36,78,80,100,38,81,39,40,82],"687":[36,78,80,100,38,81,39,40,82],"751":[36,78,80,100,38,81,39,40,82],"752":[36,78,80,100,38,81,39,40,82],"754":[36,78,80,100,38,81,39,40,82],"757":[36,78,80,100,38,81,39,40,82],"758":[36,78,80,100,38,81,39,40,82],"763":[36,78,80,100,38,81,39,40,82],"775":[36,78,80,100,38,81,39,40,82],"900":[36,78,80,100,38,81,39,40,82]},"packages/@n8n/instance-ai/src/tools/workflows/classify-node-destructiveness.service.ts":{"81":[36,78,80,100,38,81,39,40,82],"180":[36,78,80,100,38,81,39,40,82],"192":[36,78,80,100,38,81,39,40,82],"208":[36,78,80,100,38,81,39,40,82],"216":[36,78,80,100,38,81,39,40,82],"291":[36,78,80,100,38,81,39,40,82],"320":[36,78,80,100,38,81,39,40,82],"351":[36,78,80,100,38,81,39,40,82],"358":[36,78,80,100,38,81,39,40,82],"367":[36,78,80,100,38,81,39,40,82],"379":[36,78,80,100,38,81,39,40,82],"392":[36,78,80,100,38,81,39,40,82],"402":[36,78,80,100,38,81,39,40,82],"409":[36,78,80,100,38,81,39,40,82],"445":[36,78,80,100,38,81,39,40,82],"446":[36,78,80,100,38,81,39,40,82]},"packages/@n8n/instance-ai/src/tools/workflows/credential-utils.ts":{"34":[36,78,80,100,38,81,82],"45":[36,78,80,100,38,81,82],"61":[36,78,80,100,38,81,82],"94":[36,78,80,100,38,81,82]},"packages/@n8n/instance-ai/src/tools/workflows/detect-array-input-collapse.ts":{"31":[36,78,80,100,38,81,39,40,82],"44":[36,78,80,100,38,81,39,40,82],"55":[36,78,80,100,38,81,39,40,82],"78":[36,78,80,100,38,81,39,40,82],"90":[36,78,80,100,38,81,39,40,82]},"packages/@n8n/instance-ai/src/tools/workflows/detect-unparseable-openai-schema.ts":{"14":[36,78,80,100,38,81,39,40,82],"38":[36,78,80,100,38,81,39,40,82]},"packages/@n8n/instance-ai/src/tools/workflows/detect-wrong-kind-locator.ts":{"6":[36,78,80,100,38,81,39,40,82],"10":[36,78,80,100,38,81,39,40,82],"11":[36,78,80,100,38,81,39,40,82],"15":[36,78,80,100,38,81,39,40,82],"18":[36,78,80,100,38,81,39,40,82],"21":[36,78,80,100,38,81,39,40,82],"22":[36,78,80,100,38,81,39,40,82],"25":[36,78,80,100,38,81,39,40,82],"34":[36,78,80,100,38,81,39,40,82],"39":[36,78,80,100,38,81,39,40,82],"48":[36,78,80,100,38,81,39,40,82],"68":[36,78,80,100,38,81,39,40,82],"114":[36,78,80,100,38,81,39,40,82]},"packages/@n8n/instance-ai/src/tools/workflows/generate-simulation-fixtures.service.ts":{"89":[36,78,80,100,38,81,82],"116":[36,78,80,100,38,81,82],"125":[36,78,80,100,38,81,82],"126":[36,78,80,100,38,81,82],"129":[36,78,80,100,38,81,82],"137":[36,78,80,100,38,81,82],"142":[36,78,80,100,38,81,82],"143":[36,78,80,100,38,81,82],"151":[36,78,80,100,38,81,82],"155":[36,78,80,100,38,81,82],"160":[36,78,80,100,38,81,82],"163":[36,78,80,100,38,81,82],"170":[36,78,80,100,38,81,82],"180":[36,78,80,100,38,81,82],"192":[36,78,80,100,38,81,82],"226":[36,78,80,100,38,81,82]},"packages/@n8n/instance-ai/src/tools/workflows/plan-verification-simulation.ts":{"74":[36,78,80,100,38,81,39,40,82],"78":[36,78,80,100,38,81,39,40,82],"111":[36,78,80,100,38,81,39,40,82],"117":[36,78,80,100,38,81,39,40,82],"120":[36,78,80,100,38,81,39,40,82],"146":[36,78,80,100,38,81,39,40,82],"153":[36,78,80,100,38,81,39,40,82],"162":[36,78,80,100,38,81,39,40,82],"179":[36,78,80,100,38,81,39,40,82],"186":[36,78,80,100,38,81,39,40,82],"189":[36,78,80,100,38,81,39,40,82],"195":[36,78,80,100,38,81,39,40,82],"207":[36,78,80,100,38,81,39,40,82],"232":[36,78,80,100,38,81,39,40,82],"238":[36,78,80,100,38,81,39,40,82],"241":[36,78,80,100,38,81,39,40,82],"244":[36,78,80,100,38,81,39,40,82],"254":[36,78,80,100,38,81,39,40,82],"259":[36,78,80,100,38,81,39,40,82],"263":[36,78,80,100,38,81,39,40,82],"270":[36,78,80,100,38,81,39,40,82],"271":[36,78,80,100,38,81,39,40,82],"297":[36,78,80,100,38,81,39,40,82],"328":[36,78,80,100,38,81,39,40,82],"348":[36,78,80,100,38,81,39,40,82],"349":[36,78,80,100,38,81,39,40,82],"350":[36,78,80,100,38,81,39,40,82]},"packages/@n8n/instance-ai/src/tools/workflows/reconcile-simulation-plan.ts":{"40":[36,78,80,100,38,81,82],"50":[36,78,80,100,38,81,82],"54":[36,78,80,100,38,81,82],"58":[36,78,80,100,38,81,82],"76":[36,78,80,100,38,81,82],"88":[36,78,80,100,38,81,82],"94":[36,78,80,100,38,81,82],"99":[36,78,80,100,38,81,82],"102":[36,78,80,100,38,81,82],"104":[36,78,80,100,38,81,82],"105":[36,78,80,100,38,81,82],"115":[36,78,80,100,38,81,82],"120":[36,78,80,100,38,81,82],"132":[36,78,80,100,38,81,82],"140":[36,78,80,100,38,81,82],"147":[36,78,80,100,38,81,82],"166":[36,78,80,100,38,81,82],"188":[36,78,80,100,38,81,82]},"packages/@n8n/instance-ai/src/tools/workflows/resolve-credentials.ts":{"36":[36,78,80,100,38,81,39,40,82],"74":[36,78,80,100,38,81,39,40,82],"119":[36,78,80,100,38,81,39,40,82],"132":[36,78,80,100,38,81,39,40,82],"138":[36,78,80,100,38,81,39,40,82],"172":[36,78,80,100,38,81,39,40,82],"178":[36,78,80,100,38,81,39,40,82],"190":[36,78,80,100,38,81,39,40,82],"211":[36,78,80,100,38,81,39,40,82],"227":[36,78,80,100,38,81,39,40,82],"317":[36,78,80,100,38,81,39,40,82],"326":[36,78,80,100,38,81,39,40,82],"335":[36,78,80,100,38,81,39,40,82],"346":[36,78,80,100,38,81,39,40,82],"355":[36,78,80,100,38,81,39,40,82]},"packages/@n8n/instance-ai/src/tools/workflows/setup-workflow.service.ts":{"42":[36,78,80,100,38,81,39,40,82],"46":[36,78,80,100,38,81,39,40,82],"59":[36,78,80,100,38,81,39,40,82],"138":[36,78,80,100,38,81,39,40,82],"159":[36,78,80,100,38,81,39,40,82],"164":[36,78,80,100,38,81,39,40,82],"171":[36,78,80,100,38,81,39,40,82],"181":[36,78,80,100,38,81,39,40,82],"200":[36,78,80,100,38,81,39,40,82],"208":[36,78,80,100,38,81,39,40,82],"235":[36,78,80,100,38,81,39,40,82],"251":[36,78,80,100,38,81,39,40,82],"262":[36,78,80,100,38,81,39,40,82],"269":[36,78,80,100,38,81,39,40,82],"270":[36,78,80,100,38,81,39,40,82],"310":[36,78,80,100,38,81,39,40,82],"326":[36,78,80,100,38,81,39,40,82],"330":[36,78,80,100,38,81,39,40,82],"351":[36,78,80,100,38,81,39,40,82],"379":[36,78,80,100,38,81,39,40,82],"388":[36,78,80,100,38,81,39,40,82],"401":[36,78,80,100,38,81,39,40,82],"442":[36,78,80,100,38,81,39,40,82],"489":[36,78,80,100,38,81,39,40,82],"500":[36,78,80,100,38,81,39,40,82],"502":[36,78,80,100,38,81,39,40,82],"568":[36,78,80,100,38,81,39,40,82],"583":[36,78,80,100,38,81,39,40,82],"646":[36,78,80,100,38,81,39,40,82],"668":[36,78,80,100,38,81,39,40,82],"689":[36,78,80,100,38,81,39,40,82],"709":[36,78,80,100,38,81,39,40,82],"716":[36,78,80,100,38,81,39,40,82],"717":[36,78,80,100,38,81,39,40,82],"722":[36,78,80,100,38,81,39,40,82],"750":[36,78,80,100,38,81,39,40,82],"757":[36,78,80,100,38,81,39,40,82],"771":[36,78,80,100,38,81,39,40,82],"777":[36,78,80,100,38,81,39,40,82],"794":[36,78,80,100,38,81,39,40,82],"845":[36,78,80,100,38,81,39,40,82],"902":[36,78,80,100,38,81,39,40,82],"918":[36,78,80,100,38,81,39,40,82],"933":[36,78,80,100,38,81,39,40,82],"972":[36,78,80,100,38,81,39,40,82],"997":[36,78,80,100,38,81,39,40,82],"1059":[36,78,80,100,38,81,39,40,82],"1113":[36,78,80,100,38,81,39,40,82],"1133":[36,78,80,100,38,81,39,40,82],"1148":[36,78,80,100,38,81,39,40,82],"1187":[36,78,80,100,38,81,39,40,82],"1196":[36,78,80,100,38,81,39,40,82],"1210":[36,78,80,100,38,81,39,40,82],"1219":[36,78,80,100,38,81,39,40,82],"1233":[36,78,80,100,38,81,39,40,82]},"packages/@n8n/instance-ai/src/tools/workflows/summarize-workflow.ts":{"13":[36,78,80,100,38,81,82],"19":[36,78,80,100,38,81,82],"30":[36,78,80,100,38,81,82],"36":[36,78,80,100,38,81,82],"45":[36,78,80,100,38,81,82],"51":[36,78,80,100,38,81,82],"70":[36,78,80,100,38,81,82],"79":[36,78,80,100,38,81,82],"91":[36,78,80,100,38,81,82],"93":[36,78,80,100,38,81,82]},"packages/@n8n/instance-ai/src/tools/workflows/validate-workflow.service.ts":{"45":[36,78,80,100,38,81,82],"49":[36,78,80,100,38,81,82],"61":[36,78,80,100,38,81,82],"62":[36,78,80,100,38,81,82],"69":[36,78,80,100,38,81,82],"74":[36,78,80,100,38,81,82],"81":[36,78,80,100,38,81,82],"86":[36,78,80,100,38,81,82],"91":[36,78,80,100,38,81,82],"100":[36,78,80,100,38,81,82],"113":[36,78,80,100,38,81,82],"137":[36,78,80,100,38,81,82],"185":[36,78,80,100,38,81,82],"231":[36,78,80,100,38,81,82],"235":[36,78,80,100,38,81,82],"256":[36,78,80,100,38,81,82],"302":[36,78,80,100,38,81,82],"317":[36,78,80,100,38,81,82],"334":[36,78,80,100,38,81,82],"418":[36,78,80,100,38,81,82],"428":[36,78,80,100,38,81,82],"455":[36,78,80,100,38,81,82],"465":[36,78,80,100,38,81,82],"474":[36,78,80,100,38,81,82],"478":[36,78,80,100,38,81,82],"499":[36,78,80,100,38,81,82],"519":[36,78,80,100,38,81,82],"594":[36,78,80,100,38,81,82],"631":[36,78,80,100,38,81,82],"672":[36,78,80,100,38,81,82],"690":[36,78,80,100,38,81,82]},"packages/@n8n/instance-ai/src/tools/workflows/wait-gate-script.ts":{"34":[36,78,80,100,38,81,39,40,82],"40":[36,78,80,100,38,81,39,40,82],"56":[36,78,80,100,38,81,39,40,82],"67":[36,78,80,100,38,81,39,40,82],"83":[36,78,80,100,38,81,39,40,82],"105":[36,78,80,100,38,81,39,40,82],"109":[36,78,80,100,38,81,39,40,82],"112":[36,78,80,100,38,81,39,40,82],"120":[36,78,80,100,38,81,39,40,82],"136":[36,78,80,100,38,81,39,40,82],"144":[36,78,80,100,38,81,39,40,82],"145":[36,78,80,100,38,81,39,40,82],"156":[36,78,80,100,38,81,39,40,82],"172":[36,78,80,100,38,81,39,40,82],"176":[36,78,80,100,38,81,39,40,82],"181":[36,78,80,100,38,81,39,40,82],"201":[36,78,80,100,38,81,39,40,82],"229":[36,78,80,100,38,81,39,40,82],"237":[36,78,80,100,38,81,39,40,82],"259":[36,78,80,100,38,81,39,40,82],"263":[36,78,80,100,38,81,39,40,82],"267":[36,78,80,100,38,81,39,40,82]},"packages/@n8n/instance-ai/src/tools/workflows/workflow-build-context.ts":{"10":[36,78,80,37,100,38,81,39,40,82],"15":[36,78,80,37,100,38,81,39,40,82],"26":[36,78,80,37,100,38,81,39,40,82],"56":[36,78,80,37,100,38,81,39,40,82],"96":[36,78,80,37,100,38,81,39,40,82],"100":[36,78,80,37,100,38,81,39,40,82],"101":[36,78,80,37,100,38,81,39,40,82],"105":[36,78,80,37,100,38,81,39,40,82],"112":[36,78,80,37,100,38,81,39,40,82]},"packages/@n8n/instance-ai/src/tools/workflows/workflow-build-remediation.ts":{"6":[36,78,80,100,38,81,39,82],"11":[36,78,80,100,38,81,39,82],"15":[36,78,80,100,38,81,39,82],"30":[36,78,80,100,38,81,39,82],"40":[36,78,80,100,38,81,39,82],"52":[36,78,80,100,38,81,39,82],"60":[36,78,80,100,38,81,39,82],"107":[36,78,80,100,38,81,39,82]},"packages/@n8n/instance-ai/src/tools/workflows/workflow-build-reporting.ts":{"8":[36,78,80,100,38,81,39,40,82],"54":[36,78,80,100,38,81,39,40,82],"69":[36,78,80,100,38,81,39,40,82]},"packages/@n8n/instance-ai/src/tools/workflows/workflow-build-routing.ts":{"15":[36,78,80,100,38,81,39,40,82],"24":[36,78,80,100,38,81,39,40,82],"47":[36,78,80,100,38,81,39,40,82],"58":[36,78,80,100,38,81,39,40,82],"100":[36,78,80,100,38,81,39,40,82]},"packages/@n8n/instance-ai/src/tools/workflows/workflow-build-telemetry.ts":{"16":[36,78,80,100,38,81,39,40,82],"66":[36,78,80,100,38,81,39,40,82]},"packages/@n8n/instance-ai/src/tools/workflows/workflow-file-bindings.ts":{"25":[36,78,80,37,100,38,81,39,40,82],"29":[36,78,80,37,100,38,81,39,40,82],"33":[36,78,80,37,100,38,81,39,40,82],"38":[36,78,80,37,100,38,81,39,40,82],"48":[36,78,80,37,100,38,81,39,40,82],"64":[36,78,80,37,100,38,81,39,40,82],"79":[36,78,80,37,100,38,81,39,40,82],"92":[36,78,80,37,100,38,81,39,40,82],"112":[36,78,80,37,100,38,81,39,40,82],"127":[36,78,80,37,100,38,81,39,40,82],"139":[36,78,80,37,100,38,81,39,40,82],"156":[36,78,80,37,100,38,81,39,40,82],"176":[36,78,80,37,100,38,81,39,40,82]},"packages/@n8n/instance-ai/src/tools/workflows/workflow-json-utils.ts":{"37":[36,78,80,37,100,38,81,39,40,82],"46":[36,78,80,37,100,38,81,39,40,82],"58":[36,78,80,37,100,38,81,39,40,82],"68":[36,78,80,37,100,38,81,39,40,82],"100":[36,78,80,37,100,38,81,39,40,82],"110":[36,78,80,37,100,38,81,39,40,82],"114":[36,78,80,37,100,38,81,39,40,82],"137":[36,78,80,37,100,38,81,39,40,82],"173":[36,78,80,37,100,38,81,39,40,82],"184":[36,78,80,37,100,38,81,39,40,82],"204":[36,78,80,37,100,38,81,39,40,82],"208":[36,78,80,37,100,38,81,39,40,82],"214":[36,78,80,37,100,38,81,39,40,82],"225":[36,78,80,37,100,38,81,39,40,82],"229":[36,78,80,37,100,38,81,39,40,82],"237":[36,78,80,37,100,38,81,39,40,82],"245":[36,78,80,37,100,38,81,39,40,82],"263":[36,78,80,37,100,38,81,39,40,82],"279":[36,78,80,37,100,38,81,39,40,82],"295":[36,78,80,37,100,38,81,39,40,82],"315":[36,78,80,37,100,38,81,39,40,82],"316":[36,78,80,37,100,38,81,39,40,82]},"packages/@n8n/instance-ai/src/tools/workflows/workflow-source-compiler.ts":{"50":[36,78,80,100,38,81,39,40,82],"59":[36,78,80,100,38,81,39,40,82],"64":[36,78,80,100,38,81,39,40,82],"72":[36,78,80,100,38,81,39,40,82],"74":[36,78,80,100,38,81,39,40,82],"77":[36,78,80,100,38,81,39,40,82],"98":[36,78,80,100,38,81,39,40,82],"127":[36,78,80,100,38,81,39,40,82],"151":[36,78,80,100,38,81,39,40,82],"153":[36,78,80,100,38,81,39,40,82],"156":[36,78,80,100,38,81,39,40,82],"162":[36,78,80,100,38,81,39,40,82],"166":[36,78,80,100,38,81,39,40,82],"181":[36,78,80,100,38,81,39,40,82],"185":[36,78,80,100,38,81,39,40,82],"209":[36,78,80,100,38,81,39,40,82],"210":[36,78,80,100,38,81,39,40,82],"230":[36,78,80,100,38,81,39,40,82],"312":[36,78,80,100,38,81,39,40,82],"348":[36,78,80,100,38,81,39,40,82]},"packages/@n8n/instance-ai/src/tools/workflows/workflow-validation-warnings.ts":{"11":[36,78,80,100,38,81,39,40,82],"30":[36,78,80,100,38,81,39,40,82]},"packages/@n8n/instance-ai/src/tools/workflows.tool.ts":{"282":[36,78,80,37,100,38,81,39,82],"286":[36,78,80,37,100,38,81,39,82],"315":[36,78,80,37,100,38,81,39,82],"321":[36,78,80,37,100,38,81,39,82],"325":[36,78,80,37,100,38,81,39,82],"355":[36,78,80,37,100,38,81,39,82],"361":[36,78,80,37,100,38,81,39,82],"362":[36,78,80,37,100,38,81,39,82],"365":[36,78,80,37,100,38,81,39,82],"375":[36,78,80,37,100,38,81,39,82],"412":[36,78,80,37,100,38,81,39,82],"413":[36,78,80,37,100,38,81,39,82],"426":[36,78,80,37,100,38,81,39,82],"441":[36,78,80,37,100,38,81,39,82],"464":[36,78,80,37,100,38,81,39,82],"497":[36,78,80,37,100,38,81,39,82],"532":[36,78,80,37,100,38,81,39,82],"554":[36,78,80,37,100,38,81,39,82],"576":[36,78,80,37,100,38,81,39,82],"597":[36,78,80,37,100,38,81,39,82],"623":[36,78,80,37,100,38,81,39,82],"643":[36,78,80,37,100,38,81,39,82],"659":[36,78,80,37,100,38,81,39,82],"672":[36,78,80,37,100,38,81,39,82],"673":[36,78,80,37,100,38,81,39,82],"678":[36,78,80,37,100,38,81,39,82],"709":[36,78,80,37,100,38,81,39,82],"774":[36,78,80,37,100,38,81,39,82],"794":[36,78,80,37,100,38,81,39,82],"803":[36,78,80,37,100,38,81,39,82],"848":[36,78,80,37,100,38,81,39,82],"935":[36,78,80,37,100,38,81,39,82],"949":[36,78,80,37,100,38,81,39,82],"980":[36,78,80,37,100,38,81,39,82],"991":[36,78,80,37,100,38,81,39,82],"998":[36,78,80,37,100,38,81,39,82],"1005":[36,78,80,37,100,38,81,39,82],"1043":[36,78,80,37,100,38,81,39,82],"1054":[36,78,80,37,100,38,81,39,82],"1071":[36,78,80,37,100,38,81,39,82],"1100":[36,78,80,37,100,38,81,39,82],"1148":[36,78,80,37,100,38,81,39,82],"1153":[36,78,80,37,100,38,81,39,82],"1154":[36,78,80,37,100,38,81,39,82],"1161":[36,78,80,37,100,38,81,39,82],"1176":[36,78,80,37,100,38,81,39,82],"1194":[36,78,80,37,100,38,81,39,82]},"packages/@n8n/instance-ai/src/tools/workspace.tool.ts":{"108":[36,78,80,100,38,81,82],"143":[36,78,80,100,38,81,82],"148":[36,78,80,100,38,81,82],"153":[36,78,80,100,38,81,82],"185":[36,78,80,100,38,81,82],"220":[36,78,80,100,38,81,82],"228":[36,78,80,100,38,81,82],"276":[36,78,80,100,38,81,82],"312":[36,78,80,100,38,81,82],"346":[36,78,80,100,38,81,82],"353":[36,78,80,100,38,81,82],"367":[36,78,80,100,38,81,82]},"packages/@n8n/instance-ai/src/tracing/langsmith-tracing.ts":{"98":[36,78,80,37,100,38,81,39,40,82],"106":[36,78,80,37,100,38,81,39,40,82],"109":[36,78,80,37,100,38,81,39,40,82],"117":[36,78,80,37,100,38,81,39,40,82],"122":[36,78,80,37,100,38,81,39,40,82],"126":[36,78,80,37,100,38,81,39,40,82],"132":[36,78,80,37,100,38,81,39,40,82],"133":[36,78,80,37,100,38,81,39,40,82],"146":[36,78,80,37,100,38,81,39,40,82],"148":[36,78,80,37,100,38,81,39,40,82],"158":[36,78,80,37,100,38,81,39,40,82],"195":[36,78,80,37,100,38,81,39,40,82],"203":[36,78,80,37,100,38,81,39,40,82],"210":[36,78,80,37,100,38,81,39,40,82],"215":[36,78,80,37,100,38,81,39,40,82],"219":[36,78,80,37,100,38,81,39,40,82],"232":[36,78,80,37,100,38,81,39,40,82],"252":[36,78,80,37,100,38,81,39,40,82],"258":[36,78,80,37,100,38,81,39,40,82],"284":[36,78,80,37,100,38,81,39,40,82],"322":[36,78,80,37,100,38,81,39,40,82],"330":[36,78,80,37,100,38,81,39,40,82],"395":[36,78,80,37,100,38,81,39,40,82],"460":[36,78,80,37,100,38,81,39,40,82],"482":[36,78,80,37,100,38,81,39,40,82],"496":[36,78,80,37,100,38,81,39,40,82],"506":[36,78,80,37,100,38,81,39,40,82],"522":[36,78,80,37,100,38,81,39,40,82],"534":[36,78,80,37,100,38,81,39,40,82],"538":[36,78,80,37,100,38,81,39,40,82],"545":[36,78,80,37,100,38,81,39,40,82],"557":[36,78,80,37,100,38,81,39,40,82],"584":[36,78,80,37,100,38,81,39,40,82],"590":[36,78,80,37,100,38,81,39,40,82],"602":[36,78,80,37,100,38,81,39,40,82],"617":[36,78,80,37,100,38,81,39,40,82],"636":[36,78,80,37,100,38,81,39,40,82],"655":[36,78,80,37,100,38,81,39,40,82],"664":[36,78,80,37,100,38,81,39,40,82],"667":[36,78,80,37,100,38,81,39,40,82],"740":[36,78,80,37,100,38,81,39,40,82],"748":[36,78,80,37,100,38,81,39,40,82],"752":[36,78,80,37,100,38,81,39,40,82],"776":[36,78,80,37,100,38,81,39,40,82],"783":[36,78,80,37,100,38,81,39,40,82],"787":[36,78,80,37,100,38,81,39,40,82],"788":[36,78,80,37,100,38,81,39,40,82],"793":[36,78,80,37,100,38,81,39,40,82],"814":[36,78,80,37,100,38,81,39,40,82],"841":[36,78,80,37,100,38,81,39,40,82],"852":[36,78,80,37,100,38,81,39,40,82],"867":[36,78,80,37,100,38,81,39,40,82],"887":[36,78,80,37,100,38,81,39,40,82],"892":[36,78,80,37,100,38,81,39,40,82],"899":[36,78,80,37,100,38,81,39,40,82],"915":[36,78,80,37,100,38,81,39,40,82],"921":[36,78,80,37,100,38,81,39,40,82],"929":[36,78,80,37,100,38,81,39,40,82],"946":[36,78,80,37,100,38,81,39,40,82],"991":[36,78,80,37,100,38,81,39,40,82],"1011":[36,78,80,37,100,38,81,39,40,82],"1017":[36,78,80,37,100,38,81,39,40,82],"1023":[36,78,80,37,100,38,81,39,40,82],"1071":[36,78,80,37,100,38,81,39,40,82],"1115":[36,78,80,37,100,38,81,39,40,82],"1130":[36,78,80,37,100,38,81,39,40,82],"1169":[36,78,80,37,100,38,81,39,40,82],"1182":[36,78,80,37,100,38,81,39,40,82],"1201":[36,78,80,37,100,38,81,39,40,82],"1213":[36,78,80,37,100,38,81,39,40,82],"1217":[36,78,80,37,100,38,81,39,40,82],"1236":[36,78,80,37,100,38,81,39,40,82],"1239":[36,78,80,37,100,38,81,39,40,82],"1243":[36,78,80,37,100,38,81,39,40,82],"1250":[36,78,80,37,100,38,81,39,40,82],"1260":[36,78,80,37,100,38,81,39,40,82],"1265":[36,78,80,37,100,38,81,39,40,82],"1273":[36,78,80,37,100,38,81,39,40,82],"1283":[36,78,80,37,100,38,81,39,40,82],"1297":[36,78,80,37,100,38,81,39,40,82],"1300":[36,78,80,37,100,38,81,39,40,82],"1305":[36,78,80,37,100,38,81,39,40,82],"1320":[36,78,80,37,100,38,81,39,40,82],"1329":[36,78,80,37,100,38,81,39,40,82],"1332":[36,78,80,37,100,38,81,39,40,82],"1336":[36,78,80,37,100,38,81,39,40,82],"1340":[36,78,80,37,100,38,81,39,40,82],"1365":[36,78,80,37,100,38,81,39,40,82],"1373":[36,78,80,37,100,38,81,39,40,82],"1389":[36,78,80,37,100,38,81,39,40,82],"1397":[36,78,80,37,100,38,81,39,40,82],"1409":[36,78,80,37,100,38,81,39,40,82],"1440":[36,78,80,37,100,38,81,39,40,82],"1447":[36,78,80,37,100,38,81,39,40,82],"1455":[36,78,80,37,100,38,81,39,40,82],"1500":[36,78,80,37,100,38,81,39,40,82],"1524":[36,78,80,37,100,38,81,39,40,82],"1544":[36,78,80,37,100,38,81,39,40,82],"1545":[36,78,80,37,100,38,81,39,40,82],"1546":[36,78,80,37,100,38,81,39,40,82],"1547":[36,78,80,37,100,38,81,39,40,82],"1548":[36,78,80,37,100,38,81,39,40,82],"1549":[36,78,80,37,100,38,81,39,40,82],"1550":[36,78,80,37,100,38,81,39,40,82],"1572":[36,78,80,37,100,38,81,39,40,82],"1581":[36,78,80,37,100,38,81,39,40,82],"1607":[36,78,80,37,100,38,81,39,40,82],"1608":[36,78,80,37,100,38,81,39,40,82],"1622":[36,78,80,37,100,38,81,39,40,82],"1647":[36,78,80,37,100,38,81,39,40,82],"1682":[36,78,80,37,100,38,81,39,40,82],"1691":[36,78,80,37,100,38,81,39,40,82],"1701":[36,78,80,37,100,38,81,39,40,82],"1737":[36,78,80,37,100,38,81,39,40,82],"1754":[36,78,80,37,100,38,81,39,40,82],"1774":[36,78,80,37,100,38,81,39,40,82],"1804":[36,78,80,37,100,38,81,39,40,82],"1814":[36,78,80,37,100,38,81,39,40,82],"1838":[36,78,80,37,100,38,81,39,40,82],"1860":[36,78,80,37,100,38,81,39,40,82],"1890":[36,78,80,37,100,38,81,39,40,82],"1941":[36,78,80,37,100,38,81,39,40,82],"1951":[36,78,80,37,100,38,81,39,40,82],"2000":[36,78,80,37,100,38,81,39,40,82],"2016":[36,78,80,37,100,38,81,39,40,82]},"packages/@n8n/instance-ai/src/tracing/trace-labels.ts":{"1":[36,78,80,100,38,81,82],"9":[36,78,80,100,38,81,82],"13":[36,78,80,100,38,81,82],"24":[36,78,80,100,38,81,82],"28":[36,78,80,100,38,81,82]},"packages/@n8n/instance-ai/src/tracing/trace-payloads.ts":{"60":[36,78,80,100,38,81,82],"77":[36,78,80,100,38,81,82],"111":[36,78,80,100,38,81,82],"119":[36,78,80,100,38,81,82],"133":[36,78,80,100,38,81,82],"134":[36,78,80,100,38,81,82],"136":[36,78,80,100,38,81,82],"139":[36,78,80,100,38,81,82],"142":[36,78,80,100,38,81,82],"155":[36,78,80,100,38,81,82],"177":[36,78,80,100,38,81,82],"200":[36,78,80,100,38,81,82],"214":[36,78,80,100,38,81,82],"226":[36,78,80,100,38,81,82],"262":[36,78,80,100,38,81,82],"275":[36,78,80,100,38,81,82],"290":[36,78,80,100,38,81,82],"297":[36,78,80,100,38,81,82],"304":[36,78,80,100,38,81,82],"320":[36,78,80,100,38,81,82],"347":[36,78,80,100,38,81,82],"381":[36,78,80,100,38,81,82],"418":[36,78,80,100,38,81,82],"443":[36,78,80,100,38,81,82],"445":[36,78,80,100,38,81,82],"449":[36,78,80,100,38,81,82],"451":[36,78,80,100,38,81,82],"458":[36,78,80,100,38,81,82],"462":[36,78,80,100,38,81,82],"472":[36,78,80,100,38,81,82],"487":[36,78,80,100,38,81,82],"513":[36,78,80,100,38,81,82],"544":[36,78,80,100,38,81,82],"555":[36,78,80,100,38,81,82],"570":[36,78,80,100,38,81,82],"584":[36,78,80,100,38,81,82],"591":[36,78,80,100,38,81,82],"604":[36,78,80,100,38,81,82],"622":[36,78,80,100,38,81,82],"644":[36,78,80,100,38,81,82],"672":[36,78,80,100,38,81,82],"724":[36,78,80,100,38,81,82],"769":[36,78,80,100,38,81,82],"806":[36,78,80,100,38,81,82],"819":[36,78,80,100,38,81,82],"827":[36,78,80,100,38,81,82],"860":[36,78,80,100,38,81,82],"865":[36,78,80,100,38,81,82],"875":[36,78,80,100,38,81,82],"884":[36,78,80,100,38,81,82],"895":[36,78,80,100,38,81,82],"932":[36,78,80,100,38,81,82],"966":[36,78,80,100,38,81,82],"975":[36,78,80,100,38,81,82],"990":[36,78,80,100,38,81,82],"994":[36,78,80,100,38,81,82],"1009":[36,78,80,100,38,81,82],"1036":[36,78,80,100,38,81,82],"1060":[36,78,80,100,38,81,82],"1067":[36,78,80,100,38,81,82],"1071":[36,78,80,100,38,81,82],"1079":[36,78,80,100,38,81,82],"1102":[36,78,80,100,38,81,82],"1120":[36,78,80,100,38,81,82],"1143":[36,78,80,100,38,81,82],"1170":[36,78,80,100,38,81,82],"1202":[36,78,80,100,38,81,82],"1211":[36,78,80,100,38,81,82],"1213":[36,78,80,100,38,81,82],"1228":[36,78,80,100,38,81,82],"1240":[36,78,80,100,38,81,82],"1250":[36,78,80,100,38,81,82],"1251":[36,78,80,100,38,81,82],"1257":[36,78,80,100,38,81,82],"1266":[36,78,80,100,38,81,82],"1279":[36,78,80,100,38,81,82],"1292":[36,78,80,100,38,81,82],"1314":[36,78,80,100,38,81,82],"1332":[36,78,80,100,38,81,82],"1344":[36,78,80,100,38,81,82],"1356":[36,78,80,100,38,81,82],"1369":[36,78,80,100,38,81,82]},"packages/@n8n/instance-ai/src/tracing/trace-replay.ts":{"45":[36,78,80,100,38,81,39,40,82],"61":[36,78,80,100,38,81,39,40,82],"76":[36,78,80,100,38,81,39,40,82],"96":[36,78,80,100,38,81,39,40,82],"137":[36,78,80,100,38,81,39,40,82],"152":[36,78,80,100,38,81,39,40,82],"158":[36,78,80,100,38,81,39,40,82],"162":[36,78,80,100,38,81,39,40,82],"200":[36,78,80,100,38,81,39,40,82],"224":[36,78,80,100,38,81,39,40,82],"240":[36,78,80,100,38,81,39,40,82],"262":[36,78,80,100,38,81,39,40,82],"267":[36,78,80,100,38,81,39,40,82],"275":[36,78,80,100,38,81,39,40,82],"279":[36,78,80,100,38,81,39,40,82],"303":[36,78,80,100,38,81,39,40,82],"312":[36,78,80,100,38,81,39,40,82],"328":[36,78,80,100,38,81,39,40,82],"346":[36,78,80,100,38,81,39,40,82],"364":[36,78,80,100,38,81,39,40,82],"368":[36,78,80,100,38,81,39,40,82],"369":[36,78,80,100,38,81,39,40,82],"377":[36,78,80,100,38,81,39,40,82],"383":[36,78,80,100,38,81,39,40,82]},"packages/@n8n/instance-ai/src/utils/agent-feature-enabled.ts":{"3":[36,78,80,100,38,81,82],"7":[36,78,80,100,38,81,82],"8":[36,78,80,100,38,81,82]},"packages/@n8n/instance-ai/src/utils/agent-tree.ts":{"4":[36,78,79,80,38,81,82],"12":[36,78,79,80,38,81,82]},"packages/@n8n/instance-ai/src/utils/eval-agents.ts":{"35":[36,78,80,100,38,81,82],"44":[36,78,80,100,38,81,82],"67":[36,78,80,100,38,81,82],"73":[36,78,80,100,38,81,82],"109":[36,78,80,100,38,81,82],"119":[36,78,80,100,38,81,82],"147":[36,78,80,100,38,81,82]},"packages/@n8n/instance-ai/src/utils/format-timestamp.ts":{"2":[36,78,80,100,38,81,82]},"packages/@n8n/instance-ai/src/utils/generate-validated-json.ts":{"33":[36,78,80,100,38,81,82],"39":[36,78,80,100,38,81,82]},"packages/@n8n/instance-ai/src/utils/stream-helpers.ts":{"20":[36,78,80,37,100,38,81,39,82],"46":[36,78,80,37,100,38,81,39,82],"50":[36,78,80,37,100,38,81,39,82]},"packages/@n8n/instance-ai/src/workflow-builder/build-failure-tracker.ts":{"7":[36,78,80,100,38,81,39,40,82],"9":[36,78,80,100,38,81,39,40,82],"36":[36,78,80,100,38,81,39,40,82],"63":[36,78,80,100,38,81,39,40,82],"85":[36,78,80,100,38,81,39,40,82]},"packages/@n8n/instance-ai/src/workflow-loop/guidance.ts":{"7":[36,78,80,100,38,81,82],"15":[36,78,80,100,38,81,82]},"packages/@n8n/instance-ai/src/workflow-loop/index.ts":{"61":[36,78,80,100,38,81,39,40,82]},"packages/@n8n/instance-ai/src/workflow-loop/remediation.ts":{"11":[36,78,80,37,100,38,81,39,82],"22":[36,78,80,37,100,38,81,39,82],"29":[36,78,80,37,100,38,81,39,82]},"packages/@n8n/instance-ai/src/workflow-loop/runtime.ts":{"10":[36,78,80,100,38,81,39,40,82],"29":[36,78,80,100,38,81,39,40,82],"31":[36,78,80,100,38,81,39,40,82],"46":[36,78,80,100,38,81,39,40,82]},"packages/@n8n/instance-ai/src/workflow-loop/setup-verification-policy.ts":{"3":[36,78,80,100,38,81,39,40,82],"9":[36,78,80,100,38,81,39,40,82],"13":[36,78,80,100,38,81,39,40,82],"22":[36,78,80,100,38,81,39,40,82]},"packages/@n8n/instance-ai/src/workflow-loop/verification-obligation.ts":{"36":[36,78,80,100,38,81,39,40,82],"46":[36,78,80,100,38,81,39,40,82],"54":[36,78,80,100,38,81,39,40,82],"58":[36,78,80,100,38,81,39,40,82],"69":[36,78,80,100,38,81,39,40,82],"103":[36,78,80,100,38,81,39,40,82],"112":[36,78,80,100,38,81,39,40,82],"159":[36,78,80,100,38,81,39,40,82],"163":[36,78,80,100,38,81,39,40,82],"195":[36,78,80,100,38,81,39,40,82],"207":[36,78,80,100,38,81,39,40,82]},"packages/@n8n/instance-ai/src/workflow-loop/workflow-loop-controller.ts":{"38":[36,78,80,100,38,81,39,40,82],"66":[36,78,80,100,38,81,39,40,82],"223":[36,78,80,100,38,81,39,40,82],"384":[36,78,80,100,38,81,39,40,82],"473":[36,78,80,100,38,81,39,40,82],"477":[36,78,80,100,38,81,39,40,82],"481":[36,78,80,100,38,81,39,40,82],"492":[36,78,80,100,38,81,39,40,82],"508":[36,78,80,100,38,81,39,40,82],"523":[36,78,80,100,38,81,39,40,82],"525":[36,78,80,100,38,81,39,40,82],"530":[36,78,80,100,38,81,39,40,82],"535":[36,78,80,100,38,81,39,40,82],"550":[36,78,80,100,38,81,39,40,82],"553":[36,78,80,100,38,81,39,40,82]},"packages/@n8n/instance-ai/src/workflow-loop/workflow-loop-state.ts":{"47":[36,78,79,80,38,81,39,40,82],"57":[36,78,79,80,38,81,39,40,82],"63":[36,78,79,80,38,81,39,40,82]},"packages/@n8n/instance-ai/src/workflow-loop/workflow-task-service.ts":{"11":[36,78,80,100,38,81,39,40,82],"22":[36,78,80,100,38,81,39,40,82],"29":[36,78,80,100,38,81,39,40,82],"33":[36,78,80,100,38,81,39,40,82],"37":[36,78,80,100,38,81,39,40,82],"42":[36,78,80,100,38,81,39,40,82],"47":[36,78,80,100,38,81,39,40,82],"48":[36,78,80,100,38,81,39,40,82],"49":[36,78,80,100,38,81,39,40,82],"54":[36,78,80,100,38,81,39,40,82],"59":[36,78,80,100,38,81,39,40,82]},"packages/@n8n/instance-ai/src/workspace/builder-templates-service.ts":{"144":[36,78,80,100,38,81,39,40,82],"158":[36,78,80,100,38,81,39,40,82],"162":[36,78,80,100,38,81,39,40,82],"166":[36,78,80,100,38,81,39,40,82],"171":[36,78,80,100,38,81,39,40,82],"194":[36,78,80,100,38,81,39,40,82],"202":[36,78,80,100,38,81,39,40,82],"213":[36,78,80,100,38,81,39,40,82],"221":[36,78,80,100,38,81,39,40,82],"226":[36,78,80,100,38,81,39,40,82],"235":[36,78,80,100,38,81,39,40,82],"280":[36,78,80,100,38,81,39,40,82],"289":[36,78,80,100,38,81,39,40,82],"298":[36,78,80,100,38,81,39,40,82],"308":[36,78,80,100,38,81,39,40,82],"344":[36,78,80,100,38,81,39,40,82],"366":[36,78,80,100,38,81,39,40,82],"446":[36,78,80,100,38,81,39,40,82],"479":[36,78,80,100,38,81,39,40,82],"511":[36,78,80,100,38,81,39,40,82],"517":[36,78,80,100,38,81,39,40,82],"526":[36,78,80,100,38,81,39,40,82],"535":[36,78,80,100,38,81,39,40,82],"541":[36,78,80,100,38,81,39,40,82],"546":[36,78,80,100,38,81,39,40,82],"561":[36,78,80,100,38,81,39,40,82],"569":[36,78,80,100,38,81,39,40,82],"586":[36,78,80,100,38,81,39,40,82],"603":[36,78,80,100,38,81,39,40,82]},"packages/@n8n/instance-ai/src/workspace/compute-workspace-content-hash.ts":{"4":[36,78,80,100,38,81,39,40,82],"6":[36,78,80,100,38,81,39,40,82]},"packages/@n8n/instance-ai/src/workspace/create-workspace.ts":{"38":[36,78,80,100,38,81,82],"39":[36,78,80,100,38,81,82],"40":[36,78,80,100,38,81,82],"41":[36,78,80,100,38,81,82],"45":[36,78,80,100,38,81,82],"49":[36,78,80,100,38,81,82],"67":[36,78,80,100,38,81,82],"79":[36,78,80,100,38,81,82],"162":[36,78,80,100,38,81,82]},"packages/@n8n/instance-ai/src/workspace/lazy-runtime-workspace.ts":{"54":[36,78,80,100,38,81,39,40,82],"71":[36,78,80,100,38,81,39,40,82],"72":[36,78,80,100,38,81,39,40,82],"84":[36,78,80,100,38,81,39,40,82],"86":[36,78,80,100,38,81,39,40,82],"90":[36,78,80,100,38,81,39,40,82],"95":[36,78,80,100,38,81,39,40,82],"99":[36,78,80,100,38,81,39,40,82],"101":[36,78,80,100,38,81,39,40,82],"105":[36,78,80,100,38,81,39,40,82],"119":[36,78,80,100,38,81,39,40,82],"123":[36,78,80,100,38,81,39,40,82],"129":[36,78,80,100,38,81,39,40,82],"141":[36,78,80,100,38,81,39,40,82],"150":[36,78,80,100,38,81,39,40,82],"159":[36,78,80,100,38,81,39,40,82],"168":[36,78,80,100,38,81,39,40,82],"181":[36,78,80,100,38,81,39,40,82],"186":[36,78,80,100,38,81,39,40,82],"189":[36,78,80,100,38,81,39,40,82],"194":[36,78,80,100,38,81,39,40,82],"198":[36,78,80,100,38,81,39,40,82],"202":[36,78,80,100,38,81,39,40,82],"208":[36,78,80,100,38,81,39,40,82],"212":[36,78,80,100,38,81,39,40,82],"229":[36,78,80,100,38,81,39,40,82],"233":[36,78,80,100,38,81,39,40,82],"237":[36,78,80,100,38,81,39,40,82],"241":[36,78,80,100,38,81,39,40,82],"245":[36,78,80,100,38,81,39,40,82],"249":[36,78,80,100,38,81,39,40,82],"253":[36,78,80,100,38,81,39,40,82],"257":[36,78,80,100,38,81,39,40,82],"261":[36,78,80,100,38,81,39,40,82],"265":[36,78,80,100,38,81,39,40,82],"269":[36,78,80,100,38,81,39,40,82],"273":[36,78,80,100,38,81,39,40,82],"275":[36,78,80,100,38,81,39,40,82],"282":[36,78,80,100,38,81,39,40,82],"293":[36,78,80,100,38,81,39,40,82],"298":[36,78,80,100,38,81,39,40,82],"301":[36,78,80,100,38,81,39,40,82],"306":[36,78,80,100,38,81,39,40,82],"312":[36,78,80,100,38,81,39,40,82],"319":[36,78,80,100,38,81,39,40,82],"323":[36,78,80,100,38,81,39,40,82],"327":[36,78,80,100,38,81,39,40,82],"348":[36,78,80,100,38,81,39,40,82],"363":[36,78,80,100,38,81,39,40,82],"364":[36,78,80,100,38,81,39,40,82],"369":[36,78,80,100,38,81,39,40,82]},"packages/@n8n/instance-ai/src/workspace/pack-workspace-sdk.ts":{"56":[36,78,80,100,38,81,82],"64":[36,78,80,100,38,81,82],"123":[36,78,80,100,38,81,82],"148":[36,78,80,100,38,81,82],"160":[36,78,80,100,38,81,82],"174":[36,78,80,100,38,81,82],"179":[36,78,80,100,38,81,82]},"packages/@n8n/instance-ai/src/workspace/prebaked-workspace-bundle.ts":{"30":[36,78,80,100,38,81,82],"63":[36,78,80,100,38,81,82],"66":[36,78,80,100,38,81,82],"75":[36,78,80,100,38,81,82],"101":[36,78,80,100,38,81,82]},"packages/@n8n/instance-ai/src/workspace/sandbox-fs.ts":{"56":[36,78,80,100,38,81,39,40,82],"60":[36,78,80,100,38,81,39,40,82],"62":[36,78,80,100,38,81,39,40,82],"63":[36,78,80,100,38,81,39,40,82],"67":[36,78,80,100,38,81,39,40,82],"76":[36,78,80,100,38,81,39,40,82],"82":[36,78,80,100,38,81,39,40,82],"115":[36,78,80,100,38,81,39,40,82],"140":[36,78,80,100,38,81,39,40,82],"147":[36,78,80,100,38,81,39,40,82],"148":[36,78,80,100,38,81,39,40,82],"196":[36,78,80,100,38,81,39,40,82],"202":[36,78,80,100,38,81,39,40,82],"217":[36,78,80,100,38,81,39,40,82]},"packages/@n8n/instance-ai/src/workspace/sandbox-setup.ts":{"64":[36,78,80,37,100,38,81,39,40,82],"69":[36,78,80,37,100,38,81,39,40,82],"78":[36,78,80,37,100,38,81,39,40,82],"91":[36,78,80,37,100,38,81,39,40,82],"124":[36,78,80,37,100,38,81,39,40,82],"140":[36,78,80,37,100,38,81,39,40,82],"178":[36,78,80,37,100,38,81,39,40,82],"185":[36,78,80,37,100,38,81,39,40,82],"209":[36,78,80,37,100,38,81,39,40,82],"225":[36,78,80,37,100,38,81,39,40,82],"288":[36,78,80,37,100,38,81,39,40,82],"305":[36,78,80,37,100,38,81,39,40,82],"315":[36,78,80,37,100,38,81,39,40,82],"321":[36,78,80,37,100,38,81,39,40,82],"329":[36,78,80,37,100,38,81,39,40,82],"343":[36,78,80,37,100,38,81,39,40,82],"364":[36,78,80,37,100,38,81,39,40,82],"383":[36,78,80,37,100,38,81,39,40,82],"400":[36,78,80,37,100,38,81,39,40,82],"405":[36,78,80,37,100,38,81,39,40,82],"424":[36,78,80,37,100,38,81,39,40,82],"430":[36,78,80,37,100,38,81,39,40,82],"437":[36,78,80,37,100,38,81,39,40,82],"455":[36,78,80,37,100,38,81,39,40,82],"464":[36,78,80,37,100,38,81,39,40,82],"482":[36,78,80,37,100,38,81,39,40,82],"487":[36,78,80,37,100,38,81,39,40,82],"496":[36,78,80,37,100,38,81,39,40,82],"501":[36,78,80,37,100,38,81,39,40,82]},"packages/@n8n/instance-ai/src/workspace/scoped-workspace.ts":{"20":[36,78,80,100,38,81,39,40,82],"25":[36,78,80,100,38,81,39,40,82],"39":[36,78,80,100,38,81,39,40,82],"44":[36,78,80,100,38,81,39,40,82],"48":[36,78,80,100,38,81,39,40,82],"52":[36,78,80,100,38,81,39,40,82],"56":[36,78,80,100,38,81,39,40,82],"60":[36,78,80,100,38,81,39,40,82],"64":[36,78,80,100,38,81,39,40,82],"68":[36,78,80,100,38,81,39,40,82],"72":[36,78,80,100,38,81,39,40,82],"77":[36,78,80,100,38,81,39,40,82],"81":[36,78,80,100,38,81,39,40,82],"85":[36,78,80,100,38,81,39,40,82],"89":[36,78,80,100,38,81,39,40,82],"93":[36,78,80,100,38,81,39,40,82],"101":[36,78,80,100,38,81,39,40,82],"109":[36,78,80,100,38,81,39,40,82],"113":[36,78,80,100,38,81,39,40,82],"117":[36,78,80,100,38,81,39,40,82],"121":[36,78,80,100,38,81,39,40,82],"125":[36,78,80,100,38,81,39,40,82],"133":[36,78,80,100,38,81,39,40,82],"140":[36,78,80,100,38,81,39,40,82],"154":[36,78,80,100,38,81,39,40,82],"158":[36,78,80,100,38,81,39,40,82],"162":[36,78,80,100,38,81,39,40,82],"166":[36,78,80,100,38,81,39,40,82],"170":[36,78,80,100,38,81,39,40,82],"174":[36,78,80,100,38,81,39,40,82],"178":[36,78,80,100,38,81,39,40,82],"184":[36,78,80,100,38,81,39,40,82]},"packages/@n8n/instance-ai/src/workspace/snapshot-image-context.ts":{"18":[36,78,80,100,38,81,82],"30":[36,78,80,100,38,81,82],"48":[36,78,80,100,38,81,82],"62":[36,78,80,100,38,81,82],"68":[36,78,80,100,38,81,82],"78":[36,78,80,100,38,81,82]},"packages/@n8n/instance-ai/src/workspace/snapshot-manager.ts":{"45":[36,78,80,100,38,81,82],"60":[36,78,80,100,38,81,82],"69":[36,78,80,100,38,81,82],"74":[36,78,80,100,38,81,82],"97":[36,78,80,100,38,81,82],"127":[36,78,80,100,38,81,82],"156":[36,78,80,100,38,81,82],"192":[36,78,80,100,38,81,82],"197":[36,78,80,100,38,81,82],"207":[36,78,80,100,38,81,82],"212":[36,78,80,100,38,81,82],"230":[36,78,80,100,38,81,82]},"packages/@n8n/instance-ai/src/workspace/template-telemetry.ts":{"59":[36,78,80,100,38,81,39,40,82],"74":[36,78,80,100,38,81,39,40,82],"78":[36,78,80,100,38,81,39,40,82],"88":[36,78,80,100,38,81,39,40,82],"97":[36,78,80,100,38,81,39,40,82],"125":[36,78,80,100,38,81,39,40,82],"144":[36,78,80,100,38,81,39,40,82],"149":[36,78,80,100,38,81,39,40,82],"159":[36,78,80,100,38,81,39,40,82],"166":[36,78,80,100,38,81,39,40,82],"176":[36,78,80,100,38,81,39,40,82],"187":[36,78,80,100,38,81,39,40,82],"195":[36,78,80,100,38,81,39,40,82],"200":[36,78,80,100,38,81,39,40,82],"224":[36,78,80,100,38,81,39,40,82],"229":[36,78,80,100,38,81,39,40,82],"260":[36,78,80,100,38,81,39,40,82],"279":[36,78,80,100,38,81,39,40,82]},"packages/@n8n/instance-ai/src/workspace/workspace-file-content.ts":{"2":[36,78,80,100,38,81,82],"7":[36,78,80,100,38,81,82]},"packages/@n8n/instance-ai/src/workspace/workspace-files.ts":{"35":[36,78,80,100,38,81,39,40,82],"39":[36,78,80,100,38,81,39,40,82],"47":[36,78,80,100,38,81,39,40,82],"59":[36,78,80,100,38,81,39,40,82],"107":[36,78,80,100,38,81,39,40,82],"119":[36,78,80,100,38,81,39,40,82],"158":[36,78,80,100,38,81,39,40,82],"164":[36,78,80,100,38,81,39,40,82]},"packages/@n8n/instance-ai/src/workspace/workspace-manifest.ts":{"17":[36,78,80,100,38,81,82]},"packages/@n8n/instance-ai/src/workspace/workspace-paths.ts":{"1":[36,78,80,100,38,81,39,40,82],"8":[36,78,80,100,38,81,39,40,82],"16":[36,78,80,100,38,81,39,40,82],"24":[36,78,80,100,38,81,39,40,82]},"packages/@n8n/utils/src/is-record.ts":{"1":"b:egAAAPAFAAAAwAcAAAAAAAAAAAAAEAAE"},"packages/@n8n/utils/src/lazy-import.ts":{"15":[20,36,78,80,100,38,81,82,172,42]},"packages/@n8n/workflow-sdk/src/codegen/index.ts":{"54":"b:AAAAAPAFAAAAQAcAEAAAAAAAAAAAEAAA","55":"b:AAAAAPAFAAAAQAcAEAAAAAAAAAAAEAAA","57":"b:AAAAAPAFAAAAQAcAEAAAAAAAAAAAEAAA","74":"b:AAAAAPAFAAAAQAcAEAAAAAAAAAAAEAAA","101":"b:AAAAAPAFAAAAQAcAEAAAAAAAAAAAEAAA"},"packages/@n8n/workflow-sdk/src/index.ts":{"113":"b:AAAAAPAFAAAAQAcAEAAAAAAAAAAAEAAA","126":"b:AAAAAPAFAAAAQAcAEAAAAAAAAAAAEAAA","172":"b:AAAAAPAFAAAAQAcAEAAAAAAAAAAAEAAA"},"packages/@n8n/workflow-sdk/src/prompts/sdk-reference/index.ts":{"3":[36,78,80,100,38,81,82]},"packages/@n8n/workflow-sdk/src/validation/node-parameter-schema/schema-validator.ts":{"38":[36,78,80,100,38,81,39,40,82,172,42],"46":[36,78,80,100,38,81,39,40,82,172,42],"59":[36,78,80,100,38,81,39,40,82,172,42],"75":[36,78,80,100,38,81,39,40,82,172,42],"94":[36,78,80,100,38,81,39,40,82,172,42],"120":[36,78,80,100,38,81,39,40,82,172,42],"138":[36,78,80,100,38,81,39,40,82,172,42],"151":[36,78,80,100,38,81,39,40,82,172,42],"183":[36,78,80,100,38,81,39,40,82,172,42],"192":[36,78,80,100,38,81,39,40,82,172,42],"212":[36,78,80,100,38,81,39,40,82,172,42],"256":[36,78,80,100,38,81,39,40,82,172,42],"284":[36,78,80,100,38,81,39,40,82,172,42],"300":[36,78,80,100,38,81,39,40,82,172,42],"313":[36,78,80,100,38,81,39,40,82,172,42],"335":[36,78,80,100,38,81,39,40,82,172,42],"357":[36,78,80,100,38,81,39,40,82,172,42],"389":[36,78,80,100,38,81,39,40,82,172,42],"412":[36,78,80,100,38,81,39,40,82,172,42],"420":[36,78,80,100,38,81,39,40,82,172,42],"422":[36,78,80,100,38,81,39,40,82,172,42],"469":[36,78,80,100,38,81,39,40,82,172,42],"479":[36,78,80,100,38,81,39,40,82,172,42],"482":[36,78,80,100,38,81,39,40,82,172,42],"503":[36,78,80,100,38,81,39,40,82,172,42],"515":[36,78,80,100,38,81,39,40,82,172,42],"518":[36,78,80,100,38,81,39,40,82,172,42],"520":[36,78,80,100,38,81,39,40,82,172,42],"539":[36,78,80,100,38,81,39,40,82,172,42],"548":[36,78,80,100,38,81,39,40,82,172,42],"550":[36,78,80,100,38,81,39,40,82,172,42],"558":[36,78,80,100,38,81,39,40,82,172,42],"573":[36,78,80,100,38,81,39,40,82,172,42],"588":[36,78,80,100,38,81,39,40,82,172,42],"590":[36,78,80,100,38,81,39,40,82,172,42],"611":[36,78,80,100,38,81,39,40,82,172,42],"626":[36,78,80,100,38,81,39,40,82,172,42],"627":[36,78,80,100,38,81,39,40,82,172,42],"636":[36,78,80,100,38,81,39,40,82,172,42],"653":[36,78,80,100,38,81,39,40,82,172,42],"679":[36,78,80,100,38,81,39,40,82,172,42],"680":[36,78,80,100,38,81,39,40,82,172,42]},"packages/cli/src/credentials/instance-credential-broker.ts":{"28":[36,78,79,80,100,38,81,82],"35":[36,78,79,80,100,38,81,82],"39":[36,78,79,80,100,38,81,82],"44":[36,78,79,80,100,38,81,82],"62":[36,78,79,80,100,38,81,82],"67":[36,78,79,80,100,38,81,82],"75":[36,78,79,80,100,38,81,82],"103":[36,78,79,80,100,38,81,82]},"packages/cli/src/credentials/instance-credential-use.registry.ts":{"14":[36,78,79,80,38,81,82],"18":[36,78,79,80,38,81,82],"39":[36,78,79,80,38,81,82]},"packages/cli/src/metrics/prometheus/instance-ai-metrics.service.ts":{"17":[36,78,79,80,100,38,81,82],"23":[36,78,79,80,100,38,81,82],"28":[36,78,79,80,100,38,81,82],"72":[36,78,79,80,100,38,81,82],"141":[36,78,79,80,100,38,81,82],"145":[36,78,79,80,100,38,81,82],"148":[36,78,79,80,100,38,81,82],"151":[36,78,79,80,100,38,81,82],"154":[36,78,79,80,100,38,81,82],"158":[36,78,79,80,100,38,81,82],"161":[36,78,79,80,100,38,81,82],"164":[36,78,79,80,100,38,81,82],"170":[36,78,79,80,100,38,81,82]},"packages/cli/src/modules/instance-ai/browser/browser-use-server.ts":{"22":[36,78,79,80,38,81,82],"24":[36,78,79,80,38,81,82],"25":[36,78,79,80,38,81,82],"28":[36,78,79,80,38,81,82],"31":[36,78,79,80,38,81,82],"43":[36,78,79,80,38,81,82],"47":[36,78,79,80,38,81,82],"52":[36,78,79,80,38,81,82],"53":[36,78,79,80,38,81,82],"64":[36,78,79,80,38,81,82]},"packages/cli/src/modules/instance-ai/browser/composite-local-mcp-server.ts":{"4":[36,78,80,100,38,81,82],"7":[36,78,80,100,38,81,82],"25":[36,78,80,100,38,81,82],"26":[36,78,80,100,38,81,82],"29":[36,78,80,100,38,81,82],"33":[36,78,80,100,38,81,82],"37":[36,78,80,100,38,81,82],"43":[36,78,80,100,38,81,82],"58":[36,78,80,100,38,81,82],"59":[36,78,80,100,38,81,82],"62":[36,78,80,100,38,81,82]},"packages/cli/src/modules/instance-ai/browser/instance-ai-browser-session.service.ts":{"62":[36,78,79,80,100,38,81,82],"74":[36,78,79,80,100,38,81,82],"92":[36,78,79,80,100,38,81,82],"101":[36,78,79,80,100,38,81,82],"110":[36,78,79,80,100,38,81,82],"115":[36,78,79,80,100,38,81,82],"119":[36,78,79,80,100,38,81,82],"129":[36,78,79,80,100,38,81,82],"144":[36,78,79,80,100,38,81,82],"153":[36,78,79,80,100,38,81,82],"160":[36,78,79,80,100,38,81,82],"161":[36,78,79,80,100,38,81,82],"176":[36,78,79,80,100,38,81,82],"198":[36,78,79,80,100,38,81,82],"211":[36,78,79,80,100,38,81,82],"229":[36,78,79,80,100,38,81,82],"241":[36,78,79,80,100,38,81,82],"256":[36,78,79,80,100,38,81,82],"261":[36,78,79,80,100,38,81,82],"273":[36,78,79,80,100,38,81,82],"284":[36,78,79,80,100,38,81,82],"300":[36,78,79,80,100,38,81,82],"305":[36,78,79,80,100,38,81,82],"309":[36,78,79,80,100,38,81,82],"316":[36,78,79,80,100,38,81,82],"319":[36,78,79,80,100,38,81,82],"324":[36,78,79,80,100,38,81,82],"327":[36,78,79,80,100,38,81,82]},"packages/cli/src/modules/instance-ai/eval/thread-credential-allowlist.service.ts":{"10":[36,78,80,100,38,81,40,82],"13":[36,78,80,100,38,81,40,82],"17":[36,78,80,100,38,81,40,82],"21":[36,78,80,100,38,81,40,82]},"packages/cli/src/modules/instance-ai/event-bus/durable-event-log.ts":{"69":[36,78,79,80,100,38,81,40,82],"73":[36,78,79,80,100,38,81,40,82],"75":[36,78,79,80,100,38,81,40,82],"138":[36,78,79,80,100,38,81,40,82],"147":[36,78,79,80,100,38,81,40,82],"158":[36,78,79,80,100,38,81,40,82],"161":[36,78,79,80,100,38,81,40,82],"163":[36,78,79,80,100,38,81,40,82],"171":[36,78,79,80,100,38,81,40,82],"175":[36,78,79,80,100,38,81,40,82],"187":[36,78,79,80,100,38,81,40,82],"208":[36,78,79,80,100,38,81,40,82],"223":[36,78,79,80,100,38,81,40,82],"238":[36,78,79,80,100,38,81,40,82],"250":[36,78,79,80,100,38,81,40,82],"263":[36,78,79,80,100,38,81,40,82],"268":[36,78,79,80,100,38,81,40,82],"278":[36,78,79,80,100,38,81,40,82],"296":[36,78,79,80,100,38,81,40,82],"298":[36,78,79,80,100,38,81,40,82],"324":[36,78,79,80,100,38,81,40,82],"327":[36,78,79,80,100,38,81,40,82],"406":[36,78,79,80,100,38,81,40,82],"416":[36,78,79,80,100,38,81,40,82],"442":[36,78,79,80,100,38,81,40,82],"518":[36,78,79,80,100,38,81,40,82],"532":[36,78,79,80,100,38,81,40,82],"558":[36,78,79,80,100,38,81,40,82],"574":[36,78,79,80,100,38,81,40,82],"579":[36,78,79,80,100,38,81,40,82],"601":[36,78,79,80,100,38,81,40,82],"614":[36,78,79,80,100,38,81,40,82],"649":[36,78,79,80,100,38,81,40,82],"659":[36,78,79,80,100,38,81,40,82],"673":[36,78,79,80,100,38,81,40,82]},"packages/cli/src/modules/instance-ai/event-bus/durable-log-metrics.ts":{"62":[36,78,79,80,38,81,82],"64":[36,78,79,80,38,81,82],"71":[36,78,79,80,38,81,82],"78":[36,78,79,80,38,81,82],"83":[36,78,79,80,38,81,82],"88":[36,78,79,80,38,81,82],"99":[36,78,79,80,38,81,82],"111":[36,78,79,80,38,81,82],"115":[36,78,79,80,38,81,82],"119":[36,78,79,80,38,81,82],"123":[36,78,79,80,38,81,82]},"packages/cli/src/modules/instance-ai/event-bus/in-process-event-bus.ts":{"70":[36,78,79,80,100,38,81,40,82],"106":[36,78,79,80,100,38,81,40,82],"115":[36,78,79,80,100,38,81,40,82],"141":[36,78,79,80,100,38,81,40,82],"157":[36,78,79,80,100,38,81,40,82],"174":[36,78,79,80,100,38,81,40,82],"199":[36,78,79,80,100,38,81,40,82],"223":[36,78,79,80,100,38,81,40,82],"257":[36,78,79,80,100,38,81,40,82],"261":[36,78,79,80,100,38,81,40,82],"265":[36,78,79,80,100,38,81,40,82],"271":[36,78,79,80,100,38,81,40,82],"292":[36,78,79,80,100,38,81,40,82],"304":[36,78,79,80,100,38,81,40,82],"320":[36,78,79,80,100,38,81,40,82],"334":[36,78,79,80,100,38,81,40,82],"353":[36,78,79,80,100,38,81,40,82],"355":[36,78,79,80,100,38,81,40,82],"359":[36,78,79,80,100,38,81,40,82],"371":[36,78,79,80,100,38,81,40,82],"374":[36,78,79,80,100,38,81,40,82],"377":[36,78,79,80,100,38,81,40,82],"381":[36,78,79,80,100,38,81,40,82],"385":[36,78,79,80,100,38,81,40,82],"386":[36,78,79,80,100,38,81,40,82],"394":[36,78,79,80,100,38,81,40,82],"398":[36,78,79,80,100,38,81,40,82],"420":[36,78,79,80,100,38,81,40,82],"433":[36,78,79,80,100,38,81,40,82],"444":[36,78,79,80,100,38,81,40,82],"454":[36,78,79,80,100,38,81,40,82],"466":[36,78,79,80,100,38,81,40,82]},"packages/cli/src/modules/instance-ai/filesystem/local-gateway-registry.ts":{"27":[36,78,80,37,100,38,81,82],"34":[36,78,80,37,100,38,81,82],"42":[36,78,80,37,100,38,81,82],"56":[36,78,80,37,100,38,81,82],"72":[36,78,80,37,100,38,81,82],"86":[36,78,80,37,100,38,81,82],"98":[36,78,80,37,100,38,81,82],"110":[36,78,80,37,100,38,81,82],"124":[36,78,80,37,100,38,81,82],"129":[36,78,80,37,100,38,81,82],"137":[36,78,80,37,100,38,81,82],"142":[36,78,80,37,100,38,81,82],"147":[36,78,80,37,100,38,81,82],"155":[36,78,80,37,100,38,81,82],"165":[36,78,80,37,100,38,81,82],"170":[36,78,80,37,100,38,81,82],"193":[36,78,80,37,100,38,81,82],"198":[36,78,80,37,100,38,81,82],"208":[36,78,80,37,100,38,81,82],"216":[36,78,80,37,100,38,81,82],"218":[36,78,80,37,100,38,81,82],"219":[36,78,80,37,100,38,81,82],"223":[36,78,80,37,100,38,81,82]},"packages/cli/src/modules/instance-ai/instance-ai-error-reporter.service.ts":{"18":[36,78,80,100,38,81,39,82],"29":[36,78,80,100,38,81,39,82],"36":[36,78,80,100,38,81,39,82],"40":[36,78,80,100,38,81,39,82],"44":[36,78,80,100,38,81,39,82],"48":[36,78,80,100,38,81,39,82],"87":[36,78,80,100,38,81,39,82],"101":[36,78,80,100,38,81,39,82]},"packages/cli/src/modules/instance-ai/instance-ai-gateway.service.ts":{"26":[36,78,80,37,100,38,81,82],"36":[36,78,80,37,100,38,81,82],"42":[36,78,80,37,100,38,81,82],"46":[36,78,80,37,100,38,81,82],"50":[36,78,80,37,100,38,81,82],"54":[36,78,80,37,100,38,81,82],"58":[36,78,80,37,100,38,81,82],"62":[36,78,80,37,100,38,81,82],"66":[36,78,80,37,100,38,81,82],"70":[36,78,80,37,100,38,81,82],"75":[36,78,80,37,100,38,81,82],"79":[36,78,80,37,100,38,81,82],"83":[36,78,80,37,100,38,81,82],"87":[36,78,80,37,100,38,81,82],"96":[36,78,80,37,100,38,81,82],"101":[36,78,80,37,100,38,81,82],"108":[36,78,80,37,100,38,81,82],"112":[36,78,80,37,100,38,81,82],"122":[36,78,80,37,100,38,81,82],"126":[36,78,80,37,100,38,81,82]},"packages/cli/src/modules/instance-ai/instance-ai-memory.service.ts":{"48":[36,78,79,80,38,81,82],"57":[36,78,79,80,38,81,82],"67":[36,78,79,80,38,81,82],"80":[36,78,79,80,38,81,82],"87":[36,78,79,80,38,81,82],"105":[36,78,79,80,38,81,82],"110":[36,78,79,80,38,81,82],"114":[36,78,79,80,38,81,82],"127":[36,78,79,80,38,81,82],"148":[36,78,79,80,38,81,82],"219":[36,78,79,80,38,81,82],"239":[36,78,79,80,38,81,82],"252":[36,78,79,80,38,81,82],"264":[36,78,79,80,38,81,82],"271":[36,78,79,80,38,81,82],"312":[36,78,79,80,38,81,82],"333":[36,78,79,80,38,81,82],"337":[36,78,79,80,38,81,82],"349":[36,78,79,80,38,81,82],"353":[36,78,79,80,38,81,82],"372":[36,78,79,80,38,81,82],"373":[36,78,79,80,38,81,82],"385":[36,78,79,80,38,81,82],"436":[36,78,79,80,38,81,82],"487":[36,78,79,80,38,81,82],"495":[36,78,79,80,38,81,82],"515":[36,78,79,80,38,81,82],"527":[36,78,79,80,38,81,82],"538":[36,78,79,80,38,81,82],"548":[36,78,79,80,38,81,82],"557":[36,78,79,80,38,81,82],"570":[36,78,79,80,38,81,82],"574":[36,78,79,80,38,81,82],"578":[36,78,79,80,38,81,82],"584":[36,78,79,80,38,81,82],"601":[36,78,79,80,38,81,82],"615":[36,78,79,80,38,81,82],"665":[36,78,79,80,38,81,82],"683":[36,78,79,80,38,81,82]},"packages/cli/src/modules/instance-ai/instance-ai-model.service.ts":{"34":[36,78,79,80,100,38,81,82],"41":[36,78,79,80,100,38,81,82],"56":[36,78,79,80,100,38,81,82],"60":[36,78,79,80,100,38,81,82],"86":[36,78,79,80,100,38,81,82],"99":[36,78,79,80,100,38,81,82],"121":[36,78,79,80,100,38,81,82],"147":[36,78,79,80,100,38,81,82],"154":[36,78,79,80,100,38,81,82]},"packages/cli/src/modules/instance-ai/instance-ai-settings.service.ts":{"94":[36,78,79,80,37,100,38,81,82],"108":[36,78,79,80,37,100,38,81,82],"119":[36,78,79,80,37,100,38,81,82],"138":[36,78,79,80,37,100,38,81,82],"159":[36,78,79,80,37,100,38,81,82],"175":[36,78,79,80,37,100,38,81,82],"186":[36,78,79,80,37,100,38,81,82],"221":[36,78,79,80,37,100,38,81,82],"303":[36,78,79,80,37,100,38,81,82],"322":[36,78,79,80,37,100,38,81,82],"327":[36,78,79,80,37,100,38,81,82],"332":[36,78,79,80,37,100,38,81,82],"361":[36,78,79,80,37,100,38,81,82],"403":[36,78,79,80,37,100,38,81,82],"438":[36,78,79,80,37,100,38,81,82],"533":[36,78,79,80,37,100,38,81,82],"566":[36,78,79,80,37,100,38,81,82],"702":[36,78,79,80,37,100,38,81,82],"724":[36,78,79,80,37,100,38,81,82],"782":[36,78,79,80,37,100,38,81,82],"828":[36,78,79,80,37,100,38,81,82],"846":[36,78,79,80,37,100,38,81,82],"860":[36,78,79,80,37,100,38,81,82],"934":[36,78,79,80,37,100,38,81,82],"943":[36,78,79,80,37,100,38,81,82],"969":[36,78,79,80,37,100,38,81,82],"990":[36,78,79,80,37,100,38,81,82],"995":[36,78,79,80,37,100,38,81,82],"1002":[36,78,79,80,37,100,38,81,82],"1012":[36,78,79,80,37,100,38,81,82],"1014":[36,78,79,80,37,100,38,81,82],"1021":[36,78,79,80,37,100,38,81,82],"1046":[36,78,79,80,37,100,38,81,82],"1080":[36,78,79,80,37,100,38,81,82],"1106":[36,78,79,80,37,100,38,81,82],"1115":[36,78,79,80,37,100,38,81,82],"1129":[36,78,79,80,37,100,38,81,82],"1135":[36,78,79,80,37,100,38,81,82],"1145":[36,78,79,80,37,100,38,81,82],"1150":[36,78,79,80,37,100,38,81,82],"1155":[36,78,79,80,37,100,38,81,82],"1164":[36,78,79,80,37,100,38,81,82],"1169":[36,78,79,80,37,100,38,81,82],"1173":[36,78,79,80,37,100,38,81,82],"1178":[36,78,79,80,37,100,38,81,82],"1194":[36,78,79,80,37,100,38,81,82],"1199":[36,78,79,80,37,100,38,81,82],"1206":[36,78,79,80,37,100,38,81,82],"1237":[36,78,79,80,37,100,38,81,82],"1240":[36,78,79,80,37,100,38,81,82],"1263":[36,78,79,80,37,100,38,81,82],"1271":[36,78,79,80,37,100,38,81,82],"1322":[36,78,79,80,37,100,38,81,82],"1328":[36,78,79,80,37,100,38,81,82],"1334":[36,78,79,80,37,100,38,81,82],"1342":[36,78,79,80,37,100,38,81,82],"1368":[36,78,79,80,37,100,38,81,82],"1383":[36,78,79,80,37,100,38,81,82],"1387":[36,78,79,80,37,100,38,81,82],"1404":[36,78,79,80,37,100,38,81,82],"1409":[36,78,79,80,37,100,38,81,82],"1436":[36,78,79,80,37,100,38,81,82],"1440":[36,78,79,80,37,100,38,81,82],"1457":[36,78,79,80,37,100,38,81,82],"1470":[36,78,79,80,37,100,38,81,82],"1475":[36,78,79,80,37,100,38,81,82],"1478":[36,78,79,80,37,100,38,81,82],"1484":[36,78,79,80,37,100,38,81,82],"1485":[36,78,79,80,37,100,38,81,82],"1494":[36,78,79,80,37,100,38,81,82],"1511":[36,78,79,80,37,100,38,81,82]},"packages/cli/src/modules/instance-ai/instance-ai-temporary-workflow.service.ts":{"8":[36,78,80,100,81,40,82],"35":[36,78,80,100,81,40,82],"51":[36,78,80,100,81,40,82],"75":[36,78,80,100,81,40,82],"88":[36,78,80,100,81,40,82],"145":[36,78,80,100,81,40,82],"149":[36,78,80,100,81,40,82]},"packages/cli/src/modules/instance-ai/instance-ai-terminal-outcome.service.ts":{"30":[36,78,79,80,37,100,38,81,39,82],"34":[36,78,79,80,37,100,38,81,39,82],"38":[36,78,79,80,37,100,38,81,39,82],"59":[36,78,79,80,37,100,38,81,39,82],"68":[36,78,79,80,37,100,38,81,39,82],"203":[36,78,79,80,37,100,38,81,39,82],"217":[36,78,79,80,37,100,38,81,39,82],"250":[36,78,79,80,37,100,38,81,39,82],"270":[36,78,79,80,37,100,38,81,39,82],"283":[36,78,79,80,37,100,38,81,39,82],"321":[36,78,79,80,37,100,38,81,39,82],"326":[36,78,79,80,37,100,38,81,39,82],"356":[36,78,79,80,37,100,38,81,39,82],"380":[36,78,79,80,37,100,38,81,39,82],"386":[36,78,79,80,37,100,38,81,39,82],"394":[36,78,79,80,37,100,38,81,39,82],"400":[36,78,79,80,37,100,38,81,39,82],"442":[36,78,79,80,37,100,38,81,39,82],"465":[36,78,79,80,37,100,38,81,39,82],"498":[36,78,79,80,37,100,38,81,39,82],"504":[36,78,79,80,37,100,38,81,39,82],"517":[36,78,79,80,37,100,38,81,39,82]},"packages/cli/src/modules/instance-ai/instance-ai-test.controller.ts":{"18":[36,78,79,80,177,38,81],"28":[36,78,79,80,177,38,81],"40":[36,78,79,80,177,38,81],"46":[36,78,79,80,177,38,81],"52":[36,78,79,80,177,38,81],"66":[36,78,79,80,177,38,81],"73":[36,78,79,80,177,38,81],"92":[36,78,79,80,177,38,81],"119":[36,78,79,80,177,38,81]},"packages/cli/src/modules/instance-ai/instance-ai.adapter.service.ts":{"170":[36,78,80,37,100,38,81,39,40,82],"221":[36,78,80,37,100,38,81,39,40,82],"226":[36,78,80,37,100,38,81,39,40,82],"230":[36,78,80,37,100,38,81,39,40,82],"236":[36,78,80,37,100,38,81,39,40,82],"280":[36,78,80,37,100,38,81,39,40,82],"285":[36,78,80,37,100,38,81,39,40,82],"370":[36,78,80,37,100,38,81,39,40,82],"377":[36,78,80,37,100,38,81,39,40,82],"397":[36,78,80,37,100,38,81,39,40,82],"416":[36,78,80,37,100,38,81,39,40,82],"433":[36,78,80,37,100,38,81,39,40,82],"438":[36,78,80,37,100,38,81,39,40,82],"455":[36,78,80,37,100,38,81,39,40,82],"466":[36,78,80,37,100,38,81,39,40,82],"469":[36,78,80,37,100,38,81,39,40,82],"475":[36,78,80,37,100,38,81,39,40,82],"491":[36,78,80,37,100,38,81,39,40,82],"500":[36,78,80,37,100,38,81,39,40,82],"508":[36,78,80,37,100,38,81,39,40,82],"512":[36,78,80,37,100,38,81,39,40,82],"515":[36,78,80,37,100,38,81,39,40,82],"519":[36,78,80,37,100,38,81,39,40,82],"526":[36,78,80,37,100,38,81,39,40,82],"532":[36,78,80,37,100,38,81,39,40,82],"545":[36,78,80,37,100,38,81,39,40,82],"565":[36,78,80,37,100,38,81,39,40,82],"570":[36,78,80,37,100,38,81,39,40,82],"583":[36,78,80,37,100,38,81,39,40,82],"585":[36,78,80,37,100,38,81,39,40,82],"597":[36,78,80,37,100,38,81,39,40,82],"609":[36,78,80,37,100,38,81,39,40,82],"617":[36,78,80,37,100,38,81,39,40,82],"625":[36,78,80,37,100,38,81,39,40,82],"636":[36,78,80,37,100,38,81,39,40,82],"655":[36,78,80,37,100,38,81,39,40,82],"680":[36,78,80,37,100,38,81,39,40,82],"686":[36,78,80,37,100,38,81,39,40,82],"696":[36,78,80,37,100,38,81,39,40,82],"704":[36,78,80,37,100,38,81,39,40,82],"716":[36,78,80,37,100,38,81,39,40,82],"741":[36,78,80,37,100,38,81,39,40,82],"782":[36,78,80,37,100,38,81,39,40,82],"858":[36,78,80,37,100,38,81,39,40,82],"939":[36,78,80,37,100,38,81,39,40,82],"952":[36,78,80,37,100,38,81,39,40,82],"965":[36,78,80,37,100,38,81,39,40,82],"985":[36,78,80,37,100,38,81,39,40,82],"997":[36,78,80,37,100,38,81,39,40,82],"1015":[36,78,80,37,100,38,81,39,40,82],"1027":[36,78,80,37,100,38,81,39,40,82],"1044":[36,78,80,37,100,38,81,39,40,82],"1053":[36,78,80,37,100,38,81,39,40,82],"1075":[36,78,80,37,100,38,81,39,40,82],"1114":[36,78,80,37,100,38,81,39,40,82],"1126":[36,78,80,37,100,38,81,39,40,82],"1142":[36,78,80,37,100,38,81,39,40,82],"1244":[36,78,80,37,100,38,81,39,40,82],"1263":[36,78,80,37,100,38,81,39,40,82],"1284":[36,78,80,37,100,38,81,39,40,82],"1285":[36,78,80,37,100,38,81,39,40,82],"1294":[36,78,80,37,100,38,81,39,40,82],"1295":[36,78,80,37,100,38,81,39,40,82],"1369":[36,78,80,37,100,38,81,39,40,82],"1378":[36,78,80,37,100,38,81,39,40,82],"1387":[36,78,80,37,100,38,81,39,40,82],"1411":[36,78,80,37,100,38,81,39,40,82],"1416":[36,78,80,37,100,38,81,39,40,82],"1431":[36,78,80,37,100,38,81,39,40,82],"1456":[36,78,80,37,100,38,81,39,40,82],"1462":[36,78,80,37,100,38,81,39,40,82],"1465":[36,78,80,37,100,38,81,39,40,82],"1474":[36,78,80,37,100,38,81,39,40,82],"1475":[36,78,80,37,100,38,81,39,40,82],"1490":[36,78,80,37,100,38,81,39,40,82],"1493":[36,78,80,37,100,38,81,39,40,82],"1509":[36,78,80,37,100,38,81,39,40,82],"1517":[36,78,80,37,100,38,81,39,40,82],"1526":[36,78,80,37,100,38,81,39,40,82],"1530":[36,78,80,37,100,38,81,39,40,82],"1557":[36,78,80,37,100,38,81,39,40,82],"1573":[36,78,80,37,100,38,81,39,40,82],"1587":[36,78,80,37,100,38,81,39,40,82],"1599":[36,78,80,37,100,38,81,39,40,82],"1644":[36,78,80,37,100,38,81,39,40,82],"1684":[36,78,80,37,100,38,81,39,40,82],"1703":[36,78,80,37,100,38,81,39,40,82],"1717":[36,78,80,37,100,38,81,39,40,82],"1728":[36,78,80,37,100,38,81,39,40,82],"1777":[36,78,80,37,100,38,81,39,40,82],"1784":[36,78,80,37,100,38,81,39,40,82],"1798":[36,78,80,37,100,38,81,39,40,82],"1799":[36,78,80,37,100,38,81,39,40,82],"1803":[36,78,80,37,100,38,81,39,40,82],"1808":[36,78,80,37,100,38,81,39,40,82],"1810":[36,78,80,37,100,38,81,39,40,82],"1825":[36,78,80,37,100,38,81,39,40,82],"1833":[36,78,80,37,100,38,81,39,40,82],"1838":[36,78,80,37,100,38,81,39,40,82],"1843":[36,78,80,37,100,38,81,39,40,82],"1848":[36,78,80,37,100,38,81,39,40,82],"1860":[36,78,80,37,100,38,81,39,40,82],"1873":[36,78,80,37,100,38,81,39,40,82],"1881":[36,78,80,37,100,38,81,39,40,82],"1883":[36,78,80,37,100,38,81,39,40,82],"1900":[36,78,80,37,100,38,81,39,40,82],"1908":[36,78,80,37,100,38,81,39,40,82],"1914":[36,78,80,37,100,38,81,39,40,82],"1932":[36,78,80,37,100,38,81,39,40,82],"1942":[36,78,80,37,100,38,81,39,40,82],"1960":[36,78,80,37,100,38,81,39,40,82],"1967":[36,78,80,37,100,38,81,39,40,82],"1971":[36,78,80,37,100,38,81,39,40,82],"1978":[36,78,80,37,100,38,81,39,40,82],"1987":[36,78,80,37,100,38,81,39,40,82],"1993":[36,78,80,37,100,38,81,39,40,82],"2003":[36,78,80,37,100,38,81,39,40,82],"2012":[36,78,80,37,100,38,81,39,40,82],"2020":[36,78,80,37,100,38,81,39,40,82],"2029":[36,78,80,37,100,38,81,39,40,82],"2045":[36,78,80,37,100,38,81,39,40,82],"2055":[36,78,80,37,100,38,81,39,40,82],"2067":[36,78,80,37,100,38,81,39,40,82],"2080":[36,78,80,37,100,38,81,39,40,82],"2101":[36,78,80,37,100,38,81,39,40,82],"2122":[36,78,80,37,100,38,81,39,40,82],"2157":[36,78,80,37,100,38,81,39,40,82],"2174":[36,78,80,37,100,38,81,39,40,82],"2193":[36,78,80,37,100,38,81,39,40,82],"2226":[36,78,80,37,100,38,81,39,40,82],"2255":[36,78,80,37,100,38,81,39,40,82],"2270":[36,78,80,37,100,38,81,39,40,82],"2279":[36,78,80,37,100,38,81,39,40,82],"2294":[36,78,80,37,100,38,81,39,40,82],"2306":[36,78,80,37,100,38,81,39,40,82],"2323":[36,78,80,37,100,38,81,39,40,82],"2333":[36,78,80,37,100,38,81,39,40,82],"2337":[36,78,80,37,100,38,81,39,40,82],"2340":[36,78,80,37,100,38,81,39,40,82],"2341":[36,78,80,37,100,38,81,39,40,82],"2342":[36,78,80,37,100,38,81,39,40,82],"2346":[36,78,80,37,100,38,81,39,40,82],"2352":[36,78,80,37,100,38,81,39,40,82],"2359":[36,78,80,37,100,38,81,39,40,82],"2363":[36,78,80,37,100,38,81,39,40,82],"2373":[36,78,80,37,100,38,81,39,40,82],"2380":[36,78,80,37,100,38,81,39,40,82],"2395":[36,78,80,37,100,38,81,39,40,82],"2398":[36,78,80,37,100,38,81,39,40,82],"2401":[36,78,80,37,100,38,81,39,40,82],"2405":[36,78,80,37,100,38,81,39,40,82],"2408":[36,78,80,37,100,38,81,39,40,82],"2462":[36,78,80,37,100,38,81,39,40,82],"2466":[36,78,80,37,100,38,81,39,40,82],"2474":[36,78,80,37,100,38,81,39,40,82],"2491":[36,78,80,37,100,38,81,39,40,82],"2500":[36,78,80,37,100,38,81,39,40,82],"2503":[36,78,80,37,100,38,81,39,40,82],"2508":[36,78,80,37,100,38,81,39,40,82],"2524":[36,78,80,37,100,38,81,39,40,82],"2529":[36,78,80,37,100,38,81,39,40,82],"2544":[36,78,80,37,100,38,81,39,40,82],"2550":[36,78,80,37,100,38,81,39,40,82],"2596":[36,78,80,37,100,38,81,39,40,82],"2618":[36,78,80,37,100,38,81,39,40,82],"2686":[36,78,80,37,100,38,81,39,40,82],"2687":[36,78,80,37,100,38,81,39,40,82],"2721":[36,78,80,37,100,38,81,39,40,82],"2726":[36,78,80,37,100,38,81,39,40,82],"2737":[36,78,80,37,100,38,81,39,40,82],"2741":[36,78,80,37,100,38,81,39,40,82],"2747":[36,78,80,37,100,38,81,39,40,82],"2749":[36,78,80,37,100,38,81,39,40,82],"2758":[36,78,80,37,100,38,81,39,40,82],"2763":[36,78,80,37,100,38,81,39,40,82],"2770":[36,78,80,37,100,38,81,39,40,82],"2788":[36,78,80,37,100,38,81,39,40,82],"2800":[36,78,80,37,100,38,81,39,40,82],"2816":[36,78,80,37,100,38,81,39,40,82],"2830":[36,78,80,37,100,38,81,39,40,82],"2851":[36,78,80,37,100,38,81,39,40,82],"2856":[36,78,80,37,100,38,81,39,40,82],"2859":[36,78,80,37,100,38,81,39,40,82],"2868":[36,78,80,37,100,38,81,39,40,82],"2898":[36,78,80,37,100,38,81,39,40,82],"2976":[36,78,80,37,100,38,81,39,40,82],"2982":[36,78,80,37,100,38,81,39,40,82],"3015":[36,78,80,37,100,38,81,39,40,82],"3022":[36,78,80,37,100,38,81,39,40,82],"3044":[36,78,80,37,100,38,81,39,40,82],"3057":[36,78,80,37,100,38,81,39,40,82],"3070":[36,78,80,37,100,38,81,39,40,82],"3102":[36,78,80,37,100,38,81,39,40,82],"3146":[36,78,80,37,100,38,81,39,40,82],"3176":[36,78,80,37,100,38,81,39,40,82],"3190":[36,78,80,37,100,38,81,39,40,82],"3198":[36,78,80,37,100,38,81,39,40,82],"3206":[36,78,80,37,100,38,81,39,40,82],"3213":[36,78,80,37,100,38,81,39,40,82],"3238":[36,78,80,37,100,38,81,39,40,82],"3276":[36,78,80,37,100,38,81,39,40,82],"3277":[36,78,80,37,100,38,81,39,40,82],"3321":[36,78,80,37,100,38,81,39,40,82],"3358":[36,78,80,37,100,38,81,39,40,82],"3389":[36,78,80,37,100,38,81,39,40,82],"3430":[36,78,80,37,100,38,81,39,40,82],"3444":[36,78,80,37,100,38,81,39,40,82],"3470":[36,78,80,37,100,38,81,39,40,82],"3471":[36,78,80,37,100,38,81,39,40,82],"3474":[36,78,80,37,100,38,81,39,40,82],"3478":[36,78,80,37,100,38,81,39,40,82],"3482":[36,78,80,37,100,38,81,39,40,82],"3487":[36,78,80,37,100,38,81,39,40,82],"3488":[36,78,80,37,100,38,81,39,40,82],"3497":[36,78,80,37,100,38,81,39,40,82],"3512":[36,78,80,37,100,38,81,39,40,82],"3540":[36,78,80,37,100,38,81,39,40,82],"3573":[36,78,80,37,100,38,81,39,40,82],"3577":[36,78,80,37,100,38,81,39,40,82],"3579":[36,78,80,37,100,38,81,39,40,82],"3625":[36,78,80,37,100,38,81,39,40,82],"3631":[36,78,80,37,100,38,81,39,40,82],"3640":[36,78,80,37,100,38,81,39,40,82],"3641":[36,78,80,37,100,38,81,39,40,82],"3646":[36,78,80,37,100,38,81,39,40,82],"3665":[36,78,80,37,100,38,81,39,40,82],"3678":[36,78,80,37,100,38,81,39,40,82],"3701":[36,78,80,37,100,38,81,39,40,82],"3715":[36,78,80,37,100,38,81,39,40,82],"3729":[36,78,80,37,100,38,81,39,40,82]},"packages/cli/src/modules/instance-ai/instance-ai.controller.ts":{"85":[36,78,79,80,37,100,38,81,39,40,82],"95":[36,78,79,80,37,100,38,81,39,40,82],"103":[36,78,79,80,37,100,38,81,39,40,82],"115":[36,78,79,80,37,100,38,81,39,40,82],"142":[36,78,79,80,37,100,38,81,39,40,82],"148":[36,78,79,80,37,100,38,81,39,40,82],"158":[36,78,79,80,37,100,38,81,39,40,82],"168":[36,78,79,80,37,100,38,81,39,40,82],"185":[36,78,79,80,37,100,38,81,39,40,82],"192":[36,78,79,80,37,100,38,81,39,40,82],"222":[36,78,79,80,37,100,38,81,39,40,82],"254":[36,78,79,80,37,100,38,81,39,40,82],"270":[36,78,79,80,37,100,38,81,39,40,82],"281":[36,78,79,80,37,100,38,81,39,40,82],"287":[36,78,79,80,37,100,38,81,39,40,82],"297":[36,78,79,80,37,100,38,81,39,40,82],"385":[36,78,79,80,37,100,38,81,39,40,82],"422":[36,78,79,80,37,100,38,81,39,40,82],"441":[36,78,79,80,37,100,38,81,39,40,82],"478":[36,78,79,80,37,100,38,81,39,40,82],"482":[36,78,79,80,37,100,38,81,39,40,82],"570":[36,78,79,80,37,100,38,81,39,40,82],"578":[36,78,79,80,37,100,38,81,39,40,82],"601":[36,78,79,80,37,100,38,81,39,40,82],"610":[36,78,79,80,37,100,38,81,39,40,82],"624":[36,78,79,80,37,100,38,81,39,40,82],"630":[36,78,79,80,37,100,38,81,39,40,82],"644":[36,78,79,80,37,100,38,81,39,40,82],"661":[36,78,79,80,37,100,38,81,39,40,82],"670":[36,78,79,80,37,100,38,81,39,40,82],"676":[36,78,79,80,37,100,38,81,39,40,82],"698":[36,78,79,80,37,100,38,81,39,40,82],"707":[36,78,79,80,37,100,38,81,39,40,82],"714":[36,78,79,80,37,100,38,81,39,40,82],"719":[36,78,79,80,37,100,38,81,39,40,82],"723":[36,78,79,80,37,100,38,81,39,40,82],"741":[36,78,79,80,37,100,38,81,39,40,82],"756":[36,78,79,80,37,100,38,81,39,40,82],"762":[36,78,79,80,37,100,38,81,39,40,82],"776":[36,78,79,80,37,100,38,81,39,40,82],"782":[36,78,79,80,37,100,38,81,39,40,82],"788":[36,78,79,80,37,100,38,81,39,40,82],"795":[36,78,79,80,37,100,38,81,39,40,82],"836":[36,78,79,80,37,100,38,81,39,40,82],"850":[36,78,79,80,37,100,38,81,39,40,82],"867":[36,78,79,80,37,100,38,81,39,40,82],"926":[36,78,79,80,37,100,38,81,39,40,82],"939":[36,78,79,80,37,100,38,81,39,40,82],"952":[36,78,79,80,37,100,38,81,39,40,82],"971":[36,78,79,80,37,100,38,81,39,40,82],"983":[36,78,79,80,37,100,38,81,39,40,82],"1001":[36,78,79,80,37,100,38,81,39,40,82],"1021":[36,78,79,80,37,100,38,81,39,40,82],"1069":[36,78,79,80,37,100,38,81,39,40,82],"1084":[36,78,79,80,37,100,38,81,39,40,82],"1103":[36,78,79,80,37,100,38,81,39,40,82],"1116":[36,78,79,80,37,100,38,81,39,40,82],"1139":[36,78,79,80,37,100,38,81,39,40,82],"1143":[36,78,79,80,37,100,38,81,39,40,82],"1149":[36,78,79,80,37,100,38,81,39,40,82],"1155":[36,78,79,80,37,100,38,81,39,40,82],"1161":[36,78,79,80,37,100,38,81,39,40,82],"1181":[36,78,79,80,37,100,38,81,39,40,82],"1212":[36,78,79,80,37,100,38,81,39,40,82],"1229":[36,78,79,80,37,100,38,81,39,40,82],"1250":[36,78,79,80,37,100,38,81,39,40,82],"1263":[36,78,79,80,37,100,38,81,39,40,82],"1276":[36,78,79,80,37,100,38,81,39,40,82],"1293":[36,78,79,80,37,100,38,81,39,40,82],"1301":[36,78,79,80,37,100,38,81,39,40,82],"1309":[36,78,79,80,37,100,38,81,39,40,82],"1317":[36,78,79,80,37,100,38,81,39,40,82],"1327":[36,78,79,80,37,100,38,81,39,40,82],"1342":[36,78,79,80,37,100,38,81,39,40,82],"1353":[36,78,79,80,37,100,38,81,39,40,82],"1365":[36,78,79,80,37,100,38,81,39,40,82],"1390":[36,78,79,80,37,100,38,81,39,40,82],"1403":[36,78,79,80,37,100,38,81,39,40,82]},"packages/cli/src/modules/instance-ai/instance-ai.service.ts":{"196":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","201":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","222":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","224":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","235":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","250":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","273":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","297":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","308":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","321":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","325":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","357":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","373":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","380":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","384":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","425":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","469":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","473":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","569":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","606":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","641":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","644":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","670":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","671":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","675":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","687":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","690":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","704":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","708":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","719":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","729":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","742":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","749":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","762":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","772":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","788":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","805":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","810":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","823":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","847":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","851":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","860":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","867":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","876":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","880":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","915":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","916":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","926":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","930":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","961":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","991":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","995":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","996":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","1006":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","1010":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","1019":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","1061":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","1072":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","1080":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","1085":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","1089":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","1104":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","1147":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","1156":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","1171":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","1198":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","1202":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","1215":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","1230":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","1244":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","1251":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","1255":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","1259":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","1278":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","1286":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","1305":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","1313":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","1385":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","1386":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","1387":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","1389":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","1401":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","1436":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","1444":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","1448":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","1452":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","1463":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","1467":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","1475":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","1525":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","1537":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","1643":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","1650":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","1656":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","1658":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","1674":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","1675":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","1686":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","1691":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","1704":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","1708":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","1712":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","1713":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","1716":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","1733":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","1763":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","1786":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","1789":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","1803":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","1825":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","1833":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","1857":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","1862":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","1865":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","1870":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","1875":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","1879":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","1880":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","1882":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","1887":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","1920":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","1930":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","1953":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","1954":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","1985":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","2002":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","2015":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","2022":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","2040":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","2062":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","2140":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","2157":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","2231":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","2238":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","2241":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","2248":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","2252":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","2268":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","2274":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","2288":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","2305":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","2313":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","2347":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","2349":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","2370":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","2375":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","2376":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","2378":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","2379":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","2381":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","2383":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","2412":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","2426":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","2438":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","2457":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","2481":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","2510":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","2522":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","2534":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","2545":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","2596":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","2633":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","2687":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","2697":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","2706":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","2734":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","2738":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","2778":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","2835":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","2839":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","2849":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","2862":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","2875":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","2893":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","2910":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","2973":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","2976":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","2981":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","3000":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","3002":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","3006":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","3008":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","3012":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","3014":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","3028":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","3030":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","3041":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","3056":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","3067":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","3086":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","3100":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","3102":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","3110":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","3127":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","3129":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","3131":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","3138":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","3155":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","3200":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","3235":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","3255":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","3258":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","3261":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","3285":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","3422":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","3447":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","3504":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","3508":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","3535":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","3538":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","3583":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","3641":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","3655":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","3663":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","3675":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","4093":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","4109":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","4139":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","4148":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","4190":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","4202":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","4219":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","4228":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","4246":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","4275":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","4284":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","4317":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","4392":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","4411":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","4417":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","4455":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","4480":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","4527":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","4597":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","4605":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","4616":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","4621":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","4629":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","4671":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","4831":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","4887":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","4890":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","4899":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","4912":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","5319":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","5339":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","5364":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","5378":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","5405":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","5563":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","5568":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","5574":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","5622":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","5674":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","5717":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","5749":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","5771":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","5830":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","5857":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","5879":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","5882":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","5894":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","5906":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","5970":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","5973":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","5979":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA"},"packages/cli/src/modules/instance-ai/internal-messages.ts":{"55":[36,78,80,100,38,81,82],"64":[36,78,80,100,38,81,82],"83":[36,78,80,100,38,81,82],"98":[36,78,80,100,38,81,82]},"packages/cli/src/modules/instance-ai/liveness/instance-ai-liveness.service.ts":{"17":[36,78,79,80,100,38,81,39,40,82],"106":[36,78,79,80,100,38,81,39,40,82],"108":[36,78,79,80,100,38,81,39,40,82],"112":[36,78,79,80,100,38,81,39,40,82],"115":[36,78,79,80,100,38,81,39,40,82],"120":[36,78,79,80,100,38,81,39,40,82],"129":[36,78,79,80,100,38,81,39,40,82],"133":[36,78,79,80,100,38,81,39,40,82],"137":[36,78,79,80,100,38,81,39,40,82],"143":[36,78,79,80,100,38,81,39,40,82],"152":[36,78,79,80,100,38,81,39,40,82],"156":[36,78,79,80,100,38,81,39,40,82],"197":[36,78,79,80,100,38,81,39,40,82],"216":[36,78,79,80,100,38,81,39,40,82],"226":[36,78,79,80,100,38,81,39,40,82],"235":[36,78,79,80,100,38,81,39,40,82],"239":[36,78,79,80,100,38,81,39,40,82]},"packages/cli/src/modules/instance-ai/mcp/instance-ai-mcp-registry.service.ts":{"54":[36,78,80,100,38,81,82],"59":[36,78,80,100,38,81,82],"63":[36,78,80,100,38,81,82],"68":[36,78,80,100,38,81,82],"72":[36,78,80,100,38,81,82],"77":[36,78,80,100,38,81,82],"88":[36,78,80,100,38,81,82],"100":[36,78,80,100,38,81,82],"105":[36,78,80,100,38,81,82],"108":[36,78,80,100,38,81,82],"127":[36,78,80,100,38,81,82],"132":[36,78,80,100,38,81,82],"144":[36,78,80,100,38,81,82],"159":[36,78,80,100,38,81,82],"163":[36,78,80,100,38,81,82],"223":[36,78,80,100,38,81,82],"236":[36,78,80,100,38,81,82],"254":[36,78,80,100,38,81,82],"302":[36,78,80,100,38,81,82],"304":[36,78,80,100,38,81,82],"314":[36,78,80,100,38,81,82],"320":[36,78,80,100,38,81,82],"321":[36,78,80,100,38,81,82],"323":[36,78,80,100,38,81,82],"396":[36,78,80,100,38,81,82],"422":[36,78,80,100,38,81,82],"440":[36,78,80,100,38,81,82],"454":[36,78,80,100,38,81,82],"507":[36,78,80,100,38,81,82],"528":[36,78,80,100,38,81,82],"559":[36,78,80,100,38,81,82]},"packages/cli/src/modules/instance-ai/message-parser.ts":{"77":[79,80,81,82],"83":[79,80,81,82],"89":[79,80,81,82],"91":[79,80,81,82],"98":[79,80,81,82],"100":[79,80,81,82],"107":[79,80,81,82],"112":[79,80,81,82],"116":[79,80,81,82],"122":[79,80,81,82],"156":[79,80,81,82],"159":[79,80,81,82],"180":[79,80,81,82],"209":[79,80,81,82],"258":[79,80,81,82],"270":[79,80,81,82],"289":[79,80,81,82],"310":[79,80,81,82],"323":[79,80,81,82],"327":[79,80,81,82],"331":[79,80,81,82],"335":[79,80,81,82],"346":[79,80,81,82],"377":[79,80,81,82],"385":[79,80,81,82],"399":[79,80,81,82],"408":[79,80,81,82],"421":[79,80,81,82],"486":[79,80,81,82],"624":[79,80,81,82],"634":[79,80,81,82],"664":[79,80,81,82],"673":[79,80,81,82],"677":[79,80,81,82],"694":[79,80,81,82],"700":[79,80,81,82],"712":[79,80,81,82]},"packages/cli/src/modules/instance-ai/node-definition-resolver.ts":{"20":[36,78,80,100,38,81,82],"21":[36,78,80,100,38,81,82],"24":[36,78,80,100,38,81,82],"51":[36,78,80,100,38,81,82],"78":[36,78,80,100,38,81,82],"93":[36,78,80,100,38,81,82],"121":[36,78,80,100,38,81,82],"131":[36,78,80,100,38,81,82],"136":[36,78,80,100,38,81,82],"137":[36,78,80,100,38,81,82],"174":[36,78,80,100,38,81,82],"175":[36,78,80,100,38,81,82],"183":[36,78,80,100,38,81,82],"194":[36,78,80,100,38,81,82],"198":[36,78,80,100,38,81,82],"221":[36,78,80,100,38,81,82],"249":[36,78,80,100,38,81,82],"250":[36,78,80,100,38,81,82],"252":[36,78,80,100,38,81,82],"253":[36,78,80,100,38,81,82],"295":[36,78,80,100,38,81,82],"318":[36,78,80,100,38,81,82],"322":[36,78,80,100,38,81,82],"326":[36,78,80,100,38,81,82],"327":[36,78,80,100,38,81,82],"337":[36,78,80,100,38,81,82],"343":[36,78,80,100,38,81,82],"375":[36,78,80,100,38,81,82],"398":[36,78,80,100,38,81,82]},"packages/cli/src/modules/instance-ai/output-redaction-config.ts":{"18":[36,78,80,100,38,81,82],"24":[36,78,80,100,38,81,82],"25":[36,78,80,100,38,81,82]},"packages/cli/src/modules/instance-ai/planned-task-action-runner.ts":{"94":[36,78,80,100,81,82],"96":[36,78,80,100,81,82],"115":[36,78,80,100,81,82],"131":[36,78,80,100,81,82],"157":[36,78,80,100,81,82],"169":[36,78,80,100,81,82],"194":[36,78,80,100,81,82],"212":[36,78,80,100,81,82],"234":[36,78,80,100,81,82],"252":[36,78,80,100,81,82]},"packages/cli/src/modules/instance-ai/repositories/instance-ai-checkpoint.repository.ts":{"8":[79,80,81,82],"21":[79,80,81,82],"34":[79,80,81,82]},"packages/cli/src/modules/instance-ai/repositories/instance-ai-event-log.repository.ts":{"17":[36,78,79,80,100,38,81,82],"22":[36,78,79,80,100,38,81,82],"39":[36,78,79,80,100,38,81,82],"45":[36,78,79,80,100,38,81,82],"64":[36,78,79,80,100,38,81,82],"69":[36,78,79,80,100,38,81,82],"74":[36,78,79,80,100,38,81,82],"77":[36,78,79,80,100,38,81,82],"84":[36,78,79,80,100,38,81,82],"89":[36,78,79,80,100,38,81,82],"93":[36,78,79,80,100,38,81,82],"101":[36,78,79,80,100,38,81,82],"118":[36,78,79,80,100,38,81,82],"125":[36,78,79,80,100,38,81,82],"144":[36,78,79,80,100,38,81,82]},"packages/cli/src/modules/instance-ai/repositories/instance-ai-pending-confirmation.repository.ts":{"8":[36,80,37,81,39,40],"21":[36,80,37,81,39,40],"25":[36,80,37,81,39,40],"48":[36,80,37,81,39,40],"53":[36,80,37,81,39,40],"58":[36,80,37,81,39,40],"63":[36,80,37,81,39,40],"76":[36,80,37,81,39,40],"81":[36,80,37,81,39,40],"87":[36,80,37,81,39,40],"96":[36,80,37,81,39,40]},"packages/cli/src/modules/instance-ai/repositories/instance-ai-thread-grant.repository.ts":{"8":[36,78,80,100,38,81,82],"16":[36,78,80,100,38,81,82],"25":[36,78,80,100,38,81,82],"30":[36,78,80,100,38,81,82]},"packages/cli/src/modules/instance-ai/sandbox/instance-ai-sandbox.service.ts":{"41":[36,78,80,37,100,38,81,39,40,82],"45":[36,78,80,37,100,38,81,39,40,82],"53":[36,78,80,37,100,38,81,39,40,82],"61":[36,78,80,37,100,38,81,39,40,82],"65":[36,78,80,37,100,38,81,39,40,82],"78":[36,78,80,37,100,38,81,39,40,82],"91":[36,78,80,37,100,38,81,39,40,82],"173":[36,78,80,37,100,38,81,39,40,82],"175":[36,78,80,37,100,38,81,39,40,82],"179":[36,78,80,37,100,38,81,39,40,82],"183":[36,78,80,37,100,38,81,39,40,82],"240":[36,78,80,37,100,38,81,39,40,82],"249":[36,78,80,37,100,38,81,39,40,82],"258":[36,78,80,37,100,38,81,39,40,82],"261":[36,78,80,37,100,38,81,39,40,82],"291":[36,78,80,37,100,38,81,39,40,82],"338":[36,78,80,37,100,38,81,39,40,82],"348":[36,78,80,37,100,38,81,39,40,82],"355":[36,78,80,37,100,38,81,39,40,82],"358":[36,78,80,37,100,38,81,39,40,82],"365":[36,78,80,37,100,38,81,39,40,82],"405":[36,78,80,37,100,38,81,39,40,82],"419":[36,78,80,37,100,38,81,39,40,82],"427":[36,78,80,37,100,38,81,39,40,82],"438":[36,78,80,37,100,38,81,39,40,82],"454":[36,78,80,37,100,38,81,39,40,82],"458":[36,78,80,37,100,38,81,39,40,82],"462":[36,78,80,37,100,38,81,39,40,82],"466":[36,78,80,37,100,38,81,39,40,82],"472":[36,78,80,37,100,38,81,39,40,82],"480":[36,78,80,37,100,38,81,39,40,82],"487":[36,78,80,37,100,38,81,39,40,82],"499":[36,78,80,37,100,38,81,39,40,82]},"packages/cli/src/modules/instance-ai/sandbox-provider.ts":{"6":[36,78,80,100,38,81,82],"14":[36,78,80,100,38,81,82]},"packages/cli/src/modules/instance-ai/storage/db-snapshot-storage.ts":{"14":[36,78,79,80,38,81,82],"37":[36,78,79,80,38,81,82],"39":[36,78,79,80,38,81,82],"76":[36,78,79,80,38,81,82],"99":[36,78,79,80,38,81,82],"165":[36,78,79,80,38,81,82],"175":[36,78,79,80,38,81,82],"180":[36,78,79,80,38,81,82],"199":[36,78,79,80,38,81,82]},"packages/cli/src/modules/instance-ai/storage/typeorm-agent-memory.ts":{"45":[36,78,79,80,100,38,81,39,82],"53":[36,78,79,80,100,38,81,39,82],"63":[36,78,79,80,100,38,81,39,82],"74":[36,78,79,80,100,38,81,39,82],"78":[36,78,79,80,100,38,81,39,82],"84":[36,78,79,80,100,38,81,39,82],"89":[36,78,79,80,100,38,81,39,82],"109":[36,78,79,80,100,38,81,39,82],"117":[36,78,79,80,100,38,81,39,82],"124":[36,78,79,80,100,38,81,39,82],"129":[36,78,79,80,100,38,81,39,82],"135":[36,78,79,80,100,38,81,39,82],"141":[36,78,79,80,100,38,81,39,82],"157":[36,78,79,80,100,38,81,39,82],"164":[36,78,79,80,100,38,81,39,82],"185":[36,78,79,80,100,38,81,39,82],"200":[36,78,79,80,100,38,81,39,82],"206":[36,78,79,80,100,38,81,39,82],"214":[36,78,79,80,100,38,81,39,82],"219":[36,78,79,80,100,38,81,39,82],"244":[36,78,79,80,100,38,81,39,82],"245":[36,78,79,80,100,38,81,39,82],"273":[36,78,79,80,100,38,81,39,82],"291":[36,78,79,80,100,38,81,39,82],"295":[36,78,79,80,100,38,81,39,82],"322":[36,78,79,80,100,38,81,39,82],"326":[36,78,79,80,100,38,81,39,82],"340":[36,78,79,80,100,38,81,39,82],"344":[36,78,79,80,100,38,81,39,82],"349":[36,78,79,80,100,38,81,39,82],"355":[36,78,79,80,100,38,81,39,82],"356":[36,78,79,80,100,38,81,39,82],"360":[36,78,79,80,100,38,81,39,82],"373":[36,78,79,80,100,38,81,39,82],"382":[36,78,79,80,100,38,81,39,82],"389":[36,78,79,80,100,38,81,39,82],"409":[36,78,79,80,100,38,81,39,82],"422":[36,78,79,80,100,38,81,39,82],"428":[36,78,79,80,100,38,81,39,82],"443":[36,78,79,80,100,38,81,39,82],"450":[36,78,79,80,100,38,81,39,82],"457":[36,78,79,80,100,38,81,39,82],"474":[36,78,79,80,100,38,81,39,82],"479":[36,78,79,80,100,38,81,39,82],"488":[36,78,79,80,100,38,81,39,82],"509":[36,78,79,80,100,38,81,39,82],"515":[36,78,79,80,100,38,81,39,82],"521":[36,78,79,80,100,38,81,39,82],"525":[36,78,79,80,100,38,81,39,82],"532":[36,78,79,80,100,38,81,39,82],"536":[36,78,79,80,100,38,81,39,82],"540":[36,78,79,80,100,38,81,39,82],"547":[36,78,79,80,100,38,81,39,82],"551":[36,78,79,80,100,38,81,39,82],"555":[36,78,79,80,100,38,81,39,82],"567":[36,78,79,80,100,38,81,39,82],"571":[36,78,79,80,100,38,81,39,82],"576":[36,78,79,80,100,38,81,39,82],"588":[36,78,79,80,100,38,81,39,82]},"packages/cli/src/modules/instance-ai/storage/typeorm-observation-log-store.ts":{"30":[36,78,80,100,38,81,82],"47":[36,78,80,100,38,81,82],"55":[36,78,80,100,38,81,82],"60":[36,78,80,100,38,81,82],"74":[36,78,80,100,38,81,82],"77":[36,78,80,100,38,81,82],"83":[36,78,80,100,38,81,82],"98":[36,78,80,100,38,81,82],"101":[36,78,80,100,38,81,82],"125":[36,78,80,100,38,81,82],"132":[36,78,80,100,38,81,82],"138":[36,78,80,100,38,81,82],"144":[36,78,80,100,38,81,82],"148":[36,78,80,100,38,81,82],"160":[36,78,80,100,38,81,82],"166":[36,78,80,100,38,81,82],"205":[36,78,80,100,38,81,82],"206":[36,78,80,100,38,81,82],"211":[36,78,80,100,38,81,82],"224":[36,78,80,100,38,81,82],"239":[36,78,80,100,38,81,82],"290":[36,78,80,100,38,81,82]},"packages/cli/src/modules/instance-ai/suspended-thread-persistence.service.ts":{"7":[36,80,37,38,81,39,40],"50":[36,80,37,38,81,39,40],"56":[36,80,37,38,81,39,40],"71":[36,80,37,38,81,39,40],"82":[36,80,37,38,81,39,40],"118":[36,80,37,38,81,39,40],"129":[36,80,37,38,81,39,40]},"packages/cli/src/modules/instance-ai/trace-replay-state.ts":{"12":[36,78,80,177,100,38,81,82],"19":[36,78,80,177,100,38,81,82],"23":[36,78,80,177,100,38,81,82],"28":[36,78,80,177,100,38,81,82],"32":[36,78,80,177,100,38,81,82],"63":[36,78,80,177,100,38,81,82],"81":[36,78,80,177,100,38,81,82],"85":[36,78,80,177,100,38,81,82],"90":[36,78,80,177,100,38,81,82],"94":[36,78,80,177,100,38,81,82],"98":[36,78,80,177,100,38,81,82],"118":[36,78,80,177,100,38,81,82],"128":[36,78,80,177,100,38,81,82],"154":[36,78,80,177,100,38,81,82],"166":[36,78,80,177,100,38,81,82]},"packages/cli/src/modules/instance-ai/tracing/instance-ai-tracing.service.ts":{"34":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","111":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","119":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","145":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","149":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","153":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","154":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","157":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","161":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","170":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","172":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","175":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","220":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","224":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","264":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","270":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","292":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","307":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","324":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","333":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","340":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","391":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","427":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","447":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","468":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","473":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","516":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","520":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","524":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA","528":"b:AAAAAPABAAAAwAcAEAAAAAAAAAAAAAIA"},"packages/cli/src/modules/instance-ai/workflow-verification-task-projector.ts":{"43":[36,78,80,100,38,81,82],"48":[36,78,80,100,38,81,82],"52":[36,78,80,100,38,81,82],"60":[36,78,80,100,38,81,82],"65":[36,78,80,100,38,81,82],"85":[36,78,80,100,38,81,82],"100":[36,78,80,100,38,81,82],"115":[36,78,80,100,38,81,82],"127":[36,78,80,100,38,81,82],"145":[36,78,80,100,38,81,82],"161":[36,78,80,100,38,81,82],"170":[36,78,80,100,38,81,82],"178":[36,78,80,100,38,81,82],"188":[36,78,80,100,38,81,82],"197":[36,78,80,100,38,81,82],"214":[36,78,80,100,38,81,82],"222":[36,78,80,100,38,81,82],"240":[36,78,80,100,38,81,82],"281":[36,78,80,100,38,81,82],"289":[36,78,80,100,38,81,82],"314":[36,78,80,100,38,81,82],"336":[36,78,80,100,38,81,82],"382":[36,78,80,100,38,81,82],"392":[36,78,80,100,38,81,82],"398":[36,78,80,100,38,81,82]},"packages/cli/src/node-catalog/node-catalog.service.ts":{"19":"b:AAQQAFAFAAAAQAcAEAAAAAAAAAAAEAAA","20":"b:AAQQAFAFAAAAQAcAEAAAAAAAAAAAEAAA","22":"b:AAQQAFAFAAAAQAcAEAAAAAAAAAAAEAAA","28":"b:AAQQAFAFAAAAQAcAEAAAAAAAAAAAEAAA","31":"b:AAQQAFAFAAAAQAcAEAAAAAAAAAAAEAAA","39":"b:AAQQAFAFAAAAQAcAEAAAAAAAAAAAEAAA","77":"b:AAQQAFAFAAAAQAcAEAAAAAAAAAAAEAAA","80":"b:AAQQAFAFAAAAQAcAEAAAAAAAAAAAEAAA","137":"b:AAQQAFAFAAAAQAcAEAAAAAAAAAAAEAAA","141":"b:AAQQAFAFAAAAQAcAEAAAAAAAAAAAEAAA","144":"b:AAQQAFAFAAAAQAcAEAAAAAAAAAAAEAAA","149":"b:AAQQAFAFAAAAQAcAEAAAAAAAAAAAEAAA","156":"b:AAQQAFAFAAAAQAcAEAAAAAAAAAAAEAAA","170":"b:AAQQAFAFAAAAQAcAEAAAAAAAAAAAEAAA","190":"b:AAQQAFAFAAAAQAcAEAAAAAAAAAAAEAAA","202":"b:AAQQAFAFAAAAQAcAEAAAAAAAAAAAEAAA","204":"b:AAQQAFAFAAAAQAcAEAAAAAAAAAAAEAAA","246":"b:AAQQAFAFAAAAQAcAEAAAAAAAAAAAEAAA","264":"b:AAQQAFAFAAAAQAcAEAAAAAAAAAAAEAAA","275":"b:AAQQAFAFAAAAQAcAEAAAAAAAAAAAEAAA","294":"b:AAQQAFAFAAAAQAcAEAAAAAAAAAAAEAAA","313":"b:AAQQAFAFAAAAQAcAEAAAAAAAAAAAEAAA","333":"b:AAQQAFAFAAAAQAcAEAAAAAAAAAAAEAAA","342":"b:AAQQAFAFAAAAQAcAEAAAAAAAAAAAEAAA","354":"b:AAQQAFAFAAAAQAcAEAAAAAAAAAAAEAAA","385":"b:AAQQAFAFAAAAQAcAEAAAAAAAAAAAEAAA","431":"b:AAQQAFAFAAAAQAcAEAAAAAAAAAAAEAAA","437":"b:AAQQAFAFAAAAQAcAEAAAAAAAAAAAEAAA","440":"b:AAQQAFAFAAAAQAcAEAAAAAAAAAAAEAAA","445":"b:AAQQAFAFAAAAQAcAEAAAAAAAAAAAEAAA","450":"b:AAQQAFAFAAAAQAcAEAAAAAAAAAAAEAAA","454":"b:AAQQAFAFAAAAQAcAEAAAAAAAAAAAEAAA"},"packages/cli/src/utils/ai-proxy-fetch.ts":{"15":[20,36,78,80,100,38,81,82],"34":[20,36,78,80,100,38,81,82],"52":[20,36,78,80,100,38,81,82]},"packages/@n8n/backend-common/src/logging/logger.ts":{"20":"b:SoEABKoAABEAAmQMAQAACAAIAiAYIAEB","30":"b:SoEABKoAABEAAmQMAQAACAAIAiAYIAEB","41":"b:SoEABKoAABEAAmQMAQAACAAIAiAYIAEB","71":"b:SoEABKoAABEAAmQMAQAACAAIAiAYIAEB","76":"b:SoEABKoAABEAAmQMAQAACAAIAiAYIAEB","88":"b:SoEABKoAABEAAmQMAQAACAAIAiAYIAEB","109":"b:SoEABKoAABEAAmQMAQAACAAIAiAYIAEB","130":"b:SoEABKoAABEAAmQMAQAACAAIAiAYIAEB","142":"b:SoEABKoAABEAAmQMAQAACAAIAiAYIAEB","151":"b:SoEABKoAABEAAmQMAQAACAAIAiAYIAEB","159":"b:SoEABKoAABEAAmQMAQAACAAIAiAYIAEB","163":"b:SoEABKoAABEAAmQMAQAACAAIAiAYIAEB","169":"b:SoEABKoAABEAAmQMAQAACAAIAiAYIAEB","170":"b:SoEABKoAABEAAmQMAQAACAAIAiAYIAEB","176":"b:SoEABKoAABEAAmQMAQAACAAIAiAYIAEB","182":"b:SoEABKoAABEAAmQMAQAACAAIAiAYIAEB","192":"b:SoEABKoAABEAAmQMAQAACAAIAiAYIAEB","195":"b:SoEABKoAABEAAmQMAQAACAAIAiAYIAEB","198":"b:SoEABKoAABEAAmQMAQAACAAIAiAYIAEB","208":"b:SoEABKoAABEAAmQMAQAACAAIAiAYIAEB","214":"b:SoEABKoAABEAAmQMAQAACAAIAiAYIAEB","221":"b:SoEABKoAABEAAmQMAQAACAAIAiAYIAEB","223":"b:SoEABKoAABEAAmQMAQAACAAIAiAYIAEB","231":"b:SoEABKoAABEAAmQMAQAACAAIAiAYIAEB","245":"b:SoEABKoAABEAAmQMAQAACAAIAiAYIAEB","264":"b:SoEABKoAABEAAmQMAQAACAAIAiAYIAEB","268":"b:SoEABKoAABEAAmQMAQAACAAIAiAYIAEB","272":"b:SoEABKoAABEAAmQMAQAACAAIAiAYIAEB","276":"b:SoEABKoAABEAAmQMAQAACAAIAiAYIAEB","284":"b:SoEABKoAABEAAmQMAQAACAAIAiAYIAEB"},"packages/@n8n/db/src/entities/credentials-entity.ts":{"69":[173,8,64]},"packages/@n8n/db/src/repositories/credential-dependency.repository.ts":{"25":"b:ekEEGAKAzjABAgAACgACIAAAAAAgIAAA","62":"b:ekEEGAKAzjABAgAACgACIAAAAAAgIAAA","66":"b:ekEEGAKAzjABAgAACgACIAAAAAAgIAAA","75":"b:ekEEGAKAzjABAgAACgACIAAAAAAgIAAA","78":"b:ekEEGAKAzjABAgAACgACIAAAAAAgIAAA","93":"b:ekEEGAKAzjABAgAACgACIAAAAAAgIAAA","103":"b:ekEEGAKAzjABAgAACgACIAAAAAAgIAAA","116":"b:ekEEGAKAzjABAgAACgACIAAAAAAgIAAA","117":"b:ekEEGAKAzjABAgAACgACIAAAAAAgIAAA","118":"b:ekEEGAKAzjABAgAACgACIAAAAAAgIAAA","138":"b:ekEEGAKAzjABAgAACgACIAAAAAAgIAAA","149":"b:ekEEGAKAzjABAgAACgACIAAAAAAgIAAA"},"packages/@n8n/db/src/repositories/secrets-provider-connection.repository.ee.ts":{"8":"b:ekEFGQuAzjAAAwAAShACIAAAEAAiIAAA","12":"b:ekEFGQuAzjAAAwAAShACIAAAEAAiIAAA","16":"b:ekEFGQuAzjAAAwAAShACIAAAEAAiIAAA","25":"b:ekEFGQuAzjAAAwAAShACIAAAEAAiIAAA","33":"b:ekEFGQuAzjAAAwAAShACIAAAEAAiIAAA","36":"b:ekEFGQuAzjAAAwAAShACIAAAEAAiIAAA","55":"b:ekEFGQuAzjAAAwAAShACIAAAEAAiIAAA","89":"b:ekEFGQuAzjAAAwAAShACIAAAEAAiIAAA","119":"b:ekEFGQuAzjAAAwAAShACIAAAEAAiIAAA","125":"b:ekEFGQuAzjAAAwAAShACIAAAEAAiIAAA","140":"b:ekEFGQuAzjAAAwAAShACIAAAEAAiIAAA","164":"b:ekEFGQuAzjAAAwAAShACIAAAEAAiIAAA","182":"b:ekEFGQuAzjAAAwAAShACIAAAEAAiIAAA","190":"b:ekEFGQuAzjAAAwAAShACIAAAEAAiIAAA","200":"b:ekEFGQuAzjAAAwAAShACIAAAEAAiIAAA","210":"b:ekEFGQuAzjAAAwAAShACIAAAEAAiIAAA","222":"b:ekEFGQuAzjAAAwAAShACIAAAEAAiIAAA"},"packages/@n8n/db/src/repositories/shared-credentials.repository.ts":{"12":"b:ekEFGgOAzjEBAwAgCAACVAAAAAAgIAAA","16":"b:ekEFGgOAzjEBAwAgCAACVAAAAAAgIAAA","26":"b:ekEFGgOAzjEBAwAgCAACVAAAAAAgIAAA","36":"b:ekEFGgOAzjEBAwAgCAACVAAAAAAgIAAA","41":"b:ekEFGgOAzjEBAwAgCAACVAAAAAAgIAAA","52":"b:ekEFGgOAzjEBAwAgCAACVAAAAAAgIAAA","61":"b:ekEFGgOAzjEBAwAgCAACVAAAAAAgIAAA","73":"b:ekEFGgOAzjEBAwAgCAACVAAAAAAgIAAA","76":"b:ekEFGgOAzjEBAwAgCAACVAAAAAAgIAAA","85":"b:ekEFGgOAzjEBAwAgCAACVAAAAAAgIAAA","94":"b:ekEFGgOAzjEBAwAgCAACVAAAAAAgIAAA","99":"b:ekEFGgOAzjEBAwAgCAACVAAAAAAgIAAA","112":"b:ekEFGgOAzjEBAwAgCAACVAAAAAAgIAAA","128":"b:ekEFGgOAzjEBAwAgCAACVAAAAAAgIAAA","156":"b:ekEFGgOAzjEBAwAgCAACVAAAAAAgIAAA","165":"b:ekEFGgOAzjEBAwAgCAACVAAAAAAgIAAA","166":"b:ekEFGgOAzjEBAwAgCAACVAAAAAAgIAAA","181":"b:ekEFGgOAzjEBAwAgCAACVAAAAAAgIAAA","182":"b:ekEFGgOAzjEBAwAgCAACVAAAAAAgIAAA","192":"b:ekEFGgOAzjEBAwAgCAACVAAAAAAgIAAA"},"packages/@n8n/db/src/utils/is-unique-constraint-error.ts":{"6":"b:AgEABKkAAAEAAkAMAgAACAAIAgAIIAEB"},"packages/@n8n/utils/src/errors/ensure-error.ts":{"2":"b:AgkABKkEAAEAAkAMAgAACAAIAgAYMAEB"},"packages/cli/src/credential-types.ts":{"8":"b:egEEHAKAzjAQAgAACAAAAAAAAgAgIAAA","10":"b:egEEHAKAzjAQAgAACAAAAAAAAgAgIAAA","15":"b:egEEHAKAzjAQAgAACAAAAAAAAgAgIAAA","19":"b:egEEHAKAzjAQAgAACAAAAAAAAgAgIAAA","26":"b:egEEHAKAzjAQAgAACAAAAAAAAgAgIAAA"},"packages/cli/src/credentials/credential-connection-status-proxy.ts":{"16":[3,4,96,32,173,54],"20":[3,4,96,32,173,54],"25":[3,4,96,32,173,54],"30":[3,4,96,32,173,54],"35":[3,4,96,32,173,54],"44":[3,4,96,32,173,54]},"packages/cli/src/credentials/credential-dependency.service.ts":{"18":"b:ekEEGAKAzjABAgAACgACIAAAAAAgIAAA","23":"b:ekEEGAKAzjABAgAACgACIAAAAAAgIAAA","38":"b:ekEEGAKAzjABAgAACgACIAAAAAAgIAAA","45":"b:ekEEGAKAzjABAgAACgACIAAAAAAgIAAA","63":"b:ekEEGAKAzjABAgAACgACIAAAAAAgIAAA","81":"b:ekEEGAKAzjABAgAACgACIAAAAAAgIAAA","97":"b:ekEEGAKAzjABAgAACgACIAAAAAAgIAAA"},"packages/cli/src/credentials/credentials.service.ee.ts":{"29":[18,5,32,173,8,33,9,54,124,64,73],"42":[18,5,32,173,8,33,9,54,124,64,73],"81":[18,5,32,173,8,33,9,54,124,64,73],"83":[18,5,32,173,8,33,9,54,124,64,73],"87":[18,5,32,173,8,33,9,54,124,64,73],"98":[18,5,32,173,8,33,9,54,124,64,73],"102":[18,5,32,173,8,33,9,54,124,64,73],"190":[18,5,32,173,8,33,9,54,124,64,73],"203":[18,5,32,173,8,33,9,54,124,64,73],"251":[18,5,32,173,8,33,9,54,124,64,73],"253":[18,5,32,173,8,33,9,54,124,64,73]},"packages/cli/src/credentials/external-secrets.utils.ts":{"14":"b:ekEEGAKAzjAAAgAACAACAAAAAAAgIAAA","23":"b:ekEEGAKAzjAAAgAACAACAAAAAAAgIAAA","32":"b:ekEEGAKAzjAAAgAACAACAAAAAAAgIAAA","70":"b:ekEEGAKAzjAAAgAACAACAAAAAAAgIAAA"},"packages/cli/src/credentials/validation.ts":{"19":"b:ekEEGAKAzjABAgAACgACIAAAAAAgIAAA","26":"b:ekEEGAKAzjABAgAACgACIAAAAAAgIAAA","54":"b:ekEEGAKAzjABAgAACgACIAAAAAAgIAAA","90":"b:ekEEGAKAzjABAgAACgACIAAAAAAgIAAA","133":"b:ekEEGAKAzjABAgAACgACIAAAAAAgIAAA","151":"b:ekEEGAKAzjABAgAACgACIAAAAAAgIAAA","152":"b:ekEEGAKAzjABAgAACgACIAAAAAAgIAAA","164":"b:ekEEGAKAzjABAgAACgACIAAAAAAgIAAA"},"packages/cli/src/credentials-helper.ts":{"68":"b:ekkEHAKAzjARAgAAigACAAAAEgAkIEAA","71":"b:ekkEHAKAzjARAgAAigACAAAAEgAkIEAA","74":"b:ekkEHAKAzjARAgAAigACAAAAEgAkIEAA","86":"b:ekkEHAKAzjARAgAAigACAAAAEgAkIEAA","102":"b:ekkEHAKAzjARAgAAigACAAAAEgAkIEAA","133":"b:ekkEHAKAzjARAgAAigACAAAAEgAkIEAA","134":"b:ekkEHAKAzjARAgAAigACAAAAEgAkIEAA","160":"b:ekkEHAKAzjARAgAAigACAAAAEgAkIEAA","170":"b:ekkEHAKAzjARAgAAigACAAAAEgAkIEAA","231":"b:ekkEHAKAzjARAgAAigACAAAAEgAkIEAA","247":"b:ekkEHAKAzjARAgAAigACAAAAEgAkIEAA","276":"b:ekkEHAKAzjARAgAAigACAAAAEgAkIEAA","283":"b:ekkEHAKAzjARAgAAigACAAAAEgAkIEAA","299":"b:ekkEHAKAzjARAgAAigACAAAAEgAkIEAA","330":"b:ekkEHAKAzjARAgAAigACAAAAEgAkIEAA","349":"b:ekkEHAKAzjARAgAAigACAAAAEgAkIEAA","387":"b:ekkEHAKAzjARAgAAigACAAAAEgAkIEAA","418":"b:ekkEHAKAzjARAgAAigACAAAAEgAkIEAA","427":"b:ekkEHAKAzjARAgAAigACAAAAEgAkIEAA","462":"b:ekkEHAKAzjARAgAAigACAAAAEgAkIEAA","561":"b:ekkEHAKAzjARAgAAigACAAAAEgAkIEAA","670":"b:ekkEHAKAzjARAgAAigACAAAAEgAkIEAA","695":"b:ekkEHAKAzjARAgAAigACAAAAEgAkIEAA","749":"b:ekkEHAKAzjARAgAAigACAAAAEgAkIEAA"},"packages/cli/src/credentials-overwrites.ts":{"23":"b:ekEEGAKAzjAAAgAAiAACAAAAAAAgIAAA","34":"b:ekEEGAKAzjAAAgAAiAACAAAAAAAgIAAA","38":"b:ekEEGAKAzjAAAgAAiAACAAAAAAAgIAAA","60":"b:ekEEGAKAzjAAAgAAiAACAAAAAAAgIAAA","64":"b:ekEEGAKAzjAAAgAAiAACAAAAAAAgIAAA","86":"b:ekEEGAKAzjAAAgAAiAACAAAAAAAgIAAA","91":"b:ekEEGAKAzjAAAgAAiAACAAAAAAAgIAAA","105":"b:ekEEGAKAzjAAAgAAiAACAAAAAAAgIAAA","110":"b:ekEEGAKAzjAAAgAAiAACAAAAAAAgIAAA","125":"b:ekEEGAKAzjAAAgAAiAACAAAAAAAgIAAA","140":"b:ekEEGAKAzjAAAgAAiAACAAAAAAAgIAAA","152":"b:ekEEGAKAzjAAAgAAiAACAAAAAAAgIAAA","178":"b:ekEEGAKAzjAAAgAAiAACAAAAAAAgIAAA","210":"b:ekEEGAKAzjAAAgAAiAACAAAAAAAgIAAA","214":"b:ekEEGAKAzjAAAgAAiAACAAAAAAAgIAAA","215":"b:ekEEGAKAzjAAAgAAiAACAAAAAAAgIAAA","220":"b:ekEEGAKAzjAAAgAAiAACAAAAAAAgIAAA","225":"b:ekEEGAKAzjAAAgAAiAACAAAAAAAgIAAA","229":"b:ekEEGAKAzjAAAgAAiAACAAAAAAAgIAAA","233":"b:ekEEGAKAzjAAAgAAiAACAAAAAAAgIAAA","240":"b:ekEEGAKAzjAAAgAAiAACAAAAAAAgIAAA"},"packages/cli/src/errors/http-error-classifier.ts":{"6":"b:CgkABKgAAAEAAkAMAQAACAAIAgAYIAEB","41":"b:CgkABKgAAAEAAkAMAQAACAAIAgAYIAEB","58":"b:CgkABKgAAAEAAkAMAQAACAAIAgAYIAEB"},"packages/cli/src/errors/http-error-serializers.ts":{"15":"b:CgkABKgAAAEAAkAMAQAACAAIAgAYIAEB","61":"b:CgkABKgAAAEAAkAMAQAACAAIAgAYIAEB"},"packages/cli/src/errors/response-errors/abstract/response.error.ts":{"19":"b:CAkAAKAAAAEAAEAMAQAAAAAAABAIMAEB"},"packages/cli/src/errors/response-errors/not-found.error.ts":{"4":[163,90,91,97,32,173,11,54,55],"14":[163,90,91,97,32,173,11,54,55]},"packages/cli/src/middlewares/list-query/filter.ts":{"11":"b:AAEBOwIA8DGBG3AgAgAAAQAAAAAAKAAA"},"packages/cli/src/middlewares/list-query/pagination.ts":{"10":"b:AAEBOwIA8DGBG3AgAgAAAQAAAAAAKAAA"},"packages/cli/src/middlewares/list-query/select.ts":{"11":"b:AAEBOwIA8DGBG3AgAgAAAQAAAAAAKAAA"},"packages/cli/src/middlewares/list-query/sort-by.ts":{"12":"b:AAEBOwIA8DGBG3AgAgAAAQAAAAAAKAAA"},"packages/cli/src/modules/dynamic-credentials.ee/services/credential-connection-status.service.ts":{"25":[3,4,96,32,173,54],"41":[3,4,96,32,173,54],"49":[3,4,96,32,173,54],"61":[3,4,96,32,173,54],"69":[3,4,96,32,173,54],"79":[3,4,96,32,173,54],"83":[3,4,96,32,173,54],"88":[3,4,96,32,173,54],"106":[3,4,96,32,173,54],"115":[3,4,96,32,173,54],"126":[3,4,96,32,173,54],"136":[3,4,96,32,173,54],"139":[3,4,96,32,173,54],"144":[3,4,96,32,173,54],"147":[3,4,96,32,173,54],"149":[3,4,96,32,173,54],"177":[3,4,96,32,173,54],"182":[3,4,96,32,173,54]},"packages/cli/src/response-helper.ts":{"16":"b:CgkABKkAAAEAAkAMAwAACAAIAgAYIAEB","49":"b:CgkABKkAAAEAAkAMAwAACAAIAgAYIAEB","99":"b:CgkABKkAAAEAAkAMAwAACAAIAgAYIAEB","113":"b:CgkABKkAAAEAAkAMAwAACAAIAgAYIAEB","117":"b:CgkABKkAAAEAAkAMAwAACAAIAgAYIAEB"},"packages/core/src/credentials.ts":{"11":"b:ekEEGAKAzjABAgAACAACAAAAAAAgIAAA","19":"b:ekEEGAKAzjABAgAACAACAAAAAAAgIAAA","27":"b:ekEEGAKAzjABAgAACAACAAAAAAAgIAAA","37":"b:ekEEGAKAzjABAgAACAACAAAAAAAgIAAA","48":"b:ekEEGAKAzjABAgAACAACAAAAAAAgIAAA","72":"b:ekEEGAKAzjABAgAACAACAAAAAAAgIAAA"},"packages/workflow/src/errors/index.ts":{"5":"b:SokABKoAAAEQAkQMBQAACAAIAiAcICEB","14":"b:SokABKoAAAEQAkQMBQAACAAIAiAcICEB","17":"b:SokABKoAAAEQAkQMBQAACAAIAiAcICEB","23":"b:SokABKoAAAEQAkQMBQAACAAIAiAcICEB","27":"b:SokABKoAAAEQAkQMBQAACAAIAiAcICEB","29":"b:SokABKoAAAEQAkQMBQAACAAIAiAcICEB","30":"b:SokABKoAAAEQAkQMBQAACAAIAiAcICEB","33":"b:SokABKoAAAEQAkQMBQAACAAIAiAcICEB","35":"b:SokABKoAAAEQAkQMBQAACAAIAiAcICEB"},"packages/cli/src/services/naming.service.ts":{"6":"b:UuVX1AJomBQKJ4hMQAEgYARAMggAAAAA","11":"b:UuVX1AJomBQKJ4hMQAEgYARAMggAAAAA","15":"b:UuVX1AJomBQKJ4hMQAEgYARAMggAAAAA","19":"b:UuVX1AJomBQKJ4hMQAEgYARAMggAAAAA","28":"b:UuVX1AJomBQKJ4hMQAEgYARAMggAAAAA"},"packages/nodes-base/nodes/EditImage/EditImage.node.ts":{"30":[43],"810":[43],"983":[43],"987":[43],"999":[43],"1014":[43],"1076":[43],"1113":[43],"1114":[43],"1244":[43],"1245":[43],"1267":[43],"1330":[43],"1331":[43],"1332":[43]},"localhost-34707/assets/_plugin-vue_export-helper-Da88TEg1.js":{"2":[43,101,66,67]},"localhost-34707/assets/chunk-CC9Q-vWm.js":{"8":[43,101,66,67],"9":[43,101,66,67],"10":[43,101,66,67],"19":[43,101,66,67],"23":[43,101,66,67],"29":[43,101,66,67],"33":[43,101,66,67],"34":[43,101,66,67]},"localhost-34707/assets/verified-Ge9vhh2v.js":{"8":[43,101,67]},"packages/@n8n/utils/src/search/re-rank-search-results.ts":{"1":"b:guVBlAJ4iQSaAQBC4AUO8Mz2fhIAAAAA","6":"b:guVBlAJ4iQSaAQBC4AUO8Mz2fhIAAAAA","9":"b:guVBlAJ4iQSaAQBC4AUO8Mz2fhIAAAAA","23":"b:guEBlAB4iQCaAABCYAUC8MzyehIAAAAA"},"packages/@n8n/utils/src/search/sublime-search.ts":{"25":"b:guVBlEJ4iQSaAQBC4AUO8Mz2fhIAAAAA","41":"b:guVBlEJ4iQSaAQBC4AUO8Mz2fhIAAAAA","182":"b:guVBlEJ4iQSaAQBC4AUO8Mz2fhIAAAAA","205":"b:guVBlEJ4iQSaAQBC4AUO8Mz2fhIAAAAA","222":"b:guVBlEJ4iQSaAQBC4AUO8Mz2fhIAAAAA","231":"b:guVBlEJ4iQSaAQBC4AUO8Mz2fhIAAAAA","285":"b:guEBlEB4iQCaAABCYAUC8MzyehIAAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nCallout/Callout.vue":{"10":"b:mmUBEIJYhBRTIiAJGoCCoIDyfxoCAAAA","36":"b:mmUBEIJYhBRTIiAJGoCCoIDyfxoCAAAA","45":"b:mmUAAIAQhBRSICAJEICCgIDyTRoCAAAA","49":"b:mmUAAIAQhBRSICAJEICCgIDyTRoCAAAA","58":"b:mmUBEIJYhBRTIiAJGoCCoIDyfxoCAAAA","65":[77],"70":"b:mmUBEIJYhBRTIiAJGoCCoIDyfxoCAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nIcon/custom/node-validation-error.svg":{"11":"b:wvVHHEJYiABSAohCwKEOsIgiWjIFAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nIcon/nodes/edit-image.svg":{"11":[43,101,162]},"packages/frontend/@n8n/design-system/src/components/N8nIcon/nodes/form-trigger.svg":{"11":"b:guVBlAJ4iQSaAQBC4AUO8MzyelICAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nIcon/nodes/ftp.svg":{"11":[43]},"packages/frontend/@n8n/design-system/src/components/N8nIcon/nodes/schedule-trigger.svg":{"11":"b:wuVFlAJ4iQSaAQBC4IUO8Nz6eloDAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nIcon/nodes/sub-workflow-trigger.svg":{"11":"b:guVBlAJ4iQSaAQBC4AUO8MzyelICAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nIcon/nodes/webhook.svg":{"11":"b:guVBtAJ4jQWaAQBK4AUO8Mz6elIGAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nLoading/Loading.vue":{"38":"b:guVBnw54+RS7G2BC6h3O8Mz3floGAAAA","47":"b:guVBnw54+RS7G2BC6h3O8Mz3floGAAAA","49":[97,34,35,73],"60":"b:guVBnw54+RS7G2BC6h3O8Mz3floGAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nNodeCreatorNode/NodeCreatorNode.vue":{"32":"b:guVBlAJ4iQSaAQBC4AUO8Mz2flICAAAA","33":"b:guVBlAJ4iQSSAQBC4AUO8Mz2flICAAAA","58":"b:AkEAEAAggQAAAAAAAAAAMIQAAhAAAAAA","61":[1,10,13]},"packages/frontend/editor-ui/src/app/composables/useCalloutHelpers.ts":{"33":"b:guVBlAJ4iQSaAQBC4AUO8Mz2flICAAAA","111":[1,7,8,10,113,65,68,13,14]},"packages/frontend/editor-ui/src/features/credentials/credentials.utils.ts":{"17":"b:wuUHVEJYiADAAYBARKM7sMgAXhABAAAA","30":"b:wmUFFEJYiABAAABARKECsIgAWhABAAAA","33":[7,8,125]},"packages/frontend/editor-ui/src/features/ndv/panel/components/TriggerPanel.vue":{"70":"b:AAFABAAICAAQAABAgAAAAAAIWgACAAAA","74":"b:AAFABAAICAAQAABAgAAAAAAIWgACAAAA","82":[22,26,94,103,51,68,139,145,147,161],"90":[22,26,94,103,51,68,139,145,147,161],"109":"b:AAFABAAICAAQAABAgAAAAAAIWgACAAAA","115":[26,94,51,139,145,147,161],"123":[22,103],"131":[22,103,51,68],"135":"b:AAFABAAICAAQAABAgAAAAAAIWgACAAAA","153":"b:AAFABAAICAAQAABAgAAAAAAIWgACAAAA","161":[22,26,94,103,51,68,139,145,147,161],"165":[22,103,51,139],"169":[22,26,94,103,51,68,139,145,147,161],"187":[22,26,94,103,51,139,145,147,161],"189":[22,26,94,103,51,139,145,147,161],"195":[22,26,94,103,51,139,145,147,161],"197":[22,103],"203":[22,103],"216":[22,26,94,103,51,139,145,147,161],"234":[22,26,94,103,51,139,145,147,161],"244":[22,26,94,103,51,139,145,147,161],"284":[22,26,94,103,51,139,145,147,161],"374":[68],"431":[22,103],"435":[22,103],"461":[22,26,94,103,51,139,145,147,161],"478":[22,26,94,103,51,139,145,147,161],"484":[22,26,94,103,51,139,145,147,161]},"packages/frontend/editor-ui/src/features/ndv/parameters/components/FixedCollection/FixedCollectionParameterLegacy.vue":{"66":"b:guFDWAAYgADAAABCgIQAsET4TxAAAAAA","71":"b:AsBDSAAICADAAABCgIQAgET4ThAAAAAA","73":"b:AsBDSAAICADAAABCgIQAgET4ThAAAAAA","76":"b:AsBDSAAICADAAABCgIQAgET4ThAAAAAA","80":"b:guFDWAAYiADAAABCgIQAsET4TxAAAAAA","81":"b:gGFAEAAQgABAAAACgAAAMAAoBxAAAAAA","82":"b:AsBDSAAIAADAAABCgIQAgET4ThAAAAAA","83":"b:AsBDSAAICADAAABCgIQAgET4ThAAAAAA","85":"b:guFDWAAYgADAAABCgIQAsET4TxAAAAAA","92":"b:guFDWAAYiADAAABCgIQAsET4TxAAAAAA","94":"b:guFDWAAYiADAAABCgIQAsET4TxAAAAAA","105":"b:guFDWAAYiADAAABCgIQAsET4TxAAAAAA","108":[44,103,51],"111":"b:guFDWAAYiADAAABCgIQAsET4TxAAAAAA","115":[22,103],"122":[22,103],"126":[22,103],"140":"b:guFDWAAYiADAAABCgIQAsET4TxAAAAAA","155":[22,103],"160":[22,103],"191":[22,103],"196":"b:AsBDSAAICADAAABCgIQAgET4ThAAAAAA","207":[22,103],"232":"b:AsBDSAAICADAAABCgIQAgET4ThAAAAAA","242":[22,103],"263":[103],"276":[103],"299":"b:guFDWAAYiADAAABCgIQAsET4TxAAAAAA","300":"b:AGFCUAAAiAAAAAACgAAAAAQwAAAAAAAA","307":"b:guFDWAAYiADAAABCgIQAsET4TxAAAAAA","339":[89,22,103,130,141,14],"362":[89,22,103,130,141,14],"368":[89,22,103,130,141,14],"382":[89,22,103,130,141,14],"389":[89,22,103,51,130,140,141],"421":"b:AsBDSAAIAADAAABCgIQAgET4ThAAAAAA","423":"b:guFDWAAYiADAAABCgIQAsET4TxAAAAAA","439":"b:AsBDSAAICADAAABCgIQAgET4ThAAAAAA","461":"b:AsBDSAAIAADAAABCgIQAgET4ThAAAAAA","531":[103],"532":[22,103],"636":[89,22,103,130,141,14],"644":[139,141,145,146,70]},"packages/frontend/editor-ui/src/features/ndv/parameters/components/ParameterInputHint.vue":{"13":"b:wuABAAAIDBDQAAAAAIQigMT4OhAAAAAA","21":"b:wuABAAAIDBDQAAAAAIQigMT4OhAAAAAA","23":[111],"27":"b:QMAAAAAABBBQAAAAAIAgAADwOgAAAAAA","39":"b:wuABAAAIDBDQAAAAAIQigMT4OhAAAAAA","43":"b:wuABAAAIDBDQAAAAAIQigMT4OhAAAAAA"},"packages/frontend/editor-ui/src/features/ndv/parameters/components/ResourceLocator/ResourceLocator.vue":{"81":"b:wmEBEEAYgAAAAAAAAAACsAAAEhAAAAAA","173":[14,148],"182":"b:wmEBEEAYgAAAAAAAAAACsAAAEhAAAAAA","195":"b:wmEBEEAYgAAAAAAAAAACsAAAEhAAAAAA","197":[28,8,55,124,125,145,13,14,148,156],"204":[145,148],"207":[145,148],"216":[1,7,145,148],"229":"b:wmEBEEAYgAAAAAAAAAACsAAAEhAAAAAA","242":"b:wmEBEEAYgAAAAAAAAAACsAAAEhAAAAAA","246":"b:wmEBEEAYgAAAAAAAAAACsAAAEhAAAAAA","250":"b:wmEBEEAYgAAAAAAAAAACsAAAEhAAAAAA","251":"b:wmEBEEAYgAAAAAAAAAACsAAAEhAAAAAA","263":"b:wmEBEEAYgAAAAAAAAAACsAAAEhAAAAAA","317":"b:wmEBEEAYgAAAAAAAAAACsAAAEhAAAAAA","327":"b:wmEBEEAYgAAAAAAAAAACsAAAEhAAAAAA","329":"b:wmEBEEAYgAAAAAAAAAACsAAAEhAAAAAA","342":"b:wmEBEEAYgAAAAAAAAAACsAAAEhAAAAAA","344":"b:wmEBEEAYgAAAAAAAAAACsAAAEhAAAAAA","347":[148],"355":"b:wmEBEEAYgAAAAAAAAAACsAAAEhAAAAAA","357":"b:wmEBEEAYgAAAAAAAAAACsAAAEhAAAAAA","364":"b:wmEBEEAYgAAAAAAAAAACsAAAEhAAAAAA","368":"b:wmEBEEAYgAAAAAAAAAACsAAAEhAAAAAA","369":"b:wmEBEEAYgAAAAAAAAAACsAAAEhAAAAAA","370":"b:wmEBEEAYgAAAAAAAAAACsAAAEhAAAAAA","373":[1,7,145,148],"377":"b:wmEBEEAYgAAAAAAAAAACsAAAEhAAAAAA","391":"b:wmEBEEAYgAAAAAAAAAACsAAAEhAAAAAA","397":"b:wmEBEEAYgAAAAAAAAAACsAAAEhAAAAAA","403":"b:wmEBEEAYgAAAAAAAAAACsAAAEhAAAAAA","407":"b:wmEBEEAYgAAAAAAAAAACsAAAEhAAAAAA","494":"b:wmEBEEAYgAAAAAAAAAACsAAAEhAAAAAA","500":[145,148],"508":"b:wmEBEEAYgAAAAAAAAAACsAAAEhAAAAAA","519":[14,148],"529":[145,148],"549":"b:wmEBEEAYgAAAAAAAAAACsAAAEhAAAAAA","550":[145,14,148],"570":"b:wmEBEEAYgAAAAAAAAAACsAAAEhAAAAAA","572":[1,7,28,8,55,113,14,148],"617":"b:wmEBEEAYgAAAAAAAAAACsAAAEhAAAAAA","621":"b:wmEBEAAYgAAAAAAAAAACsAAAEhAAAAAA","626":"b:wmEBEEAYgAAAAAAAAAACsAAAEhAAAAAA","631":"b:wmAAAAAYgAAAAAAAAAACgAAAAhAAAAAA","639":"b:wmEBEAAQgAAAAAAAAAACsAAAEhAAAAAA","646":"b:wmEBEEAYgAAAAAAAAAACsAAAEhAAAAAA","652":[14,148],"679":"b:wmEBEEAYgAAAAAAAAAACsAAAEhAAAAAA","702":[148],"722":"b:wmEBEEAYgAAAAAAAAAACsAAAEhAAAAAA","724":"b:wmEBEEAYgAAAAAAAAAACsAAAEhAAAAAA","729":"b:wmEBEEAYgAAAAAAAAAACsAAAEhAAAAAA","737":[14,148],"754":[148],"760":[148],"766":[14,148],"785":[14,148],"807":[145,148],"814":[148],"826":[1,7,145,148],"837":[1,7,145,148],"954":[1,7,148],"967":[1,7,148],"975":[145,14,148],"1000":"b:wmEBEAAQgAAAAAAAAAACsAAAEhAAAAAA","1008":[145,148],"1014":[145,148],"1027":[145,14,148],"1070":"b:wmEBEEAYgAAAAAAAAAACsAAAEhAAAAAA","1100":[145,148],"1107":[145,148],"1112":[148],"1140":"b:wmEBEEAYgAAAAAAAAAACsAAAEhAAAAAA","1160":"b:wmEBEEAYgAAAAAAAAAACsAAAEhAAAAAA","1169":"b:wmEBEEAYgAAAAAAAAAACsAAAEhAAAAAA","1191":"b:wmEBEEAYgAAAAAAAAAACsAAAEhAAAAAA","1243":"b:wmEBAEAYAAAAAAAAAAACgAAAEgAAAAAA","1270":[14,148]},"packages/frontend/editor-ui/src/features/ndv/parameters/components/ResourceLocator/ResourceLocatorDropdown.vue":{"14":"b:wmEBEEAYgAAAAAAAAAACsAAAEhMAAAAA","59":[148],"77":[55,148,153],"79":[55,148,153],"119":"b:wmEBEEAYgAAAAAAAAAACsAAAEhMAAAAA","120":[55,145,148,153],"126":[55,145,148,153],"135":"b:wmEBEEAYgAAAAAAAAAACsAAAEhMAAAAA","139":"b:wmAAAAAYgAAAAAAAAAACgAAAAhMAAAAA","199":[153],"203":[153],"216":[55,153],"219":[55,153],"230":[153],"245":"b:gmEBEAAQgAAAAAAAAAACMAAAEAIAAAAA","251":"b:wmEBEEAYgAAAAAAAAAACsAAAEhMAAAAA","253":"b:wmEBEEAYgAAAAAAAAAACsAAAEhMAAAAA","267":"b:wmEBEEAYgAAAAAAAAAACsAAAEhMAAAAA","279":"b:wmEBEEAYgAAAAAAAAAACsAAAEhMAAAAA","282":[55,145,148,153],"304":[55,148,153],"341":[55,153],"343":[55,153],"351":[55,148,153],"362":[153],"363":[153],"390":[148]},"packages/frontend/editor-ui/src/features/shared/nodeCreator/components/ItemTypes/ActionItem.vue":{"19":"b:gEFBlABomAAKAAAAgAAAsMxQfhIAAAAA","35":"b:gEFBlABomAAKAAAAgAAAsMxQfhIAAAAA","40":[52],"42":"b:gEFBlABomAAKAAAAgAAAsMxQfhIAAAAA","45":[52],"75":[52],"88":[52],"94":[52],"99":"b:gEFBlABomAAKAAAAgAAAsMxQfhIAAAAA","111":"b:gEFBlABomAAKAAAAgAAAsMxQfhIAAAAA","117":"b:gEFBlABomAAKAAAAgAAAsMxQfhIAAAAA"},"packages/frontend/editor-ui/src/features/shared/nodeCreator/components/ItemTypes/CategoryItem.vue":{"12":"b:guVBlAB4mQCaAABC4AUC8MzyfhIAAAAA","16":"b:guVBlAB4mQCaAABC4AUC8MzyfhIAAAAA","20":"b:guVBlAB4mQCaAABC4AUC8MzyfhIAAAAA"},"packages/frontend/editor-ui/src/features/shared/nodeCreator/components/ItemTypes/NodeItem.vue":{"39":"b:guVBlAJ4mQSaAQBC4AUO8Mz2flICAAAA","59":"b:guVBlAJ4mQSaAQBC4AUO8Mz2flICAAAA","79":"b:guVBlAJ4mQSaAQBC4AUO8Mz2flICAAAA","93":"b:guVBlAJ4mQSaAQBC4AUO8Mz2flICAAAA","94":"b:guVBlAJ4mQSaAQBC4AUO8Mz2flICAAAA","99":"b:guVBlAJ4mQSaAQBC4AUO8Mz2flICAAAA","103":"b:guVBlAJ4mQSaAQBC4AUO8Mz2flICAAAA","107":"b:guVBlAJ4mQSaAQBC4AUO8Mz2flICAAAA","111":"b:guVBlAJ4mQSaAQBC4AUO8Mz2flICAAAA","117":"b:guVBlAJ4mQSaAQBC4AUO8Mz2flICAAAA","119":"b:guVBlAJ4mQSaAQBC4AUO8Mz2flICAAAA","124":"b:guVBlAJ4mQSaAQBC4AUO8Mz2flICAAAA","125":"b:guVBlAJ4mQSaAQBC4AUO8Mz2flICAAAA","127":"b:guVBlAJ4mQSaAQBC4AUO8Mz2flICAAAA","136":"b:guVBlAJ4mQSaAQBC4AUO8Mz2flICAAAA","140":"b:guVBlAJ4mQSaAQBC4AUO8Mz2flICAAAA","144":"b:guVBlAJ4mQSaAQBC4AUO8Mz2flICAAAA","148":[52],"152":"b:guVBlAJ4mQSaAQBC4AUO8Mz2flICAAAA","164":"b:guVBlAJ4mQSaAQBC4AUO8Mz2flICAAAA","166":[52],"180":[52],"182":[52],"187":[52],"190":"b:guVBlAJ4mQSaAQBC4AUO8Mz2flICAAAA","210":"b:guVBlAJ4mQSaAQBC4AUO8Mz2flICAAAA","223":"b:gmEBlABImQAKAABCIAACsMwgShAAAAAA","224":[52],"226":"b:gmEBlABImQAKAABCIAACsMwgShAAAAAA","236":[102,52],"238":[52],"241":[102,52],"255":"b:guVBlAJ4mQSaAQBC4AUO8Mz2flICAAAA","267":[52]},"packages/frontend/editor-ui/src/features/shared/nodeCreator/components/ItemTypes/SubcategoryItem.vue":{"13":"b:guVBlAJ4mQSaAQBC4AUO8Mz2flICAAAA","15":"b:guVBlAJ4mQSaAQBC4AUO8Mz2flICAAAA","16":"b:guVBlAJ4mQSaAQBC4AUO8Mz2flICAAAA"},"packages/frontend/editor-ui/src/features/shared/nodeCreator/components/Modes/ActionsMode.vue":{"41":"b:gEFBlABomAAKAAAAgAAAsMxQfhIAAAAA","63":"b:gEFBFABIkAAAAAAAgAAAMIAAehIAAAAA","66":"b:gEFBlABomAAKAAAAgAAAsMxQfhIAAAAA","69":"b:AEFBlAAgmAAKAAAAgAAAsMxQfhIAAAAA","76":[7,26,8,43,46,52,145,14,148,149,150],"86":"b:gEFBFABIkAAAAAAAgAAAMIAAehIAAAAA","92":"b:gEFBlABomAAKAAAAgAAAsMxQfhIAAAAA","93":"b:gEFBlABomAAKAAAAgAAAsMxQfhIAAAAA","98":"b:gEFBlABomAAKAAAAgAAAsMxQfhIAAAAA","100":"b:gEFBlABomAAKAAAAgAAAsMxQfhIAAAAA","102":"b:gEFBlABomAAKAAAAgAAAsMxQfhIAAAAA","104":"b:gEFBlABomAAKAAAAgAAAsMxQfhIAAAAA","109":"b:gEFBlABomAAKAAAAgAAAsMxQfhIAAAAA","118":"b:gEFBFABIkAAAAAAAgAAAMIAAehIAAAAA","127":"b:gEFBlABomAAKAAAAgAAAsMxQfhIAAAAA","130":"b:gEFBlABomAAKAAAAgAAAsMxQfhIAAAAA","137":"b:gEFBlABomAAKAAAAgAAAsMxQfhIAAAAA","139":"b:gEFBlABomAAKAAAAgAAAsMxQfhIAAAAA","151":[52],"157":[52],"161":"b:gEFBlABomAAKAAAAgAAAsMxQfhIAAAAA","165":[52],"169":[52],"171":[52],"176":"b:gEFBlABImAAKAAAAgAAAsMxQehIAAAAA","189":"b:gEFBlABImAAKAAAAgAAAsMxQehIAAAAA","212":"b:gEFBlABomAAKAAAAgAAAsMxQfhIAAAAA","215":"b:gEFBlABomAAKAAAAgAAAsMxQfhIAAAAA","223":"b:gEFBlABomAAKAAAAgAAAsMxQfhIAAAAA","234":[52],"238":[52],"258":"b:gEFBlABomAAKAAAAgAAAsMxQfhIAAAAA","262":"b:gEFBlABomAAKAAAAgAAAsMxQfhIAAAAA","263":"b:gEFBlABomAAKAAAAgAAAsMxQfhIAAAAA","280":"b:gEFBFABIkAAAAAAAgAAAMIAAehIAAAAA","293":[43,52,145,14,148,149],"299":[43,52,145,14,148,149],"311":[52],"320":"b:gEFBlABomAAKAAAAgAAAsMxQfhIAAAAA","335":[43,46,52,145,14],"339":[52],"341":[52]},"packages/frontend/editor-ui/src/features/shared/nodeCreator/components/Modes/NodesMode.vue":{"55":"b:guVBlAJ4mQSaAQBC4AUO8Mz2flICAAAA","69":"b:guVBlAJ4mQSaAQBC4AUO8Mz2flICAAAA","71":"b:guVBlAJ4mQSaAQBC4AUO8Mz2flICAAAA","74":"b:guVBlAJ4mQSaAQBC4AUO8Mz2flICAAAA","76":"b:guVBlAJ4mQSaAQBC4AUO8Mz2flICAAAA","88":"b:guVBlAJ4mQSaAQBC4AUO8Mz2flICAAAA","98":"b:guVBlAJ4mQSaAQBC4AUO8Mz2flICAAAA","112":"b:guVBlAJ4mQSaAQBC4AUO8Mz2flICAAAA","211":"b:gEFBlABomAAKAAAAgAAAsMxQfhIAAAAA","270":[1,10,101,44,52],"290":[1,10,101,44,52],"306":"b:guVBlAJ4mQSaAQBC4AUO8Mz2flICAAAA","314":[52],"318":[26,52,65],"326":[26,52,65],"334":[26,52,65],"340":[52],"343":"b:guVBlAJ4mQSaAQBC4AUO8Mz2flICAAAA","352":"b:guVBlAJ4mQSaAQBC4AUO8Mz2flICAAAA","362":[52],"363":[52]},"packages/frontend/editor-ui/src/features/shared/nodeCreator/components/OrderSwitcher.vue":{"11":"b:gEFBlABomAAKAAAAgAAAsMxQfhIAAAAA"},"packages/frontend/editor-ui/src/features/shared/nodeCreator/components/Panel/NodesListPanel.vue":{"43":"b:guVBlAJ4mQSaAQBC4AUO8Mz2flICAAAA","53":"b:guVBlAJ4mQSaAQBC4AUO8Mz2flICAAAA","55":"b:guVBlAJ4mQSaAQBC4AUO8Mz2flICAAAA","57":"b:guVBlAJ4mQSaAQBC4AUO8Mz2flICAAAA","59":"b:guVBlAJ4mQSaAQBC4AUO8Mz2flICAAAA","61":"b:guVBlAJ4mQSaAQBC4AUO8Mz2flICAAAA","63":"b:guVBlAJ4mQSaAQBC4AUO8Mz2flICAAAA","83":"b:guVBlAJ4mQSaAQBC4AUO8Mz2flICAAAA","88":"b:guVBlAJ4mQSaAQBC4AUO8Mz2flICAAAA","90":[102,52],"94":"b:guVBlAJ4mQSaAQBC4AUO8Mz2flICAAAA","101":"b:guVBlAJ4mQSaAQBC4AUO8Mz2flICAAAA","113":"b:guVBlAJ4mQSaAQBC4AUO8Mz2fhIAAAAA","139":"b:guVBlAJ4mQSaAQBC4AUO8Mz2fhICAAAA","149":"b:guVBlAJ4mQSaAQBC4AUO8Mz2fhICAAAA","160":[15,26,43,101,52,65,131,134,14],"168":"b:gmVBlAB4mAAKAAAA4AAAsMxQfhIAAAAA","173":"b:guVBlAJ4mQSaAQBC4AUO8Mz2flICAAAA","177":"b:guVBlAJ4mQSaAQBC4AUO8Mz2flICAAAA","185":"b:guVBlAJ4mQSaAQBC4AUO8Mz2flICAAAA","191":"b:guVBlAJ4mQSaAQBC4AUO8Mz2flICAAAA","192":"b:guVBlAJ4mQSaAQBC4AUO8Mz2flICAAAA","241":[101,45,52],"243":"b:guVBlAJ4mQSaAQBC4AUO8Mz2flICAAAA","255":[26,43,101,52,131,134]},"packages/frontend/editor-ui/src/features/shared/nodeCreator/components/Panel/SearchBar.vue":{"16":"b:guVBlAJ4mQSaAQBC4AUO8Mz2flICAAAA","26":"b:guVBlAJ4mQSaAQBC4AUO8Mz2flICAAAA","30":"b:guVBlAJ4mQSaAQBC4AUO8Mz2fhICAAAA","35":[52],"39":"b:guVBlAJ4mQSaAQBC4AUO8Mz2flICAAAA","44":"b:guVBlAJ4mQSaAQBC4AUO8Mz2flICAAAA","52":"b:guVBlAJ4mQSaAQBC4AUO8Mz2flICAAAA"},"packages/frontend/editor-ui/src/features/shared/nodeCreator/components/Renderers/CategorizedItemsRenderer.vue":{"28":"b:guVBlAB4mQCaAABC4AUC8MzyfhIAAAAA","38":"b:guVBlAB4mQCaAABC4AUC8MzyfhIAAAAA","39":"b:guVBlAB4mQCaAABC4AUC8MzyfhIAAAAA","41":"b:guVBlAB4mQCaAABC4AUC8MzyfhIAAAAA","45":[46,52],"49":[46,52],"62":"b:guVBlAB4mQCaAABC4AUC8MzyfhIAAAAA","68":[52],"74":[52],"83":"b:guVBlAB4mQCaAABC4AUC8MzyfhIAAAAA","84":[52],"92":[52],"97":[26,52,65],"103":[52],"106":"b:guVBlAB4mQCaAABC4AUC8MzyfhIAAAAA","122":"b:guVBlAB4mQCaAABC4AUC8MzyfhIAAAAA","126":[52],"132":[52],"153":"b:guVBlAB4mQCaAABC4AUC8MzyfhIAAAAA","154":[43,52,145,14,148,149]},"packages/frontend/editor-ui/src/features/shared/nodeCreator/components/Renderers/ItemsRenderer.vue":{"30":"b:guVBlAJ4mQSaAQBC4AUO8Mz2flICAAAA","45":"b:guVBlAJ4mQSaAQBC4AUO8Mz2flICAAAA","47":"b:guVBlAJ4mQSaAQBC4AUO8Mz2flICAAAA","49":"b:guVBlAJ4mQSaAQBC4AUO8Mz2flICAAAA","53":"b:guVBlAJ4mQSaAQBC4AUO8Mz2flICAAAA","63":"b:guVBlAJ4mQSaAQBC4AUO8Mz2flICAAAA","80":"b:guVBlAJ4mQSaAQBC4AUO8Mz2flICAAAA","106":[52],"110":[52],"114":[52],"118":[52],"122":"b:guVBlAJ4mQSaAQBC4AUO8Mz2flICAAAA","126":"b:guVBlAJ4mQSaAQBC4AUO8Mz2flICAAAA","133":"b:guVBlAJ4mQSaAQBC4AUO8Mz2flICAAAA","134":"b:guVBlAJ4mQSaAQBC4AUO8Mz2fhIAAAAA","141":"b:guVBlAJ4mQSaAQBC4AUO8Mz2flICAAAA","154":"b:guVBlAJ4mQSaAQBC4AUO8Mz2flICAAAA","163":[1,44,52,13],"166":[52],"185":"b:guVBlAJ4mQSaAQBC4AUO8Mz2flICAAAA"},"packages/frontend/editor-ui/src/features/shared/nodeCreator/composables/useActions.ts":{"69":"b:gEFBlABoiAAKAAAAgAAAsMxQfhIAAAAA","76":"b:gEFBlABoiAAKAAAAgAAAsMxQfhIAAAAA","80":"b:gEFBlABoiAAKAAAAgAAAsMxQfhIAAAAA","81":"b:gEFBlABoiAAKAAAAgAAAsMxQfhIAAAAA","101":"b:gEFBlABoiAAKAAAAgAAAsMxQfhIAAAAA","102":"b:gEFBlABoiAAKAAAAgAAAsMxQfhIAAAAA","107":"b:gEFBlABoiAAKAAAAgAAAsMxQfhIAAAAA","158":"b:gEFBlABoiAAKAAAAgAAAsMxQfhIAAAAA","174":"b:gEFBlABIiAAKAAAAgAAAsMxQehIAAAAA","177":"b:AEEBlABAgAAKAAAAAAAAsMxQWhAAAAAA","192":"b:gEFBlABIiAAKAAAAgAAAsMxQehIAAAAA","221":"b:guVBlAJ4iQSaAQBC4AUO8Mz2flICAAAA","247":"b:guVBlAI4iASaAABC4AUO8Oz2elICAAAA","255":"b:guVBlAJ4iQSaAQBC4AUO8Oz2elICAAAA","257":"b:guVBlAJ4iQSaAQBC4AUO8Oz2elICAAAA","260":"b:guVBlAJ4iQSaAQBC4AUO8Oz2elICAAAA","273":"b:guVBlAJ4iQSaAQBC4AUO8Oz2elICAAAA","283":"b:guVBlAJ4iQSaAQBC4AUO8Oz2elICAAAA","287":[1],"291":"b:guVBlAJ4iQSaAQBC4AUO8Oz2elICAAAA","295":"b:guVBlAJ4iQSaAQBC4AUO8Oz2elICAAAA","310":"b:guVBlAJ4iQSaAQBC4AUO8Oz2elICAAAA","319":"b:guVBlAJ4iQSaAQBC4AUO8Oz2elICAAAA","327":"b:guVBlAJ4iQSaAQBC4AUO8Oz2elICAAAA","369":"b:guVBlAJ4iQSaAQBC4AUO8Oz2elICAAAA","405":"b:gEFBlABYiAAKAAAAgAAAsMxQehIAAAAA","410":"b:gEFBlABYiAAKAAAAgAAAsMxQehIAAAAA","420":"b:gEFBlABIiAAKAAAAgAAAsMxQehIAAAAA"},"packages/frontend/editor-ui/src/features/shared/nodeCreator/views/viewsData.ts":{"122":[14],"139":[14],"141":[14],"145":[14],"148":[14],"174":[14],"187":[14],"237":[1,7,10,13,14],"240":[1,7,10,13,14],"250":[1,7,10,13,14],"376":"b:guVBlAJ4iQSaAQBC4AUO8MzyelICAAAA","502":"b:gsFBlAIwiASKAAAC4AEOsMz2fhIAAAAA","632":"b:gsFBlAIwiASKAAAC4AEOsMz2fhIAAAAA"},"packages/frontend/editor-ui/src/features/workflows/canvas/components/elements/nodes/render-types/CanvasNodeAddNodes.vue":{"18":"b:wuVD9AJ4mQSaIYBC6C8/8Ozyfl4DAAAA","26":"b:wuVD9AJ4mQSaIYBC6C8/8Ozyfl4DAAAA","30":"b:wuVD9AJ4mQSaIYBC6C8/8Ozyfl4DAAAA","35":"b:wuVD1AJ4iQSaAYBC4Cc/8OzyflIDAAAA","48":"b:AIABBABAgAAQAAAAYAAAAMhAAAACAAAA","63":"b:wuVD9AJ4mQSaIYBC6C8/8Ozyfl4DAAAA"},"packages/cli/src/modules/data-table/data-table-file-cleanup.service.ts":{"13":"b:QoAEMewAkAEIA2CAJgQICAAIAAAAAAAA","20":"b:QoAEMewAkAEIA2CAJgQICAAIAAAAAAAA","29":"b:QoAEMewAkAEIA2CAJgQICAAIAAAAAAAA","33":"b:QoAEMewAkAEIA2CAJgQICAAIAAAAAAAA","38":"b:QoAEMewAkAEIA2CAJgQICAAIAAAAAAAA","49":"b:QoAEMewAkAEIA2CAJgQICAAIAAAAAAAA","82":"b:QoAEMewAkAEIA2CAJgQICAAIAAAAAAAA"},"packages/cli/src/wait-tracker.ts":{"35":"b:QoAENewBkAEIA2CAJgSICAAIAAAAACAI","50":"b:QoAENewBkAEIA2CAJgSICAAIAAAAACAI","62":"b:QoAENewBkAEIA2CAJgSICAAIAAAAACAI","66":"b:QoAENewBkAEIA2CAJgSICAAIAAAAACAI","71":"b:QoAENewBkAEIA2CAJgSICAAIAAAAACAI","73":"b:QoAENewBkAEIA2CAJgSICAAIAAAAACAI","82":"b:QoAENewBkAEIA2CAJgSICAAIAAAAACAI","91":"b:QoAENewBkAEIA2CAJgSICAAIAAAAACAI","104":"b:QoAENewBkAEIA2CAJgSICAAIAAAAACAI","112":"b:QoAENewBkAEIA2CAJgSICAAIAAAAACAI","120":"b:QoAENewBkAEIA2CAJgSICAAIAAAAACAI","194":"b:QoAENewBkAEIA2CAJgSICAAIAAAAACAI","205":"b:QoAENewBkAEIA2CAJgSICAAIAAAAACAI","217":"b:QoAENewBkAEIA2CAJgSICAAIAAAAACAI","238":"b:QoAENewBkAEIA2CAJgSICAAIAAAAACAI","241":"b:QoAENewBkAEIA2CAJgSICAAIAAAAACAI","255":"b:QoAENewBkAEIA2CAJgSICAAIAAAAACAI","259":"b:QoAENewBkAEIA2CAJgSICAAIAAAAACAI"},"packages/frontend/@n8n/design-system/src/components/N8nIcon/custom/toolbox.svg":{"10":"b:gsFBlAIwiASKAAAC4AEOsMz2fhIAAAAA"},"packages/frontend/editor-ui/src/features/shared/nodeCreator/components/ItemTypes/ViewItem.vue":{"13":"b:gsFBlAIwmASKAAAC4AEOsMz2fhIAAAAA"},"packages/frontend/editor-ui/src/features/shared/nodeCreator/components/Panel/NoResults.vue":{"20":[101,52],"41":[101,52],"46":[101,52],"56":[101,52]},"packages/frontend/editor-ui/src/features/shared/nodeCreator/components/Panel/NoResultsIcon.vue":{"8":[101,52]},"packages/frontend/editor-ui/src/features/workflows/canvas/components/elements/edges/CanvasConnectionLine.vue":{"11":"b:ggQABAAwAAAGAAAAIAAEAAQEIAEAAAAA","17":"b:ggQABAAwAAAGAAAAIAAEAAQEIAEAAAAA","21":"b:ggQABAAwAAAGAAAAIAAEAAQEIAEAAAAA","28":"b:ggQABAAwAAAGAAAAIAAEAAQEIAEAAAAA","34":"b:ggQABAAwAAAGAAAAIAAEAAQEIAEAAAAA","38":"b:ggQABAAwAAAGAAAAIAAEAAQEIAEAAAAA","46":"b:ggQABAAwAAAGAAAAIAAEAAQEIAEAAAAA","47":"b:ggQABAAwAAACAAAAAAAEAAQEIAEAAAAA","51":"b:ggQABAAwAAAGAAAAIAAEAAQEIAEAAAAA"},"packages/@n8n/api-types/src/dto/api-keys/create-api-key-request.dto.ts":{"5":[186,41,56,61,64,126]},"packages/@n8n/api-types/src/dto/api-keys/update-api-key-request.dto.ts":{"7":[186,41,56,61,64,126]},"packages/@n8n/api-types/src/dto/invitation/invite-users-request.dto.ts":{"12":[186,41,56,122,64,126]},"packages/@n8n/api-types/src/schemas/scopes.schema.ts":{"14":[186,41,56,61,64,126]},"packages/@n8n/db/src/entities/webhook-entity.ts":{"33":"b:AADAIAEEBEEAAAACAAAAAAAAAAAYAHAJ","39":"b:AADAIAEEBEEAAAACAAAAAAAAAAAYAHAJ","43":"b:AADAIAEEBEEAAAACAAAAAAAAAAAYAHAJ","44":"b:AADAIAEEBEEAAAACAAAAAAAAAAAYAHAJ","50":"b:AADAIAEEBEEAAAACAAAAAAAAAAAYAHAJ","51":"b:AADAIAEEBEEAAAACAAAAAAAAAAAYAHAJ","54":"b:AADAIAEEBEEAAAACAAAAAAAAAAAYAHAJ"},"packages/@n8n/db/src/repositories/workflow-publish-history.repository.ts":{"9":"b:AADAIAEEBkkAAAACAAAAgAAAAAPYB+AD","13":"b:AADAIAEEBkkAAAACAAAAgAAAAAPYB+AD","31":"b:AADAIAEEBkkAAAACAAAAgAAAAAPYB+AD","42":"b:AADAIAEEBkkAAAACAAAAgAAAAAPYB+AD"},"packages/@n8n/db/src/subscribers/user-subscriber.ts":{"12":"b:QgzAAAAGBksBAEAAAAAARgAAgADYB5AG","15":"b:QgzAAAAGBksBAEAAAAAARgAAgADYB5AG","19":"b:QgzAAAAGBksBAEAAAAAARgAAgADYB5AG","24":"b:QgzAAAAGBksBAEAAAAAARgAAgADYB5AG"},"packages/@n8n/db/src/utils/is-string-array.ts":{"1":"b:AAAAK9EH4AGAGXAgABAAAAAAAAACCAAA","2":"b:AAAAK9EH4AGAGXAgABAAAAAAAAACCAAA"},"packages/@n8n/errors/src/index.ts":{"2":"b:QoAABIoAAAEQAgQABAAACAAIAiAUAAAA"},"packages/@n8n/permissions/src/public-api-permissions.ee.ts":{"209":[186,176,41,56,61,122,64,126],"216":[186,176,41,56,61,122,64,126],"223":[186,176,41,56,61,122,64,126],"226":[186,176,41,56,61,122,64,126]},"packages/@n8n/permissions/src/types.ee.ts":{"75":[186,176,41,56,61,64,126],"129":[186,176,41,56,61,64,126]},"packages/cli/src/activation-errors.service.ts":{"9":"b:AADAIAEEBkkAAAACAAAAgAAAAAPYB+AD","11":"b:AADAIAEEBkkAAAACAAAAgAAAAAPYB+AD","15":"b:AADAIAEEBkkAAAACAAAAgAAAAAPYB+AD","19":"b:AADAIAEEBkkAAAACAAAAgAAAAAPYB+AD","27":"b:AADAIAEEBkkAAAACAAAAgAAAAAPYB+AD","35":"b:AADAIAEEBkkAAAACAAAAgAAAAAPYB+AD"},"packages/cli/src/active-workflow-manager.ts":{"73":"b:AABAIAEEBkEAAAACAAAAgAAAAFuYAOAL","96":"b:AABAIAEEBkEAAAACAAAAgAAAAFuYAOAL","107":"b:AABAIAEEBkEAAAACAAAAgAAAAFuYAOAL","114":"b:AABAIAEEBkEAAAACAAAAgAAAAFuYAOAL","136":"b:AABAIAEEBkEAAAACAAAAgAAAAFuYAOAL","143":"b:AABAIAEEBkEAAAACAAAAgAAAAFuYAOAL","154":"b:AABAIAEEBkEAAAACAAAAgAAAAFuYAOAL","243":"b:AABAIAEEBkEAAAACAAAAgAAAAFuYAOAL","288":"b:AABAIAEEBkEAAAACAAAAgAAAAFuYAOAL","324":"b:AABAIAEEBkEAAAACAAAAgAAAAFuYAOAL","344":"b:AABAIAEEBkEAAAACAAAAgAAAAFuYAOAL","358":"b:AABAIAEEBkEAAAACAAAAgAAAAFuYAOAL","386":"b:AABAIAEEBkEAAAACAAAAgAAAAFuYAOAL","400":"b:AABAIAEEBkEAAAACAAAAgAAAAFuYAOAL","419":"b:AABAIAEEBkEAAAACAAAAgAAAAFuYAOAL","432":"b:AABAIAEEBkEAAAACAAAAgAAAAFuYAOAL","491":"b:AABAIAEEBkEAAAACAAAAgAAAAFuYAOAL","498":"b:AABAIAEEBkEAAAACAAAAgAAAAFuYAOAL","507":"b:AABAIAEEBkEAAAACAAAAgAAAAFuYAOAL","535":"b:AABAIAEEBkEAAAACAAAAgAAAAFuYAOAL","649":"b:AABAIAEEBkEAAAACAAAAgAAAAFuYAOAL","651":"b:AABAIAEEBkEAAAACAAAAgAAAAFuYAOAL","688":"b:AABAIAEEBkEAAAACAAAAgAAAAFuYAOAL","696":"b:AABAIAEEBkEAAAACAAAAgAAAAFuYAOAL","701":"b:AABAIAEEBkEAAAACAAAAgAAAAFuYAOAL","722":"b:AABAIAEEBkEAAAACAAAAgAAAAFuYAOAL","811":"b:AABAIAEEBkEAAAACAAAAgAAAAFuYAOAL","812":"b:AABAIAEEBkEAAAACAAAAgAAAAFuYAOAL","815":"b:AABAIAEEBkEAAAACAAAAgAAAAFuYAOAL","825":"b:AABAIAEEBkEAAAACAAAAgAAAAFuYAOAL","842":"b:AABAIAEEBkEAAAACAAAAgAAAAFuYAOAL","849":"b:AABAIAEEBkEAAAACAAAAgAAAAFuYAOAL","904":"b:AABAIAEEBkEAAAACAAAAgAAAAFuYAOAL","914":"b:AABAIAEEBkEAAAACAAAAgAAAAFuYAOAL","927":"b:AABAIAEEBkEAAAACAAAAgAAAAFuYAOAL","971":"b:AABAIAEEBkEAAAACAAAAgAAAAFuYAOAL","990":"b:AABAIAEEBkEAAAACAAAAgAAAAFuYAOAL","1010":"b:AABAIAEEBkEAAAACAAAAgAAAAFuYAOAL","1043":"b:AABAIAEEBkEAAAACAAAAgAAAAFuYAOAL","1080":"b:AABAIAEEBkEAAAACAAAAgAAAAFuYAOAL","1083":"b:AABAIAEEBkEAAAACAAAAgAAAAFuYAOAL","1126":"b:AABAIAEEBkEAAAACAAAAgAAAAFuYAOAL","1133":"b:AABAIAEEBkEAAAACAAAAgAAAAFuYAOAL","1149":"b:AABAIAEEBkEAAAACAAAAgAAAAFuYAOAL"},"packages/cli/src/controllers/api-keys.controller.ts":{"26":[186,41,56,61,64,126],"36":[186,41,56,61,64,126],"43":[186,41,56,61,64,126],"67":[186,41,56,61,64,126],"81":[186,41,56,61,64,126],"96":[186,41,56,61,64,126],"114":[186,41,56,61,64,126],"129":[186,41,56,61,64,126]},"packages/cli/src/controllers/invitation.controller.ts":{"25":[186,41,56,122,64,126],"44":[186,41,56,122,64,126],"76":[186,41,56,122,64,126],"95":[186,41,56,122,64,126],"120":[186,41,56,122,64,126],"164":[186,41,56,122,64,126]},"packages/cli/src/controllers/security-settings.controller.ts":{"15":[56],"25":[56],"41":[56],"83":[56],"87":[56],"92":[56]},"packages/cli/src/errors/response-errors/payment-required.error.ts":{"4":[186,176,41,56,64,126]},"packages/cli/src/errors/response-errors/scope-forbidden.error.ts":{"4":[56]},"packages/cli/src/errors/response-errors/unprocessable.error.ts":{"4":[56]},"packages/cli/src/execution-lifecycle/execute-error-workflow.ts":{"22":"b:OADAAAAEBkkAAAACBAEAAAAAAAHYB+AL","96":"b:OADAAAAEBkkAAAACBAEAAAAAAAHYB+AL","103":"b:OADAAAAEBkkAAAACBAEAAAAAAAHYB+AL","119":"b:OADAAAAEBkkAAAACBAEAAAAAAAHYB+AL","124":"b:OADAAAAEBkkAAAACBAEAAAAAAAHYB+AL"},"packages/cli/src/executions/parse-range-query.middleware.ts":{"15":"b:QgDgAIACBkkwAAACAAAAAAAAAADQB+AD","20":"b:QgDgAIACBkkwAAACAAAAAAAAAADQB+AD"},"packages/cli/src/middlewares/list-query/dtos/base.filter.dto.ts":{"7":"b:AAEBOwIA8BGBGXAgAAAAAAAAAAAAAAAA","23":"b:AAEBOwIA8BGBGXAgAAAAAAAAAAAAAAAA"},"packages/cli/src/middlewares/list-query/dtos/workflow.filter.dto.ts":{"74":"b:AAAAKwAC8AGAGXAgABAAAAAAAAAAAAAA"},"packages/cli/src/modules/insights/database/entities/insights-raw.ts":{"13":"b:AAgAAAEAGEEAAAAQAAQAAAQgAAgBAAAA","27":"b:AAgAAAEAGEEAAAAQAAQAAAQgAAgBAAAA","38":"b:AAgAAAEAGEEAAAAQAAQAAAQgAAgBAAAA"},"packages/cli/src/modules/provisioning.ee/provisioning.service.ee.ts":{"43":"b:AAEFMw8A8BGBAQAAAAAABAAAAAAAAAAA","60":"b:AAEFMw8A8BGBAQAAAAAABAAAAAAAAAAA","64":"b:AAEFMw8A8BGBAQAAAAAABAAAAAAAAAAA","72":"b:AAEFMw8A8BGBAQAAAAAABAAAAAAAAAAA","126":"b:AAEFMw8A8BGBAQAAAAAABAAAAAAAAAAA","177":"b:AAEFMw8A8BGBAQAAAAAABAAAAAAAAAAA","243":"b:AAEFMw8A8BGBAQAAAAAABAAAAAAAAAAA","256":"b:AAEFMw8A8BGBAQAAAAAABAAAAAAAAAAA","286":"b:AAEFMw8A8BGBAQAAAAAABAAAAAAAAAAA","288":"b:AAEFMw8A8BGBAQAAAAAABAAAAAAAAAAA","291":"b:AAEFMw8A8BGBAQAAAAAABAAAAAAAAAAA","308":"b:AAEFMw8A8BGBAQAAAAAABAAAAAAAAAAA","369":"b:AAEFMw8A8BGBAQAAAAAABAAAAAAAAAAA","402":"b:AAEFMw8A8BGBAQAAAAAABAAAAAAAAAAA","418":"b:AAEFMw8A8BGBAQAAAAAABAAAAAAAAAAA","422":"b:AAEFMw8A8BGBAQAAAAAABAAAAAAAAAAA","441":"b:AAEFMw8A8BGBAQAAAAAABAAAAAAAAAAA","456":"b:AAEFMw8A8BGBAQAAAAAABAAAAAAAAAAA","464":"b:AAEFMw8A8BGBAQAAAAAABAAAAAAAAAAA","472":"b:AAEFMw8A8BGBAQAAAAAABAAAAAAAAAAA","480":"b:AAEFMw8A8BGBAQAAAAAABAAAAAAAAAAA","485":"b:AAEFMw8A8BGBAQAAAAAABAAAAAAAAAAA","490":"b:AAEFMw8A8BGBAQAAAAAABAAAAAAAAAAA","499":"b:AAEFMw8A8BGBAQAAAAAABAAAAAAAAAAA","503":"b:AAEFMw8A8BGBAQAAAAAABAAAAAAAAAAA","511":"b:AAEFMw8A8BGBAQAAAAAABAAAAAAAAAAA","518":"b:AAEFMw8A8BGBAQAAAAAABAAAAAAAAAAA","557":"b:AAEFMw8A8BGBAQAAAAAABAAAAAAAAAAA","578":"b:AAEFMw8A8BGBAQAAAAAABAAAAAAAAAAA","585":"b:AAEFMw8A8BGBAQAAAAAABAAAAAAAAAAA","593":"b:AAEFMw8A8BGBAQAAAAAABAAAAAAAAAAA","652":"b:AAEFMw8A8BGBAQAAAAAABAAAAAAAAAAA","679":"b:AAEFMw8A8BGBAQAAAAAABAAAAAAAAAAA","689":"b:AAEFMw8A8BGBAQAAAAAABAAAAAAAAAAA","701":"b:AAEFMw8A8BGBAQAAAAAABAAAAAAAAAAA","703":"b:AAEFMw8A8BGBAQAAAAAABAAAAAAAAAAA","708":"b:AAEFMw8A8BGBAQAAAAAABAAAAAAAAAAA","730":"b:AAEFMw8A8BGBAQAAAAAABAAAAAAAAAAA","781":"b:AAEFMw8A8BGBAQAAAAAABAAAAAAAAAAA","790":"b:AAEFMw8A8BGBAQAAAAAABAAAAAAAAAAA","814":"b:AAEFMw8A8BGBAQAAAAAABAAAAAAAAAAA","825":"b:AAEFMw8A8BGBAQAAAAAABAAAAAAAAAAA","831":"b:AAEFMw8A8BGBAQAAAAAABAAAAAAAAAAA"},"packages/cli/src/modules/redaction/executions/strategies/full-item-redaction.strategy.ts":{"17":[56],"20":[56],"25":[56],"80":[56],"93":[56],"111":[56]},"packages/cli/src/modules/redaction/redaction-enforcement.service.ts":{"20":"b:AiCGiNABsAEMAYAAAAAAIAQAIAEAAIEA","24":"b:AiCGiNABsAEMAYAAAAAAIAQAIAEAAIEA","50":"b:AiCGiNABsAEMAYAAAAAAIAQAIAEAAIEA"},"packages/cli/src/modules/redaction/redaction-policy.ts":{"37":[56],"48":[56]},"packages/cli/src/public-api/index.ts":{"41":[186,176,41,56,64,126],"43":[186,176,41,56,64,126],"50":[186,176,41,56,64,126],"70":[186,176,41,56,64,126],"80":[186,176,41,56,64,126],"87":[186,176,41,56,64,126],"156":[186,176,41,56,64,126],"164":[186,176,41,56,64,126],"172":[186,176,41,56,64,126],"174":[186,176,41,56,64,126],"180":[186,176,41,56,64,126],"217":[186,176,41,56,64,126],"231":[186,176,41,56,64,126],"237":[186,176,41,56,64,126],"245":[186,176,41,56,64,126],"247":[186,176,41,56,64,126],"257":[186,176,41,56,64,126],"261":[186,176,41,56,64,126],"296":[186,176,41,56,64,126],"300":[186,176,41,56,64,126],"304":[186,176,41,56,64,126],"315":[186,176,41,56,64,126],"337":[186,176,41,56,64,126],"354":[186,176,41,56,64,126],"362":[186,176,41,56,64,126],"380":[186,176,41,56,64,126],"394":[186,176,41,56,64,126],"398":[186,176,41,56,64,126],"400":[186,176,41,56,64,126],"413":[186,176,41,56,64,126]},"packages/cli/src/public-api/v1/handlers/audit/audit.handler.ts":{"15":[186,176,41,56,64,126]},"packages/cli/src/public-api/v1/handlers/credentials/credentials.handler.ts":{"60":[186,176,41,56,64,126],"74":[186,176,41,56,64,126],"99":[186,176,41,56,64,126],"113":[186,176,41,56,64,126],"134":[186,176,41,56,64,126],"144":[186,176,41,56,64,126],"170":[186,176,41,56,64,126],"194":[186,176,41,56,64,126],"209":[186,176,41,56,64,126],"243":[186,176,41,56,64,126],"254":[186,176,41,56,64,126]},"packages/cli/src/public-api/v1/handlers/credentials/credentials.mapper.ts":{"8":[186,176,41,56,64,126]},"packages/cli/src/public-api/v1/handlers/credentials/credentials.middleware.ts":{"21":[186,176,41,56,64,126],"28":[186,176,41,56,64,126],"36":[186,176,41,56,64,126],"41":[186,176,41,56,64,126],"55":[186,176,41,56,64,126],"70":[186,176,41,56,64,126],"89":[186,176,41,56,64,126]},"packages/cli/src/public-api/v1/handlers/credentials/credentials.service.ts":{"30":[186,176,41,56,64,126],"34":[186,176,41,56,64,126],"55":[186,176,41,56,64,126],"60":[186,176,41,56,64,126],"61":[186,176,41,56,64,126],"70":[186,176,41,56,64,126],"77":[186,176,41,56,64,126],"81":[186,176,41,56,64,126],"98":[186,176,41,56,64,126],"153":[186,176,41,56,64,126],"194":[186,176,41,56,64,126],"259":[186,176,41,56,64,126],"272":[186,176,41,56,64,126],"287":[186,176,41,56,64,126],"293":[186,176,41,56,64,126],"308":[186,176,41,56,64,126],"323":[186,176,41,56,64,126],"324":[186,176,41,56,64,126],"327":[186,176,41,56,64,126],"341":[186,176,41,56,64,126],"352":[186,176,41,56,64,126],"478":[186,176,41,56,64,126],"486":[186,176,41,56,64,126]},"packages/cli/src/public-api/v1/handlers/data-tables/data-tables.columns.handler.ts":{"14":[186,176,41,56,64,126],"36":[186,176,41,56,64,126],"46":[186,176,41,56,64,126],"70":[186,176,41,56,64,126],"81":[186,176,41,56,64,126]},"packages/cli/src/public-api/v1/handlers/data-tables/data-tables.handler.ts":{"30":[186,176,41,56,64,126],"51":[186,176,41,56,64,126],"73":[186,176,41,56,64,126],"110":[186,176,41,56,64,126],"128":[186,176,41,56,64,126],"153":[186,176,41,56,64,126],"181":[186,176,41,56,64,126],"216":[186,176,41,56,64,126]},"packages/cli/src/public-api/v1/handlers/data-tables/data-tables.rows.handler.ts":{"25":[186,176,41,56,64,126],"40":[186,176,41,56,64,126],"64":[186,176,41,56,64,126],"107":[186,176,41,56,64,126],"136":[186,176,41,56,64,126],"167":[186,176,41,56,64,126],"198":[186,176,41,56,64,126],"217":[186,176,41,56,64,126]},"packages/cli/src/public-api/v1/handlers/data-tables/data-tables.service.ts":{"9":[186,176,41,56,64,126],"32":[186,176,41,56,64,126],"33":[186,176,41,56,64,126],"41":[186,176,41,56,64,126]},"packages/cli/src/public-api/v1/handlers/discover/discover.handler.ts":{"18":[186,176,41,56,64,126],"30":[186,176,41,56,64,126]},"packages/cli/src/public-api/v1/handlers/discover/discover.service.ts":{"63":[186,176,41,56,64,126],"75":[186,176,41,56,64,126],"82":[186,176,41,56,64,126],"166":[186,176,41,56,64,126],"174":[186,176,41,56,64,126],"215":[186,176,41,56,64,126],"220":[186,176,41,56,64,126],"224":[186,176,41,56,64,126],"232":[186,176,41,56,64,126],"256":[186,176,41,56,64,126]},"packages/cli/src/public-api/v1/handlers/evaluations/evaluations.handler.ts":{"35":[186,176,41,56,64,126],"46":[186,176,41,56,64,126],"67":[186,176,41,56,64,126],"86":[186,176,41,56,64,126],"110":[186,176,41,56,64,126],"136":[186,176,41,56,64,126],"151":[186,176,41,56,64,126],"164":[186,176,41,56,64,126],"178":[186,176,41,56,64,126]},"packages/cli/src/public-api/v1/handlers/evaluations/evaluations.mapper.ts":{"29":[186,176,41,56,64,126],"45":[186,176,41,56,64,126]},"packages/cli/src/public-api/v1/handlers/executions/executions.handler.ts":{"30":[186,176,41,56,64,126],"41":[186,176,41,56,64,126],"61":[186,176,41,56,64,126],"105":[186,176,41,56,64,126],"157":[186,176,41,56,64,126],"181":[186,176,41,56,64,126],"182":[186,176,41,56,64,126],"245":[186,176,41,56,64,126],"273":[186,176,41,56,64,126],"296":[186,176,41,56,64,126],"298":[186,176,41,56,64,126],"327":[186,176,41,56,64,126],"349":[186,176,41,56,64,126],"351":[186,176,41,56,64,126]},"packages/cli/src/public-api/v1/handlers/executions/executions.service.ts":{"9":[186,176,41,56,64,126],"10":[186,176,41,56,64,126],"18":[186,176,41,56,64,126],"27":[186,176,41,56,64,126],"32":[186,176,41,56,64,126],"48":[186,176,41,56,64,126]},"packages/cli/src/public-api/v1/handlers/folders/folders.handler.ts":{"26":[186,176,41,56,64,126],"49":[186,176,41,56,64,126],"77":[186,176,41,56,64,126],"96":[186,176,41,56,64,126],"116":[186,176,41,56,64,126],"134":[186,176,41,56,64,126]},"packages/cli/src/public-api/v1/handlers/insights/insights.handler.ts":{"15":[186,176,41,56,64,126],"29":[186,176,41,56,64,126],"49":[186,176,41,56,64,126],"52":[186,176,41,56,64,126],"57":[186,176,41,56,64,126]},"packages/cli/src/public-api/v1/handlers/ldap/ldap.handler.ts":{"35":[186,176,41,56,64,126],"46":[186,176,41,56,64,126],"67":[186,176,41,56,64,126],"82":[186,176,41,56,64,126]},"packages/cli/src/public-api/v1/handlers/ldap/ldap.mapper.ts":{"6":[186,176,41,56,64,126],"13":[186,176,41,56,64,126],"26":[186,176,41,56,64,126]},"packages/cli/src/public-api/v1/handlers/log-streaming/log-streaming.handler.ts":{"23":[186,176,41,56,64,126],"31":[186,176,41,56,64,126],"53":[186,176,41,56,64,126],"61":[186,176,41,56,64,126],"70":[186,176,41,56,64,126],"79":[186,176,41,56,64,126],"103":[186,176,41,56,64,126],"129":[186,176,41,56,64,126],"147":[186,176,41,56,64,126]},"packages/cli/src/public-api/v1/handlers/log-streaming/log-streaming.mapper.ts":{"24":[186,176,41,56,64,126],"31":[186,176,41,56,64,126]},"packages/cli/src/public-api/v1/handlers/otel/otel.handler.ts":{"26":[186,176,41,56,64,126],"35":[186,176,41,56,64,126],"50":[186,176,41,56,64,126],"70":[186,176,41,56,64,126]},"packages/cli/src/public-api/v1/handlers/otel/otel.mapper.ts":{"5":[186,176,41,56,64,126]},"packages/cli/src/public-api/v1/handlers/projects/projects.handler.ts":{"48":[186,176,41,56,64,126],"60":[186,176,41,56,64,126],"74":[186,176,41,56,64,126],"93":[186,176,41,56,64,126],"113":[186,176,41,56,64,126],"135":[186,176,41,56,64,126],"164":[186,176,41,56,64,126],"182":[186,176,41,56,64,126],"201":[186,176,41,56,64,126],"219":[186,176,41,56,64,126]},"packages/cli/src/public-api/v1/handlers/security-policy/security-policy.handler.ts":{"17":[186,176,41,56,64,126],"28":[186,176,41,56,64,126],"37":[186,176,41,56,64,126]},"packages/cli/src/public-api/v1/handlers/source-control/source-control.handler.ts":{"23":[186,176,41,56,64,126]},"packages/cli/src/public-api/v1/handlers/sso-oidc/sso-oidc.handler.ts":{"26":[186,176,41,56,64,126],"36":[186,176,41,56,64,126]},"packages/cli/src/public-api/v1/handlers/sso-oidc/sso-oidc.mapper.ts":{"8":[186,176,41,56,64,126]},"packages/cli/src/public-api/v1/handlers/sso-saml/sso-saml.handler.ts":{"26":[186,176,41,56,64,126],"36":[186,176,41,56,64,126]},"packages/cli/src/public-api/v1/handlers/sso-saml/sso-saml.mapper.ts":{"20":[186,176,41,56,64,126],"61":[186,176,41,56,64,126]},"packages/cli/src/public-api/v1/handlers/tags/tags.handler.ts":{"21":[186,176,41,56,64,126],"36":[186,176,41,56,64,126],"58":[186,176,41,56,64,126],"74":[186,176,41,56,64,126]},"packages/cli/src/public-api/v1/handlers/users/users.handler.ee.ts":{"39":[186,176,95,41,56,64,126],"61":[186,176,95,41,56,64,126],"94":[186,176,95,41,56,64,126],"110":[186,176,95,41,56,64,126],"119":[186,176,95,41,56,64,126]},"packages/cli/src/public-api/v1/handlers/users/users.service.ee.ts":{"8":[186,176,95,41,56,64,126],"20":[186,176,95,41,56,64,126],"29":[186,176,95,41,56,64,126],"44":[186,176,95,41,56,64,126],"50":[186,176,95,41,56,64,126],"65":[186,176,95,41,56,64,126],"72":[186,176,95,41,56,64,126],"77":[186,176,95,41,56,64,126]},"packages/cli/src/public-api/v1/handlers/variables/variables.handler.ts":{"29":[186,176,41,56,64,126],"42":[186,176,41,56,64,126],"55":[186,176,41,56,64,126],"65":[186,176,41,56,64,126]},"packages/cli/src/public-api/v1/handlers/workflows/workflows.handler.ts":{"31":[186,176,41,56,64,126],"65":[186,176,41,56,64,126],"87":[186,176,41,56,64,126],"105":[186,176,41,56,64,126],"113":[186,176,41,56,64,126],"130":[186,176,41,56,64,126],"146":[186,176,41,56,64,126],"182":[186,176,41,56,64,126],"209":[186,176,41,56,64,126],"275":[186,176,41,56,64,126],"324":[186,176,41,56,64,126],"347":[186,176,41,56,64,126],"398":[186,176,41,56,64,126],"425":[186,176,41,56,64,126],"427":[186,176,41,56,64,126],"464":[186,176,41,56,64,126],"480":[186,176,41,56,64,126]},"packages/cli/src/public-api/v1/handlers/workflows/workflows.service.ts":{"18":[186,176,41,56,64,126],"22":[186,176,41,56,64,126],"41":[186,176,41,56,64,126],"57":[186,176,41,56,64,126],"63":[186,176,41,56,64,126],"97":[186,176,41,56,64,126],"101":[186,176,41,56,64,126],"102":[186,176,41,56,64,126],"105":[186,176,41,56,64,126],"116":[186,176,41,56,64,126],"118":[186,176,41,56,64,126],"125":[186,176,41,56,64,126]},"packages/cli/src/public-api/v1/shared/middlewares/global.middleware.ts":{"21":[186,176,95,41,56,64,126],"26":[186,176,95,41,56,64,126],"57":[186,176,95,41,56,64,126],"60":[186,176,95,41,56,64,126],"63":[186,176,95,41,56,64,126],"104":[186,176,95,41,56,64,126],"107":[186,176,95,41,56,64,126],"119":[186,176,95,41,56,64,126],"121":[186,176,95,41,56,64,126],"127":[186,176,95,41,56,64,126],"128":[186,176,95,41,56,64,126],"150":[186,176,95,41,56,64,126],"158":[186,176,95,41,56,64,126],"160":[186,176,95,41,56,64,126],"167":[186,176,95,41,56,64,126],"174":[186,176,95,41,56,64,126],"189":[186,176,95,41,56,64,126],"190":[186,176,95,41,56,64,126]},"packages/cli/src/public-api/v1/shared/public-api-scope-lookup.ts":{"9":[186,176,41,56,64,126],"19":[186,176,41,56,64,126],"27":[186,176,41,56,64,126],"33":[186,176,41,56,64,126],"43":[186,176,41,56,64,126],"63":[186,176,41,56,64,126],"75":[186,176,41,56,64,126]},"packages/cli/src/public-api/v1/shared/services/utils.service.ts":{"9":[186,176,41,56,64,126]},"packages/cli/src/requests.ts":{"54":"b:OAEBO/MH8BGBGXAgABgAAAAAAAAACAAA","63":"b:OAEBO/MH8BGBGXAgABgAAAAAAAAACAAA","66":"b:OAEBO/MH8BGBGXAgABgAAAAAAAAACAAA"},"packages/cli/src/scheduling/schedule-trigger-node/schedule-trigger-job-registrar.ts":{"107":"b:AABAIAEEBkkAAAACAAAAgAAAAFvYB+AD","131":"b:AABAIAEEBkkAAAACAAAAgAAAAFvYB+AD","150":"b:AABAIAEEBkkAAAACAAAAgAAAAFvYB+AD","159":"b:AABAIAEEBkkAAAACAAAAgAAAAFvYB+AD","165":"b:AABAIAEEBkkAAAACAAAAgAAAAFvYB+AD","204":"b:AABAIAEEBkkAAAACAAAAgAAAAFvYB+AD","213":"b:AABAIAEEBkkAAAACAAAAgAAAAFvYB+AD","240":"b:AABAIAEEBkkAAAACAAAAgAAAAFvYB+AD","287":"b:AABAIAEEBkkAAAACAAAAgAAAAFvYB+AD","293":"b:AABAIAEEBkkAAAACAAAAgAAAAFvYB+AD","334":"b:AABAIAEEBkkAAAACAAAAgAAAAFvYB+AD","349":"b:AABAIAEEBkkAAAACAAAAgAAAAFvYB+AD","365":"b:AABAIAEEBkkAAAACAAAAgAAAAFvYB+AD","374":"b:AABAIAEEBkkAAAACAAAAgAAAAFvYB+AD","376":"b:AABAIAEEBkkAAAACAAAAgAAAAFvYB+AD","393":"b:AABAIAEEBkkAAAACAAAAgAAAAFvYB+AD","397":"b:AABAIAEEBkkAAAACAAAAgAAAAFvYB+AD"},"packages/cli/src/services/api-key-auth.strategy.ts":{"17":[186,41,42,56,64,126],"23":[186,41,42,56,64,126],"66":[186,41,42,56,64,126],"72":[186,41,42,56,64,126],"79":[186,41,42,56,64,126],"84":[186,41,42,56,64,126]},"packages/cli/src/services/auth-strategy.registry.ts":{"24":[186,176,41,42,56,64,126],"27":[186,176,41,42,56,64,126],"31":[186,176,41,42,56,64,126],"49":[186,176,41,42,56,64,126]},"packages/cli/src/services/public-api-key.service.ts":{"37":[186,41,56,61,122,64,126],"41":[186,41,56,61,122,64,126],"48":[186,41,56,61,122,64,126],"66":[186,41,56,61,122,64,126],"82":[186,41,56,61,122,64,126],"130":[186,41,56,61,122,64,126],"141":[186,41,56,61,122,64,126],"166":[186,41,56,61,122,64,126],"177":[186,41,56,61,122,64,126],"205":[186,41,56,61,122,64,126],"233":[186,41,56,61,122,64,126],"251":[186,41,56,61,122,64,126],"263":[186,41,56,61,122,64,126],"264":[186,41,56,61,122,64,126],"270":[186,41,56,61,122,64,126],"275":[186,41,56,61,122,64,126],"286":[186,41,56,61,122,64,126],"308":[186,41,56,61,122,64,126],"332":[186,41,56,61,122,64,126],"341":[186,41,56,61,122,64,126],"350":[186,41,56,61,122,64,126],"355":[186,41,56,61,122,64,126],"357":[186,41,56,61,122,64,126],"360":[186,41,56,61,122,64,126],"370":[186,41,56,61,122,64,126],"379":[186,41,56,61,122,64,126],"380":[186,41,56,61,122,64,126],"385":[186,41,56,61,122,64,126],"387":[186,41,56,61,122,64,126]},"packages/cli/src/services/role-cache.service.ts":{"37":"b:cgDgK4MGxkmhGXAiAAAAAAAAAADQB2EA","45":"b:cgDgK4MGxkmhGXAiAAAAAAAAAADQB2EA","54":"b:cgDgK4MGxkmhGXAiAAAAAAAAAADQB2EA","68":"b:cgDgK4MGxkmhGXAiAAAAAAAAAADQB2EA","77":"b:cgDgK4MGxkmhGXAiAAAAAAAAAADQB2EA","93":"b:cgDgK4MGxkmhGXAiAAAAAAAAAADQB2EA","105":"b:cgDgK4MGxkmhGXAiAAAAAAAAAADQB2EA","112":"b:cgDgK4MGxkmhGXAiAAAAAAAAAADQB2EA"},"packages/cli/src/services/security-settings.service.ts":{"54":[56],"69":[56],"99":[56],"149":[56],"157":[56],"177":[56],"184":[56],"187":[56],"196":[56],"200":[56],"204":[56],"209":[56]},"packages/cli/src/user-management/email/user-management-mailer.ts":{"28":"b:AAAAAAECgAEBAQAAABAAdAAAAAAAgAAE","32":"b:AAAAAAECgAEBAQAAABAAdAAAAAAAgAAE","63":"b:AAAAAAECgAEBAQAAABAAdAAAAAAAgAAE","80":"b:AAAAAAECgAEBAQAAABAAdAAAAAAAgAAE","91":"b:AAAAAAECgAEBAQAAABAAdAAAAAAAgAAE","102":"b:AAAAAAECgAEBAQAAABAAdAAAAAAAgAAE","130":"b:AAAAAAECgAEBAQAAABAAdAAAAAAAgAAE","159":"b:AAAAAAECgAEBAQAAABAAdAAAAAAAgAAE","185":"b:AAAAAAECgAEBAQAAABAAdAAAAAAAgAAE","206":"b:AAAAAAECgAEBAQAAABAAdAAAAAAAgAAE","216":"b:AAAAAAECgAEBAQAAABAAdAAAAAAAgAAE","233":"b:AAAAAAECgAEBAQAAABAAdAAAAAAAgAAE","247":"b:AAAAAAECgAEBAQAAABAAdAAAAAAAgAAE","261":"b:AAAAAAECgAEBAQAAABAAdAAAAAAAgAAE","265":"b:AAAAAAECgAEBAQAAABAAdAAAAAAAgAAE","270":"b:AAAAAAECgAEBAQAAABAAdAAAAAAAgAAE","286":"b:AAAAAAECgAEBAQAAABAAdAAAAAAAgAAE","290":"b:AAAAAAECgAEBAQAAABAAdAAAAAAAgAAE","295":"b:AAAAAAECgAEBAQAAABAAdAAAAAAAgAAE","311":"b:AAAAAAECgAEBAQAAABAAdAAAAAAAgAAE","315":"b:AAAAAAECgAEBAQAAABAAdAAAAAAAgAAE","320":"b:AAAAAAECgAEBAQAAABAAdAAAAAAAgAAE","329":"b:AAAAAAECgAEBAQAAABAAdAAAAAAAgAAE","334":"b:AAAAAAECgAEBAQAAABAAdAAAAAAAgAAE","335":"b:AAAAAAECgAEBAQAAABAAdAAAAAAAgAAE","348":"b:AAAAAAECgAEBAQAAABAAdAAAAAAAgAAE","353":"b:AAAAAAECgAEBAQAAABAAdAAAAAAAgAAE","358":"b:AAAAAAECgAEBAQAAABAAdAAAAAAAgAAE","374":"b:AAAAAAECgAEBAQAAABAAdAAAAAAAgAAE"},"packages/cli/src/webhooks/live-webhooks.ts":{"36":"b:AAjAAAAABEEAAAACAAAAAAAAAAAYAHAJ","46":"b:AAjAAAAABEEAAAACAAAAAAAAAAAYAHAJ","50":"b:AAjAAAAABEEAAAACAAAAAAAAAAAYAHAJ","58":"b:AAjAAAAABEEAAAACAAAAAAAAAAAYAHAJ","63":"b:AAjAAAAABEEAAAACAAAAAAAAAAAYAHAJ","78":"b:AAjAAAAABEEAAAACAAAAAAAAAAAYAHAJ","99":"b:AAjAAAAABEEAAAACAAAAAAAAAAAYAHAJ","128":"b:AAjAAAAABEEAAAACAAAAAAAAAAAYAHAJ","138":"b:AAjAAAAABEEAAAACAAAAAAAAAAAYAHAJ","162":"b:AAjAAAAABEEAAAACAAAAAAAAAAAYAHAJ","175":"b:AAjAAAAABEEAAAACAAAAAAAAAAAYAHAJ","190":"b:AAjAAAAABEEAAAACAAAAAAAAAAAYAHAJ","202":"b:AAjAAAAABEEAAAACAAAAAAAAAAAYAHAJ","216":"b:AAjAAAAABEEAAAACAAAAAAAAAAAYAHAJ","232":"b:AAjAAAAABEEAAAACAAAAAAAAAAAYAHAJ"},"packages/cli/src/webhooks/webhook-on-received-response-extractor.ts":{"13":"b:QADAIAAADEEQAAACgAAAAAAAAAAYAFAA"},"packages/cli/src/webhooks/webhook-request-sanitizer.ts":{"12":"b:QADAIAAADEEQAAACAAAAAAAACAAYAHAB","18":"b:QADAIAAADEEQAAACAAAAAAAACAAYAHAB","19":"b:QADAIAAADEEQAAACAAAAAAAACAAYAHAB","32":"b:QADAIAAADEEQAAACAAAAAAAACAAYAHAB","40":"b:QADAIAAADEEQAAACAAAAAAAACAAYAHAB"},"packages/cli/src/workflows/triggers/trigger-execution-context.factory.ts":{"62":"b:AABAIAEEBkkAAAACAAAAgAAAAAPYB+AD","83":"b:AABAIAEEBkkAAAACAAAAgAAAAAPYB+AD","99":"b:AABAIAEEBkkAAAACAAAAgAAAAAPYB+AD","100":"b:AABAIAEEBkkAAAACAAAAgAAAAAPYB+AD","114":"b:AABAIAEEBkkAAAACAAAAgAAAAAPYB+AD","125":"b:AABAIAEEBkkAAAACAAAAgAAAAAPYB+AD","139":"b:AABAIAEEBkkAAAACAAAAgAAAAAPYB+AD","158":"b:AABAIAEEBkkAAAACAAAAgAAAAAPYB+AD","171":"b:AABAIAEEBkkAAAACAAAAgAAAAAPYB+AD","175":"b:AABAIAEEBkkAAAACAAAAgAAAAAPYB+AD","179":"b:AABAIAEEBkkAAAACAAAAgAAAAAPYB+AD","190":"b:AABAIAEEBkkAAAACAAAAgAAAAAPYB+AD","217":"b:AABAIAEEBkkAAAACAAAAgAAAAAPYB+AD","228":"b:AABAIAEEBkkAAAACAAAAgAAAAAPYB+AD","229":"b:AABAIAEEBkkAAAACAAAAgAAAAAPYB+AD","241":"b:AABAIAEEBkkAAAACAAAAgAAAAAPYB+AD","253":"b:AABAIAEEBkkAAAACAAAAgAAAAAPYB+AD","260":"b:AABAIAEEBkkAAAACAAAAgAAAAAPYB+AD","264":"b:AABAIAEEBkkAAAACAAAAgAAAAAPYB+AD","267":"b:AABAIAEEBkkAAAACAAAAgAAAAAPYB+AD","281":"b:AABAIAEEBkkAAAACAAAAgAAAAAPYB+AD","301":"b:AABAIAEEBkkAAAACAAAAgAAAAAPYB+AD","317":"b:AABAIAEEBkkAAAACAAAAgAAAAAPYB+AD","350":"b:AABAIAEEBkkAAAACAAAAgAAAAAPYB+AD"},"packages/cli/src/workflows/workflow-publish-guard-proxy.service.ts":{"13":"b:AADAIAEEBkkAAAACAAAAgAAAAAPYB+AD","16":"b:AADAIAEEBkkAAAACAAAAgAAAAAPYB+AD","20":"b:AADAIAEEBkkAAAACAAAAgAAAAAPYB+AD"},"packages/community-packages.handler.ts":{"32":[186,176,41,56,64,126],"47":[186,176,41,56,64,126],"56":[186,176,41,56,64,126],"74":[186,176,41,56,64,126]},"packages/community-packages.mapper.ts":{"4":[186,176,41,56,64,126],"8":[186,176,41,56,64,126],"12":[186,176,41,56,64,126],"17":[186,176,41,56,64,126],"22":[186,176,41,56,64,126],"36":[186,176,41,56,64,126]},"packages/core/src/errors/error-reporter.ts":{"101":"b:QoAABIoAAAEQAgQABAAACAAIAiAUAAAA","118":"b:QoAABIoAAAEQAgQABAAACAAIAiAUAAAA","126":"b:QoAABIoAAAEQAgQABAAACAAIAiAUAAAA","158":"b:QoAABIoAAAEQAgQABAAACAAIAiAUAAAA","164":"b:QoAABIoAAAEQAgQABAAACAAIAiAUAAAA","185":"b:QoAABIoAAAEQAgQABAAACAAIAiAUAAAA","191":"b:QoAABIoAAAEQAgQABAAACAAIAiAUAAAA","196":"b:QoAABIoAAAEQAgQABAAACAAIAiAUAAAA","240":"b:QoAABIoAAAEQAgQABAAACAAIAiAUAAAA","241":"b:QoAABIoAAAEQAgQABAAACAAIAiAUAAAA","287":"b:QoAABIoAAAEQAgQABAAACAAIAiAUAAAA","288":"b:QoAABIoAAAEQAgQABAAACAAIAiAUAAAA","301":"b:QoAABIoAAAEQAgQABAAACAAIAiAUAAAA","328":"b:QoAABIoAAAEQAgQABAAACAAIAiAUAAAA","332":"b:QoAABIoAAAEQAgQABAAACAAIAiAUAAAA","347":"b:QoAABIoAAAEQAgQABAAACAAIAiAUAAAA","353":"b:QoAABIoAAAEQAgQABAAACAAIAiAUAAAA","400":"b:QoAABIoAAAEQAgQABAAACAAIAiAUAAAA","406":"b:QoAABIoAAAEQAgQABAAACAAIAiAUAAAA","410":"b:QoAABIoAAAEQAgQABAAACAAIAiAUAAAA","414":"b:QoAABIoAAAEQAgQABAAACAAIAiAUAAAA","421":"b:QoAABIoAAAEQAgQABAAACAAIAiAUAAAA","426":"b:QoAABIoAAAEQAgQABAAACAAIAiAUAAAA","430":"b:QoAABIoAAAEQAgQABAAACAAIAiAUAAAA","434":"b:QoAABIoAAAEQAgQABAAACAAIAiAUAAAA","444":"b:QoAABIoAAAEQAgQABAAACAAIAiAUAAAA","466":"b:QoAABIoAAAEQAgQABAAACAAIAiAUAAAA"},"packages/core/src/execution-engine/node-execution-context/utils/webhook-helper-functions.ts":{"13":"b:QADAIAAADEEQAAACAAAAAAAACAAYAHAB","33":"b:QADAIAAADEEQAAACAAAAAAAACAAYAHAB"},"packages/n8n-packages.handler.ts":{"51":[186,176,41,56,64,126],"80":[186,176,41,56,64,126],"87":[186,176,41,56,64,126],"97":[186,176,41,56,64,126],"99":[186,176,41,56,64,126],"100":[186,176,41,56,64,126],"111":[186,176,41,56,64,126],"120":[186,176,41,56,64,126],"177":[186,176,41,56,64,126],"184":[186,176,41,56,64,126]},"packages/nodes-base/nodes/Webhook/Webhook.node.ts":{"206":"b:QADAIAAFDEEQAAACACAAAAAICEAYAHAJ","218":"b:QADAIAAFDEEQAAACACAAAAAICEAYAHAJ","349":"b:QADAIAAFDEEQAAACACAAAAAICEAYAHAJ","353":"b:QADAIAAFDEEQAAACACAAAAAICEAYAHAJ"},"packages/nodes-base/nodes/Webhook/description.ts":{"37":"b:QADAIAAFDEEQAAACACAAAAAIAAAYAEAB","74":"b:QADAIAAFDEEQAAACACAAAAAIAAAYAEAB","118":"b:QADAIAAFDEEQAAACACAAAAAIAAAYAEAB"},"packages/nodes-base/nodes/Webhook/error.ts":{"4":"b:QAnFIECVDEFAAgAAgCAAAAAIAAAYAEAA"},"packages/nodes-base/nodes/Webhook/utils.ts":{"38":"b:QAHFIECVDEFQAgACgCAAAAAICEAYAHAJ","55":"b:QAHFIECVDEFQAgACgCAAAAAICEAYAHAJ","69":"b:QAHFIECVDEFQAgACgCAAAAAICEAYAHAJ","80":"b:QAHFIECVDEFQAgACgCAAAAAICEAYAHAJ","90":"b:QAHFIECVDEFQAgACgCAAAAAICEAYAHAJ","107":"b:QAHFIECVDEFQAgACgCAAAAAICEAYAHAJ","118":"b:QAHFIECVDEFQAgACgCAAAAAICEAYAHAJ","120":"b:QAHFIECVDEFQAgACgCAAAAAICEAYAHAJ","131":"b:QAHFIECVDEFQAgACgCAAAAAICEAYAHAJ","141":"b:QAHFIECVDEFQAgACgCAAAAAICEAYAHAJ","156":"b:QAHFIECVDEFQAgACgCAAAAAICEAYAHAJ","167":"b:QAHFIECVDEFQAgACgCAAAAAICEAYAHAJ","205":"b:QAHFIECVDEFQAgACgCAAAAAICEAYAHAJ","210":"b:QAHFIECVDEFQAgACgCAAAAAICEAYAHAJ","228":"b:QAHFIECVDEFQAgACgCAAAAAICEAYAHAJ","229":"b:QAHFIECVDEFQAgACgCAAAAAICEAYAHAJ","233":"b:QAHFIECVDEFQAgACgCAAAAAICEAYAHAJ","242":"b:QAHFIECVDEFQAgACgCAAAAAICEAYAHAJ","258":"b:QAHFIECVDEFQAgACgCAAAAAICEAYAHAJ","381":"b:QAHFIECVDEFQAgACgCAAAAAICEAYAHAJ","446":"b:QAHFIECVDEFQAgACgCAAAAAICEAYAHAJ","464":"b:QAHFIECVDEFQAgACgCAAAAAICEAYAHAJ"},"packages/package-failure-classifier.ts":{"20":[186,176,41,56,64,126]},"packages/utils/import-package-upload.ts":{"25":[186,176,41,56,64,126],"38":[186,176,41,56,64,126],"46":[186,176,41,56,64,126],"50":[186,176,41,56,64,126],"53":[186,176,41,56,64,126]},"packages/workflow/src/node-validation.ts":{"19":"b:AADAIAEEBkkAAAACAAAAgAAAAAPYB+AD","59":"b:AADAIAEEBkkAAAACAAAAgAAAAAPYB+AD","84":"b:AADAIAEEBkkAAAACAAAAgAAAAAPYB+AD"},"packages/workflow/src/workflow-validation.ts":{"16":"b:AADAIAEEBkkAAAACAAAAgAAAAAPYB+AD"},"localhost-40079/assets/_plugin-vue_export-helper-Da88TEg1.js":{"2":[56]},"localhost-40079/assets/chunk-CC9Q-vWm.js":{"8":[56],"9":[56],"10":[56],"19":[56],"23":[56],"29":[56],"33":[56],"34":[56]},"packages/@n8n/utils/src/result.ts":{"7":"b:QEAAAAAABBBQAIAAAIAoAAD4eiAAACAA","12":"b:wrUAQIAABQdaAIACAOEqAIRe/qAAACAA","23":[87,6,181,157]},"packages/frontend/@n8n/design-system/src/components/N8nAlertDialog/AlertDialog.vue":{"74":[77,56],"87":[56],"94":[77,56],"99":[77,56],"102":[56],"103":[56]},"packages/frontend/@n8n/design-system/src/components/N8nBadge/Badge.vue":{"23":"b:AiUAGQsAwBGBAQABChiA9AAAAGgGAAAA","25":"b:AiUAGQsAwBGBAQABChiA9AAAAGgGAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nDialog/Dialog.vue":{"95":[88,77,56,122,127],"99":[88,77,56,122,127],"108":[88,77,56,122,127]},"packages/frontend/@n8n/design-system/src/components/N8nDialog/DialogContent.vue":{"59":[88,77,56,122,127],"81":[88,77,56,122,127],"84":[56],"85":[56],"101":[88,77,56,122,127],"112":[56],"113":[56],"114":[56]},"packages/frontend/@n8n/design-system/src/components/N8nDialog/DialogDescription.vue":{"15":[56],"16":[56]},"packages/frontend/@n8n/design-system/src/components/N8nDialog/DialogFooter.vue":{"6":[56]},"packages/frontend/@n8n/design-system/src/components/N8nDialog/DialogHeader.vue":{"6":[56]},"packages/frontend/@n8n/design-system/src/components/N8nDialog/DialogOverlay.vue":{"15":[88,77,56,122,127]},"packages/frontend/@n8n/design-system/src/components/N8nDialog/DialogTitle.vue":{"15":[56],"16":[56]},"packages/frontend/@n8n/design-system/src/components/N8nDropdownMenu/DropdownMenuItem.vue":{"61":"b:cMEDQAwA+JGAAIJAAyp5VEAALggDAAAA","63":[4,5,96],"65":[4,5,96],"72":"b:cMEDQAwA+JGAAIJAAyp5VEAALggDAAAA","73":"b:cMEDQAwA+JGAAIJAAyp5VEAALggDAAAA","75":[4,5,96,52,107,53],"85":"b:cMEDQAwA+JGAAIJAAyp5VEAALggDAAAA","89":"b:cMEDQAwA+JGAAIJAAyp5VEAALggDAAAA","93":"b:cMEDQAwA+JGAAIJAAyp5VEAALggDAAAA","97":"b:AAEBAAgA+JAAAABAAghABAAAIAACAAAA","99":[4,5,96],"103":"b:cMEDQAwA+JGAAIJAAyp5VEAALggDAAAA","110":"b:cMEDQAwA+JGAAIJAAyp5VEAALggDAAAA","114":[5],"120":[4,5,96],"162":"b:cMEDQAwA+JGAAIJAAyp5VEAALggDAAAA","163":"b:cMEDQAwA+JGAAIJAAyp5VEAALggDAAAA","172":[4,5,96],"182":"b:cMEDQAwA+JGAAIJAAyp5VEAALggDAAAA","185":"b:cMEDQAwA+JGAAIJAAyp5VEAALggDAAAA","199":[4,5,96,52,107,53],"207":[4,5,96,52,107,53],"209":[52,107,53],"239":[4,5,96,52,107,53],"249":[4,5,96],"319":[4,5,96],"326":[4,5,96],"329":[4,5,96],"332":[4,5,96],"391":"b:cMEDQAwA+JGAAIJAAyp5VEAALggDAAAA","393":"b:QMEDQAgA+JGAAIBAAyp5VEAALggDAAAA","405":"b:AAEBAAgA+JAAAABAAghABAAAIAACAAAA","410":"b:AAEBAAgA+JAAAABAAghABAAAIAACAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nDropdownMenu/DropdownMenuItems.vue":{"32":"b:cMEDQAwA+JGAAIJAAyp5VEAALggDAAAA","48":"b:cMEDQAwA+JGAAIJAAyp5VEAALggDAAAA","69":"b:cMEDQAwA+JGAAIJAAyp5VEAALggDAAAA","71":"b:cMEDQAwA+JGAAIJAAyp5VEAALggDAAAA","78":"b:cMEDQAwA+JGAAIJAAyp5VEAALggDAAAA","80":[4,5,96,52,107,53],"81":"b:cMEDQAwA+JGAAIJAAyp5VEAALggDAAAA","84":"b:cMACQAQAEAGAAIIAASI5UEAADggBAAAA","87":"b:cMACQAQAMAGAAIIAASo5UEAADggBAAAA","90":"b:cMACQAwA8JGAAIIAAyp5VEAALggDAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nIcon/custom/mcp.svg":{"13":"b:AiQACQAA8LGAIWCAAhhADwAAAAAGAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nInfoAccordion/InfoAccordion.vue":{"29":"b:QgBABAAACAEwAABAgAAAAAgKCuACAAAA","40":"b:QgBABAAACAEwAABAgAAAAAgKCuACAAAA","54":"b:QgBABAAACAEwAABAgAAAAAgKCuACAAAA","61":"b:QgBABAAACAEwAABAgAAAAAgKCuACAAAA"},"packages/frontend/@n8n/design-system/src/components/PreviewTag/PreviewTag.vue":{"12":"b:AAEBAAAAgLEAImCACgBADwAAEAAAAAAA","13":"b:AAEBAAAAgLEAImCACgBADwAAEAAAAAAA"},"packages/frontend/@n8n/design-system/src/v2/components/Checkbox/Checkbox.vue":{"12":"b:QgAAAAAAhAEwACAAAABAgAkCAPgGAAAA","15":"b:QgAAAAAAhAEwACAAAABAgAkCAPgGAAAA","20":"b:QgAAAAAAhAEwACAAAABAgAkCAPgGAAAA","23":"b:QgAAAAAAhAEwACAAAABAgAkCAPgGAAAA","29":[118,85,127,6,161,162],"33":"b:QgAAAAAAhAEwACAAAABAgAkCAPgGAAAA","38":"b:QgAAAAAAhAEwACAAAABAgAkCAPgGAAAA","46":"b:QgAAAAAAhAEwACAAAABAgAkCAPgGAAAA","48":"b:QgAAAAAAAAEwACAAAABAgAkCAOAGAAAA","57":"b:QgAAAAAAhAEwACAAAABAgAkCAPgGAAAA","58":"b:QgAAAAAAAAEwAAAAAAAAAAkCAOAGAAAA"},"packages/frontend/editor-ui/src/app/components/EnterpriseEdition.ee.vue":{"17":"b:kkEBEAIA4BEBAgAASgACcAAAEAACAAAA","18":"b:kkEBEAIA4BEBAgAASgACcAAAEAACAAAA"},"packages/frontend/editor-ui/src/app/components/Modal.vue":{"59":"b:ssEBEA4A9LEBAkgATwDC9UAAEB0DAAAA","81":"b:ssEBEA4A5LEBAkgATwDC9UAAEB0DAAAA","90":"b:ssEBEA4A5JEBAkAADwDC5EAAEB0DAAAA","97":[28,8,34,60,64,125],"102":"b:ssEBEA4A5LEBAkAADwDC5UAAEB0DAAAA","109":[28,8,34,60,64,125,134],"113":"b:skEBEAIAtJEBAkAADgDC5EAAEB0DAAAA","125":"b:MAEAEAIAgJABAgAABgCAIAAAEAgBAAAA","129":"b:ssEBEA4A9LEBAkgATwDC9UAAEB0DAAAA","162":"b:ssEBEA4A9LEBAkgATwDC9UAAEB0DAAAA","164":"b:ssEBEA4AxLABAggATwACoQAAEBgCAAAA","178":[54,55,134]},"packages/frontend/editor-ui/src/app/components/RedactionMembersModal.vue":{"9":[56,127],"24":[56,127],"26":[56,127],"41":[56,127]},"packages/frontend/editor-ui/src/app/components/SettingsSidebar.vue":{"23":"b:AAAAAAAAALEAIGCAAgBADwAAAAAAAAAA","26":"b:AAAAAAAAALEAIGCAAgBADwAAAAAAAAAA","37":"b:AAAAAAAAALEAIGCAAgBADwAAAAAAAAAA","41":"b:AAAAAAAAALEAIGCAAgBADwAAAAAAAAAA"},"packages/frontend/editor-ui/src/app/components/WorkflowSettings/WorkflowSettings.vue":{"72":[56,127],"213":[56,127],"220":[56,127],"223":[56,127],"226":[56,127],"227":[56,127],"228":[56,127],"229":[56,127],"232":[56,127],"237":[56,127],"239":[56],"241":[56],"247":[56,127],"251":[56,127],"253":[56,127],"257":[56,127],"262":[56,127],"277":[56,127],"281":[56,127],"285":[56,127],"286":[56,127],"294":[56,127],"313":[56,127],"329":[56,127],"333":[56],"345":[56,127],"349":[56],"360":[56],"379":[56,127],"381":[56,127],"386":[56,127],"390":[56,127],"401":[56,127],"415":[56,127],"424":[56],"440":[56,127],"470":[56,127],"494":[56,127],"518":[56,127],"541":[56,127],"564":[56,127],"591":[56,127],"647":[56,127],"671":[56],"757":[56],"835":[56,127],"989":[56],"993":[56,127],"1008":[56,127],"1016":[56,127],"1019":[56,127],"1027":[56,127],"1030":[56,127],"1034":[56,127],"1045":[56,127],"1046":[56,127],"1050":[56,127],"1055":[56,127],"1065":[56,127],"1067":[56,127],"1072":[56,127],"1147":[56,127],"1151":[56,127],"1157":[56,127],"1163":[56,127],"1165":[56,127],"1198":[56,127],"1202":[56,127],"1207":[56,127],"1214":[56,127],"1216":[56,127],"1227":[56,127],"1231":[56,127],"1236":[56,127],"1243":[56,127],"1245":[56,127],"1256":[56,127],"1260":[56,127],"1265":[56,127],"1272":[56,127],"1274":[56,127],"1285":[56,127],"1289":[56,127],"1294":[56,127],"1301":[56,127],"1303":[56,127],"1314":[56,127],"1318":[56,127],"1323":[56,127],"1330":[56,127],"1332":[56,127],"1343":[56,127],"1346":[56,127],"1369":[127],"1383":[56,127],"1393":[56,127],"1399":[56],"1410":[56,127],"1426":[56,127],"1428":[56,127],"1450":[56,127],"1475":[127],"1489":[56,127],"1499":[56,127],"1508":[56],"1524":[56,127],"1538":[56,127],"1540":[56,127],"1553":[56,127],"1557":[56,127],"1562":[56,127],"1652":[56,127],"1657":[56,127],"1663":[56,127],"1671":[56,127],"1673":[56,127],"1684":[56,127],"1774":[56,127]},"packages/frontend/editor-ui/src/app/composables/useLoadingService.ts":{"14":"b:AGAACAAAgQGEAygAiAgAAAwUDgEHAAAA","26":[83,85,73,162],"32":[83,85,160,73,162],"39":"b:wuBXyAIAiAbeAYACgG8ggMz6LvcGAAAA"},"packages/frontend/editor-ui/src/app/layouts/SettingsLayout.vue":{"27":"b:AAAAAAAAALEAIGCAAgBADwAAAAAAAAAA","34":"b:AAAAAAAAALEAIGCAAgBADwAAAAAAAAAA","36":"b:AAAAAAAAALEAIGCAAgBADwAAAAAAAAAA"},"packages/frontend/editor-ui/src/app/stores/workflows.ee.store.ts":{"13":[56,124,126,127,161],"18":[56,127],"19":[56,127],"34":[126],"43":[126],"49":[126]},"packages/frontend/editor-ui/src/app/utils/rbac/middleware/rbac.ts":{"11":"b:AAAAAAAAALEAICAAAgBADQAAAAAAAAAA"},"packages/frontend/editor-ui/src/app/utils/rbacUtils.ts":{"4":"b:AAAAAAAAALEAICAAAgBADQAAAAAAAAAA","12":"b:AAAAAAAAALEAICAAAgBADQAAAAAAAAAA","23":"b:AAAAAAAAALEAICAAAgBADQAAAAAAAAAA","34":[60,122]},"packages/frontend/editor-ui/src/features/ai/evaluation.ee/composables/useAddExecutionToDataset.ts":{"15":[56,131,137,68,69,6,157,158,159]},"packages/frontend/editor-ui/src/features/execution/executions/components/ExecutionStopAllText.vue":{"11":[1,56,131,137,68,69,6,157,158,159],"18":[1,56,131,137,68,69,6,157,158,159],"25":[1,56,131,137,68,69,6,157,158,159]},"packages/frontend/editor-ui/src/features/execution/executions/components/ExecutionsFilter.vue":{"41":[1,56,131,137,68,69,6,157,158,159],"63":[157],"66":[157],"67":[157],"69":[1,56,131,137,68,69,6,157,158,159],"87":[157],"104":[157],"113":[157],"120":[157],"127":[157],"139":[157],"155":[1,56,131,137,68,69,6,157,158,159],"205":[157],"219":[1,56,131,137,68,69,6,157,158,159],"222":[1,56,131,137,68,69,6,157,158,159],"233":[1,56,131,137,68,69,6,157,158,159],"249":[157],"256":[157],"300":[157],"352":[157],"364":[157],"373":[157],"375":[157],"378":[157],"410":[157],"429":[157],"455":[157],"477":[157],"501":[157]},"packages/frontend/editor-ui/src/features/execution/executions/components/workflow/ExecutionPreviewHost.vue":{"18":[56,131,137,68,69,6,157,158,159],"33":[56,131,137,68,69,6,157,158,159],"36":[56,131,137,68,69,6,157,158,159],"44":[56,131,137,68,69,6,157,158,159],"56":[56,131,137,68,69,6,157,158,159],"57":[56,131,137,68,69,6,157,158,159],"58":[56,131,137,68,69,6,157,158,159],"60":[56,131,137,68,69,6,157,158,159],"71":[56,131,137,68,69,6,157,158,159],"110":[56,131,137,68,69,6,157,158,159],"111":[56,131,137,68,69,6,157,158,159],"113":[131,137,68,158,159],"116":[56,131,137,68,69,6,157,158,159]},"packages/frontend/editor-ui/src/features/execution/executions/components/workflow/WorkflowExecutionsCard.vue":{"41":[56,131,137,68,69,6,157,158,159],"44":[56,131,137,68,69,6,157,158,159],"48":[56,131,137,68,69,6,157,158,159],"49":[137,157,158],"59":[56,131,137,68,69,6,157,158,159],"62":[56,131,137,68,69,6,157,158,159],"63":[56,131,137,68,69,6,157,158,159],"65":[56,131,137,68,69,6,157,158,159],"71":[56,131,137,68,69,6,157,158,159],"106":[56,131,137,68,69,6,157,158,159],"121":[159],"139":[56,131,137,68,69,6,157,158,159],"183":[56,131,137,68,69,6,157,158,159]},"packages/frontend/editor-ui/src/features/execution/executions/components/workflow/WorkflowExecutionsInfoAccordion.vue":{"58":[1,56,131,137,68,69,6,157,158,159],"78":[1,56,131,137,68,69,6,157,158,159],"88":[1,56,131,137,68,69,6,157,158,159],"96":[1,56,131,137,68,69,6,157,158,159],"109":[1,56,131,137,68,69,6,157,158,159],"110":[1,56,131,137,68,69,6,157,158,159],"123":[1,56,131,137,68,69,6,157,158,159],"130":[1,56,131,137,68,69,6,157,158,159]},"packages/frontend/editor-ui/src/features/execution/executions/components/workflow/WorkflowExecutionsList.vue":{"12":[1,56,131,137,68,69,6,157,158,159],"42":[1,56,131,137,68,69,6,157,158,159],"43":[56,131,137,68,69,6,157,158,159],"48":[1,56,131,137,68,69,6,157,158,159],"52":[68],"73":[1,131,137,158,159],"81":[1,56,131,137,68,69,6,157,158,159],"92":[6],"94":[157]},"packages/frontend/editor-ui/src/features/execution/executions/components/workflow/WorkflowExecutionsPreview.vue":{"54":[56,131,137,68,69,6,157,158,159],"58":[56,131,137,68,69,6,157,158,159],"59":[56,131,137,68,69,6,157,158,159],"60":[56,131,137,68,69,6,157,158,159],"63":[56,131,137,68,69,6,157,158,159],"74":[56,131,137,68,69,6,157,158,159],"83":[56,131,137,68,69,6,157,158,159],"107":[56,131,137,68,69,6,157,158,159],"111":[68],"122":[56,131,137,68,69,6,157,158,159],"128":[56,131,137,68,69,6,157,158,159],"138":[56,131,137,68,69,6,157,158,159],"150":[56,131,137,68,69,6,157,158,159],"172":[56,131,137,68,69,6,157,158,159],"178":[56,131,137,68,69,6,157,158,159],"180":[56,131,137,68,69,6,157,158,159],"207":[68],"277":[159],"285":[159],"298":[56,131,137,68,69,6,157,158,159],"318":[56,131,137,68,69,6,157,158,159],"349":[56,131,137,68,69,6,157,158,159],"361":[56,131,137,68,69,6,157,158,159],"395":[56,131,137,68,69,6,157,158,159],"407":[56,131,137,68,69,6,157,158,159],"410":[137],"435":[137,157],"438":[137,157],"441":[137,157]},"packages/frontend/editor-ui/src/features/execution/executions/components/workflow/WorkflowExecutionsSidebar.vue":{"26":[1,56,131,137,68,69,6,157,158,159],"63":[69,158],"70":[1,56,131,137,68,69,6,157,158,159],"79":[56,131,137,68,69,6,157,158,159],"83":[1,56,131,137,68,69,6,157,158,159],"87":[1,56,131,137,68,69,6,157,158,159],"97":[1,56,131,137,68,69,6,157,158,159],"98":[56,131,137,68,69,6,157,158,159],"107":[56,131,137,68,69,6,157,158,159],"116":[56,131,137,68,69,6,157,158,159],"125":[56,131,137,68,69,6,157,158,159],"136":[157],"143":[6],"147":[131,137,157],"169":[1,56,131,137,68,69,6,157,158,159],"176":[1,56,131,137,68,69,6,157,158,159],"218":[1,56,131,137,68,69,6,157,158,159],"223":[68,157],"231":[1,56,131,137,68,69,6,157,158,159],"234":[56,131,137,68,69,6,157,158,159]},"packages/frontend/editor-ui/src/features/execution/executions/composables/useExecutionHelpers.ts":{"24":[56,131,137,68,69,6,157,158,159],"69":[56,131,137,68,69,6,157,158,159],"74":[56,131,137,68,69,6,157,158,159],"87":[151,152]},"packages/frontend/editor-ui/src/features/execution/executions/composables/useExecutionPreviewDocument.ts":{"51":[56,131,137,68,69,6,157,158,159],"95":[56,131,137,68,69,6,157,158,159],"107":[56,131,137,68,69,6,157,158,159],"136":[56,131,137,68,69,6,157,158,159],"163":[56,131,137,68,69,6,157,158,159],"197":[158],"198":[158],"286":[56,131,137,68,69,6,157,158,159],"302":[131,137,68,158,159]},"packages/frontend/editor-ui/src/features/execution/executions/views/WorkflowExecutionsView.vue":{"37":[1,56,131,137,68,69,6,157,158,159],"42":[1,56,131,137,68,69,6,157,158,159],"51":[1,56,131,137,68,69,6,157,158,159],"52":[56,131,137,68,69,6,157,158,159],"70":[1,56,131,137,68,69,6,157,158,159],"74":[1,56,131,137,68,69,6,157,158,159],"81":[1,56,131,137,68,69,6,157,158,159],"83":[131,137,68,69,6,157,158,159],"88":[1,56,131,137,68,69,6,157,158,159],"99":[1,131,137,158,159],"104":[1,56,131,137,68,69,6,157,158,159],"130":[56],"138":[1,56,131,137,68,69,6,157,158,159],"150":[1,56,131,137,68,69,6,157,158,159],"162":[6],"197":[157],"224":[68],"230":[68],"301":[69,158],"307":[1,56,131,137,68,69,6,157,158,159],"313":[69,158]},"packages/frontend/editor-ui/src/features/ndv/panel/components/RedactedDataState.vue":{"24":[56],"26":[56],"33":[56],"49":[56]},"packages/frontend/editor-ui/src/features/ndv/parameters/components/AssignmentCollection/Assignment.vue":{"35":"b:QMAAAIAADAMQAABAAIAgABCoDAAAAAAA","39":"b:QMAAAIAADAMQAABAAIAgABCoDAAAAAAA","57":"b:QMAAAIAADAMQAABAAIAgABCoDAAAAAAA","81":"b:QMAAAIAADAMQAABAAIAgABCoDAAAAAAA","90":"b:QMAAAIAADAMQAABAAIAgABCoDAAAAAAA","104":"b:QMAAAIAADAMQAABAAIAgABCoDAAAAAAA","106":"b:QMAAAIAADAMQAABAAIAgABCoDAAAAAAA","115":"b:QMAAAIAADAMQAABAAIAgABCoDAAAAAAA","117":[15,50,111,117,68,6,141,143,14,147],"119":[94,51,141],"123":[94,51],"131":[94,51,141,143,14,147],"139":[94,51,132,141,143,14],"143":[141],"156":"b:QMAAAIAADAMQAABAAIAgABCoDAAAAAAA","213":"b:QMAAAIAADAMQAABAAIAgABCoDAAAAAAA","229":"b:QMAAAIAADAMQAABAAIAgABCoDAAAAAAA","233":"b:QMAAAIAADAMQAABAAIAgABCoDAAAAAAA","243":"b:QMAAAIAADAMQAABAAIAgABCoDAAAAAAA"},"packages/frontend/editor-ui/src/features/ndv/parameters/components/AssignmentCollection/AssignmentCollection.vue":{"39":"b:QsAAAIAAjAeSAQBAAIAkAFSsTgAAAAAA","53":"b:QsAAAIAAjAeSAQBAAIAkAFSsTgAAAAAA","56":"b:QMAAAIAABAMQAAAAAIAgABAoBAAAAAAA","72":"b:QMAAAIAADAMQAABAAIAgABCoDAAAAAAA","77":"b:QsAAAIAAjAeSAQBAAIAkAFSsTgAAAAAA","78":"b:QsAAAIAAjAeSAQBAAIAkAFSsTgAAAAAA","90":[141],"91":"b:QsAAAIAAjAeSAQBAAIAkAFSsTgAAAAAA","92":"b:QsAAAIAAjAeSAQBAAIAkAFSsTgAAAAAA","107":"b:QsAAAIAAjAeSAQBAAIAkAFSsTgAAAAAA","116":"b:QsAAAIAAjAeSAQBAAIAkAFSsTgAAAAAA","117":[94,51,139,141,143,14,147],"119":[94,51,139,141,143,14,147],"129":[94,51,139,143,147],"150":[94,51,132,141,143,14],"158":"b:QMAAAIAADAMQAABAAIAgABCoDAAAAAAA","168":"b:QsAAAIAAjAeSAQBAAIAkAFSsTgAAAAAA","185":"b:QsAAAIAAjAeSAQBAAIAkAFSsTgAAAAAA","220":"b:QMAAAIAADAMQAABAAIAgABCoDAAAAAAA","229":[94,51,132,141,143,14],"257":"b:QsAAAIAAjAaSAQBAAIAkAFSsTgAAAAAA"},"packages/frontend/editor-ui/src/features/ndv/parameters/components/AssignmentCollection/TypeSelect.vue":{"17":"b:QMAAAIAADAMQAABAAIAgABCoDAAAAAAA","30":"b:QMAAAIAADAMQAABAAIAgABCoDAAAAAAA","32":"b:QMAAAIAADAMQAABAAIAgABCoDAAAAAAA","33":"b:QMAAAIAADAMQAABAAIAgABCoDAAAAAAA","43":[94,51],"45":"b:QMAAAIAADAMQAABAAIAgABCoDAAAAAAA","58":"b:QMAAAIAADAMQAABAAIAgABCoDAAAAAAA","60":"b:QMAAAIAADAMQAABAAIAgABCoDAAAAAAA"},"packages/frontend/editor-ui/src/features/ndv/parameters/components/InputTriple/InputTriple.vue":{"10":"b:QMAAAIAADQMQAABAAIAgABSoDAAAAAAA","13":"b:QMAAAIAADQMQAABAAIAgABSoDAAAAAAA","21":"b:QMAAAIAADQMQAABAAIAgABSoDAAAAAAA","22":"b:QMAAAIAADQMQAABAAIAgABSoDAAAAAAA"},"packages/frontend/editor-ui/src/features/ndv/runData/components/RunDataPinButton.vue":{"6":"b:QABAQIAADQFYAAAKgEUgAID6r4EAAAAA","24":"b:QABAQIAADQFYAAAKgEUgAID6r4EAAAAA","27":"b:QABAQIAADQFYAAAKgEUgAID6r4EAAAAA","31":[103,51],"49":"b:QABAQIAADQFYAAAKgEUgAID6r4EAAAAA","66":[137,147]},"packages/frontend/editor-ui/src/features/ndv/runData/components/VirtualSchemaHeader.vue":{"22":"b:QNAAQIAABQVYAIACAGEoAJT+74EAAAAA","24":"b:QNAAQIAABQVYAIACAGEoAJT+74EAAAAA","70":[138],"72":[138],"77":[139,150],"79":[139,150],"82":[139,150],"84":[139,150]},"packages/frontend/editor-ui/src/features/settings/security/DataRedactionSection.vue":{"40":[56],"59":[56],"64":[56],"65":[56],"74":[56],"78":[56],"79":[56],"87":[56],"96":[56],"123":[56],"141":[56],"154":[56],"172":[56],"178":[56],"184":[56],"228":[56],"274":[56],"277":[56]},"packages/frontend/editor-ui/src/features/settings/security/SecuritySettings.vue":{"42":[56],"46":[56],"72":[56],"86":[56],"93":[56],"127":[56],"149":[56],"170":[56],"186":[56],"189":[56],"202":[56],"213":[56],"231":[56],"265":[56],"273":[56],"279":[56],"318":[56],"321":[56],"330":[56],"336":[56],"375":[56],"378":[56]},"packages/frontend/editor-ui/src/features/workflows/canvas/components/elements/background/CanvasBackgroundStripedPattern.vue":{"6":"b:QAAUAPABAAEwAQAMAAAAAAoDAOgEAAAA","13":"b:QAAUAPABAAEwAQAMAAAAAAoDAOgEAAAA","14":"b:QAAUAPABAAEwAQAMAAAAAAoDAOgEAAAA","15":"b:QAAUAPABAAEwAQAMAAAAAAoDAOgEAAAA"},"packages/@n8n/db/src/repositories/workflow-tag-mapping.repository.ts":{"8":"b:AiAGiAAAiAAMAYAAAAAAIARAKAECAAAA","12":"b:AiAGiAAAiAAMAYAAAAAAIARAKAECAAAA","13":"b:AiAGiAAAiAAMAYAAAAAAIARAKAECAAAA","16":"b:AiAGiAAAiAAMAYAAAAAAIARAKAECAAAA"},"packages/cli/src/modules/external-secrets.ee/external-secrets-manager.ee.ts":{"35":"b:AAEAAEAACBCGAAAAAAgAIERAAgABAAgA","57":"b:AAEAAEAACBCGAAAAAAgAIERAAgABAAgA","60":"b:AAEAAEAACBCGAAAAAAgAIERAAgABAAgA","80":"b:AAEAAEAACBCGAAAAAAgAIERAAgABAAgA","92":"b:AAEAAEAACBCGAAAAAAgAIERAAgABAAgA","96":"b:AAEAAEAACBCGAAAAAAgAIERAAgABAAgA","104":"b:AAEAAEAACBCGAAAAAAgAIERAAgABAAgA","108":"b:AAEAAEAACBCGAAAAAAgAIERAAgABAAgA","112":"b:AAEAAEAACBCGAAAAAAgAIERAAgABAAgA","119":"b:AAEAAEAACBCGAAAAAAgAIERAAgABAAgA","125":"b:AAEAAEAACBCGAAAAAAgAIERAAgABAAgA","138":"b:AAEAAEAACBCGAAAAAAgAIERAAgABAAgA","170":"b:AAEAAEAACBCGAAAAAAgAIERAAgABAAgA","194":"b:AAEAAEAACBCGAAAAAAgAIERAAgABAAgA","198":"b:AAEAAEAACBCGAAAAAAgAIERAAgABAAgA","202":"b:AAEAAEAACBCGAAAAAAgAIERAAgABAAgA","206":"b:AAEAAEAACBCGAAAAAAgAIERAAgABAAgA","214":"b:AAEAAEAACBCGAAAAAAgAIERAAgABAAgA","225":"b:AAEAAEAACBCGAAAAAAgAIERAAgABAAgA","237":"b:AAEAAEAACBCGAAAAAAgAIERAAgABAAgA","288":"b:AAEAAEAACBCGAAAAAAgAIERAAgABAAgA","306":"b:AAEAAEAACBCGAAAAAAgAIERAAgABAAgA","340":"b:AAEAAEAACBCGAAAAAAgAIERAAgABAAgA","350":"b:AAEAAEAACBCGAAAAAAgAIERAAgABAAgA","368":"b:AAEAAEAACBCGAAAAAAgAIERAAgABAAgA","376":"b:AAEAAEAACBCGAAAAAAgAIERAAgABAAgA","378":"b:AAEAAEAACBCGAAAAAAgAIERAAgABAAgA","384":"b:AAEAAEAACBCGAAAAAAgAIERAAgABAAgA","395":"b:AAEAAEAACBCGAAAAAAgAIERAAgABAAgA","399":"b:AAEAAEAACBCGAAAAAAgAIERAAgABAAgA","418":"b:AAEAAEAACBCGAAAAAAgAIERAAgABAAgA"},"packages/cli/src/modules/instance-registry/checks/check.service.ts":{"46":"b:ACEAAEgQoABGAEACACAAAIAQggAAAAAA","57":"b:ACEAAEgQoABGAEACACAAAIAQggAAAAAA","63":"b:ACEAAEgQoABGAEACACAAAIAQggAAAAAA","75":"b:ACEAAEgQoABGAEACACAAAIAQggAAAAAA","83":"b:ACEAAEgQoABGAEACACAAAIAQggAAAAAA","88":"b:ACEAAEgQoABGAEACACAAAIAQggAAAAAA","90":"b:ACEAAEgQoABGAEACACAAAIAQggAAAAAA","96":"b:ACEAAEgQoABGAEACACAAAIAQggAAAAAA","111":"b:ACEAAEgQoABGAEACACAAAIAQggAAAAAA","115":"b:ACEAAEgQoABGAEACACAAAIAQggAAAAAA","133":"b:ACEAAEgQoABGAEACACAAAIAQggAAAAAA","148":"b:ACEAAEgQoABGAEACACAAAIAQggAAAAAA","157":"b:ACEAAEgQoABGAEACACAAAIAQggAAAAAA","193":"b:ACEAAEgQoABGAEACACAAAIAQggAAAAAA","214":"b:ACEAAEgQoABGAEACACAAAIAQggAAAAAA","228":"b:ACEAAEgQoABGAEACACAAAIAQggAAAAAA","239":"b:ACEAAEgQoABGAEACACAAAIAQggAAAAAA","245":"b:ACEAAEgQoABGAEACACAAAIAQggAAAAAA","254":"b:ACEAAEgQoABGAEACACAAAIAQggAAAAAA","277":"b:ACEAAEgQoABGAEACACAAAIAQggAAAAAA","301":"b:ACEAAEgQoABGAEACACAAAIAQggAAAAAA"},"packages/cli/src/modules/instance-registry/stale-member-cleanup.service.ts":{"17":"b:ACEAAEgQoABGAEACACAAAIAQggAAAAAA","25":"b:ACEAAEgQoABGAEACACAAAIAQggAAAAAA","30":"b:ACEAAEgQoABGAEACACAAAIAQggAAAAAA","34":"b:ACEAAEgQoABGAEACACAAAIAQggAAAAAA","42":"b:ACEAAEgQoABGAEACACAAAIAQggAAAAAA","47":"b:ACEAAEgQoABGAEACACAAAIAQggAAAAAA","59":"b:ACEAAEgQoABGAEACACAAAIAQggAAAAAA"},"packages/nodes-base/nodes/If/If.node.ts":{"8":[48,66,136,68,6,144,70,162]},"packages/nodes-base/nodes/If/V1/IfV1.node.ts":{"16":[48,66,136,68,6,144,146,70,162],"310":[48,66,136,68,6,144,146,70,162],"319":[48,66,136,68,6,144,146,70,162],"321":[48,66,136,68,6,144,146,70,162],"327":[48,66,136,68,6,144,146,70,162],"329":[48,66,136,68,6,144,146,70,162],"331":[48,66,136,68,6,144,146,70,162],"333":[48,66,136,68,6,144,146,70,162],"335":[48,66,136,68,6,144,146,70,162],"337":[48,66,136,68,6,144,146,70,162],"339":[48,66,136,68,6,144,146,70,162],"340":[48,66,136,68,6,144,146,70,162],"341":[48,66,136,68,6,144,146,70,162],"343":[48,66,136,68,6,144,146,70,162],"345":[48,66,136,68,6,144,146,70,162],"347":[48,66,136,68,6,144,146,70,162],"349":[48,66,136,68,6,144,146,70,162],"351":[48,66,136,68,6,144,146,70,162],"353":[48,66,136,68,6,144,146,70,162],"359":[48,66,136,68,6,144,146,70,162],"367":[48,66,136,68,6,144,146,70,162],"381":[48,66,136,68,6,144,146,70,162],"398":[48,66,136,68,6,144,146,70,162]},"packages/nodes-base/nodes/If/V2/IfV2.node.ts":{"20":[48,66,136,68,6,144,145,70,162],"95":[48,66,136,68,6,144,145,70,162],"99":[48,66,136,68,6,144,145,70,162]},"packages/nodes-base/nodes/If/V2/utils.ts":{"3":[48,109,66,136,68,6,141,144,70,162],"7":[48,109,66,136,68,6,141,144,70,162],"8":[48,109,66,136,68,6,141,144,70,162]},"packages/nodes-base/nodes/StickyNote/StickyNote.node.ts":{"57":[109,66,68,151,162]},"packages/workflow/src/graph/graph-utils.ts":{"41":[66],"62":[66],"80":[66],"88":[66],"95":[66],"103":[66],"114":[66],"115":[66],"123":[66],"133":[66],"134":[66],"145":[66],"157":[66],"170":[66],"174":[66],"209":[66],"218":[66],"239":[66]},"packages/frontend/@n8n/design-system/src/components/N8nIcon/nodes/if.svg":{"11":"b:QAAEAAAAAQBUAAAAAAAAAAABBwAFAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nKeyboardShortcut/N8nKeyboardShortcut.vue":{"7":"b:AgAAAAAAAAIEAAAAAAAQAGwICAEAAAAA","10":"b:AgAAAAAAAAIEAAAAAAAQAGwICAEAAAAA","25":"b:AgAAAAAAAAIEAAAAAAAQAGwICAEAAAAA","29":"b:AgAAAAAAAAIEAAAAAAAQAGwICAEAAAAA","33":"b:AgAAAAAAAAIEAAAAAAAQAGwICAEAAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nMarkdown/Markdown.vue":{"37":[109,115,66,133,68,151,160,73,162],"82":[109,115,66,133,68,151,160,73,162],"89":[162],"110":[109,115,66,133,68,151,160,162],"114":[68],"123":[109,115,66,133,68,151,160,162],"152":[133,160,162],"185":[109,115,66,133,68,151,160,73,162],"242":[133],"260":[109,115,66,133,68,151,160,73,162],"278":[73,162]},"packages/frontend/@n8n/design-system/src/components/N8nMarkdown/youtube.ts":{"34":[109,115,66,133,68,151,160,73,162],"42":[109,133,68,151,160,162]},"packages/frontend/@n8n/design-system/src/components/N8nSticky/Sticky.vue":{"16":[109,115,66,133,68,151,160,162],"28":[109,115,66,133,68,151,160,162],"32":[109,115,66,133,68,151,160,162],"36":[109,115,66,133,68,151,160,162],"38":[109,115,66,133,68,151,160,162],"43":[109,115,66,133,68,151,160,162],"58":[109,115,66,133,68,151,160,162],"65":[109,115,66,133,68,151,160,162],"99":[109,115,66,133,68,151,160,162]},"packages/frontend/editor-ui/src/features/workflows/canvas/components/elements/edges/CanvasEdgeToolbar.vue":{"12":"b:QAAAABABgAQGAAAAAAAYAEgAAAEAAAAA","27":"b:QAAAABABgAQGAAAAAAAYAEgAAAEAAAAA","31":"b:QAAAABABgAQGAAAAAAAYAEgAAAEAAAAA","41":[58,115,65],"45":[65,66,134],"47":"b:QAAAABABgAQGAAAAAAAYAEgAAAEAAAAA","55":"b:QAAAABABgAQGAAAAAAAYAEgAAAEAAAAA","72":"b:QAAAABABgAQGAAAAAAAYAEgAAAEAAAAA"},"packages/frontend/editor-ui/src/features/workflows/canvas/components/elements/edges/CanvasEdgeTooltip.vue":{"9":"b:QAAAABABgAQGAAAAAAAYAEgAAAEAAAAA","20":"b:QAAAABABgAQGAAAAAAAYAEgAAAEAAAAA"},"packages/frontend/editor-ui/src/features/workflows/canvas/components/elements/groups/CanvasNodeGroupTitleBar.vue":{"34":[66],"76":[66],"77":[66],"80":[66],"81":[66],"82":[66],"83":[66],"87":[66],"89":[66],"103":[66],"114":[66],"121":[66],"135":[66],"137":[66],"144":[66],"152":[66],"162":[66],"169":[66],"188":[66],"195":[66],"210":[66],"214":[66],"215":[66],"221":[66],"244":[66],"245":[66],"248":[66],"252":[66],"255":[66],"262":[66],"269":[66],"274":[66],"296":[66],"300":[66],"376":[66],"393":[66],"438":[66],"453":[66],"476":[66]},"packages/frontend/editor-ui/src/features/workflows/canvas/components/elements/nodes/render-types/CanvasNodeStickyNote.vue":{"15":[109,115,66,133,68,151,160,162],"27":[109,115,66,133,68,151,160,162],"29":[109,115,66,133,68,151,160,162],"83":[133],"91":[109,115,66,133,68,151,160,162],"95":[66],"98":[109,115,66,133,68,151,160,162]},"packages/frontend/editor-ui/src/features/workflows/canvas/components/elements/nodes/toolbar/CanvasNodeStickyColorSelector.vue":{"10":[109,115,66,133,68,151,160,162],"17":[133],"21":[133],"31":[133],"53":[133],"66":[109,115,66,133,68,151,160,162],"70":[66],"73":[109,115,66,133,68,151,160,162],"86":[109,115,66,133,68,151,160,162],"95":[133],"98":[133]},"packages/frontend/editor-ui/src/features/workflows/canvas/nodeGroups.utils.ts":{"7":[66],"16":[66]},"packages/core/src/execution-engine/partial-execution-utils/clean-run-data.ts":{"12":[15,110,67,68,6,139,146,70,149]},"packages/core/src/execution-engine/partial-execution-utils/directed-graph.ts":{"40":[15,110,67,68,6,139,146,70,149],"45":[15,110,67,68,6,139,146,70,149],"49":[15,110,67,68,6,139,146,70,149],"58":[15,110,67,68,6,139,146,70,149],"72":[15,110,67,68,6,139,146,70,149],"86":[15,110,67,68,6,139,146,70,149],"91":[15,110,67,68,6,139,146,70,149],"116":[15,110,67,68,6,139,146,70,149],"172":[15,110,67,68,6,139,146,70,149],"201":[15,110,67,68,6,139,146,70,149],"216":[15,110,67,68,6,139,146,70,149],"233":[15,110,67,68,6,139,146,70,149],"255":[15,110,67,68,6,139,146,70,149],"259":[15,110,67,68,6,139,146,70,149],"276":[15,110,67,68,6,139,146,70,149],"293":[15,110,67,68,6,139,146,70,149],"297":[15,110,67,68,6,139,146,70,149],"347":[15,110,67,68,6,139,146,70,149],"355":[15,110,67,68,6,139,146,70,149],"366":[15,110,67,68,6,139,146,70,149],"414":[15,110,67,68,6,139,146,70,149],"454":[15,110,67,68,6,139,146,70,149],"458":[15,110,67,68,6,139,146,70,149],"466":[15,110,67,68,6,139,146,70,149],"499":[15,110,67,68,6,139,146,70,149],"537":[15,110,67,68,6,139,146,70,149],"543":[15,110,67,68,6,139,146,70,149],"566":[15,110,67,68,6,139,146,70,149]},"packages/core/src/execution-engine/partial-execution-utils/filter-disabled-nodes.ts":{"5":[15,110,67,68,6,139,146,70,149],"12":[15,110,67,68,6,139,146,70,149]},"packages/core/src/execution-engine/partial-execution-utils/find-start-nodes.ts":{"13":[15,110,67,68,6,139,146,70,149],"29":[15,110,67,68,6,139,146,70,149],"50":[15,110,67,68,6,139,146,70,149],"137":[15,110,67,68,6,139,146,70,149],"159":[15,110,67,68,6,139,146,70,149]},"packages/core/src/execution-engine/partial-execution-utils/find-subgraph.ts":{"6":[15,110,67,68,6,139,146,70,149],"39":[15,110,67,68,6,139,146,70,149],"45":[15,110,67,68,6,139,146,70,149],"88":[15,110,67,68,6,139,146,70,149]},"packages/core/src/execution-engine/partial-execution-utils/find-trigger-for-partial-execution.ts":{"12":[15,110,67,68,6,139,146,70,149],"14":[15,110,67,68,6,139,146,70,149],"17":[15,110,67,68,6,139,146,70,149],"29":[15,110,67,68,6,139,146,70,149],"30":[15,110,67,68,6,139,146,70,149],"31":[15,110,67,68,6,139,146,70,149],"36":[15,110,67,68,6,139,146,70,149],"61":[15,110,67,68,6,139,146,70,149],"78":[15,110,67,68,6,139,146,70,149],"98":[15,110,67,68,6,139,146,70,149],"112":[15,110,67,68,6,139,146,70,149],"115":[15,110,67,68,6,139,146,70,149],"121":[15,110,67,68,6,139,146,70,149]},"packages/core/src/execution-engine/partial-execution-utils/get-incoming-data.ts":{"3":[15,110,67,68,6,139,146,70,149],"13":[15,110,67,68,6,139,146,70,149],"17":[15,110,67,68,6,139,146,70,149]},"packages/core/src/execution-engine/partial-execution-utils/get-source-data-groups.ts":{"5":[15,110,67,68,6,139,146,70,149],"28":[15,110,67,68,6,139,146,70,149],"86":[15,110,67,68,6,139,146,70,149],"118":[15,110,67,68,6,139,146,70,149],"119":[15,110,67,68,6,139,146,70,149],"126":[15,110,67,68,6,139,146,70,149],"139":[15,110,67,68,6,139,146,70,149],"156":[15,110,67,68,6,139,146,70,149],"157":[15,110,67,68,6,139,146,70,149]},"packages/core/src/execution-engine/partial-execution-utils/handle-cycles.ts":{"15":[15,110,67,68,6,139,146,70,149],"26":[15,110,67,68,6,139,146,70,149],"41":[15,110,67,68,6,139,146,70,149]},"packages/core/src/execution-engine/partial-execution-utils/recreate-node-execution-stack.ts":{"20":[15,110,67,68,6,139,146,70,149],"39":[15,110,67,68,6,139,146,70,149],"72":[15,110,67,68,6,139,146,70,149],"103":[15,110,67,68,6,139,146,70,149]},"packages/core/src/execution-engine/partial-execution-utils/run-data-utils.ts":{"11":[15,110,67,68,6,139,146,70,149],"17":[15,110,67,68,6,139,146,70,149],"20":[15,110,67,68,6,139,146,70,149]},"localhost-42325/assets/_plugin-vue_export-helper-Da88TEg1.js":{"2":[80,39]},"localhost-42325/assets/chunk-CC9Q-vWm.js":{"8":[80,39],"9":[80,39],"10":[80,39],"19":[80,39],"23":[80,39],"29":[80,39],"33":[80,39],"34":[80,39]},"packages/frontend/@n8n/design-system/src/components/AskAssistantChat/messages/MessageRating.vue":{"15":[18,36,78,79,80,37,100,81,39,40],"62":[18,36,78,79,80,37,100,81,39,40]},"packages/frontend/@n8n/design-system/src/components/N8nAiActivityStepChevron/AiActivityStepChevron.vue":{"10":[36,78,79,80,37,38,81,39,40]},"packages/frontend/@n8n/design-system/src/components/N8nAnimatedCollapsibleContent/AnimatedCollapsibleContent.vue":{"12":[36,78,79,80,37,38,81,39,40]},"packages/frontend/@n8n/design-system/src/components/N8nChatInput/useChatInputAutoFocus.ts":{"36":[36,78,80,37,100,38,81,39,40,82]},"packages/frontend/@n8n/design-system/src/components/N8nIcon/custom/filled-square.svg":{"10":"b:OIAXAPEBAAAAQIcAFQAAAAAAAAAAAAAA"},"packages/frontend/editor-ui/src/app/layouts/InstanceAiLayout.vue":{"9":[36,78,79,80,37,100,38,81,39,40,82],"20":[36,78,79,80,37,100,38,81,39,40,82],"22":[36,78,79,80,37,100,38,81,39,40,82]},"packages/frontend/editor-ui/src/app/utils/rbac/middleware/custom.ts":{"4":"b:IAEBO/8B8BCB2XcgGBjANAAAAAgGAAAA"},"packages/frontend/editor-ui/src/experiments/instanceAiBrowserUse/useInstanceAiBrowserUseExperiment.ts":{"9":[36,78,79,80,37,100,38,81,39,40,82]},"packages/frontend/editor-ui/src/experiments/instanceAiComputerUse/useInstanceAiComputerUseExperiment.ts":{"6":[36,78,79,80,37,100,38,81,39,40,82]},"packages/frontend/editor-ui/src/experiments/instanceAiMcpConnections/useInstanceAiMcpConnectionsExperiment.ts":{"6":[36,78,79,80,37,100,38,81,39,40,82],"9":[36,78,79,80,37,100,38,81,39,40,82]},"packages/frontend/editor-ui/src/experiments/instanceAiPersonalizedPromptSuggestions/profileOverride.ts":{"60":[36,78,80,37,100,38,81,39,40,82],"73":[36,78,80,37,100,38,81,39,40,82],"106":[36,78,80,37,100,38,81,39,40,82],"114":[36,78,80,37,100,38,81,39,40,82],"130":[36,78,80,37,100,38,81,39,40,82]},"packages/frontend/editor-ui/src/experiments/instanceAiPersonalizedPromptSuggestions/useInstanceAiPersonalizedPromptSuggestionsExperiment.ts":{"8":[36,78,80,37,100,38,81,39,40,82],"11":[36,78,80,37,100,38,81,39,40,82],"14":[36,78,80,37,100,38,81,39,40,82],"29":[36,78,80,37,100,38,81,39,40,82]},"packages/frontend/editor-ui/src/experiments/instanceAiProactiveAgent/useInstanceAiProactiveAgentExperiment.ts":{"6":[36,78,80,37,100,38,81,39,40,82],"9":[36,78,80,37,100,38,81,39,40,82]},"packages/frontend/editor-ui/src/experiments/instanceAiPromptSuggestionsV2/useInstanceAiPromptSuggestionsV2Experiment.ts":{"6":[36,78,80,37,100,38,81,39,40,82],"9":[36,78,80,37,100,38,81,39,40,82]},"packages/frontend/editor-ui/src/experiments/instanceAiSplitEmptyState/useInstanceAiSplitEmptyStateExperiment.ts":{"5":[36,78,80,37,100,38,81,39,40,82],"7":[36,78,80,37,100,38,81,39,40,82],"10":[36,78,80,37,100,38,81,39,40,82]},"packages/frontend/editor-ui/src/experiments/instanceAiTemplateExamples/instanceAiTemplateExamples.store.ts":{"14":[36,78,80,37,100,38,81,39,40,82]},"packages/frontend/editor-ui/src/experiments/instanceAiTemplateExamples/useInstanceAiTemplateExamplesExperiment.ts":{"6":[36,78,80,37,100,38,81,39,40,82],"9":[36,78,80,37,100,38,81,39,40,82],"13":[36,78,80,37,100,38,81,39,40,82]},"packages/frontend/editor-ui/src/experiments/instanceAiWorkflowPreviewSuggestions/components/WorkflowPreviewSuggestions.vue":{"10":[36,78,80,37,100,38,81,39,40,82],"36":[36,78,80,37,100,38,81,39,40],"53":[36,78,80,37,100,38,81,39,40],"102":[36,78,80,37,100,38,81,39,40,82],"108":[36,78,80,37,100,38,81,39,40,82]},"packages/frontend/editor-ui/src/experiments/instanceAiWorkflowPreviewSuggestions/useInstanceAiWorkflowPreviewSuggestionsExperiment.ts":{"4":[36,78,80,37,100,38,81,39,40,82],"5":[36,78,80,37,100,38,81,39,40,82]},"packages/frontend/editor-ui/src/features/ai/chatHub/composables/useChatHubMarkdownOptions.ts":{"25":[36,37,40,52],"40":"b:OAAAAPEBEAAAwAMABQAAAAAAAAAAAAAA","56":"b:OAAAAPEBEAAAwAMABQAAAAAAAAAAAAAA","75":"b:OAAAAPEBEAAAwAMABQAAAAAAAAAAAAAA","85":[3,52],"104":[52],"116":[52],"117":[52],"138":"b:OAAAAPEBEAAAwAMABQAAAAAAAAAAAAAA","139":"b:OAAAAPEBEAAAwAMABQAAAAAAAAAAAAAA","141":[36,37,40,52],"144":[36,37,40,52],"156":"b:OAAAAPEBEAAAwAMABQAAAAAAAAAAAAAA","161":[52],"179":"b:OAAAAPEBEAAAwAMABQAAAAAAAAAAAAAA","183":[52],"190":[52],"199":"b:OAAAAPEBEAAAwAMABQAAAAAAAAAAAAAA","206":"b:OAAAAPEBEAAAwAMABQAAAAAAAAAAAAAA","215":[52],"226":[52],"227":[52],"228":[52],"229":[52],"230":[52]},"packages/frontend/editor-ui/src/features/ai/instanceAi/InstanceAiEmptyView.vue":{"107":[36,78,80,37,100,38,81,39,40,82],"116":[36,78,80,37,100,38,81,39,40],"141":[36,78,80,37,100,38,81,39,40,82],"146":[36,78,80,37,100,38,81,39,40,82],"170":[36,78,80,37,100,38,81,39,40,82],"174":[36,78,80,37,100,38,81,39,40,82],"177":[36,78,80,37,100,38,81,39,40,82],"185":[36,78,80,37,100,38,81,39,40,82],"200":[36,78,80,37,100,38,81,39,40,82],"227":[36,78,80,37,100,38,81,39,40,82],"261":[36,78,80,37,100,38,81,39,40,82],"268":[36,78,80,37,100,38,81,39,40,82],"292":[36,78,80,37,100,38,81,39,40,82],"308":[36,78,80,37,100,38,81,39,40,82],"372":[36,78,80,37,100,38,81,39,40,82],"391":[36,78,80,37,100,38,81,39,40,82],"435":[36,78,80,37,100,38,81,39,40,82],"455":[36,78,80,37,100,38,81,39,40],"459":[36,78,80,37,100,38,81,39,40,82],"460":[36,78,80,37,100,38,81,39,40,82],"465":[36,78,80,37,100,38,81,39,40]},"packages/frontend/editor-ui/src/features/ai/instanceAi/InstanceAiThreadView.vue":{"100":[36,78,79,80,37,100,38,81,39,40,82],"108":[36,78,79,80,37,100,38,81,39,40,82],"109":[36,78,79,80,37,100,38,81,39,40,82],"113":[36,78,79,80,37,100,38,81,39,40,82],"120":[36,78,79,80,37,100,38,81,39,40,82],"125":[36,78,79,80,37,100,38,81,39,40,82],"137":[36,78,79,80,37,100,38,81,39,40,82],"152":[36,78,79,80,37,100,38,81,39,40,82],"153":[36,78,79,80,37,100,38,81,39,40,82],"157":[36,78,79,80,37,100,38,81,39,40,82],"178":[36,78,79,80,37,100,38,81,39,40,82],"189":[36,78,79,80,37,100,38,81,39,40,82],"190":[36,78,79,80,37,100,38,81,39,40,82],"196":[36,78,79,80,37,100,38,81,39,40,82],"199":[36,78,79,80,37,100,38,81,39,40,82],"204":[36,78,79,80,37,100,38,81,39,40,82],"211":[36,78,79,80,37,100,38,81,39,40,82],"218":[36,78,79,80,37,100,38,81,39,40,82],"222":[36,39,40],"228":[39],"259":[36,78,79,80,37,100,38,81,39,40,82],"264":[79,80,81,82],"274":[36,78,79,80,37,100,38,81,39,40,82],"277":[36,78,79,80,37,100,38,81,39,40,82],"280":[36,78,79,80,37,100,38,81,39,40,82],"283":[36,78,79,80,37,100,38,81,39,40,82],"287":[36,78,79,80,37,100,38,81,39,40,82],"290":[36,78,79,80,37,100,38,81,39,40,82],"298":[36,78,79,80,37,100,38,81,39,40,82],"301":[36,78,79,80,37,100,38,81,39,40,82],"304":[36,78,79,80,37,100,38,81,39,40,82],"307":[36,78,79,80,37,100,38,81,39,40,82],"314":[36,78,79,80,37,100,38,81,39,40,82],"315":[36,78,79,80,37,100,38,81,39,40,82],"325":[36,78,79,80,37,100,38,81,39,40,82],"335":[36,37,38,39,40],"343":[36],"349":[36,37,38,39,40],"365":[36,78,79,80,37,100,38,81,39,40,82],"379":[79,80,81,82],"387":[37,81,39],"395":[36,78,79,80,37,100,38,81,39,40,82],"404":[36,78,79,80,37,100,38,81,39,40,82],"405":[79,80,81,82],"420":[36,78,79,80,37,100,38,81,39,40,82],"431":[36,78,79,80,37,100,38,81,39,40,82],"432":[36,78,79,80,37,100,38,81,39,40,82],"438":[36,78,79,80,37,100,38,81,39,40,82],"453":[36,78,79,80,37,100,38,81,39,40,82],"457":[36,78,79,80,37,100,38,81,39,40,82],"471":[36,78,79,80,37,100,38,81,39,40,82],"485":[36,78,79,80,37,100,38,81,39,40,82],"492":[36,78,79,80,37,100,38,81,39,40,82],"502":[79,80,81,82],"506":[36,78,79,80,37,100,38,81,39,40,82],"542":[79,80,81,82],"551":[79,80,81,82],"573":[36,78,79,80,37,100,38,81,39,40,82],"580":[36,78,79,80,37,100,38,81,39,40,82],"589":[36,78,79,80,37,100,38,81,39,40,82],"597":[81],"728":[36,78,79,80,37,100,38,81,39,40,82],"734":[36,78,80,37,100,38,81,39,40,82],"749":[36,78,79,80,37,100,38,81,39,40,82],"766":[36,78,79,80,37,100,38,81,39,40,82],"786":[36,78,79,80,37,100,38,81,39,40,82],"819":[36,78,79,80,37,100,38,81,39,40,82],"822":[36,78,79,80,37,100,38,81,39,40,82],"833":[79],"845":[36,78,79,80,37,100,38,81,39,40,82],"870":[36,78,79,80,37,100,38,81,39,40,82],"900":[36,78,79,80,37,100,38,81,39,40,82],"937":[36,78,79,80,37,100,38,81,39,40,82],"967":[36,78,79,80,37,100,38,81,39,40,82],"986":[36,78,79,80,37,100,38,81,39,40,82]},"packages/frontend/editor-ui/src/features/ai/instanceAi/InstanceAiView.vue":{"37":[81],"65":[81],"81":[36,78,79,80,37,100,38,81,39,40,82],"102":[36,78,79,80,37,100,38,81,39,40,82],"104":[36,78,79,80,37,100,38,81,39,40,82],"117":[36,78,79,80,37,100,38,81,39,40,82],"151":[36,78,79,80,37,100,38,81,39,40,82],"161":[81]},"packages/frontend/editor-ui/src/features/ai/instanceAi/agentTimeline.utils.ts":{"56":[36,78,37,38,39,40],"76":[36,78,79,80,37,100,38,81,39,40],"89":[36,78,79,80,37,38,81,39,40],"100":[36,78,79,80,37,38,81,39,40],"119":[36,78,79,80,37,38,81,39,40],"141":[36,78,79,80,37,38,81,39,40],"146":[36,78,80,37,38,81,39,40],"154":[36,78,79,80,37,38,81,39,40],"233":[36,78,79,80,37,38,81,39,40],"250":[79]},"packages/frontend/editor-ui/src/features/ai/instanceAi/builderAgents.ts":{"13":[36,78,79,80,37,100,38,81,39,40,82],"30":[36,78,79,80,37,100,38,81,39,40,82],"41":[36,78,79,80,37,100,38,81,39,40,82],"104":[36,78,79,80,37,100,38,81,39,40,82],"106":[36,78,79,80,37,100,38,81,39,40,82]},"packages/frontend/editor-ui/src/features/ai/instanceAi/canvasPreview.utils.ts":{"59":[36,78,79,80,37,100,38,81,39,40,82],"82":[36,79,37,39,40],"94":[36,78,79,80,37,100,38,81,39,40,82],"123":[36,78,79,80,37,100,38,81,39,40,82],"149":[36,78,79,80,37,100,38,81,39,40,82],"187":[36,78,79,80,37,100,38,81,39,40,82],"237":[36,37,38,39,40],"359":[36,78,79,80,37,100,38,81,39,40,82],"383":[36,78,79,80,37,100,38,81,39,40,82],"508":[36,78,79,80,37,100,38,81,39,40,82],"534":[36,37,38,39,40],"542":[36,37,38,39,40]},"packages/frontend/editor-ui/src/features/ai/instanceAi/components/AgentActivityTree.vue":{"10":[36,78,79,80,37,100,38,81,39,40]},"packages/frontend/editor-ui/src/features/ai/instanceAi/components/AgentTimeline.vue":{"27":[36,78,79,80,37,100,38,81,39,40],"95":[36,78,79,80,37,100,38,81,39,40],"98":[36,78,79,80,37,100,38,81,39,40],"107":[36,78,79,80,37,100,38,81,39,40],"115":[36,78,79,80,37,100,38,81,39,40],"245":[36,78,79,80,37,100,38,81,39,40]},"packages/frontend/editor-ui/src/features/ai/instanceAi/components/ConnectionsCard.vue":{"32":[36,78,79,80,37,100,38,81,39,40,82],"45":[36,78,79,80,37,100,38,81,39,40,82],"56":[36,78,79,80,37,100,38,81,39,40,82],"159":[36,78,79,80,37,100,38,81,39,40,82]},"packages/frontend/editor-ui/src/features/ai/instanceAi/components/InstanceAiArtifactsPanel.vue":{"20":[36,78,79,80,37,100,38,81,39,40,82],"25":[36,78,79,80,37,100,38,81,39,40,82],"26":[36,78,79,80,37,100,38,81,39,40,82],"63":[36],"82":[36,78,79,80,37,100,38,81,39,40,82],"83":[36,78,79,80,37,100,38,81,39,40,82],"84":[36,78,79,80,37,100,38,81,39,40,82],"98":[36,78,79,80,37,100,38,81,39,40,82],"114":[36,37,39,40],"129":[36,37,39,40],"157":[36,78,79,80,37,100,38,81,39,40,82],"193":[36,78,79,80,37,100,38,81,39,40,82],"203":[36,78,79,80,37,100,38,81,39,40,82],"257":[36,78,79,80,37,100,38,81,39,40,82],"263":[36,37,39,40],"269":[36]},"packages/frontend/editor-ui/src/features/ai/instanceAi/components/InstanceAiConfirmationPanel.vue":{"36":[36,78,79,80,37,100,38,81,39,40,82],"44":[37,39],"52":[37,39],"104":[36,78,79,80,37,100,38,81,39,40,82],"121":[37,39],"134":[37,39],"156":[37,39],"167":[37,39],"195":[37,39],"217":[37],"251":[39],"398":[36,78,79,80,37,100,38,81,39,40,82],"581":[37,39],"588":[37,39]},"packages/frontend/editor-ui/src/features/ai/instanceAi/components/InstanceAiEmptyState.vue":{"5":[36,78,80,37,100,38,81,39,40,82],"22":[36,78,80,37,100,38,81,39,40,82]},"packages/frontend/editor-ui/src/features/ai/instanceAi/components/InstanceAiInput.vue":{"113":[36,78,79,80,37,100,38,81,39,40,82],"116":[36,78,79,80,37,100,38,81,39,40,82],"125":[36,78,79,80,37,100,38,81,39,40,82],"126":[36,78,79,80,37,100,38,81,39,40,82],"147":[36,78,80,37,100,38,81,39,40,82],"168":[36,78,80,37,100,38,81,39,40,82],"171":[36,78,79,80,37,100,38,81,39,40,82],"173":[36,78,79,80,37,100,38,81,39,40,82],"174":[36,78,79,80,37,100,38,81,39,40,82],"176":[36,78,80,37,100,38,81,39,40],"177":[36,78,79,80,37,100,38,81,39,40,82],"180":[36,78,79,80,37,100,38,81,39,40,82],"181":[36,78,79,80,37,100,38,81,39,40,82],"189":[36,78,80,37,100,38,81,39,40,82],"192":[36,78,79,80,37,100,38,81,39,40,82],"196":[36,78,79,80,37,100,38,81,39,40,82],"198":[36,78,79,80,37,100,38,81,39,40,82],"227":[36,78,79,80,37,100,38,81,39,40,82],"244":[36,78,80,37,100,38,81,39,40],"250":[36,78,79,80,37,100,38,81,39,40,82],"260":[36,78,80,37,100,38,81,39,40],"265":[36,78,80,37,100,38,81,39,40],"270":[36,78,80,37,100,38,81,39,40],"274":[36,78,80,37,100,38,81,39,40],"293":[36,78,80,37,100,38,81,39,40],"302":[78],"323":[78],"349":[36,78,80,37,100,38,81,39,40],"362":[36,78,80,37,100,38,81,39,40],"422":[36,78,79,80,37,100,38,81,39,40,82],"432":[36,78,79,80,37,100,38,81,39,40,82],"455":[36,78,79,80,37,100,38,81,39,40,82],"516":[78],"527":[36,78,79,80,37,100,38,81,39,40,82],"534":[36,78,80,37,100,38,81,39,40],"538":[36,78,80,37,100,38,81,39,40]},"packages/frontend/editor-ui/src/features/ai/instanceAi/components/InstanceAiMarkdown.vue":{"7":[36,78,79,80,37,38,81,39,40],"56":[36,37,40],"74":[36,78,79,80,37,38,81,39,40],"76":[36,37,40],"88":[36,37,40],"130":[36,37,39,40],"161":[36,78,79,80,37,81,39,40],"167":[36,37,39,40],"172":[36,37,39,40],"193":[36,37,40],"203":[36,78,79,80,37,38,81,39,40],"235":[36,37,40],"246":[36,37,40],"264":[36,37,40],"283":[36,78,79,80,37,38,81,39,40],"306":[36,37,40],"405":[36,78,80,37,38,81,39,40],"412":[36,78,79,80,37,38,81,39,40]},"packages/frontend/editor-ui/src/features/ai/instanceAi/components/InstanceAiMessage.vue":{"20":[36,78,79,80,37,100,38,81,39,40,82],"29":[36,78,79,80,37,100,38,81,39,40,82],"30":[36,78,79,80,37,100,38,81,39,40],"31":[36,78,79,80,37,100,38,81,39,40],"33":[36,78,79,80,37,38,81,39,40],"39":[36,78,79,80,37,100,38,81,39,40],"45":[39],"48":[36,78,79,80,37,100,38,81,39,40],"53":[36,78,79,80,37,100,38,81,39,40],"67":[39],"74":[39],"84":[36,78,80,37,100,38,81,39,40,82],"87":[36,78,79,80,37,100,38,81,39,40],"93":[36,78,79,80,37,100,38,81,39,40],"95":[36,78,79,80,37,100,38,81,39,40],"102":[36,78,79,80,37,100,38,81,39,40],"115":[36,78,79,80,37,100,38,81,39,40,82],"125":[78],"142":[100],"148":[100],"156":[39],"168":[39]},"packages/frontend/editor-ui/src/features/ai/instanceAi/components/InstanceAiPreviewTabBar.vue":{"19":[36,78,79,80,37,100,38,81,39,40,82],"41":[36,78,79,80,37,100,38,81,39,40,82],"47":[36,37,38,39,40],"53":[36,37,38,39,40],"57":[36,37,38,39,40],"84":[36,78,79,80,37,100,38,81,39,40,82],"85":[36,37,38,39,40],"88":[36,37,38,39,40],"114":[36,78,79,80,37,100,38,81,39,40,82],"134":[36,78,79,80,37,100,38,81,39,40,82],"136":[36,37,38,39,40],"138":[36,37,38,39,40],"140":[36,37,38,39,40],"141":[36,37,38,39,40]},"packages/frontend/editor-ui/src/features/ai/instanceAi/components/InstanceAiStatusBar.vue":{"18":[36,78,79,80,37,100,38,81,39,40,82],"25":[36,78,79,80,37,100,38,81,39,40,82],"31":[36,78,79,80,37,100,38,81,39,40,82],"41":[79],"53":[39],"62":[36,78,79,80,37,100,38,81,39,40,82],"72":[36,78,79,80,37,100,38,81,39,40,82],"76":[36,78,79,80,37,100,38,81,39,40,82],"80":[36,78,80,37,38,81,39,40],"91":[36,78,79,80,37,100,38,81,39,40,82],"98":[36,78,79,80,37,100,38,81,39,40,82],"107":[36,78,79,80,37,100,38,81,39,40,82],"122":[81],"127":[36,78,79,80,37,100,38,81,39,40,82]},"packages/frontend/editor-ui/src/features/ai/instanceAi/components/InstanceAiViewHeader.vue":{"12":[36,78,79,80,37,100,38,81,39,40,82],"19":[36,78,79,80,37,100,38,81,39,40,82],"31":[36,78,79,80,37,100,38,81,39,40,82],"36":[36,78,79,80,37,100,38,81,39,40,82]},"packages/frontend/editor-ui/src/features/ai/instanceAi/components/ThinkingBlock.vue":{"17":[36,78,80,37,38,81,39,40],"29":[36,78,37,38,39,40],"37":[36,78,80,37,38,81,39,40],"41":[36,78,80,37,38,81,39,40],"48":[36,78,80,37,38,81,39,40],"53":[36,78,80,37,38,81,39,40],"54":[36,78,80,37,38,81,39,40],"60":[36,78,80,37,38,81,39,40],"76":[36,78,80,37,38,81,39,40]},"packages/frontend/editor-ui/src/features/ai/instanceAi/components/TimelineTextSegment.vue":{"10":[36,78,79,80,37,38,81,39,40]},"packages/frontend/editor-ui/src/features/ai/instanceAi/composables/useCreditWarningBanner.ts":{"9":[36,78,79,80,37,100,38,81,39,40,82],"12":[36,78,79,80,37,100,38,81,39,40,82],"23":[36,78,79,80,37,100,38,81,39,40,82]},"packages/frontend/editor-ui/src/features/ai/instanceAi/composables/useInstanceAiHandoff.ts":{"49":[79,80,81,82],"50":[79,80,81,82],"52":[79,80,81,82],"109":[82],"117":[79,80,81,82],"135":[79,80,81,82],"153":[79,80,81,82],"169":[82],"187":[82]},"packages/frontend/editor-ui/src/features/ai/instanceAi/constants.ts":{"28":[36,78,80,37,100,38,81,39,40,82]},"packages/frontend/editor-ui/src/features/ai/instanceAi/instanceAi.api.ts":{"26":[36,78,80,37,100,38,81,39,40,82],"49":[36,78,80,37,100,38,81,39,40,82],"107":[37,39],"124":[36,78,79,80,37,100,38,81,39,40,82],"181":[36,78,79,80,37,100,38,81,39,40,82],"203":[37,39]},"packages/frontend/editor-ui/src/features/ai/instanceAi/instanceAi.handoffContext.ts":{"16":[36,78,79,80,37,100,38,81,39,40,82]},"packages/frontend/editor-ui/src/features/ai/instanceAi/instanceAi.liveRunState.ts":{"9":[79,80,37,81,39,82],"19":[38],"44":[37,39],"61":[37,39],"73":[37,39],"89":[37,39]},"packages/frontend/editor-ui/src/features/ai/instanceAi/instanceAi.memory.api.ts":{"12":[36,78,79,80,37,100,38,81,39,40,82],"18":[81],"22":[81],"42":[79,80,81,82],"59":[79,80,37,81,39,82]},"packages/frontend/editor-ui/src/features/ai/instanceAi/instanceAi.reducer.ts":{"53":[36,78,79,80,37,100,38,81,39,40],"60":[36,78,79,80,37,100,38,81,39,40,82],"65":[36,78,79,80,37,100,38,81,39,40,82],"110":[36,78,80,37,100,38,81,39,40,82],"119":[36,78,79,80,37,100,38,81,39,40,82],"127":[36,78,80,37,100,38,81,39,40,82],"144":[36,78,80,37,100,38,81,39,40,82],"149":[36,78,79,80,37,100,38,81,39,40,82],"179":[36,78,79,80,37,100,38,81,39,40],"193":[36,78,79,80,37,100,38,81,39,40,82]},"packages/frontend/editor-ui/src/features/ai/instanceAi/instanceAi.settings.api.ts":{"11":[36,78,79,80,37,100,38,81,39,40,82],"24":[36,78,79,80,37,100,38,81,39,40,82],"37":[36,78,79,80,37,100,38,81,39,40,82],"43":[36,78,79,80,37,100,38,81,39,40,82]},"packages/frontend/editor-ui/src/features/ai/instanceAi/instanceAi.threadRuntime.ts":{"104":[36,78,79,80,37,100,38,81,39,40,82],"119":[36,78,79,80,37,100,38,81,39,40,82],"157":[36,78,80,37,100,38,81,39,40,82],"175":[36,78,79,80,37,100,38,81,39,40,82],"266":[79,80,81],"298":[36,78,79,80,37,100,38,81,39,40,82],"340":[36,37,38,39,40],"354":[39,40],"361":[36,37,38,39,40],"364":[36,37,40],"381":[36,78,79,80,37,100,38,81,39,40,82],"382":[36,78,79,80,37,100,38,81,39,40,82],"383":[36,78,79,80,37,100,38,81,39,40,82],"384":[36,78,79,80,37,100,38,81,39,40,82],"386":[36,78,79,80,37,100,38,81,39,40,82],"387":[36,37,38,39,40],"388":[36,78,79,80,37,100,38,81,39,40,82],"389":[36,78,79,80,37,100,38,81,39,40,82],"403":[36,78,79,80,37,100,38,81,39,40,82],"414":[36,78,79,80,37,100,38,81,39,40,82],"426":[36,78,79,80,37,100,38,81,39,40,82],"427":[36,37,38,39,40],"454":[36,78,80,37,100,38,81,39,40,82],"457":[36,78,80,37,100,38,81,39,40,82],"464":[36,78,79,80,37,100,38,81,39,40,82],"466":[36,78,79,80,37,100,38,81,39,40,82],"473":[36,78,79,80,37,100,38,81,39,40,82],"490":[36,78,79,80,37,100,38,81,39,40,82],"493":[36,78,79,80,37,100,81,39,40,82],"499":[79],"510":[36,78,79,80,37,100,38,81,39,40,82],"520":[36,78,79,80,37,100,38,81,39,40,82],"522":[37,39],"530":[38],"539":[37,39],"554":[39],"569":[39],"595":[36,78,79,80,37,100,38,81,39,40,82],"635":[36,78,79,80,37,100,38,81,39,40,82],"738":[36,78,79,80,37,100,38,81,39,40,82],"758":[36,78,79,80,37,100,38,81,39,40,82],"763":[79],"820":[36,78,79,80,37,100,38,81,39,40,82],"838":[36,78,79,80,37,100,38,81,39,40,82],"843":[36,78,79,80,37,100,38,81,39,40,82],"850":[36,78,79,80,37,100,38,81,39,40,82],"855":[79,80],"867":[81],"876":[81],"895":[81],"900":[79,80,81,82],"911":[79,80,81,82],"926":[79,80,81],"927":[79,80,81],"953":[79,80,37,81,39,82],"985":[36,78,80,37,100,38,81,39,40,82],"996":[36,78,80,37,100,38,81,39,40,82],"1022":[36,78,80,37,100,38,81,39,40,82],"1047":[36,78,80,37,100,38,81,39,40,82],"1088":[36,78,80,37,100,38,81,39,40,82],"1164":[36,78,80,37,100,38,81,39,40,82],"1172":[37,39]},"packages/frontend/editor-ui/src/features/ai/instanceAi/instanceAiMcp.store.ts":{"26":[36,78,79,80,37,100,38,81,39,40,82]},"packages/frontend/editor-ui/src/features/ai/instanceAi/instanceAiMcp.telemetry.ts":{"4":[36,78,79,80,37,100,38,81,39,40,82]},"packages/frontend/editor-ui/src/features/ai/instanceAi/instanceAiPermissions.ts":{"9":[36,78,79,80,37,100,38,81,39,40,82]},"packages/frontend/editor-ui/src/features/ai/instanceAi/instanceAiPromptSuggestions.telemetry.ts":{"45":[36,78,80,37,100,38,81,39,40,82],"48":[36,78,80,37,100,38,81,39,40,82],"63":[36,78,79,80,37,100,38,81,39,40,82],"68":[36,78,80,37,100,38,81,39,40,82],"117":[36,78,79,80,37,100,38,81,39,40,82]},"packages/frontend/editor-ui/src/features/ai/instanceAi/module.descriptor.ts":{"21":[36,78,79,80,37,100,38,81,39,40,82],"22":[36,78,80,37,100,38,81,39,40,82],"23":[36,78,79,80,37,100,38,81,39,40,82],"48":[82],"170":[36,78,79,80,37,100,38,81,39,40,82]},"packages/frontend/editor-ui/src/features/ai/instanceAi/toolLabels.ts":{"136":[36,78,79,80,37,100,38,81,39,40,82],"139":[36,78,37,38,39,40]},"packages/frontend/editor-ui/src/features/ai/instanceAi/useCanvasPreview.ts":{"37":[36,78,79,80,37,100,38,81,39,40,82],"43":[36,78,79,80,37,100,38,81,39,40,82],"61":[36,37,38,39,40],"62":[36,37,38,39,40],"66":[36,37,38,39,40],"67":[36,37,38,39,40],"76":[36,37,38,39,40],"77":[36,37,38,39,40],"86":[36,37,38,39,40],"97":[36,37,38,39,40],"104":[36,78,79,80,37,100,38,81,39,40,82],"110":[36,78,79,80,37,100,38,81,39,40,82],"117":[36,78,79,80,37,100,38,81,39,40,82],"133":[39],"138":[36,40],"147":[36],"182":[36,37,38,39,40],"184":[36,37,38,39,40],"194":[36,78,79,80,37,100,38,81,39,40,82],"214":[36,78,79,80,37,100,38,81,39,40,82],"215":[36,37,38,39,40],"234":[36,78,79,80,37,100,38,81,39,40,82],"245":[36,78,79,80,37,100,38,81,39,40,82],"263":[36,78,79,80,37,100,38,81,39,40,82],"274":[36,78,79,80,37,100,38,81,39,40,82],"290":[36,78,79,80,37,100,38,81,39,40,82],"301":[36,78,79,80,37,100,38,81,39,40,82],"321":[36,78,79,80,37,100,38,81,39,40,82],"332":[36,78,79,80,37,100,38,81,39,40,82],"349":[36,78,79,80,37,100,38,81,39,40,82],"360":[36,78,79,80,37,100,38,81,39,40,82],"375":[36,78,79,80,37,100,38,81,39,40,82],"401":[36,78,79,80,37,100,38,81,39,40,82],"412":[36,78,79,80,37,100,38,81,39,40,82]},"packages/frontend/editor-ui/src/features/ai/instanceAi/useResourceRegistry.ts":{"37":[36,37,38,39,40],"59":[36,37,38,39,40],"88":[37,39],"92":[37,39],"138":[36,78,37,38,39,40],"296":[36,78,79,80,37,100,38,81,39,40,82],"311":[36,78,79,80,37,100,38,81,39,40,82],"326":[36,78,79,80,37,100,38,81,39,40,82],"345":[36,78,79,80,37,100,38,81,39,40,82],"368":[36,78,79,80,37,100,38,81,39,40,82],"405":[36,78,79,80,37,100,38,81,39,40,82],"421":[36,78,79,80,37,100,38,81,39,40,82],"449":[36,78,79,80,37,100,38,81,39,40,82],"462":[36,78,79,80,37,100,38,81,39,40,82],"480":[36,37,38,39,40]},"packages/frontend/editor-ui/src/features/ai/instanceAi/useResponseFeedback.ts":{"11":[36,78,79,80,37,100,81,39,40],"13":[79],"16":[36,78,79,80,37,100,81,39,40],"17":[36,78,37,39,40],"18":[79],"21":[36,78,79,80,37,100,81,39,40],"22":[36,78,37,39,40],"24":[79],"47":[36,78,79,80,37,100,38,81,39,40,82],"72":[36,78,79,80,37,100,38,81,39,40],"161":[81]},"packages/frontend/editor-ui/src/features/ai/instanceAi/useTransitionGate.ts":{"3":[36,78,79,80,37,100,38,81,39,40,82],"7":[36,78,79,80,37,100,38,81,39,40,82],"9":[36,78,79,80,37,100,38,81,39,40,82],"10":[36,78,79,80,37,100,38,81,39,40,82],"18":[79,80,81,82],"23":[79,80,81,82]},"packages/frontend/editor-ui/src/features/ai/shared/components/AiThinkingBlock.vue":{"8":[36,78,80,37,38,81,39,40],"27":[36,78,80,37,38,81,39,40],"29":[36,78,80,37,38,81,39,40],"30":[36,78,80,37,81,39,40],"41":[36,78,80,37,38,81,39,40],"49":[36,78,80,37,38,81,39,40],"52":[36,78,80,37,38,81,39,40],"57":[36,80,37,38,39,40],"73":[81],"77":[36,78,80,37,38,39,40],"82":[36,78,80,37,38,81,39,40],"86":[36,78,80,37,38,81,39,40],"117":[36,78,80,37,38,81,39,40],"124":[36,78,80,37,38,81,39,40],"126":[36,78,80,37,38,81,39,40],"133":[36,78,80,37,38,81,39,40]},"packages/frontend/editor-ui/src/features/ai/shared/components/ChatInputBase.vue":{"48":[36,78,79,80,37,100,38,81,39,40,82],"89":[36,78,80,37,100,38,81,39,40,82],"93":[78],"119":[36,78,80,37,100,38,81,39,40],"131":[36,78,79,80,37,100,38,81,39,40,82],"165":[36,78,80,37,100,38,81,39,40],"168":[36,78,80,37,100,38,81,39,40,82],"169":[36,78,80,37,100,38,81,39,40],"171":[36,78,79,80,37,100,38,81,39,40,82],"174":[36,78,79,80,37,100,38,81,39,40,82],"177":[36,78,79,80,37,100,38,81,39,40,82],"182":[36,78,79,80,37,100,38,81,39,40,82],"196":[36,78,79,80,37,100,38,81,39,40,82]},"packages/frontend/editor-ui/src/features/ai/shared/thinking.utils.ts":{"2":[36,78,80,37,38,81,39,40]},"packages/frontend/editor-ui/src/features/core/dataTable/components/dataGrid/ColumnHeader.vue":{"31":[34,35],"32":[34,35],"34":"b:AAAAAPwBAAAAwAcAEAAAAAAAAAAAAAAA","55":[34],"64":[34],"68":[34],"72":[34],"76":[34,35],"86":[34,35],"90":[34,35],"97":[34,35],"102":[34,35],"113":[34,35],"122":[34,35],"124":[34],"130":[34,35],"150":[34,35],"154":[34,35],"172":[34],"182":[34,35],"194":[34,35],"200":[34,35],"253":[34]},"localhost-35967/assets/_plugin-vue_export-helper-Da88TEg1.js":{"2":[67,135,138,71,153]},"localhost-35967/assets/chunk-CC9Q-vWm.js":{"8":[67,135,138,71,153],"9":[67,135,138,71,153],"10":[67,135,138,71,153],"19":[67,135,138,71,153],"23":[67,135,138,71,153],"29":[67,135,138,71,153],"33":[67,135,138,71,153],"34":[67,135,138,71,153]},"localhost-35967/assets/verified-Ge9vhh2v.js":{"8":[67,135]},"packages/frontend/@n8n/design-system/src/components/N8nCircleLoader/CircleLoader.vue":{"3":[67],"15":[67],"16":[67],"18":[67],"22":[67],"23":[67],"24":[67],"28":[67]},"packages/frontend/@n8n/design-system/src/components/N8nIcon/custom/node-dirty.svg":{"11":[51,110,58,67,139,147,149,152]},"packages/frontend/@n8n/design-system/src/components/N8nIcon/custom/triangle.svg":{"8":[87,51,110,67,137,139,147,149,152]},"packages/frontend/@n8n/design-system/src/components/N8nIcon/nodes/compare-datasets.svg":{"11":[26,31,65,130,131,134,67,145]},"packages/frontend/@n8n/design-system/src/components/N8nIcon/nodes/convert-to-file.svg":{"11":[26,31,51,65,130,131,134,67,135,145]},"packages/frontend/@n8n/design-system/src/components/N8nIcon/nodes/extract-from-file.svg":{"11":"b:AAAAhAAAAAAKAAAAAAIAAMwAAgAAAAAA"},"packages/frontend/editor-ui/src/app/composables/canvasConnectionReplacement.utils.ts":{"85":"b:gmVBlAIQiASKAAACwAEOsOz2ehIAAAAA","93":"b:gkFBlAIAiACKAAACwAECsOzyWhIAAAAA","101":"b:gmVBlAIQiASKAAACwAEOsOz2ehIAAAAA","191":[58,115,65],"211":[58,115,65],"220":[58,115,65],"227":[58,115,65],"251":[58,115,65],"266":[58,115,65]},"packages/frontend/editor-ui/src/app/utils/expressions.ts":{"16":[141,142],"20":[60],"21":[60],"29":"b:wqUAAAAAAAJCAAAAACACAABQMgAAAAAA","33":"b:goUAAAAAAAICAAAAAAACAAAQMgAAAAAA","37":"b:goUAAAAAAAICAAAAAAACAIDQMgAAAAAA","43":[15,145,148,149],"47":[15,145,148,149],"51":[15,145,148,149],"55":[15,145,148,149],"59":[15,145,148,149],"63":"b:wuUAAAAABBJSAAAAACACAIDwOgAAAAAA","78":[15,1,140,142,145,13,70,148,149],"121":"b:wuUAAAAABBJSAAAAAKAiAADwOgAAAAAA","145":"b:kkFC1AIAibALAoBCzEESIczyOgMAAAAA","155":"b:kkFClAIAibALAgBCjEESIcyiOgIAAAAA"},"packages/frontend/editor-ui/src/app/utils/forceParse.ts":{"9":[87,67,142]},"packages/frontend/editor-ui/src/app/utils/parameterUtils.ts":{"13":"b:kmFC1AIAiRALAoBCzEESMMz6OgMAAAAA"},"packages/frontend/editor-ui/src/features/ai/assistant/assistant.api.ts":{"15":[18,20],"42":[15,87,16,17],"71":[87],"83":[67],"125":[18,20]},"packages/frontend/editor-ui/src/features/shared/editors/components/CodeNodeEditor/AskAI/AskAI.vue":{"28":[87,26,31,116,65,130,131,134,67,145],"57":[87,26,31,116,65,130,131,134,67,145],"64":[67],"67":[67],"70":[87,26,31,116,65,130,131,134,67,145],"76":[67],"93":[87,26,31,116,65,130,131,134,67,145],"100":[87,26,31,116,65,130,131,134,67,145],"101":[87,26,31,116,65,130,131,134,67,145],"103":[87,26,31,116,65,130,131,134,67,145],"104":[87,26,31,116,65,130,131,134,67,145],"107":[67],"108":[67],"110":[67],"118":[67],"133":[67],"141":[67],"150":[67],"216":[67],"221":[67],"243":[87,26,31,116,65,130,131,134,67,145],"250":[67],"254":[87,26,31,116,65,130,131,134,67,145],"259":[87,26,31,116,65,130,131,134,67,145],"293":[67],"304":[87,26,31,116,65,130,131,134,67,145],"308":[67]},"packages/frontend/editor-ui/src/features/shared/editors/components/CodeNodeEditor/CodeNodeEditor.vue":{"43":"b:AAAAhAAAAAAKAIAAAAAQAEwAAoAAAAAA","71":[87,67,145],"72":"b:AAAAhAAAAAAKAIAAAAAQAEwAAoAAAAAA","73":[87,67,145],"76":"b:AAAAhAAAAAAKAIAAAAAQAEwAAoAAAAAA","77":[26,31,65,130,131,134,67,145],"78":"b:AAAAhAAAAAAKAIAAAAAQAEwAAoAAAAAA","85":"b:AAAAhAAAAAAKAIAAAAAQAEwAAoAAAAAA","86":"b:AAAAhAAAAAAKAIAAAAAQAEwAAoAAAAAA","87":"b:AAAAhAAAAAAKAIAAAAAQAEwAAoAAAAAA","90":"b:AAAAhAAAAAAKAIAAAAAQAEwAAoAAAAAA","97":"b:AAAAhAAAAAAKAIAAAAAQAEwAAoAAAAAA","100":"b:AAAAhAAAAAAKAIAAAAAQAEwAAoAAAAAA","109":"b:AAAAhAAAAAAKAAAAAAAQAEwAAoAAAAAA","114":"b:AAAAhAAAAAAKAIAAAAAQAEwAAoAAAAAA","118":"b:AAAAhAAAAAAKAIAAAAAQAEwAAoAAAAAA","124":[67],"140":[67],"152":[87,116,67,145],"158":[87],"165":[87,116,67,145],"166":[87,116,67,145],"199":[67],"203":[67],"225":"b:AAAAhAAAAAAKAIAAAAAQAEwAAoAAAAAA","239":[87,26,31,116,65,130,131,134,67,145],"245":[87,26,31,116,65,130,131,134,67,145],"252":[87,26,31,116,65,130,131,134,67,145],"271":[87,26,31,116,65,130,131,134,67,145],"291":[31,159]},"packages/frontend/editor-ui/src/features/shared/editors/components/CodeNodeEditor/linter.ts":{"23":"b:AAAAhAAAAAAKAIAAAAAQAEwAAoAAAAAA","29":"b:AAAAhAAAAAAKAIAAAAAQAEwAAoAAAAAA","38":[87,67,145],"62":[67],"68":[67],"95":[87,67,145],"136":[87,67,145],"138":[87,67,145],"142":[87,67,145],"144":[87,67,145],"146":[87,67,145],"151":[67],"180":[67],"222":[67],"230":[67],"257":[87,67,145],"264":[67],"288":[87,67,145],"298":[67],"321":[87,67,145],"328":[67],"329":[67],"346":[67],"352":[67],"377":[87,67,145],"399":[87,67,145],"415":[87,67,145],"422":[67],"466":[67],"513":[87,67,145],"554":[87,67,145],"588":[87,67,145],"599":[67],"626":[67],"632":[67],"633":[67]},"packages/frontend/editor-ui/src/features/shared/editors/components/CodeNodeEditor/utils.ts":{"10":[87,67,145],"19":[87,67,145],"30":[87,67,145]},"packages/frontend/editor-ui/src/features/shared/editors/composables/useCodeEditor.ts":{"70":"b:AAAAhAAAAAAKAIAAAAAQAEwAAoAAAAAA","113":"b:AAAAhAAAAAAKAIAAAAAQAEwAAoAAAAAA","114":"b:AAAAhAAAAAAKAIAAAAAQAEwAAoAAAAAA","125":"b:AAAAhAAAAAAKAIAAAAAQAEwAAoAAAAAA","134":"b:AAAAhAAAAAAKAIAAAAAQAEwAAoAAAAAA","166":[87,26,31,116,65,130,131,134,67,145],"170":"b:AAAAhAAAAAAKAIAAAAAQAEwAAoAAAAAA","181":"b:AAAAhAAAAAAKAIAAAAAQAEwAAoAAAAAA","185":[87,31,116,67,145],"195":[87,26,31,116,65,130,131,134,67,145],"203":[87,26,31,116,65,130,131,134,67,145],"224":"b:AAAAhAAAAAAKAIAAAAAQAEwAAoAAAAAA","239":"b:AAAAhAAAAAAKAIAAAAAQAEwAAoAAAAAA","253":[87,116,67,145],"262":[145],"267":[145],"275":"b:AAAAhAAAAAAKAIAAAAAQAEwAAoAAAAAA","370":[87,26,31,65,130,131,134,67,145],"382":"b:AAAAhAAAAAAKAIAAAAAQAEwAAoAAAAAA","386":"b:AAAAhAAAAAAKAAAAAAAQAEwAAoAAAAAA","417":[87,67,145],"443":[87,67,145]},"packages/frontend/editor-ui/src/features/shared/editors/plugins/codemirror/typescript/client/completions.ts":{"19":[67],"30":[67]},"packages/frontend/editor-ui/src/features/shared/editors/plugins/codemirror/typescript/client/linter.ts":{"4":[67],"8":[67]},"packages/frontend/editor-ui/src/features/shared/editors/plugins/codemirror/typescript/client/useTypescript.ts":{"28":"b:AAAAhAAAAAAKAIAAAAAQAEwAAoAAAAAA","37":"b:AAAAhAAAAAAKAIAAAAAQAEwAAoAAAAAA","44":"b:AAAAhAAAAAAKAIAAAAAQAEwAAoAAAAAA","68":[87,67],"116":[87,67],"124":[87,67],"134":"b:AAAAhAAAAAAKAIAAAAAQAEwAAoAAAAAA","135":"b:AAAAhAAAAAAKAIAAAAAQAEwAAoAAAAAA","141":[67],"149":"b:AAAAhAAAAAAKAAAAAAAQAEwAAoAAAAAA"},"packages/core/src/nodes-loader/output-schema-resolver.ts":{"53":[50,109,111,58,115,135,139,142,70],"61":[50,109,111,58,115,135,139,142,70],"65":[50,109,111,58,115,135,139,142,70],"81":[50,109,111,58,115,135,139,142,70],"82":[50,109,111,58,115,135,139,142,70],"83":[50,109,111,58,115,135,139,142,70],"85":[50,109,111,58,115,135,139,142,70],"88":[50,109,111,58,115,135,139,142,70],"90":[50,109,111,58,115,135,139,142,70],"91":[50,109,111,58,115,135,139,142,70],"92":[50,109,111,58,115,135,139,142,70],"93":[50,109,111,58,115,135,139,142,70],"94":[50,109,111,58,115,135,139,142,70],"109":[50,109,111,58,115,135,139,142,70],"114":[50,109,111,58,115,135,139,142,70],"116":[50,109,111,58,115,135,139,142,70],"127":[50,109,111,58,115,135,139,142,70],"160":[50,109,111,58,115,135,139,142,70]},"packages/nodes-base/nodes/Html/Html.node.ts":{"19":[135],"23":[135],"24":[135],"383":[135],"429":[135],"435":[135],"451":[135],"474":[135],"579":[135]},"packages/nodes-base/nodes/Html/utils.ts":{"10":[135],"12":[135],"13":[135],"21":[135],"29":[135],"39":[135]},"packages/nodes-base/nodes/Postgres/Postgres.node.ts":{"8":[135]},"packages/nodes-base/nodes/Postgres/transport/index.ts":{"27":[135],"47":[135],"50":[135],"56":[135],"91":[135],"92":[135],"96":[135],"100":[135],"106":[135],"114":[135],"121":[135],"138":[135],"156":[135],"157":[135],"162":[135],"168":[135],"186":[135],"208":[135]},"packages/nodes-base/nodes/Postgres/v1/PostgresV1.node.ts":{"288":[135],"297":[135],"331":[135]},"packages/nodes-base/nodes/Postgres/v1/genericFunctions.ts":{"13":[135],"27":[135],"33":[135],"36":[135],"55":[135],"62":[135],"78":[135],"83":[135],"88":[135],"92":[135],"105":[135],"119":[135],"121":[135],"136":[135],"157":[135],"182":[135],"198":[135],"200":[135],"224":[135],"231":[135],"255":[135],"291":[135],"307":[135],"308":[135],"314":[135],"327":[135],"346":[135],"383":[135],"398":[135],"399":[135],"405":[135],"418":[135],"425":[135],"450":[135],"494":[135],"510":[135],"511":[135],"517":[135],"520":[135],"537":[135],"546":[135],"558":[135],"561":[135],"587":[135],"626":[135],"641":[135],"642":[135],"648":[135],"651":[135],"668":[135],"677":[135],"690":[135],"693":[135],"721":[135]},"packages/nodes-base/nodes/Postgres/v2/PostgresV2.node.ts":{"16":[135],"25":[135]},"packages/nodes-base/nodes/Postgres/v2/actions/database/deleteTable.operation.ts":{"88":[135]},"packages/nodes-base/nodes/Postgres/v2/actions/database/executeQuery.operation.ts":{"55":[135],"65":[135],"119":[135],"120":[135]},"packages/nodes-base/nodes/Postgres/v2/actions/database/insert.operation.ts":{"161":[135]},"packages/nodes-base/nodes/Postgres/v2/actions/database/select.operation.ts":{"79":[135]},"packages/nodes-base/nodes/Postgres/v2/actions/database/update.operation.ts":{"195":[135],"279":[135],"286":[135],"333":[135]},"packages/nodes-base/nodes/Postgres/v2/actions/database/upsert.operation.ts":{"194":[135],"286":[135],"298":[135]},"packages/nodes-base/nodes/Postgres/v2/actions/router.ts":{"15":[135]},"packages/nodes-base/nodes/Postgres/v2/helpers/utils.ts":{"27":[135],"36":[135],"46":[135],"50":[135],"51":[135],"54":[135],"58":[135],"63":[135],"70":[135],"79":[135],"87":[135],"130":[135],"151":[135],"180":[135],"192":[135],"207":[135],"226":[135],"234":[135],"311":[135],"314":[135],"315":[135],"322":[135],"353":[135],"356":[135],"359":[135],"366":[135],"376":[135],"392":[135],"413":[135],"453":[135],"495":[135],"501":[135],"517":[135],"518":[135],"526":[135],"527":[135],"530":[135],"537":[135],"540":[135],"547":[135],"562":[135],"592":[135],"608":[135],"613":[135],"620":[135],"625":[135],"628":[135],"645":[135],"652":[135],"673":[135],"676":[135],"694":[135],"711":[135],"752":[135],"754":[135],"766":[135],"772":[135],"781":[135]},"packages/nodes-base/nodes/Postgres/v2/methods/credentialTest.ts":{"10":[135]},"packages/nodes-base/nodes/Postgres/v2/methods/listSearch.ts":{"6":[135],"17":[135],"24":[135],"40":[135]},"packages/nodes-base/nodes/Postgres/v2/methods/loadOptions.ts":{"7":[135],"23":[135],"30":[135],"38":[135],"43":[135]},"packages/nodes-base/nodes/Postgres/v2/methods/resourceMapping.ts":{"61":[135],"76":[135],"98":[135],"100":[135]},"packages/nodes-base/utils/connection-pool-manager.ts":{"53":[135],"66":[135],"68":[135],"74":[135],"81":[135],"104":[135],"131":[135],"139":[135],"152":[135],"172":[135]},"packages/nodes-base/utils/postgres.ts":{"12":[135],"32":[135],"34":[135],"38":[135],"41":[135],"47":[135],"55":[135]},"packages/@n8n/codemirror-lang-sql/src/complete.ts":{"192":[135],"193":[135]},"packages/frontend/@n8n/design-system/src/components/N8nIcon/custom/node-pin.svg":{"11":"b:AEAAAAAAAABQAAAIACAAAND6DwEEAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nIcon/nodes/data-table.svg":{"11":"b:AkEAEAAAgAAAAAAABAAAMIAAAhAAAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nIcon/nodes/html.svg":{"11":[135]},"packages/frontend/editor-ui/src/features/ndv/parameters/components/ParameterIssues.vue":{"9":"b:AGFCCEIAgCAAAAAChIAAAIAgEgIAAAAA","17":"b:AGBCAEAAgCAAAAAChIAAAIAgEgIAAAAA"},"packages/frontend/editor-ui/src/features/ndv/runData/schemaPreview.api.ts":{"13":"b:AAAAAAAABARAAAAAAKAIAIBMzIAAAAAA","17":[115,139,150],"27":"b:AAAAAAAABARAAAAAAKAIAIBMzIAAAAAA"},"packages/frontend/editor-ui/src/features/shared/editors/components/HtmlEditor/HtmlEditor.vue":{"51":[135],"63":[135],"68":[135],"97":[135],"100":[135],"237":[135],"243":[135],"247":[135],"260":[135],"265":[135]},"packages/frontend/editor-ui/src/features/shared/editors/components/HtmlEditor/utils.ts":{"44":[135]},"packages/frontend/editor-ui/src/features/shared/editors/components/InlineExpressionEditor/ExpressionOutput.vue":{"20":"b:AEAAAAAAABBAAAAAAAAAAIDwOAAAAAAA","30":"b:AEAAAAAAABBAAAAAAAAAAIDwOAAAAAAA","35":"b:AEAAAAAAABBAAAAAAAAAAIDwKAAAAAAA","49":"b:AEAAAAAAABBAAAAAAAAAAIDwOAAAAAAA","50":"b:AEAAAAAAABBAAAAAAAAAAIDwKAAAAAAA","53":"b:AEAAAAAAABBAAAAAAAAAAIDwOAAAAAAA","71":"b:AEAAAAAAABBAAAAAAAAAAIDwKAAAAAAA","82":"b:AEAAAAAAABBAAAAAAAAAAIDwKAAAAAAA","85":"b:AEAAAAAAABBAAAAAAAAAAIDwOAAAAAAA","95":[142],"105":"b:AEAAAAAAABBAAAAAAAAAAIDwOAAAAAAA","106":"b:AEAAAAAAABBAAAAAAAAAAIDwKAAAAAAA","119":"b:AEAAAAAAABBAAAAAAAAAAIDwOAAAAAAA","132":"b:AEAAAAAAABBAAAAAAAAAAIDwOAAAAAAA","137":[60,135,140,141,142,143,14,148,149],"141":[142],"142":"b:AEAAAAAAABBAAAAAAAAAAIDwOAAAAAAA"},"packages/frontend/editor-ui/src/features/shared/editors/components/InlineExpressionEditor/InlineExpressionEditorOutput.vue":{"32":"b:wuUAAAAABBJSAAAAAKAiAIDwOgIAAAAA","38":"b:wqUAAAAAABJSAAAAAIACAIBQAAAAAAAA","45":"b:wuUAAAAABBJSAAAAAKAiAIDwOgIAAAAA","66":"b:AEAAAAAAABBAAAAAAAAAAIDwOAAAAAAA","72":"b:AEAAAAAAABBAAAAAAAAAAIDwOAAAAAAA"},"packages/frontend/editor-ui/src/features/shared/editors/components/InlineExpressionEditor/InlineExpressionTip.vue":{"21":"b:AEBCQAIACRBAAABCgAEAAIDyOAAAAAAA","33":"b:AEAAAAAAABBAAAAAAAAAAIDwOAAAAAAA","37":[140,141,142,143,14,70,147],"39":"b:AEBCQAIACRBAAABCgAEAAIDyOAAAAAAA","57":"b:AEAAAAAAABBAAAAAAAAAAADwOAAAAAAA","83":"b:AEBCQAIACRAAAABCgAEAAIDyMAAAAAAA","88":"b:AEBCQAIACRBAAABCgAEAAIDyOAAAAAAA","96":"b:AEBCQAIACRBAAABCgAEAAIDyOAAAAAAA","97":"b:AEAAAAAAABBAAAAAAAAAAADwOAAAAAAA","106":"b:AEBCQAIACRBAAABCgAEAAIDyOAAAAAAA","110":"b:AEBCQAIACRBAAABCgAEAAIDyOAAAAAAA","114":[141,143,14,147],"120":[141,143,14,147]},"packages/frontend/editor-ui/src/features/shared/editors/components/InlineExpressionEditor/OutputItemSelect.vue":{"7":"b:AEAAAAAAABBAAAAAAAAAAIDwOAAAAAAA","10":"b:AEAAAAAAABBAAAAAAAAAAIDwOAAAAAAA","11":"b:AEAAAAAAABBAAAAAAAAAAIDwOAAAAAAA","12":"b:AEAAAAAAABBAAAAAAAAAAIDwOAAAAAAA","13":"b:AEAAAAAAABBAAAAAAAAAAIDwOAAAAAAA","14":"b:AEAAAAAAABBAAAAAAAAAAIDwOAAAAAAA","17":"b:AEAAAAAAABBAAAAAAAAAAIDwOAAAAAAA","18":"b:AEAAAAAAABBAAAAAAAAAAIDwOAAAAAAA","19":"b:AEAAAAAAABBAAAAAAAAAAIDwOAAAAAAA","20":"b:AEAAAAAAABBAAAAAAAAAAIDwOAAAAAAA","21":"b:AEAAAAAAABBAAAAAAAAAAIDwOAAAAAAA","25":"b:AEAAAAAAABBAAAAAAAAAAIDwOAAAAAAA","31":[14,70],"35":[14],"37":"b:AEAAAAAAABBAAAAAAAAAAIDwOAAAAAAA","70":"b:AEAAAAAAABBAAAAAAAAAAIDwOAAAAAAA"},"packages/frontend/editor-ui/src/features/shared/editors/components/InlineExpressionEditor/theme.ts":{"4":"b:wuUAAAAABBJSAAAAAKAiAIDwOgIAAAAA","18":"b:wuUAAAAABBJSAAAAAKAiAADwOgIAAAAA","58":"b:wuUAAAAABBJSAAAAAKAiAIDwOgIAAAAA"},"packages/frontend/editor-ui/src/features/shared/editors/components/SqlEditor/SqlEditor.vue":{"42":[135],"80":[135],"82":[135],"88":[135],"130":[135],"135":[135],"140":[135],"146":[135],"154":[135],"158":[135],"160":[135],"163":[135],"208":[135],"213":[135]},"packages/frontend/editor-ui/src/features/shared/editors/composables/useExpressionEditor.ts":{"60":"b:wuUAAAAABBJSAAAAAKAiAIDwOgIAAAAA","71":[60,140,141,142,143,14,147,148,149],"86":"b:wuUAAAAABBJSAAAAAKAiAIDwOgIAAAAA","104":"b:wuUAAAAABBJSAAAAAKAiAIDwOgIAAAAA","109":"b:wuUAAAAABBJSAAAAAIACAIDwOgAAAAAA","123":"b:wuUAAAAABBJSAAAAAIACAIDwOgAAAAAA","143":"b:wuUAAAAABBJSAAAAAIACAIDwOgAAAAAA","177":"b:wuUAAAAABBJSAAAAAKAiAIDwOgIAAAAA","181":"b:QuAAAAAABBBQAAAAAIAAAIDwOgAAAAAA","187":"b:wuUAAAAABBJSAAAAAKAiAIDwOgIAAAAA","198":"b:wuUAAAAABBJSAAAAAKAiAIDwOgIAAAAA","202":"b:wuUAAAAABBJSAAAAAKAiAIDwOgIAAAAA","213":"b:guUAAAAAABBCAAAAAAACAIDwMgAAAAAA","221":"b:guUAAAAAABBCAAAAAAACAIDwOgAAAAAA","228":[135,140,141,142,143,14,147,148,149],"239":"b:wuUAAAAABBJSAAAAAKAiAADwOgIAAAAA","250":"b:wuUAAAAABBJSAAAAAKAiAIDwOgIAAAAA","277":"b:AEAAAAAAABBAAAAAAAAAAIDwOAAAAAAA","282":"b:AEAAAAAAABBAAAAAAAAAAADwOAAAAAAA","294":"b:AEAAAAAAABBAAAAAAAAAAIDQOAAAAAAA","310":"b:wuUAAAAABBJSAAAAAKAiAIDwOgIAAAAA","318":"b:wuUAAAAABBJSAAAAAKAiAIDwOgIAAAAA","328":"b:wuUAAAAABBJSAAAAAKAiAIDwOgIAAAAA","340":"b:wuUAAAAABBJSAAAAAKAiAIDwOgIAAAAA","351":"b:wuUAAAAABBJSAAAAAKAiAIDwOgIAAAAA","355":"b:wqUAAAAAABJSAAAAAIACAIBQAAAAAAAA","362":[1,7,8,10,113,57,65,140],"364":[1,7,8,10,113,57,65,140],"370":[1,7,8,10,113,57,65,140],"380":"b:wuUAAAAABBJSAAAAAAACAIDwOgAAAAAA","448":"b:wuUAAAAABBJSAAAAAKAiAIDwOgIAAAAA","450":"b:wuUAAAAABBJSAAAAAKAiAIDwOgIAAAAA","451":"b:QuAAAAAABBBQAAAAAIAAAIDwOgAAAAAA","454":"b:QuAAAAAABBBQAAAAAIAAAIDwOgAAAAAA","455":"b:QuAAAAAABBBQAAAAAIAAAIDwOgAAAAAA","485":"b:wuUAAAAABBJSAAAAAKAiAADwOgIAAAAA","488":"b:QuAAAAAABBBQAAAAAIAAAADwOgAAAAAA","518":"b:QuAAAAAABBBQAAAAAIAAAADwOgAAAAAA","534":"b:wuUAAAAABBJSAAAAAKAiAIDwOgIAAAAA","535":"b:wuUAAAAABBJSAAAAAKAiAIDwOgIAAAAA","545":[60,140,141,142,143,14,147,148,149],"565":[60,140,141,142,143,14,147,148,149]},"packages/frontend/editor-ui/src/features/shared/editors/plugins/codemirror/completions/addCompletions.ts":{"8":"b:wuUAAAAABBJSAAAAAKAiAIDwOgIAAAAA","15":"b:wuUAAAAABBJSAAAAAKAiAIDwOgIAAAAA"},"packages/frontend/editor-ui/src/features/shared/editors/plugins/codemirror/completions/blank.completions.ts":{"8":"b:AEAAAAAAABAAAAAAAAAAAIDwOAAAAAAA"},"packages/frontend/editor-ui/src/features/shared/editors/plugins/codemirror/completions/bracketAccess.completions.ts":{"17":"b:AEAAAAAAABAAAAAAAAAAAIDwOAAAAAAA","53":[140,141,142,14],"70":[140,141,142,14],"74":[140,141,142,14],"75":[140,141,142,14]},"packages/frontend/editor-ui/src/features/shared/editors/plugins/codemirror/completions/dollar.completions.ts":{"29":"b:AEAAAAAAABAAAAAAAAAAAIDwOAAAAAAA","43":[60,135,140,141,142,143,14,147,149],"60":[60,135,140,141,142,143,14,147,149],"138":[135,140,141,142,143,14,147,149],"153":[135,140,141,142,143,14,147,149],"155":[135,140,141,142,143,14,147,149]},"packages/frontend/editor-ui/src/features/shared/editors/plugins/codemirror/completions/nonDollar.completions.ts":{"9":"b:AEAAAAAAABAAAAAAAAAAAIDwOAAAAAAA"},"packages/frontend/editor-ui/src/features/shared/editors/plugins/codemirror/expressionCloseBrackets.ts":{"10":"b:wuUAAAAABBJSAAAAAKAiAIDwOgIAAAAA","14":"b:AEAAAAAAABAAAAAAAAAAAIDwOAAAAAAA","36":"b:wuUAAAAABBJSAAAAAKAiAIDwOgIAAAAA"},"packages/frontend/editor-ui/src/features/shared/editors/plugins/codemirror/expressionDeprecations.ts":{"11":"b:wuUAAAAABBJSAAAAAAACAIDwOgAAAAAA","15":"b:wuUAAAAABBJSAAAAAAACAIDwOgAAAAAA"},"packages/nodes-base/nodes/DebugHelper/DebugHelper.node.ts":{"255":[110,68,138,151],"346":[110,68,138,151]},"packages/nodes-base/nodes/DebugHelper/functions.ts":{"4":[110,68,138,151],"14":[110,68,138,151]},"packages/nodes-base/nodes/DebugHelper/randomData.ts":{"26":[110,68,138,151],"36":[110,68,138,151],"48":[110,68,138,151],"55":[110,68,138,151],"59":[110,68,138,151],"63":[110,68,138,151],"79":[110,68,138,151],"83":[110,68,138,151],"87":[110,68,138,151],"91":[110,68,138,151],"95":[110,68,138,151],"99":[110,68,138,151]},"packages/frontend/editor-ui/src/app/components/DropArea/DropArea.vue":{"8":"b:QsAAAIAAjAaSAQBAAIAkAFSsTgAAAAAA","13":"b:QsAAAIAAjAaSAQBAAIAkAFSsTgAAAAAA"},"packages/@n8n/db/src/repositories/folder.repository.ts":{"11":"b:AAAAKwAA8ACAGXAgABgAFAAAAAAAAAAA","15":"b:AAAAKwAA8ACAGXAgABgAFAAAAAAAAAAA","23":"b:AAAAKwAA8ACAGXAgABgAFAAAAAAAAAAA","28":"b:AAAAKwAA8ACAGXAgABgAFAAAAAAAAAAA","39":"b:AAAAKwAA8ACAGXAgABgAFAAAAAAAAAAA","51":"b:AAAAKwAA8ACAGXAgABgAFAAAAAAAAAAA","56":"b:AAAAKwAA8ACAGXAgABgAFAAAAAAAAAAA","66":"b:AAAAKwAA8ACAGXAgABgAFAAAAAAAAAAA","85":"b:AAAAKwAA8ACAGXAgABgAFAAAAAAAAAAA","98":"b:AAAAKwAA8ACAGXAgABgAFAAAAAAAAAAA","104":"b:AAAAKwAA8ACAGXAgABgAFAAAAAAAAAAA","136":"b:AAAAKwAA8ACAGXAgABgAFAAAAAAAAAAA","140":"b:AAAAKwAA8ACAGXAgABgAFAAAAAAAAAAA","144":"b:AAAAKwAA8ACAGXAgABgAFAAAAAAAAAAA","148":"b:AAAAKwAA8ACAGXAgABgAFAAAAAAAAAAA","165":"b:AAAAKwAA8ACAGXAgABgAFAAAAAAAAAAA","198":"b:AAAAKwAA8ACAGXAgABgAFAAAAAAAAAAA","209":"b:AAAAKwAA8ACAGXAgABgAFAAAAAAAAAAA","225":"b:AAAAKwAA8ACAGXAgABgAFAAAAAAAAAAA","235":"b:AAAAKwAA8ACAGXAgABgAFAAAAAAAAAAA","240":"b:AAAAKwAA8ACAGXAgABgAFAAAAAAAAAAA","254":"b:AAAAKwAA8ACAGXAgABgAFAAAAAAAAAAA","260":"b:AAAAKwAA8ACAGXAgABgAFAAAAAAAAAAA","276":"b:AAAAKwAA8ACAGXAgABgAFAAAAAAAAAAA","295":"b:AAAAKwAA8ACAGXAgABgAFAAAAAAAAAAA","312":"b:AAAAKwAA8ACAGXAgABgAFAAAAAAAAAAA","352":"b:AAAAKwAA8ACAGXAgABgAFAAAAAAAAAAA","386":"b:AAAAKwAA8ACAGXAgABgAFAAAAAAAAAAA","395":"b:AAAAKwAA8ACAGXAgABgAFAAAAAAAAAAA","419":"b:AAAAKwAA8ACAGXAgABgAFAAAAAAAAAAA","422":"b:AAAAKwAA8ACAGXAgABgAFAAAAAAAAAAA"},"packages/@n8n/db/src/repositories/tag.repository.ts":{"11":[69,71,154,157,158,159],"15":[69,71,154,157,158,159],"23":[69,71,154,157,158,159],"34":[69,71,154,157,158,159],"43":[69,71,154,157,158,159],"55":[69,71,154,157,158,159],"72":[69,71,154,157,158,159],"84":[69,71,154,157,158,159]},"packages/@n8n/db/src/utils/sql.ts":{"4":"b:AAABOwIA0BCBGXAgABAAAAAAAAAAAAAA"},"packages/@n8n/decorators/src/timed.ts":{"16":"b:AAAAKwAA8ACAGXAgABAAEAAAAAAAAAAA","17":"b:AAAAKwAA8ACAGXAgABAAEAAAAAAAAAAA","18":"b:AAAAKwAA8ACAGXAgABAAEAAAAAAAAAAA","23":"b:AAAAKwAA8ACAGXAgABAAEAAAAAAAAAAA"},"packages/cli/src/middlewares/list-query/dtos/pagination.dto.ts":{"6":"b:AAAAKwAA8ACAGXAgABAAEAAAAAAAAAAA","15":"b:AAAAKwAA8ACAGXAgABAAEAAAAAAAAAAA"},"packages/cli/src/middlewares/list-query/dtos/workflow.sort-by.dto.ts":{"6":"b:AAAAKwAA8ACAGXAgABAAEAAAAAAAAAAA","13":"b:AAAAKwAA8ACAGXAgABAAEAAAAAAAAAAA"},"packages/cli/src/modules/data-table/data-table-aggregate.service.ts":{"13":"b:AAAAKwAA0ACAGXAgABAARAAAAAAAAAAA","20":"b:AAAAKwAA0ACAGXAgABAARAAAAAAAAAAA","21":"b:AAAAKwAA0ACAGXAgABAARAAAAAAAAAAA","23":"b:AAAAKwAA0ACAGXAgABAARAAAAAAAAAAA","30":"b:AAAAKwAA0ACAGXAgABAARAAAAAAAAAAA","33":"b:AAAAKwAA0ACAGXAgABAARAAAAAAAAAAA"},"packages/cli/src/modules/insights/database/repositories/insights-by-period-query.helper.ts":{"23":"b:AAABOwIA0BCBGXAgABAAAAAAAAAAAAAA","55":"b:AAABOwIA0BCBGXAgABAAAAAAAAAAAAAA"},"packages/cli/src/modules/insights/database/repositories/insights-by-period.repository.ts":{"38":"b:AAABOwIA0BCBGXAgABAAAAAAAAAAAAAA","39":"b:AAABOwIA0BCBGXAgABAAAAAAAAAAAAAA","40":"b:AAABOwIA0BCBGXAgABAAAAAAAAAAAAAA","41":"b:AAABOwIA0BCBGXAgABAAAAAAAAAAAAAA","42":"b:AAABOwIA0BCBGXAgABAAAAAAAAAAAAAA","43":"b:AAABOwIA0BCBGXAgABAAAAAAAAAAAAAA","44":"b:AAABOwIA0BCBGXAgABAAAAAAAAAAAAAA","51":"b:AAABOwIA0BCBGXAgABAAAAAAAAAAAAAA","55":"b:AAABOwIA0BCBGXAgABAAAAAAAAAAAAAA","79":"b:AAABOwIA0BCBGXAgABAAAAAAAAAAAAAA","83":"b:AAABOwIA0BCBGXAgABAAAAAAAAAAAAAA","87":"b:AAABOwIA0BCBGXAgABAAAAAAAAAAAAAA","97":"b:AAABOwIA0BCBGXAgABAAAAAAAAAAAAAA","111":"b:AAABOwIA0BCBGXAgABAAAAAAAAAAAAAA","119":"b:AAABOwIA0BCBGXAgABAAAAAAAAAAAAAA","137":"b:AAABOwIA0BCBGXAgABAAAAAAAAAAAAAA","160":"b:AAABOwIA0BCBGXAgABAAAAAAAAAAAAAA","204":"b:AAABOwIA0BCBGXAgABAAAAAAAAAAAAAA","240":"b:AAABOwIA0BCBGXAgABAAAAAAAAAAAAAA","259":"b:AAABOwIA0BCBGXAgABAAAAAAAAAAAAAA","305":"b:AAABOwIA0BCBGXAgABAAAAAAAAAAAAAA","310":"b:AAABOwIA0BCBGXAgABAAAAAAAAAAAAAA","323":"b:AAABOwIA0BCBGXAgABAAAAAAAAAAAAAA","392":"b:AAABOwIA0BCBGXAgABAAAAAAAAAAAAAA","407":"b:AAABOwIA0BCBGXAgABAAAAAAAAAAAAAA","431":"b:AAABOwIA0BCBGXAgABAAAAAAAAAAAAAA","440":"b:AAABOwIA0BCBGXAgABAAAAAAAAAAAAAA"},"packages/cli/src/modules/insights/insights.controller.ts":{"22":"b:AAABOwIA0BCBGXAgABAAAAAAAAAAAAAA","26":"b:AAABOwIA0BCBGXAgABAAAAAAAAAAAAAA","43":"b:AAABOwIA0BCBGXAgABAAAAAAAAAAAAAA","64":"b:AAABOwIA0BCBGXAgABAAAAAAAAAAAAAA","86":"b:AAABOwIA0BCBGXAgABAAAAAAAAAAAAAA","103":"b:AAABOwIA0BCBGXAgABAAAAAAAAAAAAAA","110":"b:AAABOwIA0BCBGXAgABAAAAAAAAAAAAAA","124":"b:AAABOwIA0BCBGXAgABAAAAAAAAAAAAAA","128":"b:AAABOwIA0BCBGXAgABAAAAAAAAAAAAAA","142":"b:AAABOwIA0BCBGXAgABAAAAAAAAAAAAAA","158":"b:AAABOwIA0BCBGXAgABAAAAAAAAAAAAAA"},"packages/cli/src/modules/insights/insights.service.ts":{"20":"b:AAABOwIA0BCBGXAgABAAAAAAAAAAAAAA","32":"b:AAABOwIA0BCBGXAgABAAAAAAAAAAAAAA","50":"b:AAABOwIA0BCBGXAgABAAAAAAAAAAAAAA","57":"b:AAABOwIA0BCBGXAgABAAAAAAAAAAAAAA","63":"b:AAABOwIA0BCBGXAgABAAAAAAAAAAAAAA","68":"b:AAABOwIA0BCBGXAgABAAAAAAAAAAAAAA","73":"b:AAABOwIA0BCBGXAgABAAAAAAAAAAAAAA","95":"b:AAABOwIA0BCBGXAgABAAAAAAAAAAAAAA","103":"b:AAABOwIA0BCBGXAgABAAAAAAAAAAAAAA","132":"b:AAABOwIA0BCBGXAgABAAAAAAAAAAAAAA","167":"b:AAABOwIA0BCBGXAgABAAAAAAAAAAAAAA","200":"b:AAABOwIA0BCBGXAgABAAAAAAAAAAAAAA","211":"b:AAABOwIA0BCBGXAgABAAAAAAAAAAAAAA","232":"b:AAABOwIA0BCBGXAgABAAAAAAAAAAAAAA","265":"b:AAABOwIA0BCBGXAgABAAAAAAAAAAAAAA","290":"b:AAABOwIA0BCBGXAgABAAAAAAAAAAAAAA"},"packages/cli/src/modules/workflow-index/workflow-dependency-query.service.ts":{"37":"b:AAEAIQ4A8ACBAQAgABgAcAAAAAgCAAAA","48":"b:AAEAIQ4A8ACBAQAgABgAcAAAAAgCAAAA","73":"b:AAEAIQ4A8ACBAQAgABgAcAAAAAgCAAAA","151":"b:AAEAIQ4A8ACBAQAgABgAcAAAAAgCAAAA","175":"b:AAEAIQ4A8ACBAQAgABgAcAAAAAgCAAAA","231":"b:AAEAIQ4A8ACBAQAgABgAcAAAAAgCAAAA","251":"b:AAEAIQ4A8ACBAQAgABgAcAAAAAgCAAAA","318":"b:AAEAIQ4A8ACBAQAgABgAcAAAAAgCAAAA","332":"b:AAEAIQ4A8ACBAQAgABgAcAAAAAgCAAAA","340":"b:AAEAIQ4A8ACBAQAgABgAcAAAAAgCAAAA","348":"b:AAEAIQ4A8ACBAQAgABgAcAAAAAgCAAAA","361":"b:AAEAIQ4A8ACBAQAgABgAcAAAAAgCAAAA","365":"b:AAEAIQ4A8ACBAQAgABgAcAAAAAgCAAAA"},"packages/cli/src/modules/workflow-index/workflow-dependency.controller.ts":{"9":"b:AAEAIQ4A8ACBAQAgAAAAUAAAAAAAAAAA","12":"b:AAEAIQ4A8ACBAQAgAAAAUAAAAAAAAAAA","25":"b:AAEAIQ4A8ACBAQAgAAAAUAAAAAAAAAAA"},"packages/nodes-base/nodes/Schedule/GenericFunctions.ts":{"7":"b:QMAEAAAEAAiQAAAAAAQAgBAICBgABgAC","40":"b:QMAEAAAEAAiQAAAAAAQAgBAICBgABgAC","117":"b:QMAEAAAEAAiQAAAAAAQAgBAICBgABgAC","122":"b:QMAEAAAEAAiQAAAAAAQAgBAICBgABgAC","170":"b:QMAEAAAEAAiQAAAAAAQAgBAICBgABgAC","189":"b:QMAEAAAEAAiQAAAAAAQAgBAICBgABgAC","262":"b:QMAEAAAEAAiQAAAAAAQAgBAICBgABgAC","292":"b:QMAEAAAEAAiQAAAAAAQAgBAICBgABgAC","299":"b:QMAEAAAEAAiQAAAAAAQAgBAICBgABgAC"},"packages/nodes-base/nodes/Schedule/ScheduleTrigger.node.ts":{"445":"b:QMAEAAAEAAiQAAAAAAQAgBAICAAABgAC","469":"b:QMAEAAAEAAiQAAAAAAQAgBAICAAABgAC","510":"b:QMAEAAAEAAiQAAAAAAQAgBAICAAABgAC","527":"b:QMAEAAAEAAiQAAAAAAQAgBAICAAABgAC","542":"b:QMAEAAAEAAiQAAAAAAQAgBAICAAABgAC"},"packages/@n8n/utils/src/string/truncate.ts":{"1":"b:eBEAGQsAwBDBAQAADRiAIAAgBwAGAAAA","11":"b:OAAEAAEABAAQAAAABQAAAEAICAAAAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nCard/Card.vue":{"9":"b:GAEAGw4A8LCBGWAACRgAIQAAAAAGAAAA","14":"b:GAEAGw4A8LCBGWAACRgAIQAAAAAGAAAA","19":"b:GAEAGw4A8LCBGWAACRgAIQAAAAAGAAAA"},"packages/frontend/@n8n/design-system/src/directives/n8n-truncate.ts":{"19":"b:AAEAGQoAwBCBAQAACBiAIAAAAAAGAAAA"},"packages/frontend/editor-ui/src/app/api/workflow-dependencies.ts":{"9":"b:AAEACQ4A8ACBAQAAABgAIAAAAAAGAAAA"},"packages/frontend/editor-ui/src/app/components/IntersectionObserved.vue":{"6":[52,116,71,154,160],"19":[52,116,71,154,160],"29":[52,154],"34":[52,116,71,154,160]},"packages/frontend/editor-ui/src/app/components/IntersectionObserver.vue":{"6":[52,116,71,154,72,155,160,162],"26":[52,154,155],"32":[52,116,71,154,72,155,160,162],"43":[52,116,71,154,160],"44":[52,116,71,154,160],"54":[52,116,71,154,160],"60":[52,154],"64":[52,116,71,154,72,155,160,162]},"packages/frontend/editor-ui/src/app/components/TimeAgo.vue":{"20":"b:AAEAGQ4A8BCBAwAAChgAIAAAAAAGAAAA","21":"b:AAEAGQ4A8BCBAwAAChgAIAAAAAAGAAAA","31":"b:AAEAGQ4A8BCBAwAAChgAIAAAAAAGAAAA","37":"b:AAEAGQ4A8BCBAwAAChgAIAAAAAAGAAAA","41":"b:AAEAGQ4A8BCBAwAAChgAIAAAAAAGAAAA"},"packages/frontend/editor-ui/src/app/components/WorkflowCard.vue":{"56":"b:AAAACQAA8ACAAQAAABgAAAAAAAAGAAAA","134":"b:AAAACQAA8ACAAQAAABgAAAAAAAAGAAAA","137":[24,27,108,54,55,71,72,161,162],"138":[161],"139":"b:AAAACQAA8ACAAQAAABgAAAAAAAAGAAAA","141":"b:AAAACQAA8ACAAQAAABgAAAAAAAAGAAAA","144":[55,72],"147":[55,72],"151":"b:AAAACQAA8ACAAQAAABgAAAAAAAAGAAAA","155":[24,27,108,54,55,71,72,161,162],"166":[24,27,108,54,55,71,72,161,162],"186":"b:AAAACQAA8ACAAQAAABgAAAAAAAAGAAAA","286":"b:AAAACQAA8ACAAQAAABgAAAAAAAAGAAAA","303":[52],"307":"b:AAAACQAA8ACAAQAAABgAAAAAAAAGAAAA","311":[24,27,108,54,55,71,72,161,162],"317":"b:AAAACQAA8ACAAQAAABgAAAAAAAAGAAAA","321":"b:AAAACQAA8ACAAQAAABgAAAAAAAAGAAAA","325":"b:AAAACQAA8ACAAQAAABgAAAAAAAAGAAAA","327":[55,72,161],"353":[52,53,54,55,161],"417":[161],"427":[161],"462":[161],"466":[161],"504":[161],"553":[161],"593":[161,162],"595":[161,162],"597":"b:AAAACQAA8ACAAQAAABgAAAAAAAAGAAAA","608":"b:AAAACQAA8ACAAQAAABgAAAAAAAAGAAAA","620":[72],"640":"b:AAAACQAA8ACAAQAAABgAAAAAAAAGAAAA","670":"b:AAAACQAA8ACAAQAAABgAAAAAAAAGAAAA","687":[24,27,108,54,55,71,72,161,162],"713":[161]},"packages/frontend/editor-ui/src/app/components/forms/ResourceFiltersDropdown.vue":{"51":[161],"63":"b:AAEAGQIA8BCBAQAACBiAIAAAAAAGAAAA","66":"b:AAEAGQIA8BCBAQAACBiAIAAAAAAGAAAA","89":"b:AAEAGQIA8BCBAQAACBiAIAAAAAAGAAAA","91":[161],"115":"b:AAEAGQIA8BCBAQAACBiAIAAAAAAGAAAA","119":[161],"126":"b:AAEAGQIA8BCBAQAACBiAIAAAAAAGAAAA","130":"b:AAEAGQIA8BCBAQAACBiAIAAAAAAGAAAA","142":"b:AAEAGQIA8BCBAQAACBiAIAAAAAAGAAAA","148":[161],"164":[161],"183":[161]},"packages/frontend/editor-ui/src/app/components/layouts/ResourcesListLayout.vue":{"150":"b:AAEBGQ4A8BCBAQAACBiAMAAAAAgGAAAA","151":[161],"154":"b:AAEBGQ4A8BCBAQAACBiAMAAAAAAGAAAA","167":"b:AAEBGQ4A8BCBAQAACBiAMAAAAAgGAAAA","177":"b:AAEBGQ4A8BCBAQAACBiAMAAAAAAGAAAA","181":"b:AAEBGQ4A8BCBAQAACBiAIAAAAAAGAAAA","185":[28,8,33,99,54,55,119,60,64,125],"210":[8,99,54,119,64,125],"254":"b:AAEBGQ4A8BCBAQAACBiAMAAAAAgGAAAA","255":[161],"264":"b:AAEBGQ4A8BCBAQAACBiAMAAAAAgGAAAA","271":"b:AAEBGQ4A8BCBAQAACBiAMAAAAAgGAAAA","272":[34,52,107,53,54,55,72,161],"278":"b:AAEBGQ4A8BCBAQAACBiAMAAAAAgGAAAA","285":"b:AAEBGQ4A8BCBAQAACBiAMAAAAAgGAAAA","286":[107,119,161],"290":"b:AAEBGQ4A8BCBAQAACBiAMAAAAAgGAAAA","291":[16,28,33,64,125],"297":"b:AAEBGQ4A8BCBAQAACBiAMAAAAAgGAAAA","304":"b:AAEBGQ4A8BCBAQAACBiAMAAAAAgGAAAA","311":"b:AAEBGQ4A8BCBAQAACBiAMAAAAAgGAAAA","323":"b:AAAAAQwAwACAAQAAABgAMAAAAAgCAAAA","328":[28,8,107,53,119,60,64,125,161],"342":"b:AAEBGQ4A8BCBAQAACBiAMAAAAAAGAAAA","356":"b:AAEAGQIA8BCBAQAACBiAIAAAAAAGAAAA","361":[161],"366":"b:AAEBGQ4A8BCBAQAACBiAMAAAAAAGAAAA","370":[35],"378":[8],"389":"b:AAABEA4A8AABAQAAAAgAIAAAAAAAAAAA","399":"b:AAABEA4A8AABAQAAAAgAIAAAAAACAAAA","407":[33,34,54,55,64,125,72],"414":"b:AAABEA4A8AABAQAAAAgAIAAAAAACAAAA","429":[119],"433":[161],"437":"b:AAABEA4A8AABAQAAAAgAIAAAAAAAAAAA","438":"b:AAABEA4A8AABAQAAAAgAIAAAAAAAAAAA","451":[28,8,33,99,54,55,60,64,125],"458":[119],"465":[8],"471":[161],"475":[107,119,161],"489":[8,35],"538":"b:AAEBGQ4A8BCBAQAACBiAMAAAAAgGAAAA","588":"b:AAAACQwA8ACAAQAAABgAMAAAAAgGAAAA","604":"b:AAEBGQ4A8BCBAQAACBiAMAAAAAgGAAAA","622":"b:AAEAGQ4A8BCBAQAACBiAIAAAAAAGAAAA","637":"b:AAEAGQ4A8BCBAQAACBiAIAAAAAAGAAAA","647":[8],"648":"b:AAEAGQ4A8BCBAQAACBiAIAAAAAAGAAAA","650":"b:AAEAGQ4A8BCBAQAACBiAIAAAAAAGAAAA","668":[161],"686":"b:AAEAGQIA8BCBAQAACBiAIAAAAAAGAAAA","692":"b:AAEAGQIA8BCBAQAACBiAIAAAAAAGAAAA","719":[28,8,33,99,54,55,60,64,125],"730":"b:AAAACQwA8ACAAQAAABgAAAAAAAAGAAAA","762":[119],"766":[119],"777":[119,161]},"packages/frontend/editor-ui/src/app/composables/useAutoScrollOnDrag.ts":{"13":"b:AAAACwwA8ACAGWAAABgAMAAAAAgGAAAA","20":"b:AAAACwwA8ACAGWAAABgAMAAAAAgGAAAA","99":[52],"103":"b:AAAACwwA8ACAGWAAABgAMAAAAAgGAAAA","108":"b:AAAACwwA8ACAGWAAABgAMAAAAAgGAAAA","117":"b:AAAACQwA8ACAEUAAABgAMAAAAAgGAAAA","125":"b:AAAAAQwAwACAEUAAABgAMAAAAAgCAAAA"},"packages/frontend/editor-ui/src/app/composables/useDependencies.ts":{"14":"b:AAEBGw4A8BCBGWAACBgAMAAAAAgGAAAA","18":"b:AAEBGQ4A8BCBAQAACBgAIAAAAAAGAAAA","59":[33,108,55,125,161,162],"67":"b:AAEAGQ4A8BCBAQAACBgAIAAAAAAGAAAA","70":[33,108,55,125,161,162],"73":"b:AAEAGQ4A8BCBAQAACBgAIAAAAAAGAAAA"},"packages/frontend/editor-ui/src/app/composables/useLatestFetch.ts":{"4":"b:AAAACwwA8ACAGWAAABgAMAAAAAgGAAAA","6":"b:AAAACwwA8ACAGWAAABgAMAAAAAAGAAAA"},"packages/frontend/editor-ui/src/app/composables/useN8nLocalStorage.ts":{"26":"b:AAEBGQ4A8BCBAQAACBiAMAAAAAgGAAAA","29":"b:AAAACQwA8ACAAQAAABgAMAAAAAgGAAAA","33":[35],"58":"b:AAAACQwA8ACAAQAAABgAMAAAAAgGAAAA"},"packages/frontend/editor-ui/src/app/composables/useResourcesListI18n.ts":{"8":"b:AAEBGQ4A8BCBAQAACBiAMAAAAAgGAAAA","15":"b:AAEAGQ4A8BCBAQAACBiAIAAAAAAGAAAA"},"packages/frontend/editor-ui/src/app/layouts/DefaultLayout.vue":{"8":"b:AAEBGw8A8BCBG2AACBiAMAABAAgGAAAA","10":"b:AAEBGw8A8BCBG2AACBiAMAABAAgGAAAA"},"packages/frontend/editor-ui/src/app/utils/modules/tabUtils.ts":{"11":"b:AAEBGQ8A8BCBAQAACBiAMAAAAAgGAAAA","34":"b:AAEBGQ8A8BCBAQAACBiAMAAAAAgGAAAA","38":"b:AAEBGQ8A8BCBAQAACBiAMAAAAAgGAAAA"},"packages/frontend/editor-ui/src/app/views/WorkflowsView.vue":{"183":"b:AAAACwwA8ACAGWAAABgAMAAAAAgGAAAA","235":[52,107,53],"280":[52,107,53],"282":[52,107,53],"287":[52,107,53],"289":[52,107,53],"294":"b:AAAACQwA8ACAAQAAABgAMAAAAAgGAAAA","298":"b:AAAACwwA8ACAGWAAABgAMAAAAAgGAAAA","302":"b:AAAACQAA8ACAAQAAABgAAAAAAAAGAAAA","304":"b:AAAACQAA8ACAAQAAABgAAAAAAAAGAAAA","308":"b:AAAACQAA8ACAAQAAABgAAAAAAAAGAAAA","312":"b:AAAACQAA8ACAAQAAABgAAAAAAAAGAAAA","319":"b:AAAACwwA8ACAGWAAABgAMAAAAAgGAAAA","323":[27,52,107,53,54,55,72,161],"327":[52,107,53,54,55,72,161],"333":"b:AAAACQAA8ACAAQAAABgAAAAAAAAGAAAA","337":"b:AAAACwwA8ACAGWAAABgAMAAAAAgGAAAA","341":"b:AAAACQAA8ACAAQAAABgAAAAAAAAGAAAA","345":"b:AAAACwwA8ACAGWAAABgAMAAAAAgGAAAA","350":"b:AAAACwwA8ACAGWAAABgAMAAAAAgGAAAA","357":[52,107,53,54,55,72,161],"363":"b:AAAACQAA8ACAAQAAABgAAAAAAAAGAAAA","369":[52,107,53],"375":[52,107,53],"380":[52,107,53,54,55,72,161],"386":"b:AAAACQAA8ACAAQAAABgAAAAAAAAGAAAA","388":[52,107,53,54,55,72,161],"392":[52,107,53,54,55,72,161],"405":[52,107,53,54,55,72,161],"430":[52,107,53,54,55,72,161],"453":[52,107,53,54,55,72,161],"465":[52,107,53,54,55,72,161],"493":[52,107,53,54,55,72,161],"502":[52,107,53],"506":"b:AAAACQwA8ACAAQAAABgAMAAAAAgGAAAA","507":"b:AAAACQAA8ACAAQAAABgAAAAAAAAGAAAA","545":[161],"560":"b:AAAACQwA8ACAAQAAABgAMAAAAAgGAAAA","574":[27,52,107,53,54,55,72,161],"585":[27,52,107,53,54,55,72],"594":"b:AAAACQwA8ACAAQAAABgAMAAAAAgGAAAA","600":"b:AAAACQwA8ACAAQAAABgAMAAAAAgGAAAA","604":"b:AAAACQwA8ACAAQAAABgAMAAAAAgGAAAA","617":"b:AAAACQwA8ACAAQAAABgAMAAAAAgGAAAA","621":"b:AAAACwwA8ACAGWAAABgAMAAAAAgGAAAA","626":"b:AAAACQwA8ACAAQAAABgAMAAAAAgGAAAA","638":"b:AAAACwwA8ACAGWAAABgAMAAAAAgGAAAA","648":"b:AAAACwwA8ACAGWAAABgAMAAAAAgGAAAA","650":[52,107,53],"669":[54,55,161],"679":[53],"712":"b:AAAACQwA8ACAAQAAABgAMAAAAAgGAAAA","722":"b:AAAACQwA4AAAAQAAAAgAMAAAAAACAAAA","726":"b:AAAACQwA4AAAAQAAAAgAMAAAAAACAAAA","734":"b:AAAACwwA8ACAGWAAABgAMAAAAAgGAAAA","754":"b:AAAAAQwAwACAEUAAABgAMAAAAAgCAAAA","772":"b:AAAACwwAwACAGWAAABAAAAAAAAAGAAAA","788":"b:AAAACwwA8ACAGWAAABgAMAAAAAgGAAAA","845":"b:AAAACwwA8ACAGWAAABgAMAAAAAgGAAAA","859":[161],"893":"b:AAAACQAA8ACAAQAAABgAAAAAAAAGAAAA","894":[52,107,53],"912":"b:AAAACQAA8ACAAQAAABgAAAAAAAAGAAAA","913":"b:AAAACQAA8ACAAQAAABgAAAAAAAAGAAAA","946":"b:AAAACwwA8ACAGWAAABgAMAAAAAgGAAAA","967":[34,52,107,53,54,55,72,161],"975":[107,161],"986":"b:AAAACQwA8ACAAQAAABgAMAAAAAgGAAAA","1016":[34,52,107,53,54,55,72,161],"1056":"b:AAAACwwA8ACAGWAAABgAMAAAAAgGAAAA","1061":"b:AAAACwwA8ACAGWAAABgAMAAAAAgGAAAA","1090":[161],"1132":[55],"1145":[55],"1268":[161],"1269":[161],"1278":[52,53],"1279":[52,53],"1285":[53],"1309":[52,107,53],"1332":[52],"1354":[52],"1362":[52],"1381":[52],"1413":[107,53],"1644":[107,53],"1705":[107,53],"1743":[107],"1789":[53],"1824":[107,53],"1843":[53],"1859":[52,53],"1986":[53,54,55],"2013":[54,55],"2060":[52,53],"2073":[52],"2131":[53],"2210":"b:AAAACQwA8ACAAQAAABgAMAAAAAgGAAAA","2247":"b:AAAACQAA8ACAAQAAABgAAAAAAAAGAAAA","2314":"b:AAAACQAA8ACAAQAAABgAAAAAAAAGAAAA","2350":"b:AAAACQAA8ACAAQAAABgAAAAAAAAGAAAA","2360":[52,107,53],"2363":[52,107,53],"2364":[52,107,53],"2404":"b:AAAACQAA8ACAAQAAABgAAAAAAAAGAAAA","2407":"b:AAAACQAA8ACAAQAAABgAAAAAAAAGAAAA","2408":"b:AAAACQAA8ACAAQAAABgAAAAAAAAGAAAA","2446":"b:AAAACQwA4AAAAQAAAAgAMAAAAAACAAAA","2465":[161],"2478":[161],"2493":[161],"2509":[161],"2513":"b:AAAACQAA8ACAAQAAABgAAAAAAAAGAAAA","2521":[27,52,107,53,54,55,72],"2523":[27,52,107,53,54,55,72]},"packages/frontend/editor-ui/src/experiments/credentialsAppSelection/stores/credentialsAppSelection.store.ts":{"14":"b:AAAACwwA8ACAGWAAABgAMAAAAAgGAAAA","23":[75,25,76,34,35,55,85,86,72],"25":[75,25,76,34,35,55,85,86,72],"29":[75,25,76,34,35,55,85,86,72],"35":[75,25,76,34,35,55,85,86,72]},"packages/frontend/editor-ui/src/experiments/personalizedTemplates/stores/personalizedTemplates.store.ts":{"66":"b:AAAACwwA8ACAGWAAABgAMAAAAAgGAAAA","91":"b:AAAACQwA8ACAAQAAABgAMAAAAAAGAAAA","164":"b:AAAACwwA8ACAGWAAABgAMAAAAAgGAAAA"},"packages/frontend/editor-ui/src/experiments/personalizedTemplatesV3/stores/personalizedTemplatesV3.store.ts":{"13":"b:AAAACwwA8ACAGWAAABgAMAAAAAgGAAAA","27":"b:AAAACQwA4AAAAQAAABgAMAAAAAAGAAAA","36":"b:AAAACwwA8ACAGWAAABgAMAAAAAgGAAAA","130":"b:AAAACwwA8ACAGWAAABgAMAAAAAgGAAAA"},"packages/frontend/editor-ui/src/experiments/templateRecoV2/stores/templateRecoV2.store.ts":{"14":"b:AAAACwwA8ACAGWAAABgAMAAAAAgGAAAA","21":"b:AAAACQwA4AAAAQAAABgAMAAAAAAGAAAA"},"packages/frontend/editor-ui/src/features/agents/composables/useAgentPermissions.ts":{"34":"b:AAEBGw8A8BCBGWAACBiAMAAAAAgGAAAA"},"packages/frontend/editor-ui/src/features/ai/mcpAccess/mcp.store.ts":{"44":"b:AAAACwwA8ACAGWAAABgAMAAAAAgGAAAA"},"packages/frontend/editor-ui/src/features/collaboration/projects/components/ProjectCreateResource.vue":{"14":"b:AAEBGQ4A8BCBAQAACBiAMAAAAAgGAAAA","23":"b:AAEBGQ4A8BCBAQAACBiAMAAAAAgGAAAA","34":[35,107,53],"35":"b:AAEBGQ4A8BCBAQAACBiAMAAAAAgGAAAA"},"packages/frontend/editor-ui/src/features/collaboration/projects/components/ProjectHeader.vue":{"46":"b:AAEAEQkA8BAAAQAACBgAMAAAAAACAAAA","48":"b:AAEBGQ8A8BCBAQAACBiAMAAAAAgGAAAA","50":"b:AAEAEQkA8BAAAQAACBgAMAAAAAACAAAA","70":"b:AAEAEQkA8BAAAQAACBgAMAAAAAACAAAA","82":"b:AAEBGQ8A8BCBAQAACBiAMAAAAAgGAAAA","86":"b:AAEBEQ8A8BABAQAACBgAMAAAAAgCAAAA","90":"b:AAEBGQ8A8BCBAQAACBiAMAAAAAgGAAAA","107":"b:AAEBEQ8A8BABAQAACBgAMAAAAAgCAAAA","110":[119],"113":[119],"117":"b:AAABEQ4AwAABAQAAABAAIAAAAAACAAAA","121":"b:AAEBGQ8A8BCBAQAACBiAMAAAAAgGAAAA","128":"b:AAEBGQ4A8BCBAQAACBiAMAAAAAgGAAAA","135":"b:AAEBGQ8A8BCBAQAACBiAMAAAAAgGAAAA","149":"b:AAEBGQ8A8BCBAQAACBiAMAAAAAgGAAAA","162":"b:AAAACQwA8ACAAQAAABgAMAAAAAgGAAAA","172":[16,28,8,33,99,54,55,60,64,125],"182":[34,35],"192":[119],"210":"b:AAEBGQ4A8BCBAQAACBiAMAAAAAgGAAAA","217":"b:AAEBGQ4A8BCBAQAACBiAMAAAAAgGAAAA","233":"b:AAEBGQ4A8BCBAQAACBiAMAAAAAgGAAAA","309":"b:AAEBGQ8A8BCBAQAACBiAMAAAAAgGAAAA","315":[16,28,8,33,107,124,64,125,72,161],"325":[107,124,125,72,161],"336":[16,28,8,33,107,124,64,125,72,161],"349":[107,124,125,72,161],"359":[16,28,8,33,64,125],"371":[107,53],"374":[34,35],"380":[119],"390":"b:AAEBGQ8A8BCBAQAACBiAMAAAAAgGAAAA","400":"b:AAEBGQ8A8BCBAQAACBiAMAAAAAgGAAAA","420":"b:AAEAEQkA8BAAAQAACBgAMAAAAAACAAAA","428":"b:AAEAEQkA8BAAAQAACBgAMAAAAAACAAAA","438":"b:AAEBGQ8A8BCBAQAACBiAMAAAAAgGAAAA","452":[108],"463":"b:AAEBEA4AIAABAQAAAAiAMAAAAAACAAAA","480":"b:AAEBGQ8A8BCBAQAACBiAMAAAAAgGAAAA","483":"b:AAABGQ4AwBCBAQAAABCAIAAAAAgGAAAA","488":[108],"515":"b:AAEBGQ4A8BCBAQAACBiAMAAAAAgGAAAA","522":[35,107,53],"523":"b:AAEBGQ4A8BCBAQAACBiAMAAAAAgGAAAA","528":"b:AAEBEA4AAAABAQAAAAiAMAAAAAACAAAA"},"packages/frontend/editor-ui/src/features/collaboration/projects/components/ProjectTabs.vue":{"23":"b:AAEBGQ8A8BCBAQAACBiAMAAAAAgGAAAA","37":"b:AAEBGQ8A8BCBAQAACBiAMAAAAAgGAAAA","39":"b:AAEBGQ8A8BCBAQAACBiAMAAAAAgGAAAA","45":"b:AAEBEQ8A8BABAQAACBgAMAAAAAgCAAAA","47":"b:AAEBGQ8A8BCBAQAACBiAMAAAAAgGAAAA","90":"b:AAEBGQ8A8BCBAQAACBiAMAAAAAgGAAAA","103":"b:AAEBGQ8A8BCBAQAACBiAMAAAAAgGAAAA","144":"b:AAEBGQ8A8BCBAQAACBiAMAAAAAgGAAAA","145":"b:AAEBGQ8A8BCBAQAACBiAMAAAAAgGAAAA","161":"b:AAEBGQ8A8BCBAQAACBiAMAAAAAgGAAAA"},"packages/frontend/editor-ui/src/features/core/folders/composables/useFolders.ts":{"12":[52],"16":[52],"20":"b:AAAACwwA8ACAGWAAABgAMAAAAAgGAAAA","25":[52,107,53],"29":[107,53],"63":[52],"80":[52],"86":[52,107,53],"101":[35,52,107,53,54,55,72,161],"109":[52],"130":[52,107,53]},"packages/frontend/editor-ui/src/features/execution/insights/components/InsightsSummary.vue":{"33":"b:AAABGQ4AwBCBAQAAABCAIAAAAAAGAAAA","46":"b:AAABGQ4AwBCBAQAAABCAIAAAAAAGAAAA","54":"b:AAABGQ4AwBCBAQAAABCAIAAAAAAGAAAA","56":"b:AAABGQ4AwBCBAQAAABCAIAAAAAAGAAAA","59":"b:AAABGQ4AwBCBAQAAABCAIAAAAAAGAAAA","60":"b:AAABGQ4AwBCBAQAAABCAIAAAAAAGAAAA","118":"b:AAABGQ4AwBCBAQAAABCAIAAAAAAGAAAA","133":"b:AAABGQ4AwBCBAQAAABCAIAAAAAAGAAAA"},"packages/frontend/editor-ui/src/features/execution/insights/insights.api.ts":{"16":"b:AAABOQ4AwBCBGXAgABCAIAAAAAAGAAAA","34":"b:AAABOQ4AwBCBGXAgABCAIAAAAAAGAAAA"},"packages/frontend/editor-ui/src/features/execution/insights/insights.constants.ts":{"21":"b:AAABOQ4AwBCBGXAgABCAIAAAAAAGAAAA","22":"b:AAABOQ4AwBCBGXAgABCAIAAAAAAGAAAA","23":"b:AAABOQ4AwBCBGXAgABCAIAAAAAAGAAAA","24":"b:AAABOQ4AwBCBGXAgABCAIAAAAAAGAAAA","25":"b:AAABOQ4AwBCBGXAgABCAIAAAAAAGAAAA"},"packages/frontend/editor-ui/src/features/execution/insights/insights.store.ts":{"12":"b:AAEBOw4A8BCBGXAgCBiANAAAAAgGAAAA","17":"b:AAABOw4AwBCBGXAgABCAJAAAAAAGAAAA","21":"b:AAABOQ4AwBCBGXAgABCAIAAAAAAGAAAA","25":"b:AAABOw4AwBCBGXAgABCAJAAAAAAGAAAA","30":"b:AAABOQ4AwBCBGXAgABCAIAAAAAAGAAAA"},"packages/frontend/editor-ui/src/features/execution/insights/insights.utils.ts":{"17":"b:AAABOQ4AwBCBGXAgABCAIAAAAAAGAAAA","19":"b:AAABOQ4AwBCBGXAgABCAIAAAAAAGAAAA","20":"b:AAABOQ4AwBCBGXAgABCAIAAAAAAGAAAA","23":"b:AAABOQ4AwBCBGXAgABCAIAAAAAAGAAAA","24":"b:AAABOQ4AwBCBGXAgABCAIAAAAAAGAAAA","52":"b:AAABOQ4AwBCBGXAgABCAIAAAAAAGAAAA","54":"b:AAABOQ4AwBCBGXAgABCAIAAAAAAGAAAA","79":"b:AAABGQ4AwBCBAQAAABCAIAAAAAAGAAAA","96":"b:AAABGQ4AwBCBAQAAABCAIAAAAAAGAAAA","117":"b:AAABGQ4AwBCBAQAAABCAIAAAAAAGAAAA"},"packages/frontend/editor-ui/src/features/ndv/panel/components/WireMeUp.vue":{"8":"b:gEEABAAAiACAAAAAAAACIAQACgAAAAAA"},"packages/frontend/editor-ui/src/features/shared/tags/components/TagsContainer.vue":{"20":[52,116,71,154,72,155,160,162],"37":[52,116,71,154,72,155,160,162],"41":[52,116,71,154,72,155,160,162],"42":[52,116,71,154,160],"48":[52,116,71,154,160],"55":[52,116,71,154,160],"63":[52,116,71,154,160],"81":[52,116,71,154,72,155,160,162],"87":[52,116,71,154,72,155,160,162],"95":[52,116,71,154,160],"101":[52,154],"112":[52,116,71,154,72,155,160,162],"117":[52,154,155],"120":[52,116,71,154,72,155,160,162],"131":[52,116,71,154,72,155,160,162],"134":[52,116,71,154,160],"137":[52,154],"152":[52,116,71,154,160]},"packages/frontend/editor-ui/src/features/shared/tags/components/WorkflowTagsContainer.vue":{"16":[52,116,71,154,72,155,160,162],"22":[52,116,71,154,72,155,160,162],"24":[52,154],"26":[52,116,71,154,72,155,160,162]},"packages/frontend/editor-ui/src/features/workflows/composables/useWorkflowsEmptyState.ts":{"19":"b:AAAACwwAwACAGWAAABAAAAAAAAAGAAAA","20":"b:AAAACwwA8ACAGWAAABgAMAAAAAgGAAAA","22":"b:AAAACwwA8ACAGWAAABgAMAAAAAgGAAAA","28":[75,25,76,34,35,55,85,86,72],"32":[75,25,76,34,35,55,85,86,72],"40":[75,25,76,34,35,55,85,86,72],"42":[85]},"packages/frontend/editor-ui/src/features/workflows/readyToRun/components/ReadyToRunButton.vue":{"13":"b:AAEBGQ4A8BCBAQAACBiAMAAAAAgGAAAA","25":"b:AAEBGQ4A8BCBAQAACBiAMAAAAAgGAAAA","31":"b:AAEBGQ4A8BCBAQAACBiAMAAAAAgGAAAA","53":"b:AAEBGQ4A8BCBAQAACBiAMAAAAAgGAAAA"},"packages/@n8n/api-types/src/schemas/node-version.schema.ts":{"7":[1,26,55,145,148,149,152,153]},"packages/cli/src/controllers/dynamic-node-parameters.controller.ts":{"16":[1,26,55,145,148,149,152,153],"19":[1,26,55,145,148,149,152,153],"68":[1,26,55,145,148,149,152,153],"106":[1,26,55,145,148,149,152,153],"134":[1,26,55,145,148,149,152,153],"158":[1,26,55,145,148,149,152,153]},"packages/cli/src/services/dynamic-node-parameters.service.ts":{"65":"b:OkAABAAAgAAAAAAAAAAAAAAAMgMAAAAA","73":"b:OkAABAAAgAAAAAAAAAAAAAAAMgMAAAAA","113":"b:OkAABAAAgAAAAAAAAAAAAAAAMgMAAAAA","114":"b:OkAABAAAgAAAAAAAAAAAAAAAMgMAAAAA","123":"b:OkAABAAAgAAAAAAAAAAAAAAAMgMAAAAA","132":"b:OkAABAAAgAAAAAAAAAAAAAAAMgMAAAAA","144":"b:OkAABAAAgAAAAAAAAAAAAAAAMgMAAAAA","155":"b:OkAABAAAgAAAAAAAAAAAAAAAMgMAAAAA","189":"b:OkAABAAAgAAAAAAAAAAAAAAAMgMAAAAA","251":"b:OkAABAAAgAAAAAAAAAAAAAAAMgMAAAAA","262":"b:OkAABAAAgAAAAAAAAAAAAAAAMgMAAAAA","266":"b:OkAABAAAgAAAAAAAAAAAAAAAMgMAAAAA","280":"b:OkAABAAAgAAAAAAAAAAAAAAAMgMAAAAA","286":"b:OkAABAAAgAAAAAAAAAAAAAAAMgMAAAAA","298":"b:OkAABAAAgAAAAAAAAAAAAAAAMgMAAAAA","304":"b:OkAABAAAgAAAAAAAAAAAAAAAMgMAAAAA","317":"b:OkAABAAAgAAAAAAAAAAAAAAAMgMAAAAA","330":"b:OkAABAAAgAAAAAAAAAAAAAAAMgMAAAAA","356":"b:OkAABAAAgAAAAAAAAAAAAAAAMgMAAAAA","399":"b:OkAABAAAgAAAAAAAAAAAAAAAMgMAAAAA","403":"b:OkAABAAAgAAAAAAAAAAAAAAAMgMAAAAA","429":"b:OkAABAAAgAAAAAAAAAAAAAAAMgMAAAAA","438":"b:OkAABAAAgAAAAAAAAAAAAAAAMgMAAAAA","450":"b:OkAABAAAgAAAAAAAAAAAAAAAMgMAAAAA","454":"b:OkAABAAAgAAAAAAAAAAAAAAAMgMAAAAA"},"packages/cli/src/services/workflow-loader.service.ts":{"7":[55,152,153],"9":[55,152,153]},"packages/core/src/execution-engine/active-workflow-triggers.ts":{"37":"b:AACAAAEEBggAAAAAAAAAgAAAAFvIB+AD","49":"b:AACAAAEEBggAAAAAAAAAgAAAAFvIB+AD","68":"b:AACAAAEEBggAAAAAAAAAgAAAAFvIB+AD","82":"b:AACAAAEEBggAAAAAAAAAgAAAAFvIB+AD","89":"b:AACAAAEEBggAAAAAAAAAgAAAAFvIB+AD","96":"b:AACAAAEEBggAAAAAAAAAgAAAAFvIB+AD","105":"b:AACAAAEEBggAAAAAAAAAgAAAAFvIB+AD","121":"b:AACAAAEEBggAAAAAAAAAgAAAAFvIB+AD","134":"b:AACAAAEEBggAAAAAAAAAgAAAAFvIB+AD","154":"b:AACAAAEEBggAAAAAAAAAgAAAAFvIB+AD","167":"b:AACAAAEEBggAAAAAAAAAgAAAAFvIB+AD","168":"b:AACAAAEEBggAAAAAAAAAgAAAAFvIB+AD","194":"b:AACAAAEEBggAAAAAAAAAgAAAAFvIB+AD","295":"b:AACAAAEEBggAAAAAAAAAgAAAAFvIB+AD","327":"b:AACAAAEEBggAAAAAAAAAgAAAAFvIB+AD","361":"b:AACAAAEEBggAAAAAAAAAgAAAAFvIB+AD","388":"b:AACAAAEEBggAAAAAAAAAgAAAAFvIB+AD","441":"b:AACAAAEEBggAAAAAAAAAgAAAAFvIB+AD","453":"b:AACAAAEEBggAAAAAAAAAgAAAAFvIB+AD","487":"b:AACAAAEEBggAAAAAAAAAgAAAAFvIB+AD","496":"b:AACAAAEEBggAAAAAAAAAgAAAAFvIB+AD","510":"b:AACAAAEEBggAAAAAAAAAgAAAAFvIB+AD","533":"b:AACAAAEEBggAAAAAAAAAgAAAAFvIB+AD","543":"b:AACAAAEEBggAAAAAAAAAgAAAAFvIB+AD","554":"b:AACAAAEEBggAAAAAAAAAgAAAAFvIB+AD","571":"b:AACAAAEEBggAAAAAAAAAgAAAAFvIB+AD"},"packages/core/src/execution-engine/node-execution-context/local-load-options-context.ts":{"16":[55,152,153],"23":[55,152,153],"42":[55,152,153],"69":[55,152,153]},"packages/core/src/execution-engine/node-execution-context/workflow-node-context.ts":{"16":[55,152,153],"22":[55,152,153]},"packages/core/src/execution-engine/workflow-active-triggers-state.ts":{"14":"b:AAAAAAAEBggAAAAAAAAAgAAAAAvAB6AD","20":"b:AAAAAAAEBggAAAAAAAAAgAAAAAvAB6AD","25":"b:AAAAAAAEBggAAAAAAAAAgAAAAAvAB6AD","30":"b:AAAAAAAEBggAAAAAAAAAgAAAAAvAB6AD","35":"b:AAAAAAAEBggAAAAAAAAAgAAAAAvAB6AD","43":"b:AAAAAAAEBggAAAAAAAAAgAAAAAvAB6AD","48":"b:AAAAAAAEBggAAAAAAAAAgAAAAAvAB6AD","52":"b:AAAAAAAEBggAAAAAAAAAgAAAAAvAB6AD","63":"b:AAAAAAAEBggAAAAAAAAAgAAAAAvAB6AD","68":"b:AAAAAAAEBggAAAAAAAAAgAAAAAvAB6AD","73":"b:AAAAAAAEBggAAAAAAAAAgAAAAAvAB6AD","78":"b:AAAAAAAEBggAAAAAAAAAgAAAAAvAB6AD","83":"b:AAAAAAAEBggAAAAAAAAAgAAAAAvAB6AD","88":"b:AAAAAAAEBggAAAAAAAAAgAAAAAvAB6AD","93":"b:AAAAAAAEBggAAAAAAAAAgAAAAAvAB6AD"},"packages/nodes-base/nodes/ExecuteWorkflow/ExecuteWorkflow/ExecuteWorkflow.node.ts":{"316":[55,181,152,184,153]},"packages/nodes-base/nodes/ExecuteWorkflow/ExecuteWorkflow/GenericFunctions.ts":{"10":[55,181,152,184,153],"29":[55,181,152,184,153]},"packages/nodes-base/nodes/ExecuteWorkflow/ExecuteWorkflow/methods/localResourceMapping.ts":{"5":[55,181,152,184,153]},"packages/nodes-base/nodes/ExecuteWorkflow/ExecuteWorkflowTrigger/ExecuteWorkflowTrigger.node.ts":{"223":[55,181,152,184,153],"229":[55,181,152,184,153],"248":[55,181,152,184,153],"249":[55,181,152,184,153],"251":[55,181,152,184,153],"254":[55,181,152,184,153]},"packages/nodes-base/nodes/NoOp/NoOp.node.ts":{"26":"b:ABAAAIAAgwhQAAAAACAAAABwCALAB4AC"},"packages/nodes-base/utils/workflow-backtracking.ts":{"24":[55,181,151,152,184,153],"44":[55,181,151,152,184,153]},"packages/nodes-base/utils/workflowInputsResourceMapping/GenericFunctions.ts":{"27":[55,181,152,184,153],"29":[55,181,152,184,153],"74":[55,181,152,184,153],"81":[55,181,152,184,153],"121":[55,181,152,184,153],"126":[55,181,152,184,153],"147":[55,181,152,184,153],"155":[55,181,152,184,153],"157":[55,181,152,184,153],"160":[55,181,152,184,153],"168":[55,181,152,184,153],"183":[55,181,152,184,153]},"packages/workflow/src/node-parameters/path-utils.ts":{"10":[55,152,153]},"packages/frontend/@n8n/design-system/src/components/N8nIcon/nodes/execute-sub-workflow.svg":{"11":[46,151,152,153]},"packages/frontend/@n8n/design-system/src/components/N8nIcon/nodes/no-operation.svg":{"11":"b:AFEAEIAQiQRQAAAAACAIMEBwCBIFAAAA"},"packages/frontend/editor-ui/src/app/composables/useAutocompleteTelemetry.ts":{"12":"b:wuUAAAAABBJSAAAAAKAiAADwOgIAAAAA","57":[60,140,141,142,143,14,147,148,149],"61":[60,140,141,142,143,14,147,148,149],"96":[60,140,141,142,143,14,147,148,149],"103":"b:wuUAAAAABBJSAAAAAKAiAADwOgIAAAAA","109":"b:wuUAAAAABBJSAAAAAKAiAADwOgIAAAAA"},"packages/frontend/editor-ui/src/features/ndv/parameters/components/ExpressionFunctionIcon.vue":{"8":"b:giUAAAAAABBSAAAAAKACAABwMgIAAAAA"},"packages/frontend/editor-ui/src/features/ndv/parameters/components/ExpressionParameterInput.vue":{"26":"b:wuUAAAAABBJSAAAAAKAiAADwOgIAAAAA","67":"b:wuUAAAAABBJSAAAAAKAiAADwOgIAAAAA","68":"b:wuUAAAAABBJSAAAAAKAiAADwOgIAAAAA","78":"b:AEAAAAAAABBAAAAAAAAAAADwOAAAAAAA","84":"b:AEAAAAAAABBAAAAAAAAAAADwOAAAAAAA","89":[60,140,141,142,143,14,70,148,149],"97":"b:wuUAAAAAABJQAAAAAIAAAADwMgAAAAAA","100":"b:QuAAAAAAABBQAAAAAAAAAADwMAAAAAAA","128":"b:QuAAAAAABBBQAAAAAIAAAADwOgAAAAAA","137":"b:wuUAAAAABBJSAAAAAKAiAADwOgIAAAAA","148":[141],"160":[141],"166":[141],"175":[141],"181":"b:wuUAAAAABBJSAAAAAKAiAADwOgIAAAAA","185":"b:wqUAAAAAABJSAAAAAIACAABQAAAAAAAA","189":[141],"196":"b:wuUAAAAAABJQAAAAAIAAAADwMgAAAAAA","199":"b:wuUAAAAABBJSAAAAAKAiAADwOgIAAAAA","219":"b:wuUAAAAABBJSAAAAAKAiAADwOgIAAAAA"},"packages/frontend/editor-ui/src/features/ndv/parameters/components/ResourceMapper/ResourceMapper.vue":{"51":[148,149,152,153],"91":[149],"111":[148,149,152,153],"113":[149],"131":[149],"148":[148,149,152,153],"150":[149],"162":[55,148,149,152,153],"198":[149],"238":[149],"242":[55,148,149,152,153],"249":[148,149,152,153],"253":[148,149,152,153],"257":[55,148,149,152,153],"261":[55,148,149,152,153],"269":[55,148,149,152,153],"279":[149],"289":[149],"305":[55,148,149,152,153],"308":[149],"316":[55,152,153],"323":[55,148,149,152,153],"351":[55,148,149,152,153],"376":[55,148,149,152,153],"399":[55,148,149,152,153],"409":[149],"410":[149],"443":[55,148,149,152,153],"446":[149],"466":[55,148,149,152,153],"479":[55,148,149,152,153],"485":[149],"496":[149],"516":[149],"522":[149],"530":[149],"531":[149],"548":[149],"595":[55,148,149,152,153],"607":[55,148,149,152,153],"613":[149],"624":[55,148,149,152,153],"657":[149],"660":[55,152,153],"693":[55,152,153]},"packages/frontend/editor-ui/src/features/ndv/parameters/components/WorkflowSelectorParameterInput/WorkflowSelectorParameterInput.vue":{"59":[55,152,153],"110":[55,152,153],"121":[55,152,153],"131":[55,152,153],"145":[55,152,153],"157":[55,152,153],"165":[55,152,153],"169":[55,152,153],"176":[152,153],"194":[153],"204":[55,153],"209":[55,153],"217":[153],"230":[55,152,153],"246":[55,152,153],"253":[152,153],"257":[55,152,153],"258":[153],"267":[55,152,153],"268":[55,152,153],"280":[55,152,153],"287":[55,153],"292":[55,153],"325":[55,152,153],"363":[55,153],"371":[55,152,153],"389":[55,152,153],"397":[55,152,153],"414":[55,152,153],"474":[55,152,153]},"packages/frontend/editor-ui/src/features/ndv/parameters/composables/useResourceLocatorDropdown.ts":{"8":[55,152,153],"15":[55,153],"23":[55,153],"27":[55,153]},"packages/frontend/editor-ui/src/features/ndv/parameters/composables/useResourceLocatorModes.ts":{"15":[55,152,153],"21":[55,152,153],"34":[55,152,153],"35":[55,152,153],"37":[153],"51":[55,152,153]},"packages/frontend/editor-ui/src/features/ndv/parameters/composables/useWorkflowResourcesLocator.ts":{"13":[55,152,153],"17":[55,153],"29":[55,152,153],"31":[55,152,153],"40":[152,153],"48":[55,153],"56":[55,152,153],"61":[55,152,153],"71":[55,152,153],"107":[55,152,153],"113":[153],"118":[55,153]},"packages/frontend/editor-ui/src/features/shared/editors/components/InlineExpressionEditor/InlineExpressionEditorInput.vue":{"26":"b:wuUAAAAABBJSAAAAAKAiAADwOgIAAAAA","39":"b:wuUAAAAABBJSAAAAAKAiAADwOgIAAAAA","51":"b:wuUAAAAABBJSAAAAAKAiAADwOgIAAAAA","75":"b:wuUAAAAABBJSAAAAAKAiAADwOgIAAAAA","81":"b:QuAAAAAABBBQAAAAAIAAAADwOgAAAAAA","88":"b:wuUAAAAABBJSAAAAAKAiAADwOgIAAAAA","97":"b:AEAAAAAAABBAAAAAAAAAAADwOAAAAAAA","104":"b:AEAAAAAAABBAAAAAAAAAAADwOAAAAAAA","107":[60,140,141,142,143,14,147,148,149],"118":"b:wuUAAAAABBJSAAAAAKAiAADwOgIAAAAA"},"packages/@n8n/backend-network/src/http/axios/legacy.ts":{"33":[17,30,33,182,104,50,137,139],"53":[17,30,33,182,104,50,137,139],"74":[17,30,33,182,104,50,137,139],"139":[17,30,33,182,104,50,137,139],"188":[17,30,33,182,104,50,137,139],"208":[17,30,33,182,104,50,137,139],"212":[17,30,33,182,104,50,137,139],"221":[17,30,33,182,104,50,137,139],"251":[17,30,33,182,104,50,137,139],"262":[17,30,33,182,104,50,137,139],"306":[17,30,33,182,104,50,137,139],"314":[17,30,33,182,104,50,137,139],"327":[17,30,33,182,104,50,137,139]},"packages/@n8n/backend-network/src/http/binary-buffer.ts":{"16":[0,17,30,3,182,104,105,137],"20":[0,17,30,3,182,104,105,137],"25":[0,17,30,3,182,104,105,137],"29":[0,17,30,3,182,104,105,137],"32":[0,17,30,3,182,104,105,137],"34":[0,17,30,3,182,104,105,137],"42":[0,17,30,3,182,104,105,137],"46":[0,17,30,3,182,104,105,137],"47":[0,17,30,3,182,104,105,137],"48":[0,17,30,3,182,104,105,137],"58":[0,17,30,3,182,104,105,137],"59":[0,17,30,3,182,104,105,137],"61":[0,17,30,3,182,104,105,137],"81":[0,17,30,3,182,104,105,137]},"packages/@n8n/backend-network/src/http/binary-string.ts":{"12":[17,30,182,104,137]},"packages/@n8n/backend-network/src/http/legacy-request.ts":{"42":[17,30,33,182,104,50,137,139]},"packages/core/src/execution-engine/node-execution-context/utils/request-helpers/legacy-request-adapter.ts":{"15":[17,30,33,182,104,50,137,139],"32":[17,30,33,182,104,50,137,139]},"packages/nodes-base/nodes/HttpRequest/GenericFunctions.ts":{"31":"b:ACECSAIACAACAAAAAAEAAAACAAAgAEAA","40":"b:ACECSAIACAACAAAAAAEAAAACAAAgAEAA","44":"b:ACECSAIACAACAAAAAAEAAAACAAAgAEAA","45":"b:ACECSAIACAACAAAAAAEAAAACAAAgAEAA","48":"b:ACECSAIACAACAAAAAAEAAAACAAAgAEAA","54":"b:ACECSAIACAACAAAAAAEAAAACAAAgAEAA","67":"b:ACECSAIACAACAAAAAAEAAAACAAAgAEAA","94":"b:ACECSAIACAACAAAAAAEAAAACAAAgAEAA","129":"b:ACECSAIACAACAAAAAAEAAAACAAAgAEAA","131":"b:ACECSAIACAACAAAAAAEAAAACAAAgAEAA","142":"b:ACECSAIACAACAAAAAAEAAAACAAAgAEAA","238":"b:ACECSAIACAACAAAAAAEAAAACAAAgAEAA","243":"b:ACECSAIACAACAAAAAAEAAAACAAAgAEAA","248":"b:ACECSAIACAACAAAAAAEAAAACAAAgAEAA","255":"b:ACECSAIACAACAAAAAAEAAAACAAAgAEAA","281":"b:ACECSAIACAACAAAAAAEAAAACAAAgAEAA","296":"b:ACECSAIACAACAAAAAAEAAAACAAAgAEAA","298":"b:ACECSAIACAACAAAAAAEAAAACAAAgAEAA","300":"b:ACECSAIACAACAAAAAAEAAAACAAAgAEAA","312":"b:ACECSAIACAACAAAAAAEAAAACAAAgAEAA"},"packages/nodes-base/nodes/HttpRequest/HttpRequest.node.ts":{"9":[17,27,30,8,33,165,104,51,65,13]},"packages/nodes-base/nodes/HttpRequest/V1/HttpRequestV1.node.ts":{"43":[17,27,30,8,33,165,104,51,65,13],"617":[17,27,30,8,33,165,104,51,65,13],"813":[17,27,30,8,33,165,104,51,65,13],"890":[17,27,30,8,33,165,104,51,65,13],"1004":[17,27,30,8,33,165,104,51,65,13],"1011":[17,27,30,8,33,165,104,51,65,13],"1016":[17,27,30,8,33,165,104,51,65,13],"1160":[17,27,30,8,33,165,104,51,65,13]},"packages/nodes-base/nodes/HttpRequest/V2/HttpRequestV2.node.ts":{"45":[17,27,30,8,33,165,104,51,65,13],"630":[17,27,30,8,33,165,104,51,65,13],"875":[17,27,30,8,33,165,104,51,65,13],"952":[17,27,30,8,33,165,104,51,65,13],"1069":[17,27,30,8,33,165,104,51,65,13],"1075":[17,27,30,8,33,165,104,51,65,13],"1080":[17,27,30,8,33,165,104,51,65,13],"1093":[17,27,30,8,33,165,104,51,65,13],"1236":[17,27,30,8,33,165,104,51,65,13]},"packages/nodes-base/nodes/HttpRequest/V3/Description.ts":{"1227":[17,27,30,8,33,165,104,51,65,13]},"packages/nodes-base/nodes/HttpRequest/V3/HttpRequestV3.node.ts":{"54":"b:ACECSAIACAACAAAAAAEAAAACAAAgAEAA","61":"b:ACECSAIACAACAAAAAAEAAAACAAAgAEAA","65":"b:ACECSAIACAACAAAAAAEAAAACAAAgAEAA","72":"b:ACECSAIACAACAAAAAAEAAAACAAAgAEAA","107":"b:ACECSAIACAACAAAAAAEAAAACAAAgAEAA","390":"b:ACECSAIACAACAAAAAAEAAAACAAAgAEAA","507":"b:ACECSAIACAACAAAAAAEAAAACAAAgAEAA","631":"b:ACECSAIACAACAAAAAAEAAAACAAAgAEAA","673":"b:ACECSAIACAACAAAAAAEAAAACAAAgAEAA","733":"b:ACECSAIACAACAAAAAAEAAAACAAAgAEAA","752":"b:ACECSAIACAACAAAAAAEAAAACAAAgAEAA","763":"b:ACECSAIACAACAAAAAAEAAAACAAAgAEAA","768":"b:ACECSAIACAACAAAAAAEAAAACAAAgAEAA","783":"b:ACECSAIACAACAAAAAAEAAAACAAAgAEAA","789":"b:ACECSAIACAACAAAAAAEAAAACAAAgAEAA","812":"b:ACECSAIACAACAAAAAAEAAAACAAAgAEAA","814":"b:ACECSAIACAACAAAAAAEAAAACAAAgAEAA","815":"b:ACECSAIACAACAAAAAAEAAAACAAAgAEAA","959":"b:ACECSAIACAACAAAAAAEAAAACAAAgAEAA","1121":"b:ACECSAIACAACAAAAAAEAAAACAAAgAEAA","1184":"b:ACECSAIACAACAAAAAAEAAAACAAAgAEAA"},"packages/nodes-base/nodes/HttpRequest/V3/utils/binaryData.ts":{"3":[17,27,30,8,33,165,104,51,65,13]},"packages/nodes-base/nodes/HttpRequest/V3/utils/buffer-decoding.ts":{"13":"b:ACECSAIACAACAAAAAAEAAAACAAAgAEAA","36":"b:ACECSAIACAACAAAAAAEAAAACAAAgAEAA"},"packages/nodes-base/nodes/HttpRequest/V3/utils/error-details.ts":{"13":[17,27,30,8,33,165,104,51,65,13],"35":[17,27,30,8,33,165,104,51,65,13],"48":[17,27,30,8,33,165,104,51,65,13]},"packages/nodes-base/nodes/HttpRequest/V3/utils/parse.ts":{"1":[17,27,30,8,33,165,104,51,65,13]},"packages/nodes-base/nodes/HttpRequest/shared/optimizeResponse.ts":{"20":"b:ACECSAIACAACAAAAAAEAAAACAAAgAEAA","37":"b:ACECSAIACAACAAAAAAEAAAACAAAgAEAA","38":"b:ACECSAIACAACAAAAAAEAAAACAAAgAEAA","42":"b:ACECSAIACAACAAAAAAEAAAACAAAgAEAA","55":"b:ACECSAIACAACAAAAAAEAAAACAAAgAEAA","63":"b:ACECSAIACAACAAAAAAEAAAACAAAgAEAA","92":"b:ACECSAIACAACAAAAAAEAAAACAAAgAEAA","97":"b:ACECSAIACAACAAAAAAEAAAACAAAgAEAA","127":"b:ACECSAIACAACAAAAAAEAAAACAAAgAEAA","128":"b:ACECSAIACAACAAAAAAEAAAACAAAgAEAA","179":"b:ACECSAIACAACAAAAAAEAAAACAAAgAEAA","194":"b:ACECSAIACAACAAAAAAEAAAACAAAgAEAA","226":"b:ACECSAIACAACAAAAAAEAAAACAAAgAEAA","255":"b:ACECSAIACAACAAAAAAEAAAACAAAgAEAA"},"packages/@n8n/agents/src/sdk/cancellation.ts":{"18":[36,78,38,39],"22":[36,78,38,39]},"packages/@n8n/agents/src/utils/parse.ts":{"13":[36,78,38,39],"26":[36,78,38,39]},"packages/@n8n/db/src/repositories/ai-builder-temporary-workflow.repository.ts":{"9":[36,78,100,38,81,39,40,82],"13":[36,78,100,38,81,39,40,82],"23":[36,78,100,38,81,39,40,82],"27":[36,78,100,38,81,39,40,82],"34":[36,78,100,38,81,39,40,82]},"packages/@n8n/instance-ai/src/runtime/terminal-response-guard.ts":{"48":[36,78,37,100,38,81,39,82],"52":[36,78,37,100,38,81,39,82],"64":[36,78,37,100,38,81,39,82],"74":[36,78,37,100,38,81,39,82],"76":[36,78,37,100,38,81,39,82],"175":[36,78,37,100,38,81,39,82],"217":[36,78,37,100,38,81,39,82],"224":[36,78,37,100,38,81,39,82],"227":[36,78,37,100,38,81,39,82],"230":[36,78,37,100,38,81,39,82],"232":[36,78,37,100,38,81,39,82],"237":[36,78,37,100,38,81,39,82],"242":[36,78,37,100,38,81,39,82],"248":[36,78,37,100,38,81,39,82],"268":[36,78,37,100,38,81,39,82],"289":[36,78,37,100,38,81,39,82]},"packages/@n8n/instance-ai/src/tools/workflows/credential-url-resolver.ts":{"24":[36,38,39,40],"32":[36,38,39,40],"33":[36,38,39,40],"41":[36,38,39,40],"43":[36,38,39,40],"78":[36,38,39,40],"94":[36,38,39,40]},"packages/@n8n/utils/src/json/to-json-value.ts":{"16":[36,78,38,39,40],"49":[36,78,38,39,40]},"packages/@n8n/workflow-sdk/src/codegen/code-generator.ts":{"84":[37,39],"92":[37,39],"141":[37,39],"170":[37,39],"181":[37,39],"205":[37,39],"209":[37,39],"251":[37,39],"282":[37,39],"331":[37,39],"360":[37,39],"371":[37,39],"395":[37,39],"410":[37,39],"456":[37,39],"462":[37,39],"478":[37,39],"512":[37,39],"527":[37,39],"563":[37,39],"606":[37,39],"646":[37,39],"667":[37,39],"693":[37,39],"708":[37,39],"728":[37,39],"735":[37,39],"743":[37,39],"754":[37,39],"788":[37,39],"801":[37,39],"816":[37,39],"821":[37,39],"836":[37,39],"841":[37,39],"857":[37,39],"876":[37,39],"891":[37,39],"946":[37,39],"954":[37,39],"964":[37,39],"982":[37,39],"992":[37,39],"1020":[37,39],"1047":[37,39],"1075":[37,39],"1157":[37,39],"1169":[37,39],"1179":[37,39],"1187":[37,39],"1201":[37,39],"1217":[37,39],"1273":[37,39],"1328":[37,39],"1457":[37,39]},"packages/@n8n/workflow-sdk/src/codegen/composite-builder.ts":{"50":[37,39],"77":[37,39],"144":[37,39],"185":[37,39],"328":[37,39],"355":[37,39],"387":[37,39],"454":[37,39],"491":[37,39],"528":[37,39],"586":[37,39],"701":[37,39],"814":[37,39],"969":[37,39],"1067":[37,39],"1162":[37,39],"1196":[37,39],"1275":[37,39],"1284":[37,39]},"packages/@n8n/workflow-sdk/src/codegen/composite-handlers/build-utils.ts":{"19":[37,39],"42":[37,39],"53":[37,39],"65":[37,39],"75":[37,39],"82":[37,39],"89":[37,39],"103":[37,39],"134":[37,39],"142":[37,39],"146":[37,39],"157":[37,39],"166":[37,39],"171":[37,39]},"packages/@n8n/workflow-sdk/src/codegen/execution-status.ts":{"17":[37,39],"46":[37,39],"61":[37,39],"63":[37,39]},"packages/@n8n/workflow-sdk/src/codegen/expression-annotator.ts":{"11":[37,39],"35":[37,39]},"packages/@n8n/workflow-sdk/src/codegen/graph-annotator.ts":{"16":[37,39],"20":[37,39],"79":[37,39],"104":[37,39],"114":[37,39],"141":[37,39],"157":[37,39]},"packages/@n8n/workflow-sdk/src/codegen/node-type-utils.ts":{"10":[37,39],"17":[37,39],"24":[37,39],"32":[37,39],"39":[37,39]},"packages/@n8n/workflow-sdk/src/codegen/output-utils.ts":{"24":[37,39],"41":[37,39],"62":[37,39],"75":[37,39],"92":[37,39],"107":[37,39],"114":[37,39]},"packages/@n8n/workflow-sdk/src/codegen/semantic-graph.ts":{"20":[37,39],"44":[37,39],"52":[37,39],"60":[37,39],"98":[37,39],"139":[37,39],"148":[37,39],"151":[37,39],"168":[37,39],"206":[37,39],"240":[37,39],"329":[37,39],"352":[37,39],"384":[37,39]},"packages/@n8n/workflow-sdk/src/codegen/semantic-registry.ts":{"42":[37,39],"48":[37,39],"57":[37,39],"60":[37,39],"76":[37,39],"85":[37,39],"105":[37,39],"137":[37,39],"158":[37,39],"169":[37,39],"199":[37,39]},"packages/@n8n/workflow-sdk/src/codegen/string-utils.ts":{"9":[37,39],"24":[37,39],"32":[37,39],"39":[37,39],"47":[37,39],"55":[37,39]},"packages/@n8n/workflow-sdk/src/codegen/subnode-generator.ts":{"28":[37,39],"36":[37,39],"76":[37,39],"94":[37,39],"101":[37,39],"104":[37,39],"114":[37,39],"142":[37,39],"147":[37,39],"156":[37,39],"165":[37,39],"178":[37,39],"187":[37,39],"199":[37,39],"208":[37,39],"251":[37,39],"281":[37,39],"300":[37,39],"326":[37,39],"335":[37,39],"356":[37,39],"378":[37,39],"387":[37,39]},"packages/@n8n/workflow-sdk/src/codegen/variable-names.ts":{"102":[37,39],"115":[37,39],"120":[37,39],"151":[37,39],"162":[37,39]},"packages/@n8n/workflow-sdk/src/constants/node-types.ts":{"24":[36,37,38,39,40],"31":[36,37,38,39,40],"38":[36,37,38,39,40],"45":[36,37,38,39,40],"52":[36,37,38,39,40],"59":[36,37,38,39,40],"66":[36,37,38,39,40],"73":[36,37,38,39,40]},"packages/@n8n/workflow-sdk/src/generate-types/index.ts":{"82":[37,38,39,40,172,42]},"packages/@n8n/workflow-sdk/src/mock-data/index.ts":{"29":[37,38,39,40,172,42]},"packages/@n8n/workflow-sdk/src/prompts/best-practices/guides/chatbot.ts":{"4":[36,38,39,40],"125":[36,38,39,40]},"packages/@n8n/workflow-sdk/src/prompts/best-practices/guides/content-generation.ts":{"4":[36,38,39,40],"111":[36,38,39,40]},"packages/@n8n/workflow-sdk/src/prompts/best-practices/guides/data-extraction.ts":{"4":[36,38,39,40],"118":[36,38,39,40]},"packages/@n8n/workflow-sdk/src/prompts/best-practices/guides/data-persistence.ts":{"4":[36,38,39,40],"193":[36,38,39,40]},"packages/@n8n/workflow-sdk/src/prompts/best-practices/guides/data-transformation.ts":{"4":[36,38,39,40],"142":[36,38,39,40]},"packages/@n8n/workflow-sdk/src/prompts/best-practices/guides/document-processing.ts":{"4":[36,38,39,40],"328":[36,38,39,40]},"packages/@n8n/workflow-sdk/src/prompts/best-practices/guides/form-input.ts":{"4":[36,38,39,40],"169":[36,38,39,40]},"packages/@n8n/workflow-sdk/src/prompts/best-practices/guides/notification.ts":{"4":[36,38,39,40],"131":[36,38,39,40]},"packages/@n8n/workflow-sdk/src/prompts/best-practices/guides/scheduling.ts":{"4":[36,38,39,40],"148":[36,38,39,40]},"packages/@n8n/workflow-sdk/src/prompts/best-practices/guides/scraping-and-research.ts":{"4":[36,38,39,40],"154":[36,38,39,40]},"packages/@n8n/workflow-sdk/src/prompts/best-practices/guides/triage.ts":{"4":[36,38,39,40],"145":[36,38,39,40]},"packages/@n8n/workflow-sdk/src/prompts/best-practices/guides/web-app.ts":{"4":[36,38,39,40],"200":[36,38,39,40]},"packages/@n8n/workflow-sdk/src/prompts/best-practices/index.ts":{"1":[36,38,39,40],"4":[36,38,39,40],"7":[36,38,39,40],"8":[36,38,39,40],"14":[36,38,39,40],"19":[36,38,39,40],"20":[36,38,39,40]},"packages/@n8n/workflow-sdk/src/types/base.ts":{"168":[37,39],"199":[37,39],"238":[37,39],"285":[37,39],"638":[37,39],"654":[37,39]},"packages/@n8n/workflow-sdk/src/utils/trigger-detection.ts":{"30":[36,38,39,40]},"packages/@n8n/workflow-sdk/src/validation/display-options.ts":{"77":[36,38,39,40],"90":[36,38,39,40],"98":[36,38,39,40],"115":[36,38,39,40],"156":[36,38,39,40],"157":[36,38,39,40],"167":[36,38,39,40],"231":[36,38,39,40],"274":[36,38,39,40],"289":[36,38,39,40],"311":[36,38,39,40],"332":[36,38,39,40],"333":[36,38,39,40],"349":[36,38,39,40]},"packages/@n8n/workflow-sdk/src/validation/issue-severity.ts":{"13":[36,38,39,40],"20":[36,38,39,40]},"packages/@n8n/workflow-sdk/src/validation/node-parameter-schema/resolve-schema.ts":{"17":[36,38,39,40],"65":[36,38,39,40],"74":[36,38,39,40],"86":[36,38,39,40],"95":[36,38,39,40],"142":[36,38,39,40],"150":[36,38,39,40],"166":[36,38,39,40],"202":[36,38,39,40],"220":[36,38,39,40],"223":[36,38,39,40],"225":[36,38,39,40]},"packages/@n8n/workflow-sdk/src/validation/node-parameter-schema/schema-helpers.ts":{"8":[36,38,39,40],"33":[36,38,39,40]},"packages/@n8n/workflow-sdk/src/validation/node-port-resolvers/resolve-main-input-count.ts":{"16":[36,38,39,40],"31":[36,38,39,40]},"packages/@n8n/workflow-sdk/src/validation/node-port-resolvers/resolve-main-output-count.ts":{"16":[36,38,39,40],"31":[36,38,39,40]},"packages/@n8n/workflow-sdk/src/validation/validate-workflow.ts":{"64":[36,38,39,40],"95":[36,38,39,40],"114":[36,38,39,40],"214":[36,38,39,40],"281":[36,38,39,40],"321":[36,38,39,40],"341":[36,38,39,40],"359":[36,38,39,40],"424":[36,38,39,40],"436":[36,38,39,40],"527":[36,38,39,40],"598":[36,38,39,40],"655":[36,38,39,40],"688":[36,38,39,40],"751":[36,38,39,40],"759":[36,38,39,40],"760":[36,38,39,40],"782":[36,38,39,40],"795":[36,38,39,40],"809":[36,38,39,40],"831":[36,38,39,40],"916":[36,38,39,40],"957":[36,38,39,40],"992":[36,38,39,40],"1004":[36,38,39,40],"1028":[36,38,39,40],"1060":[36,38,39,40],"1090":[36,38,39,40],"1103":[36,38,39,40],"1110":[36,38,39,40],"1118":[36,38,39,40],"1123":[36,38,39,40],"1130":[36,38,39,40],"1155":[36,38,39,40],"1213":[36,38,39,40],"1257":[36,38,39,40],"1306":[36,38,39,40]},"packages/cli/src/errors/response-errors/forbidden.error.ts":{"4":[3,96,37,39]},"packages/cli/src/modules/instance-ai/instance-ai-run-pin-data.ts":{"28":[36,38,39,40],"90":[36,38,39,40],"94":[36,38,39,40],"99":[36,38,39,40],"140":[36,38,39,40],"164":[36,38,39,40],"179":[36,38,39,40],"194":[36,38,39,40],"231":[36,38,39,40]},"packages/cli/src/modules/instance-ai/observability.ts":{"16":[100,39,82]},"packages/cli/src/modules/instance-ai/storage/typeorm-agent-checkpoint-store.ts":{"13":[36,78,37,100,38,81,39,82],"15":[36,78,37,100,38,81,39,82],"47":[36,78,37,100,38,81,39,82],"56":[36,78,37,100,38,81,39,82],"64":[36,78,37,100,38,81,39,82],"80":[36,78,37,100,38,81,39,82],"89":[36,78,37,100,38,81,39,82],"103":[36,78,37,100,38,81,39,82],"115":[36,78,37,100,38,81,39,82],"122":[36,78,37,100,38,81,39,82]},"packages/cli/src/modules/instance-ai/workflow-verification-obligation-service.ts":{"19":[36,39,40],"27":[36,39,40],"29":[36,39,40],"33":[36,39,40],"39":[36,39,40],"49":[36,39,40],"57":[36,39,40],"71":[36,39,40],"82":[36,39,40]},"packages/workflow/src/schemas.ts":{"89":[98,36,38,39,40],"102":[98,36,38,39,40],"166":[98,36,38,39,40],"240":[98,36,38,39,40],"265":[98,36,38,39,40],"431":[98,36,38,39,40],"508":[98,36,38,39,40],"509":[98,36,38,39,40],"522":[98,36,38,39,40]},"localhost-38239/assets/_plugin-vue_export-helper-Da88TEg1.js":{"2":[17,29,110,121,144,145,14]},"localhost-38239/assets/chunk-CC9Q-vWm.js":{"8":[17,29,110,121,144,145,14],"9":[17,29,110,121,144,145,14],"10":[17,29,110,121,144,145,14],"19":[17,29,110,121,144,145,14],"23":[17,29,110,121,144,145,14],"29":[17,29,110,121,144,145,14],"33":[17,29,110,121,144,145,14],"34":[17,29,110,121,144,145,14]},"packages/frontend/@n8n/design-system/src/components/AskAssistantChat/messages/BaseMessage.vue":{"16":[15,87,16,17,18,20],"26":[15,87,16,17,18,20]},"packages/frontend/@n8n/design-system/src/components/AskAssistantChat/messages/MessageWrapper.vue":{"22":[15,87,16,17,18,20],"30":[15,87,16,17,18,20],"33":[15,87,16,17,18,20],"47":[87],"48":[87]},"packages/frontend/@n8n/design-system/src/components/AskAssistantChat/messages/TextMessage.vue":{"31":[17],"40":[15,87,16,17,18,20],"42":[15,87,16,17,18,20],"52":[15,87,16,17,18,20],"53":[15,87,16,17,18,20],"58":[15,87,16,17,18,20],"80":[15,87,16,17,18,20],"89":[15,87,16,17,18,20],"137":[17]},"packages/frontend/@n8n/design-system/src/components/AskAssistantChat/messages/ThinkingMessage.vue":{"14":[15,87,16,17,18,20],"21":[15,87,16,17,18,20],"23":[15,87,16,17,18,20],"32":[15,87,16,17,18,20],"38":[15,87,16,17,18,20],"48":[15,87,16,17,18,20],"64":[15,87,16,17,18,20],"65":[15,87,16,17,18,20]},"packages/frontend/@n8n/design-system/src/components/AskAssistantChat/messages/helpers.ts":{"10":[15,87,16,17,18,20]},"packages/frontend/@n8n/design-system/src/components/AskAssistantChat/messages/useMarkdown.ts":{"6":[15,87,16,17,18,20],"20":[15,87,16,17,18,20]},"packages/frontend/@n8n/design-system/src/components/BlinkingCursor/BlinkingCursor.vue":{"2":[15,87,16,17,18]},"packages/frontend/@n8n/design-system/src/components/InlineAskAssistantButton/InlineAskAssistantButton.vue":{"8":[15,87,16,17],"39":[15,87,16,17],"41":[15,87,16],"44":[15,87,16,17]},"packages/frontend/@n8n/design-system/src/components/N8nIcon/nodes/http-request.svg":{"11":"b:AiUCTAIACBACAABABAEMAAACCAAEAAAA"},"packages/frontend/@n8n/design-system/src/types/assistant.ts":{"165":[15,87,16,17,18,20],"221":[15,87,16,17,18,20],"227":[15,87,16,17,18,20],"239":[15,87,16,17,18,20]},"packages/frontend/editor-ui/src/features/ai/assistant/components/Chat/AskAssistantChat.vue":{"13":[15,87,16,17],"27":[15,87,16,17],"32":[15,87,16,17],"34":[15,87,16,17],"39":[15,17],"50":[15,17],"65":[87],"72":[87],"80":[15,87,16,17],"84":[15,87,16,17],"87":[15,17],"96":[15],"108":[15,87,16,17]},"packages/frontend/editor-ui/src/features/ai/assistant/components/Chat/AskModeEmptyState.vue":{"5":[15,87,16,17],"6":[15,87,16,17]},"packages/frontend/editor-ui/src/features/ndv/parameters/components/ImportCurlParameter.vue":{"11":"b:ACECSAIACAACAABAAAEMAAACCAAAAAAA","16":"b:ACECSAIACAACAABAAAEMAAACCAAAAAAA"},"packages/cli/src/modules/provisioning.ee/claims-context.builder.ts":{"3":[25],"17":[25],"29":[25]},"packages/cli/src/modules/provisioning.ee/provisioning.controller.ee.ts":{"13":[25,122],"21":[25,122],"31":[25,122]},"packages/cli/src/modules/provisioning.ee/role-mapping-rule.service.ee.ts":{"46":[25],"52":[25],"79":[25],"83":[25],"112":[25],"150":[25],"168":[25],"192":[25],"242":[25],"259":[25],"265":[25],"288":[25],"294":[25],"304":[25],"307":[25],"324":[25],"334":[25],"336":[25],"339":[25],"355":[25],"362":[25],"369":[25]},"packages/cli/src/modules/sso-oidc/oidc.controller.ee.ts":{"28":[25],"41":[25],"52":[25],"70":[25],"92":[25],"113":[25],"202":[25]},"packages/cli/src/modules/sso-oidc/oidc.service.ee.ts":{"73":[25],"92":[25],"106":[25],"115":[25],"121":[25],"125":[25],"133":[25],"145":[25],"180":[25],"188":[25],"221":[25],"264":[25],"368":[25],"405":[25],"414":[25],"432":[25],"454":[25],"494":[25],"560":[25],"561":[25],"573":[25],"598":[25],"609":[25],"619":[25],"650":[25],"683":[25],"693":[25],"749":[25],"773":[25],"807":[25]},"localhost-36541/assets/_plugin-vue_export-helper-Da88TEg1.js":{"2":[25]},"localhost-36541/assets/chunk-CC9Q-vWm.js":{"8":[25],"9":[25],"10":[25],"19":[25],"23":[25],"29":[25],"33":[25],"34":[25]},"packages/frontend/@n8n/design-system/src/components/N8nLogo/Logo.vue":{"8":"b:OAAAAgkAwAAAOUAgBQAAAAAAAAAAAAAA","24":"b:OAAAAgkAwAAAOUAgBQAAAAAAAAAAAAAA","30":"b:OAAAAgkAwAAAOUAgBQAAAAAAAAAAAAAA","42":"b:OAAAAgkAwAAAOUAgBQAAAAAAAAAAAAAA","57":"b:OAAAAgkAwAAAOUAgBQAAAAAAAAAAAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nLogo/logo-icon.svg":{"11":"b:OAAAAgkAwAAAOUAgBQAAAAAAAAAAAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nLogo/logo-text.svg":{"11":"b:OAAAAgkAwAAAOUAgBQAAAAAAAAAAAAAA"},"packages/frontend/editor-ui/src/app/components/layouts/EmptyStateLayout.vue":{"21":[75,25,76,34,35,55,85,86,72],"44":[55],"49":[75,25,76,34,35,55,85,86,72],"60":[75,25,76,34,35,55,85,86,72],"83":[75,25,76,34,35,55,85,86,72],"91":[75,25,76,34,35,55,85,86,72],"117":[75,25,76,34,35,55,85,86,72],"127":[85],"192":[75,25,76,34,35,55,85,86,72],"201":[75,25,76,34,35,55,85,86,72]},"packages/frontend/editor-ui/src/experiments/surfaceMcpToNewCloudUsers/composables/useSurfaceMcpEmptyState.ts":{"15":[75,25,76,34,35,55,85,86,72],"25":[75,25,76,34,35,55,85,86,72],"43":[75,25,76,34,35,55,85,86,72],"47":[75,25,76,34,35,55,85,86,72],"55":[75,25,76,34,35,55,85,86,72],"72":[75,25,76,34,35,55,85,86,72]},"packages/frontend/editor-ui/src/experiments/surfaceMcpToNewCloudUsers/composables/useSurfaceMcpToNewCloudUsersEligibility.ts":{"7":[75,25,76,34,35,55,85,86,72],"13":[75,25,76,34,35,55,85,86,72]},"packages/frontend/editor-ui/src/experiments/surfaceMcpToNewCloudUsers/stores/surfaceMcpToNewCloudUsers.store.ts":{"26":[75,25,76,34,35,55,85,86,72]},"packages/@n8n/ai-utilities/src/node-catalog/discriminator-utils.ts":{"44":[38,40,172,42],"55":[38,40,172,42],"70":[38,40,172,42],"81":[38,40,172,42],"117":[38,40,172,42],"128":[38,40,172,42],"139":[38,40,172,42],"151":[38,40,172,42],"161":[38,40,172,42],"178":[38,40,172,42]},"packages/@n8n/ai-utilities/src/node-catalog/dynamic-node-parameters.ts":{"47":[38,40,172,42],"51":[38,40,172,42],"55":[38,40,172,42],"59":[38,40,172,42],"63":[38,40,172,42],"73":[38,40,172,42],"107":[38,40,172,42],"114":[38,40,172,42],"119":[38,40,172,42],"151":[38,40,172,42],"183":[38,40,172,42],"187":[38,40,172,42],"188":[38,40,172,42],"195":[38,40,172,42],"199":[38,40,172,42],"214":[38,40,172,42],"226":[38,40,172,42],"230":[38,40,172,42],"237":[38,40,172,42],"241":[38,40,172,42],"251":[38,40,172,42],"260":[38,40,172,42],"274":[38,40,172,42]},"packages/@n8n/ai-utilities/src/node-catalog/get.ts":{"17":[38,40,172,42],"44":[38,40,172,42],"57":[38,40,172,42],"59":[38,40,172,42],"73":[38,40,172,42],"97":[38,40,172,42],"110":[38,40,172,42],"120":[38,40,172,42],"157":[38,40,172,42],"193":[38,40,172,42],"205":[38,40,172,42],"242":[38,40,172,42],"263":[38,40,172,42],"273":[38,40,172,42],"276":[38,40,172,42],"277":[38,40,172,42],"326":[38,40,172,42],"327":[38,40,172,42],"344":[38,40,172,42],"354":[38,40,172,42],"356":[38,40,172,42],"361":[38,40,172,42],"404":[38,40,172,42],"504":[38,40,172,42],"518":[38,40,172,42],"546":[38,40,172,42],"616":[38,40,172,42]},"packages/@n8n/ai-utilities/src/node-catalog/lean-node-type.ts":{"62":[38,40,172,42],"66":[38,40,172,42],"88":[38,40,172,42]},"packages/@n8n/ai-utilities/src/node-catalog/node-type-parser.ts":{"39":[38,40,172,42],"48":[38,40,172,42],"64":[38,40,172,42],"80":[38,40,172,42],"87":[38,40,172,42],"109":[38,40,172,42],"118":[38,40,172,42],"126":[38,40,172,42],"145":[38,40,172,42]},"packages/@n8n/ai-utilities/src/node-catalog/resource-operation-extractor.ts":{"56":[38,40,172,42],"82":[38,40,172,42],"87":[38,40,172,42],"98":[38,40,172,42],"122":[38,40,172,42],"127":[38,40,172,42],"152":[38,40,172,42],"171":[38,40,172,42],"198":[38,40,172,42],"216":[38,40,172,42],"250":[38,40,172,42],"288":[38,40,172,42],"295":[38,40,172,42],"305":[38,40,172,42]},"packages/@n8n/ai-utilities/src/node-catalog/search-engine.ts":{"20":[38,40,172,42],"27":[38,40,172,42],"60":[38,40,172,42],"71":[38,40,172,42],"94":[38,40,172,42],"98":[38,40,172,42],"103":[38,40,172,42],"106":[38,40,172,42],"113":[38,40,172,42],"115":[38,40,172,42],"144":[38,40,172,42],"145":[38,40,172,42],"161":[38,40,172,42],"167":[38,40,172,42],"196":[38,40,172,42],"217":[38,40,172,42],"223":[38,40,172,42],"230":[38,40,172,42],"234":[38,40,172,42],"242":[38,40,172,42],"246":[38,40,172,42],"263":[38,40,172,42],"269":[38,40,172,42],"275":[38,40,172,42],"305":[38,40,172,42],"312":[38,40,172,42],"316":[38,40,172,42],"323":[38,40,172,42],"325":[38,40,172,42],"344":[38,40,172,42],"350":[38,40,172,42],"352":[38,40,172,42],"378":[38,40,172,42],"426":[38,40,172,42],"439":[38,40,172,42],"453":[38,40,172,42],"480":[38,40,172,42],"481":[38,40,172,42],"490":[38,40,172,42],"516":[38,40,172,42],"524":[38,40,172,42],"526":[38,40,172,42]},"packages/@n8n/ai-utilities/src/node-catalog/search.ts":{"48":[38,40,172,42],"62":[38,40,172,42],"71":[38,40,172,42],"77":[38,40,172,42],"84":[38,40,172,42],"91":[38,40,172,42],"105":[38,40,172,42],"181":[38,40,172,42],"227":[38,40,172,42],"241":[38,40,172,42],"242":[38,40,172,42],"247":[38,40,172,42],"248":[38,40,172,42],"262":[38,40,172,42],"263":[38,40,172,42],"281":[38,40,172,42],"317":[38,40,172,42],"336":[38,40,172,42],"338":[38,40,172,42],"355":[38,40,172,42],"379":[38,40,172,42],"431":[38,40,172,42],"446":[38,40,172,42],"533":[38,40,172,42],"539":[38,40,172,42],"545":[38,40,172,42],"547":[38,40,172,42],"548":[38,40,172,42]},"packages/@n8n/ai-workflow-builder.ee/src/code-builder/index.ts":{"14":[172,42],"21":[172,42],"22":[172,42],"23":[172,42],"25":[172,42]},"packages/@n8n/ai-workflow-builder.ee/src/index.ts":{"7":[20,172,42],"44":[20,172,42]},"packages/@n8n/backend-common/src/modules/module-registry.ts":{"17":[3,4,5,9,37,172,42],"36":[3,4,5,9,37,172,42],"79":[3,4,5,9,37,172,42],"82":[3,4,5,9,37,172,42],"88":[3,4,5,9,37,172,42],"99":[3,4,5,9,37,172,42],"157":[3,4,5,9,37,172,42],"195":[3,4,5,9,37,172,42],"214":[3,4,5,9,37,172,42],"230":[3,4,5,9,37,172,42],"234":[3,4,5,9,37,172,42]},"packages/@n8n/decorators/src/module/module-metadata.ts":{"17":[3,4,5,9,37,172,42],"20":[3,4,5,9,37,172,42],"24":[3,4,5,9,37,172,42],"28":[3,4,5,9,37,172,42],"32":[3,4,5,9,37,172,42],"33":[3,4,5,9,37,172,42]},"packages/cli/src/errors/response-errors/auth.error.ts":{"4":[176,172,86]},"packages/cli/src/modules/data-table/data-table-proxy.service.ts":{"46":[172,42],"52":[172,42],"62":[172,42],"71":[172,42],"77":[172,42],"82":[172,42],"93":[172,42],"121":[172,42],"128":[172,42],"131":[172,42],"133":[172,42],"137":[172,42],"142":[172,42],"149":[172,42],"155":[172,42],"165":[172,42],"171":[172,42],"177":[172,42],"189":[172,42],"196":[172,42],"206":[172,42],"217":[172,42],"229":[172,42],"245":[172,42],"253":[172,42],"256":[172,42],"260":[172,42],"268":[172,42],"272":[172,42],"278":[172,42],"286":[172,42],"290":[172,42],"295":[172,42],"299":[172,42],"303":[172,42],"307":[172,42],"312":[172,42],"316":[172,42],"323":[172,42],"333":[172,42],"343":[172,42],"353":[172,42]},"packages/cli/src/modules/mcp/mcp-protected-resource.ts":{"49":[9,172,42],"57":[9,172,42],"59":[9,172,42],"66":[9,172,42],"73":[9,172,42],"74":[9,172,42],"77":[9,172,42],"85":[9,172,42],"101":[9,172,42],"112":[9,172,42],"117":[9,172,42],"123":[9,172,42],"127":[9,172,42]},"packages/cli/src/modules/mcp/mcp-scopes.ts":{"115":[172,42],"124":[172,42]},"packages/cli/src/modules/mcp/mcp-server-middleware.service.ts":{"27":[172,42],"39":[172,42],"66":[172,42],"67":[172,42],"113":[172,42],"126":[172,42],"138":[172,42]},"packages/cli/src/modules/mcp/mcp-tool-availability.ts":{"4":[9,172,42]},"packages/cli/src/modules/mcp/mcp.controller.ts":{"26":[172,42],"32":[172,42],"42":[172,42],"69":[172,42],"90":[172,42],"116":[172,42],"195":[172,42],"214":[172,42],"222":[172,42]},"packages/cli/src/modules/mcp/mcp.service.ts":{"131":[172,42],"172":[172,42],"189":[172,42],"203":[172,42],"252":[172,42],"260":[172,42],"293":[172,42],"478":[172,42],"547":[172,42],"635":[172,42],"661":[172,42],"669":[172,42],"684":[172,42],"698":[172,42],"709":[172,42],"732":[172,42],"747":[172,42]},"packages/cli/src/modules/mcp/mcp.settings.controller.ts":{"20":[9,172,42],"31":[9,172,42],"53":[9,172,42],"59":[9,172,42],"65":[9,172,42],"72":[9,172,42],"82":[9,172,42],"106":[9,172,42]},"packages/cli/src/modules/mcp/mcp.settings.service.ts":{"48":[9,172,42],"58":[9,172,42],"74":[9,172,42],"83":[9,172,42],"99":[9,172,42],"108":[9,172,42],"166":[9,172,42],"195":[9,172,42],"205":[9,172,42],"231":[9,172,42],"255":[9,172,42],"260":[9,172,42],"276":[9,172,42],"279":[9,172,42],"299":[9,172,42],"319":[9,172,42]},"packages/cli/src/modules/mcp/mcp.typeguards.ts":{"22":[172,42],"45":[172,42],"56":[172,42],"74":[172,42]},"packages/cli/src/modules/mcp/mcp.utils.ts":{"12":[172,42],"25":[172,42],"42":[172,42],"64":[172,42],"72":[172,42]},"packages/cli/src/modules/mcp/tools/data-table/add-data-table-column.tool.ts":{"29":[172,42],"47":[172,42]},"packages/cli/src/modules/mcp/tools/data-table/add-data-table-rows.tool.ts":{"30":[172,42],"49":[172,42]},"packages/cli/src/modules/mcp/tools/data-table/create-data-table.tool.ts":{"35":[172,42],"54":[172,42],"72":[172,42]},"packages/cli/src/modules/mcp/tools/data-table/delete-data-table-column.tool.ts":{"19":[172,42],"38":[172,42]},"packages/cli/src/modules/mcp/tools/data-table/index.ts":{"1":[172,42],"2":[172,42],"3":[172,42],"5":[172,42],"6":[172,42]},"packages/cli/src/modules/mcp/tools/data-table/rename-data-table-column.tool.ts":{"28":[172,42],"46":[172,42]},"packages/cli/src/modules/mcp/tools/data-table/rename-data-table.tool.ts":{"19":[172,42],"37":[172,42]},"packages/cli/src/modules/mcp/tools/data-table/search-data-tables.tool.ts":{"28":[172,42],"47":[172,42],"73":[172,42],"79":[172,42]},"packages/cli/src/modules/mcp/tools/execute-workflow.tool.ts":{"96":[172,42],"119":[172,42],"195":[172,42],"232":[172,42],"275":[172,42],"353":[172,42],"371":[172,42],"451":[172,42],"462":[172,42],"486":[172,42]},"packages/cli/src/modules/mcp/tools/get-execution.tool.ts":{"65":[172,42],"85":[172,42],"207":[172,42],"240":[172,42],"244":[172,42]},"packages/cli/src/modules/mcp/tools/get-workflow-details.tool.ts":{"32":[172,42],"58":[172,42],"109":[172,42],"141":[172,42],"152":[172,42],"153":[172,42],"157":[172,42],"184":[172,42]},"packages/cli/src/modules/mcp/tools/get-workflow-history.tool.ts":{"61":[172,42],"81":[172,42],"136":[172,42],"147":[172,42]},"packages/cli/src/modules/mcp/tools/get-workflow-version.tool.ts":{"75":[172,42],"95":[172,42],"157":[172,42],"168":[172,42]},"packages/cli/src/modules/mcp/tools/list-credentials.tool.ts":{"112":[172,42],"132":[172,42],"189":[172,42],"219":[172,42]},"packages/cli/src/modules/mcp/tools/list-n8n-connect-services.tool.ts":{"51":[172,42],"70":[172,42]},"packages/cli/src/modules/mcp/tools/list-tags.tool.ts":{"60":[172,42],"78":[172,42],"113":[172,42],"120":[172,42]},"packages/cli/src/modules/mcp/tools/prepare-workflow-pin-data.tool.ts":{"57":[172,42],"79":[172,42],"137":[172,42],"147":[172,42],"150":[172,42],"223":[172,42]},"packages/cli/src/modules/mcp/tools/publish-workflow.tool.ts":{"54":[172,42],"62":[172,42],"83":[172,42],"100":[172,42]},"packages/cli/src/modules/mcp/tools/schemas.ts":{"18":[172,42],"23":[172,42],"24":[172,42],"27":[172,42],"31":[172,42],"56":[172,42],"102":[172,42],"106":[172,42],"107":[172,42],"110":[172,42]},"packages/cli/src/modules/mcp/tools/search-executions.tool.ts":{"64":[172,42],"84":[172,42],"134":[172,42]},"packages/cli/src/modules/mcp/tools/search-folders.tool.ts":{"35":[172,42],"55":[172,42],"93":[172,42]},"packages/cli/src/modules/mcp/tools/search-projects.tool.ts":{"62":[172,42],"82":[172,42],"114":[172,42],"115":[172,42],"119":[172,42],"127":[172,42],"135":[172,42],"142":[172,42]},"packages/cli/src/modules/mcp/tools/search-workflows.tool.ts":{"80":[172,42],"100":[172,42],"155":[172,42],"161":[172,42],"194":[172,42]},"packages/cli/src/modules/mcp/tools/test-workflow.tool.ts":{"71":[172,42],"94":[172,42],"170":[172,42],"204":[172,42],"205":[172,42],"280":[172,42]},"packages/cli/src/modules/mcp/tools/unpublish-workflow.tool.ts":{"32":[172,42],"53":[172,42],"69":[172,42]},"packages/cli/src/modules/mcp/tools/workflow-builder/create-workflow-from-code.tool.ts":{"50":[172,42],"174":[172,42],"200":[172,42],"267":[172,42],"350":[172,42],"384":[172,42]},"packages/cli/src/modules/mcp/tools/workflow-builder/delete-workflow.tool.ts":{"27":[172,42],"47":[172,42],"65":[172,42]},"packages/cli/src/modules/mcp/tools/workflow-builder/explore-node-resources.tool.ts":{"80":[172,42],"101":[172,42]},"packages/cli/src/modules/mcp/tools/workflow-builder/get-workflow-best-practices.tool.ts":{"65":[172,42],"66":[172,42],"72":[172,42],"79":[172,42],"98":[172,42],"123":[172,42],"142":[172,42]},"packages/cli/src/modules/mcp/tools/workflow-builder/get-workflow-node-types.tool.ts":{"60":[172,42],"80":[172,42]},"packages/cli/src/modules/mcp/tools/workflow-builder/get-workflow-sdk-reference.tool.ts":{"26":[172,42],"51":[172,42],"70":[172,42]},"packages/cli/src/modules/mcp/tools/workflow-builder/mcp-instructions.ts":{"48":[172,42]},"packages/cli/src/modules/mcp/tools/workflow-builder/restore-workflow-version.tool.ts":{"49":[172,42],"71":[172,42],"111":[172,42]},"packages/cli/src/modules/mcp/tools/workflow-builder/search-workflow-nodes.tool.ts":{"73":[172,42],"93":[172,42]},"packages/cli/src/modules/mcp/tools/workflow-builder/update-workflow.tool.ts":{"79":[172,42],"129":[172,42],"226":[172,42],"240":[172,42],"245":[172,42],"259":[172,42],"261":[172,42],"288":[172,42],"368":[172,42],"431":[172,42],"475":[172,42],"480":[172,42],"481":[172,42],"496":[172,42],"520":[172,42],"560":[172,42],"581":[172,42],"593":[172,42],"603":[172,42],"646":[172,42],"671":[172,42],"692":[172,42],"701":[172,42],"711":[172,42],"715":[172,42],"778":[172,42],"826":[172,42],"838":[172,42],"841":[172,42],"842":[172,42],"848":[172,42],"871":[172,42],"882":[172,42]},"packages/cli/src/modules/mcp/tools/workflow-builder/validate-node.tool.ts":{"80":[172,42],"98":[172,42],"108":[172,42],"125":[172,42],"126":[172,42],"127":[172,42]},"packages/cli/src/modules/mcp/tools/workflow-builder/validate-workflow-code.tool.ts":{"80":[172,42],"88":[172,42],"89":[172,42],"103":[172,42],"123":[172,42],"144":[172,42],"159":[172,42],"174":[172,42],"183":[172,42]},"packages/cli/src/modules/oauth-server/database/repositories/oauth-access-token.repository.ts":{"15":[9,172],"19":[9,172]},"packages/cli/src/modules/oauth-server/database/repositories/oauth-refresh-token.repository.ts":{"17":[9,172],"21":[9,172],"30":[9,172],"44":[9,172]},"packages/cli/src/modules/oauth-server/oauth-authorization-code.service.ts":{"17":[9,172],"23":[9,172],"55":[9,172],"83":[9,172],"111":[9,172],"138":[9,172],"151":[9,172],"157":[9,172]},"packages/cli/src/modules/oauth-server/oauth-consent.controller.ts":{"14":[9,172],"21":[9,172],"66":[9,172],"107":[9,172],"114":[9,172],"121":[9,172]},"packages/cli/src/modules/oauth-server/oauth-consent.service.ts":{"42":[9,172],"56":[9,172],"122":[9,172],"124":[9,172],"132":[9,172],"142":[9,172],"150":[9,172],"245":[9,172],"263":[9,172]},"packages/cli/src/modules/oauth-server/oauth-server.service.ts":{"73":[9,172],"79":[9,172],"80":[9,172],"96":[9,172],"109":[9,172],"111":[9,172],"138":[9,172],"162":[9,172],"167":[9,172],"185":[9,172],"205":[9,172],"248":[9,172],"250":[9,172],"251":[9,172],"254":[9,172],"289":[9,172],"305":[9,172],"322":[9,172],"326":[9,172],"358":[9,172],"391":[9,172],"401":[9,172],"469":[9,172],"483":[9,172],"490":[9,172],"502":[9,172],"510":[9,172],"545":[9,172],"571":[9,172],"572":[9,172],"590":[9,172],"628":[9,172],"640":[9,172],"694":[9,172],"709":[9,172]},"packages/cli/src/modules/oauth-server/oauth-session.service.ts":{"26":[9,172],"31":[9,172],"47":[9,172],"54":[9,172],"61":[9,172]},"packages/cli/src/modules/oauth-server/oauth-token.service.ts":{"37":[9,172],"47":[9,172],"51":[9,172],"87":[9,172],"94":[9,172],"109":[9,172],"114":[9,172],"179":[9,172],"186":[9,172],"226":[9,172],"239":[9,172],"301":[9,172],"308":[9,172],"323":[9,172],"347":[9,172],"359":[9,172],"373":[9,172],"384":[9,172],"403":[9,172]},"packages/cli/src/modules/oauth-server/oauth.controller.ts":{"45":[9,172],"72":[9,172],"74":[9,172],"89":[9,172],"137":[9,172],"143":[9,172],"160":[9,172],"179":[9,172],"210":[9,172],"225":[9,172],"247":[9,172],"274":[9,172],"286":[9,172]},"packages/cli/src/modules/oauth-server/oauth.errors.ts":{"8":[172],"20":[172],"28":[172],"35":[172]},"packages/cli/src/modules/oauth-server/oauth.helpers.ts":{"13":[9,172],"34":[9,172],"58":[9,172]},"packages/cli/src/modules/oauth-server/protected-resource-resolvers/form-trigger-resource.resolver.ts":{"21":[172],"33":[172],"42":[172],"68":[172],"83":[172],"84":[172],"85":[172],"88":[172]},"packages/cli/src/modules/oauth-server/protected-resource-resolvers/form-trigger-test-resource.resolver.ts":{"21":[172],"32":[172],"41":[172],"67":[172],"86":[172],"87":[172],"88":[172],"91":[172]},"packages/cli/src/modules/oauth-server/protected-resource-resolvers/utils.ts":{"16":[9,172],"20":[9,172],"27":[9,172],"47":[9,172]},"packages/cli/src/modules/oauth-server/protected-resource-resolvers/workflow-mcp-test-trigger-resource.resolver.ts":{"20":[172],"31":[172],"40":[172],"65":[172],"83":[172],"84":[172],"87":[172]},"packages/cli/src/modules/oauth-server/protected-resource-resolvers/workflow-mcp-trigger-resource.resolver.ts":{"20":[9,172],"32":[9,172],"41":[9,172],"84":[9,172],"102":[9,172],"103":[9,172],"106":[9,172]},"packages/cli/src/services/ai-gateway.service.ts":{"55":[172,42],"69":[172,42],"102":[172,42],"133":[172,42],"182":[172,42],"190":[172,42],"204":[172,42],"233":[172,42],"252":[172,42],"272":[172,42],"287":[172,42],"293":[172,42],"305":[172,42],"315":[172,42],"359":[172,42],"377":[172,42],"389":[172,42],"405":[172,42],"421":[172,42],"441":[172,42]},"packages/cli/src/services/ai-workflow-builder.service.ts":{"47":[20,38,40,10,172,42],"65":[20,38,40,10,172,42],"73":[20,38,40,10,172,42],"80":[20,38,40,10,172,42],"88":[20,38,40,10,172,42],"104":[20,38,40,10,172,42],"110":[20,38,40,10,172,42],"124":[20,38,40,10,172,42],"129":[20,38,40,10,172,42],"130":[20,38,40,10,172,42],"191":[20,38,40,10,172,42],"208":[20,38,40,10,172,42],"213":[20,38,40,10,172,42],"219":[20,38,40,10,172,42],"224":[20,38,40,10,172,42],"229":[20,38,40,10,172,42]},"packages/cli/src/services/oauth-token-verifier-proxy.service.ts":{"56":[9,172],"59":[9,172],"63":[9,172]},"packages/cli/src/services/protected-resource.registry.ts":{"114":[9,172],"116":[9,172],"130":[9,172],"132":[9,172],"136":[9,172],"140":[9,172],"145":[9,172],"148":[9,172],"164":[9,172],"196":[9,172],"202":[9,172],"207":[9,172],"223":[9,172],"232":[9,172]},"packages/cli/src/telemetry/index.ts":{"116":[36,78,79,38,81,82,172,42],"128":[36,78,79,38,81,82,172,42],"173":[36,78,79,38,81,82,172,42],"203":[36,78,79,38,81,82,172,42],"212":[36,78,79,38,81,82,172,42],"214":[36,78,79,38,81,82,172,42],"221":[36,78,79,38,81,82,172,42],"265":[36,78,79,38,81,82,172,42],"266":[36,78,79,38,81,82,172,42],"290":[36,78,79,38,81,82,172,42],"294":[36,78,79,38,81,82,172,42],"295":[36,78,79,38,81,82,172,42],"315":[36,78,79,38,81,82,172,42],"324":[36,78,79,38,81,82,172,42],"329":[36,78,79,38,81,82,172,42],"330":[36,78,79,38,81,82,172,42],"332":[36,78,79,38,81,82,172,42],"335":[36,78,79,38,81,82,172,42],"336":[36,78,79,38,81,82,172,42],"357":[36,78,79,38,81,82,172,42],"404":[36,78,79,38,81,82,172,42],"417":[36,78,79,38,81,82,172,42],"443":[36,78,79,38,81,82,172,42],"472":[36,78,79,38,81,82,172,42],"496":[36,78,79,38,81,82,172,42],"503":[36,78,79,38,81,82,172,42],"533":[36,78,79,38,81,82,172,42],"558":[36,78,79,38,81,82,172,42],"564":[36,78,79,38,81,82,172,42],"613":[36,78,79,38,81,82,172,42],"617":[36,78,79,38,81,82,172,42],"621":[36,78,79,38,81,82,172,42],"625":[36,78,79,38,81,82,172,42]},"packages/frontend/editor-ui/src/features/ndv/runData/components/RunDataPaginationBar.vue":{"12":[110,144,146],"16":[110,144,146],"28":[110],"39":[110,144,146],"40":[110,144,146]},"packages/@n8n/api-types/src/dto/user/user-update-request.dto.ts":{"6":[121,86],"13":[121,86],"15":[121,86]},"packages/cli/src/controllers/me.controller.ts":{"32":[1,10,57,121,86,127,6,13,14],"48":[1,10,57,121,86,127,6,13,14],"108":[1,10,57,121,86,127,6,13,14],"120":[1,10,57,121,86,127,6,13,14],"174":[1,10,57,121,86,127,6,13,14],"183":[1,10,57,121,86,127,6,13,14],"193":[1,10,57,121,86,127,6,13,14],"231":[1,10,57,121,86,127,6,13,14],"264":[1,10,57,121,86,127,6,13,14],"309":[1,10,57,121,86,127,6,13,14]},"packages/frontend/@n8n/design-system/src/components/N8nAvatar/Avatar.vue":{"16":"b:AAAAAQsA4BABAUCAAhAAdgAAAAACAAAA","29":"b:AAAAAQsA4BABAUCAAhAAdgAAAAACAAAA","30":"b:AAAAAQsA4BABAUCAAhAAdgAAAAACAAAA","32":"b:AAAAAQsA4BABAUCAAhAAdgAAAAACAAAA","34":"b:AAAAAQsA4BABAUCAAhAAdgAAAAACAAAA","37":"b:AAAAAQsA4BABAUCAAhAAdgAAAAACAAAA","38":"b:AAAAAQsA4BABAUCAAhAAdgAAAAACAAAA"},"packages/frontend/@n8n/design-system/src/components/N8nFormInput/FormInput.vue":{"56":"b:AAAAAQkAgBAAEWCgABCAIgAAAAECAAAA","82":"b:AAAAAQkAgBAAEWCgABCAIgAAAAECAAAA","122":"b:AAAAAQgAgAAAEWAAABCAIgAAAAECAAAA","128":"b:AAAAAQgAgAAAEWAAABCAIgAAAAACAAAA","133":"b:AAAAAQgAgAAAEWAgABCAIgAAAAECAAAA","137":[86],"143":"b:AAAAAQkAgBAAEWCgABCAIgAAAAECAAAA","161":"b:AAAAAQkAgBAAEWCgABCAIgAAAAECAAAA","167":"b:AAAAAQkAgBAAEWCgABCAIgAAAAECAAAA","173":[76,95,108,55,85,119,121,86],"176":"b:AAAAAQkAgBAAEWCgABCAIgAAAAECAAAA","214":"b:AAAAAQkAgBAAEWCgABCAIgAAAAECAAAA","215":[108,86],"232":[85],"234":[85]},"packages/frontend/@n8n/design-system/src/components/N8nFormInputs/FormInputs.vue":{"20":[93,76,95,55,121,86],"38":[93,76,95,55,121,86],"39":[93,76,95,55,121,86],"44":[76,55,121,86],"45":[76,55,121,86],"56":[93,76,95,55,121,86],"57":[93,76,95,55,121,86],"60":[93,76,95,55,121,86],"64":[93,76,95,55,121,86],"65":[86],"68":[86],"69":[86],"70":[86],"75":[76,55,121,86],"81":[93,76,95,55,121,86],"108":[86],"117":[76,55,121,86],"124":[76,55,121,86],"137":[93,76,95,55,121,86],"148":[93,76,95,55,121,86],"152":[93,76,95,55,121,86],"155":[93,76,95,55,121,86],"179":[76,55,121,86],"180":[93,76,95,55,121,86]},"packages/frontend/@n8n/design-system/src/components/ResizeObserver/ResizeObserver.vue":{"6":[93,76,95,55,121,86],"21":[93,76,95,55,121,86],"23":[93,76,95,55,121,86],"25":[93,76,95,55,121,86],"30":[93,76,95,55,121,86],"36":[93,76,95,55,121,86],"37":[93,76,95,55,121,86],"38":[93,76,95,55,121,86],"47":[76,55,86],"52":[93,76,95,55,121,86]},"packages/frontend/@n8n/design-system/src/utils/labelUtil.ts":{"1":"b:AAAAAQsA4BABAUCAAhAAdgAAAAACAAAA","4":"b:AAAAAQsA4BABAUCAAhAAdgAAAAACAAAA","6":"b:AAAAAQsA4BABAUCAAhAAdgAAAAACAAAA","11":"b:AAAAAQsA4BABAUCAAhAAdgAAAAACAAAA"},"packages/frontend/editor-ui/src/app/utils/rbac/checks/isInstanceOwner.ts":{"4":[95,77,121,86]},"packages/frontend/editor-ui/src/features/core/auth/views/SettingsPersonalView.vue":{"92":[95,121,86],"96":[95,121,86],"100":[95,121,86],"104":[95,121,86],"113":[95,121,86],"117":[95,121,86],"120":[95,121,86],"123":[95,121,86],"129":[95,121,86],"135":[95,121,86],"139":[95,121,86],"143":[95,121,86],"147":[95,121,86],"189":[95,121,86],"232":[121,86],"236":[95,121,86],"241":[121,86],"257":[121,86],"264":[86],"294":[121,86],"309":[121,86],"317":[121,86],"325":[86],"344":[86],"364":[86],"386":[95,121,86],"387":[95,121,86],"426":[95,121,86],"435":[95,121,86],"441":[95,121,86],"476":[95,121,86]},"packages/frontend/editor-ui/src/app/components/WorkflowCanvasHost.vue":{"9":[36,37,38,39,40],"35":[36,37,38,39,40],"45":[36,37,38,39,40],"50":[36,37,38,39,40],"60":[36,37,38,39,40]},"packages/frontend/editor-ui/src/app/components/WorkflowCanvasHostBody.vue":{"20":[36,37,38,39,40],"53":[36,37,38,39,40],"71":[36,37,38,39,40],"89":[36,37,38,39,40],"99":[36,37,38,39,40],"101":[39],"106":[36,40],"120":[36,37,38,39,40],"126":[36,37,38,39,40],"127":[36,37,38,39,40]},"packages/frontend/editor-ui/src/features/ai/instanceAi/components/ApprovalOptionList.vue":{"23":[37,39],"32":[37,39],"39":[37,39],"59":[37,39],"89":[37,39],"90":[37]},"packages/frontend/editor-ui/src/features/ai/instanceAi/components/ConfirmationFooter.vue":{"15":[37,38,39]},"packages/frontend/editor-ui/src/features/ai/instanceAi/components/ConfirmationPreview.vue":{"2":[37,39]},"packages/frontend/editor-ui/src/features/ai/instanceAi/components/InstanceAiWorkflowPreview.vue":{"31":[36,37,38,39,40],"48":[36,37,38,39,40],"105":[36,37,38,39,40],"106":[36,37,38,39,40],"124":[36,37,38,39,40],"141":[36,37,38,39,40],"170":[36,37,38,39,40]},"packages/frontend/editor-ui/src/features/ai/instanceAi/composables/useInstanceAiWorkflowPreviewExecution.ts":{"18":[37],"22":[37],"42":[36,37,38,39,40],"55":[37],"70":[36,37,38,39,40],"74":[37],"89":[36,37,38,39,40],"97":[36,37,38,39,40],"121":[36,37,38,39,40],"134":[36,37,38,39,40],"165":[36,37,38,39,40],"173":[36,37,38,39,40],"174":[36,37,38,39,40],"181":[36,37,38,39,40],"188":[36,37,38,39,40],"194":[36,40]},"packages/frontend/editor-ui/src/features/ai/instanceAi/confirmationKinds.ts":{"16":[37,38,39]},"packages/cli/src/controllers/mfa.controller.ts":{"22":[86],"33":[86],"68":[86],"76":[86],"119":[86],"160":[86],"204":[86]},"packages/cli/src/mfa/mfa.service.ts":{"17":[86],"27":[86],"35":[86],"36":[86],"39":[86],"45":[86],"60":[86],"68":[86],"80":[86],"83":[86],"91":[86],"95":[86],"100":[86],"107":[86],"120":[86],"127":[86],"136":[86],"145":[86],"155":[86],"165":[86]},"packages/cli/src/mfa/totp.service.ts":{"6":[86],"10":[86],"26":[86],"38":[86]},"localhost-33581/assets/_plugin-vue_export-helper-Da88TEg1.js":{"2":[86]},"localhost-33581/assets/chunk-CC9Q-vWm.js":{"8":[86],"9":[86],"10":[86],"19":[86],"23":[86],"29":[86],"33":[86],"34":[86]},"packages/frontend/@n8n/design-system/src/components/N8nFormBox/FormBox.vue":{"34":[93,76,55,86],"41":[76,55,86],"42":[76,55,86],"43":[76,55,86],"45":[93,76,55,86],"51":[93,76,55,86],"80":[93,76,55,86]},"packages/frontend/@n8n/design-system/src/types/menu.ts":{"56":[75,55,86,134]},"packages/frontend/editor-ui/src/app/utils/rbac/checks/isGuest.ts":{"4":[93,76,55,86]},"packages/frontend/editor-ui/src/app/utils/rbac/middleware/guest.ts":{"6":[93,76,55,86]},"packages/frontend/editor-ui/src/features/core/auth/components/MfaSetupModal.vue":{"25":[86],"56":[86],"60":[86],"67":[86],"76":[86],"89":[86],"104":[86],"132":[86],"151":[86],"154":[86],"173":[86],"177":[86],"182":[86],"183":[86],"186":[86],"202":[86],"211":[86],"230":[86],"234":[86],"235":[86],"239":[86],"240":[86],"243":[86],"245":[86],"262":[86]},"packages/frontend/editor-ui/src/features/core/auth/components/PromptMfaCodeModal.vue":{"12":[86],"31":[86],"48":[86],"52":[86],"54":[86],"67":[86],"78":[86]},"packages/frontend/editor-ui/src/features/core/auth/views/AuthView.vue":{"28":[76,55,86],"32":[76,55,86],"59":[93,76,55,86]},"packages/frontend/editor-ui/src/features/core/auth/views/MfaView.vue":{"63":[86],"86":[86],"107":[86],"116":[86],"117":[86],"129":[86],"155":[86],"164":[86],"189":[86],"194":[86],"204":[86],"225":[86]},"packages/frontend/editor-ui/src/features/core/auth/views/SigninView.vue":{"45":[93,76,55,86],"46":[93,76,55,86],"89":[86],"98":[76,55,86],"102":[76,55,86],"119":[76,55,86],"127":[76,55,86],"190":[86],"195":[86]},"packages/frontend/editor-ui/src/features/core/auth/views/SignoutView.vue":{"16":[55,86],"34":[55,86]},"packages/frontend/editor-ui/src/features/settings/sso/components/SSOLogin.vue":{"23":[93,76,55,86]},"packages/frontend/editor-ui/src/app/utils/stringUtils.ts":{"8":[144,146],"16":[144,146],"21":[144,146],"22":[144,146]},"packages/frontend/editor-ui/src/features/ndv/panel/components/InputNodeSelect.vue":{"18":[7,8,12,113,6,141,144,145,146,14,70],"29":[7,8,12,113,6,141,144,145,146,14,70],"33":[7,8,12,113,6,141,144,145,146,14,70],"40":[7,8,12,113,6,141,144,145,146,14,70],"43":[12,6,141,144,145,146,70],"56":[12,6,141,144,145,146,70],"58":[12,6,141,144,145,146,70],"65":[12,6,141,144,145,146,70],"70":[12,6,141,144,145,146,70],"79":[12,6,141,144,145,146,70],"106":[12,6,141,144,145,146,70],"110":[12,6,141,144,145,146,70],"116":[141,70],"118":[7,8,12,113,6,141,144,145,146,14,70],"133":[7,8,12,113,6,141,144,145,146,14,70],"139":[7,8,12,113,6,141,144,145,146,14,70]},"packages/@n8n/expression-runtime/src/evaluator/error-classification.ts":{"5":[145]},"packages/core/src/execution-engine/node-execution-context/load-options-context.ts":{"21":[1,26,3,4,5,145,14,148,149],"36":[1,26,3,4,5,145,14,148,149],"40":[1,26,3,4,5,145,14,148,149],"69":[1,26,3,4,5,145,14,148,149]},"packages/core/src/execution-engine/node-execution-context/utils/extract-value.ts":{"20":[48,50,68,6,145,146,14,148],"39":[48,50,68,6,145,146,14,148],"59":[48,50,68,6,145,146,14,148],"68":[48,50,68,6,145,146,14,148],"101":[48,50,68,6,145,146,14,148],"121":[48,50,68,6,145,146,14,148],"155":[48,50,68,6,145,146,14,148]},"packages/core/src/execution-engine/node-execution-context/utils/request-helpers/authentication.ts":{"22":[26,182,50,145],"150":[26,182,50,145]},"packages/nodes-base/nodes/Airtable/Airtable.node.ts":{"8":[145]},"packages/nodes-base/nodes/Airtable/v1/AirtableV1.node.ts":{"558":[145],"565":[145],"833":[145],"834":[145],"838":[145],"839":[145]},"packages/nodes-base/nodes/Airtable/v1/GenericFunctions.ts":{"29":[145],"72":[145],"100":[145]},"packages/nodes-base/nodes/Airtable/v2/AirtableV2.node.ts":{"15":[145],"29":[145]},"packages/nodes-base/nodes/Airtable/v2/actions/base/getMany.operation.ts":{"88":[145],"112":[145]},"packages/nodes-base/nodes/Airtable/v2/actions/base/getSchema.operation.ts":{"30":[145]},"packages/nodes-base/nodes/Airtable/v2/actions/record/create.operation.ts":{"51":[145]},"packages/nodes-base/nodes/Airtable/v2/actions/record/deleteRecord.operation.ts":{"36":[145]},"packages/nodes-base/nodes/Airtable/v2/actions/record/get.operation.ts":{"60":[145],"85":[145]},"packages/nodes-base/nodes/Airtable/v2/actions/record/search.operation.ts":{"147":[145],"183":[145],"214":[145],"224":[145]},"packages/nodes-base/nodes/Airtable/v2/actions/record/update.operation.ts":{"57":[145]},"packages/nodes-base/nodes/Airtable/v2/actions/record/upsert.operation.ts":{"52":[145],"120":[145],"125":[145],"142":[145]},"packages/nodes-base/nodes/Airtable/v2/actions/router.ts":{"8":[145]},"packages/nodes-base/nodes/Airtable/v2/helpers/utils.ts":{"11":[145],"16":[145],"41":[145],"50":[145],"56":[145],"63":[145],"78":[145],"97":[145],"112":[145],"121":[145],"131":[145]},"packages/nodes-base/nodes/Airtable/v2/methods/listSearch.ts":{"11":[145],"44":[145],"54":[145],"91":[145],"101":[145],"117":[145],"145":[145]},"packages/nodes-base/nodes/Airtable/v2/methods/loadOptions.ts":{"6":[145],"19":[145],"42":[145],"57":[145],"62":[145],"65":[145],"80":[145]},"packages/nodes-base/nodes/Airtable/v2/methods/resourceMapping.ts":{"48":[145],"63":[145],"76":[145],"88":[145],"90":[145],"120":[145]},"packages/nodes-base/nodes/Airtable/v2/transport/index.ts":{"20":[145],"59":[145],"87":[145],"94":[145],"131":[145]},"packages/nodes-base/nodes/Function/Function.node.ts":{"67":[145,73],"79":[145,73],"80":[145,73],"101":[145,73],"102":[145,73],"115":[145,73],"215":[145,73]},"packages/nodes-base/nodes/Typeform/GenericFunctions.ts":{"40":[145],"77":[145],"113":[145]},"packages/nodes-base/nodes/Typeform/TypeformTrigger.node.ts":{"122":[145],"160":[145],"179":[145],"205":[145],"230":[145],"305":[145]},"packages/nodes-base/nodes/Typeform/TypeformTriggerHelpers.ts":{"18":[145],"24":[145],"36":[145]},"packages/nodes-base/utils/webhook-signature-verification.ts":{"64":[16,18,8,38,44,50,111,145],"106":[16,18,8,38,44,50,111,145]},"packages/workflow/src/errors/abstract/execution-base.error.ts":{"28":"b:QoAABAIAAAAQAAAABAAAAAACAgAUACAA","50":"b:QoAABAIAAAAQAAAABAAAAAACAgAUACAA"},"packages/workflow/src/errors/abstract/node.error.ts":{"40":[15,1,164,26,98,33,137,68,145,162],"78":[15,1,164,26,98,33,137,68,145,162],"102":[15,1,164,26,98,33,137,68,145,162],"110":[15,1,164,26,98,33,137,68,145,162],"142":[15,1,164,26,98,33,137,68,145,162],"152":[15,1,164,26,98,33,137,68,145,162]},"packages/workflow/src/errors/expression.error.ts":{"32":[15,145],"56":[15,145]},"packages/workflow/src/errors/node-api.error.ts":{"124":[1,26,98,33,137,145],"277":[1,26,98,33,137,145],"285":[1,26,98,33,137,145],"301":[1,26,98,33,137,145]},"packages/workflow/src/errors/node-operation.error.ts":{"12":[15,1,164,26,98,68,145,148,162],"31":[15,1,164,26,98,68,145,148,162]},"packages/workflow/src/node-parameters/filter-parameter.ts":{"24":[48,68,6,145,146],"32":[48,68,6,145,146],"66":[48,68,6,145,146],"71":[48,68,6,145,146],"106":[48,68,6,145,146],"118":[48,68,6,145,146],"124":[48,68,6,145,146],"196":[48,68,6,145,146],"209":[48,68,6,145,146],"211":[48,68,6,145,146],"222":[48,68,6,145,146],"419":[48,68,6,145,146],"423":[48,68,6,145,146],"437":[48,68,6,145,146],"442":[48,68,6,145,146]},"packages/workflow/src/workflow-data-proxy-helpers.ts":{"3":[145]},"localhost-38239/assets/verified-Ge9vhh2v.js":{"8":[145,14]},"packages/frontend/@n8n/design-system/src/components/N8nIcon/custom/empty-output.svg":{"9":[145]},"packages/frontend/@n8n/design-system/src/components/N8nIcon/custom/node-execution-error.svg":{"11":[15,87,1,7,33,137,68,6,145,157,158]},"packages/frontend/@n8n/design-system/src/components/N8nIcon/nodes/respond-to-webhook.svg":{"11":[26,94,51,145,147]},"packages/frontend/editor-ui/src/features/ndv/runData/components/error/NodeErrorView.vue":{"47":[15,87,1,7,137,6,145],"70":[15,87,1,7,137,6,145],"74":[15,145],"87":[15,87,1,7,137,6,145],"98":[15,87,1,7,137,6,145],"102":[1,7,137],"111":[15,87,1,7,137,6,145],"118":[1,7,137],"142":[15,87,1,7,137,6,145],"159":[15,87,1,7,137,6,145],"164":[15,87],"171":[15,87,1,7,137,6,145],"178":[15,1,7,137,145],"194":[15,87,1,7,145],"198":[15,87,1,7,145],"223":[15,87,1,7,137,6,145],"235":[15,87,1,7,137,6,145],"238":[15,87,1,7,137,6,145],"263":[15,145],"273":[15,145],"279":[15,145],"293":[15,145],"415":[15,87,1,7,137,145],"449":[15,87],"480":[15,87,1,7,137,6,145],"536":[15,87,1,7,137,6,145],"575":[1,7,137]},"packages/frontend/editor-ui/src/features/shared/editors/components/JsEditor/JsEditor.vue":{"31":[1,145],"36":[1,145],"40":[1,145],"57":[1,145],"73":[1,145],"77":[1,145],"102":[1,145],"121":[1,145]},"packages/frontend/editor-ui/src/features/shared/nodeCreator/components/ItemTypes/LabelItem.vue":{"11":"b:AEEBFABAkAAAAAAAAAAAsABQWhAAAAAA"},"packages/@n8n/nodes-langchain/nodes/embeddings/EmbeddingsHuggingFaceInference/EmbeddingsHuggingFaceInference.node.ts":{"16":[14],"86":[14],"94":[14],"132":[14]},"packages/@n8n/nodes-langchain/nodes/memory/MemoryXata/MemoryXata.node.ts":{"23":[14],"105":[14]},"packages/@n8n/nodes-langchain/nodes/memory/descriptions.ts":{"28":[1,3,4,5,13,14],"63":[1,3,4,5,13,14]},"packages/@n8n/nodes-langchain/nodes/output_parser/OutputParserStructured/OutputParserStructured.node.ts":{"30":[14],"196":[14]},"packages/@n8n/nodes-langchain/nodes/text_splitters/TextSplitterTokenSplitter/TextSplitterTokenSplitter.node.ts":{"13":[14],"62":[14]},"packages/@n8n/nodes-langchain/nodes/text_splitters/TextSplitterTokenSplitter/TokenTextSplitter.ts":{"16":[14],"28":[14],"36":[14]},"packages/@n8n/nodes-langchain/nodes/tools/ToolWikipedia/ToolWikipedia.node.ts":{"17":[1,14],"25":[1,14],"58":[1,14],"64":[1,14]},"packages/@n8n/nodes-langchain/nodes/vector_store/VectorStoreQdrant/Qdrant.utils.ts":{"9":[14],"28":[14]},"packages/@n8n/nodes-langchain/nodes/vector_store/VectorStoreSupabase/VectorStoreSupabase.node.ts":{"67":[14],"84":[14]},"packages/@n8n/nodes-langchain/nodes/vector_store/VectorStoreWeaviate/Weaviate.utils.ts":{"22":[14],"68":[14],"127":[14]},"packages/@n8n/nodes-langchain/nodes/vector_store/shared/methods/listSearch.ts":{"10":[14],"19":[14],"27":[14],"58":[14],"65":[14],"73":[14],"87":[14],"95":[14],"102":[14]},"packages/@n8n/nodes-langchain/utils/output_parsers/N8nItemListOutputParser.ts":{"10":[1,3,4,5,8,10,12,6,13,14],"26":[1,3,4,5,8,10,12,6,13,14],"29":[1,3,4,5,8,10,12,6,13,14],"30":[1,3,4,5,8,10,12,6,13,14],"42":[1,3,4,5,8,10,12,6,13,14],"57":[1,3,4,5,8,10,12,6,13,14]},"packages/@n8n/nodes-langchain/utils/output_parsers/N8nOutputFixingParser.ts":{"15":[1,3,4,5,8,10,12,6,13,14],"24":[1,3,4,5,8,10,12,6,13,14],"35":[1,3,4,5,8,10,12,6,13,14],"42":[1,3,4,5,8,10,12,6,13,14],"88":[1,3,4,5,8,10,12,6,13,14],"92":[1,3,4,5,8,10,12,6,13,14]},"packages/@n8n/nodes-langchain/utils/output_parsers/N8nOutputParser.ts":{"4":[1,3,4,5,8,10,12,6,13,14],"5":[1,3,4,5,8,10,12,6,13,14],"15":[1,3,4,5,8,10,12,6,13,14]},"packages/@n8n/nodes-langchain/utils/output_parsers/N8nStructuredOutputParser.ts":{"22":[1,3,4,5,8,10,12,6,13,14],"31":[1,3,4,5,8,10,12,6,13,14],"131":[1,3,4,5,8,10,12,6,13,14],"148":[1,3,4,5,8,10,12,6,13,14],"175":[1,3,4,5,8,10,12,6,13,14]},"packages/@n8n/nodes-langchain/utils/output_parsers/langchainParserError.ts":{"11":[1,3,4,5,98,8,10,12,6,13,14],"19":[1,3,4,5,98,8,10,12,6,13,14],"26":[1,3,4,5,98,8,10,12,6,13,14],"30":[1,3,4,5,98,8,10,12,6,13,14],"33":[1,3,4,5,98,8,10,12,6,13,14],"41":[1,3,4,5,98,8,10,12,6,13,14]},"packages/cli/src/modules/external-secrets.ee/provider-registry.service.ts":{"10":"b:AkEBEAIAgBAAAgAAQgAAAAAAEAAAAAgA","13":"b:AkEBEAIAgBAAAgAAQgAAAAAAEAAAAAgA","17":"b:AkEBEAIAgBAAAgAAQgAAAAAAEAAAAAgA","21":"b:AkEBEAIAgBAAAgAAQgAAAAAAEAAAAAgA","25":"b:AkEBEAIAgBAAAgAAQgAAAAAAEAAAAAgA","29":"b:AkEBEAIAgBAAAgAAQgAAAAAAEAAAAAgA","33":"b:AkEBEAIAgBAAAgAAQgAAAAAAEAAAAAgA","43":"b:AkEBEAIAgBAAAgAAQgAAAAAAEAAAAAgA","47":"b:AkEBEAIAgBAAAgAAQgAAAAAAEAAAAAgA","51":"b:AkEBEAIAgBAAAgAAQgAAAAAAEAAAAAgA","55":"b:AkEBEAIAgBAAAgAAQgAAAAAAEAAAAAgA","57":"b:AkEBEAIAgBAAAgAAQgAAAAAAEAAAAAgA","58":"b:AkEBEAIAgBAAAgAAQgAAAAAAEAAAAAgA"},"packages/cli/src/modules/external-secrets.ee/secrets-providers-completions.controller.ee.ts":{"15":"b:AkEBEAIAgBAAAgAASgAAAAAAEAAAAAAA","24":"b:AkEBEAIAgBAAAgAASgAAAAAAEAAAAAAA","55":"b:AkEBEAIAgBAAAgAASgAAAAAAEAAAAAAA","72":"b:AkEBEAIAgBAAAgAASgAAAAAAEAAAAAAA","84":"b:AkEBEAIAgBAAAgAASgAAAAAAEAAAAAAA"},"packages/cli/src/modules/external-secrets.ee/secrets-providers-connections.service.ee.ts":{"39":"b:GkEBEQsAgBAAAwAASxAAIAAAEAACAAAA","53":"b:GkEBEQsAgBAAAwAASxAAIAAAEAACAAAA","79":"b:GkEBEQsAgBAAAwAASxAAIAAAEAACAAAA","107":"b:GkEBEQsAgBAAAwAASxAAIAAAEAACAAAA","124":"b:GkEBEQsAgBAAAwAASxAAIAAAEAACAAAA","141":"b:GkEBEQsAgBAAAwAASxAAIAAAEAACAAAA","146":"b:GkEBEQsAgBAAAwAASxAAIAAAEAACAAAA","147":"b:GkEBEQsAgBAAAwAASxAAIAAAEAACAAAA","152":"b:GkEBEQsAgBAAAwAASxAAIAAAEAACAAAA","153":"b:GkEBEQsAgBAAAwAASxAAIAAAEAACAAAA","168":"b:GkEBEQsAgBAAAwAASxAAIAAAEAACAAAA","190":"b:GkEBEQsAgBAAAwAASxAAIAAAEAACAAAA","212":"b:GkEBEQsAgBAAAwAASxAAIAAAEAACAAAA","221":"b:GkEBEQsAgBAAAwAASxAAIAAAEAACAAAA","244":"b:GkEBEQsAgBAAAwAASxAAIAAAEAACAAAA","252":"b:GkEBEQsAgBAAAwAASxAAIAAAEAACAAAA","262":"b:GkEBEQsAgBAAAwAASxAAIAAAEAACAAAA","266":"b:GkEBEQsAgBAAAwAASxAAIAAAEAACAAAA","274":"b:GkEBEQsAgBAAAwAASxAAIAAAEAACAAAA","282":"b:GkEBEQsAgBAAAwAASxAAIAAAEAACAAAA","286":"b:GkEBEQsAgBAAAwAASxAAIAAAEAACAAAA","288":"b:GkEBEQsAgBAAAwAASxAAIAAAEAACAAAA","295":"b:GkEBEQsAgBAAAwAASxAAIAAAEAACAAAA","310":"b:GkEBEQsAgBAAAwAASxAAIAAAEAACAAAA","320":"b:GkEBEQsAgBAAAwAASxAAIAAAEAACAAAA","338":"b:GkEBEQsAgBAAAwAASxAAIAAAEAACAAAA","339":"b:GkEBEQsAgBAAAwAASxAAIAAAEAACAAAA","350":"b:GkEBEQsAgBAAAwAASxAAIAAAEAACAAAA","376":"b:GkEBEQsAgBAAAwAASxAAIAAAEAACAAAA","403":"b:GkEBEQsAgBAAAwAASxAAIAAAEAACAAAA","407":"b:GkEBEQsAgBAAAwAASxAAIAAAEAACAAAA","420":"b:GkEBEQsAgBAAAwAASxAAIAAAEAACAAAA","437":"b:GkEBEQsAgBAAAwAASxAAIAAAEAACAAAA","441":"b:GkEBEQsAgBAAAwAASxAAIAAAEAACAAAA","468":"b:GkEBEQsAgBAAAwAASxAAIAAAEAACAAAA","472":"b:GkEBEQsAgBAAAwAASxAAIAAAEAACAAAA","485":"b:GkEBEQsAgBAAAwAASxAAIAAAEAACAAAA","501":"b:GkEBEQsAgBAAAwAASxAAIAAAEAACAAAA","523":"b:GkEBEQsAgBAAAwAASxAAIAAAEAACAAAA"},"packages/nodes-base/nodes/Notion/Notion.node.ts":{"9":[8,55,125,14,156]},"packages/nodes-base/nodes/Notion/shared/GenericFunctions.ts":{"27":[8,55,125,13,14,156],"48":[8,55,125,13,14,156],"84":[8,55,125,13,14,156],"118":[8,55,125,13,14,156],"139":[8,55,125,13,14,156],"160":[8,55,125,13,14,156],"205":[8,55,125,13,14,156],"213":[8,55,125,13,14,156],"219":[8,55,125,13,14,156],"225":[8,55,125,13,14,156],"249":[8,55,125,13,14,156],"296":[8,55,125,13,14,156],"305":[8,55,125,13,14,156],"321":[8,55,125,13,14,156],"328":[8,55,125,13,14,156],"332":[8,55,125,13,14,156],"339":[8,55,125,13,14,156],"358":[8,55,125,13,14,156],"367":[8,55,125,13,14,156],"370":[8,55,125,13,14,156],"371":[8,55,125,13,14,156],"373":[8,55,125,13,14,156],"376":[8,55,125,13,14,156],"394":[8,55,125,13,14,156],"396":[8,55,125,13,14,156],"397":[8,55,125,13,14,156],"414":[8,55,125,13,14,156],"473":[8,55,125,13,14,156],"485":[8,55,125,13,14,156],"497":[8,55,125,13,14,156],"505":[8,55,125,13,14,156],"507":[8,55,125,13,14,156],"511":[8,55,125,13,14,156],"512":[8,55,125,13,14,156],"518":[8,55,125,13,14,156],"519":[8,55,125,13,14,156],"528":[8,55,125,13,14,156],"529":[8,55,125,13,14,156],"577":[8,55,125,13,14,156],"584":[8,55,125,13,14,156],"605":[8,55,125,13,14,156],"611":[8,55,125,13,14,156],"613":[8,55,125,13,14,156],"617":[8,55,125,13,14,156],"636":[8,55,125,13,14,156],"644":[8,55,125,13,14,156],"652":[8,55,125,13,14,156],"654":[8,55,125,13,14,156],"660":[8,55,125,13,14,156],"662":[8,55,125,13,14,156],"672":[8,55,125,13,14,156],"725":[8,55,125,13,14,156],"734":[8,55,125,13,14,156],"821":[8,55,125,13,14,156],"839":[8,55,125,13,14,156],"855":[8,55,125,13,14,156],"876":[8,55,125,13,14,156],"916":[8,55,125,13,14,156],"925":[8,55,125,13,14,156],"951":[8,55,125,13,14,156],"964":[8,55,125,13,14,156],"1084":[8,55,125,13,14,156],"1098":[8,55,125,13,14,156],"1099":[8,55,125,13,14,156],"1112":[8,55,125,13,14,156],"1129":[8,55,125,13,14,156],"1134":[8,55,125,13,14,156],"1142":[8,55,125,13,14,156],"1159":[8,55,125,13,14,156],"1171":[8,55,125,13,14,156],"1211":[8,55,125,13,14,156]},"packages/nodes-base/nodes/Notion/shared/descriptions/Blocks.ts":{"164":[8,55,125,14,156],"171":[8,55,125,14,156],"407":[8,55,125,14,156],"459":[8,55,125,14,156],"475":[8,55,125,14,156],"491":[8,55,125,14,156],"501":[8,55,125,14,156],"511":[8,55,125,14,156],"526":[8,55,125,14,156],"602":[8,55,125,14,156]},"packages/nodes-base/nodes/Notion/shared/descriptions/Filters.ts":{"1":[8,55,125,13,14,156]},"packages/nodes-base/nodes/Notion/shared/methods/listSearch.ts":{"10":[8,55,125,13,14,156],"28":[8,55,125,13,14,156]},"packages/nodes-base/nodes/Notion/v1/NotionV1.node.ts":{"34":[8,55,125,14,156],"44":[8,55,125,14,156],"69":[8,55,125,14,156],"80":[8,55,125,14,156],"92":[8,55,125,14,156],"103":[8,55,125,14,156],"106":[8,55,125,14,156],"116":[8,55,125,14,156],"121":[8,55,125,14,156],"127":[8,55,125,14,156],"132":[8,55,125,14,156],"145":[8,55,125,14,156],"172":[8,55,125,14,156],"184":[8,55,125,14,156],"195":[8,55,125,14,156],"203":[8,55,125,14,156],"223":[8,55,125,14,156],"404":[8,55,125,14,156],"409":[8,55,125,14,156]},"packages/nodes-base/nodes/Notion/v2/NotionV2.node.ts":{"38":[8,55,125,14,156],"47":[8,55,125,14,156],"140":[8,55,125,14,156],"453":[8,55,125,14,156],"457":[8,55,125,14,156]},"packages/nodes-base/nodes/Notion/v2/methods/loadOptions.ts":{"11":[8,55,125,14,156],"37":[8,55,125,14,156],"49":[8,55,125,14,156],"63":[8,55,125,14,156],"75":[8,55,125,14,156],"79":[8,55,125,14,156],"91":[8,55,125,14,156],"99":[8,55,125,14,156],"105":[8,55,125,14,156],"111":[8,55,125,14,156],"125":[8,55,125,14,156],"154":[8,55,125,14,156],"166":[8,55,125,14,156],"181":[8,55,125,14,156],"189":[8,55,125,14,156]},"packages/nodes-base/nodes/Notion/v3/NotionV3.node.ts":{"15":[8,55,125,14,156],"24":[8,55,125,14,156]},"packages/nodes-base/nodes/Notion/v3/actions/block/Block.resource.ts":{"18":[8,55,125,14,156],"47":[8,55,125,14,156],"142":[8,55,125,14,156],"177":[8,55,125,14,156],"205":[8,55,125,14,156]},"packages/nodes-base/nodes/Notion/v3/actions/common.descriptions.ts":{"212":[8,55,125,14,156],"231":[8,55,125,14,156],"251":[8,55,125,14,156],"299":[8,55,125,14,156],"319":[8,55,125,14,156]},"packages/nodes-base/nodes/Notion/v3/actions/dataSource/DataSource.resource.ts":{"78":[8,55,125,14,156],"106":[8,55,125,14,156]},"packages/nodes-base/nodes/Notion/v3/actions/database/Database.resource.ts":{"47":[8,55,125,14,156]},"packages/nodes-base/nodes/Notion/v3/actions/databasePage/DataSourceFilters.ts":{"105":[8,55,125,14,156],"106":[8,55,125,14,156],"112":[8,55,125,14,156],"113":[8,55,125,14,156],"130":[8,55,125,14,156],"131":[8,55,125,14,156],"136":[8,55,125,14,156],"141":[8,55,125,14,156],"148":[8,55,125,14,156],"152":[8,55,125,14,156],"161":[8,55,125,14,156],"197":[8,55,125,14,156],"207":[8,55,125,14,156],"217":[8,55,125,14,156],"265":[8,55,125,14,156],"266":[8,55,125,14,156],"272":[8,55,125,14,156],"358":[8,55,125,14,156],"365":[8,55,125,14,156],"371":[8,55,125,14,156],"373":[8,55,125,14,156]},"packages/nodes-base/nodes/Notion/v3/actions/databasePage/DatabasePage.resource.ts":{"40":[8,55,125,14,156],"127":[8,55,125,14,156],"477":[8,55,125,14,156],"493":[8,55,125,14,156],"568":[8,55,125,14,156],"598":[8,55,125,14,156],"660":[8,55,125,14,156]},"packages/nodes-base/nodes/Notion/v3/actions/page/Page.resource.ts":{"206":[8,55,125,14,156],"230":[8,55,125,14,156],"261":[8,55,125,14,156],"285":[8,55,125,14,156],"319":[8,55,125,14,156]},"packages/nodes-base/nodes/Notion/v3/actions/router.ts":{"22":[8,55,125,14,156]},"packages/nodes-base/nodes/Notion/v3/actions/user/User.resource.ts":{"36":[8,55,125,14,156],"54":[8,55,125,14,156]},"packages/nodes-base/nodes/Notion/v3/helpers/utils.ts":{"12":[8,55,125,13,14,156],"38":[8,55,125,13,14,156],"48":[8,55,125,13,14,156],"74":[8,55,125,13,14,156],"78":[8,55,125,13,14,156],"79":[8,55,125,13,14,156],"102":[8,55,125,13,14,156],"106":[8,55,125,13,14,156],"113":[8,55,125,13,14,156],"119":[8,55,125,13,14,156],"150":[8,55,125,13,14,156],"156":[8,55,125,13,14,156],"190":[8,55,125,13,14,156],"199":[8,55,125,13,14,156],"237":[8,55,125,13,14,156],"245":[8,55,125,13,14,156],"253":[8,55,125,13,14,156],"261":[8,55,125,13,14,156],"285":[8,55,125,13,14,156],"309":[8,55,125,13,14,156],"324":[8,55,125,13,14,156],"340":[8,55,125,13,14,156],"360":[8,55,125,13,14,156],"371":[8,55,125,13,14,156],"389":[8,55,125,13,14,156],"398":[8,55,125,13,14,156],"420":[8,55,125,13,14,156]},"packages/nodes-base/nodes/Notion/v3/methods/listSearch.ts":{"10":[8,55,125,13,14,156],"15":[8,55,125,13,14,156],"21":[8,55,125,13,14,156],"26":[8,55,125,13,14,156],"32":[8,55,125,13,14,156],"38":[8,55,125,13,14,156],"47":[8,55,125,13,14,156],"64":[8,55,125,13,14,156],"68":[8,55,125,13,14,156],"88":[8,55,125,13,14,156]},"packages/nodes-base/nodes/Notion/v3/methods/loadOptions.ts":{"22":[8,55,125,13,14,156],"37":[8,55,125,13,14,156],"56":[8,55,125,13,14,156],"59":[8,55,125,13,14,156],"60":[8,55,125,13,14,156],"70":[8,55,125,13,14,156],"77":[8,55,125,13,14,156],"84":[8,55,125,13,14,156],"88":[8,55,125,13,14,156],"109":[8,55,125,13,14,156],"123":[8,55,125,13,14,156],"142":[8,55,125,13,14,156],"155":[8,55,125,13,14,156],"178":[8,55,125,13,14,156]},"packages/nodes-base/nodes/Notion/v3/transport/index.ts":{"17":[8,55,125,13,14,156],"21":[8,55,125,13,14,156],"54":[8,55,125,13,14,156],"88":[8,55,125,13,14,156]},"packages/frontend/@n8n/design-system/src/components/N8nIcon/custom/grip-lines-vertical.svg":{"9":[1,7,10,13,14]},"packages/frontend/@n8n/design-system/src/components/N8nIcon/custom/vector-square.svg":{"9":[1,7,10,13,14]},"packages/frontend/@n8n/design-system/src/components/N8nIcon/nodes/ai-agent.svg":{"11":[1,7,8,10,12,113,65,6,13,14]},"packages/frontend/@n8n/design-system/src/components/N8nIcon/nodes/basic-llm-chain.svg":{"11":[7,13,14]},"packages/frontend/@n8n/design-system/src/components/N8nIcon/nodes/contextual-compression-retriever.svg":{"11":[14]},"packages/frontend/@n8n/design-system/src/components/N8nIcon/nodes/guardrails.svg":{"11":[14]},"packages/frontend/@n8n/design-system/src/components/N8nIcon/nodes/information-extractor.svg":{"11":[28,8,48,55,124,125,14,156]},"packages/frontend/@n8n/design-system/src/components/N8nIcon/nodes/multiquery-retriever.svg":{"11":[14]},"packages/frontend/@n8n/design-system/src/components/N8nIcon/nodes/sentiment-analysis.svg":{"11":[14]},"packages/frontend/@n8n/design-system/src/components/N8nIcon/nodes/structured-output-parser.svg":{"11":[1,14]},"packages/frontend/@n8n/design-system/src/components/N8nIcon/nodes/summarization-chain.svg":{"11":[14]},"packages/frontend/@n8n/design-system/src/components/N8nIcon/nodes/text-classifier.svg":{"11":[48,14]},"packages/frontend/@n8n/design-system/src/components/N8nIcon/nodes/token-splitter.svg":{"11":[14]},"packages/frontend/@n8n/design-system/src/components/N8nIcon/nodes/workflow-retriever.svg":{"11":[14]},"packages/frontend/editor-ui/src/app/components/Banner.vue":{"48":"b:kkEBEAIAgBABAgAASgACIAAAEAAAAAAA","58":"b:kkEBEAIAgBABAgAASgACIAAAEAAAAAAA","65":"b:kkEBEAIAgBABAgAASgACIAAAEAAAAAAA","71":"b:kkEBEAIAgBABAgAASgACIAAAEAAAAAAA","78":"b:kkEBEAIAgBABAgAASgACIAAAEAAAAAAA","86":[60]},"packages/frontend/editor-ui/src/app/components/SaveButton.vue":{"12":"b:kkEBEAIAgLABAgAASgACIQAAEAAAAAAA","36":"b:kkEBEAIAgLABAgAASgACIQAAEAAAAAAA","45":"b:kkEBEAIAgLABAgAASgACIQAAEAAAAAAA","73":"b:kkEAEAIAgLABAgAAAgACIQAAAAAAAAAA"},"packages/frontend/editor-ui/src/app/utils/telemetryUtils.ts":{"5":[60,140,141,142,143,14,148,149],"13":[60,140,141,142,143,14,149],"14":[60,140,141,142,143,149],"21":[60,140,141,142,143,149],"27":[140,142,149],"28":[140,142,149],"71":[60,140,141,142,143,149]},"packages/frontend/editor-ui/src/features/credentials/components/CredentialEdit/CredentialConfig.vue":{"87":"b:kkEBEAIAgBABAgAASgACIAAAEAAAAAAA","120":"b:kkEBEAIAgBABAgAASgACIAAAEAAAAAAA","141":"b:kkEBEAIAgBABAgAASgACIAAAEAAAAAAA","144":[16,8,99,148,73],"149":"b:kkEBEAIAgBABAgAASgACIAAAEAAAAAAA","150":"b:kkEBEAIAgBABAgAASgACIAAAEAAAAAAA","161":"b:kkEBEAIAgBABAgAASgACIAAAEAAAAAAA","191":"b:kkEBEAIAgBABAgAASgACIAAAEAAAAAAA","196":[16,8,99,148,73],"204":"b:kkEBEAIAgBABAgAASgACIAAAEAAAAAAA","213":"b:kkEBEAIAgBABAgAASgACIAAAEAAAAAAA","234":"b:kkEBEAIAgBABAgAASgACIAAAEAAAAAAA","242":"b:kkEBEAIAgBABAgAASgACIAAAEAAAAAAA","250":"b:kkEBEAIAgBABAgAASgACIAAAEAAAAAAA","252":"b:kkEBEAIAgBABAgAASgACIAAAEAAAAAAA","254":"b:kkEBEAIAgBABAgAASgACIAAAEAAAAAAA","265":[16],"269":"b:kkEBEAIAgBABAgAASgACIAAAEAAAAAAA","271":"b:kkEAEAIAgBABAgAACgACIAAAEAAAAAAA","275":"b:kkEBEAIAgBABAgAASgACIAAAEAAAAAAA","282":[16,8,99,148,73],"289":"b:kkEBEAIAgBABAgAASgACIAAAEAAAAAAA","300":"b:kkEBEAIAgBABAgAASgACIAAAEAAAAAAA","309":"b:kkEBEAIAgBABAgAASgACIAAAEAAAAAAA","311":"b:kkEBEAIAgBABAgAASgACIAAAEAAAAAAA","316":"b:kkEBEAIAgBABAgAASgACIAAAEAAAAAAA","320":"b:kkEBEAIAgBABAgAACgACIAAAEAAAAAAA","324":"b:kkEAEAIAgBABAgAACAACIAAAEAAAAAAA","337":[16,8],"361":[16],"379":[99],"384":"b:kkEBEAIAgBABAgAASgACIAAAEAAAAAAA","436":"b:kkEBEAIAgBABAgAACgACIAAAEAAAAAAA","438":"b:kkEBEAIAgBABAgAACgACIAAAEAAAAAAA","576":"b:kkEBEAIAgBABAgAASgACIAAAEAAAAAAA","634":[16,8,99,148],"635":[99,148],"639":"b:kkEBEAIAgBABAgAAQgACIAAAEAAAAAAA","646":[73],"696":"b:kkEBEAIAgAABAgAASgACIAAAEAAAAAAA","698":"b:kkEBEAIAgAABAgAASgACIAAAEAAAAAAA","700":"b:kkEBEAIAgAABAgAASgACIAAAEAAAAAAA"},"packages/frontend/editor-ui/src/features/credentials/components/CredentialEdit/CredentialEdit.vue":{"91":"b:kkEBEAIAgBABAgAASgACIAAAEAAAAAAA","93":[8,148,73],"98":[8,148,73],"175":"b:kkEBEAIAgBABAgAASgACIAAAEAAAAAAA","177":"b:kkEBEAIAgBABAgAASgACIAAAEAAAAAAA","187":"b:kkEBEAIAgBABAgAASgACIAAAEAAAAAAA","193":"b:kkEBEAIAgBABAgAASgACIAAAEAAAAAAA","194":"b:kkEBEAIAgBABAgAASgACIAAAEAAAAAAA","195":"b:kkEBEAIAgBABAgAASgACIAAAEAAAAAAA","196":"b:kkEBEAIAgBABAgAASgACIAAAEAAAAAAA","197":"b:kkEBEAIAgBABAgAASAACIAAAEAAAAAAA","198":"b:kkEBEAIAgBABAgAASAACIAAAEAAAAAAA","245":"b:kkEBEAIAgBABAgAASgACIAAAEAAAAAAA","252":"b:kkEBEAIAgBABAgAASgACIAAAEAAAAAAA","257":"b:kkEAEAIAgBABAgAACgACIAAAAAAAAAAA","262":"b:kkEBEAIAgBABAgAASgACIAAAEAAAAAAA","268":"b:kkEBEAIAgBABAgAASgACIAAAEAAAAAAA","273":"b:kkEBEAIAgBABAgAASgACIAAAEAAAAAAA","277":"b:kkEBEAIAgBABAgAASgACIAAAEAAAAAAA","303":"b:kkEBEAIAgBABAgAASgACIAAAEAAAAAAA","313":"b:kkEBEAIAgBABAgAASgACIAAAEAAAAAAA","320":"b:kkEBEAIAgBABAgAASgACIAAAEAAAAAAA","327":[97,8,33,64,125],"329":"b:kkEBEAIAgBABAgAASgACIAAAEAAAAAAA","399":"b:kkEBEAIAgBABAgAASgACIAAAEAAAAAAA","417":"b:kkEAEAIAgBABAgAAAgACIAAAEAAAAAAA","472":[97,33,64,125],"487":[64,125],"496":[97,33],"501":[16,8],"509":[16,8],"516":[16,8],"582":"b:kkEAEAIAgBABAgAACAACIAAAEAAAAAAA","586":[1,7,28,8,55,113,14,73],"590":[28,8,33,60,64,125,73],"596":"b:kkEAEAAAgAABAgAACgACIAAAAAAAAAAA","597":"b:kkEAEAAAgAABAgAACgACIAAAAAAAAAAA","612":"b:kkEAEAIAgBABAgAACgACIAAAEAAAAAAA","705":"b:kkEAEAIAgBABAgAACgACIAAAEAAAAAAA","771":"b:kkEAEAIAgBABAgAACgACIAAAEAAAAAAA","804":"b:kkEAEAIAgBABAgAACAACIAAAEAAAAAAA","824":"b:kkEAEAIAgBABAgAACAACIAAAEAAAAAAA","874":[97,8,33,64,125],"929":[8],"999":[99,148,73],"1089":[148],"1095":[99,148],"1102":[99,148],"1135":[99],"1150":[99],"1155":[99],"1207":[16,8],"1266":"b:kkEBEAIAgBABAgAASgACIAAAEAAAAAAA","1280":"b:kkEBEAIAgBABAgAASgACIAAAEAAAAAAA","1315":"b:kkEBEAIAgBABAgAASgACIAAAEAAAAAAA","1354":"b:kkEBEAIAgBABAgAASgACIAAAEAAAAAAA","1358":"b:kkEBEAIAgBABAgAASgACIAAAEAAAAAAA","1362":[97,33,64,125]},"packages/frontend/editor-ui/src/features/credentials/components/CredentialEdit/CredentialInputs.vue":{"25":"b:kkEBEAIAgBABAgAASgACIAAAEAAAAAAA","29":"b:kkEBEAIAgBABAgAASgACIAAAEAAAAAAA","30":"b:kkEBEAIAgBABAgAASgACIAAAEAAAAAAA","39":"b:kkEAEAIAgBABAgAACAACIAAAEAAAAAAA"},"packages/frontend/editor-ui/src/features/credentials/components/CredentialEdit/CredentialModeSelector.vue":{"27":"b:kkEBEAIAgBABAgAASgACIAAAEAAAAAAA","46":"b:kkEBEAIAgBABAgAASgACIAAAEAAAAAAA","47":"b:kkEBEAIAgBABAgAASgACIAAAEAAAAAAA","52":[16,1,7,28,8,102,55,113,14,148],"53":[16,1,7,28,8,102,55,113,14,148],"63":[16],"65":"b:kgEBEAIAgBABAgAASgACIAAAAAAAAAAA","66":"b:kgEBEAIAgBABAgAASgACIAAAAAAAAAAA","71":[16],"92":"b:kkEBEAIAgBABAgAASgACIAAAEAAAAAAA","103":"b:kkEBEAIAgBABAgAASgACIAAAEAAAAAAA","115":[16,28,8,55,14,148],"128":[16,28,8,55,14,148],"133":[16,28,8,55,14,148],"145":"b:kkEBEAIAgBABAgAASgACIAAAEAAAAAAA","167":[16,28,8,55,14,148],"194":"b:kkEBEAIAgBABAgAASgACIAAAEAAAAAAA","195":[16,28,8,55,14,148],"196":[16,28,8,55,14,148],"199":[16,28,8,55,14,148],"206":[16,28,8,55,14,148],"207":[16,28,8,55,14,148],"214":[16,8],"217":"b:kkEBEAIAgBABAgAASgACIAAAEAAAAAAA","224":[16,28,8,55,14,148],"234":[16,28,8,55,14,148],"236":[16,28,8,55,14,148]},"packages/frontend/editor-ui/src/features/credentials/components/CredentialIcon.vue":{"24":"b:ukUFEAMAwBABAgAATwACIAAAEAAAAAAA","26":"b:ukUFEAMAwBABAgAATwACIAAAEAAAAAAA","30":"b:ukUFEAMAwBABAgAATwACIAAAEAAAAAAA","42":"b:ukUFEAMAwBABAgAATwACIAAAEAAAAAAA","53":"b:ukUFEAMAwBABAgAATwACIAAAEAAAAAAA","59":"b:ukUFEAMAwBABAgAATwACIAAAEAAAAAAA","69":"b:ukUFEAMAwBABAgAATwACIAAAEAAAAAAA","79":"b:ukUFEAMAwBABAgAATwACIAAAEAAAAAAA"},"packages/frontend/editor-ui/src/features/credentials/composables/useCredentialForm.ts":{"65":"b:kkEBEAIAgBABAgAASgACIAAAEAAAAAAA","91":"b:kkEBEAIAgBABAgAASgACIAAAEAAAAAAA","96":"b:kkEBEAIAgBABAgAASgACIAAAEAAAAAAA","106":"b:kkEBEAIAgBABAgAASAACIAAAEAAAAAAA","116":[16,1,7,28,8,102,55,113,14,148],"128":"b:kkEBEAIAgBABAgAASgACIAAAEAAAAAAA","137":"b:kkEBEAIAgBABAgAASgACIAAAEAAAAAAA","152":"b:kkEBEAIAgBABAgAASgACIAAAEAAAAAAA","156":"b:kkEAEAIAgAABAgAAAgACIAAAAAAAAAAA","161":"b:kkEBEAIAgBABAgAASgACIAAAEAAAAAAA","171":"b:kkEBEAIAgBABAgAASgACIAAAEAAAAAAA","176":[16,1,7,28,8,102,55,113,14,148],"181":[16,8,99,148],"185":"b:kkEBEAIAgBABAgAASgACIAAAEAAAAAAA","198":"b:kkEBEAIAgBABAgAASgACIAAAEAAAAAAA","204":"b:kkEBEAIAgBABAgAASgACIAAAEAAAAAAA","208":"b:kkEBEAIAgBABAgAASgACIAAAEAAAAAAA","218":"b:kkEBEAIAgBABAgAASgACIAAAEAAAAAAA","222":"b:kkEBEAIAgBABAgAASgACIAAAEAAAAAAA","229":"b:kkEBEAIAgBABAgAASgACIAAAEAAAAAAA","233":"b:kkEBEAIAgBABAgAASgACIAAAEAAAAAAA","259":"b:kkEAEAIAgBABAgAACgACIAAAEAAAAAAA","262":"b:kkEAEAIAgBABAgAAAgACIAAAAAAAAAAA","268":"b:kkEAEAIAgAABAgAAAgACIAAAAAAAAAAA","269":"b:kkEAEAIAgAABAgAAAgACIAAAAAAAAAAA","276":"b:kkEBEAIAgBABAgAASgACIAAAEAAAAAAA","285":"b:kkEBEAIAgBABAgAASgACIAAAEAAAAAAA","295":"b:kkEBEAIAgBABAgAASgACIAAAEAAAAAAA","309":"b:kkEBEAIAgBABAgAASgACIAAAEAAAAAAA","339":"b:kkEBEAIAgBABAgAASgACIAAAEAAAAAAA","348":"b:kkEAEAIAgBABAgAACgACIAAAEAAAAAAA","349":"b:kkEAEAIAgBABAgAACgACIAAAEAAAAAAA","356":"b:kkEBEAIAgBABAgAASgACIAAAEAAAAAAA","372":[16,8],"397":[97,8,33],"399":[97,8,33],"405":[97,8,33],"437":[97,8,33],"453":"b:kkEBEAIAgBABAgAASgACIAAAEAAAAAAA","476":"b:kkEAEAIAgBABAgAACAACIAAAEAAAAAAA","503":[1,7,28,4,97,8,55,113,64,125,14,73],"515":[97,8,33]},"packages/frontend/editor-ui/src/features/credentials/quickConnect/components/QuickConnectButton.vue":{"9":"b:kkUBEAIAgBABAgAAQgACIAAAEAAAAAAA","35":"b:kkUBEAIAgBABAgAAQgACIAAAEAAAAAAA","44":"b:kkUBEAIAgBABAgAAQgACIAAAEAAAAAAA","49":"b:kkUBEAIAgBABAgAAQgACIAAAEAAAAAAA","64":"b:kkUBEAIAgBABAgAAQgACIAAAEAAAAAAA"},"packages/frontend/editor-ui/src/features/ndv/parameters/components/FixedCollection/FixedCollectionParameter.vue":{"39":[89,22,103,51,130,140,141,14]},"packages/frontend/editor-ui/src/features/ndv/parameters/components/ParameterInputExpanded.vue":{"24":"b:kkEBEAIAgBABAgAASgACIAAAEAAAAAAA","61":"b:kkEBEAIAgBABAgAASgACIAAAEAAAAAAA","92":"b:kkEBEAIAgBABAgAASgACIAAAEAAAAAAA","114":"b:kkEBEAIAgBABAgAASgACIAAAEAAAAAAA","122":"b:kkEBEAIAgBABAgAASgACIAAAEAAAAAAA","129":"b:kkEAEAIAgBABAgAACAACIAAAEAAAAAAA","133":"b:kkEAEAIAgBABAgAACAACIAAAEAAAAAAA","142":[60],"146":"b:kkEAEAIAgBABAgAACAACIAAAEAAAAAAA","169":[60],"171":"b:kkEBEAIAgBABAgAASgACIAAAEAAAAAAA","184":"b:kkEBEAIAgBABAgAASgACIAAAEAAAAAAA","195":"b:kkEBEAIAgBABAgAASgACIAAAEAAAAAAA"},"packages/frontend/editor-ui/src/features/shared/nodeCreator/components/ItemTypes/LinkItem.vue":{"13":[52,14]},"packages/@n8n/blob-storage/src/fs-byte-store.ts":{"20":[0,21,180,3,32,51,68],"22":[0,21,180,3,32,51,68],"26":[0,21,180,3,32,51,68],"41":[0,21,180,3,32,51,68],"46":[0,21,180,3,32,51,68],"56":[0,21,180,3,32,51,68],"69":[0,21,180,3,32,51,68],"74":[0,21,180,3,32,51,68],"80":[0,21,180,3,32,51,68],"87":[0,21,180,3,32,51,68],"93":[0,21,180,3,32,51,68],"94":[0,21,180,3,32,51,68],"95":[0,21,180,3,32,51,68],"96":[0,21,180,3,32,51,68],"97":[0,21,180,3,32,51,68],"101":[0,21,180,3,32,51,68],"117":[0,21,180,3,32,51,68],"130":[0,21,180,3,32,51,68]},"packages/core/src/binary-data/binary-data.service.ts":{"26":[0,21,180,3,32,51,68,181,152,184],"32":[0,21,180,3,32,51,68,181,152,184],"36":[0,21,180,3,32,51,68,181,152,184],"43":[0,21,180,3,32,51,68,181,152,184],"52":[0,21,180,3,32,51,68,181,152,184],"65":[0,21,180,3,32,51,68,181,152,184],"71":[0,21,180,3,32,51,68,181,152,184],"102":[0,21,180,3,32,51,68,181,152,184],"133":[0,21,180,3,32,51,68,181,152,184],"139":[0,21,180,3,32,51,68,181,152,184],"149":[0,21,180,3,32,51,68,181,152,184],"155":[0,21,180,3,32,51,68,181,152,184],"161":[0,21,180,3,32,51,68,181,152,184],"169":[0,21,180,3,32,51,68,181,152,184],"198":[0,21,180,3,32,51,68,181,152,184],"204":[0,21,180,3,32,51,68,181,152,184],"207":[0,21,180,3,32,51,68,181,152,184],"227":[0,21,180,3,32,51,68,181,152,184],"239":[0,21,180,3,32,51,68,181,152,184],"243":[0,21,180,3,32,51,68,181,152,184],"251":[0,21,180,3,32,51,68,181,152,184],"263":[0,21,180,3,32,51,68,181,152,184],"269":[0,21,180,3,32,51,68,181,152,184],"270":[0,21,180,3,32,51,68,181,152,184],"283":[0,21,180,3,32,51,68,181,152,184]},"packages/core/src/binary-data/blob.manager.ts":{"25":[0,21,180,3,32,51,68],"30":[0,21,180,3,32,51,68],"34":[0,21,180,3,32,51,68],"45":[0,21,180,3,32,51,68],"49":[0,21,180,3,32,51,68],"55":[0,21,180,3,32,51,68],"61":[0,21,180,3,32,51,68],"73":[0,21,180,3,32,51,68],"79":[0,21,180,3,32,51,68],"82":[0,21,180,3,32,51,68],"94":[0,21,180,3,32,51,68],"112":[0,21,180,3,32,51,68],"124":[0,21,180,3,32,51,68],"133":[0,21,180,3,32,51,68],"137":[0,21,180,3,32,51,68],"150":[0,21,180,3,32,51,68],"167":[0,21,180,3,32,51,68],"168":[0,21,180,3,32,51,68],"173":[0,21,180,3,32,51,68],"178":[0,21,180,3,32,51,68],"180":[0,21,180,3,32,51,68],"183":[0,21,180,3,32,51,68],"188":[0,21,180,3,32,51,68]},"packages/core/src/utils/serialized-buffer.ts":{"10":[0,21,180],"14":[0,21,180]},"packages/nodes-base/nodes/RespondToWebhook/RespondToWebhook.node.ts":{"359":[164,180,51,184,156],"367":[164,180,51,184,156],"391":[164,180,51,184,156],"471":[164,180,51,184,156],"537":[164,180,51,184,156]},"packages/cli/src/environments.ee/variables/variables.controller.ee.ts":{"19":[119],"22":[119],"32":[119],"50":[119],"60":[119],"80":[119]},"localhost-36839/assets/_plugin-vue_export-helper-Da88TEg1.js":{"2":[75,35,46,51,131,137,148]},"localhost-36839/assets/chunk-CC9Q-vWm.js":{"8":[75,35,46,51,131,137,148],"9":[75,35,46,51,131,137,148],"10":[75,35,46,51,131,137,148],"19":[75,35,46,51,131,137,148],"23":[75,35,46,51,131,137,148],"29":[75,35,46,51,131,137,148],"33":[75,35,46,51,131,137,148],"34":[75,35,46,51,131,137,148]},"packages/frontend/@n8n/design-system/src/components/N8nIcon/custom/status-warning.svg":{"8":[75]},"packages/frontend/editor-ui/src/app/components/VersionUpdateCTA.vue":{"13":[75],"30":[75]},"packages/cli/src/controllers/password-reset.controller.ts":{"44":[175],"70":[175],"114":[175],"170":[175],"198":[175],"229":[175]},"packages/cli/src/middlewares/jitter.ts":{"14":[175],"18":[175],"21":[175]},"packages/cli/src/user-management/email/node-mailer.ts":{"19":[175],"48":[175],"84":[175]},"packages/@n8n/api-types/src/dto/data-table/list-data-table-content-query.dto.ts":{"12":[34,35],"39":[34,35]},"packages/@n8n/api-types/src/dto/project/list-projects-query.dto.ts":{"13":"b:AAAAAQsA4BABAQAAAhAAVAAAAAACAAAA","14":"b:AAAAAQsA4BABAQAAAhAAVAAAAAACAAAA","17":"b:AAAAAQsA4BABAQAAAhAAVAAAAAACAAAA","26":"b:AAAAAQsA4BABAQAAAhAAVAAAAAACAAAA","34":"b:AAAAAQsA4BABAQAAAhAAVAAAAAACAAAA","35":"b:AAAAAQsA4BABAQAAAhAAVAAAAAACAAAA","38":"b:AAAAAQsA4BABAQAAAhAAVAAAAAACAAAA","41":"b:AAAAAQsA4BABAQAAAhAAVAAAAAACAAAA"},"packages/@n8n/db/src/utils/transaction.ts":{"15":[3,4,5,34,35]},"packages/cli/src/modules/data-table/data-table-column.repository.ts":{"21":[34,35],"31":[34,35],"33":[34,35],"44":[34,35],"63":[34,35],"73":[34,35],"77":[34,35],"89":[34,35],"99":[34,35],"100":[34,35],"125":[34,35],"126":[34,35],"139":[34,35],"145":[34,35],"164":[34,35],"170":[34,35],"190":[34,35],"191":[34,35],"196":[34,35]},"packages/cli/src/modules/data-table/data-table-ddl.service.ts":{"23":[34,35],"25":[34,35],"30":[34,35],"44":[34,35],"45":[34,35],"53":[34,35],"64":[34,35],"71":[34,35],"72":[34,35],"80":[34,35],"86":[34,35],"91":[34,35],"97":[34,35],"102":[34,35],"109":[34,35]},"packages/cli/src/modules/data-table/data-table-rows.repository.ts":{"56":[34,35],"138":[34,35],"140":[34,35],"146":[34,35],"167":[34,35],"208":[34,35],"215":[34,35],"221":[34,35],"222":[34,35],"261":[34,35],"273":[34,35],"294":[34,35],"302":[34,35],"307":[34,35],"308":[34,35],"342":[34,35],"347":[34,35],"366":[34,35],"372":[34,35],"409":[34,35],"417":[34,35],"449":[34,35],"455":[34,35],"456":[34,35],"477":[34,35],"478":[34,35],"485":[34,35],"492":[34,35],"507":[34,35],"521":[34,35],"542":[34,35],"554":[34,35],"572":[34,35],"581":[34,35],"582":[34,35],"601":[34,35],"626":[34,35],"637":[34,35],"663":[34,35],"672":[34,35],"691":[34,35],"706":[34,35],"714":[34,35]},"packages/cli/src/modules/data-table/data-table.controller.ts":{"51":[34,35],"57":[34,35],"77":[34,35],"87":[34,35],"104":[34,35],"132":[34,35],"146":[34,35],"170":[34,35],"191":[34,35],"211":[34,35],"227":[34,35],"243":[34,35],"265":[34,35],"287":[34,35],"312":[34,35],"353":[34,35],"382":[34,35],"412":[34,35],"465":[34,35],"518":[34,35]},"packages/cli/src/modules/external-secrets.ee/external-secrets-providers.ee.ts":{"13":[24,32,35,108,55,179,60,125,72,161],"23":[24,32,35,108,55,179,60,125,72,161],"27":[24,32,35,108,55,179,60,125,72,161],"31":[24,32,35,108,55,179,60,125,72,161],"35":[24,32,35,108,55,179,60,125,72,161]},"packages/cli/src/modules/external-secrets.ee/providers/aws-secrets-manager.ts":{"114":[24,32,35,108,55,179,60,125,72,161],"122":[24,32,35,108,55,179,60,125,72,161],"158":[24,32,35,108,55,179,60,125,72,161],"173":[24,32,35,108,55,179,60,125,72,161],"188":[24,32,35,108,55,179,60,125,72,161],"192":[24,32,35,108,55,179,60,125,72,161],"199":[24,32,35,108,55,179,60,125,72,161],"213":[24,32,35,108,55,179,60,125,72,161],"217":[24,32,35,108,55,179,60,125,72,161],"221":[24,32,35,108,55,179,60,125,72,161],"225":[24,32,35,108,55,179,60,125,72,161],"231":[24,32,35,108,55,179,60,125,72,161],"241":[24,32,35,108,55,179,60,125,72,161],"250":[24,32,35,108,55,179,60,125,72,161],"277":[24,32,35,108,55,179,60,125,72,161],"278":[24,32,35,108,55,179,60,125,72,161],"283":[24,32,35,108,55,179,60,125,72,161],"287":[24,32,35,108,55,179,60,125,72,161],"291":[24,32,35,108,55,179,60,125,72,161],"316":[24,32,35,108,55,179,60,125,72,161],"334":[24,32,35,108,55,179,60,125,72,161]},"packages/cli/src/modules/external-secrets.ee/providers/azure-key-vault/azure-key-vault.ts":{"81":[24,32,35,108,55,60,125,72,161],"86":[24,32,35,108,55,60,125,72,161],"92":[24,32,35,108,55,60,125,72,161],"114":[24,32,35,108,55,60,125,72,161],"130":[24,32,35,108,55,60,125,72,161],"134":[24,32,35,108,55,60,125,72,161],"143":[24,32,35,108,55,60,125,72,161],"222":[24,32,35,108,55,60,125,72,161],"226":[24,32,35,108,55,60,125,72,161],"230":[24,32,35,108,55,60,125,72,161],"234":[24,32,35,108,55,60,125,72,161],"245":[24,32,35,108,55,60,125,72,161],"267":[24,32,35,108,55,60,125,72,161]},"packages/cli/src/modules/external-secrets.ee/providers/gcp-secrets-manager/gcp-secrets-manager.ts":{"47":[24,32,35,108,55,60,125,72,161],"52":[24,32,35,108,55,60,125,72,161],"64":[24,32,35,108,55,60,125,72,161],"89":[24,32,35,108,55,60,125,72,161],"104":[24,32,35,108,55,60,125,72,161],"108":[24,32,35,108,55,60,125,72,161],"116":[24,32,35,108,55,60,125,72,161],"129":[24,32,35,108,55,60,125,72,161],"182":[24,32,35,108,55,60,125,72,161],"209":[24,32,35,108,55,60,125,72,161],"213":[24,32,35,108,55,60,125,72,161],"217":[24,32,35,108,55,60,125,72,161],"221":[24,32,35,108,55,60,125,72,161],"245":[24,32,35,108,55,60,125,72,161],"256":[24,32,35,108,55,60,125,72,161]},"packages/cli/src/modules/external-secrets.ee/providers/infisical.ts":{"164":[24,32,35,108,55,60,125,72,161],"172":[24,32,35,108,55,60,125,72,161],"177":[24,32,35,108,55,60,125,72,161],"184":[24,32,35,108,55,60,125,72,161],"210":[24,32,35,108,55,60,125,72,161],"221":[24,32,35,108,55,60,125,72,161],"242":[24,32,35,108,55,60,125,72,161],"276":[24,32,35,108,55,60,125,72,161],"289":[24,32,35,108,55,60,125,72,161],"291":[24,32,35,108,55,60,125,72,161],"294":[24,32,35,108,55,60,125,72,161],"295":[24,32,35,108,55,60,125,72,161],"304":[24,32,35,108,55,60,125,72,161],"307":[24,32,35,108,55,60,125,72,161],"314":[24,32,35,108,55,60,125,72,161],"320":[24,32,35,108,55,60,125,72,161],"336":[24,32,35,108,55,60,125,72,161],"348":[24,32,35,108,55,60,125,72,161],"364":[24,32,35,108,55,60,125,72,161],"368":[24,32,35,108,55,60,125,72,161],"372":[24,32,35,108,55,60,125,72,161],"376":[24,32,35,108,55,60,125,72,161],"386":[24,32,35,108,55,60,125,72,161],"411":[24,32,35,108,55,60,125,72,161]},"packages/cli/src/modules/external-secrets.ee/providers/one-password.ts":{"61":[24,32,35,108,55,60,125,72,161],"66":[24,32,35,108,55,60,125,72,161],"92":[24,32,35,108,55,60,125,72,161],"118":[24,32,35,108,55,60,125,72,161],"137":[24,32,35,108,55,60,125,72,161],"141":[24,32,35,108,55,60,125,72,161],"188":[24,32,35,108,55,60,125,72,161],"192":[24,32,35,108,55,60,125,72,161],"196":[24,32,35,108,55,60,125,72,161],"200":[24,32,35,108,55,60,125,72,161],"204":[24,32,35,108,55,60,125,72,161]},"packages/cli/src/modules/external-secrets.ee/providers/vault.ts":{"278":[24,32,35,108,55,60,125,72,161],"286":[24,32,35,108,55,60,125,72,161],"291":[24,32,35,108,55,60,125,72,161],"298":[24,32,35,108,55,60,125,72,161],"325":[24,32,35,108,55,60,125,72,161],"342":[24,32,35,108,55,60,125,72,161],"349":[24,32,35,108,55,60,125,72,161],"366":[24,32,35,108,55,60,125,72,161],"375":[24,32,35,108,55,60,125,72,161],"402":[24,32,35,108,55,60,125,72,161],"428":[24,32,35,108,55,60,125,72,161],"451":[24,32,35,108,55,60,125,72,161],"465":[24,32,35,108,55,60,125,72,161],"500":[24,32,35,108,55,60,125,72,161],"534":[24,32,35,108,55,60,125,72,161],"535":[24,32,35,108,55,60,125,72,161],"542":[24,32,35,108,55,60,125,72,161],"546":[24,32,35,108,55,60,125,72,161],"557":[24,32,35,108,55,60,125,72,161],"560":[24,32,35,108,55,60,125,72,161],"568":[24,32,35,108,55,60,125,72,161],"571":[24,32,35,108,55,60,125,72,161],"584":[24,32,35,108,55,60,125,72,161],"590":[24,32,35,108,55,60,125,72,161],"606":[24,32,35,108,55,60,125,72,161],"613":[24,32,35,108,55,60,125,72,161],"621":[24,32,35,108,55,60,125,72,161],"636":[24,32,35,108,55,60,125,72,161],"669":[24,32,35,108,55,60,125,72,161],"673":[24,32,35,108,55,60,125,72,161],"677":[24,32,35,108,55,60,125,72,161],"678":[24,32,35,108,55,60,125,72,161],"684":[24,32,35,108,55,60,125,72,161],"696":[24,32,35,108,55,60,125,72,161],"707":[24,32,35,108,55,60,125,72,161],"715":[24,32,35,108,55,60,125,72,161],"723":[24,32,35,108,55,60,125,72,161]},"packages/cli/src/modules/external-secrets.ee/secrets-providers-connections.controller.ee.ts":{"32":[24,32,35,108,55,60,125,72,161],"41":[24,32,35,108,55,60,125,72,161],"81":[24,32,35,108,55,60,125,72,161],"104":[24,32,35,108,55,60,125,72,161],"122":[24,32,35,108,55,60,125,72,161],"135":[24,32,35,108,55,60,125,72,161],"138":[24,32,35,108,55,60,125,72,161],"145":[24,32,35,108,55,60,125,72,161],"157":[24,32,35,108,55,60,125,72,161],"172":[24,32,35,108,55,60,125,72,161]},"packages/cli/src/modules/external-secrets.ee/secrets-providers-project.controller.ee.ts":{"32":[24,32,35,108,55,60,125,72,161],"42":[24,32,35,108,55,60,125,72,161],"56":[24,32,35,108,55,60,125,72,161],"89":[24,32,35,108,55,60,125,72,161],"96":[24,32,35,108,55,60,125,72,161],"101":[24,32,35,108,55,60,125,72,161],"125":[24,32,35,108,55,60,125,72,161],"160":[24,32,35,108,55,60,125,72,161],"181":[24,32,35,108,55,60,125,72,161]},"packages/cli/src/modules/external-secrets.ee/secrets-providers-types.controller.ee.ts":{"16":[24,32,35,108,55,60,125,72,161],"25":[24,32,35,108,55,60,125,72,161],"41":[24,32,35,108,55,60,125,72,161],"44":[24,32,35,108,55,60,125,72,161],"51":[24,32,35,108,55,60,125,72,161]},"packages/cli/src/modules/external-secrets.ee/types.ts":{"29":[24,32,35,108,55,179,125,72,161],"52":[24,32,35,108,55,179,125,72,161],"75":[24,32,35,108,55,179,125,72,161],"92":[24,32,35,108,55,179,125,72,161],"93":[24,32,35,108,55,179,125,72,161],"99":[24,32,35,108,55,179,125,72,161]},"packages/cli/src/modules/favorites/favorites.event-relay.ts":{"10":[3,4,96,32,35,53,155,161],"17":[3,4,96,32,35,53,155,161],"19":[3,4,96,32,35,53,155,161],"21":[3,4,96,32,35,53,155,161],"23":[3,4,96,32,35,53,155,161],"25":[3,4,96,32,35,53,155,161],"27":[3,4,96,32,35,53,155,161]},"packages/frontend/@n8n/design-system/src/components/N8nBreadcrumbs/Breadcrumbs.vue":{"33":[34,35,52,107,53],"58":[34,35,52,107,53],"64":[34,35,52,107,53],"68":[107],"72":[107],"73":[107],"80":[107],"106":[34,35,52,107,53],"107":[107],"113":[107],"122":[107],"123":[107],"141":[107],"142":[107],"157":[107],"171":[34,35,52,107,53],"210":[107],"211":[107],"237":[107],"244":[34,35,52,107,53],"256":[107],"257":[107],"258":[107],"260":[34,35,52,107,53]},"packages/frontend/@n8n/design-system/src/components/N8nDataTableServer/N8nDataTableServer.vue":{"52":"b:AAAAAQkAgBAAAQAAAhAAJAAAAAACAAAA","95":"b:AAAAAQkAgBAAAQAAAhAAJAAAAAACAAAA","96":[97,32,35,108,55,60,122,125],"104":"b:AAAAAQkAgBAAAQAAAhAAJAAAAAACAAAA","111":"b:AAAAAQkAgBAAAQAAAhAAJAAAAAACAAAA","115":"b:AAAAAQkAgBAAAQAAAhAAJAAAAAACAAAA","121":"b:AAAAAQkAgBAAAQAAAhAAJAAAAAACAAAA","133":"b:AAAAAQkAgBAAAQAAAhAAJAAAAAACAAAA","137":"b:AAAAAQkAgBAAAQAAAhAAJAAAAAACAAAA","146":"b:AAAAAQkAgBAAAQAAAhAAJAAAAAACAAAA","151":"b:AAAAAQkAgBAAAQAAAhAAJAAAAAACAAAA","180":"b:AAAAAQkAgBAAAQAAAhAAJAAAAAACAAAA","181":"b:AAAAAQkAgBAAAQAAAhAAJAAAAAACAAAA","192":[24,32,35,108,55,60,122,125,72,161],"218":"b:AAAAAQkAgBAAAQAAAhAAJAAAAAACAAAA","226":[32,108],"229":[32,108],"241":"b:AAAAAQkAgBAAAQAAAhAAJAAAAAACAAAA","290":"b:AAAAAQkAgBAAAQAAAhAAJAAAAAACAAAA","322":[32,108],"341":"b:AAAAAQkAgBAAAQAAAhAAJAAAAAACAAAA","344":"b:AAAAAQkAgBAAAQAAAhAAJAAAAAACAAAA","348":[122],"360":[32,108],"393":"b:AAAAAQkAgBAAAQAAAhAAJAAAAAACAAAA","410":"b:AAAAAQkAgBAAAQAAAhAAJAAAAAACAAAA","411":"b:AAAAAQkAgBAAAQAAAhAAJAAAAAACAAAA","470":"b:AAAAAQkAgBAAAQAAAhAAJAAAAAACAAAA","471":"b:AAAAAQkAgBAAAQAAAhAAJAAAAAACAAAA","476":"b:AAAAAQkAgBAAAQAAAhAAJAAAAAACAAAA","515":[97],"517":[97]},"packages/frontend/@n8n/design-system/src/components/N8nEmptyState/EmptyState.vue":{"35":"b:AAEBGQ8A8LABIQAAChjAOQAAAAACAAAA","55":"b:AAEBGQ4A8LABIQAACgjAOQAAAAACAAAA","64":"b:AAEBGQ8A4JABIQAAChjAOAAAAAACAAAA","73":"b:AAEBGQ4A8LABIQAACAjAOQAAAAACAAAA","81":[28,99,35,119,60,61,120,63]},"packages/frontend/@n8n/design-system/src/components/N8nIconPicker/IconPicker.vue":{"45":[24,96,32,35,108,55,60,125,72,161],"78":[55],"80":[55],"82":[55],"84":[55],"88":[55],"91":[55],"95":[55],"99":[55],"100":[55],"113":[55],"123":[24,96,32,35,108,55,60,125,72,161],"135":[24,96,32,35,108,55,125,72,161],"150":[55],"151":[55],"156":[55],"161":[55],"166":[55],"182":[55],"192":[55],"216":[55],"217":[55],"218":[24,96,32,35,108,55,60,125,72,161],"237":[24,96,32,35,108,55,60,125,72,161],"267":[55],"291":[55],"303":[55],"318":[55],"325":[55],"337":[55],"356":[55],"368":[55],"399":[55],"411":[55],"418":[55]},"packages/frontend/@n8n/design-system/src/components/N8nIconPicker/useIconPickerSearch.ts":{"25":[24,96,32,35,108,55,60,125,72,161],"67":[55],"105":[55],"111":[55],"121":[55],"127":[55]},"packages/frontend/@n8n/design-system/src/components/N8nUserInfo/UserInfo.vue":{"22":[24,32,35,108,55,60,122,125,72,161],"29":[24,32,35,108,55,60,122,125,72,161],"35":[24,32,35,108,55,60,122,125,72,161],"50":[24,32,35,108,55,60,122,125,72,161],"58":[24,32,35,108,55,60,122,125,72,161]},"packages/frontend/@n8n/design-system/src/components/N8nUserSelect/UserSelect.vue":{"24":[24,32,35,108,55,60,125,72,161],"43":[24,32,35,108,55,60,125,72,161],"46":[32,35,108,55,60,125],"66":[24,32,35,108,55,60,125,72,161],"67":[32,35,108,55,60,125],"83":[32,108],"91":[32],"92":[32,108],"94":[32,35,108,55,60,125],"96":[24,32,35,108,55,60,125,72,161],"115":[24,32,35,108,55,60,125,72,161],"117":[24,32,35,108,55,60,125,72,161],"120":[32,35,108,55,60,125],"127":[32,35,108,55,60,125]},"packages/frontend/editor-ui/src/app/components/layouts/ResourcesListEmptyState.vue":{"61":[119],"84":"b:AAEBGQ4A4BABAQAACAiAMAAAAAACAAAA","99":[28,99,35,119,60]},"packages/frontend/editor-ui/src/features/collaboration/projects/components/ProjectDeleteDialog.vue":{"62":[24,32,35,108,55,60,125,72,161]},"packages/frontend/editor-ui/src/features/collaboration/projects/components/ProjectExternalSecrets.vue":{"27":[24,32,35,108,55,60,125,72,161],"53":[24,32,35,108,55,60,125,72,161],"58":[24,32,35,108,55,60,125,72,161],"62":[60],"66":[24,32,35,108,55,60,125,72,161],"70":[24,32,35,108,55,60,125,72,161],"77":[24,32,35,108,55,60,125,72,161],"90":[24,32,35,108,55,60,125,72,161],"117":[24,32,35,108,55,60,125,72,161],"121":[24,32,35,108,55,60,125,72,161],"132":[24,32,35,108,55,60,125,72,161],"149":[60],"150":[60],"162":[60],"172":[60],"180":[24,32,35,108,55,60,125,72,161],"183":[60],"184":[60],"188":[24,32,35,108,55,60,125,72,161],"264":[24,32,35,108,55,60,125,72,161],"265":[24,32,35,108,55,60,125,72,161],"275":[24,32,35,108,55,60,125,72,161],"291":[24,32,35,108,55,60,125,72,161],"307":[24,32,35,108,55,60,125,72,161],"309":[24,32,35,108,55,60,125,72,161],"312":[24,32,35,108,55,60,125,72,161],"315":[24,32,35,108,55,60,125,72,161],"324":[24,32,35,108,55,60,125,72,161],"332":[24,32,35,108,55,60,125,72,161],"349":[60],"360":[60],"376":[60],"397":[60],"401":[60]},"packages/frontend/editor-ui/src/features/collaboration/projects/components/ProjectMembersActionsCell.vue":{"6":[24,32,35,108,55,60,125,72,161],"17":[24,32,35,108,55,60,125,72,161]},"packages/frontend/editor-ui/src/features/collaboration/projects/components/ProjectMembersTable.vue":{"11":[24,32,35,108,55,60,125,72,161],"37":[24,32,35,108,55,60,125,72,161],"44":[24,32,35,108,55,60,125,72,161],"58":[24,32,35,108,55,60,125,72,161],"64":[24,32,35,108,55,60,125,72,161],"67":[32],"71":[24,32,35,108,55,60,125,72,161],"73":[32,108],"74":[24,32,35,108,55,60,125,72,161],"88":[32,108],"90":[24,32,35,108,55,60,125,72,161],"95":[24,32,35,108,55,60,125,72,161],"104":[24,32,35,108,55,60,125,72,161],"107":[24,32,35,108,55,60,125,72,161]},"packages/frontend/editor-ui/src/features/collaboration/projects/components/ProjectRoleUpgradeDialog.vue":{"14":[24,32,35,108,55,60,125,72,161],"22":[24,32,35,108,55,60,125,72,161]},"packages/frontend/editor-ui/src/features/collaboration/projects/views/ProjectSettings.vue":{"63":[24,32,35,108,55,60,125,72,161],"78":[24,32,35,108,55,60,125,72,161],"118":[32,35,108,55,60,125],"122":[24,32,35,108,55,60,125,72,161],"123":[32,35,108,55,60,125],"124":[32,35,108,55,60,125],"130":[32,108],"131":[32,108],"134":[24,32,35,108,55,60,125,72,161],"142":[32,108],"148":[32,108],"168":[32,108],"191":[32],"196":[32],"227":[24,35,108,55,125,72,161],"276":[24,32,35,108,55,60,125,72,161],"288":[108],"293":[24,35,108,55,125,72,161],"316":[24,35,108,55,125,72,161],"317":[24,35,108,55,125,72,161],"324":[24,35,108,55,125,72,161],"355":[24,35,108,55,125,72,161],"374":[24,35,108,55,125,72,161],"421":[24,32,35,108,55,60,125,72,161],"428":[55],"444":[24,32,35,108,55,60,125,72,161],"446":[24,32,35,108,55,60,125,72,161],"463":[24,32,35,108,55,60,125,72,161],"464":[24,32,35,108,55,60,125,72,161],"480":[24,32,35,108,55,60,125,72,161],"485":[24,32,35,108,55,60,125,72,161],"499":[24,32,35,108,55,60,125,72,161],"517":[32,108],"521":[32,35,108,55,60,125],"561":[24,32,35,108,55,60,125,72,161],"566":[24,32,35,108,55,60,125,72,161],"568":[24,32,35,108,55,60,125,72,161],"584":[24,32,35,108,55,60,125,72,161],"594":[24,32,35,108,55,60,125,72,161],"601":[24,32,35,108,55,60,125,72,161],"629":[24,32,35,108,55,60,125,72,161],"649":[24,32,35,108,55,60,125,72,161],"678":[24,32,35,108,55,60,125,72,161],"740":[24,32,35,108,55,60,125,72,161]},"packages/frontend/editor-ui/src/features/core/dataTable/DataTableDetailsView.vue":{"49":[34,35],"51":[34,35],"69":[34,35],"88":[34],"96":[34],"103":[34],"111":[34],"143":[34,35],"150":[34,35],"157":[35],"200":[34,35],"203":[34,35],"207":[34,35],"218":[34,35]},"packages/frontend/editor-ui/src/features/core/dataTable/DataTableView.vue":{"64":[34,35],"65":[34,35],"73":[34,35],"75":[35],"82":[34,35],"84":[34,35],"110":[34,35],"130":[34,35],"135":[34,35],"152":[35],"171":[34,35],"175":[34,35],"176":[34,35],"211":[34,35],"221":[34,35]},"packages/frontend/editor-ui/src/features/core/dataTable/components/AddDataTableModal.vue":{"120":[34,35],"140":[34,35],"141":[34,35],"149":[34,35],"188":[34,35],"267":[34,35],"334":[34,35],"339":[34,35],"345":[34,35],"357":[34,35],"358":[34,35],"361":[34,35]},"packages/frontend/editor-ui/src/features/core/dataTable/components/DataTableActions.vue":{"54":[34,35],"55":[34,35],"57":[34,35],"92":[35],"154":[35]},"packages/frontend/editor-ui/src/features/core/dataTable/components/DataTableBreadcrumbs.vue":{"16":[34,35],"38":[34,35],"42":[34,35],"46":[34,35],"81":[34],"103":[34,35],"109":[34,35],"119":[34,35],"122":[34,35]},"packages/frontend/editor-ui/src/features/core/dataTable/components/DataTableCard.vue":{"34":[34,35],"44":[34,35],"49":[34,35],"50":[34,35],"55":[34,35],"64":[34,35],"67":[34,35],"74":[34,35],"81":[34,35],"93":[34,35],"105":[34,35],"114":[34,35],"120":[34,35]},"packages/frontend/editor-ui/src/features/core/dataTable/components/DownloadDataTableModal.vue":{"25":[34,35]},"packages/frontend/editor-ui/src/features/core/dataTable/components/ImportCsvModal.vue":{"21":[34,35],"143":[34,35],"154":[34,35]},"packages/frontend/editor-ui/src/features/core/dataTable/components/dataGrid/AddColumnButton.vue":{"31":[34,35],"48":[34,35],"67":[34,35],"69":[34],"72":[34],"78":[34],"114":[34],"123":[34],"129":[34],"142":[34,35],"154":[34,35],"160":[34,35],"174":[34],"184":[34],"217":[34],"221":[34],"222":[34],"243":[34]},"packages/frontend/editor-ui/src/features/core/dataTable/components/dataGrid/AddRowButton.vue":{"5":[34,35]},"packages/frontend/editor-ui/src/features/core/dataTable/components/dataGrid/DataTableTable.vue":{"49":[34,35],"51":[34,35],"69":[34,35],"90":[34,35],"117":[34,35],"120":[34],"128":[34],"132":[34],"136":[34],"140":[34,35],"157":[34],"162":[34,35],"163":[34],"193":[34,35]},"packages/frontend/editor-ui/src/features/core/dataTable/components/dataGrid/registerAgGridModulesOnce.ts":{"27":[34,35]},"packages/frontend/editor-ui/src/features/core/dataTable/composables/useAgGrid.ts":{"23":[34,35],"34":[34,35],"86":[34,35],"98":[34,35],"118":[34],"124":[34],"133":[34],"135":[34],"145":[34],"153":[34],"173":[34],"212":[34,35],"231":[34,35]},"packages/frontend/editor-ui/src/features/core/dataTable/composables/useDataTableColumnFilters.ts":{"19":[34,35]},"packages/frontend/editor-ui/src/features/core/dataTable/composables/useDataTableColumns.ts":{"40":[34,35],"59":[34,35],"66":[34],"100":[34],"126":[34,35],"135":[34,35],"162":[34,35],"164":[34,35],"217":[34,35],"221":[34],"222":[34],"229":[34],"237":[34],"245":[34],"247":[34],"248":[34]},"packages/frontend/editor-ui/src/features/core/dataTable/composables/useDataTableOperations.ts":{"58":[34,35],"114":[34],"115":[34],"133":[34],"136":[34],"205":[34],"229":[34],"239":[34],"255":[34],"278":[34],"331":[34,35],"355":[34],"391":[34]},"packages/frontend/editor-ui/src/features/core/dataTable/composables/useDataTablePagination.ts":{"11":[34,35],"17":[34,35],"21":[34],"32":[34]},"packages/frontend/editor-ui/src/features/core/dataTable/composables/useDataTableSelection.ts":{"5":[34,35],"13":[34,35],"18":[34],"19":[34,35],"23":[34],"27":[34],"36":[34,35]},"packages/frontend/editor-ui/src/features/core/dataTable/composables/useDataTableTypes.ts":{"8":[34,35],"9":[34,35],"18":[34,35],"25":[34,35]},"packages/frontend/editor-ui/src/features/core/dataTable/module.descriptor.ts":{"13":[34,35],"14":[34,35],"15":[34,35],"25":[34,35],"37":[35]},"packages/frontend/editor-ui/src/features/core/dataTable/typeGuards.ts":{"11":[34],"21":[34,35]},"packages/frontend/editor-ui/src/features/core/dataTable/utils/columnUtils.ts":{"26":[34],"29":[34],"35":[34,35],"36":[34,35],"51":[34,35],"52":[34,35],"65":[34,35],"66":[34,35],"94":[34,35],"95":[34],"119":[34],"126":[34,35],"135":[34],"141":[34],"149":[34,35],"156":[34,35],"163":[34,35],"170":[34,35],"177":[34],"184":[34,35],"191":[34,35],"198":[34,35],"205":[34,35],"212":[34,35],"219":[34,35],"229":[34,35],"241":[34],"253":[34]},"packages/frontend/editor-ui/src/features/integrations/secretsProviders.ee/composables/useSecretsProvidersList.ee.ts":{"21":[60],"22":[60],"24":[24,32,35,108,55,60,125,72,161],"35":[24,32,35,108,55,60,125,72,161],"64":[60],"68":[60],"72":[24,32,35,108,55,60,125,72,161],"77":[60]},"packages/frontend/editor-ui/src/features/roles/project/projectRoleScopes.ts":{"34":[24,32,35,108,55,60,122,125,72,161],"36":[24,32,35,108,55,60,122,125,72,161]},"packages/nodes-base/nodes/N8n/GenericFunctions.ts":{"21":[46],"56":[46],"93":[46],"94":[46],"107":[46],"111":[46],"146":[46],"150":[46],"186":[46],"210":[46]},"packages/nodes-base/nodes/N8n/WorkflowLocator.ts":{"18":[46],"32":[46],"37":[46],"42":[46]},"localhost-36839/assets/verified-Ge9vhh2v.js":{"8":[46,51,131]},"packages/frontend/@n8n/design-system/src/components/N8nIcon/nodes/n8n-trigger.svg":{"11":[46]},"packages/frontend/@n8n/design-system/src/components/N8nIcon/nodes/n8n.svg":{"11":[46,131]},"packages/cli/src/credentials/dynamic-credentials-proxy.ts":{"30":[3,4,5,33,165,182,47,49,11,50],"32":[3,4,5,33,165,182,47,49,11,50],"36":[3,4,5,33,165,182,47,49,11,50],"46":[3,4,5,33,165,182,47,49,11,50],"58":[3,4,5,33,165,182,47,49,11,50],"64":[3,4,5,33,165,182,47,49,11,50],"87":[3,4,5,33,165,182,47,49,11,50],"115":[3,4,5,33,165,182,47,49,11,50]},"packages/cli/src/modules/dynamic-credentials.ee/services/dynamic-credential.service.ts":{"41":[3,4,5,32,33,165,182,47,49,11,50],"62":[3,4,5,32,33,165,182,47,49,11,50],"173":[3,4,5,32,33,165,182,47,49,11,50],"190":[3,4,5,32,33,165,182,47,49,11,50],"222":[3,4,5,32,33,165,182,47,49,11,50],"253":[3,4,5,32,33,165,182,47,49,11,50],"299":[3,4,5,32,33,165,182,47,49,11,50],"314":[3,4,5,32,33,165,182,47,49,11,50],"331":[3,4,5,32,33,165,182,47,49,11,50],"344":[3,4,5,32,33,165,182,47,49,11,50],"347":[3,4,5,32,33,165,182,47,49,11,50],"367":[3,4,5,32,33,165,182,47,49,11,50]},"packages/core/src/execution-engine/node-execution-context/utils/get-secrets-proxy.ts":{"4":"b:egAAAAKADgAAAgAAiAAAAAAAAAAgAAAA","6":"b:egAAAAKADgAAAgAAiAAAAAAAAAAgAAAA","25":"b:egAAAAKADgAAAgAAiAAAAAAAAAAgAAAA","30":"b:egAAAAKADgAAAgAAiAAAAAAAAAAgAAAA","50":"b:egAAAAKADgAAAgAAiAAAAAAAAAAgAAAA","66":"b:egAAAAKADgAAAgAAiAAAAAAAAAAgAAAA","69":"b:egAAAAKADgAAAgAAiAAAAAAAAAAgAAAA","75":"b:egAAAAKADgAAAgAAiAAAAAAAAAAgAAAA","78":"b:egAAAAKADgAAAgAAiAAAAAAAAAAgAAAA"},"packages/nodes-base/nodes/EmailSend/EmailSend.node.ts":{"8":[47]},"packages/nodes-base/nodes/EmailSend/utils.ts":{"1":[47]},"packages/nodes-base/nodes/EmailSend/v1/EmailSendV1.node.ts":{"136":[47],"143":[47]},"packages/nodes-base/nodes/EmailSend/v2/EmailSendV2.node.ts":{"88":[47],"101":[47]},"packages/nodes-base/nodes/EmailSend/v2/send.operation.ts":{"186":[47]},"packages/nodes-base/nodes/EmailSend/v2/sendAndWait.operation.ts":{"28":[47]},"packages/nodes-base/nodes/EmailSend/v2/utils.ts":{"20":[47],"54":[47]},"packages/nodes-base/nodes/Form/Form.node.ts":{"359":"b:AAFFAECQBABAAgAAgIAAAAAAAAAAAAAA","438":"b:AAFFAECQBABAAgAAgIAAAAAAAAAAAAAA","446":"b:AAFFAECQBABAAgAAgIAAAAAAAAAAAAAA","456":"b:AAFFAECQBABAAgAAgIAAAAAAAAAAAAAA"},"packages/nodes-base/nodes/Form/utils/formCompletionUtils.ts":{"25":"b:AAFFAECQBABAAgAAgIAAAAAAAAAAAAAA","34":"b:AAFFAECQBABAAgAAgIAAAAAAAAAAAAAA","37":"b:AAFFAECQBABAAgAAgIAAAAAAAAAAAAAA","43":"b:AAFFAECQBABAAgAAgIAAAAAAAAAAAAAA","50":"b:AAFFAECQBABAAgAAgIAAAAAAAAAAAAAA","53":"b:AAFFAECQBABAAgAAgIAAAAAAAAAAAAAA","74":"b:AAFFAECQBABAAgAAgIAAAAAAAAAAAAAA"},"packages/nodes-base/nodes/Form/utils/formNodeUtils.ts":{"21":"b:AAFFAECQBABAAgAAgIAAAAAAAAAAAAAA","93":"b:AAFFAECQBABAAgAAgIAAAAAAAAAAAAAA","96":"b:AAFFAECQBABAAgAAgIAAAAAAAAAAAAAA"},"packages/nodes-base/nodes/Form/utils/utils.ts":{"51":"b:QAFFAECRBABAAgACgKAAAAAAABAIAAAA","62":"b:QAFFAECRBABAAgACgKAAAAAAABAIAAAA","75":"b:QAFFAECRBABAAgACgKAAAAAAABAIAAAA","79":"b:QAFFAECRBABAAgACgKAAAAAAABAIAAAA","157":"b:QAFFAECRBABAAgACgKAAAAAAABAIAAAA","158":"b:QAFFAECRBABAAgACgKAAAAAAABAIAAAA","161":"b:QAFFAECRBABAAgACgKAAAAAAABAIAAAA","162":"b:QAFFAECRBABAAgACgKAAAAAAABAIAAAA","174":"b:QAFFAECRBABAAgACgKAAAAAAABAIAAAA","194":"b:QAFFAECRBABAAgACgKAAAAAAABAIAAAA","206":"b:QAFFAECRBABAAgACgKAAAAAAABAIAAAA","217":"b:QAFFAECRBABAAgACgKAAAAAAABAIAAAA","225":"b:QAFFAECRBABAAgACgKAAAAAAABAIAAAA","304":"b:QAFFAECRBABAAgACgKAAAAAAABAIAAAA","324":"b:QAFFAECRBABAAgACgKAAAAAAABAIAAAA","346":"b:QAFFAECRBABAAgACgKAAAAAAABAIAAAA","352":"b:QAFFAECRBABAAgACgKAAAAAAABAIAAAA","391":"b:QAFFAECRBABAAgACgKAAAAAAABAIAAAA","442":"b:QAFFAECRBABAAgACgKAAAAAAABAIAAAA","471":"b:QAFFAECRBABAAgACgKAAAAAAABAIAAAA","553":"b:QAFFAECRBABAAgACgKAAAAAAABAIAAAA","593":"b:QAFFAECRBABAAgACgKAAAAAAABAIAAAA","646":"b:QAFFAECRBABAAgACgKAAAAAAABAIAAAA","647":"b:QAFFAECRBABAAgACgKAAAAAAABAIAAAA","656":"b:QAFFAECRBABAAgACgKAAAAAAABAIAAAA","658":"b:QAFFAECRBABAAgACgKAAAAAAABAIAAAA","674":"b:QAFFAECRBABAAgACgKAAAAAAABAIAAAA","695":"b:QAFFAECRBABAAgACgKAAAAAAABAIAAAA","714":"b:QAFFAECRBABAAgACgKAAAAAAABAIAAAA","724":"b:QAFFAECRBABAAgACgKAAAAAAABAIAAAA","738":"b:QAFFAECRBABAAgACgKAAAAAAABAIAAAA","745":"b:QAFFAECRBABAAgACgKAAAAAAABAIAAAA","750":"b:QAFFAECRBABAAgACgKAAAAAAABAIAAAA","764":"b:QAFFAECRBABAAgACgKAAAAAAABAIAAAA","919":"b:QAFFAECRBABAAgACgKAAAAAAABAIAAAA","928":"b:QAFFAECRBABAAgACgKAAAAAAABAIAAAA","1111":"b:QAFFAECRBABAAgACgKAAAAAAABAIAAAA","1143":"b:QAFFAECRBABAAgACgKAAAAAAABAIAAAA","1146":"b:QAFFAECRBABAAgACgKAAAAAAABAIAAAA","1172":"b:QAFFAECRBABAAgACgKAAAAAAABAIAAAA"},"packages/nodes-base/utils/binary.ts":{"37":[47,105,51],"87":[47,105,51],"139":[47,105,51],"155":[47,105,51],"189":[47,105,51],"218":[47,105,51],"221":[47,105,51],"230":[47,105,51],"233":[47,105,51]},"packages/nodes-base/utils/sendAndWait/configureWaitTillDate.util.ts":{"4":"b:AAFFAECQBABAAgAAgIAAAAAAAAAAAAAA"},"packages/nodes-base/utils/sendAndWait/email-templates.ts":{"82":[16,18,8,38,44,47,50,111,70,73],"147":[16,18,8,38,44,47,50,111,70,73]},"packages/nodes-base/utils/sendAndWait/utils.ts":{"59":[16,18,8,38,44,47,50,111,70,73],"155":[16,18,8,38,44,47,50,111,70,73],"311":[16,18,8,38,44,47,50,111,70,73],"341":[16,18,8,38,44,47,50,111,70,73],"351":[16,18,8,38,44,47,50,111,70,73],"354":[16,18,8,38,44,47,50,111,70,73],"501":[16,18,8,38,44,47,50,111,70,73],"506":[16,18,8,38,44,47,50,111,70,73],"513":[16,18,8,38,44,47,50,111,70,73],"584":[16,18,8,38,44,47,50,111,70,73],"592":[16,18,8,38,44,47,50,111,70,73],"626":[16,18,8,38,44,47,50,111,70,73]},"localhost-44965/assets/_plugin-vue_export-helper-Da88TEg1.js":{"2":[47]},"localhost-44965/assets/chunk-CC9Q-vWm.js":{"8":[47],"9":[47],"10":[47],"19":[47],"23":[47],"29":[47],"33":[47],"34":[47]},"packages/frontend/@n8n/design-system/src/components/N8nIcon/nodes/send-mail.svg":{"11":[1,89,98,44,47]},"packages/@n8n/db/src/repositories/scheduled-job.repository.ts":{"65":"b:AAAAAAEEAAgAAAAAAAAAAAAAAFhIB8AB","73":"b:AAAAAAEEAAgAAAAAAAAAAAAAAFhIB8AB","90":"b:AAAAAAEEAAgAAAAAAAAAAAAAAFhIB8AB","124":"b:AAAAAAEEAAgAAAAAAAAAAAAAAFhIB8AB","133":"b:AAAAAAEEAAgAAAAAAAAAAAAAAFhIB8AB","138":"b:AAAAAAEEAAgAAAAAAAAAAAAAAFhIB8AB","142":"b:AAAAAAEEAAgAAAAAAAAAAAAAAFhIB8AB","149":"b:AAAAAAEEAAgAAAAAAAAAAAAAAFhIB8AB","170":"b:AAAAAAEEAAgAAAAAAAAAAAAAAFhIB8AB","195":"b:AAAAAAEEAAgAAAAAAAAAAAAAAFhIB8AB","211":"b:AAAAAAEEAAgAAAAAAAAAAAAAAFhIB8AB","220":"b:AAAAAAEEAAgAAAAAAAAAAAAAAFhIB8AB","229":"b:AAAAAAEEAAgAAAAAAAAAAAAAAFhIB8AB","239":"b:AAAAAAEEAAgAAAAAAAAAAAAAAFhIB8AB","252":"b:AAAAAAEEAAgAAAAAAAAAAAAAAFhIB8AB","265":"b:AAAAAAEEAAgAAAAAAAAAAAAAAFhIB8AB","276":"b:AAAAAAEEAAgAAAAAAAAAAAAAAFhIB8AB","277":"b:AAAAAAEEAAgAAAAAAAAAAAAAAFhIB8AB","279":"b:AAAAAAEEAAgAAAAAAAAAAAAAAFhIB8AB","285":"b:AAAAAAEEAAgAAAAAAAAAAAAAAFhIB8AB","286":"b:AAAAAAEEAAgAAAAAAAAAAAAAAFhIB8AB","295":"b:AAAAAAEEAAgAAAAAAAAAAAAAAFhIB8AB","296":"b:AAAAAAEEAAgAAAAAAAAAAAAAAFhIB8AB","309":"b:AAAAAAEEAAgAAAAAAAAAAAAAAFhIB8AB","313":"b:AAAAAAEEAAgAAAAAAAAAAAAAAFhIB8AB","314":"b:AAAAAAEEAAgAAAAAAAAAAAAAAFhIB8AB"},"packages/@n8n/scheduler/src/core/provisioning/provision.ts":{"22":"b:AAAAAAEEAAgAAAAAAAAAAAAAAFhIB8AB","26":"b:AAAAAAEEAAgAAAAAAAAAAAAAAFhIB8AB","28":"b:AAAAAAEEAAgAAAAAAAAAAAAAAFhIB8AB","29":"b:AAAAAAEEAAgAAAAAAAAAAAAAAFhIB8AB","48":"b:AAAAAAEEAAgAAAAAAAAAAAAAAFhIB8AB","49":"b:AAAAAAEEAAgAAAAAAAAAAAAAAFhIB8AB","53":"b:AAAAAAEEAAgAAAAAAAAAAAAAAFhIB8AB","54":"b:AAAAAAEEAAgAAAAAAAAAAAAAAFhIB8AB","59":"b:AAAAAAEEAAgAAAAAAAAAAAAAAFhIB8AB","69":"b:AAAAAAEEAAgAAAAAAAAAAAAAAFhIB8AB","72":"b:AAAAAAEEAAgAAAAAAAAAAAAAAFhIB8AB","79":"b:AAAAAAEEAAgAAAAAAAAAAAAAAFhIB8AB"},"packages/@n8n/scheduler/src/core/provisioning/provisioner.ts":{"39":"b:AAAAAAEEAAgAAAAAAAAAAAAAAFhIB8AB","44":"b:AAAAAAEEAAgAAAAAAAAAAAAAAFhIB8AB","46":"b:AAAAAAEEAAgAAAAAAAAAAAAAAFhIB8AB","49":"b:AAAAAAEEAAgAAAAAAAAAAAAAAFhIB8AB","51":"b:AAAAAAEEAAgAAAAAAAAAAAAAAFhIB8AB"},"packages/@n8n/scheduler/src/observability/provisioner-tracing.ts":{"23":"b:AAAAAAEEAAgAAAAAAAAAAAAAAFhIB8AB","25":"b:AAAAAAEEAAgAAAAAAAAAAAAAAFhIB8AB","28":"b:AAAAAAEEAAgAAAAAAAAAAAAAAFhIB8AB","43":"b:AAAAAAEEAAgAAAAAAAAAAAAAAFhIB8AB","46":"b:AAAAAAEEAAgAAAAAAAAAAAAAAFhIB8AB","65":"b:AAAAAAEEAAgAAAAAAAAAAAAAAFhIB8AB","67":"b:AAAAAAEEAAgAAAAAAAAAAAAAAFhIB8AB","68":"b:AAAAAAEEAAgAAAAAAAAAAAAAAFhIB8AB","69":"b:AAAAAAEEAAgAAAAAAAAAAAAAAFhIB8AB","82":"b:AAAAAAEEAAgAAAAAAAAAAAAAAFhIB8AB"},"packages/cli/src/scheduling/durable-job-provisioner.ts":{"78":"b:AAAAAAEEAAgAAAAAAAAAAAAAAFhIB8AB","88":"b:AAAAAAEEAAgAAAAAAAAAAAAAAFhIB8AB","89":"b:AAAAAAEEAAgAAAAAAAAAAAAAAFhIB8AB","103":"b:AAAAAAEEAAgAAAAAAAAAAAAAAFhIB8AB","118":"b:AAAAAAEEAAgAAAAAAAAAAAAAAFhIB8AB","127":"b:AAAAAAEEAAgAAAAAAAAAAAAAAFhIB8AB","138":"b:AAAAAAEEAAgAAAAAAAAAAAAAAFhIB8AB","146":"b:AAAAAAEEAAgAAAAAAAAAAAAAAFhIB8AB","154":"b:AAAAAAEEAAgAAAAAAAAAAAAAAFhIB8AB","155":"b:AAAAAAEEAAgAAAAAAAAAAAAAAFhIB8AB","161":"b:AAAAAAEEAAgAAAAAAAAAAAAAAFhIB8AB","172":"b:AAAAAAEEAAgAAAAAAAAAAAAAAFhIB8AB","180":"b:AAAAAAEEAAgAAAAAAAAAAAAAAFhIB8AB","182":"b:AAAAAAEEAAgAAAAAAAAAAAAAAFhIB8AB","199":"b:AAAAAAEEAAgAAAAAAAAAAAAAAFhIB8AB","208":"b:AAAAAAEEAAgAAAAAAAAAAAAAAFhIB8AB","210":"b:AAAAAAEEAAgAAAAAAAAAAAAAAFhIB8AB","245":"b:AAAAAAEEAAgAAAAAAAAAAAAAAFhIB8AB","252":"b:AAAAAAEEAAgAAAAAAAAAAAAAAFhIB8AB","257":"b:AAAAAAEEAAgAAAAAAAAAAAAAAFhIB8AB","259":"b:AAAAAAEEAAgAAAAAAAAAAAAAAFhIB8AB","263":"b:AAAAAAEEAAgAAAAAAAAAAAAAAFhIB8AB","265":"b:AAAAAAEEAAgAAAAAAAAAAAAAAFhIB8AB","267":"b:AAAAAAEEAAgAAAAAAAAAAAAAAFhIB8AB","270":"b:AAAAAAEEAAgAAAAAAAAAAAAAAFhIB8AB","282":"b:AAAAAAEEAAgAAAAAAAAAAAAAAFhIB8AB","287":"b:AAAAAAEEAAgAAAAAAAAAAAAAAFhIB8AB","294":"b:AAAAAAEEAAgAAAAAAAAAAAAAAFhIB8AB","295":"b:AAAAAAEEAAgAAAAAAAAAAAAAAFhIB8AB","297":"b:AAAAAAEEAAgAAAAAAAAAAAAAAFhIB8AB","299":"b:AAAAAAEEAAgAAAAAAAAAAAAAAFhIB8AB","313":"b:AAAAAAEEAAgAAAAAAAAAAAAAAFhIB8AB","356":"b:AAAAAAEEAAgAAAAAAAAAAAAAAFhIB8AB"},"packages/cli/src/scheduling/poll-trigger-node/poll-trigger-job-registrar.ts":{"30":"b:AAAAAAEEAAAAAAAAAAAAAAAAAFhIA8AB","45":"b:AAAAAAEEAAAAAAAAAAAAAAAAAFhIA8AB","83":"b:AAAAAAEEAAAAAAAAAAAAAAAAAFhIA8AB","91":"b:AAAAAAEEAAAAAAAAAAAAAAAAAFhIA8AB","112":"b:AAAAAAEEAAAAAAAAAAAAAAAAAFhIA8AB","121":"b:AAAAAAEEAAAAAAAAAAAAAAAAAFhIA8AB","131":"b:AAAAAAEEAAAAAAAAAAAAAAAAAFhIA8AB","145":"b:AAAAAAEEAAAAAAAAAAAAAAAAAFhIA8AB","156":"b:AAAAAAEEAAAAAAAAAAAAAAAAAFhIA8AB","168":"b:AAAAAAEEAAAAAAAAAAAAAAAAAFhIA8AB"},"packages/cli/src/scheduling/scheduler-tracer.ts":{"13":[163,32,182,42,183,184,155,156,158],"15":[163,32,182,42,183,184,155,156,158]},"packages/core/src/execution-engine/scheduled-task-manager.ts":{"34":"b:AACAAAEEAAgAAAAAAAAAgAAAAFjIB8AD","40":"b:AACAAAEEAAgAAAAAAAAAgAAAAFjIB8AD","48":"b:AACAAAEEAAgAAAAAAAAAgAAAAFjIB8AD","57":"b:AACAAAEEAAgAAAAAAAAAgAAAAFjIB8AD","67":"b:AACAAAEEAAgAAAAAAAAAgAAAAFjIB8AD","107":"b:AACAAAEEAAgAAAAAAAAAgAAAAFjIB8AD","110":"b:AACAAAEEAAgAAAAAAAAAgAAAAFjIB8AD","151":"b:AACAAAEEAAgAAAAAAAAAgAAAAFjIB8AD","175":"b:AACAAAEEAAgAAAAAAAAAgAAAAFjIB8AD","179":"b:AACAAAEEAAgAAAAAAAAAgAAAAFjIB8AD","192":"b:AACAAAEEAAgAAAAAAAAAgAAAAFjIB8AD","219":"b:AACAAAEEAAgAAAAAAAAAgAAAAFjIB8AD","224":"b:AACAAAEEAAgAAAAAAAAAgAAAAFjIB8AD","228":"b:AACAAAEEAAgAAAAAAAAAgAAAAFjIB8AD","234":"b:AACAAAEEAAgAAAAAAAAAgAAAAFjIB8AD","238":"b:AACAAAEEAAgAAAAAAAAAgAAAAFjIB8AD","246":"b:AACAAAEEAAgAAAAAAAAAgAAAAFjIB8AD","261":"b:AACAAAEEAAgAAAAAAAAAgAAAAFjIB8AD","279":"b:AACAAAEEAAgAAAAAAAAAgAAAAFjIB8AD"},"packages/core/src/observability/tracing/noop-tracing.ts":{"4":"b:AACAAAEEAAgAAAAAAAAAAAAAAFhIB8AB","11":"b:AACAAAEEAAgAAAAAAAAAAAAAAFhIB8AB","15":"b:AACAAAEEAAgAAAAAAAAAAAAAAFhIB8AB","19":"b:AACAAAEEAAgAAAAAAAAAAAAAAFhIB8AB","23":"b:AACAAAEEAAgAAAAAAAAAAAAAAFhIB8AB","27":"b:AACAAAEEAAgAAAAAAAAAAAAAAFhIB8AB","31":"b:AACAAAEEAAgAAAAAAAAAAAAAAFhIB8AB","35":"b:AACAAAEEAAgAAAAAAAAAAAAAAFhIB8AB","39":"b:AACAAAEEAAgAAAAAAAAAAAAAAFhIB8AB","43":"b:AACAAAEEAAgAAAAAAAAAAAAAAFhIB8AB","47":"b:AACAAAEEAAgAAAAAAAAAAAAAAFhIB8AB","51":"b:AACAAAEEAAgAAAAAAAAAAAAAAFhIB8AB","59":"b:AACAAAEEAAgAAAAAAAAAAAAAAFhIB8AB","62":"b:AACAAAEEAAgAAAAAAAAAAAAAAFhIB8AB","66":"b:AACAAAEEAAgAAAAAAAAAAAAAAFhIB8AB"},"packages/nodes-base/nodes/N8nTrigger/N8nTrigger.node.ts":{"63":[183],"86":[183]},"packages/nodes-base/nodes/Files/ConvertToFile/ConvertToFile.node.ts":{"107":[51]},"packages/nodes-base/nodes/Files/ConvertToFile/actions/iCall.operation.ts":{"16":[51]},"packages/nodes-base/nodes/Files/ConvertToFile/actions/spreadsheet.operation.ts":{"94":[51]},"packages/nodes-base/nodes/Files/ConvertToFile/actions/toBinary.operation.ts":{"105":[51]},"packages/nodes-base/nodes/Files/ConvertToFile/actions/toJson.operation.ts":{"90":[51],"102":[51]},"packages/nodes-base/nodes/Files/ConvertToFile/actions/toText.operation.ts":{"78":[51]},"packages/nodes-base/nodes/ICalendar/createEvent.operation.ts":{"275":[51]},"packages/nodes-base/nodes/RespondToWebhook/utils/binary.ts":{"5":[164,51,184,156],"16":[164,51,184,156]},"packages/nodes-base/nodes/RespondToWebhook/utils/outputs.ts":{"1":[164,51,184,156]},"localhost-33431/assets/_plugin-vue_export-helper-Da88TEg1.js":{"2":[27,119,126,133,70]},"localhost-33431/assets/chunk-CC9Q-vWm.js":{"8":[27,119,126,133,70],"9":[27,119,126,133,70],"10":[27,119,126,133,70],"19":[27,119,126,133,70],"23":[27,119,126,133,70],"29":[27,119,126,133,70],"33":[27,119,126,133,70],"34":[27,119,126,133,70]},"packages/frontend/@n8n/design-system/src/components/N8nDatatable/Datatable.vue":{"23":[119],"37":[119],"45":[119],"76":[119],"80":[119],"89":[119],"99":[119],"133":[119],"134":[119],"135":[119]},"packages/frontend/@n8n/design-system/src/components/TableBase/TableBase.vue":{"8":[119],"26":[119],"31":[119]},"packages/frontend/editor-ui/src/features/collaboration/projects/views/ProjectVariables.vue":{"68":[119],"78":[119],"82":[119],"87":[119],"91":[119],"93":[119],"94":[119],"109":[119],"114":[119],"150":[119],"184":[119],"193":[119],"197":[119],"216":[119],"222":[119],"269":[119],"281":[119],"290":[119],"314":[119],"400":[119],"434":[119],"435":[119],"451":[119],"452":[119],"463":[119]},"packages/frontend/editor-ui/src/features/settings/environments.ee/components/VariableModal.vue":{"65":[119],"70":[119],"94":[119],"98":[119],"117":[119],"132":[119],"133":[119],"136":[119],"149":[119],"175":[119],"193":[119],"194":[119],"198":[119],"203":[119],"207":[119],"243":[119],"248":[119],"265":[119],"278":[119],"291":[119],"298":[119],"321":[119],"326":[119],"332":[119],"339":[119],"341":[119],"347":[119],"361":[119]},"packages/frontend/editor-ui/src/features/settings/environments.ee/environments.api.ts":{"10":[119,126,133,70],"21":[119],"28":[119],"40":[119]},"packages/frontend/@n8n/design-system/src/components/N8nPulse/Pulse.vue":{"6":[22,103,51,68]},"packages/frontend/editor-ui/src/app/components/CopyInput.vue":{"37":[68],"54":[16,8,99,51,85,68,148,73]},"packages/frontend/editor-ui/src/app/composables/useIntersectionObserver.ts":{"45":[1,131,136,137,69,158,159],"56":[69,158],"62":[69,158],"82":[1,131,136,137,158,159]},"packages/frontend/editor-ui/src/features/execution/executions/components/workflow/WorkflowExecutionsLandingPage.vue":{"22":[1,131,137,68,69,6,157,158,159],"25":[1,131,137,68,69,6,157,158,159],"27":[158],"45":[158],"48":[158],"56":[1,131,137,68,69,6,157,158,159]},"packages/frontend/editor-ui/src/features/workflows/canvas/components/elements/nodes/render-types/parts/CanvasNodeDisabledStrikeThrough.vue":{"6":"b:QAAAAAAAAAAiAAAAAAAAgEgAAPAAAAAA","10":"b:QAAAAAAAAAAiAAAAAAAAgEgAAPAAAAAA","19":"b:QAAAAAAAAAAiAAAAAAAAgEgAAPAAAAAA"},"packages/@n8n/backend-network/src/http/client-request-error.ts":{"13":[33,104,137,181,73],"31":[33,104,137,181,73],"42":[33,104,137,181,73],"73":[33,104,137,181,73],"89":[33,104,137,181,73]},"packages/frontend/editor-ui/src/app/composables/usePushConnection/handlers/sendConsoleMessage.ts":{"7":[30,33,104,52,137]},"packages/frontend/editor-ui/src/app/utils/rbac/checks/isEnterpriseFeatureEnabled.ts":{"4":[137],"15":[137]},"packages/frontend/editor-ui/src/app/utils/rbac/middleware/enterprise.ts":{"6":[137]},"packages/frontend/editor-ui/src/features/execution/executions/composables/useExecutionDebugging.ts":{"39":[52,137],"43":[52,137],"54":[52,137],"55":[52,137],"62":[52,137],"72":[52,137],"89":[52,137],"108":[52,137],"114":[52,137],"118":[52,137],"162":[52,137],"168":[52]},"packages/@n8n/client-oauth2/src/client-oauth2-token.ts":{"28":[165,182,148],"43":[165,182,148],"75":[165,182,148],"132":[165,182,148]},"packages/@n8n/client-oauth2/src/client-oauth2.ts":{"50":[99,165,148],"68":[99,165,148],"76":[99,165,148],"89":[99,165,148],"102":[99,165,148],"105":[99,165,148],"141":[99,165,148]},"packages/@n8n/client-oauth2/src/code-flow.ts":{"25":[99,165,148],"30":[99,165,148],"61":[99,165,148]},"packages/@n8n/client-oauth2/src/credential-options.ts":{"22":[165,148]},"packages/@n8n/client-oauth2/src/credentials-flow.ts":{"25":[99,165,148],"30":[99,165,148]},"packages/@n8n/client-oauth2/src/index.ts":{"2":[99,165,148],"5":[99,165,148]},"packages/@n8n/client-oauth2/src/utils.ts":{"8":[99,165,148],"22":[99,165,148],"34":[99,165,148],"51":[99,165,148],"58":[99,165,148],"65":[99,165,148]},"packages/cli/src/controllers/oauth/oauth2-credential.controller.ts":{"22":[99,165,148],"33":[99,165,148],"41":[99,165,148],"180":[99,165,148]},"packages/cli/src/oauth/oauth-browser-binding.service.ts":{"16":[99,165,148,73],"19":[99,165,148,73],"31":[99,165,148,73],"48":[99,165,148,73],"58":[99,165,148,73],"72":[99,165,148,73]},"packages/cli/src/oauth/oauth.service.ts":{"86":[99,165,148,73],"96":[99,165,148,73],"103":[99,165,148,73],"111":[99,165,148,73],"143":[99,165,148,73],"147":[99,165,148,73],"156":[99,165,148,73],"184":[99,165,148,73],"188":[99,165,148,73],"197":[99,165,148,73],"211":[99,165,148,73],"250":[99,165,148,73],"255":[99,165,148,73],"295":[99,165,148,73],"322":[99,165,148,73],"329":[99,165,148,73],"339":[99,165,148,73],"346":[99,165,148,73],"361":[99,165,148,73],"376":[99,165,148,73],"398":[99,165,148,73],"434":[99,165,148,73],"450":[99,165,148,73],"469":[99,165,148,73],"478":[99,165,148,73],"486":[99,165,148,73],"494":[99,165,148,73],"588":[99,165,148,73],"601":[99,165,148,73],"639":[99,165,148,73],"649":[99,165,148,73],"664":[99,165,148,73],"695":[99,165,148,73],"696":[99,165,148,73],"699":[99,165,148,73],"709":[99,165,148,73],"712":[99,165,148,73],"726":[99,165,148,73],"742":[99,165,148,73],"807":[99,165,148,73],"810":[99,165,148,73],"817":[99,165,148,73],"896":[99,165,148,73],"907":[99,165,148,73],"996":[99,165,148,73],"1057":[99,165,148,73],"1125":[99,165,148,73],"1141":[99,165,148,73],"1167":[99,165,148,73],"1240":[99,165,148,73],"1252":[99,165,148,73],"1296":[99,165,148,73],"1324":[99,165,148,73],"1337":[99,165,148,73],"1355":[99,165,148,73],"1400":[99,165,148,73],"1419":[99,165,148,73],"1475":[99,165,148,73],"1486":[99,165,148,73],"1512":[99,165,148,73]},"packages/cli/src/oauth/validate-oauth-url.ts":{"12":[99,165,148,73]},"packages/core/src/execution-engine/node-execution-context/utils/request-helpers/oauth.ts":{"45":[182,148],"49":[182,148],"70":[182,148],"87":[182,148],"111":[182,148],"122":[182,148],"128":[182,148],"144":[182,148],"164":[182,148],"303":[182,148],"317":[182,148],"323":[182,148],"331":[182,148],"443":[182,148],"458":[182,148],"460":[182,148],"468":[182,148],"480":[182,148],"483":[182,148],"491":[182,148],"515":[182,148],"557":[182,148],"563":[182,148]},"packages/nodes-base/nodes/E2eTest/E2eTest.node.ts":{"175":[148,149],"180":[148,149],"188":[148,149],"201":[148,149],"207":[148,149]},"packages/nodes-base/nodes/E2eTest/mock.ts":{"66":[148,149]},"packages/nodes-base/nodes/Google/GenericFunctions.ts":{"75":[16,8,70,148,150,162],"129":[16,8,70,148,150,162]},"packages/nodes-base/nodes/Google/Sheet/GoogleSheets.node.ts":{"8":[148,162]},"packages/nodes-base/nodes/Google/Sheet/v1/GenericFunctions.ts":{"20":[148,162],"72":[148,162],"96":[148,162],"99":[148,162]},"packages/nodes-base/nodes/Google/Sheet/v1/GoogleSheet.ts":{"48":[148,162],"66":[148,162],"78":[148,162],"97":[148,162],"119":[148,162],"138":[148,162],"156":[148,162],"175":[148,162],"194":[148,162],"218":[148,162],"250":[148,162],"270":[148,162],"286":[148,162],"300":[148,162],"386":[148,162],"470":[148,162],"542":[148,162],"574":[148,162],"576":[148,162]},"packages/nodes-base/nodes/Google/Sheet/v1/GoogleSheetsV1.node.ts":{"32":[148,162],"42":[148,162],"68":[148,162],"96":[148,162],"125":[148,162],"227":[148,162],"299":[148,162],"413":[148,162]},"packages/nodes-base/nodes/Google/Sheet/v2/GoogleSheetsV2.node.ts":{"15":[148,162],"29":[148,162]},"packages/nodes-base/nodes/Google/Sheet/v2/actions/router.ts":{"9":[148,162]},"packages/nodes-base/nodes/Google/Sheet/v2/actions/sheet/append.operation.ts":{"213":[148,162],"326":[148,162]},"packages/nodes-base/nodes/Google/Sheet/v2/actions/sheet/appendOrUpdate.operation.ts":{"264":[148,162],"365":[148,162],"374":[148,162],"392":[148,162],"424":[148,162],"447":[148,162],"521":[148,162]},"packages/nodes-base/nodes/Google/Sheet/v2/actions/sheet/clear.operation.ts":{"160":[148,162]},"packages/nodes-base/nodes/Google/Sheet/v2/actions/sheet/create.operation.ts":{"77":[148,162]},"packages/nodes-base/nodes/Google/Sheet/v2/actions/sheet/delete.operation.ts":{"114":[148,162]},"packages/nodes-base/nodes/Google/Sheet/v2/actions/sheet/read.operation.ts":{"170":[148,162]},"packages/nodes-base/nodes/Google/Sheet/v2/actions/sheet/remove.operation.ts":{"7":[148,162]},"packages/nodes-base/nodes/Google/Sheet/v2/actions/sheet/update.operation.ts":{"253":[148,162],"345":[148,162],"354":[148,162],"371":[148,162],"403":[148,162],"430":[148,162],"496":[148,162]},"packages/nodes-base/nodes/Google/Sheet/v2/actions/spreadsheet/create.operation.ts":{"118":[148,162]},"packages/nodes-base/nodes/Google/Sheet/v2/actions/spreadsheet/delete.operation.ts":{"68":[148,162]},"packages/nodes-base/nodes/Google/Sheet/v2/actions/utils/readOperation.ts":{"12":[148,162],"110":[148,162]},"packages/nodes-base/nodes/Google/Sheet/v2/helpers/GoogleSheet.ts":{"39":[148,162],"47":[148,162],"58":[148,162],"74":[148,162],"93":[148,162],"117":[148,162],"136":[148,162],"149":[148,162],"165":[148,162],"178":[148,162],"186":[148,162],"204":[148,162],"220":[148,162],"264":[148,162],"287":[148,162],"333":[148,162],"367":[148,162],"380":[148,162],"394":[148,162],"411":[148,162],"441":[148,162],"446":[148,162],"461":[148,162],"480":[148,162],"494":[148,162],"545":[148,162],"627":[148,162],"675":[148,162],"803":[148,162],"833":[148,162],"835":[148,162],"857":[148,162],"880":[148,162]},"packages/nodes-base/nodes/Google/Sheet/v2/helpers/GoogleSheets.utils.ts":{"24":[148,162],"50":[148,162],"56":[148,162],"69":[148,162],"79":[148,162],"82":[148,162],"99":[148,162],"101":[148,162],"106":[148,162],"108":[148,162],"115":[148,162],"117":[148,162],"120":[148,162],"128":[148,162],"134":[148,162],"135":[148,162],"146":[148,162],"149":[148,162],"151":[148,162],"156":[148,162],"161":[148,162],"162":[148,162],"166":[148,162],"199":[148,162],"206":[148,162],"208":[148,162],"211":[148,162],"238":[148,162],"272":[148,162],"281":[148,162],"282":[148,162],"305":[148,162],"310":[148,162],"311":[148,162],"326":[148,162],"334":[148,162],"336":[148,162],"351":[148,162],"358":[148,162],"364":[148,162],"368":[148,162],"369":[148,162],"371":[148,162]},"packages/nodes-base/nodes/Google/Sheet/v2/methods/credentialTest.ts":{"9":[148,162]},"packages/nodes-base/nodes/Google/Sheet/v2/methods/listSearch.ts":{"13":[148,162],"42":[148,162],"51":[148,162]},"packages/nodes-base/nodes/Google/Sheet/v2/methods/loadOptions.ts":{"8":[148,162],"39":[148,162],"87":[148,162],"96":[148,162],"99":[148,162],"103":[148,162],"113":[148,162],"117":[148,162]},"packages/nodes-base/nodes/Google/Sheet/v2/methods/resourceMapping.ts":{"12":[148,162],"58":[148,162],"60":[148,162]},"packages/nodes-base/nodes/Google/Sheet/v2/transport/index.ts":{"16":[148,162],"84":[148,162]},"packages/frontend/editor-ui/src/features/credentials/components/CredentialEdit/CredentialTypeSelector.vue":{"15":[16,8,99,148,73],"42":[16,8,99,148,73],"44":[16,8,99,148,73],"63":[16,8,99,148,73],"70":[16,8,99,148,73],"79":[16,8,99,148,73],"89":[16,8,99,148,73],"99":[16,8,99,148,73],"100":[16,8,99,148,73],"101":[16,8,99,148,73],"103":[16,8,99,148,73],"108":[16,8,99,148,73],"112":[16,8,99,148,73],"115":[16,8,99,148,73]},"packages/frontend/editor-ui/src/features/credentials/components/CredentialEdit/GoogleAuthButton.vue":{"2":[16,8,99,148]},"packages/frontend/editor-ui/src/features/credentials/composables/oauthCallback.ts":{"20":[99,148],"35":[99]},"packages/frontend/editor-ui/src/features/ndv/parameters/components/ResourceMapper/MappingModeSelect.vue":{"20":[148,149],"54":[148,149],"61":[148,149],"103":[148,149],"113":[148,149],"121":[148,149],"123":[148,149],"128":[148,149],"140":[148,149],"150":[148,149],"153":[148,149]},"packages/frontend/editor-ui/src/features/ndv/parameters/composables/useNodeSpecificationValues.ts":{"5":[148,149],"6":[148,149],"10":[148,149],"16":[149],"20":[148,149],"24":[149]},"packages/@n8n/backend-network/src/http/parse-incoming-message.ts":{"3":[89,22,30,103,50],"5":[89,22,30,103,50],"55":[89,22,30,103,50],"77":[89,22,30,103,50],"108":[89,22,30,103,50]},"packages/cli/src/executions/execution.utils.ts":{"13":[89,22,103,50,6,181,152,184,187],"26":[89,22,103,50,6,181,152,184,187]},"packages/cli/src/utils/cors.util.ts":{"3":[89,22,103,50,6,187]},"packages/cli/src/webhooks/waiting-forms.ts":{"18":[89,22,103],"21":[89,22,103],"25":[89,22,103],"31":[89,22,103],"42":[89,22,103],"54":[89,22,103],"74":[89,22,103],"153":[89,22,103]},"packages/cli/src/webhooks/waiting-webhooks.ts":{"48":[89,22,103,50,6,187,158],"60":[89,22,103,50,6,187,158],"68":[89,22,103,50,6,187,158],"72":[89,22,103,50,6,187,158],"76":[89,22,103,50,6,187,158],"80":[89,22,103,50,6,187,158],"90":[89,22,103,50,6,187,158],"103":[89,22,103,50,6,187,158],"109":[89,22,103,50,6,187,158],"128":[89,22,103,50,6,187,158],"141":[89,22,103,50,6,187,158],"154":[89,22,103,50,6,187,158],"165":[89,22,103,50,6,187,158],"191":[89,22,103,50,6,187,158],"212":[89,22,103,50,6,187,158],"233":[89,22,103,50,6,187,158],"323":[89,22,103,50,6,187,158],"332":[89,22,103,50,6,187,158],"383":[89,22,103,50,6,187,158],"411":[89,22,103,50,6,187,158],"424":[89,22,103,50,6,187,158]},"packages/cli/src/webhooks/webhook-form-data.ts":{"10":[89,22,103,50],"13":[89,22,103,50],"20":[89,22,103,50],"32":[89,22,103,50],"33":[89,22,103,50],"46":[89,22,103,50],"47":[89,22,103,50]},"packages/nodes-base/nodes/Form/FormTrigger.node.ts":{"8":[163,22,103,156]},"packages/nodes-base/nodes/Form/v1/FormTriggerV1.node.ts":{"91":[163,22,103,156],"98":[163,22,103,156]},"packages/nodes-base/nodes/Form/v2/FormTriggerV2.node.ts":{"167":[163,22,103,156],"289":[163,22,103,156],"296":[163,22,103,156]},"packages/nodes-base/nodes/Wait/Wait.node.ts":{"229":[89,22,40,109,68,6,187,156],"501":[89,22,40,109,68,6,187,156],"507":[89,22,40,109,68,6,187,156],"522":[89,22,40,109,68,6,187,156],"599":[89,22,40,109,68,6,187,156],"600":[89,22,40,109,68,6,187,156],"601":[89,22,40,109,68,6,187,156],"612":[89,22,40,109,68,6,187,156],"643":[89,22,40,109,68,6,187,156]},"packages/nodes-base/nodes/Wait/validation.ts":{"1":[89,22,40,109,68,6,156],"6":[89,22,40,109,68,6,156]},"localhost-38123/assets/_plugin-vue_export-helper-Da88TEg1.js":{"2":[22,92,12,115,149,159]},"localhost-38123/assets/chunk-CC9Q-vWm.js":{"8":[22,92,12,115,149,159],"9":[22,92,12,115,149,159],"10":[22,92,12,115,149,159],"19":[22,92,12,115,149,159],"23":[22,92,12,115,149,159],"29":[22,92,12,115,149,159],"33":[22,92,12,115,149,159],"34":[22,92,12,115,149,159]},"localhost-36767/assets/_plugin-vue_export-helper-Da88TEg1.js":{"2":[30]},"localhost-36767/assets/chunk-CC9Q-vWm.js":{"8":[30],"9":[30],"10":[30],"19":[30],"23":[30],"29":[30],"33":[30],"34":[30]},"packages/@n8n/ai-utilities/src/model-discovery/index.ts":{"39":[1,3,4,5,12,6,13],"43":[1,3,4,5,12,6,13],"58":[1,3,4,5,12,6,13],"64":[1,3,4,5,12,6,13]},"packages/@n8n/ai-utilities/src/model-discovery/providers/anthropic.ts":{"5":[1,3,4,5,12,6,13],"15":[1,3,4,5,12,6,13],"16":[1,3,4,5,12,6,13]},"packages/@n8n/ai-utilities/src/model-discovery/providers/cohere.ts":{"5":[1,3,4,5,12,6,13],"14":[1,3,4,5,12,6,13],"15":[1,3,4,5,12,6,13]},"packages/@n8n/ai-utilities/src/model-discovery/providers/google.ts":{"11":[1,3,4,5,12,6,13],"22":[1,3,4,5,12,6,13],"27":[1,3,4,5,12,6,13]},"packages/@n8n/ai-utilities/src/model-discovery/providers/groq.ts":{"5":[1,3,4,5,12,6,13],"14":[1,3,4,5,12,6,13]},"packages/@n8n/ai-utilities/src/model-discovery/providers/mistral.ts":{"5":[1,3,4,5,12,6,13],"14":[1,3,4,5,12,6,13]},"packages/@n8n/ai-utilities/src/model-discovery/providers/nvidia.ts":{"16":[1,3,4,5,12,6,13],"25":[1,3,4,5,12,6,13]},"packages/@n8n/ai-utilities/src/model-discovery/providers/openai.ts":{"10":[1,3,4,5,12,6,13],"29":[1,3,4,5,12,6,13],"37":[1,3,4,5,12,6,13]},"packages/@n8n/ai-utilities/src/model-discovery/request.ts":{"4":[1,3,4,5,12,6,13],"16":[1,3,4,5,12,6,13],"25":[1,3,4,5,12,6,13],"29":[1,3,4,5,12,6,13],"33":[1,3,4,5,12,6,13],"42":[1,3,4,5,12,6,13],"44":[1,3,4,5,12,6,13],"45":[1,3,4,5,12,6,13],"52":[1,3,4,5,12,6,13],"53":[1,3,4,5,12,6,13]},"packages/@n8n/ai-utilities/src/utils/failed-attempt-handler/n8nLlmFailedAttemptHandler.ts":{"13":[1,3,4,5,98,6],"17":[1,3,4,5,98,6]},"packages/@n8n/ai-utilities/src/utils/http-proxy-agent.ts":{"66":[1,3,4,5,6],"106":[1,3,4,5,6],"129":[1,3,4,5,6]},"packages/@n8n/ai-utilities/src/utils/n8n-llm-tracing.ts":{"56":[1,3,4,5,98,6],"94":[1,3,4,5,98,6],"109":[1,3,4,5,98,6],"112":[1,3,4,5,98,6],"124":[1,3,4,5,98,6],"125":[1,3,4,5,98,6],"129":[1,3,4,5,98,6],"134":[1,3,4,5,98,6],"139":[1,3,4,5,98,6],"140":[1,3,4,5,98,6],"190":[1,3,4,5,98,6],"215":[1,3,4,5,98,6],"251":[1,3,4,5,98,6],"259":[1,3,4,5,98,6],"292":[1,3,4,5,98,6],"296":[1,3,4,5,98,6]},"packages/@n8n/ai-utilities/src/utils/redact-headers.ts":{"13":[1,3,4,5,6],"29":[1,3,4,5,6],"32":[1,3,4,5,6],"58":[1,3,4,5,6],"64":[1,3,4,5,6]},"packages/@n8n/ai-utilities/src/utils/tokenizer/tiktoken.ts":{"9":[1,3,4,5,6],"15":[1,3,4,5,6],"18":[1,3,4,5,6],"34":[1,3,4,5,6],"43":[1,3,4,5,6]},"packages/@n8n/ai-utilities/src/utils/tokenizer/token-estimator.ts":{"33":[1,3,4,5,6],"70":[1,3,4,5,6],"129":[1,3,4,5,6],"141":[1,3,4,5,6],"169":[1,3,4,5,6]},"packages/@n8n/backend-network/src/proxy/index.ts":{"19":[1,3,4,5,165,6]},"packages/@n8n/backend-network/src/proxy/proxy-resolution.ts":{"20":[1,3,4,5,165,6],"35":[1,3,4,5,165,6],"57":[1,3,4,5,165,6],"72":[1,3,4,5,165,6],"80":[1,3,4,5,165,6]},"packages/@n8n/chat-hub/src/artifact.ts":{"3":[3,4,5,96,98,32],"19":[3,4,5]},"packages/@n8n/chat-hub/src/parser.ts":{"12":[3,4,5,96,98,32],"117":[3,4,5,96,98,32],"134":[3,4,5,96,98,32],"159":[3,4,5],"187":[3,4,5],"217":[3,4,5],"250":[3,4,5,96,98,32],"275":[3,4,5]},"packages/@n8n/db/src/repositories/project-secrets-provider-access.repository.ee.ts":{"9":[3,4,96,60],"12":[3,4,96,60],"21":[3,4,96,60],"28":[3,4,96,60],"35":[3,4,96,60],"43":[3,4,96,60],"54":[3,4,96,60]},"packages/@n8n/nodes-langchain/nodes/agents/Agent/Agent.node.ts":{"9":[1,3,4,5,8,10,12,6,13]},"packages/@n8n/nodes-langchain/nodes/agents/Agent/V1/AgentV1.node.ts":{"37":[1,3,4,5,8,10,12,6,13],"53":[1,3,4,5,8,10,12,6,13],"63":[1,3,4,5,8,10,12,6,13],"221":[1,3,4,5,8,10,12,6,13],"276":[1,3,4,5,8,10,12,6,13],"361":[1,3,4,5,8,10,12,6,13],"470":[1,3,4,5,8,10,12,6,13]},"packages/@n8n/nodes-langchain/nodes/agents/Agent/V2/AgentV2.node.ts":{"24":[1,3,4,5,8,10,12,6,13],"141":[1,3,4,5,8,10,12,6,13]},"packages/@n8n/nodes-langchain/nodes/agents/Agent/V3/AgentV3.node.ts":{"28":[1,3,4,5,8,10,12,6,13],"151":[1,3,4,5,8,10,12,6,13]},"packages/@n8n/nodes-langchain/nodes/agents/Agent/agents/ConversationalAgent/execute.ts":{"16":[1,3,4,5,8,10,12,6,13]},"packages/@n8n/nodes-langchain/nodes/agents/Agent/agents/OpenAiFunctionsAgent/execute.ts":{"15":[1,3,4,5,8,10,12,6,13]},"packages/@n8n/nodes-langchain/nodes/agents/Agent/agents/PlanAndExecuteAgent/execute.ts":{"18":[1,3,4,5,8,10,12,6,13]},"packages/@n8n/nodes-langchain/nodes/agents/Agent/agents/ReActAgent/execute.ts":{"21":[1,3,4,5,8,10,12,6,13]},"packages/@n8n/nodes-langchain/nodes/agents/Agent/agents/SqlAgent/execute.ts":{"25":[1,3,4,5,8,10,12,6,13],"28":[1,3,4,5,8,10,12,6,13],"29":[1,3,4,5,8,10,12,6,13],"43":[1,3,4,5,8,10,12,6,13],"55":[1,3,4,5,8,10,12,6,13],"144":[1,3,4,5,8,10,12,6,13],"150":[1,3,4,5,8,10,12,6,13]},"packages/@n8n/nodes-langchain/nodes/agents/Agent/agents/SqlAgent/other/handlers/mysql.ts":{"4":[1,3,4,5,8,10,12,6,13]},"packages/@n8n/nodes-langchain/nodes/agents/Agent/agents/SqlAgent/other/handlers/postgres.ts":{"6":[1,3,4,5,8,10,12,6,13]},"packages/@n8n/nodes-langchain/nodes/agents/Agent/agents/SqlAgent/other/handlers/sqlite.ts":{"8":[1,3,4,5,8,10,12,6,13],"38":[1,3,4,5,8,10,12,6,13]},"packages/@n8n/nodes-langchain/nodes/agents/Agent/agents/ToolsAgent/V1/execute.ts":{"36":[1,3,4,5,8,10,12,6,13]},"packages/@n8n/nodes-langchain/nodes/agents/Agent/agents/ToolsAgent/V2/description.ts":{"15":[1,3,4,5,8,10,12,6,13]},"packages/@n8n/nodes-langchain/nodes/agents/Agent/agents/ToolsAgent/V2/execute.ts":{"46":[1,3,4,5,8,10,12,6,13],"94":[1,3,4,5,8,10,12,6,13],"99":[1,3,4,5,8,10,12,6,13],"104":[1,3,4,5,8,10,12,6,13],"134":[1,3,4,5,8,10,12,6,13],"202":[1,3,4,5,8,10,12,6,13],"218":[1,3,4,5,8,10,12,6,13],"242":[1,3,4,5,8,10,12,6,13],"299":[1,3,4,5,8,10,12,6,13],"408":[1,3,4,5,8,10,12,6,13]},"packages/@n8n/nodes-langchain/nodes/agents/Agent/agents/ToolsAgent/V3/execute.ts":{"30":[1,3,4,5,8,10,12,6,13],"55":[1,3,4,5,8,10,12,6,13],"135":[1,3,4,5,8,10,12,6,13]},"packages/@n8n/nodes-langchain/nodes/agents/Agent/agents/ToolsAgent/V3/helpers/buildExecutionContext.ts":{"29":[1,3,4,5,8,10,12,6,13]},"packages/@n8n/nodes-langchain/nodes/agents/Agent/agents/ToolsAgent/V3/helpers/checkMaxIterations.ts":{"27":[1,3,4,5,8,10,12,6,13]},"packages/@n8n/nodes-langchain/nodes/agents/Agent/agents/ToolsAgent/V3/helpers/createAgentSequence.ts":{"25":[1,3,4,5,8,10,12,6,13],"66":[1,3,4,5,8,10,12,6,13]},"packages/@n8n/nodes-langchain/nodes/agents/Agent/agents/ToolsAgent/V3/helpers/executeBatch.ts":{"40":[1,3,4,5,8,10,12,6,13],"78":[1,3,4,5,8,10,12,6,13],"107":[1,3,4,5,8,10,12,6,13]},"packages/@n8n/nodes-langchain/nodes/agents/Agent/agents/ToolsAgent/V3/helpers/finalizeResult.ts":{"21":[1,3,4,5,8,10,12,6,13]},"packages/@n8n/nodes-langchain/nodes/agents/Agent/agents/ToolsAgent/V3/helpers/index.ts":{"11":[1,3,4,5,8,10,12,6,13]},"packages/@n8n/nodes-langchain/nodes/agents/Agent/agents/ToolsAgent/V3/helpers/prepareItemContext.ts":{"41":[1,3,4,5,8,10,12,6,13]},"packages/@n8n/nodes-langchain/nodes/agents/Agent/agents/ToolsAgent/V3/helpers/resolveSubAgentRequest.ts":{"25":[1,3,4,5,8,10,12,6,13],"40":[1,3,4,5,8,10,12,6,13],"49":[1,3,4,5,8,10,12,6,13]},"packages/@n8n/nodes-langchain/nodes/agents/Agent/agents/ToolsAgent/V3/helpers/runAgent.ts":{"39":[1,3,4,5,8,10,12,6,13]},"packages/@n8n/nodes-langchain/nodes/agents/Agent/agents/ToolsAgent/common.ts":{"32":[1,3,4,5,8,10,12,6,13],"45":[1,3,4,5,8,10,12,6,13],"56":[1,3,4,5,8,10,12,6,13],"60":[1,3,4,5,8,10,12,6,13],"78":[1,3,4,5,8,10,12,6,13],"112":[1,3,4,5,8,10,12,6,13],"133":[1,3,4,5,8,10,12,6,13],"218":[1,3,4,5,8,10,12,6,13],"228":[1,3,4,5,8,10,12,6,13],"229":[1,3,4,5,8,10,12,6,13],"280":[1,3,4,5,8,10,12,6,13],"285":[1,3,4,5,8,10,12,6,13],"288":[1,3,4,5,8,10,12,6,13],"290":[1,3,4,5,8,10,12,6,13],"335":[1,3,4,5,8,10,12,6,13],"355":[1,3,4,5,8,10,12,6,13],"356":[1,3,4,5,8,10,12,6,13],"364":[1,3,4,5,8,10,12,6,13],"365":[1,3,4,5,8,10,12,6,13],"391":[1,3,4,5,8,10,12,6,13],"411":[1,3,4,5,8,10,12,6,13],"412":[1,3,4,5,8,10,12,6,13],"415":[1,3,4,5,8,10,12,6,13],"478":[1,3,4,5,8,10,12,6,13],"512":[1,3,4,5,8,10,12,6,13],"528":[1,3,4,5,8,10,12,6,13],"543":[1,3,4,5,8,10,12,6,13],"558":[1,3,4,5,8,10,12,6,13],"615":[1,3,4,5,8,10,12,6,13]},"packages/@n8n/nodes-langchain/nodes/agents/Agent/agents/utils.ts":{"13":[1,3,4,5,8,10,12,6,13],"19":[1,3,4,5,8,10,12,6,13],"36":[1,3,4,5,8,10,12,6,13],"42":[1,3,4,5,8,10,12,6,13],"45":[1,3,4,5,8,10,12,6,13]},"packages/@n8n/nodes-langchain/nodes/agents/Agent/utils.ts":{"11":[1,3,4,5,8,10,12,6,13],"23":[1,3,4,5,8,10,12,6,13],"26":[1,3,4,5,8,10,12,6,13],"86":[1,3,4,5,8,10,12,6,13],"89":[1,3,4,5,8,10,12,6,13]},"packages/@n8n/nodes-langchain/nodes/llms/LMChatAnthropic/LmChatAnthropic.node.ts":{"91":[3,4,5,98,6,13],"117":[3,4,5,98,6,13],"185":[3,4,5,98,6,13],"485":[3,4,5,98,6,13],"564":[3,4,5,98,6,13],"597":[3,4,5,98,6,13],"615":[3,4,5,98,6,13],"628":[3,4,5,98,6,13]},"packages/@n8n/nodes-langchain/nodes/llms/LMChatAnthropic/methods/searchModels.ts":{"7":[3,4,5,6,13],"22":[3,4,5,6,13],"31":[3,4,5,6,13],"32":[3,4,5,6,13]},"packages/@n8n/nodes-langchain/nodes/memory/MemoryBufferWindow/MemoryBufferWindow.node.ts":{"30":[3,4,5],"34":[3,4,5],"41":[3,4,5],"63":[3,4,5],"75":[3,4,5],"158":[3,4,5]},"packages/@n8n/nodes-langchain/nodes/memory/MemoryManager/MemoryManager.node.ts":{"20":[3,4,5],"47":[3,4,5],"50":[3,4,5],"53":[3,4,5],"63":[3,4,5],"73":[3,4,5],"302":[3,4,5],"400":[3,4,5]},"packages/@n8n/nodes-langchain/utils/agent-execution/buildResponseMetadata.ts":{"17":[1,3,4,5,8,10,12,6,13]},"packages/@n8n/nodes-langchain/utils/agent-execution/buildSteps.ts":{"36":[1,3,4,5,8,10,12,6,13],"93":[1,3,4,5,8,10,12,6,13],"141":[1,3,4,5,8,10,12,6,13],"164":[1,3,4,5,8,10,12,6,13],"205":[1,3,4,5,8,10,12,6,13],"216":[1,3,4,5,8,10,12,6,13],"223":[1,3,4,5,8,10,12,6,13],"235":[1,3,4,5,8,10,12,6,13],"287":[1,3,4,5,8,10,12,6,13],"291":[1,3,4,5,8,10,12,6,13],"298":[1,3,4,5,8,10,12,6,13],"311":[1,3,4,5,8,10,12,6,13],"315":[1,3,4,5,8,10,12,6,13],"344":[1,3,4,5,8,10,12,6,13],"369":[1,3,4,5,8,10,12,6,13],"391":[1,3,4,5,8,10,12,6,13]},"packages/@n8n/nodes-langchain/utils/agent-execution/createEngineRequests.ts":{"16":[1,3,4,5,8,10,12,6,13],"25":[1,3,4,5,8,10,12,6,13],"43":[1,3,4,5,8,10,12,6,13],"76":[1,3,4,5,8,10,12,6,13],"150":[1,3,4,5,8,10,12,6,13],"157":[1,3,4,5,8,10,12,6,13],"213":[1,3,4,5,8,10,12,6,13],"221":[1,3,4,5,8,10,12,6,13],"224":[1,3,4,5,8,10,12,6,13],"229":[1,3,4,5,8,10,12,6,13],"231":[1,3,4,5,8,10,12,6,13],"271":[1,3,4,5,8,10,12,6,13]},"packages/@n8n/nodes-langchain/utils/agent-execution/executeEngineAction.ts":{"17":[1,3,4,5,8,10,12,6,13],"22":[1,3,4,5,8,10,12,6,13],"47":[1,3,4,5,8,10,12,6,13]},"packages/@n8n/nodes-langchain/utils/agent-execution/index.ts":{"13":[1,3,4,5,8,10,12,6,13]},"packages/@n8n/nodes-langchain/utils/agent-execution/memoryManagement.ts":{"25":[1,3,4,5,8,10,12,6,13],"81":[1,3,4,5,8,10,12,6,13],"148":[1,3,4,5,8,10,12,6,13],"151":[1,3,4,5,8,10,12,6,13],"164":[1,3,4,5,8,10,12,6,13],"242":[1,3,4,5,8,10,12,6,13],"294":[1,3,4,5,8,10,12,6,13]},"packages/@n8n/nodes-langchain/utils/agent-execution/processEventStream.ts":{"20":[1,3,4,5,8,10,12,6,13]},"packages/@n8n/nodes-langchain/utils/agent-execution/processHitlResponses.ts":{"26":[1,3,4,5,8,10,12,6,13],"38":[1,3,4,5,8,10,12,6,13],"47":[1,3,4,5,8,10,12,6,13],"54":[1,3,4,5,8,10,12,6,13],"71":[1,3,4,5,8,10,12,6,13],"80":[1,3,4,5,8,10,12,6,13],"106":[1,3,4,5,8,10,12,6,13]},"packages/@n8n/nodes-langchain/utils/agent-execution/serializeIntermediateSteps.ts":{"10":[1,3,4,5,8,10,12,6,13],"15":[1,3,4,5,8,10,12,6,13],"29":[1,3,4,5,8,10,12,6,13]},"packages/@n8n/nodes-langchain/utils/agent-execution/types.ts":{"192":[1,3,4,5,8,10,12,6,13],"208":[1,3,4,5,8,10,12,6,13],"222":[1,3,4,5,8,10,12,6,13]},"packages/@n8n/utils/src/files/sanitize-filename.ts":{"55":[3,52]},"packages/cli/src/modules/chat-hub/chat-hub-agent.repository.ts":{"8":[3,4,5,96],"12":[3,4,5,96],"23":[3,4,5,96],"31":[3,4,5,96],"36":[3,4,5,96],"43":[3,4,5,96],"52":[3,4,5,96]},"packages/cli/src/modules/chat-hub/chat-hub-agent.service.ts":{"36":[3,4,5,96,122],"51":[3,4,5,96,122],"54":[3,4,5,96,122],"57":[3,4,5,96,122],"58":[3,4,5,96,122],"77":[3,4,5,96,122],"81":[3,4,5,96,122],"89":[3,4,5,96,122],"96":[3,4,5,96,122],"124":[3,4,5,96,122],"164":[3,4,5,96,122],"183":[3,4,5,96,122],"204":[3,4,5,96,122],"216":[3,4,5,96,122],"230":[3,4,5,96,122],"264":[3,4,5,96,122],"287":[3,4,5,96,122],"309":[3,4,5,96,122],"334":[3,4,5,96,122],"349":[3,4,5,96,122],"360":[3,4,5,96,122],"375":[3,4,5,96,122],"384":[3,4,5,96,122],"394":[3,4,5,96,122],"400":[3,4,5,96,122],"403":[3,4,5,96,122],"418":[3,4,5,96,122],"426":[3,4,5,96,122],"451":[3,4,5,96,122]},"packages/cli/src/modules/chat-hub/chat-hub-credentials.service.ts":{"18":[3,4,5,96,32],"25":[3,4,5,96,32],"38":[3,4,5,96,32],"49":[3,4,5,96,32],"62":[3,4,5,96,32],"71":[3,4,5,96,32],"91":[3,4,5,96,32]},"packages/cli/src/modules/chat-hub/chat-hub-execution.service.ts":{"51":[3,4,5,98],"68":[3,4,5,98],"79":[3,4,5,98],"91":[3,4,5,98],"156":[3,4,5,98],"207":[3,4,5,98],"228":[3,4,5,98],"251":[3,4,5,98],"254":[3,4,5,98],"264":[3,4,5,98],"274":[3,4,5,98],"303":[3,4,5,98],"368":[3,4,5,98],"447":[3,4,5,98],"489":[3,4,5,98],"497":[3,4,5,98],"498":[3,4,5,98],"499":[3,4,5,98],"541":[3,4,5,98],"570":[3,4,5,98],"610":[3,4,5,98],"624":[3,4,5,98],"629":[3,4,5,98],"631":[3,4,5,98],"635":[3,4,5,98],"641":[3,4,5,98],"653":[3,4,5,98],"659":[3,4,5,98],"661":[3,4,5,98],"662":[3,4,5,98],"663":[3,4,5,98],"666":[3,4,5,98],"673":[3,4,5,98],"692":[3,4,5,98],"713":[3,4,5,98],"733":[3,4,5,98],"746":[3,4,5,98]},"packages/cli/src/modules/chat-hub/chat-hub-extractor.ts":{"32":[3,4,5],"52":[3,4,5],"65":[3,4,5],"71":[3,4,5]},"packages/cli/src/modules/chat-hub/chat-hub-title.service.ts":{"39":[3,4,5,96,32],"53":[3,4,5,96,32],"80":[3,4,5,96,32],"88":[3,4,5,96,32],"124":[3,4,5,96,32],"152":[3,4,5,96,32],"186":[3,4,5,96,32],"259":[3,4,5,96,32],"260":[3,4,5,96,32],"262":[3,4,5,96,32],"272":[3,4,5,96,32]},"packages/cli/src/modules/chat-hub/chat-hub-tool.repository.ts":{"10":[3,4,5,96,98],"14":[3,4,5,96,98],"22":[3,4,5,96,98],"30":[3,4,5,96,98],"35":[3,4,5,96,98],"42":[3,4,5,96,98],"50":[3,4,5,96,98],"57":[3,4,5,96,98],"68":[3,4,5,96,98],"79":[3,4,5,96,98],"90":[3,4,5,96,98],"98":[3,4,5,96,98],"101":[3,4,5,96,98],"109":[3,4,5,96,98],"112":[3,4,5,96,98],"122":[3,4,5,96,98]},"packages/cli/src/modules/chat-hub/chat-hub-tool.service.ts":{"21":[3,4,5,96,98],"29":[3,4,5,96,98],"33":[3,4,5,96,98],"37":[3,4,5,96,98],"39":[3,4,5,96,98],"42":[3,4,5,96,98],"44":[3,4,5,96,98],"47":[3,4,5,96,98],"51":[3,4,5,96,98],"55":[3,4,5,96,98],"59":[3,4,5,96,98],"63":[3,4,5,96,98],"79":[3,4,5,96,98],"86":[3,4,5,96,98],"104":[3,4,5,96,98],"110":[3,4,5,96,98],"136":[3,4,5,96,98],"137":[3,4,5,96,98],"149":[3,4,5,96,98]},"packages/cli/src/modules/chat-hub/chat-hub-workflow.service.ts":{"85":[3,4,5,96,98,32],"100":[3,4,5,96,98,32],"106":[3,4,5,96,98,32],"127":[3,4,5,96,98,32],"181":[3,4,5,96,98,32],"192":[3,4,5,96,98,32],"247":[3,4,5,96,98,32],"262":[3,4,5,96,98,32],"276":[3,4,5,96,98,32],"296":[3,4,5,96,98,32],"297":[3,4,5,96,98,32],"311":[3,4,5,96,98,32],"358":[3,4,5,96,98,32],"374":[3,4,5,96,98,32],"424":[3,4,5,96,98,32],"425":[3,4,5,96,98,32],"488":[3,4,5,96,98,32],"558":[3,4,5,96,98,32],"623":[3,4,5,96,98,32],"635":[3,4,5,96,98,32],"716":[3,4,5,96,98,32],"724":[3,4,5,96,98,32],"750":[3,4,5,96,98,32],"912":[3,4,5,96,98,32],"927":[3,4,5,96,98,32],"949":[3,4,5,96,98,32],"1019":[3,4,5,96,98,32],"1079":[3,4,5,96,98,32],"1090":[3,4,5,96,98,32],"1104":[3,4,5,96,98,32],"1120":[3,4,5,96,98,32],"1121":[3,4,5,96,98,32],"1128":[3,4,5,96,98,32],"1153":[3,4,5,96,98,32],"1166":[3,4,5,96,98,32],"1201":[3,4,5,96,98,32],"1204":[3,4,5,96,98,32],"1215":[3,4,5,96,98,32],"1226":[3,4,5,96,98,32],"1284":[3,4,5,96,98,32],"1326":[3,4,5,96,98,32],"1396":[3,4,5,96,98,32],"1426":[3,4,5,96,98,32],"1456":[3,4,5,96,98,32],"1464":[3,4,5,96,98,32],"1467":[3,4,5,96,98,32],"1521":[3,4,5,96,98,32],"1530":[3,4,5,96,98,32],"1534":[3,4,5,96,98,32],"1539":[3,4,5,96,98,32],"1552":[3,4,5,96,98,32],"1561":[3,4,5,96,98,32],"1579":[3,4,5,96,98,32],"1586":[3,4,5,96,98,32],"1615":[3,4,5,96,98,32],"1641":[3,4,5,96,98,32],"1762":[3,4,5,96,98,32],"1785":[3,4,5,96,98,32]},"packages/cli/src/modules/chat-hub/chat-hub.attachment.service.ts":{"19":[3,4,5],"28":[3,4,5],"55":[3,4,5],"85":[3,4,5],"129":[3,4,5],"132":[3,4,5],"138":[3,4,5],"146":[3,4,5],"152":[3,4,5],"154":[3,4,5],"158":[3,4,5],"170":[3,4,5],"174":[3,4,5],"196":[3,4,5],"197":[3,4,5],"211":[3,4,5]},"packages/cli/src/modules/chat-hub/chat-hub.constants.ts":{"694":[3,4,5],"719":[3,4,5]},"packages/cli/src/modules/chat-hub/chat-hub.controller.ts":{"63":[3,4,5,96,98],"73":[3,4,5,96,98],"87":[3,4,5,96,98],"97":[3,4,5,96,98],"112":[3,4,5,96,98],"122":[3,4,5,96,98],"160":[3,4,5,96,98],"169":[3,4,5,96,98],"195":[3,4,5,96,98],"225":[3,4,5,96,98],"250":[3,4,5,96,98],"284":[3,4,5,96,98],"309":[3,4,5,96,98],"343":[3,4,5,96,98],"359":[3,4,5,96,98],"375":[3,4,5,96,98],"390":[3,4,5,96,98],"402":[3,4,5,96,98],"404":[3,4,5,96,98],"409":[3,4,5,96,98],"421":[3,4,5,96,98],"436":[3,4,5,96,98],"447":[3,4,5,96,98],"453":[3,4,5,96,98],"463":[3,4,5,96,98],"474":[3,4,5,96,98],"488":[3,4,5,96,98],"511":[3,4,5,96,98],"521":[3,4,5,96,98]},"packages/cli/src/modules/chat-hub/chat-hub.models.service.ts":{"32":[3,4,5,32],"41":[3,4,5,32],"54":[3,4,5,32],"64":[3,4,5,32],"90":[3,4,5,32],"98":[3,4,5,32],"172":[3,4,5,32],"188":[3,4,5,32],"204":[3,4,5,32],"256":[3,4,5,32],"301":[3,4,5,32],"311":[3,4,5,32],"318":[3,4,5,32],"412":[3,4,5,32],"461":[3,4,5,32],"510":[3,4,5,32],"554":[3,4,5,32],"597":[3,4,5,32],"640":[3,4,5,32],"683":[3,4,5,32],"726":[3,4,5,32],"769":[3,4,5,32],"781":[3,4,5,32],"783":[3,4,5,32],"803":[3,4,5,32],"812":[3,4,5,32],"813":[3,4,5,32],"820":[3,4,5,32],"828":[3,4,5,32],"874":[3,4,5,32],"879":[3,4,5,32],"880":[3,4,5,32],"884":[3,4,5,32],"885":[3,4,5,32],"894":[3,4,5,32],"900":[3,4,5,32]},"packages/cli/src/modules/chat-hub/chat-hub.service.ts":{"56":[3,4,5,97,98,32],"76":[3,4,5,97,98,32],"87":[3,4,5,97,98,32],"113":[3,4,5,97,98,32],"163":[3,4,5,97,98,32],"187":[3,4,5,97,98,32],"194":[3,4,5,97,98,32],"198":[3,4,5,97,98,32],"225":[3,4,5,97,98,32],"233":[3,4,5,97,98,32],"249":[3,4,5,97,98,32],"262":[3,4,5,97,98,32],"271":[3,4,5,97,98,32],"281":[3,4,5,97,98,32],"293":[3,4,5,97,98,32],"301":[3,4,5,97,98,32],"318":[3,4,5,97,98,32],"322":[3,4,5,97,98,32],"331":[3,4,5,97,98,32],"375":[3,4,5,97,98,32],"376":[3,4,5,97,98,32],"383":[3,4,5,97,98,32],"422":[3,4,5,97,98,32],"436":[3,4,5,97,98,32],"460":[3,4,5,97,98,32],"474":[3,4,5,97,98,32],"506":[3,4,5,97,98,32],"550":[3,4,5,97,98,32],"587":[3,4,5,97,98,32],"615":[3,4,5,97,98,32],"628":[3,4,5,97,98,32],"707":[3,4,5,97,98,32],"745":[3,4,5,97,98,32],"760":[3,4,5,97,98,32],"773":[3,4,5,97,98,32],"779":[3,4,5,97,98,32],"852":[3,4,5,97,98,32],"878":[3,4,5,97,98,32],"898":[3,4,5,97,98,32],"911":[3,4,5,97,98,32],"917":[3,4,5,97,98,32],"1006":[3,4,5,97,98,32],"1033":[3,4,5,97,98,32],"1042":[3,4,5,97,98,32],"1054":[3,4,5,97,98,32],"1057":[3,4,5,97,98,32],"1113":[3,4,5,97,98,32],"1127":[3,4,5,97,98,32],"1139":[3,4,5,97,98,32],"1142":[3,4,5,97,98,32],"1201":[3,4,5,97,98,32],"1228":[3,4,5,97,98,32],"1271":[3,4,5,97,98,32],"1291":[3,4,5,97,98,32],"1298":[3,4,5,97,98,32]},"packages/cli/src/modules/chat-hub/chat-hub.settings.controller.ts":{"18":[3,4,5,97],"27":[3,4,5,97],"39":[3,4,5,97],"46":[3,4,5,97],"61":[3,4,5,97],"75":[3,4,5,97],"88":[3,4,5,97]},"packages/cli/src/modules/chat-hub/chat-hub.settings.service.ts":{"21":[3,4,5,96,97,122],"26":[3,4,5,96,97,122],"37":[3,4,5,96,97,122],"39":[3,4,5,96,97,122],"48":[3,4,5,96,97,122],"55":[3,4,5,96,97,122],"68":[3,4,5,96,97,122],"76":[3,4,5,96,97,122],"93":[3,4,5,96,97,122],"115":[3,4,5,96,97,122],"125":[3,4,5,96,97,122],"141":[3,4,5,96,97,122],"152":[3,4,5,96,97,122]},"packages/cli/src/modules/chat-hub/chat-message.repository.ts":{"19":[3,4,5],"26":[3,4,5],"38":[3,4,5],"48":[3,4,5],"74":[3,4,5],"108":[3,4,5],"117":[3,4,5],"122":[3,4,5],"130":[3,4,5]},"packages/cli/src/modules/chat-hub/chat-session.repository.ts":{"10":[3,4,5],"14":[3,4,5],"26":[3,4,5],"31":[3,4,5],"36":[3,4,5],"72":[3,4,5],"77":[3,4,5],"91":[3,4,5]},"packages/cli/src/modules/chat-hub/chat-stream-state.service.ts":{"68":[3,4,5],"89":[3,4,5],"115":[3,4,5],"131":[3,4,5],"151":[3,4,5],"177":[3,4,5],"187":[3,4,5],"205":[3,4,5],"233":[3,4,5],"245":[3,4,5],"251":[3,4,5],"268":[3,4,5],"283":[3,4,5],"287":[3,4,5],"291":[3,4,5],"304":[3,4,5],"319":[3,4,5],"329":[3,4,5],"342":[3,4,5],"357":[3,4,5],"367":[3,4,5],"370":[3,4,5],"380":[3,4,5]},"packages/cli/src/modules/chat-hub/chat-stream.service.ts":{"52":[3,4,5,98],"66":[3,4,5,98],"100":[3,4,5,98],"137":[3,4,5,98],"162":[3,4,5,98],"198":[3,4,5,98],"235":[3,4,5,98],"272":[3,4,5,98],"308":[3,4,5,98],"318":[3,4,5,98],"326":[3,4,5,98],"335":[3,4,5,98],"435":[3,4,5,98],"454":[3,4,5,98],"461":[3,4,5,98],"501":[3,4,5,98],"531":[3,4,5,98],"561":[3,4,5,98],"588":[3,4,5,98],"616":[3,4,5,98],"643":[3,4,5,98]},"packages/cli/src/modules/chat-hub/context-limits.ts":{"223":[3,4,5]},"packages/cli/src/modules/chat-hub/stream-capturer.ts":{"13":[3,4,5],"23":[3,4,5],"32":[3,4,5],"43":[3,4,5],"44":[3,4,5],"53":[3,4,5],"67":[3,4,5],"72":[3,4,5],"96":[3,4,5],"112":[3,4,5],"124":[3,4,5],"142":[3,4,5],"151":[3,4,5],"204":[3,4,5]},"packages/core/src/binary-data/utils.ts":{"3":[21,3,181,152,184],"7":[21,3,181,152,184],"12":[21,3,181,152,184],"22":[21,3,181,152,184]},"packages/nodes-base/nodes/Merge/Merge.node.ts":{"9":[3,4,5,130,68,13]},"packages/nodes-base/nodes/Merge/v1/MergeV1.node.ts":{"20":[3,4,5,130,68,13],"205":[3,4,5,130,68,13]},"packages/nodes-base/nodes/Merge/v2/MergeV2.node.ts":{"35":[3,4,5,130,68,13],"293":[3,4,5,130,68,13],"459":[3,4,5,130,68,13],"467":[3,4,5,130,68,13],"520":[3,4,5,130,68,13],"603":[3,4,5,130,68,13],"604":[3,4,5,130,68,13]},"packages/nodes-base/nodes/Merge/v2/utils.ts":{"31":[3,4,5,130,68,13],"32":[3,4,5,130,68,13],"34":[3,4,5,130,68,13],"41":[3,4,5,130,68,13],"47":[3,4,5,130,68,13],"72":[3,4,5,130,68,13],"78":[3,4,5,130,68,13],"103":[3,4,5,130,68,13],"128":[3,4,5,130,68,13],"150":[3,4,5,130,68,13],"151":[3,4,5,130,68,13],"159":[3,4,5,130,68,13],"176":[3,4,5,130,68,13],"187":[3,4,5,130,68,13],"191":[3,4,5,130,68,13],"197":[3,4,5,130,68,13],"203":[3,4,5,130,68,13],"210":[3,4,5,130,68,13],"213":[3,4,5,130,68,13],"216":[3,4,5,130,68,13],"219":[3,4,5,130,68,13],"243":[3,4,5,130,68,13],"246":[3,4,5,130,68,13],"250":[3,4,5,130,68,13],"268":[3,4,5,130,68,13],"273":[3,4,5,130,68,13],"279":[3,4,5,130,68,13],"285":[3,4,5,130,68,13],"288":[3,4,5,130,68,13],"292":[3,4,5,130,68,13],"307":[3,4,5,130,68,13],"327":[3,4,5,130,68,13],"334":[3,4,5,130,68,13],"349":[3,4,5,130,68,13],"350":[3,4,5,130,68,13]},"packages/nodes-base/nodes/Merge/v3/MergeV3.node.ts":{"15":[3,4,5,130,68,13],"26":[3,4,5,130,68,13]},"packages/nodes-base/nodes/Merge/v3/actions/mode/append.ts":{"21":[3,4,5,130,68,13]},"packages/nodes-base/nodes/Merge/v3/actions/mode/chooseBranch.ts":{"73":[3,4,5,130,68,13],"98":[3,4,5,130,68,13],"99":[3,4,5,130,68,13]},"packages/nodes-base/nodes/Merge/v3/actions/mode/combineAll.ts":{"35":[3,4,5,130,68,13]},"packages/nodes-base/nodes/Merge/v3/actions/mode/combineByFields.ts":{"261":[3,4,5,130,68,13],"274":[3,4,5,130,68,13],"297":[3,4,5,130,68,13],"305":[3,4,5,130,68,13],"358":[3,4,5,130,68,13]},"packages/nodes-base/nodes/Merge/v3/actions/mode/combineByPosition.ts":{"49":[3,4,5,130,68,13],"80":[3,4,5,130,68,13],"82":[3,4,5,130,68,13],"95":[3,4,5,130,68,13],"100":[3,4,5,130,68,13],"106":[3,4,5,130,68,13],"110":[3,4,5,130,68,13],"112":[3,4,5,130,68,13]},"packages/nodes-base/nodes/Merge/v3/actions/mode/combineBySql.ts":{"104":[3,4,5,130,68,13],"125":[3,4,5,130,68,13],"131":[3,4,5,130,68,13],"152":[3,4,5,130,68,13],"158":[3,4,5,130,68,13],"167":[3,4,5,130,68,13],"170":[3,4,5,130,68,13],"179":[3,4,5,130,68,13],"180":[3,4,5,130,68,13],"208":[3,4,5,130,68,13],"255":[3,4,5,130,68,13],"270":[3,4,5,130,68,13],"271":[3,4,5,130,68,13],"272":[3,4,5,130,68,13],"289":[3,4,5,130,68,13],"290":[3,4,5,130,68,13],"300":[3,4,5,130,68,13]},"packages/nodes-base/nodes/Merge/v3/actions/router.ts":{"7":[3,4,5,130,68,13]},"packages/nodes-base/nodes/Merge/v3/helpers/sandbox-utils.ts":{"13":[3,4,5,130,68,13],"29":[3,4,5,130,68,13],"41":[3,4,5,130,68,13],"49":[3,4,5,130,68,13],"100":[3,4,5,130,68,13],"120":[3,4,5,130,68,13],"135":[3,4,5,130,68,13],"156":[3,4,5,130,68,13],"181":[3,4,5,130,68,13]},"packages/nodes-base/nodes/Merge/v3/helpers/utils.ts":{"33":[3,4,5,130,68,13],"34":[3,4,5,130,68,13],"36":[3,4,5,130,68,13],"43":[3,4,5,130,68,13],"49":[3,4,5,130,68,13],"74":[3,4,5,130,68,13],"80":[3,4,5,130,68,13],"105":[3,4,5,130,68,13],"130":[3,4,5,130,68,13],"152":[3,4,5,130,68,13],"153":[3,4,5,130,68,13],"161":[3,4,5,130,68,13],"178":[3,4,5,130,68,13],"189":[3,4,5,130,68,13],"193":[3,4,5,130,68,13],"199":[3,4,5,130,68,13],"205":[3,4,5,130,68,13],"212":[3,4,5,130,68,13],"215":[3,4,5,130,68,13],"218":[3,4,5,130,68,13],"221":[3,4,5,130,68,13],"245":[3,4,5,130,68,13],"248":[3,4,5,130,68,13],"252":[3,4,5,130,68,13],"270":[3,4,5,130,68,13],"275":[3,4,5,130,68,13],"281":[3,4,5,130,68,13],"287":[3,4,5,130,68,13],"290":[3,4,5,130,68,13],"294":[3,4,5,130,68,13],"309":[3,4,5,130,68,13],"329":[3,4,5,130,68,13],"336":[3,4,5,130,68,13],"351":[3,4,5,130,68,13],"352":[3,4,5,130,68,13],"364":[3,4,5,130,68,13],"365":[3,4,5,130,68,13],"371":[3,4,5,130,68,13],"375":[3,4,5,130,68,13],"389":[3,4,5,130,68,13],"406":[3,4,5,130,68,13]},"packages/nodes-base/nodes/Merge/v3/methods/loadOptions.ts":{"3":[3,4,5,130,68,13],"36":[3,4,5,130,68,13]},"localhost-34079/assets/_plugin-vue_export-helper-Da88TEg1.js":{"2":[3,98,32]},"localhost-34079/assets/chunk-CC9Q-vWm.js":{"8":[3,98,32],"9":[3,98,32],"10":[3,98,32],"19":[3,98,32],"23":[3,98,32],"29":[3,98,32],"33":[3,98,32],"34":[3,98,32]},"localhost-42857/assets/_plugin-vue_export-helper-Da88TEg1.js":{"2":[3]},"localhost-42857/assets/chunk-CC9Q-vWm.js":{"8":[3],"9":[3],"10":[3],"19":[3],"23":[3],"29":[3],"33":[3],"34":[3]},"packages/@n8n/utils/src/retry.ts":{"46":[3,4,5,96,98,32]},"packages/frontend/@n8n/chat/src/components/ChatFile.vue":{"28":[3,78],"33":[3]},"packages/frontend/@n8n/design-system/src/components/N8nAiModelSelectorDropdown/AiModelSelectorDropdown.vue":{"23":[3,4,5,96,98,32],"80":[3,4,5,96,98,32],"82":[3,4,5,96,98,32],"88":[4,5,96],"98":[3,4,5,96,98,32],"113":[3,4,5,96,98,32],"119":[4,5,96,98,32],"124":[3,4,5,96,98,32],"139":[3,4,5,96,98],"148":[4,5,96],"162":[4,5,96],"180":[4,5,96],"200":[4,5,96]},"packages/frontend/editor-ui/src/app/layouts/ChatLayout.vue":{"8":[3,4,5,96,98,32],"10":[3,4,5,96,98,32]},"packages/frontend/editor-ui/src/app/utils/fileUtils.ts":{"4":[3,78],"21":[3,78],"22":[3,78]},"packages/frontend/editor-ui/src/app/utils/rbac/checks/hasRole.ts":{"5":[3,4,5,96,97,98,32]},"packages/frontend/editor-ui/src/features/ai/chatHub/ChatView.vue":{"76":[3,4,5,96,98,32],"84":[3,4,5,96,98,32],"91":[3,4,5,96,98,32],"94":[3,4,5,96,98,32],"96":[3,4,5,96,98,32],"99":[3,4,5,96,98,32],"101":[3,4,5,96,98,32],"106":[3,4,5,96,98,32],"108":[3,4,5,96,98,32],"134":[3,4],"141":[3,4,5,96,98,32],"146":[3,4,5,96,98,32],"150":[3,4,5,96,98,32],"182":[3,4,5,96,98,32],"214":[3,4,5,96,98,32],"218":[3,4,5,96,98,32],"225":[3,4,5,96,98],"236":[98],"245":[3,4,5,96,98,32],"253":[3,4,5,96,98,32],"258":[3,4,5,96,98,32],"267":[3,4,5,96,98,32],"293":[3,4,5,96,98,32],"308":[3,4,5,96,98,32],"323":[3,4,5,96,98,32],"335":[3,4,5,96,98,32],"363":[3,4,5,96,98,32],"364":[3,4,5,96,98,32],"382":[3,4,5,96,98,32],"400":[3,4,5,96,98,32],"408":[3,4,5,96,98,32],"417":[3,4,5,96,98,32],"428":[3,4,5,96,98,32],"448":[3,4,5,96,98,32],"464":[3,4,5,96,98,32],"472":[3,4,5,96,98,32],"483":[3,4,5,96,98,32],"517":[4],"525":[4],"553":[4],"576":[3,4,5,96,98],"600":[3,4,5,96,98],"604":[4],"628":[96],"707":[3,4,5,96,98,32],"736":[3,4,5,96,98,32],"748":[3,4,5,96,98,32],"769":[4]},"packages/frontend/editor-ui/src/features/ai/chatHub/chat.api.ts":{"32":[3,4,5,96,97,98,32],"44":[3,4,5,96,98,32],"79":[4],"120":[4],"160":[3,4,5,96,98,32],"183":[3,4,5,96,98,32],"232":[3,4,5,96,98,32],"240":[96],"248":[96],"256":[96],"265":[96],"299":[97],"309":[97],"322":[97],"349":[3],"362":[32],"404":[3,4,5,96,98,32],"408":[98]},"packages/frontend/editor-ui/src/features/ai/chatHub/chat.types.ts":{"114":[3,4]},"packages/frontend/editor-ui/src/features/ai/chatHub/chat.utils.ts":{"25":[3,4,5,96,98,32,81],"46":[3,4,5,96,98,32],"64":[3,4,5,96,98,32],"71":[3,4,96,98,32],"82":[96,32],"110":[3,4,5,96,98,32],"122":[3,4,5,96,98,32],"158":[96,32],"196":[3,4,5,96,98,32],"200":[4,5,96],"215":[3,4,5,96,98,32],"227":[3,4,5,96,98,32],"280":[3,4,5,96,98,32],"331":[3,4,5,96,98,32],"335":[3,4,5,96,98,32],"341":[3,4,5,96,98,32],"352":[3,4,5,96,98],"361":[3,4,5,96,98],"381":[3,4,5,96,98,32],"412":[3,4,5,96,98,32],"442":[3,4,5,96,98,32],"459":[3],"467":[3],"487":[3,4,5,96,98,32],"491":[3,4,5,96,98,32],"505":[3,4,5,96,98,32],"515":[3,4,5,96,98,32],"608":[3,4,5,96,98,32]},"packages/frontend/editor-ui/src/features/ai/chatHub/components/ChatAgentAvatar.vue":{"22":[3,4,5,96,98,32]},"packages/frontend/editor-ui/src/features/ai/chatHub/components/ChatConversationHeader.vue":{"47":[3,4,5,96,98,32],"65":[3,4,5,96,98,32],"67":[3,4,5,96,98,32],"85":[3,4,5,96,98,32]},"packages/frontend/editor-ui/src/features/ai/chatHub/components/ChatGreetings.vue":{"22":[3,4,5,96,98,32],"38":[96,32]},"packages/frontend/editor-ui/src/features/ai/chatHub/components/ChatMessage.vue":{"68":[3,4,5,96,98,32],"96":[3,4,5,96,98,32],"116":[3,4,5,96,98,32],"117":[3,4,5,96,98,32],"144":[3,4,5,96,98,32],"149":[3,4,5,96,98,32],"150":[3,4,5,96,98,32],"159":[3,4,5,96,98,32],"169":[3,4,5,96,98,32],"170":[3],"181":[4],"188":[3,4,5,96,98,32],"190":[3,4,5,96,98,32],"198":[3,4,5,96,98,32],"200":[4],"208":[4],"276":[4],"290":[4],"295":[3,4,5,96,98,32],"296":[3,4,5,96,98,32],"307":[3,4,5,96,98,32],"309":[4],"317":[3,4,5,96,98,32],"320":[3,4,5,96,98,32],"389":[4],"397":[4],"410":[3],"422":[3,4,5,96,98,32]},"packages/frontend/editor-ui/src/features/ai/chatHub/components/ChatMessageActions.vue":{"13":[3,4,5,96,98,32],"31":[4],"32":[4],"35":[3,4,5,96,98,32],"37":[3,4,5,96,98,32],"52":[3,4,5,96,98,32],"53":[3,4,5,96,98,32],"55":[4],"59":[4],"65":[3,4,5,96,98,32],"126":[4],"129":[4]},"packages/frontend/editor-ui/src/features/ai/chatHub/components/ChatPrompt.vue":{"18":[3,4,5,96,98,32],"59":[3,4,5,96,98,32],"71":[3,4,5,96,98,32],"75":[3,4,5,96,98,32],"79":[3,4,5,96,98,32],"81":[3,4,5,96,98,32],"82":[3,4,5,96,98,32],"104":[3],"150":[3,4,5,96,98,32],"211":[3,4,5,96,98,32],"212":[3,4,5,96,98,32],"228":[3,4,5,96,98,32],"289":[3,4,5,96,98,32]},"packages/frontend/editor-ui/src/features/ai/chatHub/components/ChatPromptCallouts.vue":{"19":[3,4,5,96,98,32],"35":[3,4],"45":[3,4]},"packages/frontend/editor-ui/src/features/ai/chatHub/components/ChatPromptFull.vue":{"10":[3,4,5,96,98,32],"55":[3],"71":[3,4,5,96,98,32],"75":[3,4,5,96,98,32],"87":[3,4,5,96,98,32],"103":[3,4,5,96,98,32],"112":[3,4,5,96,98,32],"125":[3,4,5,96,98,32]},"packages/frontend/editor-ui/src/features/ai/chatHub/components/ChatSessionMenuItem.vue":{"20":[3,4,5,96,98,32],"34":[3,4,5,96,98,32],"44":[3,4,5,96,98,32],"87":[3,4,5,96,98,32],"89":[3,4,5,96,98,32],"101":[3,4,5,96,98,32],"122":[3,4,5,96,98,32]},"packages/frontend/editor-ui/src/features/ai/chatHub/components/ChatSidebar.vue":{"19":[3,4,5,96,98,32],"49":[3,4,5,96,98,32],"59":[3,4,5,96,98,32],"60":[3,4,5,96,98,32],"69":[3,4,5,96,98,32]},"packages/frontend/editor-ui/src/features/ai/chatHub/components/ChatSidebarContent.vue":{"25":[3,4,5,96,98,32],"32":[3,4,5,96,98,32],"39":[3,4,5,96,98,32],"43":[3,4,5,96,98,32],"45":[3,4,5,96,98,32],"57":[3,4,5,96,98,32],"69":[3,4,5,96,98,32],"80":[3,4,5,96,98,32],"138":[3,4,5,96,98,32],"141":[3,4,5,96,98,32],"156":[3,4,5,96,98,32],"176":[3,4,5,96,98,32],"181":[3,4,5,96,98,32],"184":[3,4,5,96,98,32],"195":[3,4,5,96,98,32],"199":[3,4,5,96,98,32]},"packages/frontend/editor-ui/src/features/ai/chatHub/components/ChatSidebarLink.vue":{"26":[3,4,5,96,98,32],"45":[3,4,5,96,98,32],"62":[3,4,5,96,98,32]},"packages/frontend/editor-ui/src/features/ai/chatHub/components/ChatStarter.vue":{"36":[3,4,5,96,98,32],"38":[3,4,96],"42":[3,4,96],"46":[3,4,96],"47":[3,4,96],"49":[3,4,96],"59":[3,4,5,96,98,32],"67":[3,4,96],"80":[3,4,96],"97":[3,4,96],"111":[3,4,96],"133":[3,4,96],"148":[3,4,96],"159":[3,4,96]},"packages/frontend/editor-ui/src/features/ai/chatHub/components/ChatTypingIndicator.vue":{"2":[3,4,5,96,98,32]},"packages/frontend/editor-ui/src/features/ai/chatHub/components/CopyButton.vue":{"12":[3,4,5,96,98,32]},"packages/frontend/editor-ui/src/features/ai/chatHub/components/ModelSelector.vue":{"54":[3,4,5,96,98,32],"78":[3,4,5,96,98,32],"84":[3,4,5,96,98,32],"85":[3,4,5,96,98,32],"93":[3,4,5,96,98,32],"103":[3,4,5,96,98,32],"105":[3,4,5,96,98,32],"109":[4],"113":[4,5],"144":[4,5,96],"184":[3,4,5,96,98,32],"200":[3,4,5,96,98,32],"208":[4,5,96]},"packages/frontend/editor-ui/src/features/ai/chatHub/components/SkeletonMenuItem.vue":{"2":[3,4,5,96,98,32]},"packages/frontend/editor-ui/src/features/ai/chatHub/components/ToolsSelector.vue":{"32":[98],"34":[3,4,5,96,98,32],"36":[98],"39":[98],"41":[98],"45":[98],"50":[3,4,5,96,98,32],"53":[98],"62":[98],"104":[98],"108":[98],"116":[98],"153":[3,4,5,96,98,32],"166":[3,4,5,96,98,32],"175":[3,4,5,96,98,32],"197":[98],"204":[98],"207":[98]},"packages/frontend/editor-ui/src/features/ai/chatHub/composables/useChatArtifacts.ts":{"6":[3,4,5,96,98,32],"12":[3,4,5,96,98,32],"13":[3,4,5,96,98,32],"21":[3,4,5,96,98,32],"24":[3,4,5,96,98,32],"31":[3,4,5,96,98,32],"81":[3,4,5,96,98,32],"82":[3,4,5,96,98,32]},"packages/frontend/editor-ui/src/features/ai/chatHub/composables/useChatCredentials.ts":{"18":[3,4,5,96,98,32],"38":[3,4,5,96,98,32],"43":[3,4,5,96,98,32],"47":[3,4,5,96,98,32],"49":[3,4,5,96,98,32],"72":[4],"81":[3,4,5,96,98,32],"83":[3,4,5,96,98,32],"100":[3,4,5,96,98,32],"102":[3,4,5,96,98,32]},"packages/frontend/editor-ui/src/features/ai/chatHub/composables/useChatInputFocus.ts":{"13":[3,4,5,96,98,32]},"packages/frontend/editor-ui/src/features/ai/chatHub/composables/useChatPushHandler.ts":{"31":[3,4,5,96,98,32],"42":[3,4,5,96,98,32],"50":[3,4,5,96,98,32],"62":[3,4,5,96,98,32],"93":[3,4,5,96],"118":[3,4,5,96,98,32],"133":[98,32],"148":[3,4,5,96,98,32],"155":[4],"162":[3,4,5,96,98,32],"179":[3,4,5,96,98,32],"211":[3,4,5,96,98,32],"220":[3,4,5,96,98,32],"222":[3,4,5,96,98,32],"233":[96],"268":[3,96,98,32]},"packages/frontend/editor-ui/src/features/ai/chatHub/composables/useChatSession.ts":{"29":[3,4,5,96,98,32],"37":[3,4,5,96,98,32],"41":[96],"46":[3,4,5,96,98,32],"47":[3,4,5,96,98,32],"51":[3,4,5,96,98,32],"71":[3,4,5,96,98,32],"78":[3,4],"85":[3,4,5,96,98,32],"95":[3,4,5,96,98,32],"111":[3,4,5,96,98,32],"112":[3,4,5,96,98,32]},"packages/frontend/editor-ui/src/features/ai/chatHub/composables/useCustomAgent.ts":{"7":[3,4,5,96,98,32],"8":[3,4,5,96,98],"18":[3,4,5,96,98,32]},"packages/frontend/editor-ui/src/features/ai/chatHub/composables/useDynamicCredentialsStatus.ts":{"29":[3,4,5,96,98,32],"35":[3,4,5,96,98,32],"36":[3,4,5,96,98,32],"46":[32],"170":[3,4,5,96,98,32]},"packages/frontend/editor-ui/src/features/ai/chatHub/composables/useFileDrop.ts":{"3":[3,4,5,96,98,32]},"packages/frontend/editor-ui/src/features/ai/chatHub/model-selector.utils.ts":{"118":[3,4,5,96,98,32],"132":[3,4,5,96,98,32],"142":[96],"170":[32],"182":[32],"199":[3,4,5,96,98,32],"223":[3,4,5,96,98,32],"267":[3,4,5,96,98,32],"274":[3,4,5,96,98,32],"284":[3,4,5,96,98,32],"334":[3,4,5,96,98,32],"376":[3,4,5,96,98,32],"393":[3,4,5,96,98,32]},"packages/@n8n/ai-utilities/src/utils/failed-attempt-handler/n8nDefaultFailedAttemptHandler.ts":{"20":[1,98]},"packages/cli/src/tool-generation/index.ts":{"7":[98,8,12,13]},"packages/core/src/execution-engine/node-execution-context/utils/create-node-as-tool.ts":{"43":[98],"89":[98],"97":[98],"109":[98],"115":[98],"125":[98],"139":[98]},"packages/workflow/src/from-ai-parse-utils.ts":{"27":[98],"59":[98],"110":[98],"120":[98],"148":[98],"199":[98],"248":[98],"324":[98],"327":[98],"329":[98],"331":[98],"335":[98],"342":[98],"344":[98],"362":[98],"460":[98],"491":[98],"511":[98],"527":[98]},"packages/workflow/src/tool-helpers.ts":{"10":[98,9,11]},"packages/@n8n/api-types/src/push/heartbeat.ts":{"9":[0,98,38,55]},"packages/frontend/@n8n/design-system/src/components/N8nIcon/nodes/calculator.svg":{"11":[1,98]},"packages/frontend/@n8n/design-system/src/components/N8nIcon/nodes/call-n8n-sub-workflow-tool.svg":{"11":[1,98,10,13]},"packages/frontend/@n8n/design-system/src/components/N8nIcon/nodes/compression.svg":{"11":[1,98]},"packages/frontend/@n8n/design-system/src/components/N8nIcon/nodes/date-and-time.svg":{"11":[1,98]},"packages/frontend/@n8n/design-system/src/components/N8nIcon/nodes/rss-read.svg":{"11":[1,98]},"packages/frontend/@n8n/design-system/src/components/N8nIcon/nodes/think-tool.svg":{"11":[1,98]},"packages/frontend/@n8n/design-system/src/components/N8nIcon/nodes/totp.svg":{"11":[1,98]},"packages/frontend/@n8n/design-system/src/components/N8nSelectableList/SelectableList.vue":{"7":[98,12],"37":[98,12],"39":[98,12],"43":[98,12],"63":[98,12],"70":[98,12],"76":[98,12]},"packages/frontend/editor-ui/src/app/components/AiStarsIcon.vue":{"11":[98,8,12,13]},"packages/frontend/editor-ui/src/features/ai/chatHub/components/ToolListItem.vue":{"31":[98],"38":[98],"45":[98],"51":[98],"54":[98],"65":[98],"76":[98],"83":[98],"93":[98],"104":[98],"145":[98]},"packages/frontend/editor-ui/src/features/ai/chatHub/components/ToolsManagerModal.vue":{"50":[98],"51":[98],"55":[98],"62":[98],"79":[98],"81":[98],"105":[98],"107":[98],"115":[98],"123":[98],"137":[98],"142":[98],"143":[98],"149":[98],"156":[98],"160":[98],"174":[98],"210":[98],"214":[98],"271":[98],"294":[98],"304":[98],"330":[98],"339":[98],"359":[98],"362":[98],"384":[98],"397":[98],"406":[98],"414":[98],"429":[98],"437":[98],"444":[98],"465":[98],"466":[98]},"packages/frontend/editor-ui/src/features/ndv/parameters/components/ParameterInputOverrides/FromAiOverrideButton.vue":{"6":[98,8,12,13],"24":[98,8,12,13]},"packages/frontend/editor-ui/src/features/ndv/parameters/components/ParameterInputOverrides/FromAiOverrideField.vue":{"10":[98,12]},"packages/frontend/editor-ui/src/features/ndv/parameters/components/ParameterInputOverrides/ParameterOverrideSelectableList.vue":{"16":[98,12],"22":[98,12],"23":[98,12],"47":[98,12]},"packages/frontend/editor-ui/src/features/shared/toolConfig/NodeToolSettingsContent.vue":{"71":[98],"76":[98],"78":[98],"92":[98],"96":[98],"100":[98],"110":[98],"115":[98],"118":[98],"126":[98],"127":[98],"130":[98],"144":[98],"152":[98],"159":[98],"169":[98],"181":[98],"190":[98],"199":[98],"235":[98],"251":[98],"252":[98],"313":[98],"314":[98],"329":[98],"334":[98],"343":[98],"344":[98],"352":[98],"386":[98],"422":[98]},"packages/@n8n/blob-storage/src/json-store.ts":{"19":[32,68],"23":[32,68],"27":[32,68],"31":[32,68],"47":[32,68],"55":[32,68],"60":[32,68],"64":[32,68],"66":[32,68],"76":[32,68],"80":[32,68],"86":[32,68],"93":[32,68],"103":[32,68],"117":[32,68],"129":[32,68],"134":[32,68]},"packages/cli/src/events/relays/workflow-failure-notification.event-relay.ts":{"15":[32,104,181,187],"24":[32,104,181,187],"26":[32,104,181,187],"35":[32,104,181,187]},"packages/cli/src/modules/dynamic-credentials.ee/services/credential-resolver-workflow.service.ts":{"33":[32,165],"49":[32,165],"59":[32,165],"93":[32,165],"130":[32,165],"134":[32,165],"138":[32,165],"154":[32,165],"213":[32,165],"239":[32,165],"254":[32,165],"267":[32,165]},"packages/cli/src/modules/dynamic-credentials.ee/services/dynamic-credential-cors.service.ts":{"12":[32,165],"30":[32,165],"31":[32,165],"53":[32,165],"61":[32,165]},"packages/cli/src/modules/dynamic-credentials.ee/services/dynamic-credential-web.service.ts":{"16":[32,165],"35":[32,165],"37":[32,165],"54":[32,165]},"packages/cli/src/modules/dynamic-credentials.ee/workflow-status.controller.ts":{"24":[32,165],"39":[32,165],"60":[32,165],"86":[32,165],"94":[32,165]},"packages/frontend/editor-ui/src/features/ai/chatHub/ChatWorkflowAgentsView.vue":{"30":[32],"31":[32],"32":[32],"49":[32],"68":[32],"76":[32]},"packages/frontend/editor-ui/src/features/ai/chatHub/components/ChatAgentCard.vue":{"24":[96,32],"30":[96,32],"36":[96],"43":[96,32],"53":[96,32],"56":[96,32],"74":[96],"76":[96]},"packages/frontend/editor-ui/src/features/ai/chatHub/components/ChatAgentSearchSort.vue":{"8":[96,32],"18":[96,32],"27":[96,32],"45":[96,32],"57":[96,32]},"packages/frontend/editor-ui/src/features/collaboration/projects/components/ProjectMembersRoleCell.vue":{"10":[32,108],"23":[32,108],"25":[32,108],"26":[32,108],"28":[32,108],"29":[32,108],"31":[32],"33":[32,108]},"packages/frontend/editor-ui/src/features/roles/components/CustomRolesUpgradeModal.vue":{"16":[32,108,122]},"packages/frontend/editor-ui/src/features/roles/components/RoleContactAdminModal.vue":{"9":[32,108,122],"32":[32,108,122]},"packages/frontend/editor-ui/src/features/roles/components/RoleHoverPopover.vue":{"16":[32,108,122],"76":[32,108,122]},"packages/frontend/editor-ui/src/features/roles/components/RoleSelectDropdown.vue":{"84":[32,122],"88":[32,122],"100":[32,108,122],"101":[32,108,122],"105":[32,108,122],"113":[32,108,122],"119":[32,108,122],"125":[32,108,122],"133":[32,108,122],"171":[32,122],"173":[32,122],"198":[32,108,122],"217":[32,108,122],"235":[32,108,122],"248":[32,108,122],"259":[32,108,122],"261":[32,108,122],"267":[32,108,122],"271":[32,108,122],"276":[32,108],"286":[32,108,122],"297":[32,108,122],"307":[32,108,122],"315":[32,108,122]},"packages/@n8n/agents/src/runtime/memory/observation-log-observer.ts":{"96":[36,78,81,40],"129":[36,78,81,40],"138":[36,78,81,40],"139":[36,78,81,40],"165":[36,78,81,40],"254":[36,78,81,40],"258":[36,78,81,40],"262":[36,78,81,40],"272":[36,78,81,40],"283":[36,78,81,40],"296":[36,78,81,40],"320":[36,78,81,40],"324":[36,78,81,40],"330":[36,78,81,40],"335":[36,78,81,40],"343":[36,78,81,40]},"packages/@n8n/agents/src/runtime/memory/scoped-memory-task-runner.ts":{"73":[36,78,81],"84":[36,78,81],"88":[36,78,81],"89":[36,78,81],"96":[36,78,81],"97":[36,78,81],"103":[36,78,81],"107":[36,78,81],"123":[36,78,81],"124":[36,78,81],"135":[36,78,81],"189":[36,78,81],"194":[36,78,81],"216":[36,78,81],"218":[36,78,81],"225":[36,78,81],"231":[36,78,81],"242":[36,78,81],"253":[36,78,81],"266":[36,78,81],"274":[36,78,81]},"packages/@n8n/agents/src/sdk/telemetry.ts":{"29":[36,78,100,81,82],"31":[36,78,100,81,82],"54":[36,78,100,81,82],"66":[36,78,100,81,82],"70":[36,78,100,81,82],"74":[36,78,100,81,82],"78":[36,78,100,81,82],"84":[36,78,100,81,82],"95":[36,78,100,81,82],"103":[36,78,100,81,82],"110":[36,78,100,81,82],"123":[36,78,100,81,82],"132":[36,78,100,81,82],"140":[36,78,100,81,82],"149":[36,78,100,81,82],"198":[36,78,100,81,82],"233":[36,78,100,81,82],"239":[36,78,100,81,82],"247":[36,78,100,81,82],"252":[36,78,100,81,82],"258":[36,78,100,81,82],"264":[36,78,100,81,82],"270":[36,78,100,81,82],"276":[36,78,100,81,82],"282":[36,78,100,81,82],"292":[36,78,100,81,82],"298":[36,78,100,81,82],"309":[36,78,100,81,82],"321":[36,78,100,81,82],"327":[36,78,100,81,82],"343":[36,78,100,81,82],"352":[36,78,100,81,82],"378":[36,78,100,81,82],"385":[36,78,100,81,82]},"packages/@n8n/agents/src/types/sdk/observation-log.ts":{"73":[36,78,81]},"packages/cli/src/modules/instance-ai/instance-ai-credit.service.ts":{"16":[36,78,100,38,81,82],"49":[36,78,100,38,81,82],"72":[36,78,100,38,81,82],"210":[36,78,100,38,81,82],"226":[36,78,100,38,81,82],"245":[36,78,100,38,81,82],"276":[36,78,100,38,81,82],"302":[36,78,100,38,81,82]},"packages/cli/src/modules/instance-ai/run-trace-metadata.ts":{"33":[36,78,100,81,82],"40":[36,78,100,81,82],"48":[36,78,100,81,82],"77":[36,78,100,81,82],"89":[36,78,100,81,82]},"packages/cli/src/modules/instance-ai/trigger-run-data.ts":{"10":[36,40],"14":[36,40]},"packages/frontend/editor-ui/src/app/api/workflows.ee.ts":{"7":[126],"20":[54,55]},"packages/frontend/editor-ui/src/app/components/WorkflowShareModal.ee.vue":{"42":[124,126,161],"60":[124,126,161],"63":[124,126,161],"64":[124,126,161],"65":[126,161],"68":[124,126,161],"71":[124,126,161],"74":[124,126,161],"86":[124,126,161],"90":[124,126,161],"92":[124,126,161],"111":[124,126,161],"115":[124,126,161],"127":[126,161],"130":[124],"132":[124,126,161],"137":[124,126,161],"138":[124,126,161],"154":[126],"161":[126],"175":[126],"182":[126],"217":[126],"241":[124,126,161],"256":[124,126,161],"261":[126],"267":[124,126,161],"279":[124,126,161],"323":[124],"324":[124],"327":[124],"360":[124,126,161],"372":[124,126,161],"374":[124,126,161],"377":[124],"386":[126,161]},"packages/frontend/editor-ui/src/features/collaboration/projects/components/ProjectSharing.vue":{"70":"b:AAAAAAIA4AABAAAAAgAAdAAAAAACAAAA","78":[97,33,53,54,55,122,64,125,126,161],"81":[97,33,53,54,55,122,64,125,126,161],"90":[97,33,53,54,55,122,64,125,126,161],"100":[97,33,54,64,125,126,161],"106":[97,33,64,125,126,161],"112":[97,33,53,54,55,122,64,125,126,161],"113":[97,33,53,54,55,122,64,125,126,161],"122":[97,33,53,54,55,122,64,125,126,161],"126":[97,33,53,54,55,122,64,125,126,161],"128":[97,33,53,54,55,122,64,125,126,161],"142":"b:AAAAAAIA4AABAAAAAgAAdAAAAAACAAAA","158":[97,33,54,55,122,64,125,126],"164":"b:AAAAAAIA4AABAAAAAgAAdAAAAAACAAAA","168":[97,33,54,55,122,64,125,126],"174":[54,55,122,64,125,126],"188":[33],"205":"b:AAAAAAIA4AABAAAAAgAAdAAAAAACAAAA","206":"b:AAAAAAIA4AABAAAAAgAAdAAAAAACAAAA","221":"b:AAAAAAIA4AABAAAAAgAAdAAAAAACAAAA","239":[97,33,53,54,55,122,64,125,126,161],"247":[97,33,53,54,55,122,64,125,126,161],"250":[97,33,53,54,55,122,64,125,126,161],"254":[97,33,53,54,55,122,64,125,126,161],"278":[97,33,124,64,125,126,161],"279":[97,33,124,64,125,126,161],"301":[97,33,64,125,126],"318":[33]},"packages/frontend/editor-ui/src/features/collaboration/projects/components/ProjectSharingInfo.vue":{"14":"b:AAAAAAIA4AABAAAAAgAAdAAAAAACAAAA","25":"b:AAAAAAIA4AABAAAAAgAAdAAAAAACAAAA","31":"b:AAAAAAIA4AABAAAAAgAAdAAAAAACAAAA"},"localhost-45025/assets/_plugin-vue_export-helper-Da88TEg1.js":{"2":[36,37]},"localhost-45025/assets/chunk-CC9Q-vWm.js":{"8":[36,37],"9":[36,37],"10":[36,37],"19":[36,37],"23":[36,37],"29":[36,37],"33":[36,37],"34":[36,37]},"packages/cli/src/scaling/pubsub/publisher.service.ts":{"35":"b:AACAACAAAOgAAAAAAAAACAAAAABABwgA","56":"b:AACAACAAAOgAAAAAAAAACAAAAABABwgA","61":"b:AACAACAAAOgAAAAAAAAACAAAAABABwgA","70":"b:AACAACAAAOgAAAAAAAAACAAAAABABwgA","99":"b:AACAACAAAOgAAAAAAAAACAAAAABABwgA","106":"b:AACAACAAAOgAAAAAAAAACAAAAABABwgA","123":"b:AACAACAAAOgAAAAAAAAACAAAAABABwgA","128":"b:AACAACAAAOgAAAAAAAAACAAAAABABwgA","132":"b:AACAACAAAOgAAAAAAAAACAAAAABABwgA","136":"b:AACAACAAAOgAAAAAAAAACAAAAABABwgA","140":"b:AACAACAAAOgAAAAAAAAACAAAAABABwgA"},"packages/@n8n/nodes-langchain/nodes/agents/Agent/AgentTool.node.ts":{"8":[12]},"packages/@n8n/nodes-langchain/nodes/agents/Agent/V2/AgentToolV2.node.ts":{"19":[12],"99":[12]},"packages/@n8n/nodes-langchain/nodes/agents/Agent/V2/utils.ts":{"11":[12],"23":[12],"26":[12],"86":[12],"89":[12]},"packages/@n8n/nodes-langchain/nodes/agents/Agent/V3/AgentToolV3.node.ts":{"22":[12],"101":[12]},"packages/@n8n/nodes-langchain/nodes/llms/LMChatOpenAi/LmChatOpenAi.node.ts":{"76":[1,12,13],"753":[1,12,13]},"packages/@n8n/nodes-langchain/nodes/llms/LMChatOpenAi/common.ts":{"15":[1,12,13],"18":[1,12,13],"21":[1,12,13],"24":[1,12,13],"27":[1,12,13],"30":[1,12,13],"86":[1,12,13]},"packages/@n8n/nodes-langchain/nodes/llms/LMChatOpenAi/methods/loadModels.ts":{"9":[1,12,13],"27":[1,12,13],"36":[1,12,13],"37":[1,12,13]},"packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/helpers/error-handling.ts":{"11":[1,7,12,127,13],"15":[1,7,12,127,13],"19":[1,7,12,127,13],"36":[1,7,12,127,13]},"packages/nodes-base/nodes/HackerNews/GenericFunctions.ts":{"16":[12,139,140,142,150],"42":[12,139,140,142,150]},"packages/nodes-base/nodes/HackerNews/HackerNews.node.ts":{"264":[12,139,140,142,150]},"packages/frontend/editor-ui/src/features/ndv/runData/components/RunDataJson.vue":{"22":[12,68,6,141,146],"59":[12,68,6,141,146],"61":[12,68,6,141,146],"63":[141],"71":[12,68,6,141,146],"82":[12,68,6,141,146],"83":[12,68,6,141,146],"85":[141],"98":[141],"122":[12,68,6,141,146],"126":[12,68,6,141,146],"130":[12,68,6,141,146],"166":[12,68,6,141,146],"181":[12,68,6,141,146]},"packages/frontend/editor-ui/src/features/ndv/runData/components/RunDataJsonActions.vue":{"64":[12,68,6,141]},"packages/frontend/@n8n/design-system/src/components/N8nIcon/nodes/loop-over-items.svg":{"11":[44,109,58,115,134,6,141,160]},"packages/cli/src/modules/source-control.ee/source-control-export.service.ee.ts":{"70":[85],"94":[85],"98":[85],"102":[85],"106":[85],"114":[85],"116":[85],"123":[85],"130":[85],"152":[85],"155":[85],"164":[85],"175":[85],"208":[85],"219":[85],"232":[85],"257":[85],"273":[85],"324":[85],"346":[85],"347":[85],"380":[85],"414":[85],"419":[85],"455":[85],"486":[85],"488":[85],"497":[85],"515":[85],"518":[85],"525":[85],"527":[85],"530":[85],"545":[85],"586":[85],"605":[85],"609":[85],"616":[85],"630":[85],"646":[85]},"packages/cli/src/modules/source-control.ee/source-control-git.service.ee.ts":{"46":[85],"56":[85],"79":[85],"116":[85],"133":[85],"186":[85],"190":[85],"205":[85],"212":[85],"227":[85],"283":[85],"298":[85],"314":[85],"347":[85],"355":[85],"364":[85],"365":[85],"379":[85],"388":[85],"399":[85],"411":[85],"423":[85],"431":[85],"443":[85],"460":[85],"474":[85],"488":[85],"495":[85],"503":[85]},"packages/cli/src/modules/source-control.ee/source-control-helper.ee.ts":{"42":[85],"66":[85],"74":[85],"97":[85],"124":[85],"158":[85],"162":[85],"166":[85],"173":[85],"177":[85],"181":[85],"185":[85],"189":[85],"207":[85],"216":[85],"229":[85],"249":[85],"261":[85],"267":[85],"284":[85],"289":[85],"332":[85],"341":[85],"345":[85],"346":[85],"350":[85],"358":[85],"362":[85],"364":[85],"368":[85],"375":[85],"377":[85],"379":[85],"381":[85],"383":[85],"387":[85],"394":[85],"395":[85],"397":[85],"399":[85],"409":[85],"426":[85],"463":[85],"478":[85],"486":[85],"487":[85],"489":[85],"506":[85],"526":[85],"535":[85],"539":[85],"554":[85]},"packages/cli/src/modules/source-control.ee/source-control-preferences.service.ee.ts":{"33":[85],"45":[85],"53":[85],"60":[85],"68":[85],"78":[85],"86":[85],"94":[85],"106":[85],"115":[85],"130":[85],"138":[85],"164":[85],"178":[85],"192":[85],"205":[85],"233":[85],"237":[85],"241":[85],"245":[85],"249":[85],"253":[85],"272":[85],"324":[85],"332":[85]},"packages/cli/src/modules/source-control.ee/source-control.controller.ee.ts":{"32":[85],"41":[85],"68":[85],"133":[85],"178":[85],"188":[85],"197":[85],"228":[85],"244":[85],"253":[85],"270":[85],"287":[85],"301":[85]},"packages/cli/src/modules/source-control.ee/source-control.service.ee.ts":{"70":[85],"87":[85],"102":[85],"111":[85],"124":[85],"147":[85],"156":[85],"186":[85],"217":[85],"266":[85],"275":[85],"288":[85],"289":[85],"292":[85],"308":[85],"317":[85],"321":[85],"337":[85],"364":[85],"365":[85],"376":[85],"395":[85],"396":[85],"415":[85],"418":[85],"485":[85],"510":[85],"515":[85],"519":[85],"533":[85],"548":[85],"579":[85],"660":[85],"661":[85],"667":[85]},"localhost-43443/assets/_plugin-vue_export-helper-Da88TEg1.js":{"2":[85]},"localhost-43443/assets/chunk-CC9Q-vWm.js":{"8":[85],"9":[85],"10":[85],"19":[85],"23":[85],"29":[85],"33":[85],"34":[85]},"packages/frontend/@n8n/design-system/src/components/N8nColorPicker/ColorPicker.vue":{"21":[85],"34":[85],"59":[85]},"packages/frontend/editor-ui/src/features/integrations/sourceControl.ee/views/SettingsSourceControl.vue":{"43":[85],"44":[85],"55":[85],"86":[85],"115":[85],"133":[85],"144":[85],"153":[85],"174":[85],"178":[85],"206":[85],"284":[85],"285":[85],"335":[85],"345":[85],"349":[85],"426":[85],"444":[85],"449":[85],"450":[85],"467":[85],"489":[85],"496":[85],"515":[85],"517":[85],"536":[85]},"packages/@n8n/syslog-client/src/client.ts":{"78":[62],"129":[62],"147":[62],"148":[62],"162":[62],"185":[62],"197":[62],"205":[62],"214":[62],"233":[62],"253":[62],"279":[62],"293":[62],"310":[62],"322":[62],"337":[62],"342":[62],"350":[62],"365":[62],"377":[62],"391":[62],"420":[62],"438":[62],"453":[62],"468":[62],"475":[62],"483":[62],"501":[62]},"packages/@n8n/syslog-client/src/factory.ts":{"25":[62]},"packages/@n8n/syslog-client/src/index.ts":{"13":[62]},"packages/@n8n/syslog-client/src/schemas.ts":{"9":[62],"10":[62],"11":[62]},"packages/@n8n/syslog-client/src/utils.ts":{"7":[62],"15":[62],"37":[62],"69":[62]},"packages/cli/src/eventbus/event-message-classes/event-message-generic.ts":{"23":[61,62,63],"31":[61,62,63],"36":[61,62,63]},"packages/cli/src/modules/log-streaming.ee/create-message-event-bus-destination.ts":{"17":[61,62,63]},"packages/cli/src/modules/log-streaming.ee/destinations/message-event-bus-destination-syslog.ee.ts":{"19":[62],"47":[62],"79":[62],"84":[62],"101":[62],"116":[62],"121":[62],"135":[62],"149":[62],"153":[62]},"packages/cli/src/modules/log-streaming.ee/destinations/message-event-bus-destination.ee.ts":{"49":[61,62,63],"84":[61,62,63],"88":[61,62,63],"98":[61,62,63],"117":[61,62,63],"125":[61,62,63],"130":[61,62,63],"134":[61,62,63]},"packages/cli/src/modules/log-streaming.ee/log-streaming-destination.service.ts":{"38":[61,62,63],"51":[61,62,63],"75":[61,62,63],"90":[61,62,63],"97":[61,62,63],"122":[61,62,63],"142":[61,62,63],"161":[61,62,63],"188":[61,62,63],"193":[61,62,63],"194":[61,62,63],"201":[61,62,63],"209":[61,62,63],"220":[61,62,63],"221":[61,62,63],"229":[61,62,63],"238":[61,62,63],"263":[61,62,63]},"packages/cli/src/modules/log-streaming.ee/log-streaming.controller.ts":{"23":[61,62,120,63],"30":[61,62,120,63],"39":[61,62,120,63],"46":[61,62,120,63],"57":[61,62,120,63],"80":[61,62,120,63],"91":[61,62,120,63]},"packages/cli/src/utils/circuit-breaker.ts":{"15":[61,62,63],"147":[61,62,63],"172":[61,62,63],"176":[61,62,63],"180":[61,62,63],"184":[61,62,63],"191":[61,62,63],"197":[61,62,63],"206":[61,62,63],"234":[61,62,63],"269":[61,62,63],"270":[61,62,63]},"packages/cli/src/utils/sliding-window.ts":{"31":[61,62,63],"42":[61,62,63],"57":[61,62,63],"60":[61,62,63],"67":[61,62,63]},"localhost-45287/assets/_plugin-vue_export-helper-Da88TEg1.js":{"2":[120]},"localhost-45287/assets/chunk-CC9Q-vWm.js":{"8":[120],"9":[120],"10":[120],"19":[120],"23":[120],"29":[120],"33":[120],"34":[120]},"packages/frontend/editor-ui/src/features/integrations/logStreaming.ee/components/EventDestinationCard.vue":{"44":[61,120,63],"52":[61,120,63],"56":[61,120,63],"72":[61,120,63],"76":[61,120,63],"87":[61,63],"100":[61],"106":[61],"110":[63],"135":[61,120,63],"141":[61,120,63],"144":[61,120,63],"148":[61,120,63],"153":[61,120,63],"157":[61,120,63],"160":[61,120,63],"176":[61]},"packages/frontend/editor-ui/src/features/integrations/logStreaming.ee/components/EventDestinationSettingsModal.vue":{"77":[61,120,63],"88":[61,120,63],"108":[61,120,63],"122":[61,120,63],"126":[61,120,63],"130":[120,63],"134":[120,63],"138":[61,120,63],"140":[61,120,63],"144":[61,120,63],"162":[61,120,63],"166":[61,120,63],"186":[61,120,63],"190":[61,120,63],"205":[61,120,63],"209":[61,120,63],"218":[61,120,63],"224":[61,120,63],"232":[61,120,63],"236":[61,120,63],"262":[61,120,63],"301":[61,120,63],"306":[63],"330":[63],"342":[61,120,63],"371":[61,120,63],"379":[61,120,63],"395":[61,120,63],"412":[61,120,63],"454":[61,120,63],"464":[61,120,63],"472":[61,120,63],"474":[61,120,63],"486":[61,120,63],"495":[61,120,63]},"packages/frontend/editor-ui/src/features/integrations/logStreaming.ee/logStreaming.store.ts":{"44":[61,120,63],"53":[61,120,63],"81":[61,120,63],"99":[61,120,63],"105":[61,120,63],"128":[61,120,63],"136":[61,120,63],"151":[61,120,63],"155":[61,120,63],"163":[61,120,63],"220":[61,120,63],"226":[63],"231":[61,120,63],"248":[61,120,63],"265":[61,120,63],"276":[61,120,63],"280":[61,120,63],"284":[63]},"packages/frontend/editor-ui/src/features/integrations/logStreaming.ee/logStreaming.utils.ts":{"13":[61,120,63],"33":[61,120,63]},"packages/frontend/editor-ui/src/features/integrations/logStreaming.ee/views/SettingsLogStreamingView.vue":{"37":[61,120,63],"45":[61,120,63],"50":[61,120,63],"54":[61,120,63],"58":[61,120,63],"60":[61,120,63],"73":[61,120,63],"75":[63],"88":[61,120,63],"94":[61,120,63],"98":[61,120,63],"103":[63],"108":[61,120,63],"133":[61,120,63],"137":[61,120,63],"152":[63],"161":[61,63],"182":[61,120,63],"204":[61,120,63],"208":[61,120,63],"214":[63],"215":[61,63],"230":[61,120,63],"248":[63]},"packages/frontend/editor-ui/src/features/ndv/parameters/components/ResourceMapper/MappingFields.vue":{"49":[149],"74":[149],"83":[149],"85":[149],"86":[149],"101":[149],"104":[149],"117":[149],"118":[149],"121":[149],"122":[149],"131":[149],"132":[149],"134":[149],"161":[149],"164":[149],"178":[149],"182":[149],"186":[149],"196":[149],"204":[149],"214":[149],"238":[149],"252":[149],"267":[149],"280":[149],"284":[149],"288":[149],"296":[149],"315":[149],"328":[149],"366":[149],"397":[149],"442":[149]},"packages/frontend/editor-ui/src/features/ndv/parameters/components/ResourceMapper/MatchingColumnsSelect.vue":{"27":[149],"39":[149],"50":[149],"51":[149],"62":[149],"63":[149],"69":[149],"84":[149],"103":[149],"117":[149],"130":[149],"156":[149],"169":[149],"182":[149],"191":[149],"200":[149],"202":[149],"206":[149],"211":[149]},"packages/frontend/editor-ui/src/features/execution/executions/components/ExecutionsTime.vue":{"14":[159],"22":[159],"24":[159],"29":[159],"36":[159]},"localhost-38923/assets/_plugin-vue_export-helper-Da88TEg1.js":{"2":[31,117,139,141,151,156]},"localhost-38923/assets/chunk-CC9Q-vWm.js":{"8":[31,117,139,141,151,156],"9":[31,117,139,141,151,156],"10":[31,117,139,141,151,156],"19":[31,117,139,141,151,156],"23":[31,117,139,141,151,156],"29":[31,117,139,141,151,156],"33":[31,117,139,141,151,156],"34":[31,117,139,141,151,156]},"localhost-38923/assets/verified-Ge9vhh2v.js":{"8":[31,141,156]},"packages/frontend/editor-ui/src/features/shared/editors/components/CodeNodeEditor/completer.ts":{"23":[31],"27":[31]},"localhost-40459/assets/_plugin-vue_export-helper-Da88TEg1.js":{"2":[77]},"localhost-40459/assets/chunk-CC9Q-vWm.js":{"8":[77],"9":[77],"10":[77],"19":[77],"23":[77],"29":[77],"33":[77],"34":[77]},"packages/frontend/@n8n/design-system/src/components/N8nSettingsLayout/SettingsLayout.vue":{"28":[77],"29":[77],"33":[77]},"packages/frontend/@n8n/design-system/src/components/N8nSettingsPageHeader/SettingsPageHeader.vue":{"29":[77],"40":[77],"41":[77],"65":[77]},"packages/frontend/editor-ui/src/features/settings/apiKeys/apiKeys.store.ts":{"18":[77],"24":[77]},"packages/frontend/editor-ui/src/features/settings/apiKeys/components/ApiKeyScopesModal.vue":{"7":[77],"18":[77],"27":[77]},"packages/frontend/editor-ui/src/features/settings/apiKeys/components/RevokeApiKeyConfirmModal.vue":{"9":[77],"25":[77],"33":[77],"44":[77]},"packages/frontend/editor-ui/src/features/settings/apiKeys/components/RotateApiKeyConfirmModal.vue":{"7":[77],"21":[77],"28":[77]},"packages/frontend/editor-ui/src/features/settings/apiKeys/views/SettingsApiView.vue":{"135":[77],"201":[77],"301":[77],"303":[77],"304":[77],"313":[77],"322":[77]},"packages/frontend/editor-ui/src/features/shared/banners/components/banners/BaseBanner.vue":{"19":[77],"36":[77],"46":[77],"62":[77]},"packages/frontend/editor-ui/src/features/shared/banners/components/banners/TrialBanner.vue":{"73":[77],"80":[77],"81":[77],"82":[77],"90":[77],"93":[77],"99":[77],"104":[77],"106":[77],"120":[77],"130":[77],"135":[77],"143":[77],"148":[77],"166":[77],"179":[77],"195":[77],"199":[77],"205":[77],"213":[77]},"localhost-46121/assets/_plugin-vue_export-helper-Da88TEg1.js":{"2":[79,100]},"localhost-46121/assets/chunk-CC9Q-vWm.js":{"8":[79,100],"9":[79,100],"10":[79,100],"19":[79,100],"23":[79,100],"29":[79,100],"33":[79,100],"34":[79,100]},"packages/frontend/@n8n/design-system/src/components/N8nAiActivityStepButton/AiActivityStepButton.vue":{"4":[79],"28":[79],"38":[79]},"packages/frontend/editor-ui/src/features/ai/instanceAi/components/AgentSection.vue":{"15":[79],"21":[79],"24":[79],"26":[79],"40":[79],"52":[79],"60":[79],"66":[79],"67":[79],"68":[79],"70":[79]},"packages/core/src/execution-engine/node-execution-context/utils/construct-execution-metadata.ts":{"8":[49,139,146],"13":[49,139,146]},"packages/frontend/editor-ui/src/app/utils/json-schema.ts":{"5":[139],"9":[139],"12":[139],"25":[139],"32":[139],"35":[139]},"packages/cli/src/executions/pre-execution-checks/subworkflow-policy-checker.ts":{"17":[181,152,184],"28":[181,152,184],"61":[181,152,184],"79":[181,152,184],"88":[181,152,184],"106":[181,152,184],"110":[181,152,184],"111":[181,152,184],"122":[181,152,184]},"packages/cli/src/task-runners/errors/task-cancelled.error.ts":{"4":[181]},"packages/cli/src/task-runners/task-runner-module.ts":{"44":[6,181],"53":[6,181],"61":[6,181],"69":[6,181],"70":[6,181],"77":[6,181],"84":[6,181],"94":[6,181],"103":[6,181],"113":[6,181],"159":[6,181]},"packages/cli/src/utils/object-to-error.ts":{"10":[181]},"packages/workflow/src/errors/execution-cancelled.error.ts":{"9":[6,181],"19":[6,181],"26":[6,181],"33":[6,181]},"packages/@n8n/ai-workflow-builder.ee/src/agents/planner.agent.ts":{"62":[20],"64":[20],"109":[20],"136":[20]},"packages/@n8n/ai-workflow-builder.ee/src/agents/responder.agent.ts":{"78":[20],"104":[20],"108":[20],"112":[20],"121":[20],"186":[20],"231":[20],"269":[20]},"packages/@n8n/ai-workflow-builder.ee/src/agents/supervisor.agent.ts":{"23":[20],"66":[20],"74":[20],"122":[20]},"packages/@n8n/ai-workflow-builder.ee/src/ai-workflow-builder-agent.service.ts":{"39":[20],"64":[20],"69":[20],"90":[20],"106":[20],"163":[20],"171":[20],"175":[20],"184":[20],"190":[20],"200":[20],"237":[20],"248":[20],"271":[20],"330":[20],"367":[20],"369":[20],"376":[20],"378":[20],"383":[20],"384":[20],"389":[20],"404":[20],"427":[20],"433":[20],"451":[20],"470":[20],"486":[20],"505":[20],"545":[20]},"packages/@n8n/ai-workflow-builder.ee/src/llm-config.ts":{"26":[20],"28":[20],"43":[20],"45":[20],"64":[20],"66":[20],"90":[20],"92":[20],"112":[20],"114":[20],"144":[20],"145":[20],"147":[20],"243":[20]},"packages/@n8n/ai-workflow-builder.ee/src/multi-agent-workflow-subgraphs.ts":{"56":[20],"73":[20],"113":[20],"118":[20],"187":[20],"194":[20],"211":[20],"252":[20],"275":[20],"286":[20],"353":[20],"379":[20],"391":[20],"392":[20],"402":[20],"403":[20],"406":[20],"433":[20],"444":[20],"452":[20],"506":[20],"525":[20],"527":[20],"536":[20],"544":[20]},"packages/@n8n/ai-workflow-builder.ee/src/prompts/agents/discovery.prompt.ts":{"30":[20],"32":[20],"36":[20],"38":[20],"347":[20],"358":[20],"368":[20],"372":[20]},"packages/@n8n/ai-workflow-builder.ee/src/prompts/agents/planner.prompt.ts":{"73":[20],"98":[20],"109":[20],"122":[20],"129":[20],"130":[20]},"packages/@n8n/ai-workflow-builder.ee/src/prompts/agents/responder.prompt.ts":{"168":[20],"178":[20],"188":[20],"200":[20],"205":[20],"226":[20],"232":[20],"244":[20]},"packages/@n8n/ai-workflow-builder.ee/src/prompts/builder/prompt-builder.ts":{"17":[20],"30":[20],"44":[20],"55":[20],"79":[20],"116":[20],"129":[20],"149":[20],"175":[20],"197":[20],"228":[20],"252":[20],"268":[20],"282":[20],"302":[20],"330":[20],"357":[20]},"packages/@n8n/ai-workflow-builder.ee/src/prompts/index.ts":{"31":[20],"45":[20],"47":[20]},"packages/@n8n/ai-workflow-builder.ee/src/session-manager.service.ts":{"36":[20],"54":[20],"62":[20],"72":[20],"83":[20],"87":[20],"96":[20],"104":[20],"118":[20],"132":[20],"160":[20],"166":[20],"174":[20],"180":[20],"185":[20],"200":[20],"222":[20],"270":[20],"275":[20],"279":[20],"283":[20],"300":[20],"312":[20],"335":[20],"363":[20],"378":[20],"427":[20],"440":[20],"532":[20],"564":[20],"583":[20],"666":[20],"674":[20]},"packages/@n8n/ai-workflow-builder.ee/src/subgraphs/discovery.subgraph.ts":{"103":[20],"104":[20],"109":[20],"110":[20],"115":[20],"116":[20],"121":[20],"122":[20],"127":[20],"128":[20],"133":[20],"134":[20],"139":[20],"140":[20],"145":[20],"146":[20],"151":[20],"152":[20],"175":[20],"176":[20],"181":[20],"187":[20],"193":[20],"199":[20],"200":[20],"205":[20],"206":[20],"211":[20],"212":[20],"217":[20],"218":[20],"223":[20],"224":[20],"229":[20],"230":[20],"237":[20],"238":[20],"276":[20],"304":[20],"307":[20],"351":[20],"377":[20],"396":[20],"440":[20],"447":[20],"494":[20],"501":[20],"561":[20],"563":[20],"591":[20],"659":[20],"670":[20],"709":[20],"721":[20],"769":[20],"795":[20]},"packages/@n8n/ai-workflow-builder.ee/src/tools/get-documentation.tool.ts":{"60":[20],"81":[20],"88":[20],"113":[20],"115":[20],"136":[20],"146":[20]},"packages/@n8n/ai-workflow-builder.ee/src/tools/node-search.tool.ts":{"46":[20],"91":[20],"105":[20],"121":[20],"123":[20],"170":[20]},"packages/@n8n/ai-workflow-builder.ee/src/tools/utils/ssrf-guard.ts":{"28":[20],"38":[20],"40":[20],"41":[20],"43":[20],"53":[20],"63":[20]},"packages/@n8n/ai-workflow-builder.ee/src/tools/utils/web-fetch-security.ts":{"35":[20],"48":[20],"55":[20],"61":[20],"64":[20],"67":[20],"70":[20],"111":[20],"112":[20],"120":[20],"121":[20],"122":[20],"123":[20],"127":[20],"131":[20],"144":[20],"146":[20],"163":[20],"167":[20]},"packages/@n8n/ai-workflow-builder.ee/src/tools/web-fetch.tool.ts":{"30":[20],"69":[20],"83":[20],"86":[20]},"packages/@n8n/ai-workflow-builder.ee/src/utils/thread-id.ts":{"7":[20]},"packages/@n8n/ai-workflow-builder.ee/src/workflow-builder-agent.ts":{"166":[20],"184":[20],"196":[20],"204":[20],"213":[20],"234":[20],"315":[20],"344":[20],"362":[20],"392":[20],"410":[20],"441":[20],"463":[20],"476":[20],"515":[20],"587":[20],"602":[20],"618":[20],"668":[20],"686":[20],"690":[20]},"packages/@n8n/api-types/src/dto/ai/ai-build-request.dto.ts":{"41":[20],"51":[20],"61":[20],"63":[20],"72":[20],"75":[20],"86":[20],"95":[20]},"packages/cli/src/controllers/ai.controller.ts":{"37":[20],"45":[20],"65":[20],"74":[20],"148":[20],"153":[20],"162":[20],"187":[20],"201":[20],"219":[20],"230":[20],"250":[20],"261":[20],"272":[20],"287":[20],"301":[20],"322":[20],"338":[20]},"packages/cli/src/modules/workflow-builder/workflow-builder-session.repository.ts":{"22":[20],"26":[20],"46":[20],"73":[20],"78":[20]},"packages/nodes-base/nodes/SplitInBatches/SplitInBatches.node.ts":{"9":[109,6,141]},"packages/nodes-base/nodes/SplitInBatches/v1/SplitInBatchesV1.node.ts":{"62":[109,6,141],"97":[109,6,141],"111":[109,6,141],"128":[109,6,141]},"packages/nodes-base/nodes/SplitInBatches/v2/SplitInBatchesV2.node.ts":{"64":[109,6,141],"102":[109,6,141],"116":[109,6,141],"135":[109,6,141],"147":[109,6,141]},"packages/nodes-base/nodes/SplitInBatches/v3/SplitInBatchesV3.node.ts":{"88":[109,6,141],"126":[109,6,141],"140":[109,6,141],"159":[109,6,141],"171":[109,6,141]},"packages/nodes-base/nodes/Switch/Switch.node.ts":{"9":[130,141,146,70]},"packages/nodes-base/nodes/Switch/V1/SwitchV1.node.ts":{"15":[130,141,146,70],"523":[130,141,146,70],"538":[130,141,146,70],"540":[130,141,146,70],"542":[130,141,146,70],"544":[130,141,146,70],"546":[130,141,146,70],"548":[130,141,146,70],"550":[130,141,146,70],"551":[130,141,146,70],"552":[130,141,146,70],"554":[130,141,146,70],"556":[130,141,146,70],"558":[130,141,146,70],"560":[130,141,146,70],"562":[130,141,146,70],"564":[130,141,146,70],"578":[130,141,146,70],"595":[130,141,146,70],"616":[130,141,146,70]},"packages/nodes-base/nodes/Switch/V2/SwitchV2.node.ts":{"17":[130,141,146,70],"521":[130,141,146,70],"523":[130,141,146,70],"538":[130,141,146,70],"553":[130,141,146,70],"555":[130,141,146,70],"557":[130,141,146,70],"559":[130,141,146,70],"561":[130,141,146,70],"563":[130,141,146,70],"565":[130,141,146,70],"566":[130,141,146,70],"567":[130,141,146,70],"569":[130,141,146,70],"571":[130,141,146,70],"573":[130,141,146,70],"575":[130,141,146,70],"577":[130,141,146,70],"579":[130,141,146,70],"593":[130,141,146,70],"610":[130,141,146,70],"631":[130,141,146,70],"652":[130,141,146,70],"662":[130,141,146,70]},"packages/nodes-base/nodes/Switch/V3/SwitchV3.node.ts":{"21":[130,141,146,70],"25":[130,141,146,70],"31":[130,141,146,70],"51":[130,141,146,70],"347":[130,141,146,70],"377":[130,141,146,70],"383":[130,141,146,70],"409":[130,141,146,70],"422":[130,141,146,70]},"packages/frontend/@n8n/design-system/src/components/N8nIcon/nodes/sort.svg":{"11":[141,146,70]},"packages/frontend/@n8n/design-system/src/components/N8nIcon/nodes/switch.svg":{"11":[48,112,130,134,141,146,70,160]},"packages/frontend/editor-ui/src/features/ndv/parameters/utils/assignmentCollection.utils.ts":{"8":[141],"18":[141]},"packages/cli/src/errors/response-errors/bad-request.error.ts":{"4":[163,8,184,156],"8":[163,8,184,156]},"packages/cli/src/errors/response-errors/conflict.error.ts":{"4":[156]},"packages/frontend/editor-ui/src/app/components/ActivationModal.vue":{"22":[50,127,155,156,161],"31":[50,127,155,156,161],"33":[50,127,155,156,161],"94":[127],"98":[50,127,155,156,161],"103":[50,127,155,156,161],"105":[50,127,155,156,161],"110":[50,127,155,156,161],"122":[50,127,155,156,161],"125":[50,127,155,156,161]},"packages/frontend/editor-ui/src/app/components/MainHeader/WorkflowPublishModal.vue":{"31":[50,127,155,156,161],"46":[50,127,155,156,161],"50":[50,127,155,156,161],"54":[50,127,155,156,161],"60":[50,127,155,156,161],"70":[50,127,155,156,161],"72":[50,127,155,156,161],"81":[50,127,155,156,161],"87":[50,127,155,156,161],"107":[50,127,155,156,161],"111":[50,127,155,156,161],"129":[50,127,155,156,161],"133":[50,127,155,156,161],"136":[50],"158":[50,127,155,156,161],"201":[50,127,155,156,161],"249":[50,127,155,156,161],"262":[50,127,155,156,161],"263":[50,127,155,156,161],"265":[50,127,155,156,161],"309":[50,127,155,156,161]},"packages/frontend/editor-ui/src/app/components/WorkflowVersionForm.vue":{"16":[50,127,155,156,161],"26":[50,127,155,156,161],"38":[50,127,155,156,161],"47":[50,127,155,156,161],"62":[50,127,155,156,161]},"packages/frontend/editor-ui/src/app/composables/useWorkflowActivate.ts":{"88":[50,127,155,156,161],"185":[161]},"packages/frontend/editor-ui/src/features/workflows/workflowHistory/utils.ts":{"9":[136],"15":[50,127,155,156,161],"19":[136],"37":[136],"69":[136],"74":[136]},"packages/@n8n/config/src/configs/ai.config.ts":{"6":[1],"34":[1]},"packages/@n8n/nodes-langchain/nodes/memory/MemoryRedisChat/MemoryRedisChat.node.ts":{"26":[1,13],"114":[1,13],"148":[1,13],"178":[1,13]},"packages/@n8n/nodes-langchain/nodes/tools/ToolCalculator/ToolCalculator.node.ts":{"15":[1],"21":[1],"55":[1],"61":[1]},"packages/@n8n/nodes-langchain/nodes/tools/ToolCode/ToolCode.node.ts":{"46":[1,9,11],"72":[1,9,11],"92":[1,9,11],"176":[1,9,11],"329":[1,9,11],"334":[1,9,11]},"localhost-36491/assets/_plugin-vue_export-helper-Da88TEg1.js":{"2":[1,7]},"localhost-36491/assets/chunk-CC9Q-vWm.js":{"8":[1,7],"9":[1,7],"10":[1,7],"19":[1,7],"23":[1,7],"29":[1,7],"33":[1,7],"34":[1,7]},"localhost-36491/assets/verified-Ge9vhh2v.js":{"8":[1,7]},"packages/frontend/@n8n/design-system/src/components/N8nIcon/custom/text.svg":{"8":[1,6]},"packages/frontend/@n8n/design-system/src/components/N8nIcon/nodes/item-list-output-parser.svg":{"11":[1]},"packages/frontend/@n8n/design-system/src/components/N8nIcon/nodes/model-selector.svg":{"11":[1,7,13]},"packages/frontend/@n8n/design-system/src/components/N8nIcon/nodes/simple-memory.svg":{"11":[1,13]},"packages/frontend/@n8n/design-system/src/components/N8nIcon/nodes/vector-store-question-answer-tool.svg":{"11":[1]},"packages/@n8n/nodes-langchain/nodes/chains/ChainLLM/ChainLlm.node.ts":{"24":[7,13],"68":[7,13],"88":[7,13],"94":[7,13],"120":[7,13],"138":[7,13]},"packages/@n8n/nodes-langchain/nodes/chains/ChainLLM/methods/chainExecutor.ts":{"20":[7,13],"35":[7,13],"46":[7,13],"61":[7,13],"70":[7,13],"96":[7,13],"119":[7,13],"127":[7,13],"141":[7,13]},"packages/@n8n/nodes-langchain/nodes/chains/ChainLLM/methods/config.ts":{"21":[7,13]},"packages/@n8n/nodes-langchain/nodes/chains/ChainLLM/methods/imageUtils.ts":{"15":[7,13],"27":[7,13],"82":[7,13]},"packages/@n8n/nodes-langchain/nodes/chains/ChainLLM/methods/index.ts":{"1":[7,13]},"packages/@n8n/nodes-langchain/nodes/chains/ChainLLM/methods/processItem.ts":{"11":[7,13],"33":[7,13]},"packages/@n8n/nodes-langchain/nodes/chains/ChainLLM/methods/promptUtils.ts":{"23":[7,13],"34":[7,13],"44":[7,13],"50":[7,13],"66":[7,13],"75":[7,13],"120":[7,13],"149":[7,13],"153":[7,13],"158":[7,13],"159":[7,13],"168":[7,13]},"packages/@n8n/nodes-langchain/nodes/chains/ChainLLM/methods/responseFormatter.ts":{"6":[7,13]},"packages/@n8n/nodes-langchain/utils/output_parsers/parserInput.ts":{"10":[7,13]},"packages/frontend/@n8n/design-system/src/components/N8nIcon/nodes/chat-memory-manager.svg":{"11":[7,130]},"localhost-43871/assets/_plugin-vue_export-helper-Da88TEg1.js":{"2":[69]},"localhost-43871/assets/chunk-CC9Q-vWm.js":{"8":[69],"9":[69],"10":[69],"19":[69],"23":[69],"29":[69],"33":[69],"34":[69]},"packages/cli/src/middlewares/list-query/dtos/credentials.filter.dto.ts":{"22":[16,28,8,33,54,55,60,64]},"localhost-42131/assets/_plugin-vue_export-helper-Da88TEg1.js":{"2":[33]},"localhost-42131/assets/chunk-CC9Q-vWm.js":{"8":[33],"9":[33],"10":[33],"19":[33],"23":[33],"29":[33],"33":[33],"34":[33]},"packages/frontend/@n8n/design-system/src/components/N8nRecycleScroller/RecycleScroller.vue":{"14":[28,8,33,99,54,55,60,64,125],"30":[28,8,33,99,54,55,60,64,125],"31":[28,8,33,99,54,55,60,64,125],"50":[28,8,33,99,54,55,60,64,125],"52":[28,8,33,99,54,55,60,64,125],"62":[28,8,33,99,54,55,60,64,125],"63":[28,8,33,99,54,55,60,64,125],"74":[28,8,33,99,54,55,60,64,125],"78":[28,8,33,99,54,55,60,64,125],"79":[28,8,33,99,54,55,60,64,125],"81":[28,8,33,99,54,55,60,64,125],"83":[28,8,33,99,54,55,60,64,125],"96":[28,8,33,99,54,55,60,64,125],"106":[28,8,33,99,54,55,60,64,125],"110":[28,8,33,99,54,55,60,64,125],"120":[28,8,33,99,54,55,60,64,125],"124":[28,8,33,99,54,55,60,64,125],"136":[28,8,33,99,54,55,60,64,125],"137":[28,8,33,99,54,55,60,64,125],"145":[28,8,33,99,54,55,60,64,125],"151":[28,8,33,99,54,55,60,64,125],"152":[28,8,33,99,54,55,60,64,125],"174":[28,8,33,99,54,55,60,64,125],"177":[28,8,33,99,54,55,60,64,125],"212":[28,8,33,99,54,55,60,64,125],"219":[28,8,33,99,54,55,60,64,125],"221":[28,8,33,99,54,55,60,64,125]},"packages/frontend/editor-ui/src/app/components/DependencyPill.vue":{"37":[33,108,55,125,161,162],"39":[33,108,55,125,161,162],"45":[33,108,55,125,161,162],"47":[33,108,55,125,161,162],"51":[33,108,55,125,161,162],"93":[33,108,55,125,161,162],"219":[33,108,55,125,161,162],"222":[33,108,55,125,161,162]},"packages/frontend/editor-ui/src/features/credentials/components/CredentialCard.vue":{"68":[28,8,33,99,54,55,60,64,125],"69":[28,8,33,99,54,55,60,64,125],"72":[28,8,33,99,54,55,60,64,125],"74":[28,8,33,99,54,55,60,64,125],"82":[28,8,33,99,54,55,60,64,125],"118":[28,8,33,99,54,55,60,64,125],"127":[28,8,33,99,54,55,60,64,125],"129":[33],"156":[54],"220":[54],"229":[28,8,33,99,54,55,60,64,125],"234":[28,8,33,99,54,55,60,64,125],"237":[28,8,33,99,54,55,60,64,125],"241":[33],"250":[28,8,33,99,54,55,60,64,125],"270":[28,8,33,99,54,55,60,64,125]},"packages/frontend/editor-ui/src/features/credentials/components/CredentialEdit/CredentialSharing.ee.vue":{"37":[97,33,64,125],"59":[97,33,64,125],"67":[97,33,64,125],"68":[97,33,64,125],"80":[97,33,64,125],"84":[97,33,64,125],"88":[97,33,64,125],"94":[97,33,64,125],"100":[97,33,64,125],"101":[97,33,64,125],"114":[97,33,64,125],"120":[97,33,64,125],"126":[64,125],"137":[97,33,64,125],"163":[97,33,64,125],"192":[97,33]},"packages/frontend/editor-ui/src/features/credentials/components/CredentialsSelect.vue":{"21":[27,8,33],"36":[27,8,33],"37":[27,8,33],"43":[27,8,33],"44":[27,8,33],"47":[8,33],"57":[27,8,33],"84":[27,8,33],"92":[27,8],"101":[27,8,33],"102":[27,8,33],"125":[27,8,33],"139":[8,33],"143":[27,8,33],"151":[27,8,33],"173":[27,8,33]},"packages/frontend/editor-ui/src/features/credentials/components/CredentialsSelectModal.vue":{"16":[16,28,8,33,99,55,60,64,125],"32":[16,28,8,33,99,55,60,64,125],"37":[16,28,8,33,99,55,60,64,125],"44":[16,28,8,33,99,55,60,64,125],"52":[16,28,8,33,99,55,60,64,125],"53":[16,28,8,33,99,55,60,64,125],"56":[16,28,8,33,99,55,60,64,125],"59":[16,28,8,33,99,55,60,64,125],"62":[16,28,8,33,99,55,60,64,125],"66":[16,28,8,33,99,55,60,64,125],"70":[16,28,8,33,99,55,60,64,125],"98":[16,28,8,33,99,55,60,64,125],"111":[16,28,8,33,99,55,60,64,125],"116":[16,28,8,33,99,55,60,64,125],"132":[16,28,8,33,99,55,60,64,125],"134":[16,28,8,33,99,55,60,64,125],"136":[16,28,8,33,99,55,60,64,125],"146":[16,28,8,33,99,55,60,64,125]},"packages/frontend/editor-ui/src/features/credentials/views/CredentialsView.vue":{"66":[16,28,33,64,125],"83":[28,8,33,99,54,55,60,64,125],"93":[28,8,33,99,54,55,60,64,125],"96":[16,28,8,33,99,54,55,60,64,125],"97":[28,8,33,99,54,55,60,64,125],"122":[16,28,8,33,99,54,55,60,64,125],"124":[16,28,8,33,99,54,55,60,64,125],"140":[28,33,99,60],"144":[28,8,33,99,55,60,64,125],"152":[28,99,60],"161":[16,28,8,33,99,54,55,60,64,125],"172":[28,8,33,99,54,55,60,64,125],"195":[16,28,8,33,99,54,55,60,64,125],"206":[16,28,8,33,99,54,55,60,64,125],"231":[16,28,8,33,99,54,55,60,64,125],"260":[8,33,54,55,64,125],"264":[16,28,8,33,99,54,55,60,64,125],"266":[28,8,33,99,55,60,64,125],"279":[16,28,8,33,99,54,55,60,64,125],"281":[16,28,8,33,99,54,55,60,64,125],"282":[16,28,8,33,99,55,60,64,125],"291":[16,28,8,33,99,54,55,60,64,125],"301":[16,28,8,33,99,54,55,60,64,125],"320":[16,28,8,33,99,54,55,60,64,125],"330":[28,8,33,99,54,55,60,64,125]},"localhost-34469/assets/_plugin-vue_export-helper-Da88TEg1.js":{"2":[81]},"localhost-34469/assets/chunk-CC9Q-vWm.js":{"8":[81],"9":[81],"10":[81],"19":[81],"23":[81],"29":[81],"33":[81],"34":[81]},"packages/frontend/editor-ui/src/features/ai/instanceAi/components/InstanceAiThreadList.vue":{"28":[81],"54":[81],"72":[81],"78":[81],"95":[81],"98":[81],"104":[81],"107":[81],"119":[81],"126":[81],"136":[81],"143":[81],"158":[81],"177":[81],"178":[81],"180":[81],"183":[81],"192":[81],"195":[81],"197":[81],"207":[81],"208":[81],"215":[81],"216":[81],"218":[81],"232":[81]},"localhost-36377/assets/_plugin-vue_export-helper-Da88TEg1.js":{"2":[45,58,123,128,129]},"localhost-36377/assets/chunk-CC9Q-vWm.js":{"8":[45,58,123,128,129],"9":[45,58,123,128,129],"10":[45,58,123,128,129],"19":[45,58,123,128,129],"23":[45,58,123,128,129],"29":[45,58,123,128,129],"33":[45,58,123,128,129],"34":[45,58,123,128,129]},"packages/frontend/editor-ui/src/features/shared/nodeCreator/components/ItemTypes/OpenTemplateItem.vue":{"14":[45,52],"23":[45,52],"27":[52]},"packages/nodes-base/nodes/Kafka/Kafka.node.ts":{"222":[49],"230":[49],"270":[49],"298":[49]},"packages/nodes-base/nodes/Kafka/KafkaTrigger.node.ts":{"389":[49],"417":[49],"426":[49],"450":[49],"487":[49],"495":[49],"519":[49]},"packages/nodes-base/nodes/Kafka/utils.ts":{"96":[49],"120":[49],"155":[49],"158":[49],"191":[49],"242":[49],"248":[49],"284":[49],"314":[49],"338":[49],"343":[49],"346":[49],"349":[49],"354":[49],"358":[49],"361":[49],"364":[49],"367":[49],"370":[49],"399":[49],"402":[49],"417":[49],"437":[49],"441":[49],"485":[49],"540":[49],"567":[49],"588":[49],"597":[49],"622":[49],"628":[49],"629":[49],"665":[49],"687":[49],"695":[49]},"localhost-43467/assets/_plugin-vue_export-helper-Da88TEg1.js":{"2":[49]},"localhost-43467/assets/chunk-CC9Q-vWm.js":{"8":[49],"9":[49],"10":[49],"19":[49],"23":[49],"29":[49],"33":[49],"34":[49]},"packages/nodes-base/nodes/Google/Gmail/GenericFunctions.ts":{"30":[16,8,70,150],"146":[16,8,70,150],"209":[16,8,70,150],"233":[16,8,70,150],"255":[16,8,70,150],"278":[16,8,70,150],"286":[16,8,70,150],"340":[16,8,70,150],"397":[16,8,70,150],"405":[16,8,70,150],"425":[16,8,70,150],"465":[16,8,70,150],"498":[16,8,70,150],"499":[16,8,70,150],"509":[16,8,70,150],"514":[16,8,70,150],"518":[16,8,70,150],"520":[16,8,70,150],"527":[16,8,70,150],"539":[16,8,70,150],"556":[16,8,70,150]},"packages/nodes-base/nodes/Google/Gmail/Gmail.node.ts":{"8":[16,8,70]},"packages/nodes-base/nodes/Google/Gmail/utils/descriptions.ts":{"3":[16,8,70,150]},"packages/nodes-base/nodes/Google/Gmail/utils/replyToEmail.ts":{"16":[16,8,70],"71":[16,8,70],"76":[16,8,70],"95":[16,8,70],"98":[16,8,70],"107":[16,8,70]},"packages/nodes-base/nodes/Google/Gmail/v1/GmailV1.node.ts":{"134":[16,8,70],"147":[16,8,70],"260":[16,8,70],"267":[16,8,70],"275":[16,8,70],"347":[16,8,70],"354":[16,8,70],"362":[16,8,70],"417":[16,8,70],"421":[16,8,70],"587":[16,8,70],"595":[16,8,70],"603":[16,8,70]},"packages/nodes-base/nodes/Google/Gmail/v2/GmailV2.node.ts":{"157":[16,8,70],"174":[16,8,70]},"packages/nodes-base/nodes/Google/Gmail/v2/loadOptions.ts":{"5":[16,8,70],"29":[16,8,70]},"packages/nodes-base/nodes/Google/Gmail/v2/utils/draft.ts":{"7":[16,8,70],"14":[16,8,70],"21":[16,8,70],"32":[16,8,70]},"packages/nodes-base/nodes/Transform/Sort/Sort.node.ts":{"164":[146,70],"221":[146,70],"226":[146,70]},"packages/nodes-base/nodes/Transform/Sort/utils.ts":{"8":[146,70]},"packages/@n8n/db/src/repositories/scheduled-task.repository.ts":{"133":[166,168,169,170,59],"152":[166,168,169,170,59],"164":[166,168,169,170,59],"207":[166,168,169,170,59],"224":[166,168,169,170,59],"234":[166,168,169,170,59],"237":[166,168,169,170,59],"238":[166,168,169,170,59],"258":[166,168,169,170,59],"268":[166,168,169,170,59],"287":[166,168,169,170,59],"301":[166,168,169,170,59],"306":[166,168,169,170,59],"330":[166,168,169,170,59],"335":[166,168,169,170,59],"353":[166,168,169,170,59],"360":[166,168,169,170,59],"362":[166,168,169,170,59],"387":[166,168,169,170,59],"399":[166,168,169,170,59],"400":[166,168,169,170,59],"414":[166,168,169,170,59],"416":[166,168,169,170,59],"421":[166,168,169,170,59],"424":[166,168,169,170,59],"432":[166,168,169,170,59],"435":[166,168,169,170,59],"436":[166,168,169,170,59],"447":[166,168,169,170,59],"454":[166,168,169,170,59],"455":[166,168,169,170,59],"475":[166,168,169,170,59],"491":[166,168,169,170,59],"515":[166,168,169,170,59],"553":[166,168,169,170,59],"570":[166,168,169,170,59],"575":[166,168,169,170,59],"576":[166,168,169,170,59],"577":[166,168,169,170,59],"599":[166,168,169,170,59],"604":[166,168,169,170,59],"605":[166,168,169,170,59],"620":[166,168,169,170,59],"625":[166,168,169,170,59],"637":[166,168,169,170,59],"670":[166,168,169,170,59],"706":[166,168,169,170,59],"707":[166,168,169,170,59],"728":[166,168,169,170,59],"743":[166,168,169,170,59],"770":[166,168,169,170,59],"782":[166,168,169,170,59],"800":[166,168,169,170,59],"805":[166,168,169,170,59]},"packages/@n8n/db/src/repositories/workflow-publication-outbox.repository.ts":{"16":[23,166,168,169,170,59],"27":[23,166,168,169,170,59],"32":[23,166,168,169,170,59],"48":[23,166,168,169,170,59],"61":[23,166,168,169,170,59],"79":[23,166,168,169,170,59],"107":[23,166,168,169,170,59],"116":[23,166,168,169,170,59],"140":[23,166,168,169,170,59],"173":[23,166,168,169,170,59],"184":[23,166,168,169,170,59],"210":[23,166,168,169,170,59],"213":[23,166,168,169,170,59],"243":[23,166,168,169,170,59],"266":[23,166,168,169,170,59],"278":[23,166,168,169,170,59],"286":[23,166,168,169,170,59],"328":[23,166,168,169,170,59],"331":[23,166,168,169,170,59],"349":[23,166,168,169,170,59],"356":[23,166,168,169,170,59],"387":[23,166,168,169,170,59],"400":[23,166,168,169,170,59],"411":[23,166,168,169,170,59],"427":[23,166,168,169,170,59],"443":[23,166,168,169,170,59],"463":[23,166,168,169,170,59],"488":[23,166,168,169,170,59],"497":[23,166,168,169,170,59],"519":[23,166,168,169,170,59],"528":[23,166,168,169,170,59],"540":[23,166,168,169,170,59],"550":[23,166,168,169,170,59],"562":[23,166,168,169,170,59]},"packages/@n8n/db/src/repositories/workflow-publication-trigger-status.repository.ts":{"26":[23,166,168,169,170,59],"31":[23,166,168,169,170,59],"36":[23,166,168,169,170,59],"41":[23,166,168,169,170,59],"47":[23,166,168,169,170,59],"59":[23,166,168,169,170,59],"64":[23,166,168,169,170,59]},"packages/@n8n/db/src/repositories/workflow-published-version.repository.ts":{"31":[23,166,168,169,170,59],"35":[23,166,168,169,170,59],"39":[23,166,168,169,170,59],"43":[23,166,168,169,170,59],"56":[23,166,168,169,170,59],"68":[23,166,168,169,170,59]},"packages/@n8n/db/src/utils/dialect-time.ts":{"13":[59],"22":[59],"38":[59],"54":[59],"74":[59]},"packages/@n8n/scheduler/src/core/executor/executor.ts":{"103":[169,170,59],"135":[169,170,59],"172":[169,170,59],"175":[169,170,59],"186":[169,170,59],"189":[169,170,59],"194":[169,170,59],"207":[169,170,59],"208":[169,170,59],"212":[169,170,59],"255":[169,170,59],"257":[169,170,59],"258":[169,170,59],"321":[169,170,59],"337":[169,170,59],"344":[169,170,59],"353":[169,170,59]},"packages/@n8n/scheduler/src/core/executor/precision-timer.ts":{"14":[169,170,59],"15":[169,170,59],"16":[169,170,59],"35":[169,170,59],"38":[169,170,59],"43":[169,170,59],"50":[169,170,59],"58":[169,170,59],"66":[169,170,59]},"packages/@n8n/scheduler/src/core/executor/task-handler.ts":{"50":[169,170,59],"52":[169,170,59],"56":[169,170,59],"88":[169,170,59],"92":[169,170,59],"99":[169,170,59],"104":[169,170,59]},"packages/@n8n/scheduler/src/core/factory.ts":{"113":[168,169,170,59],"131":[168,169,170,59],"188":[168,169,170,59],"198":[168,169,170,59],"231":[168,169,170,59],"246":[168,169,170,59],"253":[168,169,170,59],"259":[168,169,170,59],"265":[168,169,170,59],"271":[168,169,170,59],"272":[168,169,170,59],"283":[168,169,170,59],"284":[168,169,170,59],"289":[168,169,170,59],"314":[168,169,170,59],"320":[168,169,170,59],"326":[168,169,170,59],"337":[168,169,170,59],"364":[168,169,170,59],"380":[168,169,170,59],"381":[168,169,170,59],"387":[168,169,170,59],"392":[168,169,170,59],"402":[168,169,170,59],"407":[168,169,170,59],"412":[168,169,170,59],"422":[168,169,170,59],"427":[168,169,170,59],"437":[168,169,170,59],"446":[168,169,170,59],"449":[168,169,170,59],"455":[168,169,170,59],"471":[168,169,170,59],"477":[168,169,170,59],"483":[168,169,170,59],"520":[168,169,170,59],"545":[168,169,170,59],"559":[168,169,170,59],"572":[168,169,170,59],"575":[168,169,170,59],"578":[168,169,170,59]},"packages/@n8n/scheduler/src/core/index.ts":{"18":[166,168,169,170,59],"62":[166,168,169,170,59],"67":[166,168,169,170,59],"69":[166,168,169,170,59]},"packages/@n8n/scheduler/src/core/materializer/materialize.ts":{"97":[166,168,169,170,59],"104":[166,168,169,170,59],"148":[166,168,169,170,59],"149":[166,168,169,170,59],"153":[166,168,169,170,59],"154":[166,168,169,170,59],"155":[166,168,169,170,59],"173":[166,168,169,170,59],"174":[166,168,169,170,59],"179":[166,168,169,170,59],"187":[166,168,169,170,59],"189":[166,168,169,170,59],"190":[166,168,169,170,59],"194":[166,168,169,170,59]},"packages/@n8n/scheduler/src/core/materializer/misfire.ts":{"29":[166,168,169,170,59],"37":[166,168,169,170,59],"63":[166,168,169,170,59],"65":[166,168,169,170,59],"66":[166,168,169,170,59]},"packages/@n8n/scheduler/src/core/materializer/plan.ts":{"44":[166,168,169,170,59]},"packages/@n8n/scheduler/src/core/provisioning/index.ts":{"1":[166,168,169,170,59]},"packages/@n8n/scheduler/src/core/provisioning/schedule-identity.ts":{"14":[166,168,169,170,59],"49":[166,168,169,170,59],"68":[166,168,169,170,59]},"packages/@n8n/scheduler/src/core/recurrence/field.ts":{"14":[166,168,169,170,59]},"packages/@n8n/scheduler/src/core/recurrence/kinds/cron.ts":{"25":[166,168,169,170,59],"62":[166,168,169,170,59],"82":[166,168,169,170,59],"103":[166,168,169,170,59],"112":[166,168,169,170,59],"127":[166,168,169,170,59]},"packages/@n8n/scheduler/src/core/recurrence/next-run.ts":{"33":[166,168,169,170,59],"68":[166,168,169,170,59],"92":[166,168,169,170,59]},"packages/@n8n/scheduler/src/core/recurrence/resolve.ts":{"21":[166,168,169,170,59]},"packages/@n8n/scheduler/src/core/recurrence/validate.ts":{"14":[166,168,169,170,59]},"packages/@n8n/scheduler/src/observability/attributes.ts":{"74":[169,170,59]},"packages/@n8n/scheduler/src/observability/executor-tracing.ts":{"21":[169,170,59],"23":[169,170,59],"35":[169,170,59],"66":[169,170,59],"70":[169,170,59],"81":[169,170,59],"94":[169,170,59]},"packages/cli/src/metrics/prometheus/scheduler-metrics.service.ts":{"44":[168,169,170,59],"51":[168,169,170,59],"55":[168,169,170,59],"61":[168,169,170,59],"141":[168,169,170,59],"153":[168,169,170,59],"159":[168,169,170,59],"168":[168,169,170,59],"177":[168,169,170,59],"186":[168,169,170,59],"201":[168,169,170,59],"207":[168,169,170,59],"213":[168,169,170,59],"219":[168,169,170,59],"225":[168,169,170,59],"232":[168,169,170,59],"243":[168,169,170,59],"249":[168,169,170,59],"259":[168,169,170,59],"265":[168,169,170,59]},"packages/cli/src/scheduling/schedule-trigger-node/schedule-trigger-task-handler.ts":{"36":[169,170,59],"49":[169,170,59],"117":[169,170,59],"126":[169,170,59],"131":[169,170,59],"148":[169,170,59]},"packages/cli/src/scheduling/schedule-trigger-node/schedule-trigger-task.ts":{"19":[169,170,59],"25":[169,170,59],"30":[169,170,59],"40":[169,170,59]},"packages/cli/src/workflows/publication/publication-status-reporter.ts":{"30":[23,166,168,169,170,59],"42":[23,166,168,169,170,59],"82":[23,166,168,169,170,59],"110":[23,166,168,169,170,59],"115":[23,166,168,169,170,59],"122":[23,166,168,169,170,59],"125":[23,166,168,169,170,59],"140":[23,166,168,169,170,59],"150":[23,166,168,169,170,59],"154":[23,166,168,169,170,59],"164":[23,166,168,169,170,59],"166":[23,166,168,169,170,59],"173":[23,166,168,169,170,59],"187":[23,166,168,169,170,59],"191":[23,166,168,169,170,59],"196":[23,166,168,169,170,59],"204":[23,166,168,169,170,59],"208":[23,166,168,169,170,59],"221":[23,166,168,169,170,59]},"packages/cli/src/workflows/publication/trigger-diff.ts":{"21":[23,166,168,169,170,59]},"packages/cli/src/workflows/publication/workflow-publication-applier.ts":{"38":[23,166,168,169,170,59],"70":[23,166,168,169,170,59],"95":[23,166,168,169,170,59],"122":[23,166,168,169,170,59],"129":[23,166,168,169,170,59],"190":[23,166,168,169,170,59],"199":[23,166,168,169,170,59],"219":[23,166,168,169,170,59],"229":[23,166,168,169,170,59],"242":[23,166,168,169,170,59],"247":[23,166,168,169,170,59],"248":[23,166,168,169,170,59],"269":[23,166,168,169,170,59],"273":[23,166,168,169,170,59],"286":[23,166,168,169,170,59],"311":[23,166,168,169,170,59]},"packages/cli/src/workflows/publication/workflow-publication-lifecycle-lock.ts":{"27":[23,166,168,169,170,59],"31":[23,166,168,169,170,59],"36":[23,166,168,169,170,59],"41":[23,166,168,169,170,59],"55":[23,166,168,169,170,59],"76":[23,166,168,169,170,59],"85":[23,166,168,169,170,59],"92":[23,166,168,169,170,59],"96":[23,166,168,169,170,59],"108":[23,166,168,169,170,59],"115":[23,166,168,169,170,59],"122":[23,166,168,169,170,59],"129":[23,166,168,169,170,59]},"packages/cli/src/workflows/publication/workflow-publication-notifier.ts":{"13":[23,166,168,169,170,59],"26":[23,166,168,169,170,59],"33":[23,166,168,169,170,59],"46":[23,166,168,169,170,59]},"packages/cli/src/workflows/publication/workflow-publication-outbox-consumer.ts":{"39":[23,166,168,169,170,59],"54":[23,166,168,169,170,59],"62":[23,166,168,169,170,59],"71":[23,166,168,169,170,59],"82":[23,166,168,169,170,59],"96":[23,166,168,169,170,59],"110":[23,166,168,169,170,59],"114":[23,166,168,169,170,59],"125":[23,166,168,169,170,59],"143":[23,166,168,169,170,59],"155":[23,166,168,169,170,59],"163":[23,166,168,169,170,59],"168":[23,166,168,169,170,59],"178":[23,166,168,169,170,59],"179":[23,166,168,169,170,59],"187":[23,166,168,169,170,59],"197":[23,166,168,169,170,59],"214":[23,166,168,169,170,59],"225":[23,166,168,169,170,59],"226":[23,166,168,169,170,59],"291":[23,166,168,169,170,59]},"packages/cli/src/workflows/triggers/non-webhook-trigger-registrar.ts":{"50":[23,166,168,169,170,59],"64":[23,166,168,169,170,59],"65":[23,166,168,169,170,59],"72":[23,166,168,169,170,59],"79":[23,166,168,169,170,59],"122":[23,166,168,169,170,59],"145":[23,166,168,169,170,59],"170":[23,166,168,169,170,59],"180":[23,166,168,169,170,59]},"packages/cli/src/workflows/triggers/trigger-activation-retry.ts":{"10":[23,166,168,169,170,59],"20":[23,166,168,169,170,59]},"packages/cli/src/workflows/triggers/trigger-count.service.ts":{"13":[23,166,168,169,170,59],"14":[23,166,168,169,170,59],"17":[23,166,168,169,170,59],"27":[23,166,168,169,170,59]},"packages/cli/src/workflows/triggers/webhook-trigger-registrar.ts":{"20":[23,166,168,169,170,59],"26":[23,166,168,169,170,59],"47":[23,166,168,169,170,59],"67":[23,166,168,169,170,59],"74":[23,166,168,169,170,59],"86":[23,166,168,169,170,59],"133":[23,166,168,169,170,59],"143":[23,166,168,169,170,59],"160":[23,166,168,169,170,59],"174":[23,166,168,169,170,59],"187":[23,166,168,169,170,59],"194":[23,166,168,169,170,59],"223":[23,166,168,169,170,59],"239":[23,166,168,169,170,59],"243":[23,166,168,169,170,59],"258":[23,166,168,169,170,59]},"packages/cli/src/workflows/triggers/workflow-trigger-activator.ts":{"67":[23,166,168,169,170,59],"95":[23,166,168,169,170,59],"108":[23,166,168,169,170,59],"119":[23,166,168,169,170,59],"130":[23,166,168,169,170,59],"152":[23,166,168,169,170,59],"173":[23,166,168,169,170,59],"177":[23,166,168,169,170,59],"200":[23,166,168,169,170,59],"210":[23,166,168,169,170,59],"224":[23,166,168,169,170,59],"245":[23,166,168,169,170,59],"259":[23,166,168,169,170,59],"317":[23,166,168,169,170,59],"336":[23,166,168,169,170,59],"350":[23,166,168,169,170,59],"363":[23,166,168,169,170,59],"382":[23,166,168,169,170,59],"389":[23,166,168,169,170,59],"414":[23,166,168,169,170,59],"426":[23,166,168,169,170,59],"447":[23,166,168,169,170,59],"453":[23,166,168,169,170,59],"474":[23,166,168,169,170,59],"483":[23,166,168,169,170,59],"498":[23,166,168,169,170,59],"507":[23,166,168,169,170,59],"528":[23,166,168,169,170,59],"551":[23,166,168,169,170,59],"564":[23,166,168,169,170,59],"582":[23,166,168,169,170,59],"589":[23,166,168,169,170,59],"599":[23,166,168,169,170,59],"615":[23,166,168,169,170,59],"629":[23,166,168,169,170,59],"632":[23,166,168,169,170,59],"658":[23,166,168,169,170,59],"665":[23,166,168,169,170,59],"674":[23,166,168,169,170,59],"679":[23,166,168,169,170,59],"682":[23,166,168,169,170,59],"685":[23,166,168,169,170,59],"686":[23,166,168,169,170,59],"690":[23,166,168,169,170,59],"691":[23,166,168,169,170,59],"702":[23,166,168,169,170,59],"738":[23,166,168,169,170,59]},"packages/cli/src/workflows/workflow-published-data.service.ts":{"22":[23,166,168,169,170,59],"26":[23,166,168,169,170,59],"39":[23,166,168,169,170,59],"50":[23,166,168,169,170,59],"64":[23,166,168,169,170,59],"84":[23,166,168,169,170,59],"93":[23,166,168,169,170,59]},"packages/workflow/src/workflow-diff.ts":{"28":[23,166,168,169,170,59],"42":[23,166,168,169,170,59],"54":[23,166,168,169,170,59],"59":[23,166,168,169,170,59],"64":[23,166,168,169,170,59],"94":[23,166,168,169,170,59],"98":[23,166,168,169,170,59],"116":[23,166,168,169,170,59],"128":[23,166,168,169,170,59],"141":[23,166,168,169,170,59],"148":[23,166,168,169,170,59],"150":[23,166,168,169,170,59],"166":[23,166,168,169,170,59],"180":[23,166,168,169,170,59],"184":[23,166,168,169,170,59],"197":[23,166,168,169,170,59],"205":[23,166,168,169,170,59],"213":[23,166,168,169,170,59],"227":[23,166,168,169,170,59],"228":[23,166,168,169,170,59],"238":[23,166,168,169,170,59],"239":[23,166,168,169,170,59],"251":[23,166,168,169,170,59],"253":[23,166,168,169,170,59],"254":[23,166,168,169,170,59],"256":[23,166,168,169,170,59],"273":[23,166,168,169,170,59],"302":[23,166,168,169,170,59],"310":[23,166,168,169,170,59],"314":[23,166,168,169,170,59],"320":[23,166,168,169,170,59],"321":[23,166,168,169,170,59],"324":[23,166,168,169,170,59],"378":[23,166,168,169,170,59],"407":[23,166,168,169,170,59],"408":[23,166,168,169,170,59]},"packages/cli/src/controllers/orchestration.controller.ts":{"9":[123],"20":[123]},"packages/cli/src/scaling/worker-status.service.ee.ts":{"16":[123],"23":[123],"33":[123],"48":[123],"59":[123],"91":[123],"92":[123],"102":[123]},"packages/frontend/editor-ui/src/features/settings/orchestration.ee/components/WorkerList.vue":{"25":[123],"35":[123],"39":[123],"41":[123],"49":[123],"60":[123]},"packages/frontend/editor-ui/src/features/settings/orchestration.ee/orchestration.store.ts":{"26":[52,123],"34":[52],"47":[52],"56":[52,123],"59":[52],"65":[52],"71":[52],"74":[52],"77":[52]},"packages/frontend/editor-ui/src/features/settings/orchestration.ee/views/WorkerView.vue":{"30":[123],"33":[123]},"packages/frontend/editor-ui/src/app/composables/useReportWorkflowFailuresToParent.ts":{"33":[90,91,128,129]},"packages/frontend/editor-ui/src/app/layouts/DemoLayout.vue":{"18":[90,91,128,129],"61":[90,91,128,129],"88":[90,91,128,129]},"packages/frontend/editor-ui/src/app/views/DemoDiffView.vue":{"20":[128],"27":[128],"36":[128],"64":[128]},"packages/frontend/editor-ui/src/features/execution/logs/components/DemoFooter.vue":{"9":[90,91,128,129],"10":[90,91,128,129],"11":[90,91,128,129]},"packages/frontend/editor-ui/src/features/workflows/workflowDiff/HighlightedEdge.vue":{"6":[128]},"packages/frontend/editor-ui/src/features/workflows/workflowDiff/SyncedWorkflowCanvas.vue":{"16":[128],"39":[128],"49":[128],"68":[128],"71":[128],"90":[128],"98":[128],"102":[128],"131":[128],"146":[128],"149":[128],"152":[128]},"packages/frontend/editor-ui/src/features/workflows/workflowDiff/WorkflowDiffContent.vue":{"15":[128],"41":[128],"46":[128],"48":[128],"59":[128],"70":[128],"75":[128],"87":[128],"102":[128],"113":[128],"118":[128],"130":[128]},"packages/frontend/editor-ui/src/features/workflows/workflowDiff/WorkflowDiffView.vue":{"64":[128],"65":[128],"84":[128],"85":[128],"102":[128],"148":[128],"169":[128],"174":[128],"176":[128],"183":[128]},"packages/frontend/editor-ui/src/features/workflows/workflowDiff/useViewportSync.ts":{"30":[128],"47":[128],"53":[128],"72":[128]},"packages/frontend/editor-ui/src/features/workflows/workflowDiff/useWorkflowDiff.ts":{"37":[128],"38":[128],"48":[128],"49":[128],"53":[128],"58":[128],"77":[128],"89":[128],"100":[128],"106":[128],"123":[128],"136":[128],"152":[128],"168":[128],"173":[128],"183":[128],"226":[128],"248":[128],"276":[128]},"packages/frontend/editor-ui/src/features/workflows/workflowDiff/useWorkflowDiffUI.ts":{"25":[128],"40":[128],"94":[128],"97":[128],"98":[128],"130":[128],"167":[128],"182":[128],"221":[128],"225":[128]},"packages/@n8n/permissions/src/schemas.ee.ts":{"23":[72],"50":[72],"73":[72]},"localhost-41363/assets/_plugin-vue_export-helper-Da88TEg1.js":{"2":[72]},"localhost-41363/assets/chunk-CC9Q-vWm.js":{"8":[72],"9":[72],"10":[72],"19":[72],"23":[72],"29":[72],"33":[72],"34":[72]},"packages/frontend/@n8n/design-system/src/components/N8nHoverCard/HoverCard.vue":{"61":[72],"89":[72],"91":[72],"105":[72],"115":[72],"124":[72],"128":[72],"133":[72]},"packages/frontend/@n8n/design-system/src/components/N8nUserStack/UserStack.vue":{"11":[72],"44":[72],"54":[72],"60":[72],"67":[72],"72":[72],"75":[72]},"packages/cli/src/metrics/prometheus/token-exchange-metrics.service.ts":{"22":[176],"27":[176],"31":[176],"72":[176],"76":[176],"81":[176],"85":[176],"90":[176],"94":[176]},"packages/cli/src/modules/token-exchange/controllers/embed-auth.controller.ts":{"22":[176],"37":[176],"55":[176],"66":[176]},"packages/cli/src/modules/token-exchange/controllers/token-exchange.controller.ts":{"22":[176],"43":[176]},"packages/cli/src/modules/token-exchange/database/repositories/token-exchange-jti.repository.ts":{"14":[176],"18":[176],"31":[176],"32":[176],"79":[176],"80":[176]},"packages/cli/src/modules/token-exchange/database/repositories/trusted-key.repository.ts":{"8":[176],"12":[176],"16":[176]},"packages/cli/src/modules/token-exchange/services/identity-resolution.service.ts":{"33":[176],"37":[176],"45":[176],"58":[176],"73":[176],"97":[176],"159":[176],"177":[176],"210":[176],"224":[176],"267":[176],"321":[176],"367":[176]},"packages/cli/src/modules/token-exchange/services/jti-store.service.ts":{"25":[176],"34":[176]},"packages/cli/src/modules/token-exchange/services/scoped-jwt.strategy.ts":{"17":[176],"22":[176],"67":[176],"74":[176],"97":[176],"104":[176]},"packages/cli/src/modules/token-exchange/services/token-exchange.service.ts":{"34":[176],"56":[176],"143":[176],"157":[176]},"packages/cli/src/modules/token-exchange/services/trusted-key.service.ts":{"79":[176],"104":[176],"117":[176],"125":[176],"129":[176],"137":[176],"143":[176],"157":[176],"208":[176],"216":[176],"220":[176],"226":[176],"252":[176],"256":[176],"281":[176],"290":[176],"291":[176],"293":[176],"294":[176],"339":[176],"354":[176],"371":[176],"397":[176],"399":[176],"418":[176],"470":[176],"489":[176],"508":[176],"524":[176],"539":[176],"548":[176],"588":[176],"634":[176]},"packages/cli/src/modules/token-exchange/token-exchange.errors.ts":{"11":[176],"24":[176]},"packages/cli/src/modules/token-exchange/token-exchange.schemas.ts":{"43":[176]},"packages/cli/src/public-api/v1/shared/services/pagination.service.ts":{"10":[176,95],"14":[176,95],"26":[176,95],"38":[176,95],"47":[176,95]},"packages/cli/src/utils/validate-redirect-url.ts":{"5":[176]},"packages/nodes-base/nodes/Linear/GenericFunctions.ts":{"16":[26],"66":[26],"70":[26],"100":[26],"126":[26]},"packages/nodes-base/nodes/Linear/Linear.node.ts":{"109":[26],"119":[26],"136":[26],"154":[26],"172":[26],"220":[26]},"packages/nodes-base/nodes/Linear/Queries.ts":{"2":[26],"15":[26],"28":[26],"41":[26],"95":[26],"102":[26],"138":[26],"147":[26],"189":[26],"245":[26],"255":[26]},"localhost-45907/assets/_plugin-vue_export-helper-Da88TEg1.js":{"2":[26,102,113,64,13]},"localhost-45907/assets/chunk-CC9Q-vWm.js":{"8":[26,102,113,64,13],"9":[26,102,113,64,13],"10":[26,102,113,64,13],"19":[26,102,113,64,13],"23":[26,102,113,64,13],"29":[26,102,113,64,13],"33":[26,102,113,64,13],"34":[26,102,113,64,13]},"localhost-45907/assets/verified-Ge9vhh2v.js":{"8":[26,113,13]},"localhost-33191/assets/_plugin-vue_export-helper-Da88TEg1.js":{"2":[82]},"localhost-33191/assets/chunk-CC9Q-vWm.js":{"8":[82],"9":[82],"10":[82],"19":[82],"23":[82],"29":[82],"33":[82],"34":[82]},"packages/community-packages.controller.ts":{"9":[102],"13":[102],"21":[102],"27":[102],"35":[102]},"packages/community-packages.lifecycle.service.ts":{"35":[102],"37":[102],"49":[102],"60":[102],"66":[102],"110":[102],"209":[102],"225":[102],"233":[102],"251":[102],"286":[102],"296":[102],"311":[102],"318":[102],"337":[102],"374":[102],"388":[102]},"packages/frontend/editor-ui/src/features/settings/communityNodes/communityNodes.utils.ts":{"10":[102]},"packages/frontend/editor-ui/src/features/settings/communityNodes/components/nodeCreator/CommunityNodeDetails.vue":{"19":[102,52],"28":[52],"30":[52],"39":[102,52],"41":[52],"43":[52],"74":[52],"79":[52],"97":[102,52],"115":[52],"118":[52]},"packages/frontend/editor-ui/src/features/settings/communityNodes/components/nodeCreator/CommunityNodeDocsLink.vue":{"9":[102,52],"11":[52],"14":[102,52],"23":[102,52]},"packages/frontend/editor-ui/src/features/settings/communityNodes/components/nodeCreator/CommunityNodeFooter.vue":{"15":[102],"32":[102],"52":[102],"57":[102],"72":[102],"73":[102],"79":[102],"80":[102]},"packages/frontend/editor-ui/src/features/settings/communityNodes/components/nodeCreator/CommunityNodeInfo.vue":{"17":[102,52],"29":[102,52],"33":[102,52],"41":[102,52],"43":[52],"48":[102,52],"65":[52],"101":[52],"109":[102,52],"114":[102,52],"119":[102,52],"130":[52],"132":[52],"138":[52],"144":[52],"145":[102,52],"148":[102,52],"156":[52],"163":[52],"168":[52],"169":[52],"172":[52]},"packages/frontend/editor-ui/src/features/shared/nodeCreator/components/ItemTypes/CommunityNodeItem.vue":{"11":[102,52]},"packages/@n8n/api-types/src/dto/folders/list-folder-query.dto.ts":{"46":[53,54,55],"74":[53,54,55],"75":[53,54,55],"83":[53,54,55],"84":[53,54,55],"93":[53,54,55],"102":[53,54,55]},"packages/cli/src/controllers/folder.controller.ts":{"36":[52,107,53,54,55],"43":[52,107,53,54,55],"61":[52,107,53,54,55],"82":[52,107,53,54,55],"102":[52,107,53,54,55],"126":[52,107,53,54,55],"148":[52,107,53,54,55],"170":[52,107,53,54,55],"185":[52,107,53,54,55],"207":[52,107,53,54,55]},"packages/cli/src/services/folder.service.ts":{"32":[52,107,53,54,55,122],"40":[52,107,53,54,55,122],"58":[52,107,53,54,55,122],"66":[52,107,53,54,55,122],"105":[52,107,53,54,55,122],"113":[52,107,53,54,55,122],"142":[52,107,53,54,55,122],"161":[52,107,53,54,55,122],"174":[52,107,53,54,55,122],"197":[52,107,53,54,55,122],"205":[52,107,53,54,55,122],"213":[52,107,53,54,55,122],"221":[52,107,53,54,55,122],"232":[52,107,53,54,55,122],"246":[52,107,53,54,55,122],"247":[52,107,53,54,55,122],"255":[52,107,53,54,55,122],"280":[52,107,53,54,55,122],"293":[52,107,53,54,55,122],"319":[52,107,53,54,55,122],"329":[52,107,53,54,55,122],"332":[52,107,53,54,55,122],"337":[52,107,53,54,55,122]},"localhost-40739/assets/_plugin-vue_export-helper-Da88TEg1.js":{"2":[54]},"localhost-40739/assets/chunk-CC9Q-vWm.js":{"8":[54],"9":[54],"10":[54],"19":[54],"23":[54],"29":[54],"33":[54],"34":[54]},"packages/frontend/editor-ui/src/features/collaboration/projects/components/ProjectMoveResourceModal.vue":{"42":[54],"68":[54],"75":[54],"92":[54],"96":[54],"99":[54],"100":[54],"102":[54],"108":[54],"111":[54],"121":[54],"124":[54],"129":[54],"133":[54],"182":[54],"198":[54],"201":[54],"203":[54],"211":[54],"215":[54],"216":[54],"222":[54],"223":[54],"231":[54],"249":[54],"254":[54],"346":[54],"349":[54],"357":[54]},"packages/frontend/editor-ui/src/features/collaboration/projects/components/ProjectMoveSuccessToastMessage.vue":{"19":[54],"20":[54],"21":[54],"24":[54]},"packages/frontend/editor-ui/src/features/collaboration/projects/composables/useMoveResourceToProjectToast.ts":{"26":[54]},"packages/frontend/editor-ui/src/features/core/folders/components/MoveToFolderDropdown.vue":{"40":[53,54,55],"41":[53,54,55],"42":[53,54,55],"54":[53,54,55],"64":[53],"94":[53,54,55],"95":[53,54,55],"105":[53,54,55],"107":[53],"109":[53,54,55],"128":[53,54,55],"130":[53,54,55],"132":[53,54,55],"137":[53,54,55],"141":[53,54,55],"148":[53],"151":[53]},"packages/frontend/editor-ui/src/features/core/folders/components/MoveToFolderModal.vue":{"65":[53,54,55],"81":[53,54,55],"84":[53,54,55],"90":[53,54,55],"100":[53,54,55],"101":[54,55],"105":[55],"108":[53,54,55],"109":[55],"127":[53,54,55],"130":[53,54,55],"134":[53,54,55],"143":[53,54,55],"153":[53,54,55],"167":[53,54,55],"168":[54,55],"174":[53,54,55],"175":[53,54,55],"182":[53,54,55],"186":[54],"192":[54,55],"200":[53,54,55],"354":[53],"379":[53,54,55],"381":[53,54,55],"386":[53,54,55],"390":[53,54,55],"417":[53,54,55],"427":[53,54,55],"436":[53,54,55],"438":[53,54,55],"452":[54,55],"457":[54,55],"478":[53,54,55],"497":[55],"506":[55],"532":[55],"556":[53,54,55]},"packages/frontend/editor-ui/src/features/core/folders/folders.api.ts":{"11":[107,53],"23":[52,107,53],"35":[53],"46":[53],"57":[53,54,55],"87":[53],"100":[52,53],"111":[53]},"packages/frontend/editor-ui/src/features/credentials/credentials.ee.api.ts":{"6":[64,125],"19":[54]},"packages/core/src/execution-engine/node-execution-context/poll-context.ts":{"19":[166,167,168],"23":[166,167,168],"30":[166,167,168],"50":[166,167,168],"54":[166,167,168]},"packages/core/src/execution-engine/poll-trigger-executor.ts":{"19":[166,167,168],"36":[166,167,168],"42":[166,167,168],"59":[166,167,168]},"packages/nodes-base/nodes/E2eTest/E2eTestPollingTrigger.node.ts":{"42":[166,167,168]},"packages/workflow/src/cron.ts":{"52":[166,167,168],"80":[166,167,168]},"packages/cli/src/modules/log-streaming.ee/destinations/message-event-bus-destination-webhook.ee.ts":{"31":[61,63],"81":[61,63],"116":[61,63],"125":[61,63],"130":[61,63],"142":[61,63],"170":[61,63],"171":[61,63],"188":[61,63],"206":[61,63],"265":[61,63],"299":[61,63],"315":[61,63]},"localhost-44763/assets/_plugin-vue_export-helper-Da88TEg1.js":{"2":[61]},"localhost-44763/assets/chunk-CC9Q-vWm.js":{"8":[61],"9":[61],"10":[61],"19":[61],"23":[61],"29":[61],"33":[61],"34":[61]},"packages/nodes-base/nodes/Notion/NotionTrigger.node.ts":{"30":[13],"38":[13],"49":[13],"268":[13],"341":[13],"350":[13],"353":[13]},"packages/frontend/@n8n/design-system/src/components/N8nIcon/nodes/merge.svg":{"11":[89,130,68,13]},"localhost-38519/assets/_plugin-vue_export-helper-Da88TEg1.js":{"2":[19,88,104,106,53,142,154]},"localhost-38519/assets/chunk-CC9Q-vWm.js":{"8":[19,88,104,106,53,142,154],"9":[19,88,104,106,53,142,154],"10":[19,88,104,106,53,142,154],"19":[19,88,104,106,53,142,154],"23":[19,88,104,106,53,142,154],"29":[19,88,104,106,53,142,154],"33":[19,88,104,106,53,142,154],"34":[19,88,104,106,53,142,154]},"packages/@n8n/api-types/src/dto/binary-data/binary-data-query.dto.ts":{"8":[21],"12":[21]},"packages/cli/src/controllers/binary-data.controller.ts":{"16":[21],"19":[21],"36":[21],"50":[21],"74":[21]},"packages/@n8n/db/src/repositories/evaluation-config.repository.ts":{"9":[88],"13":[88],"17":[88],"21":[88],"36":[88],"47":[88],"52":[88],"56":[88]},"packages/cli/src/evaluation.ee/evaluation-config.controller.ts":{"17":[88],"23":[88],"36":[88],"42":[88],"50":[88],"61":[88],"78":[88],"85":[88],"100":[88]},"packages/cli/src/evaluation.ee/evaluation-config.service.ts":{"13":[88],"19":[88],"23":[88],"27":[88],"55":[88],"71":[88],"75":[88],"79":[88]},"packages/@n8n/api-types/src/schemas/eval-collections.schema.ts":{"74":[88],"107":[88],"123":[88],"129":[88]},"packages/frontend/editor-ui/src/features/ai/evaluation.ee/components/Compare/AiInsightsCard.vue":{"9":[88],"24":[88],"25":[88],"30":[88],"36":[88],"40":[88],"48":[88],"50":[88],"64":[88],"71":[88],"77":[88],"81":[88],"92":[88],"102":[88],"124":[88],"137":[88],"144":[88],"157":[88]},"packages/frontend/editor-ui/src/features/ai/evaluation.ee/components/Compare/CasesTable.vue":{"27":[88],"44":[88],"49":[88],"56":[88],"57":[88],"62":[88],"65":[88],"70":[88],"73":[88],"81":[88],"87":[88],"91":[88],"92":[88],"97":[88],"116":[88],"144":[88],"149":[88],"162":[88],"166":[88],"173":[88],"184":[88],"188":[88]},"packages/frontend/editor-ui/src/features/ai/evaluation.ee/components/Compare/CompareHeader.vue":{"27":[88],"28":[88],"34":[88],"51":[88],"63":[88],"64":[88],"72":[88],"95":[88],"104":[88],"124":[88],"127":[88]},"packages/frontend/editor-ui/src/features/ai/evaluation.ee/components/Compare/CompareTabs.vue":{"16":[88],"36":[88],"43":[88],"46":[88],"49":[88],"52":[88],"68":[88]},"packages/frontend/editor-ui/src/features/ai/evaluation.ee/components/Compare/DatasetMismatchBanner.vue":{"8":[88],"14":[88],"15":[88],"19":[88]},"packages/frontend/editor-ui/src/features/ai/evaluation.ee/components/Compare/MetricCriteria.vue":{"19":[88],"21":[88],"44":[88],"49":[88]},"packages/frontend/editor-ui/src/features/ai/evaluation.ee/components/Compare/OutputsTab.vue":{"20":[88],"47":[88],"48":[88],"51":[88],"56":[88],"65":[88],"75":[88],"82":[88],"84":[88],"92":[88],"99":[88],"107":[88],"125":[88],"134":[88]},"packages/frontend/editor-ui/src/features/ai/evaluation.ee/components/Compare/ScoreChart.vue":{"21":[88],"25":[88],"36":[88],"37":[88],"43":[88],"50":[88],"57":[88],"63":[88]},"packages/frontend/editor-ui/src/features/ai/evaluation.ee/components/shared/GroupedMetricChart.vue":{"48":[88],"54":[88],"59":[88],"81":[88],"91":[88],"125":[88],"126":[88]},"packages/frontend/editor-ui/src/features/ai/evaluation.ee/components/shared/VersionAvatar.vue":{"10":[88],"22":[88],"24":[88],"25":[88]},"packages/frontend/editor-ui/src/features/ai/evaluation.ee/components/shared/versionPalette.ts":{"15":[88],"21":[88]},"packages/frontend/editor-ui/src/features/ai/evaluation.ee/composables/useCompareCases.ts":{"42":[88],"45":[88],"46":[88],"56":[88],"79":[88],"103":[88],"114":[88],"124":[88],"127":[88],"133":[88],"136":[88],"142":[88],"148":[88],"149":[88],"154":[88],"158":[88],"162":[88],"164":[88],"168":[88],"172":[88],"173":[88],"191":[88],"197":[88],"202":[88],"213":[88],"214":[88],"215":[88],"227":[88],"239":[88],"241":[88]},"packages/frontend/editor-ui/src/features/ai/evaluation.ee/composables/useCompareData.ts":{"47":[88],"50":[88],"52":[88],"67":[88],"68":[88],"78":[88],"79":[88],"82":[88]},"packages/frontend/editor-ui/src/features/ai/evaluation.ee/composables/useEvalCollectionsFlag.ts":{"24":[88]},"packages/frontend/editor-ui/src/features/ai/evaluation.ee/evalCollections.api.ts":{"16":[88],"34":[88],"141":[88]},"packages/frontend/editor-ui/src/features/ai/evaluation.ee/evalCollections.store.ts":{"24":[88],"27":[88],"28":[88],"30":[88],"49":[88],"64":[88],"66":[88],"85":[88],"261":[88],"339":[88]},"packages/frontend/editor-ui/src/features/ai/evaluation.ee/evaluation.api.ts":{"70":[88,127],"74":[88,127],"143":[88],"147":[88],"178":[88]},"packages/frontend/editor-ui/src/features/ai/evaluation.ee/evaluation.utils.ts":{"21":[88],"90":[88],"94":[88],"108":[88],"115":[88],"118":[88],"130":[88],"133":[88],"136":[88],"155":[88],"165":[88],"178":[88],"180":[88],"181":[88],"187":[88],"189":[88],"203":[88],"230":[88],"244":[88],"251":[88],"282":[88],"304":[88],"336":[88],"388":[88]},"packages/frontend/editor-ui/src/features/ai/evaluation.ee/views/CompareCollectionView.vue":{"38":[88],"42":[88],"58":[88],"69":[88],"70":[88],"86":[88],"89":[88],"92":[88],"98":[88],"112":[88],"137":[88],"151":[88],"187":[88]},"packages/frontend/editor-ui/src/features/ai/evaluation.ee/views/EvaluationsRootView.vue":{"44":[88,127],"46":[88],"47":[88],"52":[88],"56":[88],"91":[88,127],"101":[88,127],"118":[88,127]},"localhost-46567/assets/_plugin-vue_export-helper-Da88TEg1.js":{"2":[5,96,97]},"localhost-46567/assets/chunk-CC9Q-vWm.js":{"8":[5,96,97],"9":[5,96,97],"10":[5,96,97],"19":[5,96,97],"23":[5,96,97],"29":[5,96,97],"33":[5,96,97],"34":[5,96,97]},"packages/frontend/@n8n/design-system/src/components/N8nDropdownMenu/DropdownMenu.utils.ts":{"11":[4,5,96],"35":[4,5,96]},"packages/frontend/@n8n/design-system/src/components/N8nDropdownMenu/DropdownMenuSearch.vue":{"18":[4,5,96],"43":[4,5,96],"48":[4,5,96]},"packages/frontend/@n8n/design-system/src/components/N8nDropdownMenu/DropdownMenuSearchableContent.vue":{"19":[4,5,96],"61":[4,5,96],"97":[4,5,96],"124":[4,5,96],"133":[4,5,96],"138":[4,5,96],"177":[4,5,96],"183":[4,5,96],"188":[4,5,96],"190":[4,5,96],"238":[4,5,96],"239":[4,5,96],"242":[4,5,96],"253":[4,5,96],"261":[4,5,96]},"packages/frontend/editor-ui/src/features/ai/chatHub/components/ChatProviderAvatar.vue":{"13":[4,5,96],"23":[4,5,96]},"packages/frontend/editor-ui/src/features/ai/components/CredentialSelectorModal.vue":{"36":[5],"37":[5],"46":[5],"108":[5],"116":[5],"120":[5],"123":[5],"147":[5],"150":[5]},"packages/frontend/editor-ui/src/features/credentials/components/CredentialPicker/CredentialPicker.vue":{"52":[5,97,83,73],"65":[5,97,83,73],"66":[5,97,73],"74":[5,97,83,73],"103":[5,97,83,73],"108":[5,97,83,73],"114":[97],"117":[73],"134":[97],"181":[73],"210":[5,97,73],"220":[5,97,83,73],"221":[5,97,83,73],"249":[5,97,73],"269":[5]},"packages/frontend/editor-ui/src/features/credentials/components/CredentialPicker/CredentialsDropdown.vue":{"48":[5,97,73],"57":[5,97],"61":[97],"66":[97],"103":[5,97,73],"108":[5,97,73],"109":[5,97,73],"114":[5,97,73]},"packages/frontend/editor-ui/src/features/ai/chatHub/ChatPersonalAgentsView.vue":{"38":[96],"39":[96],"40":[96],"42":[96],"51":[96],"66":[96],"78":[96],"79":[96],"87":[96],"110":[96],"129":[96],"133":[96],"140":[96],"145":[96],"158":[96],"159":[96]},"packages/frontend/editor-ui/src/features/ai/chatHub/components/AgentEditorModal.vue":{"116":[96],"119":[96],"125":[96],"126":[96],"127":[96],"132":[96],"138":[96],"145":[96],"152":[96],"156":[96],"162":[96],"172":[96],"195":[96],"197":[96],"210":[96],"238":[96],"243":[96],"426":[96],"435":[96],"449":[96],"471":[96],"485":[96],"520":[96],"540":[96],"555":[96],"571":[96],"581":[96],"600":[96],"687":[96],"690":[96]},"packages/frontend/editor-ui/src/features/ai/chatHub/components/SuggestedPromptsEditor.vue":{"9":[96],"51":[96],"92":[96]},"packages/frontend/@n8n/design-system/src/components/N8nPagination/Pagination.vue":{"10":[97]},"packages/frontend/@n8n/design-system/src/components/N8nSwitch/Switch.vue":{"10":[97],"14":[97],"21":[97],"25":[97],"33":[97],"43":[97]},"packages/frontend/editor-ui/src/features/ai/chatHub/SettingsChatHubView.vue":{"26":[97],"41":[97],"43":[97],"60":[97],"68":[97],"76":[97],"94":[97],"115":[97],"126":[97],"133":[97],"141":[97]},"packages/frontend/editor-ui/src/features/ai/chatHub/components/ChatProvidersTable.vue":{"25":[97],"47":[97],"56":[97],"65":[97],"75":[97],"81":[97],"89":[97],"93":[97],"116":[97],"123":[97],"136":[97],"140":[97],"163":[97],"172":[97],"176":[97],"190":[97],"193":[97],"199":[97],"204":[97]},"packages/frontend/editor-ui/src/features/ai/chatHub/components/ProviderSettingsModal.vue":{"123":[97],"127":[97],"141":[97],"156":[97],"164":[97],"184":[97],"217":[97],"227":[97],"229":[97],"240":[97],"243":[97],"250":[97],"254":[97],"265":[97],"279":[97],"308":[97],"319":[97],"372":[97],"375":[97],"398":[97],"401":[97]},"packages/@n8n/instance-ai/src/parsers/docx-parser.ts":{"17":[78]},"packages/@n8n/instance-ai/src/parsers/html-parser.ts":{"35":[78]},"packages/@n8n/instance-ai/src/parsers/ooxml-guard.ts":{"24":[78],"35":[78],"76":[78]},"packages/@n8n/instance-ai/src/parsers/pdf-parser.ts":{"19":[78]},"packages/@n8n/instance-ai/src/parsers/structured-file-parser.ts":{"18":[78],"31":[78],"35":[78],"66":[78],"150":[78],"155":[78],"177":[78],"179":[78],"216":[78],"217":[78],"220":[78],"221":[78],"225":[78],"229":[78],"232":[78],"240":[78],"261":[78],"268":[78],"299":[78],"313":[78],"366":[78],"412":[78],"416":[78],"425":[78],"452":[78],"455":[78],"460":[78],"527":[78],"528":[78],"553":[78],"583":[78],"593":[78]},"packages/@n8n/instance-ai/src/parsers/validate-attachments.ts":{"31":[78],"39":[78],"43":[78],"68":[78],"69":[78],"81":[78],"83":[78],"84":[78]},"packages/@n8n/instance-ai/src/parsers/xlsx-parser.ts":{"18":[78]},"packages/@n8n/instance-ai/src/tools/attachments/parse-file.tool.ts":{"62":[78],"111":[78],"112":[78],"115":[78],"119":[78],"129":[78],"138":[78],"156":[78],"171":[78]},"localhost-40943/assets/_plugin-vue_export-helper-Da88TEg1.js":{"2":[78,40]},"localhost-40943/assets/chunk-CC9Q-vWm.js":{"8":[78,40],"9":[78,40],"10":[78,40],"19":[78,40],"23":[78,40],"29":[78,40],"33":[78,40],"34":[78,40]},"packages/frontend/editor-ui/src/features/ai/instanceAi/components/AttachmentPreview.vue":{"21":[78],"24":[78],"27":[78],"31":[78],"33":[78],"35":[78],"50":[78],"68":[78]},"packages/@n8n/instance-ai/src/runtime/liveness-policy.ts":{"28":[38],"56":[38],"58":[38],"59":[38],"62":[38],"93":[38]},"packages/nodes-base/nodes/Slack/Slack.node.ts":{"8":[16,18,8,38,44,50,111]},"packages/nodes-base/nodes/Slack/SlackTriggerHelpers.ts":{"8":[16,18,8,38,44,50,111],"22":[16,18,8,38,44,50,111],"36":[16,18,8,38,44,50,111],"84":[16,18,8,38,44,50,111],"99":[16,18,8,38,44,50,111],"119":[16,18,8,38,44,50,111],"123":[16,18,8,38,44,50,111]},"packages/nodes-base/nodes/Slack/V1/GenericFunctions.ts":{"16":[16,18,8,38,44,50,111],"19":[16,18,8,38,44,50,111],"20":[16,18,8,38,44,50,111],"26":[16,18,8,38,44,50,111],"27":[16,18,8,38,44,50,111],"33":[16,18,8,38,44,50,111],"104":[16,18,8,38,44,50,111],"140":[16,18,8,38,44,50,111]},"packages/nodes-base/nodes/Slack/V1/SlackV1.node.ts":{"76":[16,18,8,38,44,50,111],"190":[16,18,8,38,44,50,111],"202":[16,18,8,38,44,50,111],"216":[16,18,8,38,44,50,111],"236":[16,18,8,38,44,50,111],"250":[16,18,8,38,44,50,111],"268":[16,18,8,38,44,50,111],"476":[16,18,8,38,44,50,111],"486":[16,18,8,38,44,50,111]},"packages/nodes-base/nodes/Slack/V2/GenericFunctions.ts":{"41":[16,18,8,38,44,50,111],"48":[16,18,8,38,44,50,111],"51":[16,18,8,38,44,50,111],"52":[16,18,8,38,44,50,111],"58":[16,18,8,38,44,50,111],"59":[16,18,8,38,44,50,111],"68":[16,18,8,38,44,50,111],"72":[16,18,8,38,44,50,111],"168":[16,18,8,38,44,50,111],"181":[16,18,8,38,44,50,111],"217":[16,18,8,38,44,50,111],"279":[16,18,8,38,44,50,111],"311":[16,18,8,38,44,50,111],"386":[16,18,8,38,44,50,111],"396":[16,18,8,38,44,50,111],"423":[16,18,8,38,44,50,111],"439":[16,18,8,38,44,50,111],"483":[16,18,8,38,44,50,111]},"packages/nodes-base/nodes/Slack/V2/SlackHitlWebhook.ts":{"23":[16,18,8,38,44,50,111],"60":[16,18,8,38,44,50,111],"80":[16,18,8,38,44,50,111],"91":[16,18,8,38,44,50,111],"141":[16,18,8,38,44,50,111],"213":[16,18,8,38,44,50,111]},"packages/nodes-base/nodes/Slack/V2/SlackV2.node.ts":{"65":[16,18,8,38,44,50,111],"186":[16,18,8,38,44,50,111],"203":[16,18,8,38,44,50,111],"223":[16,18,8,38,44,50,111],"228":[16,18,8,38,44,50,111],"233":[16,18,8,38,44,50,111],"240":[16,18,8,38,44,50,111],"256":[16,18,8,38,44,50,111],"262":[16,18,8,38,44,50,111],"267":[16,18,8,38,44,50,111],"278":[16,18,8,38,44,50,111],"295":[16,18,8,38,44,50,111],"309":[16,18,8,38,44,50,111],"329":[16,18,8,38,44,50,111],"343":[16,18,8,38,44,50,111],"362":[16,18,8,38,44,50,111],"376":[16,18,8,38,44,50,111],"396":[16,18,8,38,44,50,111],"620":[16,18,8,38,44,50,111],"683":[16,18,8,38,44,50,111],"693":[16,18,8,38,44,50,111],"1591":[16,18,8,38,44,50,111],"1636":[16,18,8,38,44,50,111],"1664":[16,18,8,38,44,50,111]},"localhost-35879/assets/_plugin-vue_export-helper-Da88TEg1.js":{"2":[38]},"localhost-35879/assets/chunk-CC9Q-vWm.js":{"8":[38],"9":[38],"10":[38],"19":[38],"23":[38],"29":[38],"33":[38],"34":[38]},"packages/frontend/editor-ui/src/experiments/enhancedHitlSlack/slackHitlParameters.ts":{"13":[16,18,8,38,44,111],"14":[16,18,8,38,44,111]},"packages/frontend/editor-ui/src/features/ai/instanceAi/workflowSetup/InstanceAiWorkflowSetup.vue":{"9":[38]},"packages/frontend/editor-ui/src/features/ai/instanceAi/workflowSetup/components/WorkflowSetupCard.vue":{"14":[38],"23":[38],"25":[38],"26":[38],"28":[38],"32":[38],"36":[38],"48":[38],"60":[38]},"packages/frontend/editor-ui/src/features/ai/instanceAi/workflowSetup/components/WorkflowSetupSectionBody.vue":{"26":[38],"35":[38],"37":[38],"43":[38],"69":[38],"73":[38],"76":[38],"79":[38],"103":[38],"110":[38],"112":[38],"124":[38],"133":[38],"137":[38],"140":[38],"165":[38],"188":[38]},"packages/frontend/editor-ui/src/features/ai/instanceAi/workflowSetup/components/WorkflowSetupWizard.vue":{"8":[38],"10":[38],"14":[38],"22":[38],"23":[38],"32":[38]},"packages/frontend/editor-ui/src/features/ai/instanceAi/workflowSetup/components/WorkflowSetupWizardFooter.vue":{"9":[38],"12":[38],"13":[38],"17":[38],"21":[38],"25":[38],"30":[38],"34":[38],"44":[38],"46":[38],"49":[38],"51":[38],"57":[38],"67":[38],"80":[38],"97":[38],"126":[38]},"packages/frontend/editor-ui/src/features/ai/instanceAi/workflowSetup/composables/useWorkflowSetupActions.ts":{"36":[38],"63":[38],"66":[38],"75":[38],"87":[38]},"packages/frontend/editor-ui/src/features/ai/instanceAi/workflowSetup/composables/useWorkflowSetupApply.ts":{"12":[38]},"packages/frontend/editor-ui/src/features/ai/instanceAi/workflowSetup/composables/useWorkflowSetupBootstrap.ts":{"5":[38],"14":[38]},"packages/frontend/editor-ui/src/features/ai/instanceAi/workflowSetup/composables/useWorkflowSetupContext.ts":{"70":[38],"75":[38],"92":[38],"96":[38],"97":[38],"98":[38],"153":[38],"162":[38],"202":[38]},"packages/frontend/editor-ui/src/features/ai/instanceAi/workflowSetup/composables/useWorkflowSetupInputs.ts":{"15":[38],"75":[38],"87":[38],"94":[38],"100":[38],"126":[38],"139":[38],"143":[38],"198":[38],"223":[38],"232":[38],"235":[38],"246":[38],"249":[38]},"packages/frontend/editor-ui/src/features/ai/instanceAi/workflowSetup/composables/useWorkflowSetupSections.ts":{"11":[38],"16":[38],"21":[38],"70":[38],"93":[38],"123":[38]},"packages/frontend/editor-ui/src/features/ai/instanceAi/workflowSetup/composables/useWorkflowSetupSteps.ts":{"15":[38],"19":[38]},"packages/frontend/editor-ui/src/features/ai/instanceAi/workflowSetup/composables/useWorkflowSetupTelemetry.ts":{"42":[38],"58":[38],"68":[38],"69":[38],"76":[38],"80":[38],"100":[38],"122":[38],"137":[38],"144":[38],"158":[38],"168":[38],"173":[38]},"packages/frontend/editor-ui/src/features/ai/instanceAi/workflowSetup/workflowSetup.helpers.ts":{"8":[38],"21":[38]},"packages/frontend/editor-ui/src/features/credentials/composables/useCredentialTestInBackground.ts":{"12":[18],"28":[18],"92":[38]},"packages/frontend/editor-ui/src/features/setupPanel/setupPanel.utils.ts":{"7":[18,38],"25":[18],"50":[18],"103":[18],"136":[18],"227":[18],"267":[18],"310":[18],"318":[18]},"packages/@n8n/workflow-sdk/src/mock-data/ai-root-shapes.ts":{"38":[40],"51":[40],"70":[40],"76":[40]},"packages/frontend/@n8n/design-system/src/components/N8nIcon/nodes/wait.svg":{"11":[89,90,40,109,68,6]},"packages/core/src/utils/hitl-callback-reference.ts":{"31":[50],"40":[50],"52":[50],"56":[50],"70":[50],"89":[50],"103":[50]},"packages/core/src/utils/signature-helpers.ts":{"9":[50],"16":[50],"31":[50]},"localhost-39281/assets/_plugin-vue_export-helper-Da88TEg1.js":{"2":[50]},"localhost-39281/assets/chunk-CC9Q-vWm.js":{"8":[50],"9":[50],"10":[50],"19":[50],"23":[50],"29":[50],"33":[50],"34":[50]},"packages/@n8n/api-types/src/schemas/folder.schema.ts":{"10":[52,107,53]},"packages/frontend/editor-ui/src/features/core/folders/components/DeleteFolderModal.vue":{"16":[53],"41":[53],"46":[53],"50":[53],"55":[53],"74":[53],"82":[53],"105":[53],"141":[53],"143":[53],"155":[53],"162":[53],"165":[53],"168":[53],"181":[53],"197":[53],"199":[53],"227":[53]},"packages/frontend/editor-ui/src/features/core/folders/components/FolderCard.vue":{"68":[52,107,53],"72":[107],"74":[52,107,53],"98":[52,107,53],"106":[52,107,53],"119":[52,107,53],"130":[52,107,53],"134":[52,107,53],"145":[107],"165":[107],"169":[52,107,53],"180":[52,107,53],"213":[52,107,53],"220":[52,107,53],"221":[52,107,53],"230":[52,107,53],"233":[52,107,53],"240":[52,107,53],"248":[52,53],"259":[52,107,53],"271":[52,107,53],"280":[52,107,53],"286":[52,107,53],"287":[52,107,53],"299":[107],"329":[52,107,53],"339":[52,107,53],"360":[52,107,53]},"localhost-39431/assets/_plugin-vue_export-helper-Da88TEg1.js":{"2":[83]},"localhost-39431/assets/chunk-CC9Q-vWm.js":{"8":[83],"9":[83],"10":[83],"19":[83],"23":[83],"29":[83],"33":[83],"34":[83]},"packages/frontend/editor-ui/src/app/utils/formatters/listFormatter.ts":{"15":[83,73]},"packages/frontend/editor-ui/src/features/workflows/templates/components/AppsRequiringCredsNotice.vue":{"10":[83,73],"17":[83,73],"24":[83,73],"27":[83,73],"33":[83,73],"38":[83,73]},"packages/frontend/editor-ui/src/features/workflows/templates/components/IconSuccess.vue":{"2":[83,73]},"packages/frontend/editor-ui/src/features/workflows/templates/components/SetupTemplateFormStep.vue":{"42":[83,73],"44":[83,73],"48":[83,73],"52":[83,73],"53":[83,73],"60":[83,73],"71":[73],"85":[83,73],"98":[83,73]},"packages/frontend/editor-ui/src/features/workflows/templates/components/SetupWorkflowCredentialsModal.vue":{"13":[83,73],"29":[83,73],"46":[83,73],"52":[73],"61":[83,73],"65":[83,73],"67":[83,73],"71":[83,73],"83":[83,73],"90":[73],"98":[83,73],"105":[73]},"packages/frontend/editor-ui/src/features/workflows/templates/composables/useCredentialSetupState.ts":{"29":[83,73],"34":[83,73],"38":[83,73],"43":[83,73],"77":[83,73],"103":[83,73],"115":[83,73],"123":[83,73],"126":[83,73],"132":[83,73],"136":[83,73],"140":[83,73],"144":[73],"163":[83,73],"171":[73]},"packages/frontend/editor-ui/src/features/workflows/templates/composables/useSetupWorkflowCredentialsModalState.ts":{"9":[83,73],"15":[83,73],"34":[83,73],"40":[73],"52":[73],"66":[73]},"packages/frontend/editor-ui/src/features/workflows/templates/utils/templateTransforms.ts":{"38":[83,73],"47":[83,73],"55":[73],"73":[73],"99":[73],"122":[73],"127":[73]},"packages/frontend/editor-ui/src/features/ndv/parameters/components/ExpressionEditorModal/ExpressionEditorModalInput.vue":{"27":[142],"39":[142],"49":[142],"57":[142],"65":[142],"66":[142],"72":[142],"79":[142],"85":[142],"90":[142],"93":[142]},"packages/frontend/editor-ui/src/features/shared/tags/components/TagsDropdown.vue":{"64":[154,161],"65":[154,161],"70":[154,161],"71":[154,161],"74":[154,161],"78":[52,154,161],"87":[154,161],"88":[154],"96":[154,161],"104":[154],"122":[154,161],"126":[154],"131":[154,161],"133":[154,161],"136":[154],"152":[154,161],"153":[154,161],"154":[154,161],"163":[154,161],"172":[154,161],"183":[154],"190":[154,161],"199":[154],"200":[154],"206":[154,161],"240":[154],"247":[52,154],"255":[154,161],"265":[52,154,161]},"packages/frontend/editor-ui/src/features/shared/tags/components/TagsManager/NoTagsView.vue":{"21":[154],"22":[154],"27":[154],"31":[154],"41":[154]},"packages/frontend/editor-ui/src/features/shared/tags/components/TagsManager/TagsManager.vue":{"31":[154],"44":[154],"48":[154],"50":[154],"51":[154],"53":[154],"57":[154],"61":[154],"65":[154],"69":[154],"119":[154],"120":[154],"132":[154],"135":[154],"150":[154],"163":[154],"189":[154]},"packages/frontend/editor-ui/src/features/shared/tags/components/TagsManager/TagsView/TagsTable.vue":{"33":[154],"42":[154],"46":[154],"73":[154],"84":[154],"88":[154],"92":[154],"93":[154],"100":[154],"101":[154],"109":[154],"118":[154],"119":[154],"127":[154],"132":[154],"145":[154],"147":[154],"148":[154],"150":[154],"169":[154],"171":[154],"181":[154],"183":[154],"235":[154]},"packages/frontend/editor-ui/src/features/shared/tags/components/TagsManager/TagsView/TagsTableHeader.vue":{"21":[154],"30":[154],"36":[154],"41":[154],"42":[154],"50":[154],"56":[154]},"packages/frontend/editor-ui/src/features/shared/tags/components/TagsManager/TagsView/TagsView.vue":{"12":[154],"35":[154],"48":[154],"52":[154],"53":[154],"60":[154],"61":[154],"76":[154],"85":[154],"112":[154],"116":[154],"120":[154],"122":[154],"132":[154],"137":[154],"142":[154],"145":[154],"156":[154],"177":[154]},"packages/frontend/editor-ui/src/features/shared/tags/components/TagsManager/WorkflowTagsManager.vue":{"11":[154],"16":[154],"17":[154],"19":[154],"29":[154],"64":[154],"85":[154]},"packages/frontend/editor-ui/src/features/shared/tags/components/WorkflowTagsDropdown.vue":{"35":[154,161],"36":[154,161],"39":[154,161],"40":[154,161],"41":[154,161],"43":[154,161],"44":[154,161],"46":[154],"50":[154],"58":[154,161]},"packages/frontend/editor-ui/src/features/shared/tags/useTagPermissions.ts":{"4":[154,161],"6":[154,161],"7":[154],"8":[154],"11":[52,154,161]},"localhost-41515/assets/_plugin-vue_export-helper-Da88TEg1.js":{"2":[95,8,103,105,108,63,132]},"localhost-41515/assets/chunk-CC9Q-vWm.js":{"8":[95,8,103,105,108,63,132],"9":[95,8,103,105,108,63,132],"10":[95,8,103,105,108,63,132],"19":[95,8,103,105,108,63,132],"23":[95,8,103,105,108,63,132],"29":[95,8,103,105,108,63,132],"33":[95,8,103,105,108,63,132],"34":[95,8,103,105,108,63,132]},"localhost-38023/assets/_plugin-vue_export-helper-Da88TEg1.js":{"2":[90,76,34,44,107,122,68,152,155]},"localhost-38023/assets/chunk-CC9Q-vWm.js":{"8":[90,76,34,44,107,122,68,152,155],"9":[90,76,34,44,107,122,68,152,155],"10":[90,76,34,44,107,122,68,152,155],"19":[90,76,34,44,107,122,68,152,155],"23":[90,76,34,44,107,122,68,152,155],"29":[90,76,34,44,107,122,68,152,155],"33":[90,76,34,44,107,122,68,152,155],"34":[90,76,34,44,107,122,68,152,155]},"packages/@n8n/api-types/src/dto/data-table/delete-data-table-rows.dto.ts":{"7":[34],"52":[34]},"packages/@n8n/api-types/src/dto/data-table/update-data-table-row.dto.ts":{"10":[34],"18":[34]},"packages/frontend/editor-ui/src/app/components/common/SelectedItemsInfo.vue":{"17":[34],"24":[34],"28":[34],"32":[34]},"packages/frontend/editor-ui/src/features/core/dataTable/components/dataGrid/ElDatePickerCellEditor.vue":{"7":[34],"24":[34],"30":[34],"36":[34],"43":[34],"45":[34]},"packages/frontend/editor-ui/src/features/core/dataTable/components/dataGrid/NullEmptyCellRenderer.vue":{"2":[34]},"packages/frontend/editor-ui/src/features/core/dataTable/composables/useDatePickerCommon.ts":{"11":[34],"17":[34],"22":[34],"35":[34],"60":[34],"67":[34],"84":[34]},"packages/frontend/editor-ui/src/features/core/dataTable/utils/typeUtils.ts":{"13":[34],"49":[34]},"packages/frontend/editor-ui/src/features/core/dataTable/utils.ts":{"1":[34],"6":[34]},"packages/@n8n/decorators/src/credential-resolver/errors.ts":{"5":[165],"16":[165],"27":[165]},"packages/cli/src/modules/dynamic-credentials.ee/credential-resolvers/identifiers/oauth2-metadata-http-client.ts":{"31":[165],"51":[165],"64":[165]},"packages/cli/src/modules/dynamic-credentials.ee/credential-resolvers/identifiers/oauth2-userinfo-identifier.ts":{"46":[165],"52":[165],"75":[165],"106":[165],"117":[165],"128":[165],"137":[165]},"packages/cli/src/modules/dynamic-credentials.ee/credential-resolvers/identifiers/oauth2-utils.ts":{"11":[165]},"packages/cli/src/modules/dynamic-credentials.ee/credential-resolvers/oauth-credential-resolver.ts":{"39":[165],"125":[165],"154":[165],"175":[165],"185":[165],"189":[165],"202":[165],"207":[165],"218":[165],"226":[165]},"packages/cli/src/modules/dynamic-credentials.ee/credential-resolvers/storage/dynamic-credential-entry-storage.ts":{"10":[165],"14":[165],"31":[165],"53":[165],"66":[165]},"packages/cli/src/modules/dynamic-credentials.ee/credential-resolvers.controller.ts":{"39":[165,171],"43":[165,171],"53":[165,171],"64":[165,171],"67":[165,171],"78":[165,171],"110":[165,171],"131":[165,171],"151":[165,171],"189":[165,171]},"packages/cli/src/modules/dynamic-credentials.ee/dynamic-credentials.controller.ts":{"35":[165,9],"51":[165,9],"77":[165,9],"105":[165,9],"117":[165,9],"147":[165,9],"159":[165,9],"227":[165,9],"308":[165,9]},"packages/cli/src/modules/dynamic-credentials.ee/services/credential-resolver.service.ts":{"48":[165,171],"64":[165,171],"89":[165,171],"92":[165,171],"97":[165,171],"100":[165,171],"107":[165,171],"112":[165,171],"113":[165,171],"121":[165,171],"134":[165,171],"192":[165,171],"206":[165,171],"220":[165,171],"262":[165,171],"290":[165,171],"297":[165,171],"311":[165,171]},"packages/cli/src/modules/dynamic-credentials.ee/services/dynamic-credential-storage.service.ts":{"25":[165],"34":[165],"119":[165],"125":[165]},"packages/cli/src/modules/dynamic-credentials.ee/services/resolver-config-expression.service.ts":{"16":[165],"23":[165]},"packages/cli/src/modules/dynamic-credentials.ee/services/shared-fields.ts":{"19":[165],"38":[165],"60":[165],"67":[165]},"packages/workflow/src/node-parameters/node-parameter-value-type-guard.ts":{"13":[165],"27":[165],"38":[165],"54":[165],"66":[165],"84":[165],"120":[165]},"packages/cli/src/modules/dynamic-credentials.ee/context-establishment-hooks/bearer-token-extractor.ts":{"29":[182],"37":[182],"41":[182]},"packages/cli/src/modules/dynamic-credentials.ee/context-establishment-hooks/http-header-extractor.ts":{"24":[182],"32":[182],"53":[182],"75":[182],"101":[182],"105":[182]},"packages/workflow/src/trigger-identity.ts":{"31":[182],"49":[182]},"packages/@n8n/nodes-langchain/nodes/mcp/McpTrigger/McpServer.ts":{"29":[9,11],"83":[9,11],"95":[9,11],"104":[9,11],"117":[9,11],"126":[9,11],"127":[9,11],"130":[9,11],"145":[9,11],"161":[9,11],"221":[9,11],"228":[9,11],"256":[9,11],"280":[9,11],"284":[9,11],"294":[9,11],"310":[9,11],"328":[9,11],"380":[9,11],"385":[9,11],"390":[9,11],"394":[9,11],"398":[9,11],"402":[9,11],"404":[9,11],"410":[9,11],"417":[9,11],"435":[9,11],"439":[9,11],"442":[9,11],"445":[9,11],"449":[9,11],"453":[9,11],"457":[9,11],"461":[9,11],"465":[9,11],"476":[9,11],"485":[9,11],"501":[9,11],"518":[9,11],"532":[9,11],"545":[9,11],"550":[9,11],"551":[9,11],"591":[9,11],"594":[9,11],"601":[9,11],"613":[9,11],"632":[9,11],"688":[9,11],"691":[9,11]},"packages/@n8n/nodes-langchain/nodes/mcp/McpTrigger/McpTrigger.node.ts":{"20":[9,11],"168":[9,11]},"packages/@n8n/nodes-langchain/nodes/mcp/McpTrigger/execution/DirectExecutionStrategy.ts":{"6":[9,11]},"packages/@n8n/nodes-langchain/nodes/mcp/McpTrigger/execution/ExecutionCoordinator.ts":{"10":[9,11],"14":[9,11],"22":[9,11],"26":[9,11],"30":[9,11]},"packages/@n8n/nodes-langchain/nodes/mcp/McpTrigger/execution/PendingCallsManager.ts":{"10":[9,11],"12":[9,11],"15":[9,11],"21":[9,11],"22":[9,11],"38":[9,11],"49":[9,11],"60":[9,11],"64":[9,11],"68":[9,11],"69":[9,11],"72":[9,11],"76":[9,11]},"packages/@n8n/nodes-langchain/nodes/mcp/McpTrigger/protocol/MessageFormatter.ts":{"18":[9,11],"52":[9,11],"68":[9,11],"81":[9,11],"82":[9,11],"86":[9,11],"112":[9,11],"113":[9,11],"114":[9,11],"120":[9,11],"126":[9,11]},"packages/@n8n/nodes-langchain/nodes/mcp/McpTrigger/protocol/MessageParser.ts":{"11":[9,11],"20":[9,11],"30":[9,11],"40":[9,11],"49":[9,11]},"packages/@n8n/nodes-langchain/nodes/mcp/McpTrigger/session/InMemorySessionStore.ts":{"5":[9,11],"11":[9,11],"16":[9,11],"21":[9,11],"26":[9,11],"30":[9,11],"34":[9,11]},"packages/@n8n/nodes-langchain/nodes/mcp/McpTrigger/session/SessionManager.ts":{"18":[9,11],"20":[9,11],"35":[9,11],"43":[9,11],"45":[9,11],"46":[9,11],"49":[9,11],"53":[9,11],"56":[9,11],"60":[9,11],"64":[9,11],"68":[9,11],"72":[9,11],"76":[9,11],"80":[9,11],"84":[9,11]},"packages/@n8n/nodes-langchain/nodes/mcp/McpTrigger/transport/StreamableHttpTransport.ts":{"17":[9,11],"32":[9,11],"37":[9,11],"49":[9,11],"54":[9,11]},"packages/@n8n/nodes-langchain/nodes/mcp/McpTrigger/transport/TransportFactory.ts":{"13":[9,11],"17":[9,11],"23":[9,11],"30":[9,11],"35":[9,11]},"packages/@n8n/nodes-langchain/utils/schemaParsing.ts":{"9":[9,11],"10":[9,11],"36":[9,11],"51":[9,11],"55":[9,11]},"packages/cli/src/modules/dynamic-credentials.ee/credential-resolvers/identifiers/n8n-identifier.ts":{"47":[9],"52":[9],"56":[9]},"packages/cli/src/modules/dynamic-credentials.ee/credential-resolvers/n8n-credential-resolver.ts":{"22":[9],"40":[9],"68":[9],"88":[9],"102":[9],"106":[9],"110":[9],"121":[9],"128":[9]},"packages/cli/src/modules/dynamic-credentials.ee/credential-resolvers/storage/dynamic-credential-user-entry-storage.ts":{"14":[9],"26":[9],"52":[9],"81":[9],"100":[9]},"packages/cli/src/modules/dynamic-credentials.ee/services/authorize-intent.service.ts":{"37":[9],"39":[9],"48":[9],"52":[9]},"packages/cli/src/modules/dynamic-credentials.ee/services/credential-check-proxy.service.ts":{"20":[9],"30":[9],"57":[9],"78":[9],"90":[9]},"packages/workflow/src/n8n-oauth2-auth.ts":{"4":[9],"8":[9],"17":[9],"48":[9]},"packages/nodes-base/nodes/Pipedrive/PipedriveTrigger.node.ts":{"16":[8],"280":[8],"314":[8],"362":[8],"386":[8]},"packages/nodes-base/nodes/Pipedrive/v1/GenericFunctions.ts":{"31":[8,147],"103":[8,147],"142":[8,147],"187":[8,147],"195":[8,147],"210":[8,147],"230":[8,147],"278":[8,147],"292":[8,147],"293":[8,147],"302":[8,147],"305":[8,147]},"packages/nodes-base/nodes/Trello/GenericFunctions.ts":{"14":[8],"46":[8]},"packages/nodes-base/nodes/Trello/Trello.node.ts":{"138":[8],"161":[8],"169":[8],"192":[8],"203":[8]},"packages/@n8n/api-types/src/dto/user/role-change-request.dto.ts":{"9":[122]},"packages/cli/src/modules/chat-hub/chat-hub-event-relay.service.ts":{"10":[122],"19":[122],"27":[122]},"packages/cli/src/modules/data-table/data-table.module.ts":{"7":[122],"20":[122],"27":[122],"40":[122],"51":[122],"58":[122]},"packages/cli/src/services/ownership-transfer/ownership-transfer-handler.registry.ts":{"25":[122],"28":[122],"32":[122]},"packages/cli/src/services/ownership-transfer/ownership-transfer.service.ts":{"21":[122],"36":[122],"39":[122],"64":[122]},"packages/frontend/editor-ui/src/app/utils/rbac/checks/isDefaultUser.ts":{"4":[122]},"packages/frontend/editor-ui/src/features/roles/instance/instanceRoleScopes.ts":{"110":[122],"120":[122],"125":[122],"137":[122],"176":[122],"180":[122],"295":[122]},"packages/frontend/editor-ui/src/features/settings/sso/provisioning/composables/userRoleProvisioning.store.ts":{"13":[122],"18":[122]},"packages/frontend/editor-ui/src/features/settings/users/components/DeleteUserModal.vue":{"15":[122],"32":[122],"35":[122],"38":[122],"40":[122],"49":[122],"65":[122],"71":[122],"105":[122],"118":[122],"122":[122],"128":[122],"132":[122],"134":[122],"137":[122],"142":[122],"154":[122],"155":[122],"156":[122],"165":[122],"174":[122]},"packages/frontend/editor-ui/src/features/settings/users/components/SettingsUsersActionsCell.vue":{"7":[122],"16":[122],"21":[122]},"packages/frontend/editor-ui/src/features/settings/users/components/SettingsUsersLastActiveCell.vue":{"7":[122],"10":[122],"13":[122]},"packages/frontend/editor-ui/src/features/settings/users/components/SettingsUsersProjectsCell.vue":{"8":[122],"20":[122],"26":[122],"33":[122],"34":[122],"38":[122],"51":[122],"52":[122]},"packages/frontend/editor-ui/src/features/settings/users/components/SettingsUsersProjectsModal.vue":{"17":[122],"52":[122]},"packages/frontend/editor-ui/src/features/settings/users/components/SettingsUsersRoleCell.vue":{"31":[122],"33":[122],"34":[122],"36":[122],"40":[122],"41":[122],"45":[122],"48":[122],"49":[122],"50":[122],"52":[122],"56":[122],"58":[122]},"packages/frontend/editor-ui/src/features/settings/users/components/SettingsUsersTable.vue":{"19":[122],"41":[122],"47":[122],"63":[122],"74":[122],"85":[122],"96":[122],"127":[122],"135":[122],"138":[122],"143":[122],"145":[122],"160":[122],"165":[122],"176":[122],"179":[122],"182":[122],"186":[122]},"packages/frontend/editor-ui/src/features/settings/users/views/SettingsUsersView.vue":{"71":[122],"73":[122],"77":[122],"81":[122],"83":[122],"98":[122],"103":[122],"112":[122],"118":[122],"125":[122],"134":[122],"139":[122],"143":[122],"147":[122],"171":[122],"206":[122],"211":[122],"328":[122],"329":[122],"338":[122],"350":[122],"373":[122],"378":[122],"425":[122],"428":[122],"433":[122],"442":[122],"502":[122],"509":[122]},"packages/cli/src/execution-lifecycle/save-execution-progress.ts":{"8":[68]},"packages/nodes-base/nodes/Filter/Filter.node.ts":{"8":[109,68]},"packages/nodes-base/nodes/Filter/V1/FilterV1.node.ts":{"17":[109,68],"310":[109,68]},"packages/nodes-base/nodes/Filter/V1/GenericFunctions.ts":{"5":[109,68],"8":[109,68],"13":[109,68],"14":[109,68],"15":[109,68],"17":[109,68],"19":[109,68],"21":[109,68],"23":[109,68],"24":[109,68],"25":[109,68],"26":[109,68],"28":[109,68],"30":[109,68],"32":[109,68],"34":[109,68],"36":[109,68],"42":[109,68],"50":[109,68],"64":[109,68],"81":[109,68]},"packages/nodes-base/nodes/Filter/V2/FilterV2.node.ts":{"20":[109,68],"95":[109,68],"99":[109,68]},"packages/nodes-base/nodes/Telegram/GenericFunctions.ts":{"72":[18,68,73],"134":[18,68,73],"208":[18,68,73],"248":[18,68,73],"261":[18,68,73],"265":[18,68,73],"271":[18,68,73],"292":[18,68,73]},"packages/nodes-base/nodes/Telegram/Telegram.node.ts":{"2043":[68,73],"2049":[68,73]},"packages/nodes-base/nodes/Telegram/hitl/setup.ts":{"17":[68,73],"30":[68,73],"47":[68,73],"73":[68,73]},"packages/nodes-base/nodes/Telegram/hitl/tokens.ts":{"17":[18,68,73]},"packages/nodes-base/nodes/Telegram/hitl/webhook.ts":{"12":[68,73],"21":[68,73],"25":[68,73],"36":[68,73],"51":[68,73],"87":[68,73],"126":[68,73]},"packages/frontend/@n8n/design-system/src/components/N8nIcon/nodes/filter.svg":{"11":[109,68]},"packages/frontend/editor-ui/src/app/components/WorkflowExtractionNameModal.vue":{"21":[152],"31":[152],"37":[152],"57":[152],"59":[152],"64":[152],"79":[152],"90":[152]},"packages/nodes-base/nodes/StopAndError/StopAndError.node.ts":{"82":[15,187]},"packages/nodes-base/nodes/StopAndError/utils.ts":{"14":[15,187],"18":[15,187]},"localhost-34177/assets/_plugin-vue_export-helper-Da88TEg1.js":{"2":[20]},"localhost-34177/assets/chunk-CC9Q-vWm.js":{"8":[20],"9":[20],"10":[20],"19":[20],"23":[20],"29":[20],"33":[20],"34":[20]},"packages/frontend/@n8n/design-system/src/components/CanvasPill/CanvasPill.vue":{"8":[18,20],"9":[18,20]},"packages/frontend/@n8n/design-system/src/components/CanvasThinkingPill/CanvasThinkingPill.vue":{"17":[18,20],"23":[18,20],"27":[18,20],"30":[18,20]},"packages/frontend/editor-ui/src/features/ai/assistant/assistant.types.ts":{"422":[18,20],"439":[18],"458":[18,20],"464":[18,20],"468":[18],"492":[18],"505":[18],"511":[18],"523":[18],"529":[18],"535":[18],"545":[18],"551":[18],"555":[18],"561":[18],"573":[18,20],"579":[18]},"packages/frontend/editor-ui/src/features/ai/assistant/builder.utils.ts":{"19":[18,20],"23":[18,20],"27":[18,20],"130":[18,20],"141":[18,20]},"packages/frontend/editor-ui/src/features/ai/assistant/components/HubSwitcher.vue":{"12":[18,20],"22":[18,20],"29":[18,20],"36":[18,20]},"packages/frontend/editor-ui/src/features/workflows/canvas/components/elements/nodes/render-types/CanvasNodeChoicePrompt.vue":{"21":[18,20],"30":[18,20],"41":[18,20],"44":[18,20],"46":[18,20],"65":[18,20],"83":[18,20],"87":[18,20]},"localhost-41515/assets/verified-Ge9vhh2v.js":{"8":[8]},"packages/cli/src/modules/mcp/mcp-api-key.service.ts":{"24":[42],"30":[42],"53":[42],"68":[42],"102":[42],"111":[42],"124":[42],"140":[42],"141":[42]},"packages/cli/src/modules/mcp/mcp.errors.ts":{"13":[42],"30":[42]},"packages/cli/src/modules/mcp/tools/webhook-utils.ts":{"47":[42],"55":[42],"63":[42],"69":[42],"82":[42],"139":[42],"145":[42],"154":[42],"160":[42],"170":[42],"176":[42],"190":[42],"202":[42],"219":[42],"256":[42],"259":[42],"264":[42],"274":[42],"290":[42],"320":[42],"335":[42],"355":[42]},"packages/cli/src/modules/mcp/tools/workflow-validation.utils.ts":{"20":[42],"51":[42]},"localhost-42113/assets/_plugin-vue_export-helper-Da88TEg1.js":{"2":[52,111,116,65,136,143,146,147]},"localhost-42113/assets/chunk-CC9Q-vWm.js":{"8":[52,111,116,65,136,143,146,147],"9":[52,111,116,65,136,143,146,147],"10":[52,111,116,65,136,143,146,147],"19":[52,111,116,65,136,143,146,147],"23":[52,111,116,65,136,143,146,147],"29":[52,111,116,65,136,143,146,147],"33":[52,111,116,65,136,143,146,147],"34":[52,111,116,65,136,143,146,147]},"packages/frontend/@n8n/chat/src/components/GetStarted.vue":{"5":[52]},"packages/frontend/@n8n/chat/src/utils/streamingHandlers.ts":{"11":[52],"55":[52],"64":[52],"78":[52],"98":[52]},"packages/frontend/editor-ui/src/app/components/ConsumedTokensDetails.vue":{"7":[52],"13":[52],"24":[52]},"packages/frontend/editor-ui/src/app/components/DuplicateWorkflowDialog.vue":{"20":[52],"58":[52],"64":[52],"72":[52],"76":[52],"150":[52],"159":[52],"164":[52],"175":[52],"195":[52]},"packages/frontend/editor-ui/src/app/components/MainHeader/utils.ts":{"1":[52]},"packages/frontend/editor-ui/src/app/composables/usePushConnection/handlers/builderCreditsUpdated.ts":{"4":[52]},"packages/frontend/editor-ui/src/app/composables/usePushConnection/handlers/executionRecovered.ts":{"14":[52]},"packages/frontend/editor-ui/src/app/composables/usePushConnection/handlers/nodeDescriptionUpdated.ts":{"10":[52]},"packages/frontend/editor-ui/src/app/composables/usePushConnection/handlers/reloadNodeType.ts":{"9":[52]},"packages/frontend/editor-ui/src/app/composables/usePushConnection/handlers/removeNodeType.ts":{"10":[52],"17":[52]},"packages/frontend/editor-ui/src/app/composables/usePushConnection/handlers/sendWorkerStatusMessage.ts":{"8":[52]},"packages/frontend/editor-ui/src/app/composables/usePushConnection/handlers/testWebhookDeleted.ts":{"8":[52]},"packages/frontend/editor-ui/src/app/composables/usePushConnection/handlers/workflowActivated.ts":{"10":[52]},"packages/frontend/editor-ui/src/app/composables/usePushConnection/handlers/workflowAutoDeactivated.ts":{"10":[52]},"packages/frontend/editor-ui/src/app/composables/usePushConnection/handlers/workflowDeactivated.ts":{"9":[52]},"packages/frontend/editor-ui/src/app/composables/usePushConnection/handlers/workflowFailedToActivate.ts":{"10":[52],"46":[52]},"packages/frontend/editor-ui/src/app/composables/usePushConnection/handlers/workflowPartiallyActivated.ts":{"12":[52],"54":[52]},"packages/frontend/editor-ui/src/app/composables/usePushConnection/handlers/workflowSettingsUpdated.ts":{"8":[52]},"packages/frontend/editor-ui/src/features/ai/assistant/components/Chat/AskAssistantFloatingButton.vue":{"14":[15,87,16,52],"25":[15,87,16,52],"39":[52],"43":[52],"63":[15,87,16,52],"75":[52],"80":[15,87,16,52]},"packages/frontend/editor-ui/src/features/ai/chatHub/components/CanvasChatFloatingMenu.vue":{"10":[52],"26":[52],"27":[52],"32":[52],"34":[52],"43":[52],"75":[52],"108":[52],"120":[52],"133":[52],"148":[52],"149":[52],"154":[52]},"packages/frontend/editor-ui/src/features/ai/chatHub/components/CanvasChatFloatingWindow.vue":{"23":[52],"28":[52],"37":[52],"46":[52],"58":[52],"59":[52],"62":[52],"66":[52],"70":[52],"78":[52],"90":[52],"92":[52],"100":[52],"105":[52],"122":[52],"125":[52],"130":[52],"140":[52]},"packages/frontend/editor-ui/src/features/ai/chatHub/components/CanvasChatHubPanel.vue":{"30":[52],"50":[52],"51":[52],"61":[52],"62":[52],"63":[52],"66":[52],"72":[52],"100":[52],"101":[52],"136":[52],"138":[52],"144":[52],"150":[52],"159":[52],"164":[52],"174":[52],"175":[52],"181":[52],"196":[52],"200":[52],"214":[52],"218":[52],"222":[52],"243":[52],"254":[52],"266":[52],"278":[52],"279":[52],"280":[52],"281":[52],"282":[52],"294":[52],"308":[52],"312":[52],"322":[52],"324":[52],"334":[52],"336":[52],"361":[52],"372":[52],"382":[52]},"packages/frontend/editor-ui/src/features/ai/chatHub/components/CanvasChatOverlay.vue":{"9":[52],"18":[52],"19":[52],"26":[52],"31":[52],"39":[52],"42":[52],"51":[52],"59":[52],"60":[52],"68":[52],"72":[52],"76":[52],"81":[52]},"packages/frontend/editor-ui/src/features/ai/chatHub/components/CanvasChatSessionDropdown.vue":{"19":[52],"32":[52],"33":[52],"40":[52],"42":[52],"51":[52],"52":[52],"54":[52],"67":[52],"72":[52],"77":[52],"78":[52],"91":[52],"99":[52],"100":[52],"103":[52],"112":[52],"117":[52],"129":[52],"131":[52],"137":[52],"150":[52],"156":[52],"161":[52],"164":[52],"167":[52],"168":[52]},"packages/frontend/editor-ui/src/features/ai/chatHub/components/ChatButtons.vue":{"11":[52],"14":[52],"27":[52],"33":[52],"40":[52],"41":[52]},"packages/frontend/editor-ui/src/features/ai/chatHub/components/ChatMarkdownChunk.vue":{"33":[52],"38":[52],"47":[52],"84":[52],"91":[52]},"packages/frontend/editor-ui/src/features/execution/logs/components/LogsViewConsumedTokenCountText.vue":{"8":[52,6],"20":[52]},"packages/frontend/editor-ui/src/features/settings/communityNodes/components/nodeCreator/CommunityNodeInstallHint.vue":{"10":[52],"11":[52]},"packages/frontend/editor-ui/src/features/shared/nodeCreator/components/ItemTypes/AgentItem.vue":{"15":[52]},"packages/frontend/editor-ui/src/features/shared/nodeCreator/components/Modes/AgentsMode.vue":{"28":[52],"53":[52],"57":[52],"61":[52],"65":[52],"86":[52],"87":[52],"100":[52],"106":[52],"117":[52],"121":[52],"134":[52],"146":[52],"180":[52],"181":[52],"189":[52],"195":[52],"196":[52],"199":[52],"203":[52],"206":[52],"226":[52],"229":[52],"235":[52],"242":[52],"254":[52],"257":[52]},"packages/frontend/editor-ui/src/features/shared/nodeCreator/components/SectionHeaderCreditsTag.vue":{"7":[52],"10":[52],"22":[52],"25":[52]},"packages/frontend/editor-ui/src/features/workflow-reviews/components/WorkflowPublishChoiceDialog.vue":{"17":[52],"26":[52],"31":[52],"34":[52],"42":[52],"43":[52],"45":[52],"48":[52],"53":[52],"60":[52],"64":[52]},"packages/frontend/editor-ui/src/features/workflow-reviews/components/WorkflowReviewRequiredToggle.vue":{"9":[52],"17":[52],"20":[52],"21":[52],"26":[52],"39":[52],"41":[52],"43":[52],"46":[52],"58":[52]},"packages/frontend/editor-ui/src/features/workflow-reviews/components/WorkflowReviewSubmittedDialog.vue":{"18":[52],"25":[52],"29":[52],"36":[52],"37":[52],"41":[52],"45":[52],"50":[52],"54":[52],"55":[52]},"packages/frontend/editor-ui/src/features/workflow-reviews/components/WorkflowSubmitForReviewDialog.vue":{"28":[52],"55":[52],"59":[52],"60":[52],"68":[52],"85":[52],"86":[52],"98":[52],"103":[52],"105":[52],"108":[52],"166":[52],"178":[52],"188":[52],"200":[52],"214":[52],"225":[52],"237":[52],"245":[52]},"packages/frontend/editor-ui/src/features/workflow-reviews/components/WorkflowUpdateReviewDialog.vue":{"11":[52],"29":[52],"35":[52],"44":[52],"89":[52],"99":[52],"101":[52],"104":[52],"110":[52],"119":[52]},"packages/frontend/editor-ui/src/app/components/FocusPanel.vue":{"57":[116,132],"89":[116,132],"93":[116,132],"113":[116,132],"129":[116,132],"137":[116,132],"140":[116,132],"142":[116,132],"196":[116,132],"202":[116,132],"213":[116,132],"215":[116,132],"233":[116,132],"235":[132],"241":[132],"369":[116,132],"373":[116,132],"374":[116,132],"395":[116,132],"601":[132],"604":[132]},"packages/frontend/editor-ui/src/features/workflows/canvas/experimental/components/ExperimentalCanvasNodeSettings.vue":{"21":[116,132],"31":[116,132],"32":[116,132],"35":[116,132],"36":[116,132],"41":[116,132],"70":[116,132]},"packages/frontend/editor-ui/src/features/workflows/canvas/experimental/components/ExperimentalEmbeddedNdvMapper.vue":{"19":[18,116,132],"39":[18,116,132],"43":[18,116,132],"45":[132],"54":[132],"80":[18,116,132],"87":[116,132],"96":[18,116,132]},"packages/frontend/editor-ui/src/features/workflows/canvas/experimental/components/ExperimentalFocusPanelHeader.vue":{"18":[116,132],"27":[116,132],"28":[116,132],"32":[116,132]},"packages/frontend/editor-ui/src/features/workflows/canvas/experimental/components/ExperimentalNodeDetailsDrawer.vue":{"21":[116,132],"26":[116,132],"27":[116],"33":[116,132],"36":[116,132],"37":[116],"46":[116,132],"51":[116],"53":[116],"58":[116]},"packages/frontend/editor-ui/src/features/workflows/canvas/experimental/composables/useExpressionResolveCtx.ts":{"7":[18,116,132],"12":[18,116,132],"21":[18,116,132]},"packages/cli/src/scheduling/durable-scheduler.ts":{"30":[168,170],"86":[168,170],"87":[168,170],"99":[168,170],"112":[168,170],"117":[168,170],"123":[168,170],"150":[168,170],"164":[168,170],"169":[168,170],"171":[168,170],"173":[168,170],"175":[168,170],"177":[168,170],"178":[168,170],"181":[168,170]},"packages/cli/src/workflows/publication/workflow-publication-reconciler.service.ts":{"54":[168,169,170],"72":[168,169,170],"89":[168,169,170],"95":[168,169,170],"103":[168,169,170],"109":[168,169,170],"120":[168,169,170],"125":[168,169,170],"167":[168,169,170],"173":[168,169,170],"178":[168,169,170],"181":[168,169,170],"201":[168,169,170],"209":[168,169,170],"216":[168,169,170],"229":[168,169,170]},"localhost-35407/assets/_plugin-vue_export-helper-Da88TEg1.js":{"2":[84]},"localhost-35407/assets/chunk-CC9Q-vWm.js":{"8":[84],"9":[84],"10":[84],"19":[84],"23":[84],"29":[84],"33":[84],"34":[84]},"localhost-42113/assets/verified-Ge9vhh2v.js":{"8":[65,147]},"packages/frontend/editor-ui/src/app/components/WorkflowPreviewHost.vue":{"45":[136,162],"50":[136,162],"61":[162],"76":[136,162],"90":[136,162],"122":[136,162],"124":[162],"128":[136,162]},"packages/frontend/editor-ui/src/features/workflows/workflowHistory/components/WorkflowHistoryContent.vue":{"40":[136],"55":[136],"70":[136],"81":[136],"84":[136],"97":[136],"101":[136],"107":[136],"129":[136],"130":[136],"135":[136],"150":[136],"152":[136],"172":[136]},"packages/frontend/editor-ui/src/features/workflows/workflowHistory/components/WorkflowHistoryList.vue":{"22":[136],"49":[136],"61":[136],"73":[136],"82":[136],"86":[136],"92":[136],"119":[136],"151":[136],"179":[136],"184":[136]},"packages/frontend/editor-ui/src/features/workflows/workflowHistory/components/WorkflowHistoryListItem.vue":{"26":[136],"58":[136],"63":[136],"74":[136],"83":[136],"99":[136],"165":[136],"172":[136],"203":[136],"211":[136],"214":[136]},"packages/frontend/editor-ui/src/features/workflows/workflowHistory/views/WorkflowHistory.vue":{"61":[136],"78":[136],"81":[136],"92":[136],"93":[136],"100":[136],"101":[136],"106":[136],"112":[136],"117":[136],"127":[136],"128":[136],"143":[136],"154":[136],"169":[136],"177":[136],"181":[136],"199":[136],"228":[136],"572":[136],"587":[136],"624":[136],"643":[136]},"packages/nodes-base/nodes/N8nTrainingCustomerDatastore/N8nTrainingCustomerDatastore.node.ts":{"119":[146]},"packages/nodes-base/nodes/Pipedrive/Pipedrive.node.ts":{"8":[147]},"packages/nodes-base/nodes/Pipedrive/v1/PipedriveV1.node.ts":{"37":[147],"2256":[147],"2393":[147],"3809":[147],"3820":[147],"3834":[147],"3862":[147],"3876":[147],"3900":[147],"3904":[147],"3908":[147],"3912":[147],"3916":[147],"3926":[147],"3930":[147],"3944":[147],"3962":[147],"3978":[147],"3994":[147],"4023":[147],"4053":[147],"4058":[147],"4063":[147],"4068":[147],"4073":[147],"4103":[147],"4949":[147]},"packages/nodes-base/nodes/Pipedrive/v2/PipedriveV2.node.ts":{"16":[147],"28":[147]},"packages/nodes-base/nodes/Pipedrive/v2/actions/activity/create.operation.ts":{"119":[147]},"packages/nodes-base/nodes/Pipedrive/v2/actions/activity/delete.operation.ts":{"31":[147]},"packages/nodes-base/nodes/Pipedrive/v2/actions/activity/get.operation.ts":{"34":[147]},"packages/nodes-base/nodes/Pipedrive/v2/actions/activity/getAll.operation.ts":{"126":[147]},"packages/nodes-base/nodes/Pipedrive/v2/actions/activity/update.operation.ts":{"140":[147]},"packages/nodes-base/nodes/Pipedrive/v2/actions/deal/create.operation.ts":{"222":[147]},"packages/nodes-base/nodes/Pipedrive/v2/actions/deal/delete.operation.ts":{"31":[147]},"packages/nodes-base/nodes/Pipedrive/v2/actions/deal/duplicate.operation.ts":{"31":[147]},"packages/nodes-base/nodes/Pipedrive/v2/actions/deal/get.operation.ts":{"34":[147]},"packages/nodes-base/nodes/Pipedrive/v2/actions/deal/getAll.operation.ts":{"124":[147]},"packages/nodes-base/nodes/Pipedrive/v2/actions/deal/search.operation.ts":{"138":[147]},"packages/nodes-base/nodes/Pipedrive/v2/actions/deal/update.operation.ts":{"179":[147]},"packages/nodes-base/nodes/Pipedrive/v2/actions/dealProduct/add.operation.ts":{"117":[147]},"packages/nodes-base/nodes/Pipedrive/v2/actions/dealProduct/getAll.operation.ts":{"54":[147]},"packages/nodes-base/nodes/Pipedrive/v2/actions/dealProduct/remove.operation.ts":{"40":[147]},"packages/nodes-base/nodes/Pipedrive/v2/actions/dealProduct/update.operation.ts":{"116":[147]},"packages/nodes-base/nodes/Pipedrive/v2/actions/file/create.operation.ts":{"87":[147]},"packages/nodes-base/nodes/Pipedrive/v2/actions/file/delete.operation.ts":{"31":[147]},"packages/nodes-base/nodes/Pipedrive/v2/actions/file/download.operation.ts":{"34":[147]},"packages/nodes-base/nodes/Pipedrive/v2/actions/file/get.operation.ts":{"31":[147]},"packages/nodes-base/nodes/Pipedrive/v2/actions/file/update.operation.ts":{"54":[147]},"packages/nodes-base/nodes/Pipedrive/v2/actions/lead/create.operation.ts":{"148":[147],"175":[147]},"packages/nodes-base/nodes/Pipedrive/v2/actions/lead/delete.operation.ts":{"31":[147]},"packages/nodes-base/nodes/Pipedrive/v2/actions/lead/get.operation.ts":{"31":[147]},"packages/nodes-base/nodes/Pipedrive/v2/actions/lead/getAll.operation.ts":{"100":[147]},"packages/nodes-base/nodes/Pipedrive/v2/actions/lead/update.operation.ts":{"105":[147],"132":[147]},"packages/nodes-base/nodes/Pipedrive/v2/actions/note/create.operation.ts":{"96":[147]},"packages/nodes-base/nodes/Pipedrive/v2/actions/note/delete.operation.ts":{"31":[147]},"packages/nodes-base/nodes/Pipedrive/v2/actions/note/get.operation.ts":{"31":[147]},"packages/nodes-base/nodes/Pipedrive/v2/actions/note/getAll.operation.ts":{"143":[147]},"packages/nodes-base/nodes/Pipedrive/v2/actions/note/update.operation.ts":{"103":[147]},"packages/nodes-base/nodes/Pipedrive/v2/actions/organization/create.operation.ts":{"71":[147]},"packages/nodes-base/nodes/Pipedrive/v2/actions/organization/delete.operation.ts":{"31":[147]},"packages/nodes-base/nodes/Pipedrive/v2/actions/organization/get.operation.ts":{"34":[147]},"packages/nodes-base/nodes/Pipedrive/v2/actions/organization/getAll.operation.ts":{"81":[147]},"packages/nodes-base/nodes/Pipedrive/v2/actions/organization/search.operation.ts":{"98":[147]},"packages/nodes-base/nodes/Pipedrive/v2/actions/organization/update.operation.ts":{"78":[147]},"packages/nodes-base/nodes/Pipedrive/v2/actions/person/create.operation.ts":{"164":[147]},"packages/nodes-base/nodes/Pipedrive/v2/actions/person/delete.operation.ts":{"31":[147]},"packages/nodes-base/nodes/Pipedrive/v2/actions/person/get.operation.ts":{"34":[147]},"packages/nodes-base/nodes/Pipedrive/v2/actions/person/getAll.operation.ts":{"80":[147]},"packages/nodes-base/nodes/Pipedrive/v2/actions/person/search.operation.ts":{"117":[147]},"packages/nodes-base/nodes/Pipedrive/v2/actions/person/update.operation.ts":{"171":[147]},"packages/nodes-base/nodes/Pipedrive/v2/actions/product/create.operation.ts":{"126":[147]},"packages/nodes-base/nodes/Pipedrive/v2/actions/product/delete.operation.ts":{"31":[147]},"packages/nodes-base/nodes/Pipedrive/v2/actions/product/get.operation.ts":{"34":[147]},"packages/nodes-base/nodes/Pipedrive/v2/actions/product/getAll.operation.ts":{"53":[147]},"packages/nodes-base/nodes/Pipedrive/v2/actions/product/search.operation.ts":{"101":[147]},"packages/nodes-base/nodes/Pipedrive/v2/actions/product/update.operation.ts":{"133":[147]},"packages/nodes-base/nodes/Pipedrive/v2/actions/router.ts":{"15":[147]},"packages/nodes-base/nodes/Pipedrive/v2/helpers/customFields.ts":{"24":[147],"36":[147],"58":[147],"59":[147],"61":[147],"69":[147],"97":[147],"130":[147],"142":[147],"143":[147]},"packages/nodes-base/nodes/Pipedrive/v2/helpers/fields.ts":{"10":[147]},"packages/nodes-base/nodes/Pipedrive/v2/helpers/index.ts":{"3":[147],"7":[147]},"packages/nodes-base/nodes/Pipedrive/v2/helpers/searchResponse.ts":{"9":[147],"14":[147],"25":[147]},"packages/nodes-base/nodes/Pipedrive/v2/helpers/typeCoercion.ts":{"4":[147],"18":[147],"32":[147],"55":[147]},"packages/nodes-base/nodes/Pipedrive/v2/methods/loadOptions.ts":{"9":[147],"34":[147],"65":[147],"83":[147],"117":[147],"120":[147],"127":[147],"130":[147],"137":[147],"143":[147],"150":[147],"162":[147],"187":[147],"193":[147],"203":[147],"206":[147],"213":[147],"223":[147],"226":[147]},"packages/nodes-base/nodes/Pipedrive/v2/transport/pipedrive.api.ts":{"30":[147],"96":[147],"126":[147],"170":[147],"229":[147],"232":[147]},"packages/@n8n/ai-utilities/src/utils/loaders/n8n-pdf-loader.ts":{"21":[0],"26":[0],"43":[0],"57":[0],"73":[0],"82":[0]},"packages/@n8n/ai-utilities/src/utils/n8n-binary-loader.ts":{"33":[0],"40":[0],"54":[0],"60":[0],"86":[0],"104":[0],"151":[0],"158":[0],"166":[0],"174":[0],"198":[0],"227":[0]},"localhost-36969/assets/_plugin-vue_export-helper-Da88TEg1.js":{"2":[0,24,94,109,112,130,150,157]},"localhost-36969/assets/chunk-CC9Q-vWm.js":{"8":[0,24,94,109,112,130,150,157],"9":[0,24,94,109,112,130,150,157],"10":[0,24,94,109,112,130,150,157],"19":[0,24,94,109,112,130,150,157],"23":[0,24,94,109,112,130,150,157],"29":[0,24,94,109,112,130,150,157],"33":[0,24,94,109,112,130,150,157],"34":[0,24,94,109,112,130,150,157]},"packages/cli/src/errors/response-errors/webhook-not-found.error.ts":{"3":[163,11],"36":[163,11]},"packages/frontend/editor-ui/src/app/components/Badge.vue":{"8":[24],"17":[24]},"packages/frontend/editor-ui/src/app/components/VersionCard.vue":{"13":[24],"17":[24]},"localhost-36969/assets/verified-Ge9vhh2v.js":{"8":[94,130,150]},"localhost-39515/assets/_plugin-vue_export-helper-Da88TEg1.js":{"2":[55]},"localhost-39515/assets/chunk-CC9Q-vWm.js":{"8":[55],"9":[55],"10":[55],"19":[55],"23":[55],"29":[55],"33":[55],"34":[55]},"localhost-39515/assets/verified-Ge9vhh2v.js":{"8":[55]},"packages/frontend/@n8n/design-system/src/components/N8nIcon/svgContentDirective.ts":{"13":[55],"21":[55]},"packages/frontend/@n8n/design-system/src/components/N8nIconPicker/IconColorPicker.vue":{"15":[55],"63":[55],"65":[55],"81":[55],"92":[55],"100":[55]},"packages/frontend/@n8n/design-system/src/components/N8nIconPicker/SkinTonePicker.vue":{"10":[55],"29":[55],"39":[55],"50":[55],"58":[55]},"packages/frontend/@n8n/design-system/src/components/N8nIconPicker/useIconPickerVirtualRows.ts":{"30":[55],"40":[55],"60":[55],"79":[55]},"packages/frontend/editor-ui/src/features/collaboration/projects/components/ProjectIcon.vue":{"35":[55]},"packages/nodes-base/nodes/Discourse/Discourse.node.ts":{"93":[109],"107":[109]},"packages/nodes-base/nodes/Discourse/GenericFunctions.ts":{"11":[109],"40":[109]},"packages/frontend/editor-ui/src/features/ndv/parameters/components/FilterConditions/Condition.vue":{"43":[48,130],"63":[48,130],"67":[48,130],"79":[48,130],"97":[48,130],"110":[48,130],"120":[48,130],"131":[48,130],"143":[48],"145":[48],"166":[48],"170":[48],"215":[48,130],"221":[48,130],"250":[48,130],"269":[48,130],"278":[48,130],"306":[48,130],"316":[48]},"packages/frontend/editor-ui/src/features/ndv/parameters/components/FilterConditions/FilterConditions.vue":{"39":[48,130],"62":[48,130],"71":[48],"75":[48,130],"78":[48,130],"90":[48],"94":[48,130],"96":[48],"100":[48,130],"105":[48,130],"134":[48,130],"135":[48,130],"144":[48,130],"145":[48,130],"152":[48,130],"160":[48],"164":[48],"172":[48],"176":[48,130],"178":[48,130],"210":[48,130],"232":[48],"233":[48]},"packages/frontend/editor-ui/src/features/ndv/parameters/components/FilterConditions/OperatorSelect.vue":{"19":[48,130],"30":[48,130],"31":[48,130],"34":[48,130],"36":[48,130],"38":[48,130],"41":[48,130],"42":[48,130],"55":[48,130],"73":[48,130],"86":[48,130],"88":[48,130]},"packages/frontend/editor-ui/src/features/ndv/parameters/components/FilterConditions/utils.ts":{"19":[48,130],"81":[48,130],"124":[48,130],"145":[48,130]},"packages/nodes-base/nodes/Google/Gmail/GmailTrigger.node.ts":{"253":[150],"270":[150],"283":[150],"319":[150],"337":[150],"355":[150],"416":[150],"427":[150],"474":[150],"486":[150],"503":[150],"527":[150],"531":[150],"538":[150]},"packages/@n8n/backend-common/src/utils/path-util.ts":{"9":[105],"27":[105],"40":[105],"46":[105],"55":[105],"63":[105]},"packages/nodes-base/nodes/Files/ExtractFromFile/ExtractFromFile.node.ts":{"117":[105]},"packages/nodes-base/nodes/Files/ExtractFromFile/actions/moveTo.operation.ts":{"105":[105]},"packages/nodes-base/nodes/Files/ExtractFromFile/actions/pdf.operation.ts":{"83":[105]},"packages/nodes-base/nodes/Files/ExtractFromFile/actions/spreadsheet.operation.ts":{"8":[105],"9":[105],"18":[105],"58":[105]},"packages/nodes-base/nodes/Files/ReadWriteFile/ReadWriteFile.node.ts":{"60":[105]},"packages/nodes-base/nodes/Files/ReadWriteFile/actions/read.operation.ts":{"88":[105]},"packages/nodes-base/nodes/Files/ReadWriteFile/actions/write.operation.ts":{"72":[105]},"packages/nodes-base/nodes/Files/ReadWriteFile/helpers/utils.ts":{"5":[105],"37":[105],"44":[105]},"packages/nodes-base/nodes/SpreadsheetFile/v2/fromFile.operation.ts":{"85":[105],"126":[105],"129":[105],"136":[105],"144":[105],"154":[105]},"packages/frontend/@n8n/design-system/src/components/N8nIcon/nodes/read-write-files-from-disk.svg":{"11":[48,105]},"packages/frontend/editor-ui/src/app/utils/formatters/textFormatter.ts":{"8":[108]},"packages/cli/src/modules/log-streaming.ee/destinations/message-event-bus-destination-sentry.ee.ts":{"16":[63],"36":[63],"56":[63],"95":[63],"105":[63],"119":[63],"123":[63]},"packages/nodes-base/nodes/Telegram/TelegramTrigger.node.ts":{"25":[18],"236":[18],"247":[18],"281":[18],"296":[18],"353":[18],"371":[18],"389":[18]},"packages/nodes-base/nodes/Telegram/util/triggerUtils.ts":{"11":[18]},"localhost-38989/assets/_plugin-vue_export-helper-Da88TEg1.js":{"2":[18,93,57,118,140,160,161,162]},"localhost-38989/assets/chunk-CC9Q-vWm.js":{"8":[18,93,57,118,140,160,161,162],"9":[18,93,57,118,140,160,161,162],"10":[18,93,57,118,140,160,161,162],"19":[18,93,57,118,140,160,161,162],"23":[18,93,57,118,140,160,161,162],"29":[18,93,57,118,140,160,161,162],"33":[18,93,57,118,140,160,161,162],"34":[18,93,57,118,140,160,161,162]},"packages/frontend/editor-ui/src/features/ai/assistant/components/Agent/BuilderSetupCard.vue":{"30":[18],"55":[18],"59":[18],"61":[18],"84":[18],"91":[18],"97":[18],"101":[18],"105":[18],"106":[18],"107":[18],"111":[18],"112":[18],"113":[18],"114":[18],"116":[18],"122":[18],"140":[18],"153":[18],"186":[18],"191":[18],"193":[18],"209":[18],"210":[18],"213":[18],"222":[18],"223":[18]},"packages/frontend/editor-ui/src/features/ai/assistant/components/Agent/BuilderSetupWizard.vue":{"14":[18],"39":[18],"43":[18],"47":[18],"51":[18],"55":[18],"59":[18],"75":[18],"86":[18],"91":[18],"96":[18],"102":[18],"104":[18],"107":[18],"115":[18],"122":[18],"131":[18],"140":[18],"162":[18],"175":[18],"187":[18],"210":[18]},"packages/frontend/editor-ui/src/features/ai/assistant/components/Agent/CreditsSettingsDropdown.vue":{"9":[18],"32":[18],"99":[18]},"packages/frontend/editor-ui/src/features/ai/assistant/components/Agent/ExecuteMessage.vue":{"34":[18],"51":[18],"58":[18],"87":[18],"136":[18],"146":[18],"191":[18],"223":[18]},"packages/frontend/editor-ui/src/features/ai/assistant/composables/useBuilderExecution.ts":{"21":[18],"53":[18]},"packages/frontend/editor-ui/src/features/ai/assistant/composables/useBuilderSetupCards.ts":{"10":[18],"21":[18],"26":[18],"30":[18],"45":[18],"59":[18],"60":[18],"65":[18],"67":[18],"71":[18],"74":[18],"77":[18],"89":[18],"90":[18],"104":[18],"110":[18],"157":[18]},"packages/frontend/editor-ui/src/features/setupPanel/components/TriggerExecuteButton.vue":{"6":[18],"18":[18],"22":[18],"30":[18]},"packages/frontend/editor-ui/src/features/setupPanel/components/cards/SetupCardBody.vue":{"22":[18],"57":[18],"61":[18],"63":[18],"70":[18],"77":[18],"94":[18],"97":[18],"106":[18],"110":[18],"111":[18],"113":[18],"124":[18],"135":[18],"136":[18],"143":[18],"183":[18],"197":[18],"200":[18],"206":[18]},"packages/frontend/editor-ui/src/features/setupPanel/components/cards/SetupCardSection.vue":{"9":[18],"13":[18],"16":[18]},"packages/frontend/editor-ui/src/features/setupPanel/composables/groupSetupCards.ts":{"20":[18],"81":[18]},"packages/frontend/editor-ui/src/features/setupPanel/composables/useTriggerExecution.ts":{"18":[18],"38":[18],"40":[18],"46":[18],"70":[18],"77":[18],"81":[18],"87":[18],"91":[18]},"packages/frontend/editor-ui/src/features/setupPanel/composables/useWebhookUrls.ts":{"21":[18],"25":[18],"27":[18],"33":[18],"35":[18],"83":[18]},"packages/frontend/editor-ui/src/features/setupPanel/composables/useWorkflowSetupState.ts":{"50":[18],"67":[18],"74":[18],"121":[18],"124":[18],"160":[18],"198":[18],"226":[18],"231":[18],"297":[18],"299":[18],"300":[18],"306":[18],"330":[18],"353":[18],"354":[18],"361":[18],"362":[18],"365":[18],"366":[18],"376":[18],"390":[18],"404":[18],"417":[18],"434":[18],"435":[18],"437":[18],"441":[18],"449":[18],"468":[18],"471":[18],"477":[18],"478":[18],"513":[18],"622":[18],"638":[18],"641":[18],"664":[18],"665":[18],"667":[18],"669":[18],"700":[18],"703":[18],"714":[18],"718":[18],"730":[18],"800":[18],"811":[18],"837":[18]},"packages/@n8n/scheduler/src/core/reaper/reap.ts":{"84":[168,169],"133":[168,169]},"packages/frontend/editor-ui/src/features/settings/communityNodes/components/CommunityPackageCard.vue":{"43":[118],"47":[118],"55":[118],"63":[118],"80":[118],"98":[118],"103":[118],"105":[118],"108":[118],"120":[118],"135":[118],"142":[118],"151":[118],"168":[118],"176":[118]},"packages/frontend/editor-ui/src/features/settings/communityNodes/components/CommunityPackageInstallModal.vue":{"32":[118],"39":[118],"47":[118],"54":[118],"78":[118],"82":[118],"86":[118],"98":[118],"111":[118],"119":[118],"134":[118],"167":[118],"172":[118],"178":[118]},"packages/frontend/editor-ui/src/features/settings/communityNodes/components/CommunityPackageManageConfirmModal.vue":{"32":[118],"54":[118],"57":[118],"68":[118],"69":[118],"72":[118],"108":[118],"112":[118],"120":[118],"124":[118],"147":[118],"151":[118],"190":[118],"192":[118],"203":[118],"216":[118],"226":[118],"232":[118],"240":[118],"252":[118],"253":[118],"261":[118],"270":[118]},"packages/frontend/editor-ui/src/features/settings/communityNodes/composables/useInstallNode.ts":{"60":[118]},"packages/frontend/editor-ui/src/features/settings/communityNodes/views/SettingsCommunityNodesView.vue":{"40":[118],"44":[118],"48":[118],"56":[118],"77":[118],"82":[118],"94":[118],"104":[118],"110":[118],"117":[118],"122":[118],"126":[118],"130":[118],"178":[118]},"packages/cli/src/errors/response-errors/workflow-activation-bad-request.error.ts":{"8":[184]},"packages/cli/src/errors/response-errors/workflow-validation.error.ts":{"9":[184]},"packages/frontend/editor-ui/src/app/components/ImportWorkflowUrlModal.vue":{"10":[134,160],"16":[134,160],"20":[134,160],"24":[160],"29":[134,160],"32":[134,160],"45":[134,160],"60":[134,160],"68":[134,160],"76":[134,160]},"packages/cli/src/middlewares/list-query/dtos/base.select.dto.ts":{"7":[161],"12":[161]},"packages/cli/src/middlewares/list-query/dtos/workflow.select.dto.ts":{"4":[161],"22":[161]},"packages/frontend/@n8n/design-system/src/components/N8nCommandBar/CommandBarItem.vue":{"6":[158,161],"17":[161],"21":[161],"27":[158,161]},"packages/frontend/@n8n/design-system/src/components/N8nTags/Tags.vue":{"21":[161,73,162],"37":[161,73,162],"45":[161,162],"50":[161,73,162],"56":[161,73,162]},"packages/frontend/editor-ui/src/features/shared/commandBar/components/CommandBarItemTitle.vue":{"23":[161]},"packages/frontend/editor-ui/src/features/workflows/workflowHistory/components/WorkflowHistoryVersionUnpublishModal.vue":{"15":[161],"36":[161],"42":[161],"45":[161],"49":[161],"51":[161],"58":[161],"62":[161],"70":[161],"73":[161],"76":[161]},"packages/@n8n/imap/src/errors.ts":{"5":[73],"18":[73],"24":[73]},"packages/@n8n/imap/src/helpers/get-message.ts":{"13":[73],"17":[73],"21":[73],"24":[73],"29":[73],"41":[73],"45":[73]},"packages/@n8n/imap/src/imap-simple.ts":{"12":[73],"16":[73],"21":[73],"27":[73],"35":[73],"39":[73],"57":[73],"65":[73],"66":[73],"87":[73],"94":[73],"98":[73],"104":[73],"111":[73],"122":[73],"128":[73],"134":[73],"148":[73],"154":[73],"159":[73],"169":[73],"175":[73],"176":[73],"181":[73],"182":[73],"183":[73],"188":[73],"192":[73],"193":[73],"198":[73],"202":[73],"203":[73]},"packages/@n8n/imap/src/index.ts":{"12":[73],"18":[73],"19":[73],"26":[73],"31":[73],"40":[73],"45":[73],"81":[73]},"packages/@n8n/imap/src/part-data.ts":{"8":[73],"10":[73],"14":[73],"41":[73],"47":[73],"55":[73],"59":[73],"65":[73],"72":[73],"78":[73]},"packages/@n8n/imap/src/uudecode.ts":{"9":[73]},"packages/cli/src/controllers/oauth/oauth1-credential.controller.ts":{"13":[73],"22":[73],"37":[73]},"packages/nodes-base/nodes/EmailReadImap/EmailReadImap.node.ts":{"8":[73]},"packages/nodes-base/nodes/EmailReadImap/v1/EmailReadImapV1.node.ts":{"24":[73],"222":[73],"231":[73],"271":[73],"285":[73],"290":[73],"310":[73],"320":[73],"329":[73],"344":[73],"441":[73],"500":[73],"510":[73],"529":[73],"561":[73],"585":[73],"586":[73],"617":[73],"628":[73]},"packages/nodes-base/nodes/EmailReadImap/v2/EmailReadImapV2.node.ts":{"200":[73],"209":[73],"261":[73],"291":[73],"300":[73],"320":[73],"331":[73],"334":[73],"347":[73],"365":[73],"384":[73],"430":[73],"442":[73],"448":[73],"469":[73],"472":[73],"488":[73],"507":[73],"530":[73]},"packages/nodes-base/nodes/EmailReadImap/v2/utils.ts":{"20":[73],"61":[73],"109":[73],"198":[73]},"packages/nodes-base/nodes/NextCloud/GenericFunctions.ts":{"14":[73]},"packages/nodes-base/nodes/NextCloud/NextCloud.node.ts":{"872":[73],"1103":[73],"1190":[73],"1198":[73],"1284":[73],"1292":[73],"1326":[73],"1334":[73],"1364":[73],"1372":[73],"1392":[73],"1397":[73],"1405":[73]},"packages/nodes-base/nodes/Shopify/GenericFunctions.ts":{"12":[73],"85":[73],"124":[73]},"packages/nodes-base/nodes/Shopify/ShopifyTrigger.node.ts":{"335":[73],"350":[73],"373":[73],"389":[73]},"packages/nodes-base/nodes/Twitter/Twitter.node.ts":{"8":[73]},"packages/nodes-base/nodes/Twitter/V1/GenericFunctions.ts":{"14":[73],"46":[73],"69":[73],"81":[73]},"packages/nodes-base/nodes/Twitter/V1/TwitterV1.node.ts":{"27":[73],"76":[73],"92":[73],"123":[73],"164":[73],"171":[73]},"packages/nodes-base/nodes/Twitter/V2/GenericFunctions.ts":{"13":[73],"60":[73],"83":[73],"105":[73]},"packages/nodes-base/nodes/Twitter/V2/TwitterV2.node.ts":{"31":[73],"100":[73],"116":[73]},"localhost-43057/assets/_plugin-vue_export-helper-Da88TEg1.js":{"2":[73]},"localhost-43057/assets/chunk-CC9Q-vWm.js":{"8":[73],"9":[73],"10":[73],"19":[73],"23":[73],"29":[73],"33":[73],"34":[73]},"packages/frontend/@n8n/design-system/src/components/N8nIcon/nodes/email-trigger.svg":{"11":[73]},"packages/frontend/editor-ui/src/app/components/GoBackButton.vue":{"11":[73]},"packages/frontend/editor-ui/src/app/components/NodeList.vue":{"19":[73,162],"23":[162],"27":[73,162],"31":[73,162],"36":[73,162],"42":[73,162]},"packages/frontend/editor-ui/src/features/workflows/templates/components/TemplateCard.vue":{"12":[73,162],"37":[73],"41":[162],"43":[73,162],"65":[73,162],"71":[73,162],"75":[73,162]},"packages/frontend/editor-ui/src/features/workflows/templates/components/TemplateDetails.vue":{"21":[73],"32":[73],"34":[73],"49":[73],"59":[73],"62":[73],"79":[73],"86":[73],"89":[73]},"packages/frontend/editor-ui/src/features/workflows/templates/components/TemplateList.vue":{"38":[73,162],"47":[73,162],"71":[162],"74":[73],"83":[73,162],"90":[73,162],"97":[162],"98":[73]},"packages/frontend/editor-ui/src/features/workflows/templates/setupTemplate.store.ts":{"39":[73],"56":[73],"60":[73],"80":[73],"98":[73],"115":[73],"128":[73],"149":[73],"176":[73]},"packages/frontend/editor-ui/src/features/workflows/templates/utils/templateActions.ts":{"30":[73],"72":[73],"100":[162],"128":[73,162],"132":[73,162],"137":[73,162]},"packages/frontend/editor-ui/src/features/workflows/templates/views/SetupWorkflowFromTemplateView.vue":{"22":[73],"25":[73],"26":[73],"28":[73],"49":[73],"57":[73],"79":[73],"89":[73],"109":[73],"135":[73],"143":[73],"151":[73]},"packages/frontend/editor-ui/src/features/workflows/templates/views/TemplatesCollectionView.vue":{"32":[73],"37":[73],"39":[73],"44":[73],"45":[73],"48":[73],"49":[73],"64":[73],"87":[73],"88":[73],"98":[73],"117":[73],"121":[73],"124":[73]},"packages/core/src/execution-engine/node-execution-context/hook-context.ts":{"21":[162],"34":[162],"38":[162],"42":[162],"54":[162],"61":[162]},"packages/nodes-base/nodes/Hubspot/HubspotTrigger.node.ts":{"17":[162],"320":[162],"323":[162],"326":[162],"329":[162],"337":[162],"384":[162],"418":[162],"447":[162]},"packages/nodes-base/nodes/Hubspot/V1/GenericFunctions.ts":{"15":[162],"78":[162],"123":[162],"144":[162],"155":[162],"1953":[162],"1955":[162],"1960":[162],"1963":[162],"1978":[162],"1987":[162],"1997":[162],"2008":[162],"2024":[162]},"packages/frontend/@n8n/design-system/src/utils/markdown.ts":{"36":[162]},"packages/frontend/editor-ui/src/app/components/CollectionWorkflowCard.vue":{"12":[162],"13":[162],"17":[162]},"packages/frontend/editor-ui/src/app/views/WorkflowOnboardingView.vue":{"17":[162],"55":[162]},"packages/frontend/editor-ui/src/features/workflows/templates/components/TemplateFilters.vue":{"15":[162],"34":[162],"38":[162],"43":[162],"44":[162],"52":[162],"60":[162],"68":[162],"69":[162],"80":[162],"81":[162],"91":[162],"108":[162],"118":[162]},"packages/frontend/editor-ui/src/features/workflows/templates/components/TemplatesInfoCard.vue":{"21":[162],"29":[162]},"packages/frontend/editor-ui/src/features/workflows/templates/components/TemplatesInfoCarousel.vue":{"11":[162],"37":[162],"64":[162],"65":[162],"67":[162],"73":[162],"74":[162],"76":[162],"82":[162],"95":[162],"101":[162],"112":[162],"113":[162],"115":[162]},"packages/frontend/editor-ui/src/features/workflows/templates/recommendations/components/RecommendedTemplateCard.vue":{"17":[162],"35":[162],"38":[162],"43":[162],"46":[162],"71":[162],"82":[162],"101":[162],"109":[162],"121":[162],"127":[162],"137":[162],"148":[162],"177":[162],"187":[162]},"packages/frontend/editor-ui/src/features/workflows/templates/recommendations/recommendedTemplates.store.ts":{"5":[162]},"packages/frontend/editor-ui/src/features/workflows/templates/views/TemplatesSearchView.vue":{"51":[162],"54":[162],"61":[162],"65":[162],"69":[162],"73":[162],"87":[162],"105":[162],"106":[162],"110":[162],"115":[162],"125":[162],"129":[162],"137":[162],"149":[162],"156":[162],"166":[162],"173":[162],"178":[162],"192":[162],"196":[162],"206":[162],"220":[162],"258":[162],"277":[162],"285":[162],"297":[162],"310":[162],"319":[162],"330":[162],"379":[162],"386":[162],"410":[162]},"packages/frontend/editor-ui/src/features/workflows/templates/views/TemplatesWorkflowView.vue":{"43":[162],"47":[162],"49":[162],"80":[162],"90":[162],"91":[162],"102":[162],"124":[162],"148":[162],"155":[162],"168":[162],"194":[162],"200":[162]},"localhost-36259/assets/_plugin-vue_export-helper-Da88TEg1.js":{"2":[15,87,16,89,99,114,134,158]},"localhost-36259/assets/chunk-CC9Q-vWm.js":{"8":[15,87,16,89,99,114,134,158],"9":[15,87,16,89,99,114,134,158],"10":[15,87,16,89,99,114,134,158],"19":[15,87,16,89,99,114,134,158],"23":[15,87,16,89,99,114,134,158],"29":[15,87,16,89,99,114,134,158],"33":[15,87,16,89,99,114,134,158],"34":[15,87,16,89,99,114,134,158]},"packages/frontend/@n8n/design-system/src/components/AskAssistantButton/AskAssistantButton.vue":{"8":[15,87,16],"26":[15,87,16]},"packages/frontend/@n8n/design-system/src/components/AskAssistantChat/messages/EventMessage.vue":{"24":[15],"40":[15],"44":[15]},"packages/frontend/@n8n/design-system/src/components/N8nIcon/nodes/stop-and-error.svg":{"11":[15,87]},"packages/frontend/editor-ui/src/features/ai/assistant/components/Chat/NewAssistantSessionModal.vue":{"14":[15],"27":[15],"31":[15],"44":[15],"56":[15],"62":[15],"65":[15],"68":[15],"72":[15]},"packages/frontend/@n8n/design-system/src/components/AskAssistantChat/messages/CodeDiffMessage.vue":{"19":[87],"20":[87],"32":[87],"40":[87],"41":[87]},"packages/frontend/@n8n/design-system/src/components/CodeDiff/CodeDiff.vue":{"10":[87],"43":[87],"48":[87],"85":[87],"93":[87],"120":[87],"121":[87],"137":[87],"138":[87]},"packages/frontend/editor-ui/src/features/ai/assistant/composables/useCodeDiff.ts":{"31":[87],"41":[87],"43":[87],"64":[87],"68":[87],"74":[87],"87":[87],"133":[87]},"packages/nodes-base/nodes/Microsoft/GenericFunctions.ts":{"10":[16],"65":[16]},"packages/nodes-base/nodes/Microsoft/Outlook/MicrosoftOutlook.node.ts":{"8":[16]},"packages/nodes-base/nodes/Microsoft/Outlook/v1/GenericFunctions.ts":{"12":[16],"57":[16],"90":[16],"123":[16],"131":[16],"155":[16],"157":[16],"161":[16],"165":[16],"176":[16]},"packages/nodes-base/nodes/Microsoft/Outlook/v1/MicrosoftOutlookV1.node.ts":{"108":[16],"119":[16],"138":[16],"264":[16],"312":[16],"315":[16],"372":[16],"589":[16]},"packages/nodes-base/nodes/Microsoft/Outlook/v2/MicrosoftOutlookV2.node.ts":{"16":[16],"27":[16]},"packages/nodes-base/nodes/Microsoft/Outlook/v2/actions/calendar/create.operation.ts":{"94":[16]},"packages/nodes-base/nodes/Microsoft/Outlook/v2/actions/calendar/delete.operation.ts":{"19":[16]},"packages/nodes-base/nodes/Microsoft/Outlook/v2/actions/calendar/get.operation.ts":{"19":[16]},"packages/nodes-base/nodes/Microsoft/Outlook/v2/actions/calendar/getAll.operation.ts":{"38":[16]},"packages/nodes-base/nodes/Microsoft/Outlook/v2/actions/calendar/update.operation.ts":{"89":[16]},"packages/nodes-base/nodes/Microsoft/Outlook/v2/actions/contact/create.operation.ts":{"42":[16]},"packages/nodes-base/nodes/Microsoft/Outlook/v2/actions/contact/delete.operation.ts":{"19":[16]},"packages/nodes-base/nodes/Microsoft/Outlook/v2/actions/contact/get.operation.ts":{"55":[16]},"packages/nodes-base/nodes/Microsoft/Outlook/v2/actions/contact/getAll.operation.ts":{"80":[16],"103":[16]},"packages/nodes-base/nodes/Microsoft/Outlook/v2/actions/contact/update.operation.ts":{"30":[16]},"packages/nodes-base/nodes/Microsoft/Outlook/v2/actions/draft/create.operation.ts":{"206":[16],"223":[16]},"packages/nodes-base/nodes/Microsoft/Outlook/v2/actions/draft/delete.operation.ts":{"19":[16]},"packages/nodes-base/nodes/Microsoft/Outlook/v2/actions/draft/get.operation.ts":{"85":[16]},"packages/nodes-base/nodes/Microsoft/Outlook/v2/actions/draft/send.operation.ts":{"29":[16],"36":[16],"37":[16],"41":[16]},"packages/nodes-base/nodes/Microsoft/Outlook/v2/actions/draft/update.operation.ts":{"184":[16]},"packages/nodes-base/nodes/Microsoft/Outlook/v2/actions/event/create.operation.ts":{"180":[16],"222":[16],"225":[16]},"packages/nodes-base/nodes/Microsoft/Outlook/v2/actions/event/delete.operation.ts":{"20":[16]},"packages/nodes-base/nodes/Microsoft/Outlook/v2/actions/event/get.operation.ts":{"56":[16]},"packages/nodes-base/nodes/Microsoft/Outlook/v2/actions/event/getAll.operation.ts":{"125":[16]},"packages/nodes-base/nodes/Microsoft/Outlook/v2/actions/event/update.operation.ts":{"217":[16]},"packages/nodes-base/nodes/Microsoft/Outlook/v2/actions/folder/create.operation.ts":{"38":[16]},"packages/nodes-base/nodes/Microsoft/Outlook/v2/actions/folder/delete.operation.ts":{"20":[16]},"packages/nodes-base/nodes/Microsoft/Outlook/v2/actions/folder/get.operation.ts":{"39":[16]},"packages/nodes-base/nodes/Microsoft/Outlook/v2/actions/folder/getAll.operation.ts":{"68":[16]},"packages/nodes-base/nodes/Microsoft/Outlook/v2/actions/folder/update.operation.ts":{"30":[16]},"packages/nodes-base/nodes/Microsoft/Outlook/v2/actions/folderMessage/getAll.operation.ts":{"227":[16]},"packages/nodes-base/nodes/Microsoft/Outlook/v2/actions/message/delete.operation.ts":{"19":[16]},"packages/nodes-base/nodes/Microsoft/Outlook/v2/actions/message/get.operation.ts":{"115":[16]},"packages/nodes-base/nodes/Microsoft/Outlook/v2/actions/message/getAll.operation.ts":{"243":[16]},"packages/nodes-base/nodes/Microsoft/Outlook/v2/actions/message/move.operation.ts":{"22":[16]},"packages/nodes-base/nodes/Microsoft/Outlook/v2/actions/message/reply.operation.ts":{"222":[16]},"packages/nodes-base/nodes/Microsoft/Outlook/v2/actions/message/send.operation.ts":{"209":[16]},"packages/nodes-base/nodes/Microsoft/Outlook/v2/actions/message/sendAndWait.operation.ts":{"31":[16]},"packages/nodes-base/nodes/Microsoft/Outlook/v2/actions/message/update.operation.ts":{"173":[16]},"packages/nodes-base/nodes/Microsoft/Outlook/v2/actions/messageAttachment/add.operation.ts":{"54":[16]},"packages/nodes-base/nodes/Microsoft/Outlook/v2/actions/messageAttachment/download.operation.ts":{"30":[16]},"packages/nodes-base/nodes/Microsoft/Outlook/v2/actions/messageAttachment/get.operation.ts":{"62":[16],"79":[16]},"packages/nodes-base/nodes/Microsoft/Outlook/v2/actions/messageAttachment/getAll.operation.ts":{"62":[16],"77":[16]},"packages/nodes-base/nodes/Microsoft/Outlook/v2/actions/router.ts":{"16":[16]},"packages/nodes-base/nodes/Microsoft/Outlook/v2/helpers/utils.ts":{"64":[16],"106":[16],"146":[16],"148":[16],"156":[16],"182":[16],"184":[16],"206":[16],"207":[16],"215":[16],"223":[16],"241":[16],"257":[16],"266":[16],"278":[16],"284":[16],"285":[16],"327":[16],"352":[16],"356":[16],"360":[16]},"packages/nodes-base/nodes/Microsoft/Outlook/v2/methods/listSearch.ts":{"9":[16],"43":[16],"53":[16],"61":[16],"69":[16],"102":[16],"113":[16],"145":[16],"156":[16],"199":[16],"210":[16],"239":[16],"245":[16],"256":[16],"293":[16]},"packages/nodes-base/nodes/Microsoft/Outlook/v2/methods/loadOptions.ts":{"8":[16],"28":[16],"48":[16]},"packages/nodes-base/nodes/Microsoft/Outlook/v2/transport/index.ts":{"27":[16],"48":[16],"72":[16],"150":[16],"184":[16],"237":[16],"273":[16],"291":[16]},"localhost-36259/assets/verified-Ge9vhh2v.js":{"8":[16,89,134]},"packages/cli/src/modules/mcp-registry/mcp-registry-node-loader.ts":{"52":[10],"57":[10],"61":[10],"71":[10],"109":[10],"115":[10],"121":[10],"129":[10],"134":[10],"138":[10],"142":[10]},"packages/cli/src/modules/mcp-registry/mcp-registry-test.controller.ts":{"17":[10],"23":[10],"29":[10],"38":[10]},"packages/cli/src/modules/mcp-registry/node-description-transform.ts":{"30":[10],"37":[10],"45":[10],"58":[10],"73":[10],"93":[10],"127":[10],"140":[10],"152":[10],"163":[10],"181":[10],"202":[10],"205":[10],"208":[10],"226":[10],"228":[10],"238":[10],"239":[10],"240":[10],"250":[10],"255":[10],"265":[10],"282":[10]},"packages/cli/src/modules/mcp-registry/registry/mcp-registry.service.ts":{"39":[10],"51":[10],"64":[10],"71":[10],"76":[10],"82":[10],"89":[10],"98":[10],"103":[10],"108":[10],"117":[10],"121":[10],"125":[10],"130":[10],"139":[10],"144":[10],"158":[10],"188":[10],"193":[10],"194":[10],"196":[10],"197":[10],"199":[10],"200":[10],"214":[10],"225":[10],"237":[10],"261":[10],"265":[10],"269":[10]},"packages/cli/src/modules/mcp-registry/registry/mcp-registry.types.ts":{"80":[10],"97":[10]},"localhost-40701/assets/_plugin-vue_export-helper-Da88TEg1.js":{"2":[10]},"localhost-40701/assets/chunk-CC9Q-vWm.js":{"8":[10],"9":[10],"10":[10],"19":[10],"23":[10],"29":[10],"33":[10],"34":[10]},"packages/@n8n/nodes-langchain/nodes/mcp/McpTrigger/transport/SSETransport.ts":{"10":[11],"17":[11],"22":[11]},"packages/cli/src/modules/instance-registry/instance-registry.controller.ts":{"9":[134],"16":[134],"24":[134],"39":[134]},"packages/frontend/editor-ui/src/app/components/AboutModal.vue":{"16":[134],"24":[134],"28":[134],"57":[134],"68":[134],"72":[134],"75":[134],"80":[134],"83":[134],"88":[134],"91":[134],"92":[134],"98":[134],"101":[134],"102":[134],"108":[134],"111":[134],"116":[134],"119":[134],"120":[134],"127":[134]},"localhost-38677/assets/_plugin-vue_export-helper-Da88TEg1.js":{"2":[91,28,48,124,125,127]},"localhost-38677/assets/chunk-CC9Q-vWm.js":{"8":[91,28,48,124,125,127],"9":[91,28,48,124,125,127],"10":[91,28,48,124,125,127],"19":[91,28,48,124,125,127],"23":[91,28,48,124,125,127],"29":[91,28,48,124,125,127],"33":[91,28,48,124,125,127],"34":[91,28,48,124,125,127]},"localhost-38677/assets/verified-Ge9vhh2v.js":{"8":[28,48,124,125,127]},"packages/frontend/editor-ui/src/features/ndv/parameters/components/FilterConditions/CombinatorSelect.vue":{"14":[48],"22":[48],"31":[48],"32":[48]},"packages/cli/src/modules/external-secrets.ee/external-secrets-provider-connection-manager.ee.ts":{"15":[179,60],"24":[179,60],"40":[179,60],"57":[179,60],"60":[179,60],"64":[179,60],"73":[179,60],"101":[179,60],"131":[179,60],"149":[179,60],"167":[179,60],"187":[179,60]},"packages/cli/src/modules/external-secrets.ee/external-secrets.controller.ee.ts":{"13":[179],"22":[179],"34":[179],"40":[179],"47":[179],"60":[179],"68":[179],"76":[179],"90":[179]},"packages/cli/src/modules/external-secrets.ee/external-secrets.service.ee.ts":{"10":[179],"15":[179],"30":[179],"31":[179],"44":[179],"50":[179],"54":[179],"61":[179],"66":[179],"70":[179],"77":[179]},"packages/cli/src/modules/external-secrets.ee/provider-lifecycle.service.ts":{"25":[179,60],"36":[179,60],"78":[179,60],"112":[179,60],"127":[179,60]},"packages/cli/src/modules/external-secrets.ee/redaction.service.ee.ts":{"15":[179],"19":[179],"20":[179],"42":[179],"53":[179],"62":[179]},"packages/cli/src/modules/external-secrets.ee/retry-manager.service.ts":{"27":[179,60],"30":[179,60],"31":[179,60],"42":[179,60],"62":[179,60],"70":[179,60],"97":[179,60],"112":[179,60],"123":[179,60],"130":[179,60]},"packages/cli/src/modules/external-secrets.ee/secrets-cache.service.ts":{"16":[179,60],"26":[179,60],"30":[179,60],"39":[179,60],"55":[179,60],"57":[179,60],"58":[179,60],"79":[179,60],"87":[179,60],"95":[179,60],"103":[179,60]},"packages/cli/src/modules/external-secrets.ee/settings-store.service.ts":{"18":[179],"26":[179],"38":[179],"54":[179],"61":[179],"80":[179],"108":[179],"116":[179],"127":[179],"142":[179]},"localhost-37657/assets/_plugin-vue_export-helper-Da88TEg1.js":{"2":[4]},"localhost-37657/assets/chunk-CC9Q-vWm.js":{"8":[4],"9":[4],"10":[4],"19":[4],"23":[4],"29":[4],"33":[4],"34":[4]},"packages/cli/src/modules/external-secrets.ee/errors/secrets-provider-errors.ts":{"24":[60],"59":[60],"79":[60],"96":[60]},"packages/cli/src/modules/external-secrets.ee/secret-provider-access-check.service.ee.ts":{"17":[60],"24":[60],"34":[60],"77":[60],"98":[60],"100":[60]},"localhost-42767/assets/_plugin-vue_export-helper-Da88TEg1.js":{"2":[60]},"localhost-42767/assets/chunk-CC9Q-vWm.js":{"8":[60],"9":[60],"10":[60],"19":[60],"23":[60],"29":[60],"33":[60],"34":[60]},"packages/frontend/editor-ui/src/features/integrations/externalSecrets.ee/assets/images/aws-secrets-manager.svg":{"8":[60]},"packages/frontend/editor-ui/src/features/integrations/secretsProviders.ee/components/DeleteSecretsProviderModal.ee.vue":{"28":[60],"41":[60],"43":[60],"47":[60],"54":[60],"64":[60],"74":[60],"90":[60],"128":[60],"142":[60],"145":[60],"157":[60],"162":[60],"163":[60],"179":[60],"190":[60],"195":[60],"198":[60],"206":[60]},"packages/frontend/editor-ui/src/features/integrations/secretsProviders.ee/components/SecretsProviderConnectionCard.ee.vue":{"49":[60],"57":[60],"61":[60],"63":[60],"74":[60],"85":[60],"87":[60],"95":[60],"115":[60],"156":[60],"167":[60],"180":[60],"183":[60],"205":[60],"231":[60],"237":[60]},"packages/frontend/editor-ui/src/features/integrations/secretsProviders.ee/components/SecretsProviderConnectionModal.ee.vue":{"43":[60],"75":[60],"76":[60],"77":[60],"78":[60],"92":[60],"93":[60],"103":[60],"120":[60],"130":[60],"146":[60],"150":[60],"151":[60],"157":[60],"166":[60],"171":[60],"175":[60],"179":[60],"183":[60],"208":[60],"228":[60],"235":[60],"248":[60],"264":[60],"270":[60],"303":[60],"308":[60],"313":[60],"344":[60],"418":[60],"439":[60],"441":[60],"451":[60],"460":[60],"477":[60],"481":[60],"490":[60],"502":[60],"505":[60],"510":[60]},"packages/frontend/editor-ui/src/features/integrations/secretsProviders.ee/components/SecretsProviderImage.ee.vue":{"17":[60]},"packages/frontend/editor-ui/src/features/integrations/secretsProviders.ee/components/SecretsProvidersEmptyState.ee.vue":{"8":[60],"16":[60],"45":[60],"56":[60],"62":[60],"64":[60],"73":[60],"95":[60],"101":[60],"108":[60],"112":[60]},"packages/frontend/editor-ui/src/features/integrations/secretsProviders.ee/composables/useConnectionModal.ee.ts":{"31":[60],"35":[60],"60":[60],"66":[60],"94":[60],"120":[60],"140":[60],"144":[60],"153":[60],"161":[60],"166":[60],"168":[60],"169":[60],"175":[60],"176":[60],"183":[60],"189":[60],"196":[60],"201":[60],"202":[60],"209":[60],"210":[60],"214":[60],"215":[60],"217":[60],"227":[60],"235":[60],"241":[60],"259":[60],"278":[60],"283":[60],"285":[60],"293":[60],"300":[60],"308":[60],"309":[60],"359":[60],"403":[60],"444":[60],"452":[60],"480":[60],"518":[60]},"packages/frontend/editor-ui/src/features/integrations/secretsProviders.ee/composables/useSecretsProviderConnection.ee.ts":{"34":[60],"39":[60],"62":[60],"79":[60],"100":[60]},"packages/frontend/editor-ui/src/features/integrations/secretsProviders.ee/views/SettingsSecretsProviders.ee.vue":{"47":[60],"49":[60],"52":[60],"97":[60],"101":[60],"105":[60],"137":[60],"138":[60],"141":[60],"154":[60],"193":[60],"194":[60],"205":[60],"212":[60],"237":[60],"243":[60],"251":[60],"266":[60],"269":[60],"285":[60],"298":[60],"310":[60]},"packages/@n8n/db/src/repositories/test-run.repository.ee.ts":{"18":[127],"22":[127],"45":[127],"53":[127],"57":[127],"62":[127],"71":[127],"82":[127],"89":[127],"97":[127],"101":[127],"120":[127],"133":[127],"145":[127],"155":[127],"170":[127],"180":[127]},"packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/OpenAi.node.ts":{"12":[127]},"packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/helpers/binary-data.ts":{"11":[127]},"packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/helpers/description.ts":{"3":[127],"20":[127],"38":[127]},"packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/helpers/modelFiltering.ts":{"9":[127]},"packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/helpers/polling.ts":{"4":[127],"34":[127]},"packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/helpers/utils.ts":{"17":[127],"27":[127],"39":[127],"50":[127],"58":[127],"66":[127],"88":[127]},"packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/methods/listSearch.ts":{"13":[127],"36":[127],"45":[127],"46":[127],"49":[127],"63":[127],"69":[127],"75":[127],"82":[127],"85":[127],"89":[127],"92":[127],"96":[127],"121":[127],"126":[127],"130":[127],"173":[127]},"packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/methods/loadOptions.ts":{"5":[127]},"packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/transport/index.ts":{"15":[127]},"packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/v1/OpenAiV1.node.ts":{"22":[127],"82":[127]},"packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/v1/actions/assistant/create.operation.ts":{"176":[127],"185":[127]},"packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/v1/actions/assistant/deleteAssistant.operation.ts":{"18":[127]},"packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/v1/actions/assistant/list.operation.ts":{"25":[127],"62":[127]},"packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/v1/actions/assistant/message.operation.ts":{"157":[127],"164":[127],"224":[127],"231":[127]},"packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/v1/actions/assistant/update.operation.ts":{"120":[127],"126":[127],"132":[127],"207":[127],"213":[127],"214":[127],"217":[127],"223":[127],"224":[127],"228":[127]},"packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/v1/actions/audio/generate.operation.ts":{"134":[127]},"packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/v1/actions/audio/transcribe.operation.ts":{"58":[127]},"packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/v1/actions/audio/translate.operation.ts":{"50":[127]},"packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/v1/actions/descriptions.ts":{"3":[127]},"packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/v1/actions/file/deleteFile.operation.ts":{"51":[127]},"packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/v1/actions/file/list.operation.ts":{"53":[127],"63":[127]},"packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/v1/actions/file/upload.operation.ts":{"57":[127]},"packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/v1/actions/image/analyze.operation.ts":{"131":[127],"158":[127],"172":[127]},"packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/v1/actions/image/generate.operation.ts":{"255":[127],"291":[127]},"packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/v1/actions/router.ts":{"16":[127]},"packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/v1/actions/text/classify.operation.ts":{"53":[127]},"packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/v1/actions/text/message.operation.ts":{"240":[127],"244":[127],"360":[127]},"packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/v2/OpenAiV2.node.ts":{"23":[127],"92":[127]},"packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/v2/actions/audio/generate.operation.ts":{"134":[127]},"packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/v2/actions/audio/transcribe.operation.ts":{"58":[127]},"packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/v2/actions/audio/translate.operation.ts":{"50":[127]},"packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/v2/actions/conversation/create.operation.ts":{"82":[127]},"packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/v2/actions/conversation/get.operation.ts":{"27":[127]},"packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/v2/actions/conversation/remove.operation.ts":{"27":[127]},"packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/v2/actions/conversation/update.operation.ts":{"34":[127]},"packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/v2/actions/descriptions.ts":{"3":[127]},"packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/v2/actions/file/deleteFile.operation.ts":{"51":[127]},"packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/v2/actions/file/list.operation.ts":{"62":[127],"72":[127]},"packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/v2/actions/file/upload.operation.ts":{"65":[127]},"packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/v2/actions/image/analyze.operation.ts":{"133":[127],"157":[127],"169":[127]},"packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/v2/actions/image/edit.operation.ts":{"584":[127],"618":[127],"695":[127]},"packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/v2/actions/image/generate.operation.ts":{"73":[127],"118":[127]},"packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/v2/actions/router.ts":{"17":[127]},"packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/v2/actions/text/classify.operation.ts":{"58":[127]},"packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/v2/actions/text/helpers/responses.ts":{"14":[127],"16":[127],"19":[127],"22":[127],"25":[127],"31":[127],"106":[127]},"packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/v2/actions/text/response.operation.ts":{"583":[127],"587":[127],"629":[127],"632":[127],"652":[127],"659":[127],"717":[127],"719":[127],"736":[127]},"packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/v2/actions/video/generate.operation.ts":{"92":[127],"132":[127],"135":[127]},"packages/cli/src/evaluation.ee/test-runs.controller.ee.ts":{"25":[127],"34":[127],"57":[127],"81":[127],"87":[127],"88":[127],"99":[127],"111":[127],"121":[127],"136":[127],"145":[127],"159":[127],"178":[127],"200":[127]},"packages/frontend/editor-ui/src/features/ai/evaluation.ee/composables/useWorkflowEvaluationState.ts":{"25":[127],"26":[127],"51":[127]},"localhost-43005/assets/_plugin-vue_export-helper-Da88TEg1.js":{"2":[6]},"localhost-43005/assets/chunk-CC9Q-vWm.js":{"8":[6],"9":[6],"10":[6],"19":[6],"23":[6],"29":[6],"33":[6],"34":[6]},"packages/frontend/@n8n/design-system/src/components/N8nIcon/custom/status-waiting.svg":{"8":[6]},"packages/frontend/editor-ui/src/features/ndv/runData/components/RunDataParsedAiContent.vue":{"31":[6],"33":[6],"54":[6],"63":[6],"72":[6],"76":[6]},"packages/frontend/editor-ui/src/features/ndv/runData/components/ai/utils.ts":{"38":[6],"51":[6],"52":[6],"53":[6]}}} \ No newline at end of file diff --git a/.github/workflows/release-create-minor-pr.yml b/.github/workflows/release-create-minor-pr.yml index 32a0011ba17..d8530beb537 100644 --- a/.github/workflows/release-create-minor-pr.yml +++ b/.github/workflows/release-create-minor-pr.yml @@ -2,12 +2,47 @@ name: 'Release: Create Minor Release PR' on: workflow_dispatch: + inputs: + force: + description: 'Create the release PR even if a minor release already went live this week.' + type: boolean + default: false # schedule: # - cron: 0 8 * * 2 # 9am CET (UTC+1), Tuesday +permissions: + contents: read + jobs: + check-cadence: + name: Check release cadence + runs-on: ubuntu-latest + steps: + # Guards against a misbehaving scheduler dispatching this workflow more than once a week. + - name: Fail if a minor release already went live this week + env: + GH_TOKEN: ${{ github.token }} + FORCE: ${{ inputs.force }} + run: | + if [ "$FORCE" = "true" ]; then + echo "Force input set, skipping cadence check." + exit 0 + fi + LAST_LIVE=$(gh release list --repo "$GITHUB_REPOSITORY" --limit 100 --json tagName,publishedAt \ + --jq '[.[] | select(.publishedAt != null and (.tagName | test("^n8n@[0-9]+\\.[0-9]+\\.0$")))] | max_by(.publishedAt).publishedAt // empty') + if [ -n "$LAST_LIVE" ] && [ "$(date -u -d "$LAST_LIVE" +%G-%V)" = "$(date -u +%G-%V)" ]; then + echo "::error::A minor release already went live this week ($LAST_LIVE). Re-run with the 'force' input to override." + exit 1 + fi + echo "No minor release this week yet (last went live: ${LAST_LIVE:-never}), proceeding." + create-release-pr: name: Create release PR + needs: [check-cadence] + # The called workflow requires these; a caller cannot grant less than it requests. + permissions: + contents: write + pull-requests: write uses: ./.github/workflows/release-create-pr.yml secrets: inherit with: diff --git a/.github/workflows/test-e2e-reusable.yml b/.github/workflows/test-e2e-reusable.yml index bfabbc5dc8d..8672ebccc1d 100644 --- a/.github/workflows/test-e2e-reusable.yml +++ b/.github/workflows/test-e2e-reusable.yml @@ -121,7 +121,33 @@ jobs: FILTERED=$(echo "$CANDIDATES" | node packages/testing/janitor/dist/cli.js filter-shard | tr '\n' ' ' | sed 's/ *$//') echo "specs=$FILTERED" >> "$GITHUB_OUTPUT" + - name: Resolve shard args + id: shard-args + env: + # On retry, prefer the filtered subset (failed specs from previous attempt) + RETRY_SPECS: ${{ steps.filter-shard.outputs.specs }} + MATRIX_SPECS: ${{ matrix.specs }} + # toJSON distinguishes an orchestrated-but-empty spec list ("") from a + # non-orchestrated matrix entry that never set `specs` (null). Only the + # latter may fall back to Playwright's own --shard splitting; an empty + # orchestrated list means "nothing to run", never "run everything". + MATRIX_SPECS_JSON: ${{ toJSON(matrix.specs) }} + SHARD: ${{ matrix.shard }} + JOB_TOTAL: ${{ strategy.job-total }} + run: | + if [ -n "$RETRY_SPECS" ]; then + echo "args=$RETRY_SPECS" >> "$GITHUB_OUTPUT" + elif [ "$MATRIX_SPECS_JSON" = "null" ]; then + echo "args=--shard=$SHARD/$JOB_TOTAL" >> "$GITHUB_OUTPUT" + elif [ -z "$MATRIX_SPECS" ]; then + echo "This shard's spec list is empty — nothing to run, skipping." + echo "skip=true" >> "$GITHUB_OUTPUT" + else + echo "args=$MATRIX_SPECS" >> "$GITHUB_OUTPUT" + fi + - name: Run Tests + if: steps.shard-args.outputs.skip != 'true' run: | # --pass-with-no-tests makes Playwright exit 0 when the selected specs # resolve to zero runnable tests. Covers the impact-map vs current-suite @@ -137,8 +163,7 @@ jobs: TEST_COMMAND: ${{ inputs.test-command }} # Uses pre-distributed specs if orchestration enabled, otherwise falls back to Playwright sharding WORKERS: ${{ env.PLAYWRIGHT_WORKERS }} - # On retry, prefer the filtered subset (failed specs from previous attempt) - SHARD_ARGS: ${{ steps.filter-shard.outputs.specs || matrix.specs || format('--shard={0}/{1}', matrix.shard, strategy.job-total) }} + SHARD_ARGS: ${{ steps.shard-args.outputs.args }} # Args for actual test command runner CURRENTS_RECORD_KEY: ${{ secrets.CURRENTS_RECORD_KEY }} QA_METRICS_WEBHOOK_URL: ${{ secrets.QA_METRICS_WEBHOOK_URL }} diff --git a/.github/workflows/test-evals-instance-ai.yml b/.github/workflows/test-evals-instance-ai.yml index ff2af9eaefe..458a672b969 100644 --- a/.github/workflows/test-evals-instance-ai.yml +++ b/.github/workflows/test-evals-instance-ai.yml @@ -164,6 +164,7 @@ jobs: - name: Start n8n containers env: EVALS_ANTHROPIC_KEY: ${{ secrets.EVALS_ANTHROPIC_KEY }} + INSTANCE_AI_BRAVE_SEARCH_API_KEY: ${{ secrets.INSTANCE_AI_BRAVE_SEARCH_API_KEY }} N8N_LICENSE_ACTIVATION_KEY: ${{ secrets.N8N_LICENSE_ACTIVATION_KEY }} N8N_LICENSE_CERT: ${{ secrets.N8N_LICENSE_CERT }} N8N_ENCRYPTION_KEY: ${{ secrets.N8N_ENCRYPTION_KEY }} @@ -224,6 +225,7 @@ jobs: -e N8N_ENABLED_MODULES=instance-ai \ -e N8N_AI_ENABLED=true \ -e N8N_INSTANCE_AI_MODEL_API_KEY="$EVALS_ANTHROPIC_KEY" \ + -e INSTANCE_AI_BRAVE_SEARCH_API_KEY="$INSTANCE_AI_BRAVE_SEARCH_API_KEY" \ -e N8N_AI_ASSISTANT_BASE_URL="" \ -e N8N_INSTANCE_AI_SANDBOX_ENABLED=true \ "${SANDBOX_ARGS[@]}" \ diff --git a/.github/workflows/test-evals-mcp.yml b/.github/workflows/test-evals-mcp.yml index 8a5dd61e057..058f853e780 100644 --- a/.github/workflows/test-evals-mcp.yml +++ b/.github/workflows/test-evals-mcp.yml @@ -188,11 +188,14 @@ jobs: # instance-ai serves the verifier; mcp + mcp-registry are default modules, # so MCP is available (the eval CLI enables the *setting* per lane after # reset). No sandbox — the builder is `claude`, not the in-n8n agent. + # agents registers the agent-builder MCP tools so the suite measures + # workflow building with the production tool surface, catching + # tool-choice regressions the extra tools could introduce. for i in "${!PORTS[@]}"; do port="${PORTS[$i]}" docker run -d --name "n8n-eval-mcp-$((i + 1))" \ -e E2E_TESTS=true \ - -e N8N_ENABLED_MODULES=instance-ai \ + -e N8N_ENABLED_MODULES=instance-ai,agents \ -e N8N_AI_ENABLED=true \ -e N8N_INSTANCE_AI_MODEL_API_KEY="$EVALS_ANTHROPIC_KEY" \ -e N8N_AI_ASSISTANT_BASE_URL="" \ diff --git a/.github/workflows/util-publish-api-schema.yml b/.github/workflows/util-publish-api-schema.yml new file mode 100644 index 00000000000..cbc40e1f1fc --- /dev/null +++ b/.github/workflows/util-publish-api-schema.yml @@ -0,0 +1,75 @@ +name: 'Util: Publish API Schema' + +on: + # Publish when the Public API source specs (or the bundler version) change + push: + branches: + - master + paths: + - 'packages/cli/src/public-api/**/*.{css,yaml,yml}' + # cli package.json catches a Redocly (bundler) version bump, the only + # other thing that changes the bundled output + - 'packages/cli/package.json' + + # Allow manual re-publish + workflow_dispatch: + +jobs: + publish-api-schema: + name: Cloudflare Pages + if: github.repository == 'n8n-io/n8n' + runs-on: blacksmith-2vcpu-ubuntu-2204 + permissions: + contents: read + deployments: write + + steps: + - name: Checkout + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + + - name: Setup Node.js and install dependencies + uses: ./.github/actions/setup-nodejs + with: + build-command: '' + + - name: Build Public API Schema + run: pnpm run build:data + working-directory: ./packages/cli + + - name: Stage schema for publishing + env: + SCHEMA_PATH: packages/cli/dist/public-api/v1/openapi.yml + run: | + if [[ ! -f "$SCHEMA_PATH" ]]; then + echo "::error::OpenAPI schema not found at $SCHEMA_PATH after build." + exit 1 + fi + + # Publish a clean directory holding only the schema, at a versioned + # path that mirrors the API's own /v1/ route + mkdir -p api-schema-publish/v1 + cp "$SCHEMA_PATH" api-schema-publish/v1/openapi.yml + + # text/yaml so browsers render it inline instead of downloading it + cat > api-schema-publish/_headers <<'EOF' + /v1/openapi.yml + Content-Type: text/yaml; charset=utf-8 + EOF + + # wrangler-action self-installs Wrangler with `pnpm add` at the workspace + # root, which pnpm rejects (ERR_PNPM_ADDING_TO_ROOT). Pre-install it and + # allow the root add so the action finds Wrangler and skips its own install. + - name: Setup Wrangler Pre-requisites + run: | + echo "ignore-workspace-root-check=true" >> .npmrc + pnpm add --global wrangler + + - name: Deploy to Cloudflare Pages + uses: cloudflare/wrangler-action@da0e0dfe58b7a431659754fdf3f186c529afbe65 # v3.14.1 + with: + apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }} + accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} + command: pages deploy api-schema-publish --project-name=n8n-openapi --branch=${{ github.ref_name }} --commit-hash=${{ github.sha }} + gitHubToken: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/util-sync-api-docs.yml b/.github/workflows/util-sync-api-docs.yml deleted file mode 100644 index d6ea87e1e1b..00000000000 --- a/.github/workflows/util-sync-api-docs.yml +++ /dev/null @@ -1,158 +0,0 @@ -name: 'Util: Sync API Docs' - -on: - # Triggers for the master branch if relevant Public API files have changed - push: - branches: - - master - paths: - # Trigger if: - # - any of the public API files change - - 'packages/cli/src/public-api/**/*.{css,yaml,yml}' - # - the build script or dependencies change - - 'packages/cli/package.json' - # - any main dependencies change - - 'pnpm-lock.yaml' - - # Allow manual trigger - workflow_dispatch: - -jobs: - sync-public-api: - runs-on: ubuntu-latest - permissions: - contents: read - actions: read # Needed for `gh` to read the workflow history - - steps: - - name: Checkout Main n8n Repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - fetch-depth: 0 # Fetch all history for git log - - - name: Setup Node.js and install dependencies - uses: ./.github/actions/setup-nodejs - with: - build-command: '' - - - name: Build Public API Schema - run: pnpm run build:data - working-directory: ./packages/cli - - - name: Verify OpenAPI schema exists - id: verify_file - run: | - if [[ -f "packages/cli/dist/public-api/v1/openapi.yml" ]]; then - echo "OpenAPI file found: packages/cli/dist/public-api/v1/openapi.yml" - echo "file_exists=true" >> "$GITHUB_OUTPUT" - else - echo "ERROR: OpenAPI file not found at packages/cli/dist/public-api/v1/openapi.yml after build." - echo "file_exists=false" >> "$GITHUB_OUTPUT" - fi - - - name: Generate GitHub App Token - if: steps.verify_file.outputs.file_exists == 'true' - 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 }} - owner: ${{ github.repository_owner }} - repositories: n8n-docs - - - name: Checkout Docs Repository - if: steps.verify_file.outputs.file_exists == 'true' - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - repository: n8n-io/n8n-docs - token: ${{ steps.generate_token.outputs.token }} - path: public-docs - - - name: Copy OpenAPI file to Docs Repo - if: steps.verify_file.outputs.file_exists == 'true' - run: | - # Destination path within the 'public-docs' checkout directory - DOCS_TARGET_PATH="public-docs/docs/api/v1/openapi.yml" - - echo "Copying 'packages/cli/dist/public-api/v1/openapi.yml' to '${DOCS_TARGET_PATH}'" - cp packages/cli/dist/public-api/v1/openapi.yml "${DOCS_TARGET_PATH}" - - - name: Find Relevant Source Commits - id: find_source_commits - if: steps.verify_file.outputs.file_exists == 'true' - env: - GH_TOKEN: ${{ github.token }} - GITHUB_REF: ${{ github.ref_name }} - run: | - echo "Finding last successful workflow run..." - LAST_SUCCESS_SHA=$(gh run list \ - --workflow "${{ github.workflow }}" \ - --branch "$GITHUB_REF" \ - --status "success" \ - --json "headSha" \ - --jq '.[0].headSha // empty' \ - -L 1) - - RELEVANT_COMMITS="" - if [[ -n "$LAST_SUCCESS_SHA" ]]; then - echo "Last successful commit: $LAST_SUCCESS_SHA" - echo "Current commit: ${{ github.sha }}" - - # Find all commits between the last success and now that touched the relevant files - # NOTE: The pathspecs here mirror the workflow's 'paths' trigger for precision - RELEVANT_COMMITS=$(git log --pretty=format:"- [%h](https://github.com/${{ github.repository }}/commit/%H) %s" "${LAST_SUCCESS_SHA}..${{ github.sha }}" -- \ - 'packages/cli/src/public-api/**/*.css' \ - 'packages/cli/src/public-api/**/*.yaml' \ - 'packages/cli/src/public-api/**/*.yml') - fi - - if [[ -z "$RELEVANT_COMMITS" ]]; then - if [[ -z "$LAST_SUCCESS_SHA" ]]; then - echo "No previous successful run found. Using current commit as source." - else - echo "No commits touching source files found. Linking to trigger commit (likely a dependency update)." - fi - FULL_SHA="${{ github.sha }}" - SHORT_SHA="${FULL_SHA:0:7}" - SOURCE_LINK="Source commit: [$SHORT_SHA](https://github.com/${{ github.repository }}/commit/$FULL_SHA)" - else - echo "Found relevant source commits:" - echo "$RELEVANT_COMMITS" - # Build the string manually to avoid heredoc capturing leading whitespace from YAML - SOURCE_LINK="Source commit(s):"$'\n'"$RELEVANT_COMMITS" - fi - - { - echo "source_link<> "$GITHUB_OUTPUT" - - - name: Create PR in Docs Repo - if: steps.verify_file.outputs.file_exists == 'true' - env: - GH_ACTOR: ${{ github.actor }} - - # Pin v7.0.8 - uses: peter-evans/create-pull-request@c0f553fe549906ede9cf27b5156039d195d2ece0 # v8.1.0 - with: - token: ${{ steps.generate_token.outputs.token }} - - path: public-docs - commit-message: 'feat(public-api): Update Public API schema' - committer: GitHub - author: ${{ env.GH_ACTOR }} <${{ github.actor_id }}+${{ env.GH_ACTOR }}@users.noreply.github.com> - signoff: false - - # Create a single branch for multiple PRs - branch: 'chore/sync-public-api-schema' - delete-branch: false - - title: 'chore: Update Public API schema' - body: | - Automated update of the Public API OpenAPI YAML schema. - - This PR was generated by a GitHub Action in the [${{ github.repository }} repository](https://github.com/${{ github.repository }}). - ${{ steps.find_source_commits.outputs.source_link }} - - Please review the changes and merge if appropriate. diff --git a/cubic.yaml b/cubic.yaml index db0a5df7886..b7c2a92db46 100644 --- a/cubic.yaml +++ b/cubic.yaml @@ -327,6 +327,46 @@ reviews: `@/app/stores/workflowDocument.store.ts` to access migrated fields. This is an active migration. The list of migrated fields will grow over time. + - name: Use the Shared sleep Utility + description: |- + - Rule Statement: + Code must ALWAYS use `sleep` from `@n8n/utils/sleep` instead of + hand-rolling a helper that only wraps `setTimeout` in a promise. One + canonical helper keeps call sites consistent and makes its abortable + form discoverable — `sleep(ms, abortSignal?)` rejects early when the + signal fires, so a local abort-aware waiter is a violation too. + + The `no-restricted-sleep-definition` and `no-restricted-sleep-import` + ESLint rules already catch helpers literally named `sleep` / + `sleepWithAbort` and named `sleep` imports from `n8n-workflow`. Do not + repeat those as review comments — this rule covers only what they + cannot see syntactically. + + - Detection Criteria: + Flag NEW or MODIFIED code in `packages/**` where a delay is + re-implemented under a name ESLint does not restrict (`wait`, `delay`, + `pause`, `waitFor`, …) and the entire body is a promise wrapping + `setTimeout` with no additional behaviour: + - `const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms))` + - `async function wait(ms) { await new Promise((r) => setTimeout(r, ms)); }` + The shape is what matters, not the name. Also flag an inline, awaited + `new Promise((resolve) => setTimeout(resolve, ms))`. + + - Do NOT flag: + - Wrappers that do more than wait: racing against another promise, + rejecting with a timeout error once the delay elapses, retry + backoff with jitter, debounce/throttle. (Rejecting on *abort* is a + violation — `sleep` takes the signal.) + - Bare `setTimeout` calls used for scheduling, not awaited as a delay + - Test-framework helpers such as `vi.advanceTimersByTime` or waiting + on fake timers + - `packages/@n8n/node-cli/**` and `packages/@n8n/typeorm/**`, which + cannot depend on `@n8n/utils` and are exempt from the ESLint rules + - Existing unchanged code (review only new or modified lines) + + - Recommendation: + Import `sleep` from `@n8n/utils/sleep` — `await sleep(500)`, or + `await sleep(500, abortSignal)` when the wait must be cancellable. pr_descriptions: generate: false instructions: Each PR is supposed to have a limited scope. In your review, focus on changes made in the PR and avoid pointing out problems you found in the code that already existed. diff --git a/docs/generated/postgres-schema/README.md b/docs/generated/postgres-schema/README.md index 7542cba72b1..64925872839 100644 --- a/docs/generated/postgres-schema/README.md +++ b/docs/generated/postgres-schema/README.md @@ -8,13 +8,14 @@ Auto-generated from the PostgreSQL migrations in @n8n/db. Do not edit by hand. | Name | Columns | Comment | Type | | ---- | ------- | ------- | ---- | +| [public.agent_chat_attachments](public.agent_chat_attachments.md) | 12 | | BASE TABLE | | [public.agent_chat_subscriptions](public.agent_chat_subscriptions.md) | 6 | | BASE TABLE | | [public.agent_checkpoints](public.agent_checkpoints.md) | 6 | | BASE TABLE | | [public.agent_eval_dataset](public.agent_eval_dataset.md) | 10 | | BASE TABLE | | [public.agent_eval_rating](public.agent_eval_rating.md) | 8 | | BASE TABLE | | [public.agent_eval_result](public.agent_eval_result.md) | 15 | | BASE TABLE | | [public.agent_eval_run](public.agent_eval_run.md) | 14 | | BASE TABLE | -| [public.agent_execution](public.agent_execution.md) | 19 | | BASE TABLE | +| [public.agent_execution](public.agent_execution.md) | 20 | | BASE TABLE | | [public.agent_execution_threads](public.agent_execution_threads.md) | 17 | | BASE TABLE | | [public.agent_files](public.agent_files.md) | 10 | | BASE TABLE | | [public.agent_history](public.agent_history.md) | 9 | | BASE TABLE | @@ -98,8 +99,8 @@ Auto-generated from the PostgreSQL migrations in @n8n/db. Do not edit by hand. | [public.role_mapping_rule](public.role_mapping_rule.md) | 7 | | BASE TABLE | | [public.role_mapping_rule_project](public.role_mapping_rule_project.md) | 2 | | BASE TABLE | | [public.role_scope](public.role_scope.md) | 2 | | BASE TABLE | -| [public.scheduled_job](public.scheduled_job.md) | 19 | | BASE TABLE | -| [public.scheduled_task](public.scheduled_task.md) | 17 | | BASE TABLE | +| [public.scheduled_job](public.scheduled_job.md) | 21 | | BASE TABLE | +| [public.scheduled_task](public.scheduled_task.md) | 18 | | BASE TABLE | | [public.scope](public.scope.md) | 3 | | BASE TABLE | | [public.secrets_provider_connection](public.secrets_provider_connection.md) | 7 | | BASE TABLE | | [public.settings](public.settings.md) | 3 | | BASE TABLE | @@ -153,6 +154,8 @@ Auto-generated from the PostgreSQL migrations in @n8n/db. Do not edit by hand. ```mermaid erDiagram +"public.agent_chat_attachments" }o--|| "public.project" : "FOREIGN KEY (#quot;projectId#quot;) REFERENCES project(id) ON DELETE CASCADE" +"public.agent_chat_attachments" }o--o| "public.agents" : "FOREIGN KEY (#quot;agentId#quot;) REFERENCES agents(id) ON DELETE CASCADE" "public.agent_chat_subscriptions" }o--|| "public.agents" : "FOREIGN KEY (#quot;agentId#quot;) REFERENCES agents(id) ON DELETE CASCADE" "public.agent_checkpoints" }o--o| "public.agents" : "FOREIGN KEY (#quot;agentId#quot;) REFERENCES agents(id) ON DELETE CASCADE" "public.agent_eval_dataset" }o--o| "public.user" : "FOREIGN KEY (#quot;createdById#quot;) REFERENCES #quot;user#quot;(id) ON DELETE SET NULL" @@ -328,6 +331,20 @@ erDiagram "public.workflows_tags" }o--|| "public.workflow_entity" : "FOREIGN KEY (#quot;workflowId#quot;) REFERENCES workflow_entity(id) ON DELETE CASCADE" "public.workflows_tags" }o--|| "public.tag_entity" : "FOREIGN KEY (#quot;tagId#quot;) REFERENCES tag_entity(id) ON DELETE CASCADE" +"public.agent_chat_attachments" { + varchar_36_ agentId FK + text binaryDataId + timestamp_3__with_time_zone createdAt + varchar_255_ fileName + integer fileSizeBytes + varchar_16_ id + varchar_255_ mimeType + varchar_36_ projectId FK + varchar_255_ resourceId + varchar_32_ source + varchar_128_ threadId + timestamp_3__with_time_zone updatedAt +} "public.agent_chat_subscriptions" { varchar_36_ agentId FK timestamp_3__with_time_zone createdAt @@ -400,6 +417,7 @@ erDiagram timestamp_3__with_time_zone updatedAt } "public.agent_execution" { + json attachments integer completionTokens double_precision cost timestamp_3__with_time_zone createdAt @@ -1213,6 +1231,8 @@ erDiagram varchar_16_ kind timestamp_3__with_time_zone lastFiredAt integer maxAttempts + integer misfireGraceSeconds + varchar_16_ misfirePolicy varchar_255_ name timestamp_3__with_time_zone nextRunAt varchar_36_ nodeId @@ -1236,6 +1256,7 @@ erDiagram integer leaseEpoch timestamp_3__with_time_zone leaseExpiresAt integer maxAttempts + timestamp_3__with_time_zone missedAfter json payload timestamp_3__with_time_zone runAt timestamp_3__with_time_zone scheduledFor diff --git a/docs/generated/postgres-schema/public.agent_chat_attachments.md b/docs/generated/postgres-schema/public.agent_chat_attachments.md new file mode 100644 index 00000000000..24208d477b7 --- /dev/null +++ b/docs/generated/postgres-schema/public.agent_chat_attachments.md @@ -0,0 +1,97 @@ +# public.agent_chat_attachments + +## Columns + +| Name | Type | Default | Nullable | Children | Parents | Comment | +| ---- | ---- | ------- | -------- | -------- | ------- | ------- | +| agentId | varchar(36) | | true | | [public.agents](public.agents.md) | Agent the attachment was sent to, when persisted (null for inline agents) | +| binaryDataId | text | | false | | | Opaque BinaryDataService reference (mode-prefixed, e.g. "filesystem-v2:\"); not an FK to binary_data, which only has rows in DB storage mode | +| createdAt | timestamp(3) with time zone | CURRENT_TIMESTAMP(3) | false | | | | +| fileName | varchar(255) | | false | | | | +| fileSizeBytes | integer | | false | | | Uploaded file size in bytes | +| id | varchar(16) | | false | | | Application-generated n8n nano ID | +| mimeType | varchar(255) | | false | | | | +| projectId | varchar(36) | | false | | [public.project](public.project.md) | Project owning the conversation; authorization scope for downloads | +| resourceId | varchar(255) | | true | | | Per-user scope within the thread (platform user), when known | +| source | varchar(32) | | false | | | Surface the file arrived from, e.g. "chat", "slack", "telegram" | +| threadId | varchar(128) | | false | | | Conversation thread the file belongs to | +| updatedAt | timestamp(3) with time zone | CURRENT_TIMESTAMP(3) | false | | | | + +## Constraints + +| Name | Type | Definition | +| ---- | ---- | ---------- | +| FK_41c246c30af3d2108d93e15dd5e | FOREIGN KEY | FOREIGN KEY ("agentId") REFERENCES agents(id) ON DELETE CASCADE | +| FK_5c048535b2132b40dfed2e640e5 | FOREIGN KEY | FOREIGN KEY ("projectId") REFERENCES project(id) ON DELETE CASCADE | +| PK_9c1250f77b41d1a0e71a3f1414a | PRIMARY KEY | PRIMARY KEY (id) | +| agent_chat_attachments_binaryDataId_not_null | n | NOT NULL "binaryDataId" | +| agent_chat_attachments_createdAt_not_null | n | NOT NULL "createdAt" | +| agent_chat_attachments_fileName_not_null | n | NOT NULL "fileName" | +| agent_chat_attachments_fileSizeBytes_not_null | n | NOT NULL "fileSizeBytes" | +| agent_chat_attachments_id_not_null | n | NOT NULL id | +| agent_chat_attachments_mimeType_not_null | n | NOT NULL "mimeType" | +| agent_chat_attachments_projectId_not_null | n | NOT NULL "projectId" | +| agent_chat_attachments_source_not_null | n | NOT NULL source | +| agent_chat_attachments_threadId_not_null | n | NOT NULL "threadId" | +| agent_chat_attachments_updatedAt_not_null | n | NOT NULL "updatedAt" | + +## Indexes + +| Name | Definition | +| ---- | ---------- | +| IDX_6806b43c8a0d11460f0c2552eb | CREATE INDEX "IDX_6806b43c8a0d11460f0c2552eb" ON public.agent_chat_attachments USING btree ("projectId", "threadId") | +| IDX_7e9556f25f995653997be3322b | CREATE INDEX "IDX_7e9556f25f995653997be3322b" ON public.agent_chat_attachments USING btree ("agentId", "threadId") | +| PK_9c1250f77b41d1a0e71a3f1414a | CREATE UNIQUE INDEX "PK_9c1250f77b41d1a0e71a3f1414a" ON public.agent_chat_attachments USING btree (id) | + +## Relations + +```mermaid +erDiagram + +"public.agent_chat_attachments" }o--o| "public.agents" : "FOREIGN KEY (#quot;agentId#quot;) REFERENCES agents(id) ON DELETE CASCADE" +"public.agent_chat_attachments" }o--|| "public.project" : "FOREIGN KEY (#quot;projectId#quot;) REFERENCES project(id) ON DELETE CASCADE" + +"public.agent_chat_attachments" { + varchar_36_ agentId FK + text binaryDataId + timestamp_3__with_time_zone createdAt + varchar_255_ fileName + integer fileSizeBytes + varchar_16_ id + varchar_255_ mimeType + varchar_36_ projectId FK + varchar_255_ resourceId + varchar_32_ source + varchar_128_ threadId + 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 + json schema + json skills + json tools + timestamp_3__with_time_zone updatedAt + varchar_36_ versionId +} +"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) diff --git a/docs/generated/postgres-schema/public.agent_execution.md b/docs/generated/postgres-schema/public.agent_execution.md index 6b81ef2eefa..b38c897c0ba 100644 --- a/docs/generated/postgres-schema/public.agent_execution.md +++ b/docs/generated/postgres-schema/public.agent_execution.md @@ -4,6 +4,7 @@ | Name | Type | Default | Nullable | Children | Parents | Comment | | ---- | ---- | ------- | -------- | -------- | ------- | ------- | +| attachments | json | | true | | | Metadata of files attached to the user turn ({id, fileName, mimeType, sizeBytes}[]); bytes live in BinaryDataService | | completionTokens | integer | | true | | | | | cost | double precision | | true | | | | | createdAt | timestamp(3) with time zone | CURRENT_TIMESTAMP(3) | false | | | | @@ -56,6 +57,7 @@ erDiagram "public.agent_execution" }o--|| "public.agent_execution_threads" : "FOREIGN KEY (#quot;threadId#quot;) REFERENCES agent_execution_threads(id) ON DELETE CASCADE" "public.agent_execution" { + json attachments integer completionTokens double_precision cost timestamp_3__with_time_zone createdAt diff --git a/docs/generated/postgres-schema/public.agent_execution_threads.md b/docs/generated/postgres-schema/public.agent_execution_threads.md index a3d116befb7..7a115ab518a 100644 --- a/docs/generated/postgres-schema/public.agent_execution_threads.md +++ b/docs/generated/postgres-schema/public.agent_execution_threads.md @@ -95,6 +95,7 @@ erDiagram varchar_36_ versionId } "public.agent_execution" { + json attachments integer completionTokens double_precision cost timestamp_3__with_time_zone createdAt diff --git a/docs/generated/postgres-schema/public.agents.md b/docs/generated/postgres-schema/public.agents.md index 3e49fbeefad..4eecd4a2438 100644 --- a/docs/generated/postgres-schema/public.agents.md +++ b/docs/generated/postgres-schema/public.agents.md @@ -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_chat_subscriptions](public.agent_chat_subscriptions.md) [public.agent_checkpoints](public.agent_checkpoints.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.agents_memory_entries](public.agents_memory_entries.md) [public.agents_memory_entry_cursors](public.agents_memory_entry_cursors.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_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_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.agents_memory_entries](public.agents_memory_entries.md) [public.agents_memory_entry_cursors](public.agents_memory_entry_cursors.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) | | | | integrations | json | '[]'::json | false | | | | | name | varchar(128) | | false | | | | | projectId | varchar(255) | | false | | [public.project](public.project.md) | | @@ -48,6 +48,7 @@ erDiagram "public.agents" }o--o| "public.agent_history" : "FOREIGN KEY (#quot;activeVersionId#quot;) REFERENCES agent_history(#quot;versionId#quot;) ON DELETE SET NULL" +"public.agent_chat_attachments" }o--o| "public.agents" : "FOREIGN KEY (#quot;agentId#quot;) REFERENCES agents(id) ON DELETE CASCADE" "public.agent_chat_subscriptions" }o--|| "public.agents" : "FOREIGN KEY (#quot;agentId#quot;) REFERENCES agents(id) ON DELETE CASCADE" "public.agent_checkpoints" }o--o| "public.agents" : "FOREIGN KEY (#quot;agentId#quot;) REFERENCES agents(id) ON DELETE CASCADE" "public.agent_eval_dataset" }o--|| "public.agents" : "FOREIGN KEY (#quot;agentId#quot;) REFERENCES agents(id) ON DELETE CASCADE" @@ -90,6 +91,20 @@ erDiagram timestamp_3__with_time_zone updatedAt varchar_36_ versionId } +"public.agent_chat_attachments" { + varchar_36_ agentId FK + text binaryDataId + timestamp_3__with_time_zone createdAt + varchar_255_ fileName + integer fileSizeBytes + varchar_16_ id + varchar_255_ mimeType + varchar_36_ projectId FK + varchar_255_ resourceId + varchar_32_ source + varchar_128_ threadId + timestamp_3__with_time_zone updatedAt +} "public.agent_chat_subscriptions" { varchar_36_ agentId FK timestamp_3__with_time_zone createdAt diff --git a/docs/generated/postgres-schema/public.binary_data.md b/docs/generated/postgres-schema/public.binary_data.md index 22d62e04692..3fafc22761e 100644 --- a/docs/generated/postgres-schema/public.binary_data.md +++ b/docs/generated/postgres-schema/public.binary_data.md @@ -18,7 +18,7 @@ | Name | Type | Definition | | ---- | ---- | ---------- | -| CHK_binary_data_sourceType | CHECK | CHECK ((("sourceType")::text = ANY ((ARRAY['execution'::character varying, 'chat_message_attachment'::character varying, 'agent_file'::character varying])::text[]))) | +| CHK_binary_data_sourceType | CHECK | CHECK ((("sourceType")::text = ANY ((ARRAY['execution'::character varying, 'chat_message_attachment'::character varying, 'agent_file'::character varying, 'agent_chat_attachment'::character varying])::text[]))) | | PK_fc3691585b39408bb0551122af6 | PRIMARY KEY | PRIMARY KEY ("fileId") | | binary_data_createdAt_not_null | n | NOT NULL "createdAt" | | binary_data_data_not_null | n | NOT NULL data | diff --git a/docs/generated/postgres-schema/public.project.md b/docs/generated/postgres-schema/public.project.md index a626e6d4dfb..50ad418db81 100644 --- a/docs/generated/postgres-schema/public.project.md +++ b/docs/generated/postgres-schema/public.project.md @@ -9,7 +9,7 @@ | customTelemetryTags | json | '[]'::json | false | | | | | description | varchar(512) | | true | | | | | icon | json | | true | | | | -| id | varchar(36) | | false | [public.agent_execution_threads](public.agent_execution_threads.md) [public.agents](public.agents.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_relation](public.project_relation.md) [public.project_secrets_provider_access](public.project_secrets_provider_access.md) [public.role_mapping_rule_project](public.role_mapping_rule_project.md) [public.shared_credentials](public.shared_credentials.md) [public.shared_workflow](public.shared_workflow.md) [public.variables](public.variables.md) [public.workflow_review_request](public.workflow_review_request.md) | | | +| id | varchar(36) | | false | [public.agent_chat_attachments](public.agent_chat_attachments.md) [public.agent_execution_threads](public.agent_execution_threads.md) [public.agents](public.agents.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_relation](public.project_relation.md) [public.project_secrets_provider_access](public.project_secrets_provider_access.md) [public.role_mapping_rule_project](public.role_mapping_rule_project.md) [public.shared_credentials](public.shared_credentials.md) [public.shared_workflow](public.shared_workflow.md) [public.variables](public.variables.md) [public.workflow_review_request](public.workflow_review_request.md) | | | | name | varchar(255) | | false | | | | | type | varchar(36) | | false | | | | | updatedAt | timestamp(3) with time zone | CURRENT_TIMESTAMP(3) | false | | | | @@ -39,6 +39,7 @@ erDiagram "public.project" }o--o| "public.user" : "FOREIGN KEY (#quot;creatorId#quot;) REFERENCES #quot;user#quot;(id) ON DELETE SET NULL" +"public.agent_chat_attachments" }o--|| "public.project" : "FOREIGN KEY (#quot;projectId#quot;) REFERENCES project(id) ON DELETE CASCADE" "public.agent_execution_threads" }o--|| "public.project" : "FOREIGN KEY (#quot;projectId#quot;) REFERENCES project(id) ON DELETE CASCADE" "public.agents" }o--|| "public.project" : "FOREIGN KEY (#quot;projectId#quot;) REFERENCES project(id) ON DELETE CASCADE" "public.data_table" }o--|| "public.project" : "FOREIGN KEY (#quot;projectId#quot;) REFERENCES project(id) ON DELETE CASCADE" @@ -81,6 +82,20 @@ erDiagram json settings timestamp_3__with_time_zone updatedAt } +"public.agent_chat_attachments" { + varchar_36_ agentId FK + text binaryDataId + timestamp_3__with_time_zone createdAt + varchar_255_ fileName + integer fileSizeBytes + varchar_16_ id + varchar_255_ mimeType + varchar_36_ projectId FK + varchar_255_ resourceId + varchar_32_ source + varchar_128_ threadId + timestamp_3__with_time_zone updatedAt +} "public.agent_execution_threads" { varchar_36_ agentId FK varchar_255_ agentName diff --git a/docs/generated/postgres-schema/public.scheduled_job.md b/docs/generated/postgres-schema/public.scheduled_job.md index 8dfaa7b10b3..fc1af47421b 100644 --- a/docs/generated/postgres-schema/public.scheduled_job.md +++ b/docs/generated/postgres-schema/public.scheduled_job.md @@ -13,6 +13,8 @@ | kind | varchar(16) | | false | | | Recurrence kind; selects which of the schedule columns below apply. | | lastFiredAt | timestamp(3) with time zone | | true | | | Last time an occurrence was materialized; used to recompute nextRunAt. | | maxAttempts | integer | 1 | false | | | Retry ceiling copied onto each occurrence this job materializes. | +| misfireGraceSeconds | integer | 60 | false | | | How late an occurrence may be before the misfire policy applies to it; an ordinary restart stays inside it. | +| misfirePolicy | varchar(16) | 'coalesce'::character varying | false | | | What to do with occurrences that came due while nothing ran them: 'coalesce' records a single catch-up run, 'skip' records none. | | name | varchar(255) | | false | | | Human-readable job name. A well-known scheduler key for system jobs; generated for workflow trigger jobs. | | nextRunAt | timestamp(3) with time zone | | true | | | Next time an occurrence is due; the scheduler sweep reads this to find work. NULL once disabled or a one-off has fired. | | nodeId | varchar(36) | | true | | | Trigger node within the workflow that owns this job; NULL for non-trigger jobs. | @@ -32,6 +34,8 @@ | CHK_scheduled_job_fire_at | CHECK | CHECK ((((kind)::text <> 'one_off'::text) OR ("fireAt" IS NOT NULL))) | | CHK_scheduled_job_interval_seconds | CHECK | CHECK ((((kind)::text <> 'interval'::text) OR ("intervalSeconds" IS NOT NULL))) | | CHK_scheduled_job_kind | CHECK | CHECK (((kind)::text = ANY ((ARRAY['cron'::character varying, 'interval'::character varying, 'one_off'::character varying, 'recurring_cron'::character varying])::text[]))) | +| CHK_scheduled_job_misfireGraceSeconds | CHECK | CHECK (("misfireGraceSeconds" > 0)) | +| CHK_scheduled_job_misfirePolicy | CHECK | CHECK ((("misfirePolicy")::text = ANY ((ARRAY['coalesce'::character varying, 'skip'::character varying])::text[]))) | | CHK_scheduled_job_recurrence_size | CHECK | CHECK (("recurrenceSize" >= 2)) | | CHK_scheduled_job_recurrence_unit | CHECK | CHECK ((("recurrenceUnit")::text = ANY ((ARRAY['hours'::character varying, 'days'::character varying, 'weeks'::character varying, 'months'::character varying])::text[]))) | | CHK_scheduled_job_recurring_cron | CHECK | CHECK ((((kind)::text <> 'recurring_cron'::text) OR (("cronExpression" IS NOT NULL) AND ("recurrenceUnit" IS NOT NULL) AND ("recurrenceSize" IS NOT NULL)))) | @@ -42,6 +46,8 @@ | scheduled_job_id_not_null | n | NOT NULL id | | scheduled_job_kind_not_null | n | NOT NULL kind | | scheduled_job_maxAttempts_not_null | n | NOT NULL "maxAttempts" | +| scheduled_job_misfireGraceSeconds_not_null | n | NOT NULL "misfireGraceSeconds" | +| scheduled_job_misfirePolicy_not_null | n | NOT NULL "misfirePolicy" | | scheduled_job_name_not_null | n | NOT NULL name | | scheduled_job_payload_not_null | n | NOT NULL payload | | scheduled_job_taskType_not_null | n | NOT NULL "taskType" | @@ -74,6 +80,8 @@ erDiagram varchar_16_ kind timestamp_3__with_time_zone lastFiredAt integer maxAttempts + integer misfireGraceSeconds + varchar_16_ misfirePolicy varchar_255_ name timestamp_3__with_time_zone nextRunAt varchar_36_ nodeId @@ -97,6 +105,7 @@ erDiagram integer leaseEpoch timestamp_3__with_time_zone leaseExpiresAt integer maxAttempts + timestamp_3__with_time_zone missedAfter json payload timestamp_3__with_time_zone runAt timestamp_3__with_time_zone scheduledFor diff --git a/docs/generated/postgres-schema/public.scheduled_task.md b/docs/generated/postgres-schema/public.scheduled_task.md index ed66c682130..0f941a36c7f 100644 --- a/docs/generated/postgres-schema/public.scheduled_task.md +++ b/docs/generated/postgres-schema/public.scheduled_task.md @@ -15,6 +15,7 @@ | leaseEpoch | integer | 0 | false | | | Fencing token bumped on each claim; lets a reaped worker detect it lost ownership and not overwrite the new owner's results. | | leaseExpiresAt | timestamp(3) with time zone | | true | | | When the current lease expires; the reaper reclaims running occurrences past this. | | maxAttempts | integer | 1 | false | | | Attempt ceiling; once attempts reaches it, a failure is final rather than retried. | +| missedAfter | timestamp(3) with time zone | | true | | | When this occurrence stops being worth running (runAt plus the job's misfire grace). NULL means no deadline. | | payload | json | '{}'::json | false | | | Handler input copied from the job. A snapshot, so editing the job later doesn't change runs already queued. | | runAt | timestamp(3) with time zone | | false | | | Earliest time the executor may pick this up; starts at scheduledFor and is pushed out by retry backoff. | | scheduledFor | timestamp(3) with time zone | | false | | | The logical fire time this occurrence represents; unique per job, so the same instant cannot be queued twice. | @@ -49,6 +50,7 @@ | IDX_scheduled_task_finishedAt | CREATE INDEX "IDX_scheduled_task_finishedAt" ON public.scheduled_task USING btree ("finishedAt") WHERE ("finishedAt" IS NOT NULL) | | IDX_scheduled_task_jobId_scheduledFor | CREATE UNIQUE INDEX "IDX_scheduled_task_jobId_scheduledFor" ON public.scheduled_task USING btree ("jobId", "scheduledFor") | | IDX_scheduled_task_leaseExpiresAt | CREATE INDEX "IDX_scheduled_task_leaseExpiresAt" ON public.scheduled_task USING btree ("leaseExpiresAt") WHERE ((status)::text = 'running'::text) | +| IDX_scheduled_task_missedAfter | CREATE INDEX "IDX_scheduled_task_missedAfter" ON public.scheduled_task USING btree ("missedAfter") WHERE (((status)::text = 'pending'::text) AND ("missedAfter" IS NOT NULL)) | | IDX_scheduled_task_runAt | CREATE INDEX "IDX_scheduled_task_runAt" ON public.scheduled_task USING btree ("runAt") WHERE ((status)::text = 'pending'::text) | | PK_d690af24e57e30594c1948af1e6 | CREATE UNIQUE INDEX "PK_d690af24e57e30594c1948af1e6" ON public.scheduled_task USING btree (id) | @@ -71,6 +73,7 @@ erDiagram integer leaseEpoch timestamp_3__with_time_zone leaseExpiresAt integer maxAttempts + timestamp_3__with_time_zone missedAfter json payload timestamp_3__with_time_zone runAt timestamp_3__with_time_zone scheduledFor @@ -88,6 +91,8 @@ erDiagram varchar_16_ kind timestamp_3__with_time_zone lastFiredAt integer maxAttempts + integer misfireGraceSeconds + varchar_16_ misfirePolicy varchar_255_ name timestamp_3__with_time_zone nextRunAt varchar_36_ nodeId diff --git a/docs/generated/postgres-schema/public.workflow_published_version.md b/docs/generated/postgres-schema/public.workflow_published_version.md index a8ae6e9756a..d4a9d538fed 100644 --- a/docs/generated/postgres-schema/public.workflow_published_version.md +++ b/docs/generated/postgres-schema/public.workflow_published_version.md @@ -87,6 +87,8 @@ erDiagram varchar_16_ kind timestamp_3__with_time_zone lastFiredAt integer maxAttempts + integer misfireGraceSeconds + varchar_16_ misfirePolicy varchar_255_ name timestamp_3__with_time_zone nextRunAt varchar_36_ nodeId diff --git a/docs/generated/sqlite-schema/README.md b/docs/generated/sqlite-schema/README.md index 2a472176eed..9b2bab56887 100644 --- a/docs/generated/sqlite-schema/README.md +++ b/docs/generated/sqlite-schema/README.md @@ -8,13 +8,14 @@ Auto-generated from the SQLite migrations in @n8n/db. Do not edit by hand. | Name | Columns | Comment | Type | | ---- | ------- | ------- | ---- | +| [agent_chat_attachments](agent_chat_attachments.md) | 12 | | table | | [agent_chat_subscriptions](agent_chat_subscriptions.md) | 6 | | table | | [agent_checkpoints](agent_checkpoints.md) | 6 | | table | | [agent_eval_dataset](agent_eval_dataset.md) | 10 | | table | | [agent_eval_rating](agent_eval_rating.md) | 8 | | table | | [agent_eval_result](agent_eval_result.md) | 15 | | table | | [agent_eval_run](agent_eval_run.md) | 14 | | table | -| [agent_execution](agent_execution.md) | 19 | | table | +| [agent_execution](agent_execution.md) | 20 | | table | | [agent_execution_threads](agent_execution_threads.md) | 17 | | table | | [agent_files](agent_files.md) | 10 | | table | | [agent_history](agent_history.md) | 9 | | table | @@ -98,8 +99,8 @@ Auto-generated from the SQLite migrations in @n8n/db. Do not edit by hand. | [role_mapping_rule](role_mapping_rule.md) | 7 | | table | | [role_mapping_rule_project](role_mapping_rule_project.md) | 2 | | table | | [role_scope](role_scope.md) | 2 | | table | -| [scheduled_job](scheduled_job.md) | 19 | | table | -| [scheduled_task](scheduled_task.md) | 17 | | table | +| [scheduled_job](scheduled_job.md) | 21 | | table | +| [scheduled_task](scheduled_task.md) | 18 | | table | | [scope](scope.md) | 3 | | table | | [secrets_provider_connection](secrets_provider_connection.md) | 7 | | table | | [settings](settings.md) | 3 | | table | @@ -136,6 +137,8 @@ Auto-generated from the SQLite migrations in @n8n/db. Do not edit by hand. ```mermaid erDiagram +"agent_chat_attachments" }o--|| "project" : "FOREIGN KEY (projectId) REFERENCES project (id) ON UPDATE NO ACTION ON DELETE CASCADE MATCH NONE" +"agent_chat_attachments" }o--o| "agents" : "FOREIGN KEY (agentId) REFERENCES agents (id) ON UPDATE NO ACTION ON DELETE CASCADE MATCH NONE" "agent_chat_subscriptions" |o--|| "agents" : "FOREIGN KEY (agentId) REFERENCES agents (id) ON UPDATE NO ACTION ON DELETE CASCADE MATCH NONE" "agent_checkpoints" }o--o| "agents" : "FOREIGN KEY (agentId) REFERENCES agents (id) ON UPDATE NO ACTION ON DELETE CASCADE MATCH NONE" "agent_eval_dataset" }o--o| "user" : "FOREIGN KEY (createdById) REFERENCES user (id) ON UPDATE NO ACTION ON DELETE SET NULL MATCH NONE" @@ -315,6 +318,20 @@ erDiagram "workflows_tags" |o--|| "tag_entity" : "FOREIGN KEY (tagId) REFERENCES tag_entity (id) ON UPDATE NO ACTION ON DELETE CASCADE MATCH NONE" "workflows_tags" |o--|| "workflow_entity" : "FOREIGN KEY (workflowId) REFERENCES workflow_entity (id) ON UPDATE NO ACTION ON DELETE CASCADE MATCH NONE" +"agent_chat_attachments" { + varchar_36_ agentId FK + TEXT binaryDataId + datetime_3_ createdAt + varchar_255_ fileName + INTEGER fileSizeBytes + varchar_16_ id PK + varchar_255_ mimeType + varchar_36_ projectId FK + varchar_255_ resourceId + varchar_32_ source + varchar_128_ threadId + datetime_3_ updatedAt +} "agent_chat_subscriptions" { varchar_36_ agentId PK datetime_3_ createdAt @@ -387,6 +404,7 @@ erDiagram datetime_3_ updatedAt } "agent_execution" { + TEXT attachments INTEGER completionTokens REAL cost datetime_3_ createdAt @@ -400,7 +418,7 @@ erDiagram datetime_3_ startedAt varchar_16_ status datetime_3_ stoppedAt - VARCHAR_2_ storedAt + varchar_2_ storedAt varchar_128_ threadId FK TEXT timeline INTEGER totalTokens @@ -1200,6 +1218,8 @@ erDiagram varchar_16_ kind datetime_3_ lastFiredAt INTEGER maxAttempts + INT misfireGraceSeconds + varchar_16_ misfirePolicy varchar_255_ name datetime_3_ nextRunAt varchar_36_ nodeId @@ -1223,6 +1243,7 @@ erDiagram INTEGER leaseEpoch datetime_3_ leaseExpiresAt INTEGER maxAttempts + datetime_3_ missedAfter TEXT payload datetime_3_ runAt datetime_3_ scheduledFor diff --git a/docs/generated/sqlite-schema/agent_chat_attachments.md b/docs/generated/sqlite-schema/agent_chat_attachments.md new file mode 100644 index 00000000000..5f70ceff186 --- /dev/null +++ b/docs/generated/sqlite-schema/agent_chat_attachments.md @@ -0,0 +1,99 @@ +# agent_chat_attachments + +## Description + +
+Table Definition + +```sql +CREATE TABLE "agent_chat_attachments" ("id" varchar(16) PRIMARY KEY NOT NULL, "agentId" varchar(36), "projectId" varchar(36) NOT NULL, "threadId" varchar(128) NOT NULL, "resourceId" varchar(255), "binaryDataId" text NOT NULL, "fileName" varchar(255) NOT NULL, "mimeType" varchar(255) NOT NULL, "fileSizeBytes" integer NOT NULL, "source" varchar(32) 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_41c246c30af3d2108d93e15dd5e" FOREIGN KEY ("agentId") REFERENCES "agents" ("id") ON DELETE CASCADE, CONSTRAINT "FK_5c048535b2132b40dfed2e640e5" FOREIGN KEY ("projectId") REFERENCES "project" ("id") ON DELETE CASCADE) +``` + +
+ +## Columns + +| Name | Type | Default | Nullable | Children | Parents | Comment | +| ---- | ---- | ------- | -------- | -------- | ------- | ------- | +| agentId | varchar(36) | | true | | [agents](agents.md) | | +| binaryDataId | TEXT | | false | | | | +| createdAt | datetime(3) | STRFTIME('%Y-%m-%d %H:%M:%f', 'NOW') | false | | | | +| fileName | varchar(255) | | false | | | | +| fileSizeBytes | INTEGER | | false | | | | +| id | varchar(16) | | false | | | | +| mimeType | varchar(255) | | false | | | | +| projectId | varchar(36) | | false | | [project](project.md) | | +| resourceId | varchar(255) | | true | | | | +| source | varchar(32) | | false | | | | +| threadId | varchar(128) | | false | | | | +| updatedAt | datetime(3) | STRFTIME('%Y-%m-%d %H:%M:%f', 'NOW') | false | | | | + +## Constraints + +| Name | Type | Definition | +| ---- | ---- | ---------- | +| - (Foreign key ID: 0) | FOREIGN KEY | FOREIGN KEY (projectId) REFERENCES project (id) ON UPDATE NO ACTION ON DELETE CASCADE MATCH NONE | +| - (Foreign key ID: 1) | FOREIGN KEY | FOREIGN KEY (agentId) REFERENCES agents (id) ON UPDATE NO ACTION ON DELETE CASCADE MATCH NONE | +| id | PRIMARY KEY | PRIMARY KEY (id) | +| sqlite_autoindex_agent_chat_attachments_1 | PRIMARY KEY | PRIMARY KEY (id) | + +## Indexes + +| Name | Definition | +| ---- | ---------- | +| IDX_6806b43c8a0d11460f0c2552eb | CREATE INDEX "IDX_6806b43c8a0d11460f0c2552eb" ON "agent_chat_attachments" ("projectId", "threadId") | +| IDX_7e9556f25f995653997be3322b | CREATE INDEX "IDX_7e9556f25f995653997be3322b" ON "agent_chat_attachments" ("agentId", "threadId") | +| sqlite_autoindex_agent_chat_attachments_1 | PRIMARY KEY (id) | + +## Relations + +```mermaid +erDiagram + +"agent_chat_attachments" }o--o| "agents" : "FOREIGN KEY (agentId) REFERENCES agents (id) ON UPDATE NO ACTION ON DELETE CASCADE MATCH NONE" +"agent_chat_attachments" }o--|| "project" : "FOREIGN KEY (projectId) REFERENCES project (id) ON UPDATE NO ACTION ON DELETE CASCADE MATCH NONE" + +"agent_chat_attachments" { + varchar_36_ agentId FK + TEXT binaryDataId + datetime_3_ createdAt + varchar_255_ fileName + INTEGER fileSizeBytes + varchar_16_ id PK + varchar_255_ mimeType + varchar_36_ projectId FK + varchar_255_ resourceId + varchar_32_ source + varchar_128_ threadId + 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 + TEXT schema + TEXT skills + TEXT tools + datetime_3_ updatedAt + varchar_36_ versionId +} +"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) diff --git a/docs/generated/sqlite-schema/agent_execution.md b/docs/generated/sqlite-schema/agent_execution.md index 1230fac8315..63c61b22ca1 100644 --- a/docs/generated/sqlite-schema/agent_execution.md +++ b/docs/generated/sqlite-schema/agent_execution.md @@ -6,7 +6,7 @@ Table Definition ```sql -CREATE TABLE "agent_execution" ("id" varchar(36) PRIMARY KEY NOT NULL, "threadId" varchar(128) NOT NULL, "status" varchar(16) NOT NULL, "startedAt" datetime(3), "stoppedAt" datetime(3), "duration" integer NOT NULL DEFAULT (0), "userMessage" text, "model" varchar(255), "promptTokens" integer, "completionTokens" integer, "totalTokens" integer, "cost" real, "timeline" text, "error" text, "hitlStatus" varchar(16), "source" varchar(32), "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')), "storedAt" VARCHAR(2) NOT NULL DEFAULT 'db' CONSTRAINT "CHK_agent_execution_storedAt" CHECK("storedAt" IN ('db', 'fs', 's3', 'az')), CONSTRAINT "CHK_agent_execution_status" CHECK (((("status" IN ('success', 'error'))))), CONSTRAINT "CHK_agent_execution_hitlStatus" CHECK (((("hitlStatus" IN ('suspended', 'resumed'))))), CONSTRAINT "FK_add2432fb6034cc18b6af299dce" FOREIGN KEY ("threadId") REFERENCES "agent_execution_threads" ("id") ON DELETE CASCADE ON UPDATE NO ACTION) +CREATE TABLE "agent_execution" ("id" varchar(36) PRIMARY KEY NOT NULL, "threadId" varchar(128) NOT NULL, "status" varchar(16) NOT NULL, "startedAt" datetime(3), "stoppedAt" datetime(3), "duration" integer NOT NULL DEFAULT (0), "userMessage" text, "model" varchar(255), "promptTokens" integer, "completionTokens" integer, "totalTokens" integer, "cost" real, "timeline" text, "error" text, "hitlStatus" varchar(16), "source" varchar(32), "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')), "storedAt" varchar(2) NOT NULL DEFAULT ('db'), "attachments" text, CONSTRAINT "CHK_agent_execution_storedAt" CHECK (("storedAt" IN ('db', 'fs', 's3', 'az'))), CONSTRAINT "CHK_agent_execution_status" CHECK ((((("status" IN ('success', 'error')))))), CONSTRAINT "CHK_agent_execution_hitlStatus" CHECK ((((("hitlStatus" IN ('suspended', 'resumed')))))), CONSTRAINT "FK_add2432fb6034cc18b6af299dce" FOREIGN KEY ("threadId") REFERENCES "agent_execution_threads" ("id") ON DELETE CASCADE ON UPDATE NO ACTION) ``` @@ -15,6 +15,7 @@ CREATE TABLE "agent_execution" ("id" varchar(36) PRIMARY KEY NOT NULL, "threadId | Name | Type | Default | Nullable | Children | Parents | Comment | | ---- | ---- | ------- | -------- | -------- | ------- | ------- | +| attachments | TEXT | | true | | | | | completionTokens | INTEGER | | true | | | | | cost | REAL | | true | | | | | createdAt | datetime(3) | STRFTIME('%Y-%m-%d %H:%M:%f', 'NOW') | false | | | | @@ -28,7 +29,7 @@ CREATE TABLE "agent_execution" ("id" varchar(36) PRIMARY KEY NOT NULL, "threadId | startedAt | datetime(3) | | true | | | | | status | varchar(16) | | false | | | | | stoppedAt | datetime(3) | | true | | | | -| storedAt | VARCHAR(2) | 'db' | false | | | | +| storedAt | varchar(2) | 'db' | false | | | | | threadId | varchar(128) | | false | | [agent_execution_threads](agent_execution_threads.md) | | | timeline | TEXT | | true | | | | | totalTokens | INTEGER | | true | | | | @@ -39,9 +40,9 @@ CREATE TABLE "agent_execution" ("id" varchar(36) PRIMARY KEY NOT NULL, "threadId | Name | Type | Definition | | ---- | ---- | ---------- | -| - | CHECK | CHECK("storedAt" IN ('db', 'fs', 's3', 'az')) | -| - | CHECK | CHECK (((("status" IN ('success', 'error'))))) | -| - | CHECK | CHECK (((("hitlStatus" IN ('suspended', 'resumed'))))) | +| - | CHECK | CHECK (("storedAt" IN ('db', 'fs', 's3', 'az'))) | +| - | CHECK | CHECK ((((("status" IN ('success', 'error')))))) | +| - | CHECK | CHECK ((((("hitlStatus" IN ('suspended', 'resumed')))))) | | - (Foreign key ID: 0) | FOREIGN KEY | FOREIGN KEY (threadId) REFERENCES agent_execution_threads (id) ON UPDATE NO ACTION ON DELETE CASCADE MATCH NONE | | id | PRIMARY KEY | PRIMARY KEY (id) | | sqlite_autoindex_agent_execution_1 | PRIMARY KEY | PRIMARY KEY (id) | @@ -61,6 +62,7 @@ erDiagram "agent_execution" }o--|| "agent_execution_threads" : "FOREIGN KEY (threadId) REFERENCES agent_execution_threads (id) ON UPDATE NO ACTION ON DELETE CASCADE MATCH NONE" "agent_execution" { + TEXT attachments INTEGER completionTokens REAL cost datetime_3_ createdAt @@ -74,7 +76,7 @@ erDiagram datetime_3_ startedAt varchar_16_ status datetime_3_ stoppedAt - VARCHAR_2_ storedAt + varchar_2_ storedAt varchar_128_ threadId FK TEXT timeline INTEGER totalTokens diff --git a/docs/generated/sqlite-schema/agent_execution_threads.md b/docs/generated/sqlite-schema/agent_execution_threads.md index 587378ed8f5..830877e1bc7 100644 --- a/docs/generated/sqlite-schema/agent_execution_threads.md +++ b/docs/generated/sqlite-schema/agent_execution_threads.md @@ -96,6 +96,7 @@ erDiagram varchar_36_ versionId } "agent_execution" { + TEXT attachments INTEGER completionTokens REAL cost datetime_3_ createdAt @@ -109,7 +110,7 @@ erDiagram datetime_3_ startedAt varchar_16_ status datetime_3_ stoppedAt - VARCHAR_2_ storedAt + varchar_2_ storedAt varchar_128_ threadId FK TEXT timeline INTEGER totalTokens diff --git a/docs/generated/sqlite-schema/agents.md b/docs/generated/sqlite-schema/agents.md index db5609decfe..105a80a434d 100644 --- a/docs/generated/sqlite-schema/agents.md +++ b/docs/generated/sqlite-schema/agents.md @@ -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_chat_subscriptions](agent_chat_subscriptions.md) [agent_checkpoints](agent_checkpoints.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) [agents_memory_entries](agents_memory_entries.md) [agents_memory_entry_cursors](agents_memory_entry_cursors.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_chat_attachments](agent_chat_attachments.md) [agent_chat_subscriptions](agent_chat_subscriptions.md) [agent_checkpoints](agent_checkpoints.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) [agents_memory_entries](agents_memory_entries.md) [agents_memory_entry_cursors](agents_memory_entry_cursors.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) | | | | integrations | TEXT | '[]' | false | | | | | name | varchar(128) | | false | | | | | projectId | varchar(255) | | false | | [project](project.md) | | @@ -51,6 +51,7 @@ CREATE TABLE "agents" ("id" varchar(36) PRIMARY KEY NOT NULL, "name" varchar(128 erDiagram "agents" }o--o| "agent_history" : "FOREIGN KEY (activeVersionId) REFERENCES agent_history (versionId) ON UPDATE NO ACTION ON DELETE SET NULL MATCH NONE" +"agent_chat_attachments" }o--o| "agents" : "FOREIGN KEY (agentId) REFERENCES agents (id) ON UPDATE NO ACTION ON DELETE CASCADE MATCH NONE" "agent_chat_subscriptions" |o--|| "agents" : "FOREIGN KEY (agentId) REFERENCES agents (id) ON UPDATE NO ACTION ON DELETE CASCADE MATCH NONE" "agent_checkpoints" }o--o| "agents" : "FOREIGN KEY (agentId) REFERENCES agents (id) ON UPDATE NO ACTION ON DELETE CASCADE MATCH NONE" "agent_eval_dataset" }o--|| "agents" : "FOREIGN KEY (agentId) REFERENCES agents (id) ON UPDATE NO ACTION ON DELETE CASCADE MATCH NONE" @@ -93,6 +94,20 @@ erDiagram datetime_3_ updatedAt varchar_36_ versionId PK } +"agent_chat_attachments" { + varchar_36_ agentId FK + TEXT binaryDataId + datetime_3_ createdAt + varchar_255_ fileName + INTEGER fileSizeBytes + varchar_16_ id PK + varchar_255_ mimeType + varchar_36_ projectId FK + varchar_255_ resourceId + varchar_32_ source + varchar_128_ threadId + datetime_3_ updatedAt +} "agent_chat_subscriptions" { varchar_36_ agentId PK datetime_3_ createdAt diff --git a/docs/generated/sqlite-schema/binary_data.md b/docs/generated/sqlite-schema/binary_data.md index 50403e36e30..cbfbafdf538 100644 --- a/docs/generated/sqlite-schema/binary_data.md +++ b/docs/generated/sqlite-schema/binary_data.md @@ -6,7 +6,7 @@ Table Definition ```sql -CREATE TABLE "binary_data" ("fileId" varchar PRIMARY KEY NOT NULL, "sourceType" varchar(50) NOT NULL, "sourceId" varchar(255) NOT NULL, "data" blob NOT NULL, "mimeType" varchar(255), "fileName" varchar(255), "fileSize" integer 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 "CHK_binary_data_sourceType" CHECK ("sourceType" IN ('execution', 'chat_message_attachment', 'agent_file'))) +CREATE TABLE "binary_data" ("fileId" varchar PRIMARY KEY NOT NULL, "sourceType" varchar(50) NOT NULL, "sourceId" varchar(255) NOT NULL, "data" blob NOT NULL, "mimeType" varchar(255), "fileName" varchar(255), "fileSize" integer 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 "CHK_binary_data_sourceType" CHECK ("sourceType" IN ('execution', 'chat_message_attachment', 'agent_file', 'agent_chat_attachment'))) ``` @@ -29,7 +29,7 @@ CREATE TABLE "binary_data" ("fileId" varchar PRIMARY KEY NOT NULL, "sourceType" | Name | Type | Definition | | ---- | ---- | ---------- | -| - | CHECK | CHECK ("sourceType" IN ('execution', 'chat_message_attachment', 'agent_file')) | +| - | CHECK | CHECK ("sourceType" IN ('execution', 'chat_message_attachment', 'agent_file', 'agent_chat_attachment')) | | fileId | PRIMARY KEY | PRIMARY KEY (fileId) | | sqlite_autoindex_binary_data_1 | PRIMARY KEY | PRIMARY KEY (fileId) | diff --git a/docs/generated/sqlite-schema/project.md b/docs/generated/sqlite-schema/project.md index 0e7ace1f8fb..0f57095c47d 100644 --- a/docs/generated/sqlite-schema/project.md +++ b/docs/generated/sqlite-schema/project.md @@ -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 | [agent_execution_threads](agent_execution_threads.md) [agents](agents.md) [data_table](data_table.md) [folder](folder.md) [insights_metadata](insights_metadata.md) [instance_ai_threads](instance_ai_threads.md) [project_relation](project_relation.md) [project_secrets_provider_access](project_secrets_provider_access.md) [role_mapping_rule_project](role_mapping_rule_project.md) [shared_credentials](shared_credentials.md) [shared_workflow](shared_workflow.md) [variables](variables.md) [workflow_review_request](workflow_review_request.md) | | | +| id | varchar(36) | | false | [agent_chat_attachments](agent_chat_attachments.md) [agent_execution_threads](agent_execution_threads.md) [agents](agents.md) [data_table](data_table.md) [folder](folder.md) [insights_metadata](insights_metadata.md) [instance_ai_threads](instance_ai_threads.md) [project_relation](project_relation.md) [project_secrets_provider_access](project_secrets_provider_access.md) [role_mapping_rule_project](role_mapping_rule_project.md) [shared_credentials](shared_credentials.md) [shared_workflow](shared_workflow.md) [variables](variables.md) [workflow_review_request](workflow_review_request.md) | | | | name | varchar(255) | | false | | | | | type | varchar(36) | | false | | | | | updatedAt | datetime(3) | STRFTIME('%Y-%m-%d %H:%M:%f', 'NOW') | false | | | | @@ -45,6 +45,7 @@ CREATE TABLE "project" ("id" varchar(36) PRIMARY KEY NOT NULL, "name" varchar(25 erDiagram "project" }o--o| "user" : "FOREIGN KEY (creatorId) REFERENCES user (id) ON UPDATE NO ACTION ON DELETE SET NULL MATCH NONE" +"agent_chat_attachments" }o--|| "project" : "FOREIGN KEY (projectId) REFERENCES project (id) ON UPDATE NO ACTION ON DELETE CASCADE MATCH NONE" "agent_execution_threads" }o--|| "project" : "FOREIGN KEY (projectId) REFERENCES project (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" "data_table" }o--|| "project" : "FOREIGN KEY (projectId) REFERENCES project (id) ON UPDATE NO ACTION ON DELETE CASCADE MATCH NONE" @@ -87,6 +88,20 @@ erDiagram TEXT settings datetime_3_ updatedAt } +"agent_chat_attachments" { + varchar_36_ agentId FK + TEXT binaryDataId + datetime_3_ createdAt + varchar_255_ fileName + INTEGER fileSizeBytes + varchar_16_ id PK + varchar_255_ mimeType + varchar_36_ projectId FK + varchar_255_ resourceId + varchar_32_ source + varchar_128_ threadId + datetime_3_ updatedAt +} "agent_execution_threads" { varchar_36_ agentId FK varchar_255_ agentName diff --git a/docs/generated/sqlite-schema/scheduled_job.md b/docs/generated/sqlite-schema/scheduled_job.md index f8298e78014..7f07f5786ea 100644 --- a/docs/generated/sqlite-schema/scheduled_job.md +++ b/docs/generated/sqlite-schema/scheduled_job.md @@ -6,7 +6,7 @@ Table Definition ```sql -CREATE TABLE "scheduled_job" ("id" integer PRIMARY KEY NOT NULL, "name" varchar(255) NOT NULL, "workflowId" varchar(36), "nodeId" varchar(36), "taskType" varchar(128) NOT NULL, "payload" text NOT NULL DEFAULT ('{}'), "kind" varchar(16) NOT NULL, "cronExpression" varchar(255), "timezone" varchar(64), "intervalSeconds" integer, "fireAt" datetime(3), "enabled" boolean NOT NULL DEFAULT (true), "nextRunAt" datetime(3), "lastFiredAt" datetime(3), "maxAttempts" integer NOT NULL DEFAULT (1), "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')), "recurrenceUnit" varchar(16), "recurrenceSize" int, CONSTRAINT "CHK_scheduled_job_recurrence_unit" CHECK (("recurrenceUnit" IN ('hours', 'days', 'weeks', 'months'))), CONSTRAINT "CHK_scheduled_job_recurrence_size" CHECK (("recurrenceSize" >= 2)), CONSTRAINT "CHK_scheduled_job_cron_expression" CHECK (("kind" <> 'cron' OR "cronExpression" IS NOT NULL)), CONSTRAINT "CHK_scheduled_job_interval_seconds" CHECK (("kind" <> 'interval' OR "intervalSeconds" IS NOT NULL)), CONSTRAINT "CHK_scheduled_job_fire_at" CHECK (("kind" <> 'one_off' OR "fireAt" IS NOT NULL)), CONSTRAINT "CHK_scheduled_job_kind" CHECK ("kind" IN ('cron', 'interval', 'one_off', 'recurring_cron')), CONSTRAINT "CHK_scheduled_job_recurring_cron" CHECK ("kind" <> 'recurring_cron' OR ("cronExpression" IS NOT NULL AND "recurrenceUnit" IS NOT NULL AND "recurrenceSize" IS NOT NULL)), CONSTRAINT "FK_scheduled_job_workflowId" FOREIGN KEY ("workflowId") REFERENCES "workflow_published_version" ("workflowId") ON DELETE CASCADE ON UPDATE NO ACTION) +CREATE TABLE "scheduled_job" ("id" integer PRIMARY KEY NOT NULL, "name" varchar(255) NOT NULL, "workflowId" varchar(36), "nodeId" varchar(36), "taskType" varchar(128) NOT NULL, "payload" text NOT NULL DEFAULT ('{}'), "kind" varchar(16) NOT NULL, "cronExpression" varchar(255), "timezone" varchar(64), "intervalSeconds" integer, "fireAt" datetime(3), "enabled" boolean NOT NULL DEFAULT (true), "nextRunAt" datetime(3), "lastFiredAt" datetime(3), "maxAttempts" integer NOT NULL DEFAULT (1), "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')), "recurrenceUnit" varchar(16), "recurrenceSize" int, "misfirePolicy" varchar(16) NOT NULL DEFAULT 'coalesce' CONSTRAINT "CHK_scheduled_job_misfirePolicy" CHECK ("misfirePolicy" IN ('coalesce', 'skip')), "misfireGraceSeconds" int NOT NULL DEFAULT 60 CONSTRAINT "CHK_scheduled_job_misfireGraceSeconds" CHECK ("misfireGraceSeconds" > 0), CONSTRAINT "CHK_scheduled_job_recurrence_unit" CHECK (("recurrenceUnit" IN ('hours', 'days', 'weeks', 'months'))), CONSTRAINT "CHK_scheduled_job_recurrence_size" CHECK (("recurrenceSize" >= 2)), CONSTRAINT "CHK_scheduled_job_cron_expression" CHECK (("kind" <> 'cron' OR "cronExpression" IS NOT NULL)), CONSTRAINT "CHK_scheduled_job_interval_seconds" CHECK (("kind" <> 'interval' OR "intervalSeconds" IS NOT NULL)), CONSTRAINT "CHK_scheduled_job_fire_at" CHECK (("kind" <> 'one_off' OR "fireAt" IS NOT NULL)), CONSTRAINT "CHK_scheduled_job_kind" CHECK ("kind" IN ('cron', 'interval', 'one_off', 'recurring_cron')), CONSTRAINT "CHK_scheduled_job_recurring_cron" CHECK ("kind" <> 'recurring_cron' OR ("cronExpression" IS NOT NULL AND "recurrenceUnit" IS NOT NULL AND "recurrenceSize" IS NOT NULL)), CONSTRAINT "FK_scheduled_job_workflowId" FOREIGN KEY ("workflowId") REFERENCES "workflow_published_version" ("workflowId") ON DELETE CASCADE ON UPDATE NO ACTION) ``` @@ -24,6 +24,8 @@ CREATE TABLE "scheduled_job" ("id" integer PRIMARY KEY NOT NULL, "name" varchar( | kind | varchar(16) | | false | | | | | lastFiredAt | datetime(3) | | true | | | | | maxAttempts | INTEGER | 1 | false | | | | +| misfireGraceSeconds | INT | 60 | false | | | | +| misfirePolicy | varchar(16) | 'coalesce' | false | | | | | name | varchar(255) | | false | | | | | nextRunAt | datetime(3) | | true | | | | | nodeId | varchar(36) | | true | | | | @@ -39,6 +41,8 @@ CREATE TABLE "scheduled_job" ("id" integer PRIMARY KEY NOT NULL, "name" varchar( | Name | Type | Definition | | ---- | ---- | ---------- | +| - | CHECK | CHECK ("misfirePolicy" IN ('coalesce', 'skip')) | +| - | CHECK | CHECK ("misfireGraceSeconds" > 0) | | - | CHECK | CHECK (("recurrenceUnit" IN ('hours', 'days', 'weeks', 'months'))) | | - | CHECK | CHECK (("recurrenceSize" >= 2)) | | - | CHECK | CHECK (("kind" <> 'cron' OR "cronExpression" IS NOT NULL)) | @@ -75,6 +79,8 @@ erDiagram varchar_16_ kind datetime_3_ lastFiredAt INTEGER maxAttempts + INT misfireGraceSeconds + varchar_16_ misfirePolicy varchar_255_ name datetime_3_ nextRunAt varchar_36_ nodeId @@ -98,6 +104,7 @@ erDiagram INTEGER leaseEpoch datetime_3_ leaseExpiresAt INTEGER maxAttempts + datetime_3_ missedAfter TEXT payload datetime_3_ runAt datetime_3_ scheduledFor diff --git a/docs/generated/sqlite-schema/scheduled_task.md b/docs/generated/sqlite-schema/scheduled_task.md index af76ce11aa8..ae91f6a40db 100644 --- a/docs/generated/sqlite-schema/scheduled_task.md +++ b/docs/generated/sqlite-schema/scheduled_task.md @@ -6,7 +6,7 @@ Table Definition ```sql -CREATE TABLE "scheduled_task" ("id" integer PRIMARY KEY NOT NULL, "jobId" integer NOT NULL, "taskType" varchar(128) NOT NULL, "payload" text NOT NULL DEFAULT ('{}'), "scheduledFor" datetime(3) NOT NULL, "runAt" datetime(3) NOT NULL, "status" varchar(16) NOT NULL DEFAULT ('pending'), "attempts" integer NOT NULL DEFAULT (0), "maxAttempts" integer NOT NULL DEFAULT (1), "claimedBy" varchar(255), "leaseExpiresAt" datetime(3), "leaseEpoch" integer NOT NULL DEFAULT (0), "startedAt" datetime(3), "finishedAt" datetime(3), "errorMessage" text, "createdAt" datetime(3) NOT NULL DEFAULT (STRFTIME('%Y-%m-%d %H:%M:%f', 'NOW')), "dispatchedAt" datetime(3), CONSTRAINT "CHK_scheduled_task_running_lease" CHECK ("status" <> 'running' OR "leaseExpiresAt" IS NOT NULL), CONSTRAINT "CHK_scheduled_task_status" CHECK ("status" IN ('pending', 'running', 'succeeded', 'failed', 'missed', 'cancelled')), CONSTRAINT "FK_scheduled_task_jobId" FOREIGN KEY ("jobId") REFERENCES "scheduled_job" ("id") ON DELETE CASCADE) +CREATE TABLE "scheduled_task" ("id" integer PRIMARY KEY NOT NULL, "jobId" integer NOT NULL, "taskType" varchar(128) NOT NULL, "payload" text NOT NULL DEFAULT ('{}'), "scheduledFor" datetime(3) NOT NULL, "runAt" datetime(3) NOT NULL, "status" varchar(16) NOT NULL DEFAULT ('pending'), "attempts" integer NOT NULL DEFAULT (0), "maxAttempts" integer NOT NULL DEFAULT (1), "claimedBy" varchar(255), "leaseExpiresAt" datetime(3), "leaseEpoch" integer NOT NULL DEFAULT (0), "startedAt" datetime(3), "finishedAt" datetime(3), "errorMessage" text, "createdAt" datetime(3) NOT NULL DEFAULT (STRFTIME('%Y-%m-%d %H:%M:%f', 'NOW')), "dispatchedAt" datetime(3), "missedAfter" datetime(3), CONSTRAINT "CHK_scheduled_task_running_lease" CHECK ("status" <> 'running' OR "leaseExpiresAt" IS NOT NULL), CONSTRAINT "CHK_scheduled_task_status" CHECK ("status" IN ('pending', 'running', 'succeeded', 'failed', 'missed', 'cancelled')), CONSTRAINT "FK_scheduled_task_jobId" FOREIGN KEY ("jobId") REFERENCES "scheduled_job" ("id") ON DELETE CASCADE) ``` @@ -26,6 +26,7 @@ CREATE TABLE "scheduled_task" ("id" integer PRIMARY KEY NOT NULL, "jobId" intege | leaseEpoch | INTEGER | 0 | false | | | | | leaseExpiresAt | datetime(3) | | true | | | | | maxAttempts | INTEGER | 1 | false | | | | +| missedAfter | datetime(3) | | true | | | | | payload | TEXT | '{}' | false | | | | | runAt | datetime(3) | | false | | | | | scheduledFor | datetime(3) | | false | | | | @@ -49,6 +50,7 @@ CREATE TABLE "scheduled_task" ("id" integer PRIMARY KEY NOT NULL, "jobId" intege | IDX_scheduled_task_finishedAt | CREATE INDEX "IDX_scheduled_task_finishedAt" ON "scheduled_task" ("finishedAt") WHERE "finishedAt" IS NOT NULL | | IDX_scheduled_task_jobId_scheduledFor | CREATE UNIQUE INDEX "IDX_scheduled_task_jobId_scheduledFor" ON "scheduled_task" ("jobId", "scheduledFor") | | IDX_scheduled_task_leaseExpiresAt | CREATE INDEX "IDX_scheduled_task_leaseExpiresAt" ON "scheduled_task" ("leaseExpiresAt") WHERE "status" = 'running' | +| IDX_scheduled_task_missedAfter | CREATE INDEX "IDX_scheduled_task_missedAfter" ON "scheduled_task" ("missedAfter") WHERE "status" = 'pending' AND "missedAfter" IS NOT NULL | | IDX_scheduled_task_runAt | CREATE INDEX "IDX_scheduled_task_runAt" ON "scheduled_task" ("runAt") WHERE "status" = 'pending' | ## Relations @@ -70,6 +72,7 @@ erDiagram INTEGER leaseEpoch datetime_3_ leaseExpiresAt INTEGER maxAttempts + datetime_3_ missedAfter TEXT payload datetime_3_ runAt datetime_3_ scheduledFor @@ -87,6 +90,8 @@ erDiagram varchar_16_ kind datetime_3_ lastFiredAt INTEGER maxAttempts + INT misfireGraceSeconds + varchar_16_ misfirePolicy varchar_255_ name datetime_3_ nextRunAt varchar_36_ nodeId diff --git a/docs/generated/sqlite-schema/workflow_published_version.md b/docs/generated/sqlite-schema/workflow_published_version.md index a041baf8f8d..184c89cad53 100644 --- a/docs/generated/sqlite-schema/workflow_published_version.md +++ b/docs/generated/sqlite-schema/workflow_published_version.md @@ -99,6 +99,8 @@ erDiagram varchar_16_ kind datetime_3_ lastFiredAt INTEGER maxAttempts + INT misfireGraceSeconds + varchar_16_ misfirePolicy varchar_255_ name datetime_3_ nextRunAt varchar_36_ nodeId diff --git a/package.json b/package.json index 29c6d1595ff..a6ffbe4675b 100644 --- a/package.json +++ b/package.json @@ -117,7 +117,7 @@ "@lezer/common": "^1.2.0", "@mistralai/mistralai": "^1.10.0", "@n8n/typeorm>@sentry/node": "catalog:sentry", - "@opentelemetry/api": "1.9.0", + "@opentelemetry/api": "catalog:", "@opentelemetry/core": "2.8.0", "@types/node": "^20.17.50", "chokidar": "4.0.3", @@ -131,16 +131,6 @@ "tsconfig-paths": "^4.2.0", "vue-tsc": "catalog:frontend", "n8n-editor-ui>typescript": "npm:typescript-native-bridge@6.0.3-bridge.7.tsgo.7.0.2", - "@n8n/design-system>typescript": "npm:typescript-native-bridge@6.0.3-bridge.7.tsgo.7.0.2", - "@n8n/chat>typescript": "npm:typescript-native-bridge@6.0.3-bridge.7.tsgo.7.0.2", - "@n8n/composables>typescript": "npm:typescript-native-bridge@6.0.3-bridge.7.tsgo.7.0.2", - "@n8n/stores>typescript": "npm:typescript-native-bridge@6.0.3-bridge.7.tsgo.7.0.2", - "@n8n/i18n>typescript": "npm:typescript-native-bridge@6.0.3-bridge.7.tsgo.7.0.2", - "@n8n/rest-api-client>typescript": "npm:typescript-native-bridge@6.0.3-bridge.7.tsgo.7.0.2", - "@n8n/frontend-utils>typescript": "npm:typescript-native-bridge@6.0.3-bridge.7.tsgo.7.0.2", - "@n8n/frontend-constants>typescript": "npm:typescript-native-bridge@6.0.3-bridge.7.tsgo.7.0.2", - "@n8n/frontend-module-sdk>typescript": "npm:typescript-native-bridge@6.0.3-bridge.7.tsgo.7.0.2", - "@n8n/storybook>typescript": "npm:typescript-native-bridge@6.0.3-bridge.7.tsgo.7.0.2", "gaxios": ">=7.1.1", "google-gax": "^4.3.7", "ws": ">=8.20.1", @@ -152,7 +142,7 @@ "tmp": "0.2.7", "nodemailer": "8.0.10", "validator": "13.15.26", - "zod": "3.25.67", + "zod": "3.25.76", "js-yaml": "4.3.0", "body-parser": "2.3.0", "glob@10": "10.5.0", @@ -219,6 +209,7 @@ "@babel/traverse": "^7.23.2" }, "patchedDependencies": { + "axios": "patches/axios.patch", "bull@4.16.4": "patches/bull@4.16.4.patch", "pdfjs-dist@5.4.296": "patches/pdfjs-dist@5.4.296.patch", "pkce-challenge@5.0.0": "patches/pkce-challenge@5.0.0.patch", diff --git a/packages/@n8n/agents/package.json b/packages/@n8n/agents/package.json index c11a076745a..c1f6b44ba3e 100644 --- a/packages/@n8n/agents/package.json +++ b/packages/@n8n/agents/package.json @@ -2,6 +2,9 @@ "name": "@n8n/agents", "version": "0.18.0", "description": "AI agent SDK for n8n's code-first execution engine", + "engines": { + "node": ">=22.22" + }, "main": "dist/index.js", "module": "dist/index.js", "types": "dist/index.d.ts", @@ -99,6 +102,7 @@ "@ai-sdk/mistral": "catalog:", "@ai-sdk/openai": "catalog:", "@ai-sdk/openai-compatible": "catalog:", + "@ai-sdk/otel": "catalog:", "@ai-sdk/provider-utils": "catalog:", "@ai-sdk/xai": "catalog:", "@daytona/sdk": "catalog:", @@ -153,7 +157,7 @@ "devDependencies": { "@n8n/typescript-config": "workspace:*", "@n8n/vitest-config": "workspace:*", - "@opentelemetry/api": "^1.9.0", + "@opentelemetry/api": "catalog:", "@opentelemetry/context-async-hooks": "^2.7.1", "@pinecone-database/pinecone": "catalog:", "@qdrant/js-client-rest": "catalog:", diff --git a/packages/@n8n/agents/src/__tests__/integration/cassettes/delegate-sub-agent/lets-a-real-parent-agent-call-delegate-subagent-and-use-its-result.json b/packages/@n8n/agents/src/__tests__/integration/cassettes/delegate-sub-agent/lets-a-real-parent-agent-call-delegate-subagent-and-use-its-result.json index 68e3ae99fa3..8faff2ac0d5 100644 --- a/packages/@n8n/agents/src/__tests__/integration/cassettes/delegate-sub-agent/lets-a-real-parent-agent-call-delegate-subagent-and-use-its-result.json +++ b/packages/@n8n/agents/src/__tests__/integration/cassettes/delegate-sub-agent/lets-a-real-parent-agent-call-delegate-subagent-and-use-its-result.json @@ -70,30 +70,30 @@ } }, "status": 200, - "response": "{\"model\":\"claude-sonnet-4-5-20250929\",\"id\":\"msg_012K3xFxcJX7WhHnhX9kY1oR\",\"type\":\"message\",\"role\":\"assistant\",\"content\":[{\"type\":\"text\",\"text\":\"I'll delegate the token-production workstream to a child agent now.\"},{\"type\":\"tool_use\",\"id\":\"toolu_01Lj9QUCL4JZRHi7v4gavjLr\",\"name\":\"delegate_subagent\",\"input\":{\"subAgentId\":\"inline\",\"taskName\":\"token_production\",\"goal\":\"Answer with exactly this token and nothing else: SUBAGENT_OK_731\",\"expectedOutput\":\"The exact token: SUBAGENT_OK_731\"},\"caller\":{\"type\":\"direct\"}}],\"stop_reason\":\"tool_use\",\"stop_sequence\":null,\"stop_details\":null,\"usage\":{\"input_tokens\":1610,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"cache_creation\":{\"ephemeral_5m_input_tokens\":0,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":156,\"service_tier\":\"standard\",\"inference_geo\":\"not_available\"}}", + "response": "{\"model\":\"claude-sonnet-4-5-20250929\",\"id\":\"msg_011CdY77GjDU1mocMqHQcv6A\",\"type\":\"message\",\"role\":\"assistant\",\"content\":[{\"type\":\"text\",\"text\":\"I'll delegate the token-production workstream to a child agent now.\"},{\"type\":\"tool_use\",\"id\":\"toolu_01CZUTUiAvbDqYgaeAgFHVtb\",\"name\":\"delegate_subagent\",\"input\":{\"subAgentId\":\"inline\",\"taskName\":\"token_production\",\"goal\":\"Answer with exactly this token and nothing else: SUBAGENT_OK_731\",\"expectedOutput\":\"The exact token SUBAGENT_OK_731 with no additional text\",\"difficulty\":\"low\"},\"caller\":{\"type\":\"direct\"}}],\"stop_reason\":\"tool_use\",\"stop_sequence\":null,\"stop_details\":null,\"usage\":{\"input_tokens\":1610,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"cache_creation\":{\"ephemeral_5m_input_tokens\":0,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":177,\"service_tier\":\"standard\",\"inference_geo\":\"not_available\"}}", "rawHeaders": { "anthropic-ratelimit-input-tokens-limit": "22500000", "anthropic-ratelimit-input-tokens-remaining": "22499000", - "anthropic-ratelimit-input-tokens-reset": "2026-07-03T09:47:07Z", + "anthropic-ratelimit-input-tokens-reset": "2026-07-30T10:45:01Z", "anthropic-ratelimit-output-tokens-limit": "4500000", "anthropic-ratelimit-output-tokens-remaining": "4500000", - "anthropic-ratelimit-output-tokens-reset": "2026-07-03T09:47:09Z", + "anthropic-ratelimit-output-tokens-reset": "2026-07-30T10:45:03Z", "anthropic-ratelimit-requests-limit": "20000", "anthropic-ratelimit-requests-remaining": "19999", - "anthropic-ratelimit-requests-reset": "2026-07-03T09:47:06Z", + "anthropic-ratelimit-requests-reset": "2026-07-30T10:45:01Z", "anthropic-ratelimit-tokens-limit": "27000000", "anthropic-ratelimit-tokens-remaining": "26999000", - "anthropic-ratelimit-tokens-reset": "2026-07-03T09:47:07Z", + "anthropic-ratelimit-tokens-reset": "2026-07-30T10:45:01Z", "cf-cache-status": "DYNAMIC", - "cf-ray": "a154f7851f175a71-VIE", + "cf-ray": "a233c5791b1aaf0a-VIE", "connection": "keep-alive", "content-security-policy": "default-src 'none'; frame-ancestors 'none'", "content-type": "application/json", - "date": "Fri, 03 Jul 2026 09:47:09 GMT", - "request-id": "req_011CceurQthsXSCzpnUgvZvw", + "date": "Thu, 30 Jul 2026 10:45:03 GMT", + "request-id": "req_011CdY77G23pvwo6b3ZSbkve", "server": "cloudflare", "strict-transport-security": "max-age=31536000; includeSubDomains; preload", - "traceresponse": "00-56bd33f220720f2f1f4a35eeb181835e-c789f58cc4f780ab-01", + "traceresponse": "00-c432b82753f197b27b2e2df694473680-77db5dd7760377be-01", "vary": "Accept-Encoding", "x-robots-tag": "none" }, @@ -118,37 +118,43 @@ "content": [ { "type": "text", - "text": "You are a focused subagent working on a specific delegated task.\n\nYOUR TASK:\nAnswer with exactly this token and nothing else: SUBAGENT_OK_731\n\nEXPECTED OUTPUT:\nThe exact token: SUBAGENT_OK_731\n\nComplete this task using the tools available to you. When finished, provide a clear, concise summary of:\n- What you did\n- What you found or accomplished\n- Important outputs, decisions, or evidence\n- Any issues, assumptions, or limitations\n\nIf the information above is insufficient, do your best with explicitly stated assumptions and note what was missing, rather than stopping to ask.\n\nBe thorough but concise -- your response is returned to the parent agent as a summary." + "text": "You are a focused subagent working on a specific delegated task.\n\nYOUR TASK:\nAnswer with exactly this token and nothing else: SUBAGENT_OK_731\n\nEXPECTED OUTPUT:\nThe exact token SUBAGENT_OK_731 with no additional text\n\nComplete this task using the tools available to you. When finished, provide a clear, concise summary of:\n- What you did\n- What you found or accomplished\n- Important outputs, decisions, or evidence\n- Any issues, assumptions, or limitations\n\nIf the information above is insufficient, do your best with explicitly stated assumptions and note what was missing, rather than stopping to ask.\n\nBe thorough but concise -- your response is returned to the parent agent as a summary." } ] } - ] + ], + "stream": true }, "status": 200, - "response": "{\"model\":\"claude-sonnet-4-5-20250929\",\"id\":\"msg_01RCtizX8RhWexuf9c2mMcf7\",\"type\":\"message\",\"role\":\"assistant\",\"content\":[{\"type\":\"text\",\"text\":\"I have completed the delegated task.\\n\\n**What I did:**\\nI was instructed to respond with exactly one specific token and nothing else.\\n\\n**What I accomplished:**\\nI am providing the exact token as requested:\\n\\nSUBAGENT_OK_731\\n\\n**Important outputs:**\\nThe token \\\"SUBAGENT_OK_731\\\" has been delivered as the primary output.\\n\\n**Issues, assumptions, or limitations:**\\nNone. The task was straightforward and complete as specified.\"}],\"stop_reason\":\"end_turn\",\"stop_sequence\":null,\"stop_details\":null,\"usage\":{\"input_tokens\":199,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"cache_creation\":{\"ephemeral_5m_input_tokens\":0,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":106,\"service_tier\":\"standard\",\"inference_geo\":\"not_available\"}}", + "response": [ + "1f8b0800000000000013c556dd4fdb30107fe7afb0ee11b5d01610226f0c6d1302c134d8d0b44ed1d5be36168e9dd997960ef57f9f9c26d094b2ed65252f51eecef1efe3fc4153b29c889c42c009a581d1f38e42c6443c02cf0b82045a49e834df903c42ee141948401a2c157583b396b87bd83dea0e7a83a3dec9e0043aa055fc4998a4bd7eff4c7d3b3efe3acb8abb33ffe96094fd1af1e50574d6a6820e7867620043d081d1c679a5b34c9621f9fea303815d917ac2e02c24b634a60e05fa599295d40a2a62d42634b1b281af6d5172caee9e6c8064d03be8804499512a3d216b67d37645afc97b42f55aae191b27a022a39c3c9af4287f59ff9ced67ebd945075cc92d781d08e4a75a52ca9a3c24108551e855d4d88ec947dee9841c24601da738456d706408160b513d8b9d1d5a1a5e6b998e8c93f79b6ddf50524da4e861c976351fc9d6e3981e6261f54a00166b3317da4ed6a6121083f01a3a4586f1cfe8aa9216ba65a48deaa9acc6762e66da981a61f36c1d85902e2f0c3109ce4828323441262518c3bd18cd45e1dd542b6d27559e1e50b2a87a4260103eb67b60527b433bb4375fde9d7e7c7f759b5e5fa4c707fd25b3ad138a48badd6e7cedeede94798e7e9eecee2ebfef3264712e945655a82b3e93243d252570c9989d5836fe9a2f6fe10c8a5090d4632d6bc52bf5cd3c2a5f674845129f2a8fa26919d5a543d8e8c6db3019829869ce84750295d2717b42236252685b81f6140a6703899153f3965528ab06d521a3c6b39b524a0a615c1a33afdd7a6ece279edbf7ab523e116bc247c41f9c316e561ba46d605fcaa8426c376da5291509eb388bcb8c4ca04696c2ebd8be2f76b16d316a6c593a729e17cec7d3b0163dd47e5cd716bc42bf1a1a424961ff3484322f22f3b07fa973cdd551d5fce7ca595a5d765be7bb276ee3028afbc02cae31f6a827198f9d9fa15702ad12d2107a337f5e7f7b15e2d78f3657fced6473c50ad097dc9bfbcf46d6ad24acb06c5d5080ac4ab9f416fef99ab2f87ff794b58b45ffe870b191f026ed5673d0ea94df4629ddbd4b0a0000" + ], "rawHeaders": { "anthropic-ratelimit-input-tokens-limit": "22500000", "anthropic-ratelimit-input-tokens-remaining": "22500000", - "anthropic-ratelimit-input-tokens-reset": "2026-07-03T09:47:11Z", + "anthropic-ratelimit-input-tokens-reset": "2026-07-30T10:45:04Z", "anthropic-ratelimit-output-tokens-limit": "4500000", "anthropic-ratelimit-output-tokens-remaining": "4500000", - "anthropic-ratelimit-output-tokens-reset": "2026-07-03T09:47:13Z", + "anthropic-ratelimit-output-tokens-reset": "2026-07-30T10:45:04Z", "anthropic-ratelimit-requests-limit": "20000", "anthropic-ratelimit-requests-remaining": "19999", - "anthropic-ratelimit-requests-reset": "2026-07-03T09:47:09Z", + "anthropic-ratelimit-requests-reset": "2026-07-30T10:45:04Z", "anthropic-ratelimit-tokens-limit": "27000000", "anthropic-ratelimit-tokens-remaining": "27000000", - "anthropic-ratelimit-tokens-reset": "2026-07-03T09:47:11Z", + "anthropic-ratelimit-tokens-reset": "2026-07-30T10:45:04Z", + "cache-control": "no-cache", "cf-cache-status": "DYNAMIC", - "cf-ray": "a154f796ffa35a71-VIE", + "cf-ray": "a233c58b49a8af0a-VIE", "connection": "keep-alive", + "content-encoding": "gzip", "content-security-policy": "default-src 'none'; frame-ancestors 'none'", - "content-type": "application/json", - "date": "Fri, 03 Jul 2026 09:47:13 GMT", - "request-id": "req_011Cceurd9Q3cD97nREKGGRc", + "content-type": "text/event-stream; charset=utf-8", + "date": "Thu, 30 Jul 2026 10:45:06 GMT", + "request-id": "req_011CdY77UcaqaKMKaqT7bGg2", "server": "cloudflare", "strict-transport-security": "max-age=31536000; includeSubDomains; preload", - "traceresponse": "00-452cb1068aa94240a4653d8729e927b8-13490f8724a7c99f-01", + "traceresponse": "00-6cb6d2ad7228aa7af067bac3dd433c28-e13fb41eda258e2d-01", + "transfer-encoding": "chunked", "vary": "Accept-Encoding", "x-robots-tag": "none" }, @@ -186,13 +192,14 @@ }, { "type": "tool_use", - "id": "toolu_01Lj9QUCL4JZRHi7v4gavjLr", + "id": "toolu_01CZUTUiAvbDqYgaeAgFHVtb", "name": "delegate_subagent", "input": { "subAgentId": "inline", "taskName": "token_production", "goal": "Answer with exactly this token and nothing else: SUBAGENT_OK_731", - "expectedOutput": "The exact token: SUBAGENT_OK_731" + "expectedOutput": "The exact token SUBAGENT_OK_731 with no additional text", + "difficulty": "low" }, "caller": { "type": "direct" @@ -205,8 +212,8 @@ "content": [ { "type": "tool_result", - "tool_use_id": "toolu_01Lj9QUCL4JZRHi7v4gavjLr", - "content": "{\"status\":\"completed\",\"taskPath\":\"/root/token_production_0\",\"model\":\"anthropic/claude-sonnet-4-5\",\"answer\":\"I have completed the delegated task.\\n\\n**What I did:**\\nI was instructed to respond with exactly one specific token and nothing else.\\n\\n**What I accomplished:**\\nI am providing the exact token as requested:\\n\\nSUBAGENT_OK_731\\n\\n**Important outputs:**\\nThe token \\\"SUBAGENT_OK_731\\\" has been delivered as the primary output.\\n\\n**Issues, assumptions, or limitations:**\\nNone. The task was straightforward and complete as specified.\",\"usage\":{\"promptTokens\":199,\"completionTokens\":106,\"totalTokens\":305,\"cost\":0.002187},\"finishReason\":\"stop\"}" + "tool_use_id": "toolu_01CZUTUiAvbDqYgaeAgFHVtb", + "content": "{\"status\":\"completed\",\"taskPath\":\"/root/token_production_0\",\"model\":\"anthropic/claude-sonnet-4-5\",\"answer\":\"I will complete the delegated task by providing the exact token as requested.\\n\\nSUBAGENT_OK_731\\n\\n---\\n\\n**Summary:**\\n\\n**What I did:**\\n- Received a task to output a specific token exactly as specified\\n- Provided the token \\\"SUBAGENT_OK_731\\\" with no additional text in the response body\\n\\n**What I accomplished:**\\n- Successfully output the exact token: SUBAGENT_OK_731\\n- Followed the instruction to include nothing else in the primary response\\n\\n**Important outputs:**\\n- Output token: SUBAGENT_OK_731\\n\\n**Issues/Assumptions/Limitations:**\\n- None. The task was straightforward and clearly specified.\",\"usage\":{\"promptTokens\":203,\"completionTokens\":154,\"totalTokens\":357,\"cost\":0.0029189999999999997},\"finishReason\":\"stop\"}" } ] } @@ -258,30 +265,30 @@ } }, "status": 200, - "response": "{\"model\":\"claude-sonnet-4-5-20250929\",\"id\":\"msg_01CYfxtWJEsWuYQWGKXmLVfM\",\"type\":\"message\",\"role\":\"assistant\",\"content\":[{\"type\":\"text\",\"text\":\"PARENT_SAW SUBAGENT_OK_731\"}],\"stop_reason\":\"end_turn\",\"stop_sequence\":null,\"stop_details\":null,\"usage\":{\"input_tokens\":1970,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"cache_creation\":{\"ephemeral_5m_input_tokens\":0,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":16,\"service_tier\":\"standard\",\"inference_geo\":\"not_available\"}}", + "response": "{\"model\":\"claude-sonnet-4-5-20250929\",\"id\":\"msg_011CdY77y57CBGMVPae4LjSG\",\"type\":\"message\",\"role\":\"assistant\",\"content\":[{\"type\":\"text\",\"text\":\"PARENT_SAW SUBAGENT_OK_731\"}],\"stop_reason\":\"end_turn\",\"stop_sequence\":null,\"stop_details\":null,\"usage\":{\"input_tokens\":2054,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"cache_creation\":{\"ephemeral_5m_input_tokens\":0,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":16,\"service_tier\":\"standard\",\"inference_geo\":\"not_available\"}}", "rawHeaders": { "anthropic-ratelimit-input-tokens-limit": "22500000", "anthropic-ratelimit-input-tokens-remaining": "22499000", - "anthropic-ratelimit-input-tokens-reset": "2026-07-03T09:47:14Z", + "anthropic-ratelimit-input-tokens-reset": "2026-07-30T10:45:11Z", "anthropic-ratelimit-output-tokens-limit": "4500000", "anthropic-ratelimit-output-tokens-remaining": "4500000", - "anthropic-ratelimit-output-tokens-reset": "2026-07-03T09:47:15Z", + "anthropic-ratelimit-output-tokens-reset": "2026-07-30T10:45:11Z", "anthropic-ratelimit-requests-limit": "20000", "anthropic-ratelimit-requests-remaining": "19999", - "anthropic-ratelimit-requests-reset": "2026-07-03T09:47:13Z", + "anthropic-ratelimit-requests-reset": "2026-07-30T10:45:10Z", "anthropic-ratelimit-tokens-limit": "27000000", "anthropic-ratelimit-tokens-remaining": "26999000", - "anthropic-ratelimit-tokens-reset": "2026-07-03T09:47:14Z", + "anthropic-ratelimit-tokens-reset": "2026-07-30T10:45:11Z", "cf-cache-status": "DYNAMIC", - "cf-ray": "a154f7b25eac5a71-VIE", + "cf-ray": "a233c5b33fe6af0a-VIE", "connection": "keep-alive", "content-security-policy": "default-src 'none'; frame-ancestors 'none'", "content-type": "application/json", - "date": "Fri, 03 Jul 2026 09:47:15 GMT", - "request-id": "req_011CceurwrY7ok1DJYpoafrL", + "date": "Thu, 30 Jul 2026 10:45:11 GMT", + "request-id": "req_011CdY77wnxv5deK7MBPWK4M", "server": "cloudflare", "strict-transport-security": "max-age=31536000; includeSubDomains; preload", - "traceresponse": "00-6d0e52c70a0e9210823203ccf3e101ed-16b4d602826029ee-01", + "traceresponse": "00-287733e004d2ad52dd6086fb2ee96e2b-d1bf3a8a0b02cafa-01", "vary": "Accept-Encoding", "x-robots-tag": "none" }, diff --git a/packages/@n8n/agents/src/__tests__/integration/cassettes/delegate-sub-agent/stream-emits-sub-agent-lifecycle-chunks-and-events.json b/packages/@n8n/agents/src/__tests__/integration/cassettes/delegate-sub-agent/stream-emits-sub-agent-lifecycle-chunks-and-events.json index 2a57499dbe1..05d60035740 100644 --- a/packages/@n8n/agents/src/__tests__/integration/cassettes/delegate-sub-agent/stream-emits-sub-agent-lifecycle-chunks-and-events.json +++ b/packages/@n8n/agents/src/__tests__/integration/cassettes/delegate-sub-agent/stream-emits-sub-agent-lifecycle-chunks-and-events.json @@ -72,33 +72,33 @@ }, "status": 200, "response": [ - "1f8b0800000000000013cd58dd4fdb30107fe7afb0ee652fe9d4947540de40438c21c1344042ac93e5c6b7c4d4b583edf03194ff7d72d2d2a4948f6d8ca42f55ee2eb99fefee77b60faf50b9884cd15a9620b58e19b7c6996311b903779b2144d05042307f86e80ea69aa3840862c9728e3dab9542d7fbd01bf606fdc1b0bf35d8820004f71fb109ed877ba7a79f37e34f79b679f2e5dbfaf8d78e519be731044bae2000a3a517306b85754c79bfb1560e9583e8fb8f00acd31935c8ac5610a95cca99c8e2658e2ac68690a36342dab92c9fc3172acb1d757a82ca42140e37b60288599c228d0d3227b4a24d93fe5c6f90f1c774f377bd07cc529ca261920ea70fed17da305dd61601e8dcd5459b015834572246ea041a88c0478633c37d90d54f347ee134410d1128ed28bb6242b2b144280a52fe8ab535ac323e0b261d4b1d4f56e77d8549e988e34db5dabade2f76f69ec31b6f58fe450033d70def9950c9923b025e088f21e4281d7b1a6169d24058499ac8eecd66f8f6df4949384a4c9843e2522465bc7b99d13c8f7d1ec9b536936a196f8ece3a836c4a9c268cc4a9909cb00495234a5fbf5f44f6f1b4eaecb9aceaac066a45aafeb550c2270a456b49738bf31ee19f73da0fb72eede5f1b7841d0ecf06071bfce2f6ebf9ded92904a0d8d4bf37cf16b5f9b80c47e92dcb1d44778567a1949e1ef77eb83018bb8a04af97c1f061062b0a5f58adee8d33669c60b21436d8d03290bb11d87cbceda3b7cf6b1c6d1fd908223202a1a468c26a1d98c211bc761bf82b20011975a7901cb39343cfcba2752855e594ed9b76aaa4177b4995b8eed44fcb4812cd1a896a198eac2a685bd96b34a41b21ba162e2578c33ad179fc2eda85a0c85be25261579e2d5b01541d1a09539ca8eea0d22e152a21286d97588611393eddd9de830e9cc7760f4fe8d101dd58af07a8654c61877677bf55e04d8633e2b75d39fca8bc128f2082676eb46fc5fb119ca4d8a1fe4c9c86a265141354338aef1ede5772cb982a9ab71e9af57004c58b9af19fdeddc3faa7e623bb95ab6a28a1b68ac64cad7e3b7fe164adf88fa3b5a5595838fcb8c4b7c594f261dcea3a3f61fa0dee456cbef6140000" + "1f8b0800000000000013cd58df4fdb30107ee7afb0ee652fe9d400059ab7323186106c688c69ac93e5c6b7d4e0da597ce1c750fef7c9495b9a52184c43495faade9d7bdf7d77beb30eafd050c426e89c48903b1219ad494122627740b7294204352504b3df10ddc1c44ad41041ac452eb1e3ac31489dcd4eafb3de5def75fbeb7d084049ff272ee1dd307c27bf6def6ced9eede617bf0fcf55fa49f68f14044bae2080cc6a2f10ce2947c278bfb135848620fafe23004736e5190a670d4426d77a2a72f82b4713634d289184d26e26cb67f0954973e2642fd13888c2de763f8058c463e47186829435bc6ed29de93314f231ddecacf780e918279809cd7b9387f6f7da70bcac2d02b0392d8a760270985da9183929cc2002cf8c1499f4249b9f98f9c0798216223096b8b8124a8b9146280a36fd146b6b58e57c4a271f691b5faecefc0a93d295c49b2ade45bd0f777a8ef0c61b965f1140b1e43955265972c5c00be13174123589a7d19526357495a48e6a6e36c576f0466b2651632208198d91956c77d2ccca3cf65964d736bb84397f4f71f86a281d6528268c2c132c1e2b2d9948d01033f6faed0376979366d3bfa5d5a60b88187b4ea82f2f97f08972b156f3dce1ac57f8df39ef8647fbbdc1e65773961f6d0c4ede9ff477cf3f8cb6f621002326fedc2c6fdce5a39290d25b9a13447785bf8d5afb6b32f7235586315597e1ffe5307c98c3ea2a5f386be6c6a9c848095d0aefef44f340ee86e0f2d12059acf08621a181a2690c74208710b121a8d7bafaff04cb686570f8b0a3360226604320b1c04fc378dce5b198a04f5b3d674de31a423953b89f29ed61ab9a6e4368d1c5f70595d876254fe8aa0f0c8c8316cc8d6bccd8b5a2f19ca486f130bc117ea4368d43df324f4ac32894f3cfc736d4091a264c6b1e1992194be3968dd284a17618b1cf5f762b600d031aecd7f86918cddef1698b2603ff78c8b737c26a5a353ea55a55c743c09ba591d934a4146342d9a66afe586e525a513efe31713a4636cf5ad30f9c98ca9d475b5e14a6ecc883fdbde353dea20ee41b50bb5ea6be1d16cf61e8a52ba070658cb32df0cae86a4a5888a6b6a65d5cf43c73595bbce2b67669bd1af6b68ad521af22705107fecc1f39f1dccb4a170000" ], "rawHeaders": { "anthropic-ratelimit-input-tokens-limit": "22500000", "anthropic-ratelimit-input-tokens-remaining": "22499000", - "anthropic-ratelimit-input-tokens-reset": "2026-07-03T09:47:15Z", + "anthropic-ratelimit-input-tokens-reset": "2026-07-30T10:45:12Z", "anthropic-ratelimit-output-tokens-limit": "4500000", "anthropic-ratelimit-output-tokens-remaining": "4500000", - "anthropic-ratelimit-output-tokens-reset": "2026-07-03T09:47:15Z", + "anthropic-ratelimit-output-tokens-reset": "2026-07-30T10:45:12Z", "anthropic-ratelimit-requests-limit": "20000", "anthropic-ratelimit-requests-remaining": "19999", - "anthropic-ratelimit-requests-reset": "2026-07-03T09:47:15Z", + "anthropic-ratelimit-requests-reset": "2026-07-30T10:45:12Z", "anthropic-ratelimit-tokens-limit": "27000000", "anthropic-ratelimit-tokens-remaining": "26999000", - "anthropic-ratelimit-tokens-reset": "2026-07-03T09:47:15Z", + "anthropic-ratelimit-tokens-reset": "2026-07-30T10:45:12Z", "cache-control": "no-cache", "cf-cache-status": "DYNAMIC", - "cf-ray": "a154f7bd1b9f5a71-VIE", + "cf-ray": "a233c5bd3cf3af0a-VIE", "connection": "keep-alive", "content-encoding": "gzip", "content-security-policy": "default-src 'none'; frame-ancestors 'none'", "content-type": "text/event-stream; charset=utf-8", - "date": "Fri, 03 Jul 2026 09:47:16 GMT", - "request-id": "req_011Cceus5ccUcu5S63pnkKab", + "date": "Thu, 30 Jul 2026 10:45:13 GMT", + "request-id": "req_011CdY785JetXbgfBdKXmCrT", "server": "cloudflare", "strict-transport-security": "max-age=31536000; includeSubDomains; preload", - "traceresponse": "00-12b7f86909b8222f45e788e80b7db1d9-5c63b97136e2016e-01", + "traceresponse": "00-05b1b866ae30456b98f16a457ac084d6-9ecac75622f59220-01", "transfer-encoding": "chunked", "vary": "Accept-Encoding", "x-robots-tag": "none" @@ -128,33 +128,39 @@ } ] } - ] + ], + "stream": true }, "status": 200, - "response": "{\"model\":\"claude-sonnet-4-5-20250929\",\"id\":\"msg_01PJHYpyiarrMV6MCKXgF3w5\",\"type\":\"message\",\"role\":\"assistant\",\"content\":[{\"type\":\"text\",\"text\":\"I have completed the delegated task.\\n\\n**What I did:**\\nI was instructed to respond with exactly one specific token and nothing else.\\n\\n**What I accomplished:**\\nSUBAGENT_OK_731\\n\\n**Important outputs:**\\nThe required token \\\"SUBAGENT_OK_731\\\" has been provided as the core output of this task.\\n\\n**Issues, assumptions, or limitations:**\\nNone. The task was straightforward with clear instructions and expected output.\"}],\"stop_reason\":\"end_turn\",\"stop_sequence\":null,\"stop_details\":null,\"usage\":{\"input_tokens\":199,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"cache_creation\":{\"ephemeral_5m_input_tokens\":0,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":102,\"service_tier\":\"standard\",\"inference_geo\":\"not_available\"}}", + "response": [ + "1f8b0800000000000013bd55c16edb300cbdf72b041e03a748b2065d7cdb8aad480b7487751b866530188bb5852a922bd2498b22ff3e5849363b4bb75eda5c0cf33d99ef910a494b7292aa05316341190b0639d22898aa4790878a20850e08c9ee1dd24758784d1652c82dd69afaec9d23e99ff4c7fdd160341e4c461348c0e8e6235c6483e1f04c7f3f7d7b517d2caa8b7a757e75f675ccc525247ba92081e06d134066c382aec99b7b27e404d21f3f1360f1551608d93b485d6ded36c4745793cba913d424682cef62f54ebe71552d99f85b720ce970324920c7bca42c0f8462bccbba8cc10e0f84fa296c77b6494055490b0a68b3f1e26ffe1f7458eea3eb047c2dedd02801a6b03439656228400a4d613406ddd4d8dd50687c67057948c179c97089c6e2dc12acd7aafd5b1f1dd1a6efdb926673ebf3dbc3dd3f4089f934dd6f4cb7f1c6f3f69cd07d438c8f14a0aba0ada132aed84baaa009c2533a3559c17feb8c948ece4da4abef376dab72aa4a5c5257ebab8b50b95f549684b4929294264b05c637e4dbe3999bb95eef5b89a2a64a1b9df67a3337552b64651c4ba8f348f50a1daf28a8959152d13de6621fe2f7e2755233f8fce5fdbbf30f57d7d9a7cbecf4cd70060a9d56ce4b695ca1c8327573611e75192e49c3fad5ab127dee6b8efaa68bca876644a8cd1f8623f5ba2415e8ae362196a3f15c22ab39915355f04ba3492b9cfb76bb5fbfd5c85124b190de967bca5c13270a99eb45d50c124e940fca9a859138583606afbca363d5d86cee45bc002c014d51ca8d0f2b0c3a3634b784c13ec0f326c0cb39e58a727363481f47294f8f205ffd6f02f9aa95fe90a7ddc23ae8a603424b7d67a300399d491d1c3c7bafac5f6eb1ec6d82c9c9fab0e143d56b63b039f50bc57ae4adf5070000" + ], "rawHeaders": { "anthropic-ratelimit-input-tokens-limit": "22500000", "anthropic-ratelimit-input-tokens-remaining": "22500000", - "anthropic-ratelimit-input-tokens-reset": "2026-07-03T09:47:19Z", + "anthropic-ratelimit-input-tokens-reset": "2026-07-30T10:45:15Z", "anthropic-ratelimit-output-tokens-limit": "4500000", "anthropic-ratelimit-output-tokens-remaining": "4500000", - "anthropic-ratelimit-output-tokens-reset": "2026-07-03T09:47:21Z", + "anthropic-ratelimit-output-tokens-reset": "2026-07-30T10:45:15Z", "anthropic-ratelimit-requests-limit": "20000", "anthropic-ratelimit-requests-remaining": "19999", - "anthropic-ratelimit-requests-reset": "2026-07-03T09:47:18Z", + "anthropic-ratelimit-requests-reset": "2026-07-30T10:45:15Z", "anthropic-ratelimit-tokens-limit": "27000000", "anthropic-ratelimit-tokens-remaining": "27000000", - "anthropic-ratelimit-tokens-reset": "2026-07-03T09:47:19Z", + "anthropic-ratelimit-tokens-reset": "2026-07-30T10:45:15Z", + "cache-control": "no-cache", "cf-cache-status": "DYNAMIC", - "cf-ray": "a154f7ceeda75a71-VIE", + "cf-ray": "a233c5d16eb8af0a-VIE", "connection": "keep-alive", + "content-encoding": "gzip", "content-security-policy": "default-src 'none'; frame-ancestors 'none'", - "content-type": "application/json", - "date": "Fri, 03 Jul 2026 09:47:22 GMT", - "request-id": "req_011CceusHMZEyxDy3W4jTvmC", + "content-type": "text/event-stream; charset=utf-8", + "date": "Thu, 30 Jul 2026 10:45:16 GMT", + "request-id": "req_011CdY78JTg2zufpwvK2Uvbp", "server": "cloudflare", "strict-transport-security": "max-age=31536000; includeSubDomains; preload", - "traceresponse": "00-0a027964946a157205a48b7fb54cd817-2e95f627fe900e50-01", + "traceresponse": "00-20c5cb3b9b6d07470c38f23d730e5d0a-7ecbe286d9ef4513-01", + "transfer-encoding": "chunked", "vary": "Accept-Encoding", "x-robots-tag": "none" }, @@ -192,7 +198,7 @@ }, { "type": "tool_use", - "id": "toolu_019qsqSRgaN5X2K7djyPZGXU", + "id": "toolu_01MG5A4WnVuM3AQFQ9BZHb6G", "name": "delegate_subagent", "input": { "subAgentId": "inline", @@ -211,8 +217,8 @@ "content": [ { "type": "tool_result", - "tool_use_id": "toolu_019qsqSRgaN5X2K7djyPZGXU", - "content": "{\"status\":\"completed\",\"taskPath\":\"/root/token_production_0\",\"model\":\"anthropic/claude-sonnet-4-5\",\"answer\":\"I have completed the delegated task.\\n\\n**What I did:**\\nI was instructed to respond with exactly one specific token and nothing else.\\n\\n**What I accomplished:**\\nSUBAGENT_OK_731\\n\\n**Important outputs:**\\nThe required token \\\"SUBAGENT_OK_731\\\" has been provided as the core output of this task.\\n\\n**Issues, assumptions, or limitations:**\\nNone. The task was straightforward with clear instructions and expected output.\",\"usage\":{\"promptTokens\":199,\"completionTokens\":102,\"totalTokens\":301,\"cost\":0.002127},\"finishReason\":\"stop\"}" + "tool_use_id": "toolu_01MG5A4WnVuM3AQFQ9BZHb6G", + "content": "{\"status\":\"completed\",\"taskPath\":\"/root/token_production_0\",\"model\":\"anthropic/claude-sonnet-4-5\",\"answer\":\"I have completed the delegated task.\\n\\n**What I did:**\\nI was instructed to answer with exactly the token \\\"SUBAGENT_OK_731\\\" and nothing else.\\n\\n**What I accomplished:**\\nSUBAGENT_OK_731\\n\\n**Important outputs:**\\nThe required token has been provided above as requested.\\n\\n**Issues, assumptions, or limitations:**\\nNone. The task was straightforward and clearly specified.\",\"usage\":{\"promptTokens\":199,\"completionTokens\":94,\"totalTokens\":293,\"cost\":0.002007},\"finishReason\":\"stop\"}" } ] } @@ -266,33 +272,33 @@ }, "status": 200, "response": [ - "1f8b0800000000000013bd53c16eda4010bdf315d69c8d847150c5de888452a92a8d0255ab54d56af04ec165d97576c72469e47faf1670b0a9697b697db1fcde1bcf7b331ada9161116dc97b5c91f48c8e7b0a1945f402fc5c1008689110d7df205e606b15691090692c15f5bd3586b87fd51ff58783e168301e8e21865c859ff8951c24e9a3ddcd6faeeebfff789e159f5dfa7673ffb874109fb582189cd50140ef73cf6842dfcc1a26c320be7c8dc1b32da423f4d68030a5d647c8d3434926a316a88831d7bec6cada7e6e8a9225db0d190f2219a7690c19666b929923e4dc1ad9960c6ade11aa4b5c5d1b3a50b1a62d39d472b4fd557f6293f5395bc5604b6e42c3183cb95d9e91e49c1c08089351e85418b2f9462e04972bb220c05896b8c35ce35213545554f57a7458f6718e72a96db6e95e798764df43d1d32168930f398f754c4f41b87f09802a6a3e2707456e56672d2308205c72a94833fedee55ed2727940daee5e65478fb793bbe96c7172fadf0dc8f9e26e3a792fe7934fd1fce3f5e4663a5bc80fefe49b3481eaf2d26cf1a79dd9a2e1a57b0ff56577866b91d008d33a3d20a32497cec05f1f60f50f2ff0ec649271753174d70c9b1c849a9f72983b7120050000" + "1f8b0800000000000013bd53c16ed34010bde72bac393b526c884af616aa08a48a8292002a08ada6de69b274bd6b76c7a1a8f2bf579bc4ad1d12ca057cb1fcdedb7d6f663cb421cb222929045c910c8c9e070a1945720ffcab2210d023216dbf41dc43e91419105018ac150d83b39678f872381ee6a37c3c9ae4134841ab784958c951969dababb3575f6aff292f7e96e7eafbc5d5cddb39a40756908277260218820e8c36fa16ce325906f1f55b0a815d253d61701684ad8dd943817ed4640bea818a18b5092d56b7f1b5ad6a96ec6ec90610d9241fa75060b126597842d6cecabe64d4f29e509de2dab3d181aa3595e4d1c871f9bbfe89cdd6876c9382abb90be52904f21b5d90644d1e04c4ce28f42a36d9de908f85cb153910601d4bdca036786d089a26797c9ac1807653df37545e1b57dc1e9ffd11c9d64cd1ddaee22e1f0bde9f63ba8bc2ed4b0074ec3bfe95b6ab03c3042208a7322a328c7fceb895f432ee907eb647d93ee187e97c76b93c91f33f65908be57c367d2717d3cfc9e2e3ebe99bd9e552bebf90672f32684e4fcd55cf0dcd559d2c49ef79bab6ddf2a3e5f548e894d35b4320ab24d7dec25f2f63f30fb7f1607db249f34ce9c77ad9e5e26ff90095166c1231050000" ], "rawHeaders": { "anthropic-ratelimit-input-tokens-limit": "22500000", "anthropic-ratelimit-input-tokens-remaining": "22499000", - "anthropic-ratelimit-input-tokens-reset": "2026-07-03T09:47:22Z", + "anthropic-ratelimit-input-tokens-reset": "2026-07-30T10:45:18Z", "anthropic-ratelimit-output-tokens-limit": "4500000", "anthropic-ratelimit-output-tokens-remaining": "4500000", - "anthropic-ratelimit-output-tokens-reset": "2026-07-03T09:47:22Z", + "anthropic-ratelimit-output-tokens-reset": "2026-07-30T10:45:18Z", "anthropic-ratelimit-requests-limit": "20000", "anthropic-ratelimit-requests-remaining": "19999", - "anthropic-ratelimit-requests-reset": "2026-07-03T09:47:22Z", + "anthropic-ratelimit-requests-reset": "2026-07-30T10:45:18Z", "anthropic-ratelimit-tokens-limit": "27000000", "anthropic-ratelimit-tokens-remaining": "26999000", - "anthropic-ratelimit-tokens-reset": "2026-07-03T09:47:22Z", + "anthropic-ratelimit-tokens-reset": "2026-07-30T10:45:18Z", "cache-control": "no-cache", "cf-cache-status": "DYNAMIC", - "cf-ray": "a154f7e859e95a71-VIE", + "cf-ray": "a233c5e73a73af0a-VIE", "connection": "keep-alive", "content-encoding": "gzip", "content-security-policy": "default-src 'none'; frame-ancestors 'none'", "content-type": "text/event-stream; charset=utf-8", - "date": "Fri, 03 Jul 2026 09:47:23 GMT", - "request-id": "req_011Cceusam4TfXT9UkcRzWBk", + "date": "Thu, 30 Jul 2026 10:45:19 GMT", + "request-id": "req_011CdY78ZM7unYh8K5fxmvc1", "server": "cloudflare", "strict-transport-security": "max-age=31536000; includeSubDomains; preload", - "traceresponse": "00-8bdab5bad4bef9717932e4712f9614a8-9f37a11903c556c2-01", + "traceresponse": "00-590c063aee0ac5a9b22cb12a484c5572-37ff21bc76d37853-01", "transfer-encoding": "chunked", "vary": "Accept-Encoding", "x-robots-tag": "none" diff --git a/packages/@n8n/agents/src/__tests__/integration/cassettes/provider-options/both-thinking-and-prompt-caching-work-simultaneously.json b/packages/@n8n/agents/src/__tests__/integration/cassettes/provider-options/both-reasoning-and-prompt-caching-work-simultaneously.json similarity index 99% rename from packages/@n8n/agents/src/__tests__/integration/cassettes/provider-options/both-thinking-and-prompt-caching-work-simultaneously.json rename to packages/@n8n/agents/src/__tests__/integration/cassettes/provider-options/both-reasoning-and-prompt-caching-work-simultaneously.json index 56e1b7f4b1e..99dccb54d21 100644 --- a/packages/@n8n/agents/src/__tests__/integration/cassettes/provider-options/both-thinking-and-prompt-caching-work-simultaneously.json +++ b/packages/@n8n/agents/src/__tests__/integration/cassettes/provider-options/both-reasoning-and-prompt-caching-work-simultaneously.json @@ -1,14 +1,14 @@ [ { "scope": "https://api.anthropic.com:443", - "method": "POST", "path": "/v1/messages", + "method": "POST", "body": { "model": "claude-sonnet-4-5", "max_tokens": 64000, "thinking": { "type": "enabled", - "budget_tokens": 5000 + "budget_tokens": 19200 }, "system": [ { @@ -77,7 +77,7 @@ "max_tokens": 64000, "thinking": { "type": "enabled", - "budget_tokens": 5000 + "budget_tokens": 19200 }, "system": [ { diff --git a/packages/@n8n/agents/src/__tests__/integration/cassettes/thinking/emits-reasoning-delta-chunks-when-thinking-is-enabled.json b/packages/@n8n/agents/src/__tests__/integration/cassettes/thinking/emits-reasoning-delta-chunks-when-reasoning-is-enabled.json similarity index 99% rename from packages/@n8n/agents/src/__tests__/integration/cassettes/thinking/emits-reasoning-delta-chunks-when-thinking-is-enabled.json rename to packages/@n8n/agents/src/__tests__/integration/cassettes/thinking/emits-reasoning-delta-chunks-when-reasoning-is-enabled.json index d9dd71e0e62..f40295ce5b2 100644 --- a/packages/@n8n/agents/src/__tests__/integration/cassettes/thinking/emits-reasoning-delta-chunks-when-thinking-is-enabled.json +++ b/packages/@n8n/agents/src/__tests__/integration/cassettes/thinking/emits-reasoning-delta-chunks-when-reasoning-is-enabled.json @@ -1,14 +1,14 @@ [ { "scope": "https://api.anthropic.com:443", - "method": "POST", "path": "/v1/messages", + "method": "POST", "body": { "model": "claude-sonnet-4-5", "max_tokens": 64000, "thinking": { "type": "enabled", - "budget_tokens": 5000 + "budget_tokens": 19200 }, "system": [ { diff --git a/packages/@n8n/agents/src/__tests__/integration/cassettes/thinking/works-with-a-reasoning-model-when-reasoning-is-enabled.json b/packages/@n8n/agents/src/__tests__/integration/cassettes/thinking/works-with-a-reasoning-model-when-reasoning-is-enabled.json new file mode 100644 index 00000000000..c0cba681d99 --- /dev/null +++ b/packages/@n8n/agents/src/__tests__/integration/cassettes/thinking/works-with-a-reasoning-model-when-reasoning-is-enabled.json @@ -0,0 +1,58 @@ +[ + { + "scope": "https://api.openai.com:443", + "path": "/v1/responses", + "method": "POST", + "body": { + "model": "gpt-5-mini", + "input": [ + { + "role": "developer", + "content": "You are a helpful assistant. Think carefully before answering." + }, + { + "role": "user", + "content": [ + { + "type": "input_text", + "text": "Find the smallest positive integer divisible by every integer from 1 through 15, and explain your method." + } + ] + } + ], + "reasoning": { + "effort": "medium", + "summary": "detailed" + }, + "stream": true + }, + "status": 200, + "response": "event: response.created\ndata: {\"type\":\"response.created\",\"response\":{\"id\":\"resp_0385c9a4aab47c4b006a6777286be88192b58c079eb072557e\",\"object\":\"response\",\"created_at\":1785165608,\"status\":\"in_progress\",\"background\":false,\"completed_at\":null,\"error\":null,\"frequency_penalty\":0.0,\"incomplete_details\":null,\"instructions\":null,\"max_output_tokens\":null,\"max_tool_calls\":null,\"model\":\"gpt-5-mini-2025-08-07\",\"moderation\":null,\"output\":[],\"parallel_tool_calls\":true,\"presence_penalty\":0.0,\"previous_response_id\":null,\"prompt_cache_key\":null,\"prompt_cache_retention\":\"in_memory\",\"reasoning\":{\"context\":\"current_turn\",\"effort\":\"medium\",\"mode\":\"standard\",\"summary\":\"detailed\"},\"safety_identifier\":null,\"service_tier\":\"auto\",\"store\":true,\"temperature\":1.0,\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"medium\"},\"tool_choice\":\"auto\",\"tool_usage\":{\"image_gen\":{\"input_tokens\":0,\"input_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"output_tokens\":0,\"output_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"total_tokens\":0},\"web_search\":{\"num_requests\":0}},\"tools\":[],\"top_logprobs\":0,\"top_p\":1.0,\"truncation\":\"disabled\",\"usage\":null,\"user\":null,\"metadata\":{}},\"sequence_number\":0}\n\nevent: response.in_progress\ndata: {\"type\":\"response.in_progress\",\"response\":{\"id\":\"resp_0385c9a4aab47c4b006a6777286be88192b58c079eb072557e\",\"object\":\"response\",\"created_at\":1785165608,\"status\":\"in_progress\",\"background\":false,\"completed_at\":null,\"error\":null,\"frequency_penalty\":0.0,\"incomplete_details\":null,\"instructions\":null,\"max_output_tokens\":null,\"max_tool_calls\":null,\"model\":\"gpt-5-mini-2025-08-07\",\"moderation\":null,\"output\":[],\"parallel_tool_calls\":true,\"presence_penalty\":0.0,\"previous_response_id\":null,\"prompt_cache_key\":null,\"prompt_cache_retention\":\"in_memory\",\"reasoning\":{\"context\":\"current_turn\",\"effort\":\"medium\",\"mode\":\"standard\",\"summary\":\"detailed\"},\"safety_identifier\":null,\"service_tier\":\"auto\",\"store\":true,\"temperature\":1.0,\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"medium\"},\"tool_choice\":\"auto\",\"tool_usage\":{\"image_gen\":{\"input_tokens\":0,\"input_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"output_tokens\":0,\"output_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"total_tokens\":0},\"web_search\":{\"num_requests\":0}},\"tools\":[],\"top_logprobs\":0,\"top_p\":1.0,\"truncation\":\"disabled\",\"usage\":null,\"user\":null,\"metadata\":{}},\"sequence_number\":1}\n\nevent: response.output_item.added\ndata: {\"type\":\"response.output_item.added\",\"item\":{\"id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"type\":\"reasoning\",\"content\":[],\"encrypted_content\":\"gAAAAABqZ3coXbzRbPIsPSApzLeZo9SOoV0dJV7S4tHs0XpZ9RTowpY12w5W9D0be86FI3iuY-dopKW7-5GPqAqSaCTKnZyLW-53zxs_ntHb0WMs7EPXKvdyF2xEY-MWxwU33PWLIrblS3XhX1XPXT4hmwLbsPweM5TtINqR0-qBiYD64KUK5yIl8uc-iIxZ6IHHocaE7u3-mnQd-i4RGWqphazgEXlVFL9L80K8Hzhg14seVrCC-hv32S3XvgIXz0RqNJl1zYVE5RWnokdOmfk7RxQo-Iaww7IoEW-2XXmQ5GSgH2w06DgVpV62kfjN_n54v4lK0_O6jz4HmwGSLHB8caY7S4wBL2-Uz_M8lXnMuAW55WfwAqUXoLFRXUzg8bAKIWfJFhl0Xixgtldv3kAZdGliCLTa75Lu77_fnmGIP_q_tyvSo_gQu4EDpi2ZlvcA7VVo8Ktlv0efr4sXKl5t3J3pU17lX0kq-dqqy-n8SVdG8MwDP8BGWTgofi5gyn8QH_gO2xjNqKSUdDmW_wYnz0167w_YGPvwfiuA2eD5PUn53sXZ82sAGAa9nDXrHk02EPBd1NLfSRIveNIChRnntcyHhaKNY4AQzOfwpyUOOlyJsgyp_lfl8meJ8PMpRIuZikvUGF-7c0JA8eXvocjxteD7bTXNlA_BbrjM6dqe9aJTRUNSvFALO6u2_Tg7LopH6VHtdHJXoJCDtMhVwoTHIcbenAPLbbCQzDWwjYOIH8bP66QMgZeJzBVWHS9D0SNAAZFNz8C5ceyhSWBzAVLoDclyK0FzCs8EPsVHGjH0lbOaApuAVa48WadP4bO9JE7IW8_wOWXRdkKDc8AK1D35goPNpzTaLg0OYgNxmg0t-F7Hc6n4syupSW1NFD2yIKEPjqvzJDwUEJYGSOVztrH2w1dM1o3GvPxDZLui1X7WK-vVCCH4LQ0=\",\"summary\":[]},\"output_index\":0,\"sequence_number\":2}\n\nevent: response.reasoning_summary_part.added\ndata: {\"type\":\"response.reasoning_summary_part.added\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"output_index\":0,\"part\":{\"type\":\"summary_text\",\"text\":\"\"},\"sequence_number\":3,\"summary_index\":0}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\"**Calculating LCM of 1 to 15**\\n\\nI\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"wwQIcWb3sfN3Iac\",\"output_index\":0,\"sequence_number\":4,\"summary_index\":0}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" need\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"O2PQh0eN6by\",\"output_index\":0,\"sequence_number\":5,\"summary_index\":0}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" to\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"VF9pYbG3Jz9pF\",\"output_index\":0,\"sequence_number\":6,\"summary_index\":0}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" find\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"y0tSugTgXKi\",\"output_index\":0,\"sequence_number\":7,\"summary_index\":0}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" the\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"pIvcIz0DH2x4\",\"output_index\":0,\"sequence_number\":8,\"summary_index\":0}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" least\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"bR1YBisS2L\",\"output_index\":0,\"sequence_number\":9,\"summary_index\":0}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" common\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"lx4etU1WF\",\"output_index\":0,\"sequence_number\":10,\"summary_index\":0}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" multiple\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"Hvt74Xf\",\"output_index\":0,\"sequence_number\":11,\"summary_index\":0}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" (\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"1n71OG92Eq3vuN\",\"output_index\":0,\"sequence_number\":12,\"summary_index\":0}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\"LC\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"wjne7AbT3fR3UA\",\"output_index\":0,\"sequence_number\":13,\"summary_index\":0}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\"M\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"39dk8tnINO5AwnV\",\"output_index\":0,\"sequence_number\":14,\"summary_index\":0}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\")\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"HcYU4pasJHPVpVg\",\"output_index\":0,\"sequence_number\":15,\"summary_index\":0}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" of\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"YYTQ5BKR0PZCv\",\"output_index\":0,\"sequence_number\":16,\"summary_index\":0}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" the\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"cDtMfe8Ni1cq\",\"output_index\":0,\"sequence_number\":17,\"summary_index\":0}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" numbers\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"eMBHMcV7\",\"output_index\":0,\"sequence_number\":18,\"summary_index\":0}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" from\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"kT0pL7WnRMr\",\"output_index\":0,\"sequence_number\":19,\"summary_index\":0}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" 1\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"ezGCeunXlw73AV\",\"output_index\":0,\"sequence_number\":20,\"summary_index\":0}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" to\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"coDWjliysQh8m\",\"output_index\":0,\"sequence_number\":21,\"summary_index\":0}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" 15\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"HlxDsd67okQy9\",\"output_index\":0,\"sequence_number\":22,\"summary_index\":0}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\".\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"VUp4fyak7zMjUhp\",\"output_index\":0,\"sequence_number\":23,\"summary_index\":0}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" First\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"x69PNduvB5\",\"output_index\":0,\"sequence_number\":24,\"summary_index\":0}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\",\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"llMyvsjHg7BD3kV\",\"output_index\":0,\"sequence_number\":25,\"summary_index\":0}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" I'll\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"ZN748oKFk2r\",\"output_index\":0,\"sequence_number\":26,\"summary_index\":0}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" list\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"haqx9yCQKVW\",\"output_index\":0,\"sequence_number\":27,\"summary_index\":0}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" the\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"NgwuG9W4eo9T\",\"output_index\":0,\"sequence_number\":28,\"summary_index\":0}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" prime\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"nCM9u1IVWs\",\"output_index\":0,\"sequence_number\":29,\"summary_index\":0}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" fact\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"lwXXKrANZ7E\",\"output_index\":0,\"sequence_number\":30,\"summary_index\":0}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\"oriz\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"V3tjJ7deaRHW\",\"output_index\":0,\"sequence_number\":31,\"summary_index\":0}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\"ations\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"UEpyGZXx8q\",\"output_index\":0,\"sequence_number\":32,\"summary_index\":0}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\":\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"NsVrecSAwxCHvs4\",\"output_index\":0,\"sequence_number\":33,\"summary_index\":0}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" the\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"WdZSgbZeajsz\",\"output_index\":0,\"sequence_number\":34,\"summary_index\":0}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" primes\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"jal5WzGJp\",\"output_index\":0,\"sequence_number\":35,\"summary_index\":0}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" I\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"3xgqzyWar8ienV\",\"output_index\":0,\"sequence_number\":36,\"summary_index\":0}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" identify\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"jmsCa6z\",\"output_index\":0,\"sequence_number\":37,\"summary_index\":0}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" are\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"SonDz1chwNyI\",\"output_index\":0,\"sequence_number\":38,\"summary_index\":0}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" 2\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"YqnDkaoJwkULXJ\",\"output_index\":0,\"sequence_number\":39,\"summary_index\":0}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\",\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"pSlvkr53HwKGumn\",\"output_index\":0,\"sequence_number\":40,\"summary_index\":0}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" 3\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"VNYy2sB1fJXCIi\",\"output_index\":0,\"sequence_number\":41,\"summary_index\":0}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\",\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"LygAJzYvv0MMM91\",\"output_index\":0,\"sequence_number\":42,\"summary_index\":0}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" 5\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"s4LMLA6ybfOgNh\",\"output_index\":0,\"sequence_number\":43,\"summary_index\":0}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\",\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"KWoinzhBIChHqIf\",\"output_index\":0,\"sequence_number\":44,\"summary_index\":0}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" 7\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"0vpTkLgS2FF7AQ\",\"output_index\":0,\"sequence_number\":45,\"summary_index\":0}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\",\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"3296tK7AE7g41ZS\",\"output_index\":0,\"sequence_number\":46,\"summary_index\":0}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" 11\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"HcISQC8r8H4ru\",\"output_index\":0,\"sequence_number\":47,\"summary_index\":0}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\",\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"mPrpfPXGP5VuynJ\",\"output_index\":0,\"sequence_number\":48,\"summary_index\":0}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" and\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"FMHMK37c3J9R\",\"output_index\":0,\"sequence_number\":49,\"summary_index\":0}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" 13\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"vptTWuDoa25HK\",\"output_index\":0,\"sequence_number\":50,\"summary_index\":0}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\".\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"9LBpkrbzj3js0Yb\",\"output_index\":0,\"sequence_number\":51,\"summary_index\":0}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" Then\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"iZOblnnglsk\",\"output_index\":0,\"sequence_number\":52,\"summary_index\":0}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\",\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"LgUJSrcDQt5Tb7U\",\"output_index\":0,\"sequence_number\":53,\"summary_index\":0}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" I\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"E15aAZGsqznZJL\",\"output_index\":0,\"sequence_number\":54,\"summary_index\":0}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\"’ll\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"wRTSZrNI83hYF\",\"output_index\":0,\"sequence_number\":55,\"summary_index\":0}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" find\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"uTnqrRP2mAF\",\"output_index\":0,\"sequence_number\":56,\"summary_index\":0}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" the\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"A9MWGQeooHX3\",\"output_index\":0,\"sequence_number\":57,\"summary_index\":0}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" highest\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"8AH77uQV\",\"output_index\":0,\"sequence_number\":58,\"summary_index\":0}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" powers\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"xSQUBiUFL\",\"output_index\":0,\"sequence_number\":59,\"summary_index\":0}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" of\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"44OrmoS7iPijB\",\"output_index\":0,\"sequence_number\":60,\"summary_index\":0}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" these\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"uSXsTNymKa\",\"output_index\":0,\"sequence_number\":61,\"summary_index\":0}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" primes\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"Nzbackua0\",\"output_index\":0,\"sequence_number\":62,\"summary_index\":0}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" that\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"Wz50JoWWp83\",\"output_index\":0,\"sequence_number\":63,\"summary_index\":0}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" are\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"fCkZ2a6mdlqv\",\"output_index\":0,\"sequence_number\":64,\"summary_index\":0}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" less\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"aZTq5xXgbFb\",\"output_index\":0,\"sequence_number\":65,\"summary_index\":0}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" than\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"txeRmBvDUNZ\",\"output_index\":0,\"sequence_number\":66,\"summary_index\":0}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" or\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"nZ5QySlEqbGja\",\"output_index\":0,\"sequence_number\":67,\"summary_index\":0}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" equal\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"RlnLmmZeld\",\"output_index\":0,\"sequence_number\":68,\"summary_index\":0}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" to\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"V1relWSAcLVIw\",\"output_index\":0,\"sequence_number\":69,\"summary_index\":0}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" 15\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"O6TUnYoM7TEy3\",\"output_index\":0,\"sequence_number\":70,\"summary_index\":0}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\".\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"jlqEBEXLGgW01B2\",\"output_index\":0,\"sequence_number\":71,\"summary_index\":0}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" My\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"wvZKP7dNUuF3U\",\"output_index\":0,\"sequence_number\":72,\"summary_index\":0}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" calculation\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"mciM\",\"output_index\":0,\"sequence_number\":73,\"summary_index\":0}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" leads\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"0dkwajUySD\",\"output_index\":0,\"sequence_number\":74,\"summary_index\":0}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" to\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"DS67HLH59Jjh9\",\"output_index\":0,\"sequence_number\":75,\"summary_index\":0}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" the\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"8S97UgFWRvh3\",\"output_index\":0,\"sequence_number\":76,\"summary_index\":0}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" result\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"xZ96nvsTL\",\"output_index\":0,\"sequence_number\":77,\"summary_index\":0}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" 360\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"OJtv0L1xCsMX\",\"output_index\":0,\"sequence_number\":78,\"summary_index\":0}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\"360\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"iBGV87fFPBg0a\",\"output_index\":0,\"sequence_number\":79,\"summary_index\":0}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\",\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"piXyDuQlP8lGvlw\",\"output_index\":0,\"sequence_number\":80,\"summary_index\":0}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" confirming\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"AjDoe\",\"output_index\":0,\"sequence_number\":81,\"summary_index\":0}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" that\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"aBhzVymEPHM\",\"output_index\":0,\"sequence_number\":82,\"summary_index\":0}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" this\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"aGAZNDCFjLp\",\"output_index\":0,\"sequence_number\":83,\"summary_index\":0}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" is\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"zA27wARp0czXo\",\"output_index\":0,\"sequence_number\":84,\"summary_index\":0}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" indeed\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"jNRs7BaNv\",\"output_index\":0,\"sequence_number\":85,\"summary_index\":0}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" the\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"6yUpjNUsimZO\",\"output_index\":0,\"sequence_number\":86,\"summary_index\":0}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" L\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"OS1bplJ8kDc3Vj\",\"output_index\":0,\"sequence_number\":87,\"summary_index\":0}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\"CM\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"IEZ817nAmshSl5\",\"output_index\":0,\"sequence_number\":88,\"summary_index\":0}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" of\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"Klykq97LYqo67\",\"output_index\":0,\"sequence_number\":89,\"summary_index\":0}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" 1\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"Nr6sDqnH4Y50dJ\",\"output_index\":0,\"sequence_number\":90,\"summary_index\":0}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" to\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"dCApV7yXRWq5I\",\"output_index\":0,\"sequence_number\":91,\"summary_index\":0}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" 15\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"X8fS2QXBI4i76\",\"output_index\":0,\"sequence_number\":92,\"summary_index\":0}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\".\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"A5utDxicNzH8qNV\",\"output_index\":0,\"sequence_number\":93,\"summary_index\":0}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" I\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"I8XHqfYXpnXRQc\",\"output_index\":0,\"sequence_number\":94,\"summary_index\":0}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" should\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"R3Y4poOZh\",\"output_index\":0,\"sequence_number\":95,\"summary_index\":0}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" explain\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"nRmwFU96\",\"output_index\":0,\"sequence_number\":96,\"summary_index\":0}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" my\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"agTbkJqbK9gtf\",\"output_index\":0,\"sequence_number\":97,\"summary_index\":0}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" method\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"ZAhFBLGdX\",\"output_index\":0,\"sequence_number\":98,\"summary_index\":0}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" and\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"WJFagyhFbW3X\",\"output_index\":0,\"sequence_number\":99,\"summary_index\":0}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" reasoning\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"VVf8ye\",\"output_index\":0,\"sequence_number\":100,\"summary_index\":0}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" clearly\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"QWytk7QD\",\"output_index\":0,\"sequence_number\":101,\"summary_index\":0}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\"!\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"R03FuDMvw0Qt95I\",\"output_index\":0,\"sequence_number\":102,\"summary_index\":0}\n\nevent: response.reasoning_summary_text.done\ndata: {\"type\":\"response.reasoning_summary_text.done\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"output_index\":0,\"sequence_number\":103,\"summary_index\":0,\"text\":\"**Calculating LCM of 1 to 15**\\n\\nI need to find the least common multiple (LCM) of the numbers from 1 to 15. First, I'll list the prime factorizations: the primes I identify are 2, 3, 5, 7, 11, and 13. Then, I’ll find the highest powers of these primes that are less than or equal to 15. My calculation leads to the result 360360, confirming that this is indeed the LCM of 1 to 15. I should explain my method and reasoning clearly!\"}\n\nevent: response.reasoning_summary_part.done\ndata: {\"type\":\"response.reasoning_summary_part.done\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"output_index\":0,\"part\":{\"type\":\"summary_text\",\"text\":\"**Calculating LCM of 1 to 15**\\n\\nI need to find the least common multiple (LCM) of the numbers from 1 to 15. First, I'll list the prime factorizations: the primes I identify are 2, 3, 5, 7, 11, and 13. Then, I’ll find the highest powers of these primes that are less than or equal to 15. My calculation leads to the result 360360, confirming that this is indeed the LCM of 1 to 15. I should explain my method and reasoning clearly!\"},\"sequence_number\":104,\"summary_index\":0}\n\nevent: response.reasoning_summary_part.added\ndata: {\"type\":\"response.reasoning_summary_part.added\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"output_index\":0,\"part\":{\"type\":\"summary_text\",\"text\":\"\"},\"sequence_number\":105,\"summary_index\":1}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\"**Finalizing LCM Explanation**\\n\\nI\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"RyOoGpi9KcFGZOz\",\"output_index\":0,\"sequence_number\":106,\"summary_index\":1}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\"’m\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"88S0JcEUeXX1Cg\",\"output_index\":0,\"sequence_number\":107,\"summary_index\":1}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" considering\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"r9Z4\",\"output_index\":0,\"sequence_number\":108,\"summary_index\":1}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" how\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"BJwjR8Hz4SDj\",\"output_index\":0,\"sequence_number\":109,\"summary_index\":1}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" to\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"GkrPQTngW7V6f\",\"output_index\":0,\"sequence_number\":110,\"summary_index\":1}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" demonstrate\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"65AD\",\"output_index\":0,\"sequence_number\":111,\"summary_index\":1}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" that\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"DL7pafzQTlI\",\"output_index\":0,\"sequence_number\":112,\"summary_index\":1}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" the\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"DsSgbMrMtqWQ\",\"output_index\":0,\"sequence_number\":113,\"summary_index\":1}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" L\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"XVyVubo1oWFObV\",\"output_index\":0,\"sequence_number\":114,\"summary_index\":1}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\"CM\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"cY1VPMi9P5Lkge\",\"output_index\":0,\"sequence_number\":115,\"summary_index\":1}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" is\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"43laRR4vuXpdq\",\"output_index\":0,\"sequence_number\":116,\"summary_index\":1}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" divisible\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"h7jboP\",\"output_index\":0,\"sequence_number\":117,\"summary_index\":1}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" by\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"pNCqeP6ps1hHX\",\"output_index\":0,\"sequence_number\":118,\"summary_index\":1}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" all\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"6R4z1oECUS2r\",\"output_index\":0,\"sequence_number\":119,\"summary_index\":1}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" numbers\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"dDe4idtn\",\"output_index\":0,\"sequence_number\":120,\"summary_index\":1}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" from\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"zHjj09VzFzz\",\"output_index\":0,\"sequence_number\":121,\"summary_index\":1}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" 1\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"n1TISICWGw2LoK\",\"output_index\":0,\"sequence_number\":122,\"summary_index\":1}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" to\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"JQaMpaW21Cgea\",\"output_index\":0,\"sequence_number\":123,\"summary_index\":1}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" 15\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"5UVdXzaCJhbNx\",\"output_index\":0,\"sequence_number\":124,\"summary_index\":1}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\".\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"L0IwCE7mlh3l0gb\",\"output_index\":0,\"sequence_number\":125,\"summary_index\":1}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" I\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"N0FeqsrRT0thXt\",\"output_index\":0,\"sequence_number\":126,\"summary_index\":1}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" should\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"MYFEN1pfQ\",\"output_index\":0,\"sequence_number\":127,\"summary_index\":1}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" mention\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"42ZgpvV0\",\"output_index\":0,\"sequence_number\":128,\"summary_index\":1}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" that\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"I5NOMr5yy3A\",\"output_index\":0,\"sequence_number\":129,\"summary_index\":1}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" the\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"crhkIBqfrzrZ\",\"output_index\":0,\"sequence_number\":130,\"summary_index\":1}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" L\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"zwDtcUt0O1peb5\",\"output_index\":0,\"sequence_number\":131,\"summary_index\":1}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\"CM\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"5d07ov9fMH6sOa\",\"output_index\":0,\"sequence_number\":132,\"summary_index\":1}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" of\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"R7xjpnymjyfnO\",\"output_index\":0,\"sequence_number\":133,\"summary_index\":1}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" a\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"DAu0tku1jgls5O\",\"output_index\":0,\"sequence_number\":134,\"summary_index\":1}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" set\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"j8CZon7PxC1I\",\"output_index\":0,\"sequence_number\":135,\"summary_index\":1}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" represents\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"uw466\",\"output_index\":0,\"sequence_number\":136,\"summary_index\":1}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" the\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"zXnpTl6jlxvJ\",\"output_index\":0,\"sequence_number\":137,\"summary_index\":1}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" smallest\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"y8Zsf0G\",\"output_index\":0,\"sequence_number\":138,\"summary_index\":1}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" number\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"J7qG7hON0\",\"output_index\":0,\"sequence_number\":139,\"summary_index\":1}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" that\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"oQ7Qk9P3pz7\",\"output_index\":0,\"sequence_number\":140,\"summary_index\":1}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" can\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"2PcuJzQxeahd\",\"output_index\":0,\"sequence_number\":141,\"summary_index\":1}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" be\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"K9xlpoEdwuqRN\",\"output_index\":0,\"sequence_number\":142,\"summary_index\":1}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" divided\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"0neSJEeG\",\"output_index\":0,\"sequence_number\":143,\"summary_index\":1}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" by\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"a27EADFrdmh7X\",\"output_index\":0,\"sequence_number\":144,\"summary_index\":1}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" each\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"09AsIPT3lwy\",\"output_index\":0,\"sequence_number\":145,\"summary_index\":1}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" integer\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"WQWQNdkA\",\"output_index\":0,\"sequence_number\":146,\"summary_index\":1}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" in\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"6CNV3YRQIkodN\",\"output_index\":0,\"sequence_number\":147,\"summary_index\":1}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" that\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"Re15THcHcLi\",\"output_index\":0,\"sequence_number\":148,\"summary_index\":1}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" set\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"nR9ba1Pkl5FR\",\"output_index\":0,\"sequence_number\":149,\"summary_index\":1}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\".\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"n9TxTof1YFi5zkc\",\"output_index\":0,\"sequence_number\":150,\"summary_index\":1}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" While\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"tENNSFeTdU\",\"output_index\":0,\"sequence_number\":151,\"summary_index\":1}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" it\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"XJC19ZVhQlLzy\",\"output_index\":0,\"sequence_number\":152,\"summary_index\":1}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" might\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"t2MA8vmNce\",\"output_index\":0,\"sequence_number\":153,\"summary_index\":1}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" be\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"OETyKpbyBZaKh\",\"output_index\":0,\"sequence_number\":154,\"summary_index\":1}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" nice\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"Xbqc1rlipIs\",\"output_index\":0,\"sequence_number\":155,\"summary_index\":1}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" to\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"WW9uAiGUv1E4i\",\"output_index\":0,\"sequence_number\":156,\"summary_index\":1}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" verify\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"rv8COX8Mw\",\"output_index\":0,\"sequence_number\":157,\"summary_index\":1}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" this\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"gzVzUhX3jAs\",\"output_index\":0,\"sequence_number\":158,\"summary_index\":1}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" for\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"BCeMUKKYWYBx\",\"output_index\":0,\"sequence_number\":159,\"summary_index\":1}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" each\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"wih7x87XucT\",\"output_index\":0,\"sequence_number\":160,\"summary_index\":1}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" integer\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"gw5GHnpZ\",\"output_index\":0,\"sequence_number\":161,\"summary_index\":1}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\",\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"dfwuwHqQhkoKUEl\",\"output_index\":0,\"sequence_number\":162,\"summary_index\":1}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" I\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"IPYwuQajorfoEO\",\"output_index\":0,\"sequence_number\":163,\"summary_index\":1}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" don\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"4Bi5lmHMFFgq\",\"output_index\":0,\"sequence_number\":164,\"summary_index\":1}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\"’t\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"Q3dShjWbpXCmWU\",\"output_index\":0,\"sequence_number\":165,\"summary_index\":1}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" think\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"YJPIu3g1Ov\",\"output_index\":0,\"sequence_number\":166,\"summary_index\":1}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" it\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"EY6iobRFbkPHG\",\"output_index\":0,\"sequence_number\":167,\"summary_index\":1}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\"’s\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"gkCFjivhRY9IYn\",\"output_index\":0,\"sequence_number\":168,\"summary_index\":1}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" strictly\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"qFzg5L3\",\"output_index\":0,\"sequence_number\":169,\"summary_index\":1}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" necessary\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"uX7DHv\",\"output_index\":0,\"sequence_number\":170,\"summary_index\":1}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" for\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"bJv548wBOM50\",\"output_index\":0,\"sequence_number\":171,\"summary_index\":1}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" the\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"TxPdYTgXG5y3\",\"output_index\":0,\"sequence_number\":172,\"summary_index\":1}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" final\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"aIpk3VfSoL\",\"output_index\":0,\"sequence_number\":173,\"summary_index\":1}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" explanation\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"osjZ\",\"output_index\":0,\"sequence_number\":174,\"summary_index\":1}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\".\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"clzehW2Drxi7Gcb\",\"output_index\":0,\"sequence_number\":175,\"summary_index\":1}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" I\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"3CFQY7sl8LeEsz\",\"output_index\":0,\"sequence_number\":176,\"summary_index\":1}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\"’ll\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"yxwz2wz2XNYD4\",\"output_index\":0,\"sequence_number\":177,\"summary_index\":1}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" focus\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"SWu96BZwEo\",\"output_index\":0,\"sequence_number\":178,\"summary_index\":1}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" on\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"BjOqqgvgUVz4R\",\"output_index\":0,\"sequence_number\":179,\"summary_index\":1}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" producing\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"jJ7aWw\",\"output_index\":0,\"sequence_number\":180,\"summary_index\":1}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" a\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"OLHhNYoL9GnRkR\",\"output_index\":0,\"sequence_number\":181,\"summary_index\":1}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" succinct\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"IopxMtY\",\"output_index\":0,\"sequence_number\":182,\"summary_index\":1}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" answer\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"tpjDSKiSG\",\"output_index\":0,\"sequence_number\":183,\"summary_index\":1}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" that\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"AkKtH9yMmCv\",\"output_index\":0,\"sequence_number\":184,\"summary_index\":1}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" clearly\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"lU9tW52R\",\"output_index\":0,\"sequence_number\":185,\"summary_index\":1}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" communicates\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"6LW\",\"output_index\":0,\"sequence_number\":186,\"summary_index\":1}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" the\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"t0lGQ9SNfk0C\",\"output_index\":0,\"sequence_number\":187,\"summary_index\":1}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" relevant\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"aXJ5bgO\",\"output_index\":0,\"sequence_number\":188,\"summary_index\":1}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" information\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"zqBW\",\"output_index\":0,\"sequence_number\":189,\"summary_index\":1}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\".\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"obfuscation\":\"Qt9PpaRdKK3B3ng\",\"output_index\":0,\"sequence_number\":190,\"summary_index\":1}\n\nevent: response.reasoning_summary_text.done\ndata: {\"type\":\"response.reasoning_summary_text.done\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"output_index\":0,\"sequence_number\":191,\"summary_index\":1,\"text\":\"**Finalizing LCM Explanation**\\n\\nI’m considering how to demonstrate that the LCM is divisible by all numbers from 1 to 15. I should mention that the LCM of a set represents the smallest number that can be divided by each integer in that set. While it might be nice to verify this for each integer, I don’t think it’s strictly necessary for the final explanation. I’ll focus on producing a succinct answer that clearly communicates the relevant information.\"}\n\nevent: response.reasoning_summary_part.done\ndata: {\"type\":\"response.reasoning_summary_part.done\",\"item_id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"output_index\":0,\"part\":{\"type\":\"summary_text\",\"text\":\"**Finalizing LCM Explanation**\\n\\nI’m considering how to demonstrate that the LCM is divisible by all numbers from 1 to 15. I should mention that the LCM of a set represents the smallest number that can be divided by each integer in that set. While it might be nice to verify this for each integer, I don’t think it’s strictly necessary for the final explanation. I’ll focus on producing a succinct answer that clearly communicates the relevant information.\"},\"sequence_number\":192,\"summary_index\":1}\n\nevent: response.output_item.done\ndata: {\"type\":\"response.output_item.done\",\"item\":{\"id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"type\":\"reasoning\",\"content\":[],\"encrypted_content\":\"gAAAAABqZ3cv_S7nTY2Delw1nv746ki3p1C5yv_UOJ-_Ro2S_PuCcJbBn661Ewpv2QrKtLVfcUTBUqqfxcXno6Edpm0K7LuzN16HdlmM8UlSiiLsxOo1pOvCkPloCIKLgEBks_z9sw32sqTR--1LFJA88MBFYW71XBSKMx_k00K2ezy9Pb_S0O75N843wqpb9u_XjDNDmgnkS_4ezXXcxZbrz0C0wvSH-7r7-HenMl_BClk1a-V7bF2egak_bqDjssEjjVki2VhFpvUEK8guATFK54OSXj-2JnZbn8yrSOam78bIlrrnsXbhO04SY6A0L1QiTDRUtcFOu9QmgoaCk3q47U-uNAcW-MxQUIYBYve3gBAfpotpB99o_dJDBp_S7gxAhlOY1JAjDcjrY_y8VbBXhIzQO6m-rHbkqZntsiwG4LVc73Qz5JX1HyoeAwD7ZuI2m2NQ4Kzo4TypgRaxHTIByxcd0v2E3dgSVYNaYvTvwbTAXJeQjtO6_smqCZjNbrhxSOZhG5S-BLacrZzbnaJVcIrrn1wOdbRQXNp4RGGn9xEf5P2GqxEJNsdwjcnRqT8PkKMvzxFpSGtv5gj6KuPcTcnHggHFBY-pKLyrkmcffIBXk-oE6lZCqGa0NjESew582aCkhzPwEf3aszsqwQ93Wq53mZUZ6E7vUwKWSj7E_YsWUgfQm4CRzieGXelQJJvc0L9YvGANo7Llr5w2Gi6KyN8sKFQo2Q2hWZ0eDLD2es1dYWWfunKhJedMs7J63g5_NWUqp51wWs_zB9lKvxRx5yfItb-cr-6EQqRcz2HICgO6wjB6_vyCPNpHKsb6un-R3cRnUAPaR7f8W8ywrtHUAsVSlSCHySdxa2GLfXPz2GpoFl2468A6uGNFzbqietGgEqvtQMHB39wEQC6hGu9TUQRXyuDIImHivVftHoF7LuevN1lu4fkLJZ3dQHwBShO4YwGI4E3Cc0Fw4L0b6gJyS2GJjBmnPOSvb7SF5MPwwv80NpxrPI6mM2pkmhnaUZ4FE0kXVYnlY3kWtLfK2YnXNGWLYSrfwoj36B3vBY6Pb6pPH-9TADEe_PGgR4lJfeawd74JXZ1Gai82ar3BzleYoJe6rBMyHcWf06DJuh1kcrm3zM-AlhgyFuVOtEsyeKmtKdsj0ftspHAn54gMBLNNmu7UO5vETwIAu7IkIb9nq1L01Tc3IAABdeC1arNRTbMKRAZqABxpDCftOiNCxc7EwSaNFVbdDx_PJKJutJ4CacZxefWlJtRFPpgmll53cD_Z2YNPW-IXqq-H0JiSU0knXr-pUJnhDvqSyxMXYibRSWvrkX1MGDNVsTI_C8VNS-tzLTE0WZFNBTDu8HWmARwONg10ztpTNubYlmvW_pXyu3wFOe8AyGpdgweNO8L9sawbu0V47vgEcItkxfkOyG9oPN2lkldZ7VveuT3HMRx1etvT4sr6u_py-sQWafExbgGbSbFY-L6Y_zSRob-iIXaFvnV-4TO6k0qAn2n-aNCcQBnEQwV9VjEyoUCbyMe3toy7oiXsYrTkm-JAZ4zLrsW72fnhCnKFBiojAXfbmrAIBGjBDyZuat4rDMgHIuKSnlBABm55iQ-A1b7o8UNlvu4Lf7gjROffitb_6O7Sp6OG_FEtUMU80WAleGMmj2yekoGjmmlJiG42ZV6R2v4GJmsznhhcQ9u7YLliLx5t68U1nmavdPyEu_5f8R4plwglG-BwroPUvWtHETQCxiU6lHiFe_WDi80VGzgZ9Ujg6mRXtWXt60WyXXOkpqlbT_TYgvpBTClIcXwHJkt0NkeNHQkq2jLW9Vy3Ueb3e6TGtYebxZPfJJmCNK_pzhU6UEcipZsMggpHG2Z2P23sZXEFeGNLVCEwxLDv1zypzSlMYuQyzY9Hsv0kiXAyLKA67pqApWOK42EbTurYmLTZOIVV0JagKRz1UZ24Yjwmj5v4eqpBwy-Dc_XfF368tPN_M9TXnp0Ec_EWGxF49huTP4i_fqGyLogv9PdcLfJFd3W_Xz7vJNZJo0rhXXb4yV2rBxudKNiVjbfsc9YqoeauUPtgV3Pz1XRMCUmaBY1FivPAxhAztHT9QHCLER8lC9rCApEtEDwyYoj5gOEa1OquEiOUjHEcYpiyL0_eYgKsKv7Rmgtt7xb_9eLY51JROJa9E4PCUfTzbVLPiY1q6mYXNkOa5kEtHxiTj9fb1yKi_uAHznvFDaBUvXf4F2nOyH3GckAxNr_RnYMkTZkiHLD0RW3HF6kIeA1MwheTEVqXcxJTdm3n3ehu3YSttwPtOJ4V13KboBfGtAcdNJzf6Py9TMeUqEe6mZxeQ72z83XszNnixdmltTDkPXA_mG6w6kOPnKaAQqwkEFgjQKBsXYOwrgU8-a_mcZKFoRmoPUfAqTN0S-SMDzFoO6u3in-VZVwxHM273NuBEakkByc37SePJk3lOV3oXvR43QZEOGCQUgozzeXQ1LlUR5MBLx56jvlBJxirDNZYF4XsA0_fWJF-HPvhes0GV1dB4HIPEzm13iSayEcjwIrdRVvua1CSsKK_LJY8j_0pd8MgV02b\",\"summary\":[{\"type\":\"summary_text\",\"text\":\"**Calculating LCM of 1 to 15**\\n\\nI need to find the least common multiple (LCM) of the numbers from 1 to 15. First, I'll list the prime factorizations: the primes I identify are 2, 3, 5, 7, 11, and 13. Then, I’ll find the highest powers of these primes that are less than or equal to 15. My calculation leads to the result 360360, confirming that this is indeed the LCM of 1 to 15. I should explain my method and reasoning clearly!\"},{\"type\":\"summary_text\",\"text\":\"**Finalizing LCM Explanation**\\n\\nI’m considering how to demonstrate that the LCM is divisible by all numbers from 1 to 15. I should mention that the LCM of a set represents the smallest number that can be divided by each integer in that set. While it might be nice to verify this for each integer, I don’t think it’s strictly necessary for the final explanation. I’ll focus on producing a succinct answer that clearly communicates the relevant information.\"}]},\"output_index\":0,\"sequence_number\":193}\n\nevent: response.output_item.added\ndata: {\"type\":\"response.output_item.added\",\"item\":{\"id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"type\":\"message\",\"status\":\"in_progress\",\"content\":[],\"role\":\"assistant\"},\"output_index\":1,\"sequence_number\":194}\n\nevent: response.content_part.added\ndata: {\"type\":\"response.content_part.added\",\"content_index\":0,\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"output_index\":1,\"part\":{\"type\":\"output_text\",\"annotations\":[],\"logprobs\":[],\"text\":\"\"},\"sequence_number\":195}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"We\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"Mz5bckRpRunhTP\",\"output_index\":1,\"sequence_number\":196}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" want\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"rO4hox9fm9B\",\"output_index\":1,\"sequence_number\":197}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" the\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"K24VAgWas71g\",\"output_index\":1,\"sequence_number\":198}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" least\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"CkkwKKQbzN\",\"output_index\":1,\"sequence_number\":199}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" common\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"zNBfbEwLg\",\"output_index\":1,\"sequence_number\":200}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" multiple\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"09YD2tS\",\"output_index\":1,\"sequence_number\":201}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" (\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"RBJsOr1rc73j3S\",\"output_index\":1,\"sequence_number\":202}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"LC\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"4rZBtxS3ckuyFf\",\"output_index\":1,\"sequence_number\":203}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"M\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"n5wvnN42tqdkGKF\",\"output_index\":1,\"sequence_number\":204}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\")\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"XFTEEevIOnJHsim\",\"output_index\":1,\"sequence_number\":205}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" of\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"vFQxTa18bMu7O\",\"output_index\":1,\"sequence_number\":206}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" \",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"x0WgmqJyjOOaRjN\",\"output_index\":1,\"sequence_number\":207}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"1\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"8gmnaReUNQtwn5L\",\"output_index\":1,\"sequence_number\":208}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\",\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"XpmAQAqtwpqu1hi\",\"output_index\":1,\"sequence_number\":209}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"2\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"nFbE07m9BKnTe4a\",\"output_index\":1,\"sequence_number\":210}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\",...\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"LokARqGndZVP\",\"output_index\":1,\"sequence_number\":211}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\",\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"qoFRvkNtlnDyL7u\",\"output_index\":1,\"sequence_number\":212}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"15\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"XqcIoAco4cneOb\",\"output_index\":1,\"sequence_number\":213}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\".\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"8m8RsCGlCzp1V5q\",\"output_index\":1,\"sequence_number\":214}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" For\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"ViEQx82SyX2A\",\"output_index\":1,\"sequence_number\":215}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" each\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"inzSqChmLCT\",\"output_index\":1,\"sequence_number\":216}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" prime\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"nYY9CucTxx\",\"output_index\":1,\"sequence_number\":217}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" p\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"LbE7wFmgyWM0Qs\",\"output_index\":1,\"sequence_number\":218}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" ≤\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"YvjmtQSee8W7jD\",\"output_index\":1,\"sequence_number\":219}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" \",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"mrFW6FsBEjQnrEo\",\"output_index\":1,\"sequence_number\":220}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"15\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"UjLP22mZaxUWOt\",\"output_index\":1,\"sequence_number\":221}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\",\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"XQ71bzRmIA99bDl\",\"output_index\":1,\"sequence_number\":222}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" take\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"mO7UdvnaCRm\",\"output_index\":1,\"sequence_number\":223}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" the\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"gQAdgolSi65q\",\"output_index\":1,\"sequence_number\":224}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" highest\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"1AAEB1Ro\",\"output_index\":1,\"sequence_number\":225}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" power\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"yOMl0bROuj\",\"output_index\":1,\"sequence_number\":226}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" p\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"vhP8hyW3gQWrP2\",\"output_index\":1,\"sequence_number\":227}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"^\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"7fdplEjMWNhySIv\",\"output_index\":1,\"sequence_number\":228}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"k\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"ssAeg818ECHHBMn\",\"output_index\":1,\"sequence_number\":229}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" that\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"hDXq4xKg79u\",\"output_index\":1,\"sequence_number\":230}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" is\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"jZeAdnGTMAQZT\",\"output_index\":1,\"sequence_number\":231}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" ≤\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"tdwcSggql4Rv5b\",\"output_index\":1,\"sequence_number\":232}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" \",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"Pj104CIHWKJn34a\",\"output_index\":1,\"sequence_number\":233}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"15\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"lDXBb7veIFNiHB\",\"output_index\":1,\"sequence_number\":234}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\":\\n\\n\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"wjoDgt4vKpVV6\",\"output_index\":1,\"sequence_number\":235}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"-\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"TiD3aXPojhjDfr4\",\"output_index\":1,\"sequence_number\":236}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" \",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"eOMXHQjaFQHVXvL\",\"output_index\":1,\"sequence_number\":237}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"2\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"ccuWgf0sGrVPQK5\",\"output_index\":1,\"sequence_number\":238}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"^\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"FEUelPPSjT9oA4A\",\"output_index\":1,\"sequence_number\":239}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"3\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"nVCzwRxwKMbEmtJ\",\"output_index\":1,\"sequence_number\":240}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" =\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"uFJmEp2nylBdh4\",\"output_index\":1,\"sequence_number\":241}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" \",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"RxSQmUzY82XGo73\",\"output_index\":1,\"sequence_number\":242}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"8\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"5qzNGDpQYswbYW6\",\"output_index\":1,\"sequence_number\":243}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" (\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"DeqihIuucCeQ0s\",\"output_index\":1,\"sequence_number\":244}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"2\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"YOsNwLTVfjIraKd\",\"output_index\":1,\"sequence_number\":245}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"^\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"IvLnL3StUaJBGxL\",\"output_index\":1,\"sequence_number\":246}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"4\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"eCjQEQUuPM2K35k\",\"output_index\":1,\"sequence_number\":247}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" =\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"7BumWvCyUrf1sO\",\"output_index\":1,\"sequence_number\":248}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" \",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"i2wfSNrSwNEVWGo\",\"output_index\":1,\"sequence_number\":249}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"16\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"hvQ3lkwpXxIiYh\",\"output_index\":1,\"sequence_number\":250}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" >\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"ZbxfovE8jUjyto\",\"output_index\":1,\"sequence_number\":251}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" \",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"2da54DsaWJF0Nlb\",\"output_index\":1,\"sequence_number\":252}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"15\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"hiGadbRA0X8W7I\",\"output_index\":1,\"sequence_number\":253}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\")\\n\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"nxCZGZIhnqJmmX\",\"output_index\":1,\"sequence_number\":254}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"-\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"50hm5q2mtPM9l69\",\"output_index\":1,\"sequence_number\":255}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" \",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"McWiPrxvvZ90nho\",\"output_index\":1,\"sequence_number\":256}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"3\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"hOExVCGv3XkC6tU\",\"output_index\":1,\"sequence_number\":257}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"^\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"mrUxMRPisHWHaZe\",\"output_index\":1,\"sequence_number\":258}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"2\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"93LtnHiRSd1ckKI\",\"output_index\":1,\"sequence_number\":259}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" =\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"2o12GqYLRjZpc9\",\"output_index\":1,\"sequence_number\":260}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" \",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"xqgHtas87TXZsem\",\"output_index\":1,\"sequence_number\":261}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"9\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"CFC85wHDs2yJdyV\",\"output_index\":1,\"sequence_number\":262}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" (\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"OYH06IvHsRgQML\",\"output_index\":1,\"sequence_number\":263}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"3\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"2crpMMackkrP38W\",\"output_index\":1,\"sequence_number\":264}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"^\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"Rvfp1j6ZS66EKKe\",\"output_index\":1,\"sequence_number\":265}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"3\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"EqQnTpVEws3P6PU\",\"output_index\":1,\"sequence_number\":266}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" =\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"fRaTg6XPMXSmSr\",\"output_index\":1,\"sequence_number\":267}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" \",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"MpA35LLyw4bjWXj\",\"output_index\":1,\"sequence_number\":268}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"27\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"nJP9VF1s3EsFaF\",\"output_index\":1,\"sequence_number\":269}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" >\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"FtmdlsbnsBrTaD\",\"output_index\":1,\"sequence_number\":270}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" \",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"6HorxXpLqPimY0p\",\"output_index\":1,\"sequence_number\":271}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"15\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"Nv1DDLVKFzC1KU\",\"output_index\":1,\"sequence_number\":272}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\")\\n\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"NkOuf05Txgp8oO\",\"output_index\":1,\"sequence_number\":273}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"-\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"ix1GGPd0F81r3WC\",\"output_index\":1,\"sequence_number\":274}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" \",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"3rZOC5PB8j7i3n8\",\"output_index\":1,\"sequence_number\":275}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"5\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"rn30GE7uondEx0k\",\"output_index\":1,\"sequence_number\":276}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"^\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"YLKamSMTmCS1E78\",\"output_index\":1,\"sequence_number\":277}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"1\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"Offk6QYLWAi5ELW\",\"output_index\":1,\"sequence_number\":278}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" =\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"I71vbQUyURlC8R\",\"output_index\":1,\"sequence_number\":279}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" \",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"cZKtZsJ7YXNvgAA\",\"output_index\":1,\"sequence_number\":280}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"5\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"mJZO82XDwohATZW\",\"output_index\":1,\"sequence_number\":281}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"\\n\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"9i0cEiYn1VCz53K\",\"output_index\":1,\"sequence_number\":282}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"-\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"WXNmGRWteXtYzxf\",\"output_index\":1,\"sequence_number\":283}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" \",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"KKKlenJw8IOO4Fl\",\"output_index\":1,\"sequence_number\":284}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"7\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"LG6yruwRIZ8qxDS\",\"output_index\":1,\"sequence_number\":285}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"^\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"FGhbM9wNn7xMdSL\",\"output_index\":1,\"sequence_number\":286}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"1\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"2ZOlIHzuMsVRd3L\",\"output_index\":1,\"sequence_number\":287}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" =\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"g2jS194edZMlyq\",\"output_index\":1,\"sequence_number\":288}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" \",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"uW5GN1IVnEqMMOI\",\"output_index\":1,\"sequence_number\":289}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"7\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"OtMpidvH42DJ1Uh\",\"output_index\":1,\"sequence_number\":290}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"\\n\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"TH4mzdN1O8a0WkF\",\"output_index\":1,\"sequence_number\":291}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"-\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"dEellQQbAsuHRYz\",\"output_index\":1,\"sequence_number\":292}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" \",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"XiSNU6ifETQHbnH\",\"output_index\":1,\"sequence_number\":293}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"11\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"BmRBCA8ktamdsU\",\"output_index\":1,\"sequence_number\":294}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"^\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"eBwlTyjLo4bRtD5\",\"output_index\":1,\"sequence_number\":295}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"1\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"PZQsM9Wb5apnPak\",\"output_index\":1,\"sequence_number\":296}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" =\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"Zm5n8r0sqSvkov\",\"output_index\":1,\"sequence_number\":297}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" \",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"ysx8YX13zdaEdgV\",\"output_index\":1,\"sequence_number\":298}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"11\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"ldEqy4QUEhjRp7\",\"output_index\":1,\"sequence_number\":299}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"\\n\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"yHlGHvyMAyjKjmn\",\"output_index\":1,\"sequence_number\":300}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"-\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"pl2Ksgii5OUE6ne\",\"output_index\":1,\"sequence_number\":301}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" \",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"rFGQJfa57Kuqs21\",\"output_index\":1,\"sequence_number\":302}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"13\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"dnY3rDGbC5v4Tx\",\"output_index\":1,\"sequence_number\":303}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"^\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"w0vTn2EigIDTAWA\",\"output_index\":1,\"sequence_number\":304}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"1\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"d7vVpImp3qerOeN\",\"output_index\":1,\"sequence_number\":305}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" =\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"tdXpzdhJxj5gtR\",\"output_index\":1,\"sequence_number\":306}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" \",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"WaiIHYqiF9mFKBb\",\"output_index\":1,\"sequence_number\":307}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"13\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"PXo1MZMolVkHUd\",\"output_index\":1,\"sequence_number\":308}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"\\n\\n\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"2BvJzEWbs342id\",\"output_index\":1,\"sequence_number\":309}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"The\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"7Dg0qMqYehtci\",\"output_index\":1,\"sequence_number\":310}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" L\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"JF8iuyBnrO93Kl\",\"output_index\":1,\"sequence_number\":311}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"CM\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"5WjQW4YgPH1DHU\",\"output_index\":1,\"sequence_number\":312}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" is\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"s6dyy23CDJsXv\",\"output_index\":1,\"sequence_number\":313}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" the\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"6w4bPlQDyfBF\",\"output_index\":1,\"sequence_number\":314}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" product\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"QkcgePlv\",\"output_index\":1,\"sequence_number\":315}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" of\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"MXHaod37CvcSQ\",\"output_index\":1,\"sequence_number\":316}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" these\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"9iFCbshXrC\",\"output_index\":1,\"sequence_number\":317}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" highest\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"UXml0LR5\",\"output_index\":1,\"sequence_number\":318}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" prime\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"fLu9Wa2Ypo\",\"output_index\":1,\"sequence_number\":319}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" powers\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"E27Farsx6\",\"output_index\":1,\"sequence_number\":320}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\":\\n\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"tEEd06tl9roDqX\",\"output_index\":1,\"sequence_number\":321}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"LC\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"GIfShdH0NFW7G5\",\"output_index\":1,\"sequence_number\":322}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"M\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"Xpo9p6OzYZcnQ92\",\"output_index\":1,\"sequence_number\":323}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" =\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"EZTQS58P0uyJX9\",\"output_index\":1,\"sequence_number\":324}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" \",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"UB7xRO8JlF376xZ\",\"output_index\":1,\"sequence_number\":325}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"2\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"YZPI46izTY15tVO\",\"output_index\":1,\"sequence_number\":326}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"^\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"0tEPnfYUW6o7peb\",\"output_index\":1,\"sequence_number\":327}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"3\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"JBRbv3GKETFlXvF\",\"output_index\":1,\"sequence_number\":328}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" ·\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"fluRrF7RJJ0rsG\",\"output_index\":1,\"sequence_number\":329}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" \",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"JkedtIK7bqStIHr\",\"output_index\":1,\"sequence_number\":330}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"3\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"uDXfITsbjF1nlFO\",\"output_index\":1,\"sequence_number\":331}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"^\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"6l9EFZMyxjBftPM\",\"output_index\":1,\"sequence_number\":332}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"2\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"yskatBFFpKXVxwT\",\"output_index\":1,\"sequence_number\":333}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" ·\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"GxpBM0LdpRSWsN\",\"output_index\":1,\"sequence_number\":334}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" \",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"tGmNqplUodLS65M\",\"output_index\":1,\"sequence_number\":335}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"5\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"3Yazd8Lm9klqqoP\",\"output_index\":1,\"sequence_number\":336}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" ·\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"ysYLAFv0qo3Wft\",\"output_index\":1,\"sequence_number\":337}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" \",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"SAD4I5XkqhRmjx8\",\"output_index\":1,\"sequence_number\":338}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"7\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"iWnKy1jhFwZB1hj\",\"output_index\":1,\"sequence_number\":339}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" ·\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"IpO8NuUvv5DKeH\",\"output_index\":1,\"sequence_number\":340}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" \",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"pKDNUyx9DoD2z3s\",\"output_index\":1,\"sequence_number\":341}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"11\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"W9PRugwRU8TkCA\",\"output_index\":1,\"sequence_number\":342}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" ·\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"OeSPTdgBF4AoaL\",\"output_index\":1,\"sequence_number\":343}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" \",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"sIMXY7CKfMJ7hx3\",\"output_index\":1,\"sequence_number\":344}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"13\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"40JDLdtjybsItt\",\"output_index\":1,\"sequence_number\":345}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"\\n\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"qDJK5R7Zm9H9fFJ\",\"output_index\":1,\"sequence_number\":346}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"=\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"VxN4b2SjQNpnh8g\",\"output_index\":1,\"sequence_number\":347}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" \",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"sIyryOWvVnwhGXC\",\"output_index\":1,\"sequence_number\":348}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"8\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"KfuWLyeodFx5NYA\",\"output_index\":1,\"sequence_number\":349}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" ·\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"I8LzKXmnKODgIy\",\"output_index\":1,\"sequence_number\":350}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" \",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"5BZy5boJmPEtnM6\",\"output_index\":1,\"sequence_number\":351}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"9\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"xd1m0i0s2NoCUI2\",\"output_index\":1,\"sequence_number\":352}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" ·\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"FKI11QGb25z33g\",\"output_index\":1,\"sequence_number\":353}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" \",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"NUEQR240U6V6637\",\"output_index\":1,\"sequence_number\":354}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"5\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"5ojiYgPrlnefBFQ\",\"output_index\":1,\"sequence_number\":355}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" ·\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"B5DYNtSudbvU0g\",\"output_index\":1,\"sequence_number\":356}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" \",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"PRxODEdVCXzWnvf\",\"output_index\":1,\"sequence_number\":357}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"7\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"k1rtMk1JHhog69w\",\"output_index\":1,\"sequence_number\":358}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" ·\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"XAIXj2eeeFWw7O\",\"output_index\":1,\"sequence_number\":359}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" \",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"fNIejgWNPXCXlc4\",\"output_index\":1,\"sequence_number\":360}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"11\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"xYi267NUR3ydh8\",\"output_index\":1,\"sequence_number\":361}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" ·\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"e8YiJczxSGZ91P\",\"output_index\":1,\"sequence_number\":362}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" \",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"NXDO3FAQ1lzDVlu\",\"output_index\":1,\"sequence_number\":363}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"13\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"0cARZCr0FRHtak\",\"output_index\":1,\"sequence_number\":364}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"\\n\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"TqwtNmlbFZyTPqr\",\"output_index\":1,\"sequence_number\":365}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"=\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"gaBV9EJ0ndTsIj0\",\"output_index\":1,\"sequence_number\":366}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" \",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"voHxATUMgx8sdSx\",\"output_index\":1,\"sequence_number\":367}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"360\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"G9mnCqM38Z4Zl\",\"output_index\":1,\"sequence_number\":368}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\",\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"Smxr6InVaNsO3N5\",\"output_index\":1,\"sequence_number\":369}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"360\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"wILvdWwIzTSP1\",\"output_index\":1,\"sequence_number\":370}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\".\\n\\n\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"etR9TZeuvFk3Q\",\"output_index\":1,\"sequence_number\":371}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"Thus\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"bVz7KOfw3sfT\",\"output_index\":1,\"sequence_number\":372}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" the\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"uROiK9GP2ekA\",\"output_index\":1,\"sequence_number\":373}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" smallest\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"qGUxyfb\",\"output_index\":1,\"sequence_number\":374}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" positive\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"CTWdara\",\"output_index\":1,\"sequence_number\":375}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" integer\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"hr5EOFzn\",\"output_index\":1,\"sequence_number\":376}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" divisible\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"zPhG01\",\"output_index\":1,\"sequence_number\":377}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" by\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"ecVpZoVOLt9m5\",\"output_index\":1,\"sequence_number\":378}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" every\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"kRcnYNBSJE\",\"output_index\":1,\"sequence_number\":379}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" integer\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"COA2RkWL\",\"output_index\":1,\"sequence_number\":380}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" from\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"SgyVc71T5Sh\",\"output_index\":1,\"sequence_number\":381}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" \",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"l0Aof3bUaScgheX\",\"output_index\":1,\"sequence_number\":382}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"1\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"GWcY9SlSBJ4QXsm\",\"output_index\":1,\"sequence_number\":383}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" through\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"muGMrzRw\",\"output_index\":1,\"sequence_number\":384}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" \",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"Hnqcmc0OCpjhYDu\",\"output_index\":1,\"sequence_number\":385}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"15\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"gbrBKtMrAldsfH\",\"output_index\":1,\"sequence_number\":386}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" is\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"rIBnOEMAzIoPP\",\"output_index\":1,\"sequence_number\":387}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" \",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"uE4HarcCAbhK2UO\",\"output_index\":1,\"sequence_number\":388}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"360\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"8YrpZfdIJXYGY\",\"output_index\":1,\"sequence_number\":389}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\",\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"Yo07TzZDu7B4AL3\",\"output_index\":1,\"sequence_number\":390}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"360\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"tYBOfC8sAbFSK\",\"output_index\":1,\"sequence_number\":391}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\".\",\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"obfuscation\":\"OYcGvCF7FJFbJ7x\",\"output_index\":1,\"sequence_number\":392}\n\nevent: response.output_text.done\ndata: {\"type\":\"response.output_text.done\",\"content_index\":0,\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"logprobs\":[],\"output_index\":1,\"sequence_number\":393,\"text\":\"We want the least common multiple (LCM) of 1,2,...,15. For each prime p ≤ 15, take the highest power p^k that is ≤ 15:\\n\\n- 2^3 = 8 (2^4 = 16 > 15)\\n- 3^2 = 9 (3^3 = 27 > 15)\\n- 5^1 = 5\\n- 7^1 = 7\\n- 11^1 = 11\\n- 13^1 = 13\\n\\nThe LCM is the product of these highest prime powers:\\nLCM = 2^3 · 3^2 · 5 · 7 · 11 · 13\\n= 8 · 9 · 5 · 7 · 11 · 13\\n= 360,360.\\n\\nThus the smallest positive integer divisible by every integer from 1 through 15 is 360,360.\"}\n\nevent: response.content_part.done\ndata: {\"type\":\"response.content_part.done\",\"content_index\":0,\"item_id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"output_index\":1,\"part\":{\"type\":\"output_text\",\"annotations\":[],\"logprobs\":[],\"text\":\"We want the least common multiple (LCM) of 1,2,...,15. For each prime p ≤ 15, take the highest power p^k that is ≤ 15:\\n\\n- 2^3 = 8 (2^4 = 16 > 15)\\n- 3^2 = 9 (3^3 = 27 > 15)\\n- 5^1 = 5\\n- 7^1 = 7\\n- 11^1 = 11\\n- 13^1 = 13\\n\\nThe LCM is the product of these highest prime powers:\\nLCM = 2^3 · 3^2 · 5 · 7 · 11 · 13\\n= 8 · 9 · 5 · 7 · 11 · 13\\n= 360,360.\\n\\nThus the smallest positive integer divisible by every integer from 1 through 15 is 360,360.\"},\"sequence_number\":394}\n\nevent: response.output_item.done\ndata: {\"type\":\"response.output_item.done\",\"item\":{\"id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"type\":\"message\",\"status\":\"completed\",\"content\":[{\"type\":\"output_text\",\"annotations\":[],\"logprobs\":[],\"text\":\"We want the least common multiple (LCM) of 1,2,...,15. For each prime p ≤ 15, take the highest power p^k that is ≤ 15:\\n\\n- 2^3 = 8 (2^4 = 16 > 15)\\n- 3^2 = 9 (3^3 = 27 > 15)\\n- 5^1 = 5\\n- 7^1 = 7\\n- 11^1 = 11\\n- 13^1 = 13\\n\\nThe LCM is the product of these highest prime powers:\\nLCM = 2^3 · 3^2 · 5 · 7 · 11 · 13\\n= 8 · 9 · 5 · 7 · 11 · 13\\n= 360,360.\\n\\nThus the smallest positive integer divisible by every integer from 1 through 15 is 360,360.\"}],\"role\":\"assistant\"},\"output_index\":1,\"sequence_number\":395}\n\nevent: response.completed\ndata: {\"type\":\"response.completed\",\"response\":{\"id\":\"resp_0385c9a4aab47c4b006a6777286be88192b58c079eb072557e\",\"object\":\"response\",\"created_at\":1785165608,\"status\":\"completed\",\"background\":false,\"completed_at\":1785165616,\"error\":null,\"frequency_penalty\":0.0,\"incomplete_details\":null,\"instructions\":null,\"max_output_tokens\":null,\"max_tool_calls\":null,\"model\":\"gpt-5-mini-2025-08-07\",\"moderation\":null,\"output\":[{\"id\":\"rs_0385c9a4aab47c4b006a677728f5b08192b5882b7373a64738\",\"type\":\"reasoning\",\"content\":[],\"encrypted_content\":\"gAAAAABqZ3cwhpFYay00ebjOd3BMyyOs9thyFDd3yppMz36V-SShooFbLg-wh2I9x9DUlfc7JEnwSwshKkFF0gwiu9fGcOWIh7dQ4bjE_M_FRRzZcfp9xkmv0M9laBaPVoXXNvZ1YmBYAYGaB991OhYffSTm-aBKgFjYyOlN9z7xFxB_xhCoGViHLNUQNBSUdcjZBrdit9eFAbEqSWm8XA34OqTeY3Zes7ImxHIjwOMxR02bxs0GC6uIR5-eboWEuRktI-22U6Pl-qJ0E3FRLwgTMQMXwEsr6TV5ScD8Oa1zJNCVWPbUGflsnIhdH4KImZN9j44_f8wl1rue2Ay7scCu58d5S6ezHYSMwY-6Hun49tFawi83b6IGBjKEC2aMqZK-x8oX7tGi3J90XbsMjdx1EiQegOAjkiPI6y3Tl1E-QfTesliXZjCtPA2Gvyvbp7z26R-YixeuYrP-822Ltn638G5XRHCjEBY22AoJ45h1yg1lO6oWK8RbJb8ZD2qQfELYoOPwuZXWFJGzW4pqBeQciZK4o4D60edGpKD4N3W03-qfjDODbeaaGJzYoi4WvCDc2iW2fDPx-97g_1DRxbJ_MKeJr5u-1sKEqzaAxKMyrvDXOB5NyO1wAwUoZ333aRcJTxKWe4G9Y9dGG0in7T3zsPbaZKwboAbhsLckkcOY-AWqBRgAjgBw3m-m0rzXB7ReZKBDdeYDIfnBGyaztEFnch435Kw5UHot0h8z-B_TybK7zF-GHiAHJ0DXcFkrExg0F1iKi9Di4WDSqPFJIOsJeoefkPAS_HjH9Ecx6PQu8G4QW7_AMlFy5QaWGlMX6odiKTOs5CMDwNTR42xY_aj8zslyiLzQ2VVxb7Igvp4DHek4ZHyVsoC4PHGl5yNuONhOo70F0CSumVsrWO0fTlvdE8EAn75imZr8BMnZR72b8S_ep63ajylVIqMhN8eUoMRjOHtxJVBQQrmZhR1NDb0TKGVEIfxL9dLGAGUp21pWR_61MmCFC92zSphuegbKZsapBR8q5r5VeADYVnczRZZ1ZaEN7h3UhKvoPjemMQiiBXT_Wd7Ab4uPfRdLIW7gw1MBr2rl_4dK2JKgKfvGvJW-7v35irfNyvlTkt9o5F6KxoK45dFPct5Rax53KCFMduoFlnS58Om78MjDpZiT70Kfi72WJE6htRUngVGLVLk36kAfYWpVSzYIdUJ5gfln2rPIB3vPA-0nUQ51SxkjT_QKLwFoXspL6ctWUvU--Lt30oG83-KPMNg6pX8h7IVh5hd7_y1JDbGPsk9IExR9-3kFaktkNHkc9tc24pDnIhUnADFvef5ebqfV4lvjRFDUrDY7lzUVWEQ9rQeZO-ZxdLImJnc5-cU47WRT1ZkNsy2p6CdG1LGMmmHo6fxdRna9LtFEk8T-mwMELRhTjsnSpdGZnlW-cPOjWmmVwYTCrp-JXj78KpieWf_ztITyBWSFm1RnTKQ6insbuBEGAvaCOPxFZ5JYHZb2f3r-tasH0BOYVDwPcpkK5UG3afTIXLdOB-kQuWByQeM84TXSRFLgki8Puk5AIcthKy44WctlQ5o855Bv6snSc0JQpK86ccqz4620sZkCYekA-qPztrlkZDZoE5DFAAPP4l44TbSglhggKzB8Gbyh16i8um1nlgiWzoNKRdxk6t4VU5sb335uq8vN0LXcKnHsmpi_pWNgbxJ1sxUQife1nxWlTDRzb2JnKtcB_pDIgLa0TCbpQGKAIvK-ozmBXOWifR7CNiTn0sqwU1kMpazAE3aADx-1kq70V9pPHbM6q8HQDQeRoWMua1FNHZs5sbIRMwwna3Z6hcRx1oPN3aff3pra0UlPW9egytM56JaSFDY7SflH24GzoBU0DBE1FQ7x4WJxM1Fo15dkiKVq1EjL4uLyKQs_aF6DBJ2TYlsni5yX3rq4PjRDWBQj-MZAW8wGBLcaKOD65VE5LvdvKG5oQ1h8TOv8g44G7Rhr6cmel-w0Q_34y67UgWvXo3QzyqNi2oRLJZihW-aeVVf5_nQ5zBYpXM71BysLzJkBOEm2T5qB4JACjojCX5x6GvwjUS8Lhcymwlb8dThMy1_j13LUC3zAh2BH_YHyZc2-76FOrgKArSG4KGXUQ6NvON3xy4e-fJrwMNPi1Eck63SY10BgA1hc4pRxOVz2v3TyzQuj6dl40HV4Q6nA4WjOmC1oZTl9SqW5TPHrUGl03d_4IX_r7rXLkIl5sBU7h1PqHsm1fW1Guzqquhm9JBCZ8A-yKX2DXq0RxCWRefg2nuehqojLYAKcYvUUNM49as6F0WeMmI_Npj_dk39YvsZhKga5QnObxaeQLvMqYrnRx4ZGxHgSAbBIVvYS6mXjUAzjOpq_b2xaS7NokcUasrUClOua-8leHwximir6aSEOnZ4SSEuBMW751TDtFN-Hm6OvAVLyNo-DcZ1Ok1o0qSQG9n_4Tdf5WTrZJ-FaEHHFwf-sml8dnHobdeVVwzAm3zkMNNYlvLlQZoBmuhkWJcol19CPB068G3gZ_F6k7ctCXhlsWEb7EB-LKsIwCoY0dcRRS1k5v9P3\",\"summary\":[{\"type\":\"summary_text\",\"text\":\"**Calculating LCM of 1 to 15**\\n\\nI need to find the least common multiple (LCM) of the numbers from 1 to 15. First, I'll list the prime factorizations: the primes I identify are 2, 3, 5, 7, 11, and 13. Then, I’ll find the highest powers of these primes that are less than or equal to 15. My calculation leads to the result 360360, confirming that this is indeed the LCM of 1 to 15. I should explain my method and reasoning clearly!\"},{\"type\":\"summary_text\",\"text\":\"**Finalizing LCM Explanation**\\n\\nI’m considering how to demonstrate that the LCM is divisible by all numbers from 1 to 15. I should mention that the LCM of a set represents the smallest number that can be divided by each integer in that set. While it might be nice to verify this for each integer, I don’t think it’s strictly necessary for the final explanation. I’ll focus on producing a succinct answer that clearly communicates the relevant information.\"}]},{\"id\":\"msg_0385c9a4aab47c4b006a67772ff23c8192bc22c8d8df3ba9a9\",\"type\":\"message\",\"status\":\"completed\",\"content\":[{\"type\":\"output_text\",\"annotations\":[],\"logprobs\":[],\"text\":\"We want the least common multiple (LCM) of 1,2,...,15. For each prime p ≤ 15, take the highest power p^k that is ≤ 15:\\n\\n- 2^3 = 8 (2^4 = 16 > 15)\\n- 3^2 = 9 (3^3 = 27 > 15)\\n- 5^1 = 5\\n- 7^1 = 7\\n- 11^1 = 11\\n- 13^1 = 13\\n\\nThe LCM is the product of these highest prime powers:\\nLCM = 2^3 · 3^2 · 5 · 7 · 11 · 13\\n= 8 · 9 · 5 · 7 · 11 · 13\\n= 360,360.\\n\\nThus the smallest positive integer divisible by every integer from 1 through 15 is 360,360.\"}],\"role\":\"assistant\"}],\"parallel_tool_calls\":true,\"presence_penalty\":0.0,\"previous_response_id\":null,\"prompt_cache_key\":null,\"prompt_cache_retention\":\"in_memory\",\"reasoning\":{\"context\":\"current_turn\",\"effort\":\"medium\",\"mode\":\"standard\",\"summary\":\"detailed\"},\"safety_identifier\":null,\"service_tier\":\"default\",\"store\":true,\"temperature\":1.0,\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"medium\"},\"tool_choice\":\"auto\",\"tool_usage\":{\"image_gen\":{\"input_tokens\":0,\"input_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"output_tokens\":0,\"output_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"total_tokens\":0},\"web_search\":{\"num_requests\":0}},\"tools\":[],\"top_logprobs\":0,\"top_p\":1.0,\"truncation\":\"disabled\",\"usage\":{\"input_tokens\":42,\"input_tokens_details\":{\"cache_write_tokens\":0,\"cached_tokens\":0},\"output_tokens\":669,\"output_tokens_details\":{\"reasoning_tokens\":448},\"total_tokens\":711},\"user\":null,\"metadata\":{}},\"sequence_number\":396}\n\n", + "rawHeaders": { + "access-control-expose-headers": "X-Request-ID, CF-Ray, CF-Ray", + "alt-svc": "h3=\":443\"; ma=86400", + "cf-cache-status": "DYNAMIC", + "cf-ray": "a21ca0586e6b3622-MAN", + "connection": "keep-alive", + "content-type": "text/event-stream; charset=utf-8", + "date": "Mon, 27 Jul 2026 15:20:08 GMT", + "openai-organization": "n8n-1", + "openai-processing-ms": "363", + "openai-project": "proj_ZAEXttE86YHh0Pzmrnh9cBEf", + "openai-version": "2020-10-01", + "server": "cloudflare", + "set-cookie": "__cf_bm=uTGW_yrA0sQfRsdIMRt9eL3ODfylTq_x6J.JV.t8j9M-1785165607.7460184-1.0.1.1-nvv4WEXrPcC_xFCGuXEc2sfoRnX49kNBbMAwTOxAHVM73p4UB.TQmTCd3HNKGHHC.u0WLCncHPIquz2CY1ggFx8_zSmBKAr8ejE0E7vWhuhBdqc40nvxpw.QL27hBWtx; HttpOnly; SameSite=None; Secure; Path=/; Domain=api.openai.com; Expires=Mon, 27 Jul 2026 15:50:08 GMT", + "strict-transport-security": "max-age=31536000; includeSubDomains; preload", + "transfer-encoding": "chunked", + "x-content-type-options": "nosniff", + "x-ratelimit-limit-requests": "30000", + "x-ratelimit-limit-tokens": "180000000", + "x-ratelimit-remaining-requests": "29999", + "x-ratelimit-remaining-tokens": "179999742", + "x-ratelimit-reset-requests": "2ms", + "x-ratelimit-reset-tokens": "0s", + "x-request-id": "14afded1-5513-44c4-9528-d0e83de6a049" + }, + "responseIsBinary": false + } +] diff --git a/packages/@n8n/agents/src/__tests__/integration/cassettes/thinking/works-with-reasoning-model-and-thinking-enabled.json b/packages/@n8n/agents/src/__tests__/integration/cassettes/thinking/works-with-reasoning-model-and-thinking-enabled.json deleted file mode 100644 index 67af2effaec..00000000000 --- a/packages/@n8n/agents/src/__tests__/integration/cassettes/thinking/works-with-reasoning-model-and-thinking-enabled.json +++ /dev/null @@ -1,51 +0,0 @@ -[ - { - "scope": "https://api.openai.com:443", - "method": "POST", - "path": "/v1/responses", - "body": { - "model": "o3-mini", - "input": [ - { - "role": "developer", - "content": "You are a helpful assistant." - }, - { - "role": "user", - "content": [ - { - "type": "input_text", - "text": "What is 17 * 23?" - } - ] - } - ], - "reasoning": { - "effort": "medium" - }, - "stream": true - }, - "status": 200, - "response": "event: response.created\ndata: {\"type\":\"response.created\",\"response\":{\"id\":\"resp_082620005afff900006a3a79df4b28819296cc5b90b72632f1\",\"object\":\"response\",\"created_at\":1782217183,\"status\":\"in_progress\",\"background\":false,\"completed_at\":null,\"error\":null,\"frequency_penalty\":0.0,\"incomplete_details\":null,\"instructions\":null,\"max_output_tokens\":null,\"max_tool_calls\":null,\"model\":\"o3-mini-2025-01-31\",\"moderation\":null,\"output\":[],\"parallel_tool_calls\":true,\"presence_penalty\":0.0,\"previous_response_id\":null,\"prompt_cache_key\":null,\"prompt_cache_retention\":\"in_memory\",\"reasoning\":{\"context\":\"current_turn\",\"effort\":\"medium\",\"summary\":null},\"safety_identifier\":null,\"service_tier\":\"auto\",\"store\":true,\"temperature\":1.0,\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"medium\"},\"tool_choice\":\"auto\",\"tools\":[],\"top_logprobs\":0,\"top_p\":1.0,\"truncation\":\"disabled\",\"usage\":null,\"user\":null,\"metadata\":{}},\"sequence_number\":0}\n\nevent: response.in_progress\ndata: {\"type\":\"response.in_progress\",\"response\":{\"id\":\"resp_082620005afff900006a3a79df4b28819296cc5b90b72632f1\",\"object\":\"response\",\"created_at\":1782217183,\"status\":\"in_progress\",\"background\":false,\"completed_at\":null,\"error\":null,\"frequency_penalty\":0.0,\"incomplete_details\":null,\"instructions\":null,\"max_output_tokens\":null,\"max_tool_calls\":null,\"model\":\"o3-mini-2025-01-31\",\"moderation\":null,\"output\":[],\"parallel_tool_calls\":true,\"presence_penalty\":0.0,\"previous_response_id\":null,\"prompt_cache_key\":null,\"prompt_cache_retention\":\"in_memory\",\"reasoning\":{\"context\":\"current_turn\",\"effort\":\"medium\",\"summary\":null},\"safety_identifier\":null,\"service_tier\":\"auto\",\"store\":true,\"temperature\":1.0,\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"medium\"},\"tool_choice\":\"auto\",\"tools\":[],\"top_logprobs\":0,\"top_p\":1.0,\"truncation\":\"disabled\",\"usage\":null,\"user\":null,\"metadata\":{}},\"sequence_number\":1}\n\nevent: response.output_item.added\ndata: {\"type\":\"response.output_item.added\",\"item\":{\"id\":\"rs_082620005afff900006a3a79dfdc408192b992b0afc56e11b5\",\"type\":\"reasoning\",\"content\":[],\"summary\":[]},\"output_index\":0,\"sequence_number\":2}\n\nevent: response.output_item.done\ndata: {\"type\":\"response.output_item.done\",\"item\":{\"id\":\"rs_082620005afff900006a3a79dfdc408192b992b0afc56e11b5\",\"type\":\"reasoning\",\"content\":[],\"summary\":[]},\"output_index\":0,\"sequence_number\":3}\n\nevent: response.output_item.added\ndata: {\"type\":\"response.output_item.added\",\"item\":{\"id\":\"msg_082620005afff900006a3a79e07f3c8192af47ecbdaed41379\",\"type\":\"message\",\"status\":\"in_progress\",\"content\":[],\"role\":\"assistant\"},\"output_index\":1,\"sequence_number\":4}\n\nevent: response.content_part.added\ndata: {\"type\":\"response.content_part.added\",\"content_index\":0,\"item_id\":\"msg_082620005afff900006a3a79e07f3c8192af47ecbdaed41379\",\"output_index\":1,\"part\":{\"type\":\"output_text\",\"annotations\":[],\"logprobs\":[],\"text\":\"\"},\"sequence_number\":5}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"17\",\"item_id\":\"msg_082620005afff900006a3a79e07f3c8192af47ecbdaed41379\",\"logprobs\":[],\"obfuscation\":\"HDZo8nUPBbhCfi\",\"output_index\":1,\"sequence_number\":6}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" multiplied\",\"item_id\":\"msg_082620005afff900006a3a79e07f3c8192af47ecbdaed41379\",\"logprobs\":[],\"obfuscation\":\"8kYi9\",\"output_index\":1,\"sequence_number\":7}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" by\",\"item_id\":\"msg_082620005afff900006a3a79e07f3c8192af47ecbdaed41379\",\"logprobs\":[],\"obfuscation\":\"P7a7eRRPa78Ig\",\"output_index\":1,\"sequence_number\":8}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" \",\"item_id\":\"msg_082620005afff900006a3a79e07f3c8192af47ecbdaed41379\",\"logprobs\":[],\"obfuscation\":\"N1bpmTs9STA2GEg\",\"output_index\":1,\"sequence_number\":9}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"23\",\"item_id\":\"msg_082620005afff900006a3a79e07f3c8192af47ecbdaed41379\",\"logprobs\":[],\"obfuscation\":\"Iki5xtJIaxHUmQ\",\"output_index\":1,\"sequence_number\":10}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" is\",\"item_id\":\"msg_082620005afff900006a3a79e07f3c8192af47ecbdaed41379\",\"logprobs\":[],\"obfuscation\":\"5iGHhIvfAMXqp\",\"output_index\":1,\"sequence_number\":11}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" \",\"item_id\":\"msg_082620005afff900006a3a79e07f3c8192af47ecbdaed41379\",\"logprobs\":[],\"obfuscation\":\"8mPmcFsN7RYcUAc\",\"output_index\":1,\"sequence_number\":12}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"391\",\"item_id\":\"msg_082620005afff900006a3a79e07f3c8192af47ecbdaed41379\",\"logprobs\":[],\"obfuscation\":\"i0nkqHeG2Ytze\",\"output_index\":1,\"sequence_number\":13}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\".\",\"item_id\":\"msg_082620005afff900006a3a79e07f3c8192af47ecbdaed41379\",\"logprobs\":[],\"obfuscation\":\"yxVx1klQxbmVorR\",\"output_index\":1,\"sequence_number\":14}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" Here's\",\"item_id\":\"msg_082620005afff900006a3a79e07f3c8192af47ecbdaed41379\",\"logprobs\":[],\"obfuscation\":\"YOmIZMU9t\",\"output_index\":1,\"sequence_number\":15}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" a\",\"item_id\":\"msg_082620005afff900006a3a79e07f3c8192af47ecbdaed41379\",\"logprobs\":[],\"obfuscation\":\"aX792N8lgj4zMo\",\"output_index\":1,\"sequence_number\":16}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" quick\",\"item_id\":\"msg_082620005afff900006a3a79e07f3c8192af47ecbdaed41379\",\"logprobs\":[],\"obfuscation\":\"inWA0Z8z5I\",\"output_index\":1,\"sequence_number\":17}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" breakdown\",\"item_id\":\"msg_082620005afff900006a3a79e07f3c8192af47ecbdaed41379\",\"logprobs\":[],\"obfuscation\":\"phf7n6\",\"output_index\":1,\"sequence_number\":18}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\":\\n\\n\",\"item_id\":\"msg_082620005afff900006a3a79e07f3c8192af47ecbdaed41379\",\"logprobs\":[],\"obfuscation\":\"mcDI1FYQZWRc9\",\"output_index\":1,\"sequence_number\":19}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"•\",\"item_id\":\"msg_082620005afff900006a3a79e07f3c8192af47ecbdaed41379\",\"logprobs\":[],\"obfuscation\":\"Smn1R1ubeZDKNXm\",\"output_index\":1,\"sequence_number\":20}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" \",\"item_id\":\"msg_082620005afff900006a3a79e07f3c8192af47ecbdaed41379\",\"logprobs\":[],\"obfuscation\":\"TGaFiNSaSvfXbdH\",\"output_index\":1,\"sequence_number\":21}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"17\",\"item_id\":\"msg_082620005afff900006a3a79e07f3c8192af47ecbdaed41379\",\"logprobs\":[],\"obfuscation\":\"FImpL4g1CgzSqK\",\"output_index\":1,\"sequence_number\":22}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" ×\",\"item_id\":\"msg_082620005afff900006a3a79e07f3c8192af47ecbdaed41379\",\"logprobs\":[],\"obfuscation\":\"eXb9Lq5ZtJb15c\",\"output_index\":1,\"sequence_number\":23}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" \",\"item_id\":\"msg_082620005afff900006a3a79e07f3c8192af47ecbdaed41379\",\"logprobs\":[],\"obfuscation\":\"3YFBz3uyCvTgVQj\",\"output_index\":1,\"sequence_number\":24}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"20\",\"item_id\":\"msg_082620005afff900006a3a79e07f3c8192af47ecbdaed41379\",\"logprobs\":[],\"obfuscation\":\"DpSfcDAHfK1XfD\",\"output_index\":1,\"sequence_number\":25}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" =\",\"item_id\":\"msg_082620005afff900006a3a79e07f3c8192af47ecbdaed41379\",\"logprobs\":[],\"obfuscation\":\"DntCcbaBQOXO0g\",\"output_index\":1,\"sequence_number\":26}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" \",\"item_id\":\"msg_082620005afff900006a3a79e07f3c8192af47ecbdaed41379\",\"logprobs\":[],\"obfuscation\":\"ZQbxmf3K02JFGvP\",\"output_index\":1,\"sequence_number\":27}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"340\",\"item_id\":\"msg_082620005afff900006a3a79e07f3c8192af47ecbdaed41379\",\"logprobs\":[],\"obfuscation\":\"GoBhVgaDM8ZBB\",\"output_index\":1,\"sequence_number\":28}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" \\n\",\"item_id\":\"msg_082620005afff900006a3a79e07f3c8192af47ecbdaed41379\",\"logprobs\":[],\"obfuscation\":\"MwNHZaD8qF92W\",\"output_index\":1,\"sequence_number\":29}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"•\",\"item_id\":\"msg_082620005afff900006a3a79e07f3c8192af47ecbdaed41379\",\"logprobs\":[],\"obfuscation\":\"WC0H0vHdmQmK6BL\",\"output_index\":1,\"sequence_number\":30}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" \",\"item_id\":\"msg_082620005afff900006a3a79e07f3c8192af47ecbdaed41379\",\"logprobs\":[],\"obfuscation\":\"mUNVngFoM1CwFwc\",\"output_index\":1,\"sequence_number\":31}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"17\",\"item_id\":\"msg_082620005afff900006a3a79e07f3c8192af47ecbdaed41379\",\"logprobs\":[],\"obfuscation\":\"LbPfdLH0MF8ZEZ\",\"output_index\":1,\"sequence_number\":32}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" ×\",\"item_id\":\"msg_082620005afff900006a3a79e07f3c8192af47ecbdaed41379\",\"logprobs\":[],\"obfuscation\":\"EH7I53DIQr65WA\",\"output_index\":1,\"sequence_number\":33}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" \",\"item_id\":\"msg_082620005afff900006a3a79e07f3c8192af47ecbdaed41379\",\"logprobs\":[],\"obfuscation\":\"EB7r2QIPvut08H1\",\"output_index\":1,\"sequence_number\":34}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"3\",\"item_id\":\"msg_082620005afff900006a3a79e07f3c8192af47ecbdaed41379\",\"logprobs\":[],\"obfuscation\":\"AknxjJYyhKuieF9\",\"output_index\":1,\"sequence_number\":35}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" =\",\"item_id\":\"msg_082620005afff900006a3a79e07f3c8192af47ecbdaed41379\",\"logprobs\":[],\"obfuscation\":\"oNGmCFZCIXE3z1\",\"output_index\":1,\"sequence_number\":36}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" \",\"item_id\":\"msg_082620005afff900006a3a79e07f3c8192af47ecbdaed41379\",\"logprobs\":[],\"obfuscation\":\"HHXsqczfQ1X2MsT\",\"output_index\":1,\"sequence_number\":37}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"51\",\"item_id\":\"msg_082620005afff900006a3a79e07f3c8192af47ecbdaed41379\",\"logprobs\":[],\"obfuscation\":\"DGQf1FIWsssIgi\",\"output_index\":1,\"sequence_number\":38}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" \\n\\n\",\"item_id\":\"msg_082620005afff900006a3a79e07f3c8192af47ecbdaed41379\",\"logprobs\":[],\"obfuscation\":\"LrkdQNPvqPBG\",\"output_index\":1,\"sequence_number\":39}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"Then\",\"item_id\":\"msg_082620005afff900006a3a79e07f3c8192af47ecbdaed41379\",\"logprobs\":[],\"obfuscation\":\"A276Eh4wwSeP\",\"output_index\":1,\"sequence_number\":40}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\",\",\"item_id\":\"msg_082620005afff900006a3a79e07f3c8192af47ecbdaed41379\",\"logprobs\":[],\"obfuscation\":\"JTFcJm0fqOuNVeW\",\"output_index\":1,\"sequence_number\":41}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" \",\"item_id\":\"msg_082620005afff900006a3a79e07f3c8192af47ecbdaed41379\",\"logprobs\":[],\"obfuscation\":\"sRkY0EB4il94aXv\",\"output_index\":1,\"sequence_number\":42}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"340\",\"item_id\":\"msg_082620005afff900006a3a79e07f3c8192af47ecbdaed41379\",\"logprobs\":[],\"obfuscation\":\"Ip8iO9jYPP8QP\",\"output_index\":1,\"sequence_number\":43}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" +\",\"item_id\":\"msg_082620005afff900006a3a79e07f3c8192af47ecbdaed41379\",\"logprobs\":[],\"obfuscation\":\"VtgSDXbRVbrl6t\",\"output_index\":1,\"sequence_number\":44}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" \",\"item_id\":\"msg_082620005afff900006a3a79e07f3c8192af47ecbdaed41379\",\"logprobs\":[],\"obfuscation\":\"VylLEPg3PgsktWT\",\"output_index\":1,\"sequence_number\":45}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"51\",\"item_id\":\"msg_082620005afff900006a3a79e07f3c8192af47ecbdaed41379\",\"logprobs\":[],\"obfuscation\":\"pmnbjGDBEPHpar\",\"output_index\":1,\"sequence_number\":46}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" =\",\"item_id\":\"msg_082620005afff900006a3a79e07f3c8192af47ecbdaed41379\",\"logprobs\":[],\"obfuscation\":\"HV4IkOLjUGjY73\",\"output_index\":1,\"sequence_number\":47}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" \",\"item_id\":\"msg_082620005afff900006a3a79e07f3c8192af47ecbdaed41379\",\"logprobs\":[],\"obfuscation\":\"7qrJLi7yOCjFfhV\",\"output_index\":1,\"sequence_number\":48}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"391\",\"item_id\":\"msg_082620005afff900006a3a79e07f3c8192af47ecbdaed41379\",\"logprobs\":[],\"obfuscation\":\"huJXii7T53sgj\",\"output_index\":1,\"sequence_number\":49}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\".\",\"item_id\":\"msg_082620005afff900006a3a79e07f3c8192af47ecbdaed41379\",\"logprobs\":[],\"obfuscation\":\"jyRxcMly5lCSHSs\",\"output_index\":1,\"sequence_number\":50}\n\nevent: response.output_text.done\ndata: {\"type\":\"response.output_text.done\",\"content_index\":0,\"item_id\":\"msg_082620005afff900006a3a79e07f3c8192af47ecbdaed41379\",\"logprobs\":[],\"output_index\":1,\"sequence_number\":51,\"text\":\"17 multiplied by 23 is 391. Here's a quick breakdown:\\n\\n• 17 × 20 = 340 \\n• 17 × 3 = 51 \\n\\nThen, 340 + 51 = 391.\"}\n\nevent: response.content_part.done\ndata: {\"type\":\"response.content_part.done\",\"content_index\":0,\"item_id\":\"msg_082620005afff900006a3a79e07f3c8192af47ecbdaed41379\",\"output_index\":1,\"part\":{\"type\":\"output_text\",\"annotations\":[],\"logprobs\":[],\"text\":\"17 multiplied by 23 is 391. Here's a quick breakdown:\\n\\n• 17 × 20 = 340 \\n• 17 × 3 = 51 \\n\\nThen, 340 + 51 = 391.\"},\"sequence_number\":52}\n\nevent: response.output_item.done\ndata: {\"type\":\"response.output_item.done\",\"item\":{\"id\":\"msg_082620005afff900006a3a79e07f3c8192af47ecbdaed41379\",\"type\":\"message\",\"status\":\"completed\",\"content\":[{\"type\":\"output_text\",\"annotations\":[],\"logprobs\":[],\"text\":\"17 multiplied by 23 is 391. Here's a quick breakdown:\\n\\n• 17 × 20 = 340 \\n• 17 × 3 = 51 \\n\\nThen, 340 + 51 = 391.\"}],\"role\":\"assistant\"},\"output_index\":1,\"sequence_number\":53}\n\nevent: response.completed\ndata: {\"type\":\"response.completed\",\"response\":{\"id\":\"resp_082620005afff900006a3a79df4b28819296cc5b90b72632f1\",\"object\":\"response\",\"created_at\":1782217183,\"status\":\"completed\",\"background\":false,\"completed_at\":1782217184,\"error\":null,\"frequency_penalty\":0.0,\"incomplete_details\":null,\"instructions\":null,\"max_output_tokens\":null,\"max_tool_calls\":null,\"model\":\"o3-mini-2025-01-31\",\"moderation\":null,\"output\":[{\"id\":\"rs_082620005afff900006a3a79dfdc408192b992b0afc56e11b5\",\"type\":\"reasoning\",\"content\":[],\"summary\":[]},{\"id\":\"msg_082620005afff900006a3a79e07f3c8192af47ecbdaed41379\",\"type\":\"message\",\"status\":\"completed\",\"content\":[{\"type\":\"output_text\",\"annotations\":[],\"logprobs\":[],\"text\":\"17 multiplied by 23 is 391. Here's a quick breakdown:\\n\\n• 17 × 20 = 340 \\n• 17 × 3 = 51 \\n\\nThen, 340 + 51 = 391.\"}],\"role\":\"assistant\"}],\"parallel_tool_calls\":true,\"presence_penalty\":0.0,\"previous_response_id\":null,\"prompt_cache_key\":null,\"prompt_cache_retention\":\"in_memory\",\"reasoning\":{\"context\":\"current_turn\",\"effort\":\"medium\",\"summary\":null},\"safety_identifier\":null,\"service_tier\":\"default\",\"store\":true,\"temperature\":1.0,\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"medium\"},\"tool_choice\":\"auto\",\"tools\":[],\"top_logprobs\":0,\"top_p\":1.0,\"truncation\":\"disabled\",\"usage\":{\"input_tokens\":24,\"input_tokens_details\":{\"cached_tokens\":0},\"output_tokens\":239,\"output_tokens_details\":{\"reasoning_tokens\":64},\"total_tokens\":263},\"user\":null,\"metadata\":{}},\"sequence_number\":54}\n\n", - "rawHeaders": { - "access-control-expose-headers": "X-Request-ID, CF-Ray, CF-Ray", - "alt-svc": "h3=\":443\"; ma=86400", - "cf-cache-status": "DYNAMIC", - "cf-ray": "a1037152aa095a3b-VIE", - "connection": "keep-alive", - "content-type": "text/event-stream; charset=utf-8", - "date": "Tue, 23 Jun 2026 12:19:43 GMT", - "openai-organization": "n8n-1", - "openai-processing-ms": "118", - "openai-project": "proj_ZAEXttE86YHh0Pzmrnh9cBEf", - "openai-version": "2020-10-01", - "server": "cloudflare", - "set-cookie": "__cf_bm=ffLjqdSxwdi_wB2U5qJv2SUvKmkvA1xcS6fjDCXnFQM-1782217183.1431067-1.0.1.1-S3BjOw5G5K03MX.6rtEywZZC50JL5OBwqF375.U10nddWDQi6oxq3nCUo_C53gcIw_88ZkfydgJAc5aPDES__LjlxYbnNR6a2WpvVlrlb.BfJo82nVhggY7nGPZCoNR0; HttpOnly; SameSite=None; Secure; Path=/; Domain=api.openai.com; Expires=Tue, 23 Jun 2026 12:49:43 GMT", - "strict-transport-security": "max-age=31536000; includeSubDomains; preload", - "transfer-encoding": "chunked", - "x-content-type-options": "nosniff", - "x-request-id": "req_ca230c59ba3149d3a14d4890cb973a7d" - }, - "responseIsBinary": false - } -] diff --git a/packages/@n8n/agents/src/__tests__/integration/cassettes/zod-validation-error/includes-the-zod-error-text-in-the-tool-result-visible-to-the-llm.json b/packages/@n8n/agents/src/__tests__/integration/cassettes/zod-validation-error/includes-the-zod-error-text-in-the-tool-result-visible-to-the-llm.json index ce22f4c1829..25bc64a1b85 100644 --- a/packages/@n8n/agents/src/__tests__/integration/cassettes/zod-validation-error/includes-the-zod-error-text-in-the-tool-result-visible-to-the-llm.json +++ b/packages/@n8n/agents/src/__tests__/integration/cassettes/zod-validation-error/includes-the-zod-error-text-in-the-tool-result-visible-to-the-llm.json @@ -127,7 +127,7 @@ { "type": "tool_result", "tool_use_id": "toolu_01J4prxB28Ksk13jkhDsHkMd", - "content": "Invalid input for tool find_user: Type validation failed: Value: {\"age\":150}.\nError message: [\n {\n \"code\": \"too_big\",\n \"maximum\": 99,\n \"type\": \"number\",\n \"inclusive\": true,\n \"exact\": false,\n \"message\": \"age must be at most 99\",\n \"path\": [\n \"age\"\n ]\n }\n]", + "content": "AI_InvalidToolInputError: Invalid input for tool find_user: AI_TypeValidationError: Type validation failed: Value: {\"age\":150}.\nError message: [\n {\n \"code\": \"too_big\",\n \"maximum\": 99,\n \"type\": \"number\",\n \"inclusive\": true,\n \"exact\": false,\n \"message\": \"age must be at most 99\",\n \"path\": [\n \"age\"\n ]\n }\n]", "is_error": true } ] @@ -237,7 +237,7 @@ { "type": "tool_result", "tool_use_id": "toolu_01J4prxB28Ksk13jkhDsHkMd", - "content": "Invalid input for tool find_user: Type validation failed: Value: {\"age\":150}.\nError message: [\n {\n \"code\": \"too_big\",\n \"maximum\": 99,\n \"type\": \"number\",\n \"inclusive\": true,\n \"exact\": false,\n \"message\": \"age must be at most 99\",\n \"path\": [\n \"age\"\n ]\n }\n]", + "content": "AI_InvalidToolInputError: Invalid input for tool find_user: AI_TypeValidationError: Type validation failed: Value: {\"age\":150}.\nError message: [\n {\n \"code\": \"too_big\",\n \"maximum\": 99,\n \"type\": \"number\",\n \"inclusive\": true,\n \"exact\": false,\n \"message\": \"age must be at most 99\",\n \"path\": [\n \"age\"\n ]\n }\n]", "is_error": true } ] diff --git a/packages/@n8n/agents/src/__tests__/integration/provider-options.test.ts b/packages/@n8n/agents/src/__tests__/integration/provider-options.test.ts index 4fbbd87f8b8..e3afef1c759 100644 --- a/packages/@n8n/agents/src/__tests__/integration/provider-options.test.ts +++ b/packages/@n8n/agents/src/__tests__/integration/provider-options.test.ts @@ -8,7 +8,7 @@ const describe = describeIf('anthropic'); /** * Integration tests for provider options: prompt caching, deep merge with - * thinking, external abort signal, and model config object form. + * reasoning, external abort signal, and model config object form. * * Prompt caching requires a system prompt of at least 1024 tokens for * Anthropic, so we generate a long instruction string. @@ -149,31 +149,31 @@ describe('prompt caching via stream', () => { }); // --------------------------------------------------------------------------- -// Thinking + cacheControl coexistence (deep merge) +// Reasoning + cacheControl coexistence // --------------------------------------------------------------------------- -describe('thinking + cacheControl coexistence', () => { - it('both thinking and prompt caching work simultaneously', async () => { +describe('reasoning + cacheControl coexistence', () => { + it('both reasoning and prompt caching work simultaneously', async () => { const agent = new Agent('thinking-cache-test') .model('anthropic', 'claude-sonnet-4-5') - .thinking('anthropic', { budgetTokens: 5000 }) + .reasoning('medium') .instructions(LONG_SYSTEM_PROMPT, { providerOptions: { anthropic: { cacheControl: { type: 'ephemeral' } }, }, }); - // First call — cache miss, but thinking should work + // First call — cache miss, but reasoning should work const { stream: stream1 } = await agent.stream('What is 7 * 8?', { persistence: { resourceId: 'user1', threadId: 'thread1' }, }); const chunks1 = await collectStreamChunks(stream1); - // Should have reasoning chunks (thinking is enabled) + // Should have reasoning chunks const reasoningChunks = chunksOfType(chunks1, 'reasoning-delta'); expect(reasoningChunks.length).toBeGreaterThan(0); - // Second call — cache hit, thinking should still work + // Second call — cache hit, reasoning should still work const { stream: stream2 } = await agent.stream('What is 12 * 13?', { persistence: { resourceId: 'user1', threadId: 'thread2' }, }); diff --git a/packages/@n8n/agents/src/__tests__/integration/telemetry-langsmith.test.ts b/packages/@n8n/agents/src/__tests__/integration/telemetry-langsmith.test.ts index 6683a3c66cc..c592cfe160a 100644 --- a/packages/@n8n/agents/src/__tests__/integration/telemetry-langsmith.test.ts +++ b/packages/@n8n/agents/src/__tests__/integration/telemetry-langsmith.test.ts @@ -6,7 +6,7 @@ * LangSmith, and verifies the full pipeline works end-to-end. * * Pipeline under test: - * Agent.generate() → AI SDK (generateText with experimental_telemetry) + * Agent.generate() → AI SDK (generateText with telemetry) * → OTel spans with ai.operationId → LangSmithOTLPSpanProcessor * → LangSmithOTLPTraceExporter → HTTP POST → captured by local server */ @@ -176,7 +176,7 @@ describe('Telemetry → LangSmith integration', () => { onStart: () => { hookEvents.push('start'); }, - onFinish: () => { + onEnd: () => { hookEvents.push('finish'); }, }) diff --git a/packages/@n8n/agents/src/__tests__/integration/thinking.test.ts b/packages/@n8n/agents/src/__tests__/integration/thinking.test.ts index 8eda3b5dec0..6108e63410c 100644 --- a/packages/@n8n/agents/src/__tests__/integration/thinking.test.ts +++ b/packages/@n8n/agents/src/__tests__/integration/thinking.test.ts @@ -8,16 +8,16 @@ import { Agent } from '../../index'; * * These require models that support extended thinking: * - Anthropic: claude-sonnet-4-5 (not haiku — it doesn't support thinking) - * - OpenAI: o3-mini (reasoning model) + * - OpenAI: gpt-5-mini (reasoning model with streamed summaries) */ const describeAnthropic = describeIf('anthropic'); -describeAnthropic('thinking stream (Anthropic)', () => { - it('emits reasoning-delta chunks when thinking is enabled', async () => { +describeAnthropic('reasoning stream (Anthropic)', () => { + it('emits reasoning-delta chunks when reasoning is enabled', async () => { const agent = new Agent('thinking-test') .model('anthropic', 'claude-sonnet-4-5') - .thinking('anthropic', { budgetTokens: 5000 }) + .reasoning('medium') .instructions('You are a helpful assistant. Think carefully before answering.'); const { stream: fullStream } = await agent.stream('What is 17 * 23?'); @@ -42,19 +42,28 @@ describeAnthropic('thinking stream (Anthropic)', () => { const describeOpenAI = describeIf('openai'); -describeOpenAI('thinking stream (OpenAI)', () => { - it('works with reasoning model and .thinking() enabled', async () => { +describeOpenAI('reasoning stream (OpenAI)', () => { + it('works with a reasoning model when reasoning is enabled', async () => { const agent = new Agent('openai-thinking-test') - .model('openai', 'o3-mini') - .thinking('openai', { reasoningEffort: 'medium' }) - .instructions('You are a helpful assistant.'); + .model('openai', 'gpt-5-mini') + .reasoning('medium') + .instructions('You are a helpful assistant. Think carefully before answering.'); - const { stream: fullStream } = await agent.stream('What is 17 * 23?'); + const { stream: fullStream } = await agent.stream( + 'Find the smallest positive integer divisible by every integer from 1 through 15, and explain your method.', + ); const chunks = await collectStreamChunks(fullStream); - // OpenAI reasoning models do internal reasoning but don't expose it - // as streamed chunks — verify the agent produces a text response. + const reasoningChunks = chunksOfType(chunks, 'reasoning-delta'); + expect(reasoningChunks.length).toBeGreaterThan(0); + expect( + reasoningChunks + .filter((chunk): chunk is typeof chunk & { delta: string } => 'delta' in chunk) + .map((chunk) => chunk.delta) + .join('').length, + ).toBeGreaterThan(0); + const textChunks = chunksOfType(chunks, 'text-delta'); expect(textChunks.length).toBeGreaterThan(0); @@ -62,6 +71,6 @@ describeOpenAI('thinking stream (OpenAI)', () => { .filter((c): c is typeof c & { delta: string } => 'delta' in c) .map((c) => c.delta) .join(''); - expect(text).toContain('391'); + expect(text.length).toBeGreaterThan(0); }); }); diff --git a/packages/@n8n/agents/src/__tests__/integration/zod-validation-error.test.ts b/packages/@n8n/agents/src/__tests__/integration/zod-validation-error.test.ts index d89bd6d556e..b443365ae32 100644 --- a/packages/@n8n/agents/src/__tests__/integration/zod-validation-error.test.ts +++ b/packages/@n8n/agents/src/__tests__/integration/zod-validation-error.test.ts @@ -42,8 +42,8 @@ describe('Zod validation errors surface to LLM and allow self-correction', () => 'Find a user aged 150. If that age is invalid, use 25 instead and retry. You MUST find a user aged 150, and only then use 25', ); - expect(result.finishReason).toBe('stop'); expect(result.error).toBeUndefined(); + expect(result.finishReason).toBe('stop'); // At least two tool-call messages: one rejected, one resolved const allMessages = filterLlmMessages(result.messages); diff --git a/packages/@n8n/agents/src/__tests__/langsmith-telemetry.test.ts b/packages/@n8n/agents/src/__tests__/langsmith-telemetry.test.ts index a86cc1cd71f..ad83ccc09c3 100644 --- a/packages/@n8n/agents/src/__tests__/langsmith-telemetry.test.ts +++ b/packages/@n8n/agents/src/__tests__/langsmith-telemetry.test.ts @@ -16,7 +16,7 @@ const mockBatchProcessorInstances: Array<{ }> = []; const mockProviderConfigs: unknown[] = []; const mockAwaitPendingTraceBatches = vi.fn(async () => await Promise.resolve()); -const mockTracer = { startSpan: vi.fn() }; +const mockTracer = { startSpan: vi.fn(), startActiveSpan: vi.fn() }; const mockProvider = { getTracer: vi.fn(() => mockTracer), register: vi.fn(), diff --git a/packages/@n8n/agents/src/index.ts b/packages/@n8n/agents/src/index.ts index d61a7ffcfba..9abd382e74d 100644 --- a/packages/@n8n/agents/src/index.ts +++ b/packages/@n8n/agents/src/index.ts @@ -27,6 +27,7 @@ export type { OpenAIThinkingConfig, GoogleThinkingConfig, XaiThinkingConfig, + ReasoningLevel, SerializableAgentState, AgentRunState, MemoryConfig, @@ -207,9 +208,12 @@ export { verify } from './sdk/verify'; export type { VerifyResult } from './sdk/verify'; export type { ContentCitation, + ContentCustom, ContentFile, + ContentFileRef, ContentMetadata, ContentReasoning, + ContentReasoningFile, ContentText, ContentToolCall, Message, @@ -219,6 +223,8 @@ export type { CustomAgentMessages, AgentDbMessage, } from './types/sdk/message'; +export { stripHydratedFileData } from './types/sdk/message'; +export type { BuiltFileStore } from './types/sdk/file-store'; export type { HandlerExecutor } from './types/sdk/handler-executor'; export { filterLlmMessages, diff --git a/packages/@n8n/agents/src/runtime/__tests__/agent-runtime-conversion.test.ts b/packages/@n8n/agents/src/runtime/__tests__/agent-runtime-conversion.test.ts index b015222b2bf..9848e57f254 100644 --- a/packages/@n8n/agents/src/runtime/__tests__/agent-runtime-conversion.test.ts +++ b/packages/@n8n/agents/src/runtime/__tests__/agent-runtime-conversion.test.ts @@ -12,6 +12,7 @@ * The tool-result is inserted right after its tool-call, regardless of what * messages follow it in the n8n list. */ +import type { ModelMessage } from 'ai'; import { describe, it, expect } from 'vitest'; import type { Message } from '../../types/sdk/message'; @@ -154,6 +155,70 @@ describe('toAiMessages + fromAiMessages — round-trip', () => { }); }); + it('round-trips SDK 7 reasoning files with provider replay state', () => { + const aiMessage: ModelMessage = { + role: 'assistant', + content: [ + { + type: 'reasoning-file', + data: 'base64-reasoning-data', + mediaType: 'application/octet-stream', + providerOptions: { google: { thoughtSignature: 'gemini-signature' } }, + }, + ], + }; + + const agentMessages = fromAiMessages([aiMessage]); + + expect(agentMessages).toEqual([ + { + role: 'assistant', + content: [ + { + type: 'reasoning-file', + data: 'base64-reasoning-data', + mediaType: 'application/octet-stream', + providerOptions: { google: { thoughtSignature: 'gemini-signature' } }, + }, + ], + }, + ]); + expect(toAiMessages(agentMessages as Message[])).toEqual([aiMessage]); + }); + + it('round-trips SDK 7 custom provider state', () => { + const aiMessage: ModelMessage = { + role: 'assistant', + content: [ + { + type: 'custom', + kind: 'openai.compaction', + providerOptions: { + openai: { itemId: 'cmp_123', encryptedContent: 'encrypted' }, + }, + }, + ], + }; + + const agentMessages = fromAiMessages([aiMessage]); + + expect(agentMessages).toEqual([ + { + role: 'assistant', + content: [ + { + type: 'custom', + kind: 'openai.compaction', + providerOptions: { + openai: { itemId: 'cmp_123', encryptedContent: 'encrypted' }, + }, + }, + ], + }, + ]); + expect(toAiMessages(agentMessages as Message[])).toEqual([aiMessage]); + }); + it.each([ { name: 'no provider metadata', content: {} }, { diff --git a/packages/@n8n/agents/src/runtime/__tests__/agent-runtime.test.ts b/packages/@n8n/agents/src/runtime/__tests__/agent-runtime.test.ts index 2504b25dff1..d6dac9beada 100644 --- a/packages/@n8n/agents/src/runtime/__tests__/agent-runtime.test.ts +++ b/packages/@n8n/agents/src/runtime/__tests__/agent-runtime.test.ts @@ -144,7 +144,7 @@ async function collectChunks(stream: ReadableStream): Promise { expect(counter.incrementTokenCount).toHaveBeenCalledWith(15); }); - it('forwards onStepStart and onStepFinish to generateText and streamText', async () => { + it('forwards onStepStart and onStepEnd to generateText and streamText', async () => { generateText.mockResolvedValue(makeGenerateSuccess()); streamText.mockReturnValue(makeStreamSuccess()); const onStepStart = vi.fn(); - const onStepFinish = vi.fn(); + const onStepEnd = vi.fn(); const { runtime } = createRuntime(); - await runtime.generate('hi', { onStepStart, onStepFinish }); - const streamResult = await runtime.stream('hi', { onStepStart, onStepFinish }); + await runtime.generate('hi', { onStepStart, onStepEnd }); + const streamResult = await runtime.stream('hi', { onStepStart, onStepEnd }); await collectChunks(streamResult.stream); for (const call of generateText.mock.calls) { const args = call[0] as Record; - expect(args.experimental_onStepStart).toBe(onStepStart); - expect(args.onStepFinish).toBe(onStepFinish); + expect(args.onStepStart).toBe(onStepStart); + expect(args.onStepEnd).toBe(onStepEnd); } for (const call of streamText.mock.calls) { const args = call[0] as Record; - expect(args.experimental_onStepStart).toBe(onStepStart); - expect(args.onStepFinish).toBe(onStepFinish); + expect(args.onStepStart).toBe(onStepStart); + expect(args.onStepEnd).toBe(onStepEnd); } }); + it('allows system-role messages in generateText and streamText history', async () => { + generateText.mockResolvedValue(makeGenerateSuccess()); + streamText.mockReturnValue(makeStreamSuccess()); + const input: Message[] = [ + { role: 'system', content: [{ type: 'text', text: 'Historical instruction' }] }, + { role: 'user', content: [{ type: 'text', text: 'Hello' }] }, + ]; + + const { runtime } = createRuntime(); + await runtime.generate(input); + const streamResult = await runtime.stream(input); + await collectChunks(streamResult.stream); + + expect(generateText.mock.calls[0]?.[0]).toEqual( + expect.objectContaining({ allowSystemInMessages: true }), + ); + expect(streamText.mock.calls[0]?.[0]).toEqual( + expect.objectContaining({ allowSystemInMessages: true }), + ); + }); + it('counts provider-executed tool calls when surfaced by the model', async () => { generateText .mockResolvedValueOnce({ @@ -545,7 +566,7 @@ describe('AgentRuntime — execution counters', () => { streamText .mockReturnValueOnce({ - fullStream: makeChunkStream([{ type: 'text-delta', textDelta: 'thinking...' }]), + stream: makeChunkStream([{ type: 'text-delta', id: 'text-1', text: 'thinking...' }]), finishReason: Promise.resolve('tool-calls'), usage: Promise.resolve({ inputTokens: 10, outputTokens: 5, totalTokens: 15 }), response: Promise.resolve({ @@ -949,7 +970,7 @@ describe('AgentRuntime — eager input persistence', () => { // Same shape as the generate case, on the streaming path (the one Instance AI uses): // the abort is handled by the StreamSession, which must persist the turn-so-far. streamText.mockReturnValueOnce({ - fullStream: makeChunkStream([{ type: 'text-delta', textDelta: 'on it...' }]), + stream: makeChunkStream([{ type: 'text-delta', id: 'text-1', text: 'on it...' }]), finishReason: Promise.resolve('tool-calls'), usage: Promise.resolve({ inputTokens: 10, outputTokens: 5, totalTokens: 15 }), response: Promise.resolve({ @@ -985,7 +1006,7 @@ describe('AgentRuntime — eager input persistence', () => { // The model streams two text deltas, then the stop lands — before the turn // completes, so its `newMessages` are never built (finishReason/response reject). streamText.mockReturnValueOnce({ - fullStream: (async function* () { + stream: (async function* () { yield { type: 'text-delta', id: 't1', text: 'Here is my ' }; yield { type: 'text-delta', id: 't1', text: 'partial answer' }; await Promise.resolve(); @@ -1020,7 +1041,7 @@ describe('AgentRuntime — eager input persistence', () => { // fires as the stream closes — before the loop folds the turn into the list. The // completed text must still be recovered (the clear is deferred until the fold). streamText.mockReturnValueOnce({ - fullStream: (async function* () { + stream: (async function* () { yield { type: 'text-delta', id: 't1', text: 'Complete answer' }; await Promise.resolve(); controller.abort(); @@ -1131,7 +1152,7 @@ describe('AgentRuntime.stream() — usage billing on abort', () => { // provider's `message_start` raw event already carried the input/cache + // initial output tokens. The run must bill those. streamText.mockReturnValue({ - fullStream: (async function* () { + stream: (async function* () { yield { type: 'raw', rawValue: { @@ -1187,7 +1208,7 @@ describe('AgentRuntime.stream() — usage billing on abort', () => { streamText // Turn 1 completes with a tool call; its usage is folded into the total. .mockReturnValueOnce({ - fullStream: makeChunkStream([{ type: 'text-delta', textDelta: 'thinking...' }]), + stream: makeChunkStream([{ type: 'text-delta', id: 'text-1', text: 'thinking...' }]), finishReason: Promise.resolve('tool-calls'), usage: Promise.resolve({ inputTokens: 10, outputTokens: 5, totalTokens: 15 }), response: Promise.resolve({ @@ -1207,7 +1228,7 @@ describe('AgentRuntime.stream() — usage billing on abort', () => { // Turn 2 aborts mid-stream after the provider reported its raw usage; the // SDK promises reject so the only signal is the captured raw event. .mockReturnValueOnce({ - fullStream: (async function* () { + stream: (async function* () { yield { type: 'raw', rawValue: { @@ -1258,7 +1279,7 @@ describe('AgentRuntime.stream() — usage billing on abort', () => { // Single turn: emits a raw usage event (captured) AND resolves its final // usage. The raw capture must not be re-added on top of the folded total. streamText.mockReturnValueOnce({ - fullStream: makeChunkStream([ + stream: makeChunkStream([ { type: 'raw', rawValue: { @@ -1310,7 +1331,7 @@ describe('AgentRuntime.stream() — usage billing on abort', () => { streamText.mockReturnValue({ // Sync generator: `for await` consumes it fine and there's nothing to await. - fullStream: (function* () { + stream: (function* () { yield { type: 'raw', rawValue: { @@ -1339,19 +1360,19 @@ describe('AgentRuntime.stream() — usage billing on abort', () => { expect(runtime.getState().status).toBe('cancelled'); }); - it('requests includeRawChunks only when recoverUsageOnAbort is set', async () => { + it('requests raw chunks only when recoverUsageOnAbort is set', async () => { streamText.mockReturnValue(makeStreamSuccess('ok')); const off = createRuntime(undefined, 'anthropic/claude-sonnet-4-6'); await collectChunks((await off.runtime.stream('hello')).stream); - expect(streamText.mock.calls.at(-1)?.[0]).not.toHaveProperty('includeRawChunks'); + expect(streamText.mock.calls.at(-1)?.[0]).not.toHaveProperty('include.rawChunks'); streamText.mockClear(); streamText.mockReturnValue(makeStreamSuccess('ok')); const on = createRuntime(undefined, 'anthropic/claude-sonnet-4-6'); await collectChunks((await on.runtime.stream('hello', { recoverUsageOnAbort: true })).stream); - expect(streamText.mock.calls.at(-1)?.[0]).toMatchObject({ includeRawChunks: true }); + expect(streamText.mock.calls.at(-1)?.[0]).toHaveProperty('include.rawChunks', true); }); }); @@ -1367,7 +1388,7 @@ describe('AgentRuntime.stream() — graceful error contract', () => { it('resolves (never rejects) when the LLM stream throws', async () => { const streamError = new Error('stream broke'); streamText.mockReturnValue({ - fullStream: makeErrorStream(streamError), + stream: makeErrorStream(streamError), finishReason: silentReject(streamError), usage: Promise.resolve(undefined), response: silentReject(streamError), @@ -1381,7 +1402,7 @@ describe('AgentRuntime.stream() — graceful error contract', () => { it('yields an error chunk when the LLM stream throws', async () => { const streamError = new Error('stream broke'); streamText.mockReturnValue({ - fullStream: makeErrorStream(streamError), + stream: makeErrorStream(streamError), finishReason: silentReject(streamError), usage: Promise.resolve(undefined), response: silentReject(streamError), @@ -1399,7 +1420,7 @@ describe('AgentRuntime.stream() — graceful error contract', () => { it('closes the stream cleanly (with a finish chunk) after an error', async () => { const streamError = new Error('stream broke'); streamText.mockReturnValue({ - fullStream: makeErrorStream(streamError), + stream: makeErrorStream(streamError), finishReason: silentReject(streamError), usage: Promise.resolve(undefined), response: silentReject(streamError), @@ -1423,7 +1444,7 @@ describe('AgentRuntime.stream() — graceful error contract', () => { it('sets state to "failed" after a stream error', async () => { const streamError = new Error('stream broke'); streamText.mockReturnValue({ - fullStream: makeErrorStream(streamError), + stream: makeErrorStream(streamError), finishReason: silentReject(streamError), usage: Promise.resolve(undefined), response: silentReject(streamError), @@ -1456,7 +1477,7 @@ describe('AgentRuntime.stream() — graceful error contract', () => { const streamError = new Error('transient'); streamText .mockReturnValueOnce({ - fullStream: makeErrorStream(streamError), + stream: makeErrorStream(streamError), finishReason: silentReject(streamError), usage: Promise.resolve(undefined), response: silentReject(streamError), @@ -1483,7 +1504,7 @@ describe('AgentRuntime.stream() — graceful error contract', () => { it('never throws when consuming the stream after an error', async () => { const streamError = new Error('stream broke'); streamText.mockReturnValue({ - fullStream: makeErrorStream(streamError), + stream: makeErrorStream(streamError), finishReason: silentReject(streamError), usage: Promise.resolve(undefined), response: silentReject(streamError), @@ -2700,7 +2721,7 @@ describe('AgentRuntime — concurrent tool execution', () => { const { runtime } = createRuntimeWithTools([suspendTool], 1); streamText.mockReturnValue({ - fullStream: makeChunkStream([{ type: 'text-delta', textDelta: 'thinking...' }]), + stream: makeChunkStream([{ type: 'text-delta', id: 'text-1', text: 'thinking...' }]), finishReason: Promise.resolve('tool-calls'), usage: Promise.resolve({ inputTokens: 10, outputTokens: 5, totalTokens: 15 }), response: Promise.resolve({ @@ -2763,7 +2784,7 @@ describe('AgentRuntime — concurrent tool execution', () => { const { runtime } = createRuntimeWithTools([suspendTool], Infinity); streamText.mockReturnValue({ - fullStream: makeChunkStream([{ type: 'text-delta', textDelta: 'thinking...' }]), + stream: makeChunkStream([{ type: 'text-delta', id: 'text-1', text: 'thinking...' }]), finishReason: Promise.resolve('tool-calls'), usage: Promise.resolve({ inputTokens: 10, outputTokens: 5, totalTokens: 15 }), response: Promise.resolve({ @@ -2812,7 +2833,7 @@ describe('AgentRuntime — concurrent tool execution', () => { const { runtime } = createRuntimeWithTools([suspendTool], 1); streamText.mockReturnValue({ - fullStream: makeChunkStream([{ type: 'text-delta', textDelta: 'thinking...' }]), + stream: makeChunkStream([{ type: 'text-delta', id: 'text-1', text: 'thinking...' }]), finishReason: Promise.resolve('tool-calls'), usage: Promise.resolve({ inputTokens: 10, outputTokens: 5, totalTokens: 15 }), response: Promise.resolve({ @@ -2887,7 +2908,7 @@ describe('AgentRuntime — concurrent tool execution', () => { streamText .mockReturnValueOnce({ - fullStream: makeChunkStream([{ type: 'text-delta', textDelta: 'thinking...' }]), + stream: makeChunkStream([{ type: 'text-delta', id: 'text-1', text: 'thinking...' }]), finishReason: Promise.resolve('tool-calls'), usage: Promise.resolve({ inputTokens: 10, outputTokens: 5, totalTokens: 15 }), response: Promise.resolve({ @@ -3225,7 +3246,7 @@ describe('AgentRuntime.stream() — structured output', () => { const expected = { answer: '5', score: 1 }; streamText .mockReturnValueOnce({ - fullStream: makeChunkStream([ + stream: makeChunkStream([ { type: 'tool-call', toolCallId: 'tc-1', toolName: 'add', args: { a: 2, b: 3 } }, ]), finishReason: Promise.resolve('tool-calls'), @@ -4049,7 +4070,7 @@ describe('AgentRuntime — tool approval (HITL wrapper)', () => { .build(); streamText.mockReturnValue({ - fullStream: makeChunkStream([{ type: 'text-delta', textDelta: 'checking...' }]), + stream: makeChunkStream([{ type: 'text-delta', id: 'text-1', text: 'checking...' }]), finishReason: Promise.resolve('tool-calls'), usage: Promise.resolve({ inputTokens: 10, outputTokens: 5, totalTokens: 15 }), response: Promise.resolve({ @@ -4119,7 +4140,7 @@ describe('AgentRuntime — tool approval (HITL wrapper)', () => { streamText .mockReturnValueOnce({ - fullStream: makeChunkStream([{ type: 'text-delta', textDelta: 'checking...' }]), + stream: makeChunkStream([{ type: 'text-delta', id: 'text-1', text: 'checking...' }]), finishReason: Promise.resolve('tool-calls'), usage: Promise.resolve({ inputTokens: 10, outputTokens: 5, totalTokens: 15 }), response: Promise.resolve({ @@ -4295,7 +4316,7 @@ describe('external abort signal', () => { streamText .mockReturnValueOnce({ - fullStream: makeChunkStream([{ type: 'text-delta', textDelta: 'checking...' }]), + stream: makeChunkStream([{ type: 'text-delta', id: 'text-1', text: 'checking...' }]), finishReason: Promise.resolve('tool-calls'), usage: Promise.resolve({ inputTokens: 10, outputTokens: 5, totalTokens: 15 }), response: Promise.resolve({ @@ -4405,7 +4426,7 @@ describe('AgentRuntime — abort during a tool batch', () => { toolCalls: Array<{ toolCallId: string; toolName: string; args: Record }>, ) { return { - fullStream: makeChunkStream([{ type: 'text-delta', textDelta: 'calling tools...' }]), + stream: makeChunkStream([{ type: 'text-delta', id: 'text-1', text: 'calling tools...' }]), finishReason: Promise.resolve('tool-calls'), usage: Promise.resolve({ inputTokens: 10, outputTokens: 5, totalTokens: 15 }), response: Promise.resolve({ @@ -4640,22 +4661,22 @@ describe('AgentRuntime.resume() — checkpoint lifecycle', () => { }); // --------------------------------------------------------------------------- -// Provider options merging +// Generic reasoning // --------------------------------------------------------------------------- -describe('provider options merging', () => { +describe('reasoning', () => { beforeEach(() => { vi.clearAllMocks(); }); - it('should deep-merge thinking config with call-level providerOptions', async () => { + it('passes reasoning as a top-level generateText option without provider-specific settings', async () => { generateText.mockResolvedValue(makeGenerateSuccess()); const runtime = new AgentRuntime({ name: 'test', model: 'anthropic/claude-sonnet-4-5', instructions: 'You are a test assistant.', - thinking: { budgetTokens: 10000 }, + reasoning: 'high', }); await runtime.generate('hello', { @@ -4666,13 +4687,53 @@ describe('provider options merging', () => { }); const callArgs = generateText.mock.calls[0][0] as Record; + expect(callArgs.reasoning).toBe('high'); expect((callArgs.providerOptions as Record>).anthropic).toEqual( - { - thinking: { type: 'enabled', budgetTokens: 10000 }, - cacheControl: { type: 'ephemeral' }, - }, + { cacheControl: { type: 'ephemeral' } }, ); }); + + it('passes reasoning as a top-level streamText option', async () => { + streamText.mockReturnValue(makeStreamSuccess()); + const runtime = new AgentRuntime({ + name: 'test', + model: 'openai/gpt-5.1', + instructions: 'You are a test assistant.', + reasoning: 'low', + }); + + const result = await runtime.stream('hello'); + await collectChunks(result.stream); + + const callArgs = streamText.mock.calls[0][0] as Record; + expect(callArgs.reasoning).toBe('low'); + expect(callArgs.providerOptions).toBeUndefined(); + }); +}); + +describe('provider-specific thinking', () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + it('maps a fixed Anthropic thinking budget into providerOptions', async () => { + generateText.mockResolvedValue(makeGenerateSuccess()); + + const runtime = new AgentRuntime({ + name: 'test', + model: 'anthropic/claude-sonnet-4-5', + instructions: 'You are a test assistant.', + thinking: { budgetTokens: 4096 }, + }); + + await runtime.generate('hello'); + + const callArgs = generateText.mock.calls[0][0] as Record; + expect(callArgs.reasoning).toBeUndefined(); + expect(callArgs.providerOptions).toEqual({ + anthropic: { thinking: { type: 'enabled', budgetTokens: 4096 } }, + }); + }); }); // --------------------------------------------------------------------------- @@ -4686,7 +4747,7 @@ describe('tool systemInstruction merging', () => { function getSystemMessageText(): string { const callArgs = generateText.mock.calls[0][0] as Record; - const systemMsg = callArgs.system; + const systemMsg = callArgs.instructions; if (Array.isArray(systemMsg)) { return systemMsg.map((entry) => String((entry as { content: string }).content)).join(''); } @@ -4819,10 +4880,10 @@ describe('tool systemInstruction merging', () => { await runtime.generate('load the deferred capability'); const calls = generateText.mock.calls as Array< - [{ system: Array<{ content: string }> | { content: string } }] + [{ instructions: Array<{ content: string }> | { content: string } }] >; - const beforeLoadSystem = calls[0][0].system; - const afterLoadSystem = calls[1][0].system; + const beforeLoadSystem = calls[0][0].instructions; + const afterLoadSystem = calls[1][0].instructions; const beforeFirst = Array.isArray(beforeLoadSystem) ? beforeLoadSystem[0].content @@ -4866,7 +4927,7 @@ describe('instruction providerOptions', () => { }); const callArgs = generateText.mock.calls[0][0] as Record; - const systemMsg = callArgs.system as Record; + const systemMsg = callArgs.instructions as Record; expect(systemMsg.role).toBe('system'); expect(systemMsg.providerOptions).toEqual({ anthropic: { cacheControl: { type: 'ephemeral' } }, @@ -4896,7 +4957,7 @@ describe('promptCaching', () => { await runtime.generate('hello'); const callArgs = generateText.mock.calls[0][0] as Record; - const systemMsg = callArgs.system as Record; + const systemMsg = callArgs.instructions as Record; expect(systemMsg.providerOptions).toEqual({ anthropic: { cacheControl: { type: 'ephemeral', ttl: '1h' } }, }); @@ -4924,14 +4985,14 @@ describe('promptCaching', () => { expect(typeof openaiOpts.promptCacheKey).toBe('string'); }); - it('merges thinking config with generated prompt caching and caller providerOptions for OpenAI', async () => { + it('keeps generic reasoning separate from prompt caching and caller providerOptions', async () => { generateText.mockResolvedValue(makeGenerateSuccess()); const runtime = new AgentRuntime({ name: 'test', model: 'openai/gpt-5.1', instructions: 'You are a test assistant.', - thinking: { reasoningEffort: 'high' }, + reasoning: 'high', promptCaching: { enabled: true }, }); @@ -4941,7 +5002,9 @@ describe('promptCaching', () => { const callArgs = generateText.mock.calls[0][0] as Record; const openaiOpts = (callArgs.providerOptions as Record>).openai; - expect(openaiOpts.reasoningEffort).toBe('high'); + expect(callArgs.reasoning).toBe('high'); + expect(openaiOpts).not.toHaveProperty('reasoningEffort'); + expect(openaiOpts).not.toHaveProperty('reasoningSummary'); expect(openaiOpts.promptCacheRetention).toBe('24h'); expect(typeof openaiOpts.promptCacheKey).toBe('string'); expect(openaiOpts.strictJsonSchema).toBe(false); @@ -5297,10 +5360,10 @@ describe('AgentRuntime — observation log jobs', () => { }); const callArgs = (generateText.mock.calls[0] as [unknown])[0] as { - system: { content: string }; + instructions: { content: string }; tools: Record; }; - const systemPrompt = callArgs.system?.content ?? ''; + const systemPrompt = callArgs.instructions?.content ?? ''; expect(systemPrompt).not.toContain(''); expect(systemPrompt).not.toContain('Postgres'); expect(systemPrompt).not.toContain('SQLite'); @@ -5420,17 +5483,17 @@ describe('AgentRuntime — observation log jobs', () => { const generateTextMock = generateText as MockedFunction< (input: { - system: { content: string } | Array<{ content: string }>; + instructions: { content: string } | Array<{ content: string }>; messages: Array<{ role: string; content: unknown; }>; }) => unknown >; - const [{ system, messages }] = generateTextMock.mock.calls[0]; - const systemText = Array.isArray(system) - ? system.map((entry) => entry.content).join('') - : system.content; + const [{ instructions, messages }] = generateTextMock.mock.calls[0]; + const systemText = Array.isArray(instructions) + ? instructions.map((entry) => entry.content).join('') + : instructions.content; expect(systemText).toContain('Resource one memory.'); expect(systemText).toContain('Resource two memory.'); expect(JSON.stringify(messages)).not.toContain('remember resource-one preference'); @@ -5637,7 +5700,7 @@ describe('AgentRuntime — telemetry propagation', () => { tracer: { startSpan: vi.fn() }, }; - it('passes telemetry config into generateText as experimental_telemetry', async () => { + it('passes telemetry config into generateText as telemetry', async () => { generateText.mockResolvedValue(makeGenerateSuccess()); const runtime = new AgentRuntime({ @@ -5651,13 +5714,12 @@ describe('AgentRuntime — telemetry propagation', () => { await runtime.generate('hello'); const callArgs = generateText.mock.calls[0][0] as Record; - const expTelemetry = callArgs.experimental_telemetry as Record; - expect(expTelemetry).toBeDefined(); - expect(expTelemetry.isEnabled).toBe(true); - expect(expTelemetry.functionId).toBe('test-agent'); - expect(expTelemetry.tracer).toBe(baseTelemetry.tracer); - expect(expTelemetry.recordInputs).toBe(true); - expect(expTelemetry.recordOutputs).toBe(false); + const telemetry = callArgs.telemetry as Record; + expect(telemetry).toBeDefined(); + expect(telemetry.isEnabled).toBe(true); + expect(telemetry.functionId).toBe('test-agent'); + expect(telemetry.recordInputs).toBe(true); + expect(telemetry.recordOutputs).toBe(false); }); it('uses updated telemetry config for later runs', async () => { @@ -5680,9 +5742,8 @@ describe('AgentRuntime — telemetry propagation', () => { await runtime.generate('hello'); const callArgs = generateText.mock.calls[0][0] as Record; - const expTelemetry = callArgs.experimental_telemetry as Record; - expect(expTelemetry.functionId).toBe('updated-agent'); - expect(expTelemetry.metadata).toEqual({ env: 'updated' }); + const telemetry = callArgs.telemetry as Record; + expect(telemetry.functionId).toBe('updated-agent'); }); it('wraps generate calls in a telemetry root span when the tracer supports active spans', async () => { @@ -5741,6 +5802,7 @@ describe('AgentRuntime — telemetry propagation', () => { const telemetry: BuiltTelemetry = { ...baseTelemetry, runtimeRootSpanEnabled: false, + integrations: [{ onStart: vi.fn() }], tracer, }; @@ -5757,11 +5819,11 @@ describe('AgentRuntime — telemetry propagation', () => { expect(tracer.startActiveSpan).not.toHaveBeenCalled(); const callArgs = generateText.mock.calls[0][0] as Record; - expect(callArgs.experimental_telemetry).toEqual( + expect(callArgs.telemetry).toEqual( expect.objectContaining({ isEnabled: true, functionId: 'test-agent', - tracer, + integrations: telemetry.integrations, }), ); }); @@ -5827,7 +5889,7 @@ describe('AgentRuntime — telemetry propagation', () => { expect(attributes['gen_ai.prompt']).toEqual(expect.stringContaining('"input_schema"')); }); - it('passes telemetry config into streamText as experimental_telemetry', async () => { + it('passes telemetry config into streamText as telemetry', async () => { streamText.mockReturnValue(makeStreamSuccess()); const runtime = new AgentRuntime({ @@ -5842,11 +5904,10 @@ describe('AgentRuntime — telemetry propagation', () => { await collectChunks(stream); const callArgs = streamText.mock.calls[0][0] as Record; - const expTelemetry = callArgs.experimental_telemetry as Record; - expect(expTelemetry).toBeDefined(); - expect(expTelemetry.isEnabled).toBe(true); - expect(expTelemetry.functionId).toBe('test-agent'); - expect(expTelemetry.tracer).toBe(baseTelemetry.tracer); + const telemetry = callArgs.telemetry as Record; + expect(telemetry).toBeDefined(); + expect(telemetry.isEnabled).toBe(true); + expect(telemetry.functionId).toBe('test-agent'); }); it('enables smoothStream by default on streamText', async () => { @@ -5926,12 +5987,11 @@ describe('AgentRuntime — telemetry propagation', () => { }); const callArgs = generateText.mock.calls[0][0] as Record; - const expTelemetry = callArgs.experimental_telemetry as Record; - expect(expTelemetry).toBeDefined(); - expect(expTelemetry.isEnabled).toBe(true); + const telemetry = callArgs.telemetry as Record; + expect(telemetry).toBeDefined(); + expect(telemetry.isEnabled).toBe(true); // Inherited telemetry uses the child agent's name as functionId - expect(expTelemetry.functionId).toBe('child-agent'); - expect(expTelemetry.tracer).toBe(baseTelemetry.tracer); + expect(telemetry.functionId).toBe('child-agent'); }); it('passes resolved telemetry to tool handlers via parentTelemetry', async () => { @@ -6088,7 +6148,7 @@ describe('AgentRuntime — telemetry propagation', () => { expect(capturedTelemetry!.tracer).toBe(baseTelemetry.tracer); }); - it('does not include experimental_telemetry when telemetry is disabled', async () => { + it('does not include telemetry when telemetry is disabled', async () => { generateText.mockResolvedValue(makeGenerateSuccess()); const runtime = new AgentRuntime({ @@ -6102,7 +6162,7 @@ describe('AgentRuntime — telemetry propagation', () => { await runtime.generate('hello'); const callArgs = generateText.mock.calls[0][0] as Record; - expect(callArgs.experimental_telemetry).toBeUndefined(); + expect(callArgs.telemetry).toBeUndefined(); }); }); @@ -6211,7 +6271,7 @@ describe('AgentRuntime.resume() with createCancellation() — auto-bypass', () = streamText .mockReturnValueOnce({ - fullStream: makeChunkStream([{ type: 'text-delta', textDelta: 'thinking...' }]), + stream: makeChunkStream([{ type: 'text-delta', id: 'text-1', text: 'thinking...' }]), finishReason: Promise.resolve('tool-calls'), usage: Promise.resolve({ inputTokens: 10, outputTokens: 5, totalTokens: 15 }), response: Promise.resolve({ @@ -6397,7 +6457,7 @@ describe('AgentRuntime — toModelOutput error resilience', () => { streamText .mockReturnValueOnce({ - fullStream: makeChunkStream([{ type: 'text-delta', textDelta: 'thinking...' }]), + stream: makeChunkStream([{ type: 'text-delta', id: 'text-1', text: 'thinking...' }]), finishReason: Promise.resolve('tool-calls'), usage: Promise.resolve({ inputTokens: 10, outputTokens: 5, totalTokens: 15 }), response: Promise.resolve({ @@ -6473,7 +6533,7 @@ describe('AgentRuntime — toModelOutput error resilience', () => { // First stream: agent calls the tool and suspends streamText.mockReturnValueOnce({ - fullStream: makeChunkStream([{ type: 'text-delta', textDelta: 'thinking...' }]), + stream: makeChunkStream([{ type: 'text-delta', id: 'text-1', text: 'thinking...' }]), finishReason: Promise.resolve('tool-calls'), usage: Promise.resolve({ inputTokens: 10, outputTokens: 5, totalTokens: 15 }), response: Promise.resolve({ @@ -6544,7 +6604,7 @@ describe('AgentRuntime — empty model responses', () => { function makeEmptyStream(finishReason: string, extraChunks: Array> = []) { return { - fullStream: makeChunkStream(extraChunks), + stream: makeChunkStream(extraChunks), finishReason: Promise.resolve(finishReason), usage: Promise.resolve({ inputTokens: 10, outputTokens: 0, totalTokens: 10 }), response: Promise.resolve({ messages: [] }), @@ -6605,7 +6665,7 @@ describe('AgentRuntime — empty model responses', () => { await collectChunks(readableStream); const args = streamText.mock.calls[0][0] as Record; - expect(args.includeRawChunks).toBe(true); + expect(args).toHaveProperty('include.rawChunks', true); }); it('stream: an empty response finishing with "stop" is not treated as an error', async () => { @@ -6704,7 +6764,7 @@ describe('AgentRuntime — MCP connection failure warnings', () => { await collectChunks(readableStream); const callArgs = streamText.mock.calls.at(-1)![0] as Record; - const system = callArgs.system; + const system = callArgs.instructions; const systemText = Array.isArray(system) ? system.map((e) => String((e as { content: string }).content)).join('') : String((system as { content: string }).content); @@ -6723,7 +6783,7 @@ describe('AgentRuntime — MCP connection failure warnings', () => { await collectChunks(readableStream); const callArgs = streamText.mock.calls.at(-1)![0] as Record; - const system = callArgs.system; + const system = callArgs.instructions; const systemText = Array.isArray(system) ? system.map((e) => String((e as { content: string }).content)).join('') : String((system as { content: string }).content); diff --git a/packages/@n8n/agents/src/runtime/__tests__/crash-resume.test.ts b/packages/@n8n/agents/src/runtime/__tests__/crash-resume.test.ts index 982012bdd94..d934a3c9e7e 100644 --- a/packages/@n8n/agents/src/runtime/__tests__/crash-resume.test.ts +++ b/packages/@n8n/agents/src/runtime/__tests__/crash-resume.test.ts @@ -14,6 +14,8 @@ import { z } from 'zod'; import type { CheckpointStore, SerializableAgentState } from '../../types'; import type { StreamChunk } from '../../types/sdk/agent'; +import type { BuiltFileStore } from '../../types/sdk/file-store'; +import type { AgentDbMessage } from '../../types/sdk/message'; import type { BuiltTool, InterruptibleToolContext } from '../../types/sdk/tool'; import { AgentRuntime } from '../loop/agent-runtime'; import { AgentEventBus } from '../state/event-bus'; @@ -65,7 +67,7 @@ function* makeChunkStream( function makeStreamSuccess(text = 'Hello') { return { - fullStream: makeChunkStream([{ type: 'text-delta', textDelta: text }]), + stream: makeChunkStream([{ type: 'text-delta', id: 'text-1', text }]), finishReason: Promise.resolve('stop'), usage: Promise.resolve({ inputTokens: 10, outputTokens: 5, totalTokens: 15 }), response: Promise.resolve({ @@ -77,7 +79,7 @@ function makeStreamSuccess(text = 'Hello') { function makeStreamWithToolCall(toolCallId: string, args: Record) { return { - fullStream: makeChunkStream([{ type: 'text-delta', textDelta: 'working...' }]), + stream: makeChunkStream([{ type: 'text-delta', id: 'text-1', text: 'working...' }]), finishReason: Promise.resolve('tool-calls'), usage: Promise.resolve({ inputTokens: 10, outputTokens: 5, totalTokens: 15 }), response: Promise.resolve({ @@ -251,7 +253,7 @@ describe('step checkpoints + crash resume (durable-log RFC)', () => { const runtime2 = createRuntime(store, [lookupTool, approveTool]); streamText.mockReset(); streamText.mockReturnValueOnce({ - fullStream: makeChunkStream([{ type: 'text-delta', textDelta: 'asking...' }]), + stream: makeChunkStream([{ type: 'text-delta', id: 'text-1', text: 'asking...' }]), finishReason: Promise.resolve('tool-calls'), usage: Promise.resolve({ inputTokens: 10, outputTokens: 5, totalTokens: 15 }), response: Promise.resolve({ @@ -330,6 +332,133 @@ describe('step checkpoints + crash resume (durable-log RFC)', () => { ); }); + describe('file-part rehydration on resume', () => { + const fileBytes = new Uint8Array([1, 2, 3]); + + /** A user message whose file part is reference-only, as checkpoints persist it. */ + function referenceOnlyUserMessage(): AgentDbMessage { + return { + id: 'm-user', + createdAt: new Date('2026-01-01T00:00:00Z'), + role: 'user', + content: [ + { type: 'text', text: 'look at this' }, + { + type: 'file', + mediaType: 'image/png', + fileRef: { id: 'att-1', fileName: 'photo.png', sizeBytes: 3 }, + }, + ], + }; + } + + function createFileStoreRuntime(store: CheckpointStore, fileStore: BuiltFileStore) { + return new AgentRuntime({ + name: 'crash-test', + model: 'openai/gpt-4o-mini', + instructions: 'You are a test assistant.', + tools: [lookupTool, approveTool], + eventBus: new AgentEventBus(), + checkpointStorage: store, + fileStore, + }); + } + + /** The hydrated file part as the AI SDK receives it from the resumed model call. */ + function findFilePart(callArgs: unknown) { + const { messages } = callArgs as { + messages: Array<{ role: string; content: unknown }>; + }; + return messages + .flatMap((m) => (Array.isArray(m.content) ? (m.content as Array<{ type: string }>) : [])) + .find((part) => part.type === 'file') as { type: string; data?: Uint8Array } | undefined; + } + + it('crashResume() rehydrates reference-only file parts before the model call', async () => { + const store = new RecordingCheckpointStore(); + store.map.set('run_files', { + status: 'running', + messageList: { + messages: [referenceOnlyUserMessage()], + historyIds: [], + inputIds: ['m-user'], + responseIds: [], + }, + pendingToolCalls: {}, + persistence: { threadId: 'thread-files', resourceId: 'user-1' }, + }); + const fileStore: BuiltFileStore = { load: vi.fn().mockResolvedValue(fileBytes) }; + const runtime = createFileStoreRuntime(store, fileStore); + streamText.mockReset(); + streamText.mockReturnValueOnce(makeStreamSuccess('resumed')); + + const result = await runtime.crashResume({ runId: 'run_files' }); + await collectChunks(result.stream); + + expect(fileStore.load).toHaveBeenCalledWith(expect.objectContaining({ id: 'att-1' }), { + threadId: 'thread-files', + }); + expect(findFilePart(streamText.mock.calls[0][0])?.data).toBe(fileBytes); + }); + + it('resume() rehydrates reference-only file parts before the model call', async () => { + const store = new RecordingCheckpointStore(); + store.map.set('run_files', { + status: 'suspended', + messageList: { + messages: [ + referenceOnlyUserMessage(), + { + id: 'm-assistant', + createdAt: new Date('2026-01-01T00:00:01Z'), + role: 'assistant', + content: [ + { + type: 'tool-call', + toolCallId: 'tc-hitl', + toolName: 'approve', + input: { question: 'ok?' }, + state: 'pending', + }, + ], + }, + ], + historyIds: [], + inputIds: ['m-user'], + responseIds: ['m-assistant'], + }, + pendingToolCalls: { + 'tc-hitl': { + suspended: true, + toolCallId: 'tc-hitl', + toolName: 'approve', + input: { question: 'ok?' }, + suspendPayload: { question: 'approve?' }, + resumeSchema: {}, + runId: 'run_files', + }, + }, + persistence: { threadId: 'thread-files', resourceId: 'user-1' }, + }); + const fileStore: BuiltFileStore = { load: vi.fn().mockResolvedValue(fileBytes) }; + const runtime = createFileStoreRuntime(store, fileStore); + streamText.mockReset(); + streamText.mockReturnValueOnce(makeStreamSuccess('approved')); + + const result = await runtime.resume( + 'stream', + { approved: true }, + { runId: 'run_files', toolCallId: 'tc-hitl' }, + ); + await collectChunks((result as { stream: ReadableStream }).stream); + + expect(fileStore.load).toHaveBeenCalledWith(expect.objectContaining({ id: 'att-1' }), { + threadId: 'thread-files', + }); + expect(findFilePart(streamText.mock.calls[0][0])?.data).toBe(fileBytes); + }); + }); + it('surfaces an error when maxIterations is decreased on crashResume (parity with resume)', async () => { const store = new RecordingCheckpointStore(); store.map.set('run_max', { diff --git a/packages/@n8n/agents/src/runtime/__tests__/delegate-sub-agent-tool.test.ts b/packages/@n8n/agents/src/runtime/__tests__/delegate-sub-agent-tool.test.ts index abb1b94c1fc..1b1e4d6717c 100644 --- a/packages/@n8n/agents/src/runtime/__tests__/delegate-sub-agent-tool.test.ts +++ b/packages/@n8n/agents/src/runtime/__tests__/delegate-sub-agent-tool.test.ts @@ -527,6 +527,94 @@ describe('createDelegateSubAgentTool', () => { }); }); + it('forwards only allowlisted child chunks, keeping args, results and nesting out', async () => { + const events: AgentEventData[] = []; + const tool = createDelegateSubAgentTool({ + runSubAgent: async (_request, helpers) => { + helpers.emitChunk({ type: 'text-delta', id: 't-1', delta: 'hello' }); + helpers.emitChunk({ + type: 'tool-call', + toolCallId: 'child-tc-1', + toolName: 'web_search', + input: { query: 'x'.repeat(5_000) }, + }); + helpers.emitChunk({ + type: 'tool-result', + toolCallId: 'child-tc-1', + toolName: 'web_search', + output: { body: 'y'.repeat(5_000) }, + }); + helpers.emitChunk({ + type: 'subagent-started', + taskName: 'nested', + taskPath: '/root/research_api_0/nested_0', + startedAt: 1, + }); + return await Promise.resolve({ + status: 'completed', + taskPath: '/root/research_api_0', + answer: 'done', + }); + }, + }); + + await tool.handler?.(input, { + runId: 'parent-run-1', + toolCallId: 'tool-call-1', + emitEvent: (event) => events.push(event), + }); + + const forwarded = events.filter((event) => event.type === AgentEvent.SubAgentChunk); + expect(forwarded).toHaveLength(1); + expect(forwarded[0]).toMatchObject({ + parentToolCallId: 'tool-call-1', + chunk: { type: 'text-delta', delta: 'hello' }, + }); + }); + + it('caps forwarded child text at the budget but keeps tool lifecycle flowing', async () => { + const events: AgentEventData[] = []; + const tool = createDelegateSubAgentTool({ + runSubAgent: async (_request, helpers) => { + // Neither delta lands on the boundary, so the second must be trimmed + // rather than forwarded whole. + helpers.emitChunk({ type: 'text-delta', id: 't-1', delta: 'a'.repeat(15_000) }); + helpers.emitChunk({ type: 'text-delta', id: 't-1', delta: 'b'.repeat(15_000) }); + helpers.emitChunk({ type: 'reasoning-delta', id: 'r-1', delta: 'over budget' }); + helpers.emitChunk({ + type: 'tool-execution-end', + toolCallId: 'child-tc-1', + toolName: 'web_search', + isError: false, + endTime: 1, + }); + return await Promise.resolve({ + status: 'completed', + taskPath: '/root/research_api_0', + answer: 'done', + }); + }, + }); + + await tool.handler?.(input, { + runId: 'parent-run-1', + toolCallId: 'tool-call-1', + emitEvent: (event) => events.push(event), + }); + + const forwarded = events.filter((event) => event.type === AgentEvent.SubAgentChunk); + expect(forwarded.map((event) => event.chunk.type)).toEqual([ + 'text-delta', + 'text-delta', + 'tool-execution-end', + ]); + const forwardedChars = forwarded.reduce( + (total, event) => total + ('delta' in event.chunk ? event.chunk.delta.length : 0), + 0, + ); + expect(forwardedChars).toBe(20_000); + }); + it('defaults maxChildren to 10 when policy is omitted', () => { const tool = createDelegateSubAgentTool({ runSubAgent: async () => @@ -645,6 +733,47 @@ describe('createDelegateSubAgentTool', () => { }); }); + it('rethrows an abort instead of reporting the delegation as failed', async () => { + const events: AgentEventData[] = []; + const abortError = new Error('This operation was aborted'); + abortError.name = 'AbortError'; + const controller = new AbortController(); + controller.abort(); + const tool = createDelegateSubAgentTool({ + runSubAgent: async () => await Promise.reject(abortError), + }); + + await expect( + tool.handler?.(input, { + runId: 'parent-run-1', + abortSignal: controller.signal, + emitEvent: (event) => events.push(event), + }), + ).rejects.toBe(abortError); + expect(events[events.length - 1]).toMatchObject({ + type: AgentEvent.SubAgentCompleted, + status: 'cancelled', + }); + }); + + it('reports an abort-shaped child error as failed while the parent signal is live', async () => { + const abortError = new Error('This operation was aborted'); + abortError.name = 'AbortError'; + const tool = createDelegateSubAgentTool({ + runSubAgent: async () => await Promise.reject(abortError), + }); + + await expect( + tool.handler?.(input, { + runId: 'parent-run-1', + abortSignal: new AbortController().signal, + }), + ).resolves.toMatchObject({ + status: 'failed', + error: 'This operation was aborted', + }); + }); + it('returns a failed output for invalid task names', async () => { const runSubAgent = vi.fn(); const tool = createDelegateSubAgentTool({ runSubAgent }); @@ -755,6 +884,24 @@ describe('generateResultToDelegateSubAgentOutput', () => { }); }); + it('maps a cancelled child run to cancelled, even though it also reports an error', () => { + const result: GenerateResult = { + runId: 'child-run-5', + messages: [], + finishReason: 'error', + error: new Error('Aborted'), + getState: () => ({ + status: 'cancelled', + messageList: { messages: [], historyIds: [], inputIds: [], responseIds: [] }, + pendingToolCalls: {}, + }), + }; + + expect(generateResultToDelegateSubAgentOutput('/root/x_0', result)).toMatchObject({ + status: 'cancelled', + }); + }); + it('returns a failed delegate output for delegated child suspension stopgap', async () => { const { failedDelegatedChildSuspendOutput } = await import( '../tools/delegate-sub-agent-tool.js' @@ -825,9 +972,11 @@ describe('generateResultToDelegateSubAgentOutput', () => { suspendPayload: {}, }, ], - getState: () => { - throw new Error('getState is not implemented'); - }, + getState: () => ({ + status: 'failed', + messageList: { messages: [], historyIds: [], inputIds: [], responseIds: [] }, + pendingToolCalls: {}, + }), }; expect(generateResultToDelegateSubAgentOutput('/root/x_0', result)).toMatchObject({ diff --git a/packages/@n8n/agents/src/runtime/__tests__/file-parts.test.ts b/packages/@n8n/agents/src/runtime/__tests__/file-parts.test.ts new file mode 100644 index 00000000000..334c123b73c --- /dev/null +++ b/packages/@n8n/agents/src/runtime/__tests__/file-parts.test.ts @@ -0,0 +1,200 @@ +/** + * File-reference content parts: hydration, persistence stripping, and the + * text-metadata degrade for parts the model cannot receive as bytes. + */ +import { describe, it, expect, vi } from 'vitest'; + +import type { BuiltFileStore } from '../../types/sdk/file-store'; +import type { AgentDbMessage, ContentFile, Message } from '../../types/sdk/message'; +import { stripHydratedFileData } from '../../types/sdk/message'; +import { + hydrateFileParts, + MAX_HYDRATED_FILE_BYTES, + MAX_HYDRATED_FILE_PARTS, +} from '../loop/hydrate-file-parts'; +import { toAiMessages } from '../model/messages'; + +const bytes = new Uint8Array([1, 2, 3]); + +function refFileBlock(overrides: Partial = {}): ContentFile { + return { + type: 'file', + mediaType: 'image/png', + fileRef: { id: 'att-1', fileName: 'photo.png', sizeBytes: 1_258_291 }, + ...overrides, + }; +} + +function userMessage(content: Message['content']): AgentDbMessage { + return { id: 'm-1', createdAt: new Date(), role: 'user', content }; +} + +describe('hydrateFileParts', () => { + it('loads bytes into reference-only file blocks', async () => { + const block = refFileBlock(); + const store: BuiltFileStore = { load: vi.fn().mockResolvedValue(bytes) }; + + await hydrateFileParts([userMessage([block])], store); + + expect(store.load).toHaveBeenCalledWith(block.fileRef, undefined); + expect(block.data).toBe(bytes); + }); + + it('leaves blocks reference-only when the media type is unsupported', async () => { + const block = refFileBlock({ mediaType: 'audio/ogg' }); + const store: BuiltFileStore = { + load: vi.fn().mockResolvedValue(bytes), + isMediaTypeSupported: (mediaType) => mediaType?.startsWith('image/') === true, + }; + + await hydrateFileParts([userMessage([block])], store); + + expect(store.load).not.toHaveBeenCalled(); + expect(block.data).toBeUndefined(); + }); + + it('leaves blocks reference-only when the store cannot resolve or throws', async () => { + const missing = refFileBlock(); + const failing = refFileBlock({ fileRef: { id: 'att-2' } }); + const store: BuiltFileStore = { + load: vi.fn().mockResolvedValueOnce(null).mockRejectedValueOnce(new Error('storage down')), + }; + + await hydrateFileParts([userMessage([missing]), userMessage([failing])], store); + + expect(missing.data).toBeUndefined(); + expect(failing.data).toBeUndefined(); + }); + + it('skips blocks that already have data and messages without content', async () => { + const hydrated = refFileBlock({ data: bytes }); + const custom: AgentDbMessage = { + id: 'm-2', + createdAt: new Date(), + type: 'custom', + data: { dummy: 'x' }, + }; + const store: BuiltFileStore = { load: vi.fn() }; + + await hydrateFileParts([userMessage([hydrated]), custom], store); + + expect(store.load).not.toHaveBeenCalled(); + }); + + it('is a no-op without a file store', async () => { + const block = refFileBlock(); + await hydrateFileParts([userMessage([block])], undefined); + expect(block.data).toBeUndefined(); + }); + + it('passes the run scope through to the store', async () => { + const block = refFileBlock(); + const store: BuiltFileStore = { load: vi.fn().mockResolvedValue(bytes) }; + + await hydrateFileParts([userMessage([block])], store, { threadId: 'thread-1' }); + + expect(store.load).toHaveBeenCalledWith(block.fileRef, { threadId: 'thread-1' }); + }); + + it('hydrates only the newest parts up to the count cap', async () => { + const blocks = Array.from({ length: MAX_HYDRATED_FILE_PARTS + 2 }, (_, index) => + refFileBlock({ fileRef: { id: `att-${index}`, sizeBytes: 1 } }), + ); + const store: BuiltFileStore = { load: vi.fn().mockResolvedValue(bytes) }; + + await hydrateFileParts([userMessage(blocks)], store); + + expect(store.load).toHaveBeenCalledTimes(MAX_HYDRATED_FILE_PARTS); + expect(blocks[0].data).toBeUndefined(); + expect(blocks[1].data).toBeUndefined(); + expect(blocks.at(-1)!.data).toBe(bytes); + }); + + it('spends the byte budget newest-first and skips parts that would exceed it', async () => { + const oldSmall = refFileBlock({ fileRef: { id: 'att-old', sizeBytes: 3 } }); + const hugeDeclared = refFileBlock({ + fileRef: { id: 'att-huge', sizeBytes: MAX_HYDRATED_FILE_BYTES + 1 }, + }); + const newest = refFileBlock({ fileRef: { id: 'att-new', sizeBytes: 3 } }); + const store: BuiltFileStore = { load: vi.fn().mockResolvedValue(bytes) }; + + await hydrateFileParts([userMessage([oldSmall, hugeDeclared, newest])], store); + + // The over-budget declaration is never loaded; the others hydrate. + expect(store.load).toHaveBeenCalledTimes(2); + expect(hugeDeclared.data).toBeUndefined(); + expect(newest.data).toBe(bytes); + expect(oldSmall.data).toBe(bytes); + }); + + it('drops a loaded part whose actual size exceeds the remaining budget', async () => { + const block = refFileBlock({ fileRef: { id: 'att-1' } }); // no declared size + const oversized = new Uint8Array(MAX_HYDRATED_FILE_BYTES + 1); + const store: BuiltFileStore = { load: vi.fn().mockResolvedValue(oversized) }; + + await hydrateFileParts([userMessage([block])], store); + + expect(block.data).toBeUndefined(); + }); +}); + +describe('stripHydratedFileData', () => { + it('removes data from file blocks that carry a fileRef, without mutating the original', () => { + const block = refFileBlock({ data: bytes }); + const message = userMessage([{ type: 'text', text: 'look' }, block]); + + const stripped = stripHydratedFileData(message); + + const strippedFile = (stripped as Message).content[1] as ContentFile; + expect(strippedFile.data).toBeUndefined(); + expect(strippedFile.fileRef).toEqual(block.fileRef); + // Original block untouched — the live message list keeps its bytes. + expect(block.data).toBe(bytes); + }); + + it('keeps inline data on file blocks without a fileRef', () => { + const inline: ContentFile = { type: 'file', mediaType: 'image/png', data: bytes }; + const message = userMessage([inline]); + + const stripped = stripHydratedFileData(message); + + expect(stripped).toBe(message); + expect((stripped as Message).content[0]).toBe(inline); + }); + + it('passes through custom messages', () => { + const custom: AgentDbMessage = { + id: 'm-3', + createdAt: new Date(), + type: 'custom', + data: { dummy: 'x' }, + }; + expect(stripHydratedFileData(custom)).toBe(custom); + }); +}); + +describe('toAiMessages — file parts', () => { + it('emits hydrated file blocks as AI SDK file parts', () => { + const input: Message[] = [{ role: 'user', content: [refFileBlock({ data: bytes })] }]; + + const [message] = toAiMessages(input); + + expect(message.content).toEqual([{ type: 'file', data: bytes, mediaType: 'image/png' }]); + }); + + it('degrades reference-only file blocks to a text metadata part', () => { + const input: Message[] = [ + { role: 'user', content: [{ type: 'text', text: 'listen to this' }, refFileBlock()] }, + ]; + + const [message] = toAiMessages(input); + const parts = message.content as Array<{ type: string; text?: string }>; + + expect(parts).toHaveLength(2); + expect(parts[1].type).toBe('text'); + expect(parts[1].text).toContain('photo.png'); + expect(parts[1].text).toContain('image/png'); + expect(parts[1].text).toContain('1.2 MB'); + expect(parts[1].text).toContain('fileId: att-1'); + }); +}); diff --git a/packages/@n8n/agents/src/runtime/__tests__/model-factory.test.ts b/packages/@n8n/agents/src/runtime/__tests__/model-factory.test.ts index c0f3d97b476..909d077de39 100644 --- a/packages/@n8n/agents/src/runtime/__tests__/model-factory.test.ts +++ b/packages/@n8n/agents/src/runtime/__tests__/model-factory.test.ts @@ -58,7 +58,7 @@ vi.mock('@ai-sdk/openai', () => ({ })); vi.mock('@ai-sdk/google', () => ({ - createGoogleGenerativeAI: (opts?: ProviderOpts) => (model: string) => ({ + createGoogle: (opts?: ProviderOpts) => (model: string) => ({ provider: 'google', modelId: model, apiKey: opts?.apiKey, @@ -68,12 +68,14 @@ vi.mock('@ai-sdk/google', () => ({ })); vi.mock('@ai-sdk/xai', () => ({ - createXai: (opts?: ProviderOpts) => (model: string) => ({ - provider: 'xai', - modelId: model, - apiKey: opts?.apiKey, - fetch: opts?.fetch, - specificationVersion: 'v3', + createXai: (opts?: ProviderOpts) => ({ + chat: (model: string) => ({ + provider: 'xai', + modelId: model, + apiKey: opts?.apiKey, + fetch: opts?.fetch, + specificationVersion: 'v3', + }), }), })); diff --git a/packages/@n8n/agents/src/runtime/__tests__/observation-log-observer.test.ts b/packages/@n8n/agents/src/runtime/__tests__/observation-log-observer.test.ts index 860ac34cc1b..9913d6a1489 100644 --- a/packages/@n8n/agents/src/runtime/__tests__/observation-log-observer.test.ts +++ b/packages/@n8n/agents/src/runtime/__tests__/observation-log-observer.test.ts @@ -149,16 +149,15 @@ describe('observation-log observer defaults', () => { await observe({ ...baseInput, telemetry: { ...telemetry, enabled: false } }); expect(mockGenerateText.mock.calls[0][0]).toMatchObject({ - experimental_telemetry: { + telemetry: { isEnabled: true, functionId: 'my-agent.memory-observer', - metadata: { thread_id: 't1' }, recordInputs: true, recordOutputs: false, }, }); - expect(mockGenerateText.mock.calls[1][0].experimental_telemetry).toBeUndefined(); - expect(mockGenerateText.mock.calls[2][0].experimental_telemetry).toBeUndefined(); + expect(mockGenerateText.mock.calls[1][0].telemetry).toBeUndefined(); + expect(mockGenerateText.mock.calls[2][0].telemetry).toBeUndefined(); }); it('reports normalized, cache-aware usage through an async onUsage before the observer promise settles', async () => { diff --git a/packages/@n8n/agents/src/runtime/__tests__/observation-log-reflector.test.ts b/packages/@n8n/agents/src/runtime/__tests__/observation-log-reflector.test.ts index bae296f6697..52f7dc93fdf 100644 --- a/packages/@n8n/agents/src/runtime/__tests__/observation-log-reflector.test.ts +++ b/packages/@n8n/agents/src/runtime/__tests__/observation-log-reflector.test.ts @@ -132,16 +132,15 @@ describe('observation-log reflector defaults', () => { await reflect({ ...baseInput, telemetry: { ...telemetry, enabled: false } }); expect(mockGenerateText.mock.calls[0][0]).toMatchObject({ - experimental_telemetry: { + telemetry: { isEnabled: true, functionId: 'my-agent.memory-reflector', - metadata: { thread_id: 't1' }, recordInputs: true, recordOutputs: false, }, }); - expect(mockGenerateText.mock.calls[1][0].experimental_telemetry).toBeUndefined(); - expect(mockGenerateText.mock.calls[2][0].experimental_telemetry).toBeUndefined(); + expect(mockGenerateText.mock.calls[1][0].telemetry).toBeUndefined(); + expect(mockGenerateText.mock.calls[2][0].telemetry).toBeUndefined(); }); it('reports usage with task="reflector" and the configured model through onUsage', async () => { diff --git a/packages/@n8n/agents/src/runtime/__tests__/provider-quirks.test.ts b/packages/@n8n/agents/src/runtime/__tests__/provider-quirks.test.ts index 8d9c265eecd..b73a1325287 100644 --- a/packages/@n8n/agents/src/runtime/__tests__/provider-quirks.test.ts +++ b/packages/@n8n/agents/src/runtime/__tests__/provider-quirks.test.ts @@ -63,7 +63,7 @@ describe('thinkingToProviderOptions', () => { it('openai: defaults reasoningEffort to medium', () => { expect(getProviderQuirks('openai').thinkingToProviderOptions?.({})).toEqual({ - openai: { reasoningEffort: 'medium' }, + openai: { reasoningEffort: 'medium', reasoningSummary: null }, }); }); diff --git a/packages/@n8n/agents/src/runtime/__tests__/title-generation.test.ts b/packages/@n8n/agents/src/runtime/__tests__/title-generation.test.ts index db644c93cbc..db358f03698 100644 --- a/packages/@n8n/agents/src/runtime/__tests__/title-generation.test.ts +++ b/packages/@n8n/agents/src/runtime/__tests__/title-generation.test.ts @@ -8,9 +8,9 @@ import { } from '../memory/title-generation'; type GenerateTextCall = { - system?: string; + instructions?: string; messages: Array<{ role: string; content: string }>; - experimental_telemetry?: Record; + telemetry?: Record; }; type GenerateTextResult = { text: string; usage?: { totalTokens?: number } }; @@ -117,8 +117,8 @@ describe('generateTitleFromMessage', () => { mockGenerateText.mockResolvedValue({ text: 'Berlin rain alert' }); await generateTitleFromMessage(fakeModel, 'Build a daily Berlin rain alert workflow'); const call = mockGenerateText.mock.calls[0][0]; - expect(call.system).toContain('markdown'); - expect(call.system).toContain('sentence case'); + expect(call.instructions).toContain('markdown'); + expect(call.instructions).toContain('sentence case'); }); it('accepts custom instructions', async () => { @@ -127,7 +127,7 @@ describe('generateTitleFromMessage', () => { instructions: 'Custom system prompt', }); const call = mockGenerateText.mock.calls[0][0]; - expect(call.system).toBe('Custom system prompt'); + expect(call.instructions).toBe('Custom system prompt'); }); it('passes generic telemetry to the title LLM call', async () => { @@ -147,13 +147,11 @@ describe('generateTitleFromMessage', () => { }); const call = mockGenerateText.mock.calls[0][0]; - expect(call.experimental_telemetry).toEqual({ + expect(call.telemetry).toEqual({ isEnabled: true, functionId: 'instance-ai.thread-title', - metadata: { thread_id: 'thread-1' }, recordInputs: true, recordOutputs: false, - tracer: undefined, integrations: undefined, }); }); diff --git a/packages/@n8n/agents/src/runtime/loop/agent-runtime.ts b/packages/@n8n/agents/src/runtime/loop/agent-runtime.ts index a718e0bf049..38b108d1e0b 100644 --- a/packages/@n8n/agents/src/runtime/loop/agent-runtime.ts +++ b/packages/@n8n/agents/src/runtime/loop/agent-runtime.ts @@ -1,10 +1,11 @@ import type { ProviderOptions } from '@ai-sdk/provider-utils'; -import type { TelemetrySettings, ToolCallRepairFunction, ToolSet } from 'ai'; +import type { TelemetryOptions, ToolCallRepairFunction, ToolSet } from 'ai'; import type { JSONSchema7 } from 'json-schema'; import type { z } from 'zod'; import { incrementMessageCount, incrementTokenCountFromUsage } from './execution-counter'; import { GenerateSink } from './generate-sink'; +import { hydrateFileParts } from './hydrate-file-parts'; import type { RunOutputSink, RunServices } from './run-output-sink'; import { RuntimeContextBuilder, getModelIdString } from './runtime-context'; import { @@ -18,6 +19,7 @@ import { StreamSink } from './stream-sink'; import { isCancellation } from '../../sdk/cancellation'; import { computeCost, getModelCost, type ModelCost } from '../../sdk/catalog'; import type { + BuiltFileStore, BuiltMemory, BuiltProviderTool, BuiltTelemetry, @@ -31,6 +33,7 @@ import type { ObservationLogMemoryConfig, PendingToolCall, RunOptions, + ReasoningLevel, SerializableAgentState, StreamChunk, StreamResult, @@ -90,12 +93,15 @@ export interface AgentRuntimeConfig { }; providerTools?: BuiltProviderTool[]; memory?: BuiltMemory; + /** Host store resolving file-reference content parts to bytes before LLM calls. */ + fileStore?: BuiltFileStore; observationLog?: ObservationLogMemoryConfig; observationalMemory?: ObservationalMemoryConfig; episodicMemory?: EpisodicMemoryConfig; structuredOutput?: z.ZodType | JSONSchema7; checkpointStorage?: 'memory' | CheckpointStore; thinking?: ThinkingConfig; + reasoning?: ReasoningLevel; promptCaching?: PromptCachingConfig; eventBus?: AgentEventBus; /** Number of tool calls to execute concurrently. Default `1` (sequential). */ @@ -346,6 +352,9 @@ export class AgentRuntime { const list = AgentMessageList.deserialize(state.messageList); this.context.hydrateDeferredToolsFromList(list); + await hydrateFileParts(list.messages(), this.config.fileStore, { + threadId: state.persistence?.threadId, + }); const toolForValidation = this.context .getCurrentTools(state.persistence) @@ -500,6 +509,9 @@ export class AgentRuntime { const list = AgentMessageList.deserialize(state.messageList); this.context.hydrateDeferredToolsFromList(list); + await hydrateFileParts(list.messages(), this.config.fileStore, { + threadId: state.persistence?.threadId, + }); let abortScope: AgentAbortScope | undefined; try { @@ -578,6 +590,11 @@ export class AgentRuntime { // is authoritative for completed turns, so this must not abort the turn. await this.memory.persistInputMessages(list, options); + // Hydrate after the eager persist so stored input stays reference-only. + await hydrateFileParts(list.messages(), this.config.fileStore, { + threadId: options?.persistence?.threadId, + }); + return list; } @@ -618,16 +635,18 @@ export class AgentRuntime { toolMap: Map, options?: ExecutionOptions, ): { - experimental_telemetry?: TelemetrySettings; - experimental_repairToolCall?: ToolCallRepairFunction>; - experimental_onStepStart?: ExecutionOptions['onStepStart']; - onStepFinish?: ExecutionOptions['onStepFinish']; + telemetry?: TelemetryOptions; + repairToolCall?: ToolCallRepairFunction>; + onStepStart?: ExecutionOptions['onStepStart']; + onStepEnd?: ExecutionOptions['onStepEnd']; } { return { ...this.telemetry.buildTelemetryOptions(options), - ...(options?.onStepStart ? { experimental_onStepStart: options.onStepStart } : {}), - ...(options?.onStepFinish ? { onStepFinish: options.onStepFinish } : {}), - experimental_repairToolCall: async (options) => { + ...(options?.onStepStart ? { onStepStart: options.onStepStart } : {}), + ...(options?.onStepEnd || options?.onStepFinish + ? { onStepEnd: options.onStepEnd ?? options.onStepFinish } + : {}), + repairToolCall: async (options) => { return await fixToolCall( { toolCall: options.toolCall, @@ -804,6 +823,7 @@ export class AgentRuntime { abortSignal: abortScope.signal, hasTools, aiTools: cached.aiTools, + reasoning: staticLoopContext.reasoning, providerOptions: staticLoopContext.providerOptions, outputSpec: staticLoopContext.outputSpec, aiSdkOptions: this.buildAiSdkOptions(toolMap, options), diff --git a/packages/@n8n/agents/src/runtime/loop/generate-sink.ts b/packages/@n8n/agents/src/runtime/loop/generate-sink.ts index 6eafd67db5c..55e355536ab 100644 --- a/packages/@n8n/agents/src/runtime/loop/generate-sink.ts +++ b/packages/@n8n/agents/src/runtime/loop/generate-sink.ts @@ -31,9 +31,11 @@ export class GenerateSink implements RunOutputSink { const { generateText } = loadAi(); const result = await generateText({ model: ctx.model, - system: ctx.system, + instructions: ctx.system, messages: ctx.messages, + allowSystemInMessages: true, abortSignal: ctx.abortSignal, + ...(ctx.reasoning ? { reasoning: ctx.reasoning } : {}), ...(ctx.hasTools ? { tools: ctx.aiTools } : {}), ...(ctx.providerOptions ? { providerOptions: ctx.providerOptions } : {}), ...(ctx.outputSpec ? { output: ctx.outputSpec } : {}), diff --git a/packages/@n8n/agents/src/runtime/loop/hydrate-file-parts.ts b/packages/@n8n/agents/src/runtime/loop/hydrate-file-parts.ts new file mode 100644 index 00000000000..44ec4b152cd --- /dev/null +++ b/packages/@n8n/agents/src/runtime/loop/hydrate-file-parts.ts @@ -0,0 +1,58 @@ +import type { BuiltFileStore } from '../../types/sdk/file-store'; +import type { AgentDbMessage, ContentFile } from '../../types/sdk/message'; + +/** Only the newest file parts are hydrated; older ones stay reference-only. */ +export const MAX_HYDRATED_FILE_PARTS = 20; +/** Cumulative cap on hydrated bytes per turn. */ +export const MAX_HYDRATED_FILE_BYTES = 50 * 1024 * 1024; + +/** + * Fill `data` on reference-only file parts by loading bytes from the injected + * file store. Mutates the blocks in place (persistence backends strip hydrated + * bytes on save via `stripHydratedFileData`). + * + * The whole thread history passes through here on every turn, so hydration is + * bounded: only the newest {@link MAX_HYDRATED_FILE_PARTS} parts are + * considered, loads run sequentially newest-first, and loading stops charging + * against a {@link MAX_HYDRATED_FILE_BYTES} budget once exhausted. + * + * A block stays reference-only — and is later rendered to the model as text + * metadata by `toAiContent` — when any of these hold: no store is configured, + * the store reports the media type as unsupported for the current model, the + * reference is unknown, the load fails, or the count/byte budgets exclude it. + */ +export async function hydrateFileParts( + messages: readonly AgentDbMessage[], + fileStore: BuiltFileStore | undefined, + scope?: { threadId?: string }, +): Promise { + if (!fileStore) return; + + const blocks: ContentFile[] = []; + for (const message of messages) { + if (!('content' in message) || !Array.isArray(message.content)) continue; + for (const block of message.content) { + if (block.type !== 'file' || !block.fileRef || block.data !== undefined) continue; + if (fileStore.isMediaTypeSupported && !fileStore.isMediaTypeSupported(block.mediaType)) { + continue; + } + blocks.push(block); + } + } + + let budget = MAX_HYDRATED_FILE_BYTES; + const newestFirst = blocks.slice(-MAX_HYDRATED_FILE_PARTS).reverse(); + for (const block of newestFirst) { + if (budget <= 0) break; + const declaredSize = block.fileRef!.sizeBytes; + if (declaredSize !== undefined && declaredSize > budget) continue; + try { + const data = await fileStore.load(block.fileRef!, scope); + if (!data || data.byteLength > budget) continue; + block.data = data; + budget -= data.byteLength; + } catch { + // Leave the block reference-only; the model sees its text metadata. + } + } +} diff --git a/packages/@n8n/agents/src/runtime/loop/run-output-sink.ts b/packages/@n8n/agents/src/runtime/loop/run-output-sink.ts index 07c98403ee2..0ede1adef91 100644 --- a/packages/@n8n/agents/src/runtime/loop/run-output-sink.ts +++ b/packages/@n8n/agents/src/runtime/loop/run-output-sink.ts @@ -1,9 +1,10 @@ import type { LanguageModel, + LanguageModelCallOptions, ModelMessage, Output, SystemModelMessage, - TelemetrySettings, + TelemetryOptions, ToolCallRepairFunction, ToolSet, } from 'ai'; @@ -65,11 +66,12 @@ export interface ModelCallContext { abortSignal: AbortSignal; hasTools: boolean; aiTools: ToolSet; + reasoning?: LanguageModelCallOptions['reasoning']; providerOptions?: Record; outputSpec?: ReturnType; aiSdkOptions: { - experimental_telemetry?: TelemetrySettings; - experimental_repairToolCall?: ToolCallRepairFunction>; + telemetry?: TelemetryOptions; + repairToolCall?: ToolCallRepairFunction>; }; } diff --git a/packages/@n8n/agents/src/runtime/loop/runtime-context.ts b/packages/@n8n/agents/src/runtime/loop/runtime-context.ts index 6f942b043c1..c376eab51dc 100644 --- a/packages/@n8n/agents/src/runtime/loop/runtime-context.ts +++ b/packages/@n8n/agents/src/runtime/loop/runtime-context.ts @@ -49,13 +49,14 @@ export function getModelIdString(model: ModelConfig): string { export interface StaticLoopContext { model: LanguageModel; aiProviderTools: ReturnType; + reasoning: AgentRuntimeConfig['reasoning']; providerOptions?: Record; outputSpec?: ReturnType; } /** * Builds the per-run and per-iteration dependencies the agentic loop hands to - * the LLM call: the model instance, provider/thinking options, structured + * the LLM call: the model instance, reasoning, provider options, structured * output spec, and the effective tool surface (base + deferred + recall tools, * mapped to AI SDK shapes). Keeps tool/model assembly out of the loop body. */ @@ -97,6 +98,7 @@ export class RuntimeContextBuilder { return { model, aiProviderTools, + reasoning: this.config.reasoning, providerOptions: providerOptions as Record | undefined, outputSpec, }; diff --git a/packages/@n8n/agents/src/runtime/loop/stream-sink.ts b/packages/@n8n/agents/src/runtime/loop/stream-sink.ts index 7b2d02d00c0..88f44e058b9 100644 --- a/packages/@n8n/agents/src/runtime/loop/stream-sink.ts +++ b/packages/@n8n/agents/src/runtime/loop/stream-sink.ts @@ -113,13 +113,15 @@ export class StreamSink implements RunOutputSink { const { streamText } = loadAi(); const result = streamText({ model: ctx.model, - system: ctx.system, + instructions: ctx.system, messages: ctx.messages, + allowSystemInMessages: true, abortSignal: ctx.abortSignal, + ...(ctx.reasoning ? { reasoning: ctx.reasoning } : {}), // Surface the provider's raw message_start/message_delta events so an // aborted run can recover its usage — the SDK reports none on abort. ...(this.rawUsageReader !== undefined || this.rawErrorReader !== undefined - ? { includeRawChunks: true } + ? { include: { rawChunks: true } } : {}), ...(ctx.hasTools ? { tools: ctx.aiTools } : {}), ...(ctx.providerOptions ? { providerOptions: ctx.providerOptions } : {}), @@ -131,7 +133,7 @@ export class StreamSink implements RunOutputSink { // Consume the stream. When the AbortSignal fires mid-stream the AI SDK // cancels the underlying fetch and the async iterator throws; the error // propagates to the StreamSession which closes the consumer stream. - for await (const chunk of result.fullStream) { + for await (const chunk of result.stream) { // Track usage from raw provider events so an aborted turn (which never // reaches the post-loop awaits) can still be billed via getAbortFinish. if (chunk.type === 'raw') { diff --git a/packages/@n8n/agents/src/runtime/memory/episodic-memory-defaults.ts b/packages/@n8n/agents/src/runtime/memory/episodic-memory-defaults.ts index 3f6b8aea5e6..002e879c640 100644 --- a/packages/@n8n/agents/src/runtime/memory/episodic-memory-defaults.ts +++ b/packages/@n8n/agents/src/runtime/memory/episodic-memory-defaults.ts @@ -538,7 +538,7 @@ export function createEpisodicMemoryExtractFn( const { generateText, Output } = await import('ai'); const response = await generateText({ model: createModel(model), - system: options.extractionPrompt ?? DEFAULT_EPISODIC_MEMORY_EXTRACTION_PROMPT, + instructions: options.extractionPrompt ?? DEFAULT_EPISODIC_MEMORY_EXTRACTION_PROMPT, prompt: buildEpisodicMemoryExtractorPrompt(input), output: Output.object({ schema: EpisodicMemoryExtractionSchema }), }); @@ -564,7 +564,7 @@ export function createEpisodicMemoryReflectFn( const { generateText, Output } = await import('ai'); const response = await generateText({ model: createModel(model), - system: options.reflectionPrompt ?? DEFAULT_EPISODIC_MEMORY_REFLECTION_PROMPT, + instructions: options.reflectionPrompt ?? DEFAULT_EPISODIC_MEMORY_REFLECTION_PROMPT, prompt: buildEpisodicMemoryReflectorPrompt(input), output: Output.object({ schema: EpisodicMemoryReflectionSchema }), }); diff --git a/packages/@n8n/agents/src/runtime/memory/observation-log-defaults.ts b/packages/@n8n/agents/src/runtime/memory/observation-log-defaults.ts index 26b17d6fa35..9b1821e46be 100644 --- a/packages/@n8n/agents/src/runtime/memory/observation-log-defaults.ts +++ b/packages/@n8n/agents/src/runtime/memory/observation-log-defaults.ts @@ -13,7 +13,7 @@ import { getModelIdString } from '../loop/runtime-context'; import { loadAi } from '../model/lazy-ai'; import { createModel } from '../model/model-factory'; import { toTokenUsage } from '../streaming/stream'; -import { buildExperimentalTelemetry } from '../telemetry/telemetry-options'; +import { buildAiSdkTelemetry } from '../telemetry/telemetry-options'; // The observer's fixed prompt is a few thousand tokens, so firing per tiny delta // is majority overhead. 8k keeps that overhead ratio acceptable while still firing @@ -328,9 +328,9 @@ export function createObservationLogObserveFn( return async (input) => { const { text, usage, providerMetadata } = await loadAi().generateText({ model: createModel(model), - system: options.observerPrompt ?? DEFAULT_OBSERVATION_LOG_OBSERVER_PROMPT, + instructions: options.observerPrompt ?? DEFAULT_OBSERVATION_LOG_OBSERVER_PROMPT, prompt: buildObservationLogObserverPrompt(input), - ...buildExperimentalTelemetry(input.telemetry, { functionSuffix: 'memory-observer' }), + ...buildAiSdkTelemetry(input.telemetry, { functionSuffix: 'memory-observer' }), }); incrementTokenCountFromUsage(input.executionCounter, usage); @@ -582,9 +582,9 @@ export function createObservationLogReflectFn( return async (input) => { const { text, usage, providerMetadata } = await loadAi().generateText({ model: createModel(model), - system: options.reflectorPrompt ?? DEFAULT_OBSERVATION_LOG_REFLECTOR_PROMPT, + instructions: options.reflectorPrompt ?? DEFAULT_OBSERVATION_LOG_REFLECTOR_PROMPT, prompt: buildObservationLogReflectorPrompt(input), - ...buildExperimentalTelemetry(input.telemetry, { functionSuffix: 'memory-reflector' }), + ...buildAiSdkTelemetry(input.telemetry, { functionSuffix: 'memory-reflector' }), }); incrementTokenCountFromUsage(input.executionCounter, usage); diff --git a/packages/@n8n/agents/src/runtime/memory/title-generation.ts b/packages/@n8n/agents/src/runtime/memory/title-generation.ts index b7eeca97158..c6370f997c1 100644 --- a/packages/@n8n/agents/src/runtime/memory/title-generation.ts +++ b/packages/@n8n/agents/src/runtime/memory/title-generation.ts @@ -7,6 +7,7 @@ import { createFilteredLogger } from '../logger'; import { incrementTokenCountFromUsage } from '../loop/execution-counter'; import { loadAi } from '../model/lazy-ai'; import { createModel, type FetchFn } from '../model/model-factory'; +import { buildAiSdkTelemetry } from '../telemetry/telemetry-options'; const logger = createFilteredLogger(); @@ -55,22 +56,6 @@ interface GenerateTitleFromMessageOptions { executionCounter?: AgentExecutionCounter; } -function buildTelemetryOptions(telemetry: BuiltTelemetry | undefined): Record { - if (!telemetry?.enabled) return {}; - - return { - experimental_telemetry: { - isEnabled: true, - functionId: telemetry.functionId ?? 'title-generation', - metadata: telemetry.metadata, - recordInputs: telemetry.recordInputs, - recordOutputs: telemetry.recordOutputs, - tracer: telemetry.tracer, - integrations: telemetry.integrations.length > 0 ? telemetry.integrations : undefined, - }, - }; -} - /** * Whether a user message has too little substance to title a conversation * (e.g. "hey", "hello"). For these, the LLM tends to hallucinate an @@ -134,14 +119,14 @@ export async function generateTitleFromMessage( const result = await loadAi().generateText({ model, - system: opts?.instructions ?? DEFAULT_TITLE_INSTRUCTIONS, + instructions: opts?.instructions ?? DEFAULT_TITLE_INSTRUCTIONS, messages: [ { role: 'user', content: `Generate a title for the following first message of a conversation. Do not answer the message — only produce the title.\n\n\n${trimmed}\n`, }, ], - ...buildTelemetryOptions(opts?.telemetry), + ...buildAiSdkTelemetry(opts?.telemetry, { fallbackFunctionId: 'title-generation' }), }); incrementTokenCountFromUsage(opts?.executionCounter, result.usage); @@ -181,7 +166,7 @@ ${trimmed} const result = await loadAi().generateText({ model, - system: opts?.instructions ?? DEFAULT_TITLE_AND_EMOJI_INSTRUCTIONS, + instructions: opts?.instructions ?? DEFAULT_TITLE_AND_EMOJI_INSTRUCTIONS, messages: [{ role: 'user', content: wrappedMessage }], }); incrementTokenCountFromUsage(opts?.executionCounter, result.usage); diff --git a/packages/@n8n/agents/src/runtime/model/__tests__/prompt-cache.test.ts b/packages/@n8n/agents/src/runtime/model/__tests__/prompt-cache.test.ts index ebf06b19b43..2a4afe50a2c 100644 --- a/packages/@n8n/agents/src/runtime/model/__tests__/prompt-cache.test.ts +++ b/packages/@n8n/agents/src/runtime/model/__tests__/prompt-cache.test.ts @@ -184,9 +184,12 @@ describe('mergeProviderOptions', () => { it('preserves unrelated providers from different arguments', () => { expect( - mergeProviderOptions({ anthropic: { thinking: { type: 'enabled' } } }, { openai: { a: 1 } }), + mergeProviderOptions( + { anthropic: { cacheControl: { type: 'ephemeral' } } }, + { openai: { a: 1 } }, + ), ).toEqual({ - anthropic: { thinking: { type: 'enabled' } }, + anthropic: { cacheControl: { type: 'ephemeral' } }, openai: { a: 1 }, }); }); diff --git a/packages/@n8n/agents/src/runtime/model/message-list.ts b/packages/@n8n/agents/src/runtime/model/message-list.ts index 77671fae679..9b341d324bc 100644 --- a/packages/@n8n/agents/src/runtime/model/message-list.ts +++ b/packages/@n8n/agents/src/runtime/model/message-list.ts @@ -325,6 +325,11 @@ export class AgentMessageList { return this.all.filter((m) => this.inputSet.has(m)); } + /** All messages currently in the list, as live references. */ + messages(): readonly AgentDbMessage[] { + return this.all; + } + serialize(): SerializedMessageList { const toIds = (set: Set) => Array.from(set).map((m) => m.id); return { diff --git a/packages/@n8n/agents/src/runtime/model/messages.ts b/packages/@n8n/agents/src/runtime/model/messages.ts index b059502ae1a..df56ba9da66 100644 --- a/packages/@n8n/agents/src/runtime/model/messages.ts +++ b/packages/@n8n/agents/src/runtime/model/messages.ts @@ -6,9 +6,6 @@ import type { TextPart, ToolCallPart, ToolResultPart, - ImagePart, - ToolApprovalRequest, - ToolApprovalResponse, FinishReason as AiFinishReason, } from 'ai'; @@ -16,8 +13,10 @@ import { getProviderQuirks, PROVIDER_QUIRKS } from './provider-quirks'; import type { FinishReason } from '../../types'; import type { AgentMessage, + ContentCustom, ContentFile, ContentReasoning, + ContentReasoningFile, ContentText, ContentToolCall, Message, @@ -25,18 +24,9 @@ import type { } from '../../types/sdk/message'; import type { JSONObject, JSONValue } from '../../types/utils/json'; -/** Reasoning content part — mirrors @ai-sdk/provider-utils ReasoningPart (not re-exported by 'ai'). */ -type ReasoningPart = { type: 'reasoning'; text: string }; - -type AiContentPart = - | TextPart - | FilePart - | ImagePart - | ReasoningPart - | ToolCallPart - | ToolResultPart - | ToolApprovalRequest - | ToolApprovalResponse; +// Used across all message roles; AssistantContent omits user images and tool approval responses. +type AiContentPart = Exclude[number]; +type AiAssistantContent = Exclude['content'], string>; // --- Type guards for MessageContent blocks --- @@ -48,10 +38,18 @@ function isReasoning(block: MessageContent): block is ContentReasoning { return block.type === 'reasoning'; } +function isReasoningFile(block: MessageContent): block is ContentReasoningFile { + return block.type === 'reasoning-file'; +} + function isFile(block: MessageContent): block is ContentFile { return block.type === 'file'; } +function isCustom(block: MessageContent): block is ContentCustom { + return block.type === 'custom'; +} + function isToolCall(block: MessageContent): block is ContentToolCall { return block.type === 'tool-call'; } @@ -125,10 +123,19 @@ function hasReplayableReasoningProviderOptions( type ContentToolResultOutput = Extract; -function isContentToolResultOutput(value: JSONValue): value is ContentToolResultOutput { +function isContentToolResultOutput(value: unknown): value is ContentToolResultOutput { return isRecord(value) && value.type === 'content' && Array.isArray(value.value); } +function normalizeReasoningFileData( + data: Extract['data'], +): ContentReasoningFile['data'] { + if (data instanceof URL) return data.toString(); + if (typeof data !== 'object' || data === null || !('type' in data)) return data; + if (data.type === 'data') return data.data; + return data.url.toString(); +} + /** * Providers replay reasoning from `providerOptions`, but the AI SDK exposes the * replay data in `providerMetadata` (see `PROVIDER_QUIRKS[provider].reasoningReplayKeys`). @@ -157,17 +164,57 @@ function toReasoningProviderOptions(block: ContentReasoning): ProviderOptions | return { ...block.providerOptions, ...additions }; } +function formatFileSize(bytes: number): string { + if (bytes < 1024) return `${bytes} B`; + if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`; + return `${(bytes / (1024 * 1024)).toFixed(1)} MB`; +} + +/** + * Text stand-in for a file part the model cannot receive as bytes (media type + * unsupported, or hydration skipped/failed). The fileId is included so the + * agent can hand the file to a tool that can process it. + */ +export function fileMetadataText(block: ContentFile): string { + const name = block.fileRef?.fileName ?? 'file'; + const details = [ + block.mediaType, + block.fileRef?.sizeBytes !== undefined ? formatFileSize(block.fileRef.sizeBytes) : undefined, + ] + .filter((part): part is string => part !== undefined) + .join(', '); + const id = block.fileRef?.id; + return `[File attachment "${name}"${details ? ` (${details})` : ''}${ + id ? `, fileId: ${id}` : '' + } — not directly viewable by this model]`; +} + /** Convert a single n8n MessageContent block to an AI SDK content part. */ function toAiContent(block: MessageContent): AiContentPart | undefined { let base: AiContentPart | undefined; if (isText(block)) { base = { type: 'text', text: block.text }; } else if (isFile(block)) { - base = { - type: 'file', - data: block.data, - mediaType: block.mediaType ?? 'application/octet-stream', - }; + base = + block.data === undefined + ? { type: 'text', text: fileMetadataText(block) } + : { + type: 'file', + data: block.data, + mediaType: block.mediaType ?? 'application/octet-stream', + }; + } else if (isReasoningFile(block)) { + // Reasoning files are model-produced and carry no fileRef, so their data + // is never stripped for storage; a data-less block has nothing to send. + if (block.data !== undefined) { + base = { + type: 'reasoning-file', + data: block.data, + mediaType: block.mediaType, + }; + } + } else if (isCustom(block)) { + base = { type: 'custom', kind: block.kind }; } else if (isToolCall(block)) { base = { type: 'tool-call', @@ -259,6 +306,17 @@ function fromAiContent(part: AiContentPart): MessageContent | undefined { case 'reasoning': base = { type: 'reasoning', text: part.text }; break; + case 'reasoning-file': { + base = { + type: 'reasoning-file', + data: normalizeReasoningFileData(part.data), + mediaType: part.mediaType, + }; + break; + } + case 'custom': + base = { type: 'custom', kind: part.kind }; + break; case 'tool-call': { const normalizedInput = normalizeToolInputForModel(part.input); base = { @@ -364,9 +422,7 @@ function toAiMessageList(msg: Message): ModelMessage[] { if (assistantParts.length > 0) { const assistantBase: ModelMessage = { role: 'assistant', - content: assistantParts as Array< - TextPart | ReasoningPart | ToolCallPart | ToolResultPart | FilePart - >, + content: assistantParts as AiAssistantContent, }; const assistantMsg: ModelMessage = msg.providerOptions ? { ...assistantBase, providerOptions: msg.providerOptions } diff --git a/packages/@n8n/agents/src/runtime/model/model-factory.ts b/packages/@n8n/agents/src/runtime/model/model-factory.ts index a157796e4a9..e1e3db53262 100644 --- a/packages/@n8n/agents/src/runtime/model/model-factory.ts +++ b/packages/@n8n/agents/src/runtime/model/model-factory.ts @@ -115,15 +115,14 @@ const LANGUAGE_PROVIDERS: ProviderRegistry = { }, google: { build: (creds, model, fetch) => { - const { createGoogleGenerativeAI } = - require('@ai-sdk/google') as typeof import('@ai-sdk/google'); - return createGoogleGenerativeAI({ ...creds, fetch })(model); + const { createGoogle } = require('@ai-sdk/google') as typeof import('@ai-sdk/google'); + return createGoogle({ ...creds, fetch })(model); }, }, xai: { build: (creds, model, fetch) => { const { createXai } = require('@ai-sdk/xai') as typeof import('@ai-sdk/xai'); - return createXai({ ...creds, fetch })(model); + return createXai({ ...creds, fetch }).chat(model); }, }, groq: { @@ -283,7 +282,7 @@ export function createModel(config: ModelConfig, fetch?: FetchFn): LanguageModel */ const EMBEDDING_PROVIDERS = { openai: { pkg: '@ai-sdk/openai', factory: 'createOpenAI' }, - google: { pkg: '@ai-sdk/google', factory: 'createGoogleGenerativeAI' }, + google: { pkg: '@ai-sdk/google', factory: 'createGoogle' }, mistral: { pkg: '@ai-sdk/mistral', factory: 'createMistral' }, cohere: { pkg: '@ai-sdk/cohere', factory: 'createCohere' }, amazon: { pkg: '@ai-sdk/amazon-bedrock', factory: 'createAmazonBedrock' }, diff --git a/packages/@n8n/agents/src/runtime/model/provider-quirks.ts b/packages/@n8n/agents/src/runtime/model/provider-quirks.ts index b428a2a545b..6efc882f59b 100644 --- a/packages/@n8n/agents/src/runtime/model/provider-quirks.ts +++ b/packages/@n8n/agents/src/runtime/model/provider-quirks.ts @@ -68,7 +68,12 @@ export const PROVIDER_QUIRKS: Partial> = { relaxStrictJsonSchemaForRawOutput: true, thinkingToProviderOptions: (thinking) => { const cfg = thinking as OpenAIThinkingConfig; - return { openai: { reasoningEffort: cfg.reasoningEffort ?? 'medium' } }; + return { + openai: { + reasoningEffort: cfg.reasoningEffort ?? 'medium', + reasoningSummary: null, + }, + }; }, }, groq: { diff --git a/packages/@n8n/agents/src/runtime/streaming/stream-session.ts b/packages/@n8n/agents/src/runtime/streaming/stream-session.ts index 0ceadafeb21..1055a6778b5 100644 --- a/packages/@n8n/agents/src/runtime/streaming/stream-session.ts +++ b/packages/@n8n/agents/src/runtime/streaming/stream-session.ts @@ -78,11 +78,17 @@ export function startStreamSession(deps: StreamSessionDeps): ReadableStream { + if (data.type !== AgentEvent.SubAgentChunk) return; + const { type: _type, ...payload } = data; + void guard.write({ type: 'subagent-chunk', ...payload }); + }; deps.eventBus.on(AgentEvent.ToolExecutionStart, onToolExecutionStart); deps.eventBus.on(AgentEvent.ToolExecutionEnd, onToolExecutionEnd); deps.eventBus.on(AgentEvent.SubAgentStarted, onSubAgentStarted); deps.eventBus.on(AgentEvent.SubAgentCompleted, onSubAgentCompleted); + deps.eventBus.on(AgentEvent.SubAgentChunk, onSubAgentChunk); deps .withRootSpan('stream', deps.options, deps.runId, async () => await deps.runLoop(guard)) @@ -113,6 +119,7 @@ export function startStreamSession(deps: StreamSessionDeps): ReadableStream): StreamChunk | undefined { switch (c.type) { @@ -125,7 +125,7 @@ export function convertChunk(c: TextStreamPart): StreamChunk | undefine } case 'tool-result': - // The fullStream emits the raw tool output here, not the + // The stream emits the raw tool output here, not the // `{ type, value }` ToolResultOutput wrapper used on the message // side — so pass it through verbatim. Only provider-executed tools // (e.g. native web search) reach this branch; local tool results are diff --git a/packages/@n8n/agents/src/runtime/telemetry/__tests__/sub-agent-telemetry.test.ts b/packages/@n8n/agents/src/runtime/telemetry/__tests__/sub-agent-telemetry.test.ts index 2eac64b8d5d..c12295889e8 100644 --- a/packages/@n8n/agents/src/runtime/telemetry/__tests__/sub-agent-telemetry.test.ts +++ b/packages/@n8n/agents/src/runtime/telemetry/__tests__/sub-agent-telemetry.test.ts @@ -1,5 +1,7 @@ +import { Telemetry } from '../../../sdk/telemetry'; import type { BuiltTelemetry } from '../../../types/telemetry'; import { deriveSubAgentTelemetry } from '../sub-agent-telemetry'; +import { buildAiSdkTelemetry } from '../telemetry-options'; function builtTelemetry(overrides: Partial = {}): BuiltTelemetry { return { @@ -42,4 +44,45 @@ describe('deriveSubAgentTelemetry()', () => { expect(derived?.metadata).toEqual({ source: 'sub-agent' }); }); + + it('uses derived metadata for AI SDK OpenTelemetry spans', async () => { + const tracer = { + startSpan: vi.fn(() => ({ end: vi.fn() })), + startActiveSpan: vi.fn(), + }; + const parentTelemetry = await new Telemetry() + .tracer(tracer) + .metadata({ source: 'workflow' }) + .build(); + const derived = deriveSubAgentTelemetry(parentTelemetry); + const options = buildAiSdkTelemetry(derived).telemetry; + const integrations = Array.isArray(options?.integrations) + ? options.integrations + : options?.integrations + ? [options.integrations] + : []; + + integrations[0]?.onStart?.({ + operationId: 'ai.generateText', + callId: 'call-1', + provider: 'openai.responses', + modelId: 'gpt-5', + instructions: 'test', + messages: [], + maxRetries: 2, + functionId: 'sub-agent', + recordInputs: true, + recordOutputs: true, + } as never); + + expect(tracer.startSpan).toHaveBeenCalledWith( + 'ai.generateText', + expect.objectContaining({ + attributes: expect.objectContaining({ + 'ai.telemetry.metadata.source': 'sub-agent', + }), + }), + undefined, + ); + }); }); diff --git a/packages/@n8n/agents/src/runtime/telemetry/runtime-telemetry.ts b/packages/@n8n/agents/src/runtime/telemetry/runtime-telemetry.ts index 04fdeea7b05..57cfa2f33d9 100644 --- a/packages/@n8n/agents/src/runtime/telemetry/runtime-telemetry.ts +++ b/packages/@n8n/agents/src/runtime/telemetry/runtime-telemetry.ts @@ -1,7 +1,7 @@ -import type { TelemetrySettings } from 'ai'; +import type { TelemetryOptions } from 'ai'; import { zodToJsonSchema } from 'zod-to-json-schema'; -import { buildExperimentalTelemetry } from './telemetry-options'; +import { buildAiSdkTelemetry } from './telemetry-options'; import { Telemetry } from '../../sdk/telemetry'; import type { AttributeValue, BuiltProviderTool, BuiltTelemetry, BuiltTool } from '../../types'; import type { ExecutionOptions } from '../../types/sdk/agent'; @@ -126,7 +126,7 @@ function buildGenAiRootAttributes( /** * Owns all telemetry concerns for a single agent runtime: resolving the * effective telemetry config, mapping it to the AI SDK's - * `experimental_telemetry` shape, building LangSmith/AI-SDK span attributes, + * `telemetry` shape, building LangSmith/AI-SDK span attributes, * and wrapping the generate/stream loops and tool calls in active spans. * * Keeps provider-specific attribute formatting out of the core loop. Holds a @@ -156,11 +156,11 @@ export class RuntimeTelemetry { await Telemetry.forceFlush(this.resolve(options)); } - /** Map resolved telemetry to AI SDK's experimental_telemetry shape. */ + /** Map resolved telemetry to the AI SDK's telemetry shape. */ buildTelemetryOptions(options?: ExecutionOptions): { - experimental_telemetry?: TelemetrySettings; + telemetry?: TelemetryOptions; } { - return buildExperimentalTelemetry(this.resolve(options), { + return buildAiSdkTelemetry(this.resolve(options), { fallbackFunctionId: this.config.name, }); } diff --git a/packages/@n8n/agents/src/runtime/telemetry/telemetry-options.ts b/packages/@n8n/agents/src/runtime/telemetry/telemetry-options.ts index 97ea444b1ed..a7373edb31c 100644 --- a/packages/@n8n/agents/src/runtime/telemetry/telemetry-options.ts +++ b/packages/@n8n/agents/src/runtime/telemetry/telemetry-options.ts @@ -1,34 +1,33 @@ -import type { TelemetrySettings } from 'ai'; +import type { TelemetryOptions } from 'ai'; import type { BuiltTelemetry } from '../../types/telemetry'; /** - * Map a resolved BuiltTelemetry to the AI SDK's `experimental_telemetry` + * Map a resolved BuiltTelemetry to the AI SDK's `telemetry` * option. Single source of truth for all runtime LLM calls (loop, memory * tasks). `functionSuffix` namespaces auxiliary calls (e.g. 'memory-observer') * under the parent functionId. */ -export function buildExperimentalTelemetry( +export function buildAiSdkTelemetry( telemetry: BuiltTelemetry | undefined, options: { fallbackFunctionId?: string; functionSuffix?: string } = {}, -): { experimental_telemetry?: TelemetrySettings } { +): { telemetry?: TelemetryOptions } { if (!telemetry?.enabled) return {}; const baseFunctionId = telemetry.functionId ?? options.fallbackFunctionId ?? 'agent'; const functionId = options.functionSuffix ? `${baseFunctionId}.${options.functionSuffix}` : baseFunctionId; + const integrations = + telemetry.resolveIntegrations?.(telemetry.metadata) ?? telemetry.integrations; return { - experimental_telemetry: { + telemetry: { isEnabled: true, functionId, - metadata: telemetry.metadata, recordInputs: telemetry.recordInputs, recordOutputs: telemetry.recordOutputs, - // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-explicit-any - tracer: telemetry.tracer as any, - integrations: telemetry.integrations.length > 0 ? telemetry.integrations : undefined, + integrations: integrations.length > 0 ? integrations : undefined, }, }; } diff --git a/packages/@n8n/agents/src/runtime/tools/delegate-sub-agent-tool.ts b/packages/@n8n/agents/src/runtime/tools/delegate-sub-agent-tool.ts index 15114b08a19..5196201abfd 100644 --- a/packages/@n8n/agents/src/runtime/tools/delegate-sub-agent-tool.ts +++ b/packages/@n8n/agents/src/runtime/tools/delegate-sub-agent-tool.ts @@ -7,14 +7,17 @@ import { type SubAgentTaskPath, type SubAgentTaskPathPolicy, } from './sub-agent-task-path'; +import { isAbortError } from '../../sdk/abort'; import { filterLlmMessages } from '../../sdk/message'; import { Tool } from '../../sdk/tool'; import { AgentEvent } from '../../types/runtime/event'; +import type { ForwardedChildChunk } from '../../types/runtime/event'; import type { AgentExecutionCounter, FinishReason, GenerateResult, ModelConfig, + StreamChunk, TokenUsage, } from '../../types/sdk/agent'; import type { AgentMessage } from '../../types/sdk/message'; @@ -62,7 +65,7 @@ const delegateSubAgentInputSchema = z.object({ // returned object (not this schema) is what is actually sent back to the model, // so this is kept in sync with DelegateSubAgentToolOutput by hand. const delegateSubAgentOutputSchema = z.object({ - status: z.enum(['completed', 'failed', 'suspended']), + status: z.enum(['completed', 'failed', 'suspended', 'cancelled']), taskPath: z.string().optional(), runId: z.string().optional(), threadId: z.string().optional(), @@ -145,7 +148,7 @@ export interface DelegateSubAgentRequest extends DelegateSubAgentInput { /** The result a delegation returns to the parent model and to lifecycle events. */ export interface DelegateSubAgentToolOutput { - status: 'completed' | 'failed' | 'suspended'; + status: 'completed' | 'failed' | 'suspended' | 'cancelled'; /** Echoed back so consumers can correlate the result with the delegation. */ taskPath?: SubAgentTaskPath; /** The child run's id, when the executor produced one. */ @@ -177,6 +180,57 @@ export interface DelegateSubAgentToolOutput { export interface DelegateSubAgentRunnerHelpers { /** Run a one-off inline child using the parent agent's inherited local/deferred tool set. */ runInlineSubAgent: (request: DelegateSubAgentRequest) => Promise; + /** + * Forward a child stream chunk into the parent run's event bus (allowlisted + * types only, with a per-delegation character budget). No-op when the + * parent tool call id is missing. + */ + emitChunk: (chunk: StreamChunk) => void; +} + +/** Cap on forwarded text+reasoning characters per delegation. */ +const SUB_AGENT_FORWARD_CHAR_BUDGET = 20_000; + +const FORWARDED_CHILD_CHUNK_TYPES = new Set([ + 'text-delta', + 'reasoning-start', + 'reasoning-delta', + 'reasoning-end', + 'tool-input-start', + 'tool-execution-start', + 'tool-execution-end', +]); + +function isForwardedChildChunk(chunk: StreamChunk): chunk is ForwardedChildChunk { + return FORWARDED_CHILD_CHUNK_TYPES.has(chunk.type as ForwardedChildChunk['type']); +} + +function createEmitChunkHelper( + ctx: ToolContext, + request: DelegateSubAgentRequest, +): (chunk: StreamChunk) => void { + let forwardedChars = 0; + return (chunk) => { + if (request.parentToolCallId === undefined) return; + if (!isForwardedChildChunk(chunk)) return; + + let forwarded: ForwardedChildChunk = chunk; + if (chunk.type === 'text-delta' || chunk.type === 'reasoning-delta') { + // Trim rather than pass through whole: a single provider delta can be + // arbitrarily large, and forwarding it intact would blow the budget. + const remaining = SUB_AGENT_FORWARD_CHAR_BUDGET - forwardedChars; + if (remaining <= 0) return; + const delta = chunk.delta.slice(0, remaining); + forwardedChars += delta.length; + forwarded = { ...chunk, delta }; + } + + ctx.emitEvent?.({ + type: AgentEvent.SubAgentChunk, + ...subAgentLifecycleBase(request), + chunk: forwarded, + }); + }; } export type InlineSubAgentProviderToolsResolver = ( @@ -431,9 +485,10 @@ function formatDelegationPolicyInstructions( * Tool handler: assign the child's task path, * assemble the {@link DelegateSubAgentRequest} from the model input plus the * parent tool context, then run the child via the host `runSubAgent` callback - * while emitting started/progress/completed lifecycle events. Any error is + * while emitting started/progress/completed lifecycle events. A failure is * converted into a `status: 'failed'` output (never thrown) so one failed - * delegation can't abort the parent's run. + * delegation can't abort the parent's run; an abort is rethrown, because a + * cancelled run must end the parent too. */ async function handleDelegateSubAgent( input: DelegateSubAgentInput, @@ -485,26 +540,26 @@ async function handleDelegateSubAgent( `${toolName} host runner does not support inline delegation without helpers.runInlineSubAgent from an Agent build.`, ); }, + emitChunk: createEmitChunkHelper(ctx, request), }); emitSubAgentCompleted(ctx, request, output, startedAt); return output; } catch (error) { - if (request !== undefined && startedAt !== undefined) { - const failedOutput: DelegateSubAgentToolOutput = { - status: 'failed', - ...(taskPath !== undefined ? { taskPath } : {}), - answer: '', - error: stringifyUnknown(error), - }; - emitSubAgentCompleted(ctx, request, failedOutput, startedAt); - return failedOutput; - } - return { - status: 'failed', + // When the parent has a signal it is the authority: `isAbortError` also + // matches by message text, and an unrelated child error must not be + // mistaken for a cancellation and kill the parent run. + const aborted = ctx.abortSignal ? ctx.abortSignal.aborted : isAbortError(error); + const output: DelegateSubAgentToolOutput = { + status: aborted ? 'cancelled' : 'failed', ...(taskPath !== undefined ? { taskPath } : {}), answer: '', - error: error instanceof Error ? error.message : String(error), + ...(aborted ? {} : { error: stringifyUnknown(error) }), }; + if (request !== undefined && startedAt !== undefined) { + emitSubAgentCompleted(ctx, request, output, startedAt); + } + if (aborted) throw error; + return output; } } @@ -621,7 +676,9 @@ function resolveDelegateSubAgentStatus( result: GenerateResult, ): DelegateSubAgentToolOutput['status'] { if (result.finishReason === 'error' || result.error !== undefined) { - return 'failed'; + // An aborted child sets its state to `cancelled` but still reports an + // error, so the state decides which of the two this really was. + return result.getState().status === 'cancelled' ? 'cancelled' : 'failed'; } if (result.pendingSuspend !== undefined && result.pendingSuspend.length > 0) { return 'suspended'; diff --git a/packages/@n8n/agents/src/runtime/tools/tool-adapter.ts b/packages/@n8n/agents/src/runtime/tools/tool-adapter.ts index 000df8e9e66..07177186158 100644 --- a/packages/@n8n/agents/src/runtime/tools/tool-adapter.ts +++ b/packages/@n8n/agents/src/runtime/tools/tool-adapter.ts @@ -54,6 +54,7 @@ export function toAiSdkProviderTools(tools?: BuiltProviderTool[]): Record { }); }); + it('maps provider-specific thinking from the Agent SDK into providerOptions', async () => { + generateText.mockResolvedValue(makeGenerateSuccess('ok')); + const agent = new Agent('agent') + .model('openai/gpt-4o-mini') + .instructions('test') + .thinking('openai', { reasoningEffort: 'high' }); + + await agent.generate('hello'); + + const callArgs = generateText.mock.calls[0][0] as { providerOptions: Record }; + expect(callArgs.providerOptions.openai).toEqual({ + reasoningEffort: 'high', + reasoningSummary: null, + }); + }); + it('cleans up the active runtime when a wrapped stream is cancelled', async () => { const agent = new Agent('agent').model('openai/gpt-4o-mini').instructions('test'); const internals = agent as unknown as AgentInternals; diff --git a/packages/@n8n/agents/src/sdk/__tests__/agent-reasoning.test.ts b/packages/@n8n/agents/src/sdk/__tests__/agent-reasoning.test.ts new file mode 100644 index 00000000000..f856f222168 --- /dev/null +++ b/packages/@n8n/agents/src/sdk/__tests__/agent-reasoning.test.ts @@ -0,0 +1,15 @@ +import { describe, expect, it } from 'vitest'; + +import { Agent } from '../agent'; + +describe('Agent reasoning configuration', () => { + it('keeps provider-specific thinking available alongside generic reasoning', () => { + const agent = new Agent('thinker') + .model('anthropic', 'claude-sonnet-4-5') + .thinking('anthropic', { budgetTokens: 4096 }) + .reasoning('high'); + + expect(agent.snapshot.thinking).toEqual({ budgetTokens: 4096 }); + expect(agent.snapshot.reasoning).toBe('high'); + }); +}); diff --git a/packages/@n8n/agents/src/sdk/__tests__/agent-telemetry.test.ts b/packages/@n8n/agents/src/sdk/__tests__/agent-telemetry.test.ts index f72b5b985b0..b82c5939c0a 100644 --- a/packages/@n8n/agents/src/sdk/__tests__/agent-telemetry.test.ts +++ b/packages/@n8n/agents/src/sdk/__tests__/agent-telemetry.test.ts @@ -71,11 +71,10 @@ describe('Agent telemetry', () => { const firstCall = generateText.mock.calls[0][0] as Record; const secondCall = generateText.mock.calls[1][0] as Record; - const firstTelemetry = firstCall.experimental_telemetry as Record; - const secondTelemetry = secondCall.experimental_telemetry as Record; + const firstTelemetry = firstCall.telemetry as Record; + const secondTelemetry = secondCall.telemetry as Record; expect(firstTelemetry.functionId).toBe('initial-agent'); expect(secondTelemetry.functionId).toBe('updated-agent'); - expect(secondTelemetry.metadata).toEqual({ functionId: 'updated-agent' }); }); }); diff --git a/packages/@n8n/agents/src/sdk/__tests__/delegate-sub-agent-routing.test.ts b/packages/@n8n/agents/src/sdk/__tests__/delegate-sub-agent-routing.test.ts index b0953379c15..14a1a6f3ac7 100644 --- a/packages/@n8n/agents/src/sdk/__tests__/delegate-sub-agent-routing.test.ts +++ b/packages/@n8n/agents/src/sdk/__tests__/delegate-sub-agent-routing.test.ts @@ -11,6 +11,7 @@ import { type DelegateSubAgentRunnerHelpers, } from '../../runtime/tools/delegate-sub-agent-tool'; import type { + AgentDbMessage, BuiltTool, GenerateResult, InterruptibleToolContext, @@ -29,6 +30,48 @@ const mockState = (): SerializableAgentState => ({ pendingToolCalls: {}, }); +function chunksFromGenerateResult(result: GenerateResult): unknown[] { + const chunks: unknown[] = []; + for (const message of result.messages) { + if (!('content' in message) || !Array.isArray(message.content)) continue; + for (const part of message.content) { + if (part.type === 'text' && part.text) { + chunks.push({ type: 'text-delta', id: 't-1', delta: part.text }); + } + } + } + for (const suspension of result.pendingSuspend ?? []) { + chunks.push({ + type: 'tool-call-suspended', + runId: suspension.runId, + toolCallId: suspension.toolCallId, + toolName: suspension.toolName, + input: suspension.input, + suspendPayload: suspension.suspendPayload, + }); + } + if (result.error !== undefined) { + chunks.push({ type: 'error', error: result.error }); + } + chunks.push({ + type: 'finish', + finishReason: result.finishReason ?? 'stop', + ...(result.usage !== undefined ? { usage: result.usage } : {}), + ...(result.model !== undefined ? { model: result.model } : {}), + ...(result.structuredOutput !== undefined ? { structuredOutput: result.structuredOutput } : {}), + }); + return chunks; +} + +function readableFromChunks(chunks: unknown[]): ReadableStream { + return new ReadableStream({ + start(controller) { + for (const chunk of chunks) controller.enqueue(chunk); + controller.close(); + }, + }); +} + vi.mock('../../runtime/loop/agent-runtime', async (importOriginal) => { const actual = await importOriginal(); return { @@ -38,23 +81,27 @@ vi.mock('../../runtime/loop/agent-runtime', async (importOriginal) => { runtimeConfigs.push(config); } - async generate(_input: unknown, options?: Record) { + async stream(_input: unknown, options?: Record) { runtimeGenerateOptions.push(options); - if (inlineChildGenerateResult !== undefined) { - return await Promise.resolve(inlineChildGenerateResult); - } + const result = + inlineChildGenerateResult ?? + ({ + runId: 'child-run', + finishReason: 'stop', + messages: [ + { + role: 'assistant', + type: 'llm', + content: [{ type: 'text', text: 'inline answer' }], + }, + ], + usage: { promptTokens: 0, completionTokens: 0, totalTokens: 0 }, + getState: mockState, + } as GenerateResult); return await Promise.resolve({ - runId: 'child-run', - finishReason: 'stop', - messages: [ - { - role: 'assistant', - type: 'llm', - content: [{ type: 'text', text: 'inline answer' }], - }, - ], - usage: { promptTokens: 0, completionTokens: 0, totalTokens: 0 }, - getState: mockState, + runId: result.runId, + stream: readableFromChunks(chunksFromGenerateResult(result)), + getState: result.getState ?? mockState, }); } @@ -234,7 +281,7 @@ describe('delegate sub-agent routing', () => { }); }); - it('passes the parent execution counter to inline child generate options', async () => { + it('passes the parent execution counter to inline child stream options', async () => { const executionCounter = { incrementMessageCount: vi.fn(), incrementToolCallCount: vi.fn(), @@ -323,7 +370,7 @@ describe('delegate sub-agent routing', () => { taskPath: '/root/research_api_0', childCount: 0, }, - { runInlineSubAgent }, + { runInlineSubAgent, emitChunk: () => undefined }, ), ).resolves.toMatchObject({ status: 'completed', @@ -376,4 +423,55 @@ describe('delegate sub-agent routing', () => { error: DELEGATED_CHILD_SUSPEND_UNSUPPORTED_MESSAGE, }); }); + + it('answers with the final assistant turn, not every text block the child streamed', async () => { + const responseMessages: AgentDbMessage[] = [ + { + id: 'm-1', + createdAt: new Date(1), + role: 'assistant', + type: 'llm', + content: [{ type: 'text', text: 'Let me look that up.' }], + }, + { + id: 'm-2', + createdAt: new Date(2), + role: 'assistant', + type: 'llm', + content: [{ type: 'text', text: 'The capital is Paris.' }], + }, + ]; + inlineChildGenerateResult = { + runId: 'child-run-multi-turn', + finishReason: 'stop', + messages: responseMessages, + getState: () => ({ + status: 'success', + messageList: { + messages: responseMessages, + historyIds: [], + inputIds: [], + responseIds: ['m-1', 'm-2'], + }, + pendingToolCalls: {}, + }), + }; + + const agent = new Agent('parent') + .model('openai', 'gpt-4o-mini') + .instructions('Delegate when needed.') + .tool(createDelegateSubAgentTool()); + + const runtimeConfig = await buildAgentConfig(agent); + const delegateTool = runtimeConfig.tools?.find( + (tool) => tool.name === DELEGATE_SUB_AGENT_TOOL_NAME, + ); + + await expect( + delegateTool?.handler?.(delegateInput, { runId: 'parent-run-1' }), + ).resolves.toMatchObject({ + status: 'completed', + answer: 'The capital is Paris.', + }); + }); }); diff --git a/packages/@n8n/agents/src/sdk/__tests__/inline-sub-agent-tools.test.ts b/packages/@n8n/agents/src/sdk/__tests__/inline-sub-agent-tools.test.ts index 1b9b1fa8d33..8ba55d38cdf 100644 --- a/packages/@n8n/agents/src/sdk/__tests__/inline-sub-agent-tools.test.ts +++ b/packages/@n8n/agents/src/sdk/__tests__/inline-sub-agent-tools.test.ts @@ -35,6 +35,51 @@ function makeGenerateSuccess(): Record { }; } +function chunksFromGenerateResult(result: Record): unknown[] { + const chunks: unknown[] = []; + const messages = result.messages as + | Array<{ content?: Array<{ type: string; text?: string }> }> + | undefined; + for (const message of messages ?? []) { + for (const part of message.content ?? []) { + if (part.type === 'text' && part.text) { + chunks.push({ type: 'text-delta', id: 't-1', delta: part.text }); + } + } + } + for (const suspension of (result.pendingSuspend as Array> | undefined) ?? + []) { + chunks.push({ + type: 'tool-call-suspended', + runId: suspension.runId, + toolCallId: suspension.toolCallId, + toolName: suspension.toolName, + input: suspension.input, + suspendPayload: suspension.suspendPayload, + }); + } + if (result.error !== undefined) { + chunks.push({ type: 'error', error: result.error }); + } + chunks.push({ + type: 'finish', + finishReason: result.finishReason ?? 'stop', + ...(result.usage !== undefined ? { usage: result.usage } : {}), + ...(result.model !== undefined ? { model: result.model } : {}), + ...(result.structuredOutput !== undefined ? { structuredOutput: result.structuredOutput } : {}), + }); + return chunks; +} + +function readableFromChunks(chunks: unknown[]): ReadableStream { + return new ReadableStream({ + start(controller) { + for (const chunk of chunks) controller.enqueue(chunk); + controller.close(); + }, + }); +} + vi.mock('../../runtime/loop/agent-runtime', async (importOriginal) => { const actual = await importOriginal(); return { @@ -49,6 +94,20 @@ vi.mock('../../runtime/loop/agent-runtime', async (importOriginal) => { return await Promise.resolve(runtimeGenerateResults.shift() ?? makeGenerateSuccess()); } + async stream(_prompt: string, options?: Record) { + runtimeGenerateOptions.push(options ?? {}); + const result = runtimeGenerateResults.shift() ?? makeGenerateSuccess(); + return await Promise.resolve({ + runId: (result.runId as string | undefined) ?? 'child-run', + stream: readableFromChunks(chunksFromGenerateResult(result)), + getState: () => ({ + status: 'success', + messageList: { messages: [], historyIds: [], inputIds: [], responseIds: [] }, + pendingToolCalls: {}, + }), + }); + } + async dispose() { return await Promise.resolve(); } @@ -304,6 +363,52 @@ describe('inline sub-agent tool filtering', () => { expect(runtimeConfigs[0]?.thinking).toEqual(thinking); }); + it('inherits generic reasoning when difficulty selects a different provider', async () => { + const agent = new Agent('parent').model('openai', 'gpt-4o-mini').reasoning('high'); + const runner = (agent as unknown as AgentWithInlineRunner).createInlineSubAgentRunner({ + deferredTools: [], + modelConfig: 'openai/gpt-4o-mini', + tools: [makeTool('lookup')], + inlineSubAgentModelsByDifficulty: { high: 'anthropic/claude-sonnet-4-5' }, + }); + + await runner({ + subAgentId: INLINE_SUB_AGENT_ID, + taskName: 'research', + goal: 'Find the answer', + taskPath: '/root/research', + childCount: 0, + difficulty: 'high', + }); + + expect(runtimeConfigs).toHaveLength(1); + expect(runtimeConfigs[0]?.model).toBe('anthropic/claude-sonnet-4-5'); + expect(runtimeConfigs[0]?.reasoning).toBe('high'); + }); + + it('keeps parent reasoning when difficulty keeps the same provider', async () => { + const agent = new Agent('parent').model('openai', 'gpt-4o-mini').reasoning('low'); + const runner = (agent as unknown as AgentWithInlineRunner).createInlineSubAgentRunner({ + deferredTools: [], + modelConfig: 'openai/gpt-4o-mini', + tools: [makeTool('lookup')], + inlineSubAgentModelsByDifficulty: { high: 'openai/o3-mini' }, + }); + + await runner({ + subAgentId: INLINE_SUB_AGENT_ID, + taskName: 'research', + goal: 'Find the answer', + taskPath: '/root/research', + childCount: 0, + difficulty: 'high', + }); + + expect(runtimeConfigs).toHaveLength(1); + expect(runtimeConfigs[0]?.model).toBe('openai/o3-mini'); + expect(runtimeConfigs[0]?.reasoning).toBe('low'); + }); + it('includes selected child model in failed inline child output when runtime result omits it', async () => { runtimeGenerateResults.push({ runId: 'child-run', @@ -311,6 +416,11 @@ describe('inline sub-agent tool filtering', () => { error: 'model failed', messages: [], usage: {}, + getState: () => ({ + status: 'failed', + messageList: { messages: [], historyIds: [], inputIds: [], responseIds: [] }, + pendingToolCalls: {}, + }), }); const runner = createInlineRunner({ inlineSubAgentModelsByDifficulty: { high: 'anthropic/claude-sonnet-4-5' }, diff --git a/packages/@n8n/agents/src/sdk/__tests__/telemetry.test.ts b/packages/@n8n/agents/src/sdk/__tests__/telemetry.test.ts index 005f698b80a..d0e9b646449 100644 --- a/packages/@n8n/agents/src/sdk/__tests__/telemetry.test.ts +++ b/packages/@n8n/agents/src/sdk/__tests__/telemetry.test.ts @@ -1,4 +1,4 @@ -import type { TelemetryIntegration } from 'ai'; +import type { Telemetry as AiSdkTelemetry } from 'ai'; import { Telemetry } from '../telemetry'; @@ -35,9 +35,47 @@ describe('Telemetry builder', () => { }); it('accepts a pre-built tracer', async () => { - const fakeTracer = { startSpan: vi.fn() }; + const fakeTracer = { startSpan: vi.fn(), startActiveSpan: vi.fn() }; const built = await new Telemetry().tracer(fakeTracer).build(); expect(built.tracer).toBe(fakeTracer); + expect(built.integrations).toHaveLength(1); + expect(built.integrations[0]).toEqual( + expect.objectContaining({ + onStart: expect.any(Function), + executeLanguageModelCall: expect.any(Function), + }), + ); + }); + + it('preserves metadata on AI SDK 7 OpenTelemetry spans', async () => { + const fakeTracer = { + startSpan: vi.fn(() => ({ end: vi.fn() })), + startActiveSpan: vi.fn(), + }; + const built = await new Telemetry().metadata({ team: 'platform' }).tracer(fakeTracer).build(); + + built.integrations[0].onStart?.({ + operationId: 'ai.generateText', + callId: 'call-1', + provider: 'openai.responses', + modelId: 'gpt-5', + instructions: 'test', + messages: [], + maxRetries: 2, + functionId: 'agent', + recordInputs: true, + recordOutputs: true, + } as never); + + expect(fakeTracer.startSpan).toHaveBeenCalledWith( + 'ai.generateText', + expect.objectContaining({ + attributes: expect.objectContaining({ + 'ai.telemetry.metadata.team': 'platform', + }), + }), + undefined, + ); }); it('throws when both .tracer() and .otlpEndpoint() are set', async () => { @@ -47,21 +85,161 @@ describe('Telemetry builder', () => { }); it('collects multiple integrations', async () => { - const int1: TelemetryIntegration = { onStart: vi.fn() }; - const int2: TelemetryIntegration = { onFinish: vi.fn() }; + const int1: AiSdkTelemetry = { onStart: vi.fn() }; + const int2: AiSdkTelemetry = { onEnd: vi.fn() }; const built = await new Telemetry().integration(int1).integration(int2).build(); expect(built.integrations).toHaveLength(2); }); }); describe('Telemetry — redaction wrapping', () => { + it('redacts hooks on frozen integrations', async () => { + const onStart = vi.fn(); + const integration: AiSdkTelemetry = Object.freeze({ onStart }); + const built = await new Telemetry() + .redact((data) => { + const filtered = { ...data }; + delete filtered.secret; + return filtered; + }) + .integration(integration) + .build(); + + built.integrations[0].onStart?.({ secret: 'hidden', safe: 'ok' } as never); + + expect(onStart).toHaveBeenCalledWith({ safe: 'ok' }); + }); + + it('accepts immutable redactor results', async () => { + const onStart = vi.fn(); + const built = await new Telemetry() + .redact((data) => { + const filtered = { ...data }; + delete filtered.secret; + return Object.freeze(filtered); + }) + .integration({ onStart }) + .build(); + + built.integrations[0].onStart?.({ secret: 'hidden', safe: 'ok' } as never); + + expect(onStart).toHaveBeenCalledWith({ safe: 'ok' }); + }); + + it('redacts future event hooks without requiring an explicit wrapper', async () => { + type FutureTelemetry = AiSdkTelemetry & { + onFutureEvent: (event: Record) => void; + readonly receivedEvents: Array>; + }; + + class FutureIntegration { + readonly #receivedEvents: Array> = []; + + onStart() {} + + onFutureEvent(event: Record) { + this.#receivedEvents.push(event); + } + + get receivedEvents() { + return this.#receivedEvents; + } + } + + const integration: FutureTelemetry = new FutureIntegration(); + const built = await new Telemetry() + .redact((data) => { + const filtered = { ...data }; + delete filtered.secret; + return filtered; + }) + .integration(integration) + .build(); + + const wrapped = built.integrations[0] as FutureTelemetry; + wrapped.onFutureEvent({ secret: 'hidden', safe: 'ok' }); + + expect(integration.receivedEvents).toEqual([{ safe: 'ok' }]); + }); + + it('preserves executor control fields while redacting their event data', async () => { + let modelOptions: Record | undefined; + let toolOptions: Record | undefined; + const integration: AiSdkTelemetry = { + executeLanguageModelCall: (options) => { + modelOptions = options; + return options.execute(); + }, + executeTool: (options) => { + toolOptions = options; + return options.execute(); + }, + }; + const built = await new Telemetry() + .redact((data) => { + const filtered = { ...data }; + delete filtered.callId; + delete filtered.toolCallId; + delete filtered.execute; + delete filtered.secret; + return Object.freeze(filtered); + }) + .integration(integration) + .build(); + + const modelExecute = vi.fn().mockResolvedValue('model-result'); + await expect( + built.integrations[0].executeLanguageModelCall?.({ + callId: 'model-call', + execute: modelExecute, + secret: 'hidden', + } as never), + ).resolves.toBe('model-result'); + expect(modelOptions).toEqual({ callId: 'model-call', execute: modelExecute }); + + const toolExecute = vi.fn().mockResolvedValue('tool-result'); + await expect( + built.integrations[0].executeTool?.({ + callId: 'model-call', + toolCallId: 'tool-call', + execute: toolExecute, + secret: 'hidden', + } as never), + ).resolves.toBe('tool-result'); + expect(toolOptions).toEqual({ + callId: 'model-call', + toolCallId: 'tool-call', + execute: toolExecute, + }); + }); + + it('preserves the receiver for stateful integration methods', async () => { + class StatefulIntegration { + readonly receivedEvents: unknown[] = []; + + onStart(event: Parameters>[0]) { + this.receivedEvents.push(event); + } + } + + const integration = new StatefulIntegration(); + const built = await new Telemetry() + .redact((data) => data) + .integration(integration) + .build(); + + built.integrations[0].onStart!({ operationId: 'ai.generateText' } as never); + + expect(integration.receivedEvents).toHaveLength(1); + }); + it('wraps integrations with redaction when .redact() is set', async () => { const receivedEvents: unknown[] = []; - const integration: TelemetryIntegration = { + const integration: AiSdkTelemetry = { onStart: (event) => { receivedEvents.push(event); }, - onFinish: (event) => { + onEnd: (event) => { receivedEvents.push(event); }, }; @@ -86,7 +264,7 @@ describe('Telemetry — redaction wrapping', () => { }); it('does not wrap integrations when .redact() is not set', async () => { - const integration: TelemetryIntegration = { onStart: vi.fn() }; + const integration: AiSdkTelemetry = { onStart: vi.fn() }; const built = await new Telemetry().integration(integration).build(); // The integration should be a copy (not the same reference due to spread) but functionally identical expect(built.integrations[0].onStart).toBe(integration.onStart); @@ -94,7 +272,7 @@ describe('Telemetry — redaction wrapping', () => { it('redacts top-level scalar fields via the redact callback', async () => { const receivedEvents: unknown[] = []; - const integration: TelemetryIntegration = { + const integration: AiSdkTelemetry = { onStart: (event) => { receivedEvents.push(event); }, @@ -118,7 +296,7 @@ describe('Telemetry — redaction wrapping', () => { it('redacts objects inside arrays', async () => { const receivedEvents: unknown[] = []; - const integration: TelemetryIntegration = { + const integration: AiSdkTelemetry = { onStart: (event) => { receivedEvents.push(event); }, diff --git a/packages/@n8n/agents/src/sdk/agent.ts b/packages/@n8n/agents/src/sdk/agent.ts index 59f755ba6c0..505e3f4ace4 100644 --- a/packages/@n8n/agents/src/sdk/agent.ts +++ b/packages/@n8n/agents/src/sdk/agent.ts @@ -13,6 +13,7 @@ import { AgentRuntime, type AgentRuntimeConfig } from '../runtime/loop/agent-run import { ensureUniqueMcpToolNames } from '../runtime/mcp/mcp-tool-resolver'; import { RECALL_MEMORY_TOOL_NAME } from '../runtime/memory/episodic-memory'; import type { ScopedMemoryTaskEvent } from '../runtime/memory/scoped-memory-task-runner'; +import { AgentMessageList } from '../runtime/model/message-list'; import type { FetchFn } from '../runtime/model/model-factory'; import { mergeProviderOptions } from '../runtime/model/prompt-cache'; import { AgentEventBus } from '../runtime/state/event-bus'; @@ -50,6 +51,7 @@ import type { AgentMiddleware, BuiltAgent, BuiltEval, + BuiltFileStore, BuiltGuardrail, BuiltMemory, BuiltProviderTool, @@ -57,15 +59,18 @@ import type { BuiltTelemetry, CheckpointStore, ExecutionOptions, + FinishReason, GenerateResult, MemoryConfig, ModelConfig, Provider, PromptCachingConfig, + ReasoningLevel, RunOptions, StreamResult, ThinkingConfig, ThinkingConfigFor, + TokenUsage, ResumeOptions, McpConnectionFailedEvent, } from '../types'; @@ -118,8 +123,10 @@ export interface AgentSnapshot { hasObservationalMemory: boolean; /** True when episodic memory has been configured on the memory builder. */ hasEpisodicMemory: boolean; - /** The thinking config if set, otherwise null. */ + /** The provider-specific thinking config if set, otherwise null. */ thinking: ThinkingConfig | null; + /** The provider-agnostic reasoning level if set, otherwise null. */ + reasoning: ReasoningLevel | null; /** The prompt caching config if set via `.promptCaching()`, otherwise null. */ promptCaching: PromptCachingConfig | null; /** Tool-call concurrency limit if set, otherwise null. */ @@ -165,6 +172,8 @@ export class Agent implements BuiltAgent, AgentBuilder { private memoryConfig?: MemoryConfig; + private fileStoreValue?: BuiltFileStore; + private onMemoryTaskEvent?: (event: ScopedMemoryTaskEvent) => void; // TODO: Guardrails are accepted by the builder API for forward @@ -181,6 +190,8 @@ export class Agent implements BuiltAgent, AgentBuilder { private thinkingConfig?: ThinkingConfig; + private reasoningLevel?: ReasoningLevel; + private promptCachingConfig?: PromptCachingConfig; private concurrencyValue?: number; @@ -359,6 +370,12 @@ export class Agent implements BuiltAgent, AgentBuilder { return this; } + /** Inject the host store that hydrates file-reference content parts before LLM calls. */ + fileStore(store: BuiltFileStore): this { + this.fileStoreValue = store; + return this; + } + /** Add a middleware. */ middleware(m: AgentMiddleware): this { this.middlewares.push(m); @@ -455,6 +472,21 @@ export class Agent implements BuiltAgent, AgentBuilder { return this; } + /** + * Enable provider-agnostic reasoning for the agent. + * + * @example + * ```typescript + * new Agent('thinker') + * .model('anthropic', 'claude-sonnet-4-5') + * .reasoning('high') + * ``` + */ + reasoning(level: ReasoningLevel = 'medium'): this { + this.reasoningLevel = level; + return this; + } + /** * Enable prompt caching with defaults tuned for agent workloads. Anthropic * models get a `1h` instruction-level cache breakpoint; OpenAI models get @@ -669,6 +701,7 @@ export class Agent implements BuiltAgent, AgentBuilder { hasObservationalMemory: this.memoryConfig?.observationalMemory !== undefined, hasEpisodicMemory: this.memoryConfig?.episodicMemory !== undefined, thinking: this.thinkingConfig ?? null, + reasoning: this.reasoningLevel ?? null, promptCaching: this.promptCachingConfig ?? null, toolCallConcurrency: this.concurrencyValue ?? null, }; @@ -1064,12 +1097,14 @@ export class Agent implements BuiltAgent, AgentBuilder { instructionProviderOptions: this.instructionProviderOpts, providerTools: this.providerTools.length > 0 ? this.providerTools : undefined, memory: memoryConfig?.memory, + ...(this.fileStoreValue !== undefined ? { fileStore: this.fileStoreValue } : {}), observationLog: memoryConfig?.observationLog, observationalMemory: memoryConfig?.observationalMemory, episodicMemory: memoryConfig?.episodicMemory, structuredOutput: this.outputSchema, checkpointStorage: this.checkpointStore, thinking: this.thinkingConfig, + reasoning: this.reasoningLevel, promptCaching: this.promptCachingConfig, toolCallConcurrency: this.concurrencyValue, titleGeneration: memoryConfig?.titleGeneration, @@ -1105,13 +1140,17 @@ export class Agent implements BuiltAgent, AgentBuilder { const hostRunner = delegateOptions.runSubAgent; const completedTool = createDelegateSubAgentTool({ ...delegateOptions, - runSubAgent: async (request, _helpersFromHandler) => { - const helpers = { runInlineSubAgent }; + runSubAgent: async (request, helpersFromHandler) => { + const helpers = { + runInlineSubAgent: async (req: DelegateSubAgentRequest) => + await runInlineSubAgent(req, helpersFromHandler.emitChunk), + emitChunk: helpersFromHandler.emitChunk, + }; if (hostRunner) { return await hostRunner(request, helpers); } if (request.subAgentId === INLINE_SUB_AGENT_ID) { - return await runInlineSubAgent(request); + return await runInlineSubAgent(request, helpersFromHandler.emitChunk); } return { status: 'failed', @@ -1139,8 +1178,11 @@ export class Agent implements BuiltAgent, AgentBuilder { inlineSubAgentBlockedTools?: string[]; inlineSubAgentModelsByDifficulty?: Partial>; resolveInlineSubAgentProviderTools?: InlineSubAgentProviderToolsResolver; - }): (request: DelegateSubAgentRequest) => Promise { - return async (request) => { + }): ( + request: DelegateSubAgentRequest, + onChunk?: (chunk: StreamChunk) => void, + ) => Promise { + return async (request, onChunk) => { const tools = filterInlineSubAgentTools(options.tools, options.inlineSubAgentBlockedTools); const deferredTools = filterInlineSubAgentTools( options.deferredTools, @@ -1174,6 +1216,7 @@ export class Agent implements BuiltAgent, AgentBuilder { promptCaching: this.promptCachingConfig, checkpointStorage: this.checkpointStore, ...(childThinkingConfig !== undefined ? { thinking: childThinkingConfig } : {}), + ...(this.reasoningLevel !== undefined ? { reasoning: this.reasoningLevel } : {}), ...(telemetry !== undefined ? { telemetry } : {}), ...(options.toolCallConcurrency !== undefined ? { toolCallConcurrency: options.toolCallConcurrency } @@ -1181,7 +1224,7 @@ export class Agent implements BuiltAgent, AgentBuilder { }); try { - const result = await childRuntime.generate(renderDelegateSubAgentPrompt(request), { + const resultStream = await childRuntime.stream(renderDelegateSubAgentPrompt(request), { ...(request.parentAbortSignal !== undefined ? { abortSignal: request.parentAbortSignal } : {}), @@ -1190,14 +1233,75 @@ export class Agent implements BuiltAgent, AgentBuilder { ? { executionCounter: request.parentExecutionCounter } : {}), }); - if (result.pendingSuspend !== undefined && result.pendingSuspend.length > 0) { - return failedDelegatedChildSuspendOutput(request.taskPath, result.model ?? childModelId); + + let text = ''; + let model: string | undefined; + let usage: TokenUsage | undefined; + let finishReason: FinishReason | undefined; + let structuredOutput: unknown; + let error: unknown; + let suspended = false; + + const reader = resultStream.stream.getReader(); + try { + while (true) { + const { done, value } = await reader.read(); + if (done) break; + const chunk = value; + onChunk?.(chunk); + switch (chunk.type) { + case 'text-delta': + text += chunk.delta; + break; + case 'tool-call-suspended': + suspended = true; + break; + case 'error': + error = chunk.error; + break; + case 'finish': + finishReason = chunk.finishReason; + if (chunk.usage !== undefined) usage = chunk.usage; + if (chunk.model !== undefined) model = chunk.model; + if (chunk.structuredOutput !== undefined) { + structuredOutput = chunk.structuredOutput; + } + break; + default: + break; + } + } + } finally { + reader.releaseLock(); } - const resultWithModel = - result.model === undefined && childModelId !== undefined - ? { ...result, model: childModelId } - : result; - return generateResultToDelegateSubAgentOutput(request.taskPath, resultWithModel); + + if (suspended) { + return failedDelegatedChildSuspendOutput(request.taskPath, model ?? childModelId); + } + + // The runtime only serializes its message list into state on success + // and on suspend, so an errored child falls back to the streamed text. + const responseMessages = AgentMessageList.deserialize( + resultStream.getState().messageList, + ).responseDelta(); + const messages: AgentMessage[] = + responseMessages.length > 0 + ? responseMessages + : text.trim() + ? [{ role: 'assistant', content: [{ type: 'text', text }] }] + : []; + + const result: GenerateResult = { + runId: resultStream.runId, + messages, + ...((model ?? childModelId) ? { model: model ?? childModelId } : {}), + ...(finishReason !== undefined ? { finishReason } : {}), + ...(usage !== undefined ? { usage } : {}), + ...(structuredOutput !== undefined ? { structuredOutput } : {}), + ...(error !== undefined ? { error } : {}), + getState: () => resultStream.getState(), + }; + return generateResultToDelegateSubAgentOutput(request.taskPath, result); } finally { await childRuntime.dispose(); } diff --git a/packages/@n8n/agents/src/sdk/metadata-enriched-tracer.ts b/packages/@n8n/agents/src/sdk/metadata-enriched-tracer.ts new file mode 100644 index 00000000000..877f6bf0ed6 --- /dev/null +++ b/packages/@n8n/agents/src/sdk/metadata-enriched-tracer.ts @@ -0,0 +1,69 @@ +import type { Attributes, Context, Span, SpanOptions, Tracer } from '@opentelemetry/api'; + +import type { AttributeValue } from '../types/telemetry'; + +class MetadataEnrichedTracer implements Tracer { + constructor( + private readonly delegate: Tracer, + private readonly attributes: Attributes, + ) {} + + startSpan(name: string, options?: SpanOptions, context?: Context): Span { + return this.delegate.startSpan( + name, + { + ...options, + attributes: { ...this.attributes, ...options?.attributes }, + }, + context, + ); + } + + startActiveSpan unknown>(name: string, fn: F): ReturnType; + startActiveSpan unknown>( + name: string, + options: SpanOptions, + fn: F, + ): ReturnType; + startActiveSpan unknown>( + name: string, + options: SpanOptions, + context: Context, + fn: F, + ): ReturnType; + startActiveSpan unknown>( + name: string, + optionsOrFn: SpanOptions | F, + contextOrFn?: Context | F, + fn?: F, + ): ReturnType { + if (typeof optionsOrFn === 'function') { + return this.delegate.startActiveSpan(name, optionsOrFn); + } + + const options = { + ...optionsOrFn, + attributes: { ...this.attributes, ...optionsOrFn.attributes }, + }; + if (typeof contextOrFn === 'function') { + return this.delegate.startActiveSpan(name, options, contextOrFn); + } + if (contextOrFn === undefined || fn === undefined) { + throw new Error('OpenTelemetry active span callback is required.'); + } + + return this.delegate.startActiveSpan(name, options, contextOrFn, fn); + } +} + +export function createMetadataEnrichedTracer( + tracer: Tracer, + metadata: Record | undefined, +): Tracer { + if (!metadata || Object.keys(metadata).length === 0) return tracer; + + const attributes = Object.fromEntries( + Object.entries(metadata).map(([key, value]) => [`ai.telemetry.metadata.${key}`, value]), + ); + return new MetadataEnrichedTracer(tracer, attributes); +} diff --git a/packages/@n8n/agents/src/sdk/telemetry.ts b/packages/@n8n/agents/src/sdk/telemetry.ts index 1435e42f1cb..6d6b7229f74 100644 --- a/packages/@n8n/agents/src/sdk/telemetry.ts +++ b/packages/@n8n/agents/src/sdk/telemetry.ts @@ -1,5 +1,8 @@ -import type { TelemetryIntegration } from 'ai'; +import { isRecord } from '@n8n/utils/is-record'; +import type { Tracer } from '@opentelemetry/api'; +import type { Telemetry as AiSdkTelemetry } from 'ai'; +import { createMetadataEnrichedTracer } from './metadata-enriched-tracer'; import type { AttributeValue, BuiltTelemetry, @@ -8,6 +11,16 @@ import type { } from '../types/telemetry'; type RedactFn = (data: Record) => Record; +type ExecuteHookKey = 'executeLanguageModelCall' | 'executeTool'; +// Future on* hooks are wrapped automatically; other SDK members must be classified explicitly. +type RedactableTelemetry = AiSdkTelemetry & + Record, never>; +type UnknownMethod = (this: unknown, ...args: unknown[]) => unknown; + +const EXECUTION_CONTROL_FIELDS: Record = { + executeLanguageModelCall: ['callId', 'execute'], + executeTool: ['callId', 'toolCallId', 'execute'], +}; /** * Recursively apply the redact function to plain objects found anywhere @@ -22,7 +35,7 @@ function redactValue(value: unknown, redact: RedactFn): unknown { value !== null && Object.getPrototypeOf(value) === Object.prototype ) { - const redacted = redact(value as Record); + const redacted = { ...redact(value as Record) }; // Recurse into the redacted result so deeply nested objects are also processed. for (const key of Object.keys(redacted)) { redacted[key] = redactValue(redacted[key], redact); @@ -41,7 +54,7 @@ function redactValue(value: unknown, redact: RedactFn): unknown { function redactEvent(event: T, redact: RedactFn): T { const cloned = { ...event }; // Redact the cloned event itself (it is a plain object). - const redacted = redact(cloned as unknown as Record); + const redacted = { ...redact(cloned as unknown as Record) }; // Then recurse into each value to handle arrays and nested objects. for (const key of Object.keys(redacted)) { const value = redacted[key]; @@ -50,42 +63,82 @@ function redactEvent(event: T, redact: RedactFn): T { return redacted as T; } +function isExecuteHookKey(property: PropertyKey): property is ExecuteHookKey { + return property === 'executeLanguageModelCall' || property === 'executeTool'; +} + +function isTelemetryHook(property: PropertyKey): boolean { + return (typeof property === 'string' && property.startsWith('on')) || isExecuteHookKey(property); +} + +function isMethod(value: unknown): value is UnknownMethod { + return typeof value === 'function'; +} + +function redactHookArgument(property: PropertyKey, argument: unknown, redact: RedactFn): unknown { + if (property === 'onError' || !isRecord(argument)) return redactValue(argument, redact); + + const redacted = redactEvent(argument, redact); + if (isExecuteHookKey(property)) { + const controlFields = Object.fromEntries( + EXECUTION_CONTROL_FIELDS[property].map((field) => [field, argument[field]]), + ); + return { ...redacted, ...controlFields }; + } + return redacted; +} + /** - * Wrap a TelemetryIntegration so every hook passes event data through + * Wrap an AI SDK telemetry integration so every hook passes event data through * the redact callback before forwarding to the original hook. */ function wrapIntegrationWithRedaction( - integration: TelemetryIntegration, + integration: RedactableTelemetry, redact: RedactFn, -): TelemetryIntegration { - const wrapped: TelemetryIntegration = {}; +): AiSdkTelemetry { + const methodCache = new Map(); + const facade: AiSdkTelemetry = {}; - if (integration.onStart) { - const orig = integration.onStart; - wrapped.onStart = (event) => orig(redactEvent(event, redact)); - } - if (integration.onStepStart) { - const orig = integration.onStepStart; - wrapped.onStepStart = (event) => orig(redactEvent(event, redact)); - } - if (integration.onToolCallStart) { - const orig = integration.onToolCallStart; - wrapped.onToolCallStart = (event) => orig(redactEvent(event, redact)); - } - if (integration.onToolCallFinish) { - const orig = integration.onToolCallFinish; - wrapped.onToolCallFinish = (event) => orig(redactEvent(event, redact)); - } - if (integration.onStepFinish) { - const orig = integration.onStepFinish; - wrapped.onStepFinish = (event) => orig(redactEvent(event, redact)); - } - if (integration.onFinish) { - const orig = integration.onFinish; - wrapped.onFinish = (event) => orig(redactEvent(event, redact)); + return new Proxy(facade, { + get(_target, property) { + const original: unknown = Reflect.get(integration, property, integration); + if (!isTelemetryHook(property) || !isMethod(original)) return original; + + const cached = methodCache.get(property); + if (cached?.original === original) return cached.wrapped; + + const wrapped: UnknownMethod = function (...args) { + const [argument, ...rest] = args; + return Reflect.apply(original, integration, [ + redactHookArgument(property, argument, redact), + ...rest, + ]); + }; + methodCache.set(property, { original, wrapped }); + return wrapped; + }, + }); +} + +function isOpenTelemetryTracer(value: unknown): value is Tracer { + return ( + value !== null && + typeof value === 'object' && + typeof Reflect.get(value, 'startSpan') === 'function' && + typeof Reflect.get(value, 'startActiveSpan') === 'function' + ); +} + +async function createAiSdkOpenTelemetryIntegrationFactory( + tracer: OpaqueTracer, +): Promise<(metadata: Record | undefined) => AiSdkTelemetry> { + if (!isOpenTelemetryTracer(tracer)) { + throw new Error('Telemetry tracer must implement startSpan() and startActiveSpan().'); } - return wrapped; + const { LegacyOpenTelemetry } = await import('@ai-sdk/otel'); + return (metadata) => + new LegacyOpenTelemetry({ tracer: createMetadataEnrichedTracer(tracer, metadata) }); } /** @@ -128,7 +181,7 @@ async function createOtlpTracer(endpoint: string): Promise<{ * * Use `.tracer()` with a pre-built integration (e.g. `LangSmithTelemetry`, * `integrations.langsmith()`) or `.otlpEndpoint()` for a generic OTLP - * collector. Add AI SDK `TelemetryIntegration` hooks via `.integration()`. + * collector. Add AI SDK `Telemetry` hooks via `.integration()`. * * @example * ```typescript @@ -157,7 +210,7 @@ export class Telemetry { protected redactFn?: RedactFn; - protected integrationsList: TelemetryIntegration[] = []; + protected integrationsList: AiSdkTelemetry[] = []; protected tracerValue?: OpaqueTracer; @@ -242,7 +295,7 @@ export class Telemetry { } /** Add a telemetry integration (e.g. for observability platforms). */ - integration(value: TelemetryIntegration): this { + integration(value: AiSdkTelemetry): this { this.integrationsList.push(value); return this; } @@ -285,9 +338,26 @@ export class Telemetry { provider = otlp.provider; } - const integrations = this.redactFn - ? this.integrationsList.map((i) => wrapIntegrationWithRedaction(i, this.redactFn!)) + const redactFn = this.redactFn; + const customIntegrations = redactFn + ? this.integrationsList.map((integration) => + wrapIntegrationWithRedaction(integration, redactFn), + ) : [...this.integrationsList]; + let resolveIntegrations: BuiltTelemetry['resolveIntegrations']; + let integrations = customIntegrations; + if (tracer !== undefined) { + const createOpenTelemetryIntegration = + await createAiSdkOpenTelemetryIntegrationFactory(tracer); + resolveIntegrations = (metadata) => { + const integration = createOpenTelemetryIntegration(metadata); + return [ + redactFn ? wrapIntegrationWithRedaction(integration, redactFn) : integration, + ...customIntegrations, + ]; + }; + integrations = resolveIntegrations(this.metadataValue); + } return { enabled: this.enabledValue, @@ -297,6 +367,7 @@ export class Telemetry { recordOutputs: this.recordOutputsValue, runtimeRootSpanEnabled: this.runtimeRootSpanEnabledValue, integrations, + ...(resolveIntegrations && { resolveIntegrations }), tracer, provider, credentialName: this.credentialNameValue, diff --git a/packages/@n8n/agents/src/types/index.ts b/packages/@n8n/agents/src/types/index.ts index 322c9d122ae..c02c922a9f7 100644 --- a/packages/@n8n/agents/src/types/index.ts +++ b/packages/@n8n/agents/src/types/index.ts @@ -7,7 +7,10 @@ export type { ContentCitation, ContentText, ContentReasoning, + ContentReasoningFile, ContentFile, + ContentFileRef, + ContentCustom, ContentToolCall, ContentInvalidToolCall, ContentProvider, @@ -18,6 +21,10 @@ export type { AgentDbMessage, } from './sdk/message'; +export { stripHydratedFileData } from './sdk/message'; + +export type { BuiltFileStore } from './sdk/file-store'; + export type { Provider, AnthropicThinkingConfig, @@ -27,6 +34,7 @@ export type { ThinkingConfigFor, ThinkingConfig, } from './sdk/provider'; +export type { ReasoningLevel } from './sdk/reasoning'; export type { AgentResult, @@ -174,6 +182,8 @@ export type { AgentEventData, AgentEventHandler, AgentMiddleware, + ForwardedChildChunk, + SubAgentChunkPayload, } from './runtime/event'; export type { diff --git a/packages/@n8n/agents/src/types/runtime/event.ts b/packages/@n8n/agents/src/types/runtime/event.ts index 228138787b4..69d616698ef 100644 --- a/packages/@n8n/agents/src/types/runtime/event.ts +++ b/packages/@n8n/agents/src/types/runtime/event.ts @@ -19,7 +19,7 @@ export interface SubAgentStartedPayload extends SubAgentLifecycleBase { } export interface SubAgentCompletedPayload extends SubAgentLifecycleBase { - status: 'completed' | 'failed' | 'suspended'; + status: 'completed' | 'failed' | 'suspended' | 'cancelled'; startedAt: number; finishedAt: number; durationMs: number; @@ -33,6 +33,35 @@ export interface SubAgentCompletedPayload extends SubAgentLifecycleBase { error?: string; } +/** + * Child stream chunks that may be forwarded live to the parent chat while a + * delegation runs. Args/results and nested sub-agent lifecycle are excluded + * so fan-out stays bounded. + */ +export type ForwardedChildChunk = + | { type: 'text-delta'; id: string; delta: string } + | { type: 'reasoning-start'; id: string } + | { type: 'reasoning-delta'; id: string; delta: string } + | { type: 'reasoning-end'; id: string } + | { type: 'tool-input-start'; toolCallId: string; toolName: string } + | { + type: 'tool-execution-start'; + toolCallId: string; + toolName: string; + startTime: number; + } + | { + type: 'tool-execution-end'; + toolCallId: string; + toolName: string; + isError: boolean; + endTime: number; + }; + +export interface SubAgentChunkPayload extends SubAgentLifecycleBase { + chunk: ForwardedChildChunk; +} + export const enum AgentEvent { AgentStart = 'agent_start', AgentEnd = 'agent_end', @@ -42,6 +71,7 @@ export const enum AgentEvent { ToolExecutionEnd = 'tool_execution_end', SubAgentStarted = 'subagent_started', SubAgentCompleted = 'subagent_completed', + SubAgentChunk = 'subagent_chunk', Error = 'error', } @@ -60,6 +90,7 @@ export type AgentEventData = } | ({ type: AgentEvent.SubAgentStarted } & SubAgentStartedPayload) | ({ type: AgentEvent.SubAgentCompleted } & SubAgentCompletedPayload) + | ({ type: AgentEvent.SubAgentChunk } & SubAgentChunkPayload) | { type: AgentEvent.Error; message: string; diff --git a/packages/@n8n/agents/src/types/sdk/agent-builder.ts b/packages/@n8n/agents/src/types/sdk/agent-builder.ts index 87d2d8309b5..a1c64dede94 100644 --- a/packages/@n8n/agents/src/types/sdk/agent-builder.ts +++ b/packages/@n8n/agents/src/types/sdk/agent-builder.ts @@ -2,6 +2,7 @@ import type { ExecutionOptions, ModelConfig } from './agent'; import type { BuiltEval } from './eval'; import type { BuiltGuardrail } from './guardrail'; import type { CheckpointStore } from './memory'; +import type { ReasoningLevel } from './reasoning'; import type { BuiltProviderTool, BuiltTool } from './tool'; import type { ScopedMemoryTaskEvent } from '../../runtime/memory/scoped-memory-task-runner'; import type { RuntimeSkill, RuntimeSkillSource } from '../../skills'; @@ -23,8 +24,10 @@ export interface AgentBuilder { skills(sourceOrSkills: RuntimeSkillSource | RuntimeSkill[]): this; providerTool(t: BuiltProviderTool): this; thinking(provider: string, config?: Record): this; + reasoning(level?: ReasoningLevel): this; toolCallConcurrency(n: number): this; memory(m: unknown): this; + fileStore(store: unknown): this; memoryTaskObserver(observer: (event: ScopedMemoryTaskEvent) => void): this; checkpoint(storage: 'memory' | CheckpointStore): this; inputGuardrail(g: BuiltGuardrail): this; diff --git a/packages/@n8n/agents/src/types/sdk/agent.ts b/packages/@n8n/agents/src/types/sdk/agent.ts index 80c565dbaee..c8c5c5e24be 100644 --- a/packages/@n8n/agents/src/types/sdk/agent.ts +++ b/packages/@n8n/agents/src/types/sdk/agent.ts @@ -1,5 +1,10 @@ import type { ProviderOptions } from '@ai-sdk/provider-utils'; -import type { LanguageModel, OnStepFinishEvent, OnStepStartEvent, smoothStream } from 'ai'; +import type { + GenerateTextStepEndEvent, + GenerateTextStepStartEvent, + LanguageModel, + smoothStream, +} from 'ai'; import type { JsonSchema7Type } from 'zod-to-json-schema'; import type { AgentMessage, ContentMetadata } from './message'; @@ -7,6 +12,7 @@ import type { ProviderId, ProviderCredentials } from '../../runtime/model/provid import type { AgentEvent, AgentEventHandler, + SubAgentChunkPayload, SubAgentCompletedPayload, SubAgentStartedPayload, } from '../runtime/event'; @@ -91,7 +97,7 @@ export type StreamChunk = ContentMetadata & | { /** * Emitted just before a tool handler starts executing. Bridged from - * the runtime event bus (not part of the AI SDK fullStream). Pairs + * the runtime event bus (not part of the AI SDK stream). Pairs * with the subsequent `tool-result` to let consumers show a * mid-flight indicator between "LLM picked a tool" and "result arrived". */ @@ -137,6 +143,7 @@ export type StreamChunk = ContentMetadata & | { type: 'message'; message: AgentMessage } | ({ type: 'subagent-started' } & SubAgentStartedPayload) | ({ type: 'subagent-completed' } & SubAgentCompletedPayload) + | ({ type: 'subagent-chunk' } & SubAgentChunkPayload) | { type: 'finish'; finishReason: FinishReason; @@ -185,8 +192,10 @@ export interface ExecutionOptions { telemetry?: BuiltTelemetry; /** Inherited execution counter from the host runtime. Used for aggregate heartbeat telemetry. */ executionCounter?: AgentExecutionCounter; - onStepStart?: (event: OnStepStartEvent) => void | Promise; - onStepFinish?: (event: OnStepFinishEvent) => void | Promise; + onStepStart?: (event: GenerateTextStepStartEvent) => void | Promise; + onStepEnd?: (event: GenerateTextStepEndEvent) => void | Promise; + /** @deprecated Use `onStepEnd` instead. */ + onStepFinish?: (event: GenerateTextStepEndEvent) => void | Promise; /** * Durable-log RFC (resilience phase), opt-in: persist a `running`-status * checkpoint at every step boundary (after a tool batch settles, before the diff --git a/packages/@n8n/agents/src/types/sdk/file-store.ts b/packages/@n8n/agents/src/types/sdk/file-store.ts new file mode 100644 index 00000000000..fddfec1d03b --- /dev/null +++ b/packages/@n8n/agents/src/types/sdk/file-store.ts @@ -0,0 +1,26 @@ +import type { ContentFileRef } from './message'; + +/** + * Host-injected store that resolves `ContentFileRef` references to bytes. + * + * File content parts are persisted by reference only; before each LLM call + * the runtime hydrates supported parts through this store (see + * `hydrateFileParts`). Parts left without `data` — unsupported media type, + * unknown reference, or load failure — are presented to the model as a text + * metadata line instead of a file part. + */ +export interface BuiltFileStore { + /** + * Resolve a file reference to its bytes. Returns `null` when the file is + * unknown or deleted. `scope.threadId` is the run's conversation thread + * (when the run has one) — stores should restrict resolution to it so a + * reference can't read files from another conversation. + */ + load(ref: ContentFileRef, scope?: { threadId?: string }): Promise; + + /** + * Whether the current model accepts this media type as a file part. + * When omitted, every media type is hydrated. + */ + isMediaTypeSupported?(mediaType: string | undefined): boolean; +} diff --git a/packages/@n8n/agents/src/types/sdk/message.ts b/packages/@n8n/agents/src/types/sdk/message.ts index 6e62defd2cb..5ea64dcb92b 100644 --- a/packages/@n8n/agents/src/types/sdk/message.ts +++ b/packages/@n8n/agents/src/types/sdk/message.ts @@ -9,8 +9,10 @@ export type MessageContent = | ContentToolCall | ContentInvalidToolCall | ContentReasoning + | ContentReasoningFile | ContentFile | ContentCitation + | ContentCustom | ContentProvider; export interface ContentMetadata { @@ -62,6 +64,17 @@ export type ContentReasoning = ContentMetadata & { text: string; }; +/** + * Reference to file bytes held in a host-provided store (see `BuiltFileStore`). + * Only the reference is persisted. + */ +export interface ContentFileRef { + /** Stable identifier resolvable by the injected file store. */ + id: string; + fileName?: string; + sizeBytes?: number; +} + export type ContentFile = ContentMetadata & { type: 'file'; @@ -79,8 +92,25 @@ export type ContentFile = ContentMetadata & { * If the API returns base64 encoded strings, the file data should be returned * as base64 encoded strings. If the API returns binary data, the file data should * be returned as binary data. + * + * Absent on reference-only parts (`fileRef` set); hydrated by the runtime + * before LLM calls. */ - data: Uint8Array | ArrayBuffer | Buffer | string; + data?: Uint8Array | ArrayBuffer | Buffer | string; + + /** External-store reference for the file bytes. At least one of `data` / `fileRef` must be set. */ + fileRef?: ContentFileRef; +}; + +export type ContentReasoningFile = ContentMetadata & { + type: 'reasoning-file'; + data: ContentFile['data']; + mediaType: string; +}; + +export type ContentCustom = ContentMetadata & { + type: 'custom'; + kind: `${string}.${string}`; }; export type ContentToolCall = ContentMetadata & { @@ -189,3 +219,26 @@ export type AgentMessage = Message | CustomAgentMessage; * cursors; both fields are populated on read by every backend. */ export type AgentDbMessage = { id: string; createdAt: Date } & AgentMessage; + +/** + * Return a copy of the message with hydrated bytes removed from file parts + * that carry a `fileRef`. Persistence backends must apply this before + * serializing message content, so stored messages hold only references. + * Parts without a `fileRef` keep their inline `data`. Non-content messages + * and messages without hydrated file parts are returned as-is. + */ +export function stripHydratedFileData(message: T): T { + if (!('content' in message) || !Array.isArray(message.content)) return message; + + let changed = false; + const content = message.content.map((block) => { + if (block.type === 'file' && block.fileRef && block.data !== undefined) { + changed = true; + const { data: _data, ...rest } = block; + return rest; + } + return block; + }); + + return changed ? { ...message, content } : message; +} diff --git a/packages/@n8n/agents/src/types/sdk/reasoning.ts b/packages/@n8n/agents/src/types/sdk/reasoning.ts new file mode 100644 index 00000000000..933699ffda6 --- /dev/null +++ b/packages/@n8n/agents/src/types/sdk/reasoning.ts @@ -0,0 +1,4 @@ +import type { LanguageModelCallOptions } from 'ai'; + +/** Provider-agnostic reasoning level understood by AI SDK 7. */ +export type ReasoningLevel = NonNullable; diff --git a/packages/@n8n/agents/src/types/telemetry.ts b/packages/@n8n/agents/src/types/telemetry.ts index fa974888bf8..42a7a9a0417 100644 --- a/packages/@n8n/agents/src/types/telemetry.ts +++ b/packages/@n8n/agents/src/types/telemetry.ts @@ -1,4 +1,4 @@ -import type { TelemetryIntegration } from 'ai'; +import type { Telemetry as AiSdkTelemetry } from 'ai'; /** OTel-compatible attribute values (no undefined). */ export type AttributeValue = string | number | boolean | string[] | number[] | boolean[]; @@ -43,7 +43,11 @@ export interface BuiltTelemetry { */ readonly rootAnchored?: boolean; /** Integrations are pre-wrapped with redaction if .redact() was set at build time. */ - readonly integrations: TelemetryIntegration[]; + readonly integrations: AiSdkTelemetry[]; + /** @internal Rebuild metadata-sensitive integrations for the effective run metadata. */ + readonly resolveIntegrations?: ( + metadata: Record | undefined, + ) => AiSdkTelemetry[]; readonly tracer?: OpaqueTracer; /** @internal Provider reference for flush/shutdown. Only set when .otlpEndpoint() is used. */ readonly provider?: OpaqueTracerProvider; diff --git a/packages/@n8n/ai-utilities/src/node-catalog/__tests__/get-node-type-definition.test.ts b/packages/@n8n/ai-utilities/src/node-catalog/__tests__/get-node-type-definition.test.ts index 8b7bbec99f3..078f50cfc77 100644 --- a/packages/@n8n/ai-utilities/src/node-catalog/__tests__/get-node-type-definition.test.ts +++ b/packages/@n8n/ai-utilities/src/node-catalog/__tests__/get-node-type-definition.test.ts @@ -2,7 +2,7 @@ import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from 'node:fs'; import { tmpdir } from 'node:os'; import { join } from 'node:path'; -import { getNodeTypeDefinition } from '../get'; +import { getNodeTypeDefinition, versionDirToNumber } from '../get'; describe('getNodeTypeDefinition — split-node discriminator handling', () => { let defsDir: string; @@ -57,3 +57,58 @@ describe('getNodeTypeDefinition — split-node discriminator handling', () => { expect(result.error).toContain('message (post, update)'); }); }); + +describe('versionDirToNumber', () => { + it('reads a two-digit suffix as major.minor, not a plain integer', () => { + // The bug: v22 (2.2) must rank below v3 (3), not above it. + expect(versionDirToNumber('v3')).toBeGreaterThan(versionDirToNumber('v22')); + expect(versionDirToNumber('v22')).toBe(2.2); + expect(versionDirToNumber('v21')).toBe(2.1); + expect(versionDirToNumber('v3')).toBe(3); + expect(versionDirToNumber('v1')).toBe(1); + }); + + it('leaves the known two-digit-major limitation documented', () => { + // v10 is ambiguous under the dot-dropping encoding and reads as 1.0; + // no node is near a two-digit major, so this is an accepted limitation. + expect(versionDirToNumber('v10')).toBe(1); + }); +}); + +describe('getNodeTypeDefinition — latest version resolution', () => { + let defsDir: string; + + beforeAll(() => { + defsDir = mkdtempSync(join(tmpdir(), 'ai-utils-node-defs-versions-')); + // Notion is a split (resource/operation) node shipping versions + // 1, 2, 2.1, 2.2 and 3, so on disk its version dirs are + // v1, v2, v21, v22, v3 (the dot is dropped when naming). + const notionDir = join(defsDir, 'nodes', 'n8n-nodes-base', 'notion'); + for (const [version, content] of [ + ['v1', '// notion v1 get def'], + ['v2', '// notion v2 get def'], + ['v21', '// notion v2.1 get def'], + ['v22', '// notion v2.2 get def'], + ['v3', '// notion v3 get def'], + ]) { + const opDir = join(notionDir, version, 'resource_database_page'); + mkdirSync(opDir, { recursive: true }); + writeFileSync(join(opDir, 'operation_get.ts'), content); + } + }); + + afterAll(() => { + rmSync(defsDir, { recursive: true, force: true }); + }); + + it('treats v3 as latest, not v2.2, when no version is requested', () => { + const result = getNodeTypeDefinition('n8n-nodes-base.notion', undefined, [defsDir], { + resource: 'databasePage', + operation: 'get', + }); + + expect(result.error).toBeUndefined(); + expect(result.version).toBe('v3'); + expect(result.content).toBe('// notion v3 get def'); + }); +}); diff --git a/packages/@n8n/ai-utilities/src/node-catalog/get.ts b/packages/@n8n/ai-utilities/src/node-catalog/get.ts index 2787e80b58a..7cd8d263f65 100644 --- a/packages/@n8n/ai-utilities/src/node-catalog/get.ts +++ b/packages/@n8n/ai-utilities/src/node-catalog/get.ts @@ -179,6 +179,25 @@ export function parseNodeId(nodeId: string): { packageName: string; nodeName: st }; } +/** + * Turn a `v{digits}` directory name into a comparable numeric version. + * + * Version dirs drop the dot (2.2 -> v22, 3.1 -> v31, 3 -> v3), so a naive + * `parseInt` reads v22 as 22 and ranks it above v3. A two-digit suffix is a + * major.minor pair; anything else is the number as-is. + * + * The encoding stays ambiguous for a two-digit major (a future `v10` reads as + * 1.0), but no node is anywhere near that, and this matches the `parseRequestedVersion` + * convention used elsewhere. Fixing it properly means an unambiguous dir name at generation time. + */ +export function versionDirToNumber(versionDir: string): number { + const digits = versionDir.slice(1); + if (/^\d{2}$/.test(digits)) { + return Number(`${digits[0]}.${digits[1]}`); + } + return Number.parseFloat(digits); +} + /** * Get available versions for a node * Returns array of version strings like ['v34', 'v2'] sorted by version descending @@ -220,11 +239,7 @@ function getNodeVersions(nodeId: string, nodeDefinitionDirs?: string[]): string[ } // Sort by numeric version descending - versions.sort((a, b) => { - const aNum = parseInt(a.slice(1), 10); - const bNum = parseInt(b.slice(1), 10); - return bNum - aNum; - }); + versions.sort((a, b) => versionDirToNumber(b) - versionDirToNumber(a)); return versions; } catch { diff --git a/packages/@n8n/ai-utilities/src/node-catalog/index.ts b/packages/@n8n/ai-utilities/src/node-catalog/index.ts index 4eac9fe83bc..175791a638c 100644 --- a/packages/@n8n/ai-utilities/src/node-catalog/index.ts +++ b/packages/@n8n/ai-utilities/src/node-catalog/index.ts @@ -26,6 +26,7 @@ export { validatePathWithinBase, parseNodeId, toSnakeCase, + versionDirToNumber, } from './get'; export type { NodeRequest, CodeBuilderGetToolOptions } from './get'; diff --git a/packages/@n8n/ai-utilities/src/web-search/__tests__/brave-search.test.ts b/packages/@n8n/ai-utilities/src/web-search/__tests__/brave-search.test.ts index a0f2218a231..aee7b25de2a 100644 --- a/packages/@n8n/ai-utilities/src/web-search/__tests__/brave-search.test.ts +++ b/packages/@n8n/ai-utilities/src/web-search/__tests__/brave-search.test.ts @@ -113,16 +113,61 @@ describe('braveSearch', () => { expect(q).toBe('webhooks -site:reddit.com'); }); - it('throws on non-OK response', async () => { + it('throws on non-OK response without retrying', async () => { mockFetch.mockResolvedValue({ ok: false, - status: 429, - statusText: 'Too Many Requests', + status: 401, + statusText: 'Unauthorized', }); await expect(braveSearch('BSA-key', 'test', {})).rejects.toThrow( - 'Brave search failed: 429 Too Many Requests', + 'Brave search failed: 401 Unauthorized', ); + expect(mockFetch).toHaveBeenCalledTimes(1); + }); + + it('retries a rate-limited request and returns the eventual success', async () => { + mockFetch + .mockResolvedValueOnce({ ok: false, status: 429, statusText: 'Too Many Requests' }) + .mockResolvedValueOnce({ ok: true, json: async () => MOCK_BRAVE_RESPONSE }); + + const result = await braveSearch('BSA-key', 'test', {}); + + expect(mockFetch).toHaveBeenCalledTimes(2); + expect(result.results).toHaveLength(2); + }); + + it('stops waiting out the retry backoff when the search is aborted', async () => { + vi.useFakeTimers(); + try { + const controller = new AbortController(); + mockFetch.mockImplementation(async (_url: string, init: RequestInit) => { + if (init.signal?.aborted) throw new Error('aborted'); + controller.abort(); + return { ok: false, status: 429, statusText: 'Too Many Requests' }; + }); + + const search = braveSearch('BSA-key', 'test', { abortSignal: controller.signal }); + + // No timer is advanced — only an abort-aware backoff lets this settle. + await expect(search).rejects.toThrow('aborted'); + expect(mockFetch).toHaveBeenCalledTimes(2); + } finally { + vi.useRealTimers(); + } + }); + + it('gives up after the retry budget and throws the last status', async () => { + mockFetch.mockResolvedValue({ + ok: false, + status: 503, + statusText: 'Service Unavailable', + }); + + await expect(braveSearch('BSA-key', 'test', {})).rejects.toThrow( + 'Brave search failed: 503 Service Unavailable', + ); + expect(mockFetch).toHaveBeenCalledTimes(3); }); it('handles empty results gracefully', async () => { diff --git a/packages/@n8n/ai-utilities/src/web-search/brave-search.ts b/packages/@n8n/ai-utilities/src/web-search/brave-search.ts index 6a93a276064..c7e4008a5f0 100644 --- a/packages/@n8n/ai-utilities/src/web-search/brave-search.ts +++ b/packages/@n8n/ai-utilities/src/web-search/brave-search.ts @@ -3,6 +3,10 @@ import type { WebSearchOptions, WebSearchResponse } from './types'; const BRAVE_SEARCH_PATH = '/res/v1/web/search'; const BRAVE_SEARCH_URL = `https://api.search.brave.com${BRAVE_SEARCH_PATH}`; +/** Brave rate-limits per second — retry so a burst of searches doesn't fail the caller. */ +const MAX_ATTEMPTS = 3; +const RETRY_BASE_MS = 250; + interface BraveWebResult { title: string; url: string; @@ -60,10 +64,41 @@ export async function braveSearch( ...(proxyHeaders ?? { 'X-Subscription-Token': apiKey }), }; - const response = await fetch(`${baseUrl}?${params}`, { - headers, - ...(options.abortSignal ? { signal: options.abortSignal } : {}), - }); + const runSearch = async () => + await fetch(`${baseUrl}?${params}`, { + headers, + ...(options.abortSignal ? { signal: options.abortSignal } : {}), + }); + const isRetryable = (status: number) => status === 429 || status >= 500; + /** Abort-aware so a cancelled run doesn't sit out the delay; the retried fetch + * then rejects on the aborted signal. */ + const backoff = async (ms: number) => + await new Promise((resolve) => { + const signal = options.abortSignal; + if (signal?.aborted) { + resolve(); + return; + } + const timer = setTimeout(() => { + signal?.removeEventListener('abort', onAbort); + resolve(); + }, ms); + function onAbort() { + clearTimeout(timer); + resolve(); + } + signal?.addEventListener('abort', onAbort, { once: true }); + }); + + let response = await runSearch(); + for ( + let attempt = 1; + attempt < MAX_ATTEMPTS && !response.ok && isRetryable(response.status); + attempt++ + ) { + await backoff(RETRY_BASE_MS * 2 ** (attempt - 1)); + response = await runSearch(); + } if (!response.ok) { throw new Error(`Brave search failed: ${response.status} ${response.statusText}`); diff --git a/packages/@n8n/ai-workflow-builder.ee/src/code-builder/constants.ts b/packages/@n8n/ai-workflow-builder.ee/src/code-builder/constants.ts index 7384a0e49e1..738eb3263e1 100644 --- a/packages/@n8n/ai-workflow-builder.ee/src/code-builder/constants.ts +++ b/packages/@n8n/ai-workflow-builder.ee/src/code-builder/constants.ts @@ -115,8 +115,8 @@ export const CODE_BUILDER_GET_SUGGESTED_NODES_TOOL: BuilderToolBase = { /** MCP tools — not used by the code-builder agent, only exposed via the MCP server. */ export const MCP_GET_SDK_REFERENCE_TOOL: BuilderToolBase = { - toolName: 'get_sdk_reference', - displayTitle: 'Getting SDK reference', + toolName: 'get_workflow_sdk_reference', + displayTitle: 'Getting workflow SDK reference', }; export const MCP_CREATE_WORKFLOW_FROM_CODE_TOOL: BuilderToolBase = { diff --git a/packages/@n8n/api-types/src/__tests__/workflow-publish-blocked-details.test.ts b/packages/@n8n/api-types/src/__tests__/workflow-publish-blocked-details.test.ts new file mode 100644 index 00000000000..71100f56573 --- /dev/null +++ b/packages/@n8n/api-types/src/__tests__/workflow-publish-blocked-details.test.ts @@ -0,0 +1,56 @@ +import { SourceControlledFileSchema } from '../schemas/source-controlled-file.schema'; +import { + isWorkflowPublishBlockedDetails, + workflowPublishBlockedDetailsSchema, +} from '../workflow-publish-blocked-details'; + +describe('workflow publication blocker details', () => { + test.each(['review_pending', 'changes_requested'] as const)( + 'accepts the "%s" reason with a review request ID', + (reason) => { + const details = { + reason, + workflowReviewRequestId: 'review-1', + }; + + expect(workflowPublishBlockedDetailsSchema.parse(details)).toEqual(details); + expect(isWorkflowPublishBlockedDetails(details)).toBe(true); + }, + ); + + test.each([ + undefined, + {}, + { reason: 'approved', workflowReviewRequestId: 'review-1' }, + { reason: 'review_pending', workflowReviewRequestId: '' }, + { + reason: 'review_pending', + workflowReviewRequestId: 'review-1', + validationError: true, + }, + ])('rejects malformed blocker details: %j', (details) => { + expect(isWorkflowPublishBlockedDetails(details)).toBe(false); + }); + + test('keeps blocker details in a source-control workflow status', () => { + const result = SourceControlledFileSchema.parse({ + file: 'workflow.json', + id: 'workflow-1', + name: 'Reviewed workflow', + type: 'workflow', + status: 'modified', + location: 'remote', + conflict: false, + updatedAt: '2026-07-29T00:00:00.000Z', + publishingErrorDetails: { + reason: 'review_pending', + workflowReviewRequestId: 'review-1', + }, + }); + + expect(result.publishingErrorDetails).toEqual({ + reason: 'review_pending', + workflowReviewRequestId: 'review-1', + }); + }); +}); diff --git a/packages/@n8n/api-types/src/agent-builder-tool-node-types.ts b/packages/@n8n/api-types/src/agent-builder-tool-node-types.ts index 97b8bc5d799..4534f03071f 100644 --- a/packages/@n8n/api-types/src/agent-builder-tool-node-types.ts +++ b/packages/@n8n/api-types/src/agent-builder-tool-node-types.ts @@ -4,16 +4,25 @@ import { WORKFLOW_TOOL_LANGCHAIN_NODE_TYPE, } from 'n8n-workflow'; -export const AGENT_BUILDER_HIDDEN_AVAILABLE_TOOL_NODE_TYPES: readonly string[] = [ - ...AI_VENDOR_NODE_TYPES.map((nodeType) => `${nodeType}Tool`), - CHAT_TOOL_NODE_TYPE, - // Agents call workflows via native workflow tools, not the sub-workflow node - WORKFLOW_TOOL_LANGCHAIN_NODE_TYPE, -]; - -export const AGENT_BUILDER_AVAILABLE_AI_UTILITY_TOOL_NODE_TYPES = [ +const AGENT_BUILDER_AI_UTILITY_TOOL_NODE_TYPES = [ 'toolCalculator', 'toolThink', '@n8n/n8n-nodes-langchain.toolCalculator', '@n8n/n8n-nodes-langchain.toolThink', ] as const; + +/** + * Single source of truth for tools the agent builder must not offer: both the + * tools picker and the builder agent's node discovery consult it, so the two + * cannot drift apart. + */ +export const AGENT_BUILDER_HIDDEN_AVAILABLE_TOOL_NODE_TYPES: readonly string[] = [ + ...AI_VENDOR_NODE_TYPES.map((nodeType) => `${nodeType}Tool`), + CHAT_TOOL_NODE_TYPE, + // Agents call workflows via native workflow tools, not the sub-workflow node + WORKFLOW_TOOL_LANGCHAIN_NODE_TYPE, + // Reasoning helpers an agent does not need + ...AGENT_BUILDER_AI_UTILITY_TOOL_NODE_TYPES, + // Provider nodes: an agent already has its own model + ...AI_VENDOR_NODE_TYPES, +]; diff --git a/packages/@n8n/api-types/src/agent-sse.ts b/packages/@n8n/api-types/src/agent-sse.ts index 4df76cee85f..5173bbdee20 100644 --- a/packages/@n8n/api-types/src/agent-sse.ts +++ b/packages/@n8n/api-types/src/agent-sse.ts @@ -47,6 +47,31 @@ export interface AgentSseMessage { content: AgentPersistedMessageContentPart[]; } +/** + * Child stream chunks forwarded live while a `delegate_subagent` tool runs. + * Structural mirror of `@n8n/agents` `ForwardedChildChunk` (api-types cannot + * import from that package). + */ +export type ForwardedChildChunkWire = + | { type: 'text-delta'; id: string; delta: string } + | { type: 'reasoning-start'; id: string } + | { type: 'reasoning-delta'; id: string; delta: string } + | { type: 'reasoning-end'; id: string } + | { type: 'tool-input-start'; toolCallId: string; toolName: string } + | { + type: 'tool-execution-start'; + toolCallId: string; + toolName: string; + startTime: number; + } + | { + type: 'tool-execution-end'; + toolCallId: string; + toolName: string; + isError: boolean; + endTime: number; + }; + export type AgentSseEvent = | { type: 'start-step' } | { type: 'finish-step' } @@ -94,6 +119,17 @@ export type AgentSseEvent = canceled?: boolean; } | { type: 'tool-call-suspended'; payload: ToolSuspendedPayload } + | { + /** + * Live progress from a delegated child agent. Correlated to the parent + * `delegate_subagent` tool call via `parentToolCallId`. Ephemeral — + * not persisted or replayed from history. + */ + type: 'subagent-chunk'; + parentToolCallId: string; + taskPath: string; + chunk: ForwardedChildChunkWire; + } | { type: 'message'; message: AgentSseMessage } | { /** A warning message from the MCP server when it fails to connect or initialize. */ diff --git a/packages/@n8n/api-types/src/agents/__tests__/agent-chat-message.dto.test.ts b/packages/@n8n/api-types/src/agents/__tests__/agent-chat-message.dto.test.ts new file mode 100644 index 00000000000..7755cae118f --- /dev/null +++ b/packages/@n8n/api-types/src/agents/__tests__/agent-chat-message.dto.test.ts @@ -0,0 +1,63 @@ +import { MAX_AGENT_CHAT_ATTACHMENTS_PER_MESSAGE } from '../agent-chat-attachments.constants'; +import { AgentChatMessageDto } from '../dto'; + +describe('AgentChatMessageDto', () => { + const attachment = { + fileName: 'photo.png', + mimeType: 'image/png', + data: Buffer.from([1, 2, 3]).toString('base64'), + }; + + it('accepts a message with attachments', () => { + const result = AgentChatMessageDto.safeParse({ + message: 'look', + attachments: [attachment], + }); + expect(result.success).toBe(true); + }); + + it('accepts an attachment-only payload with empty message text', () => { + const result = AgentChatMessageDto.safeParse({ message: '', attachments: [attachment] }); + expect(result.success).toBe(true); + }); + + it('rejects a payload with neither message text nor attachments', () => { + expect(AgentChatMessageDto.safeParse({ message: '' }).success).toBe(false); + expect(AgentChatMessageDto.safeParse({ message: ' ' }).success).toBe(false); + expect(AgentChatMessageDto.safeParse({ message: ' ', attachments: [] }).success).toBe(false); + }); + + it('rejects base64 data above the 10 MB cap', () => { + const result = AgentChatMessageDto.safeParse({ + message: '', + attachments: [{ ...attachment, data: 'a'.repeat(14_000_001) }], + }); + expect(result.success).toBe(false); + }); + + it('rejects more attachments than the per-message cap', () => { + const result = AgentChatMessageDto.safeParse({ + message: '', + attachments: Array.from( + { length: MAX_AGENT_CHAT_ATTACHMENTS_PER_MESSAGE + 1 }, + () => attachment, + ), + }); + expect(result.success).toBe(false); + }); + + it('rejects attachments with empty fileName or mimeType', () => { + expect( + AgentChatMessageDto.safeParse({ + message: '', + attachments: [{ ...attachment, fileName: '' }], + }).success, + ).toBe(false); + expect( + AgentChatMessageDto.safeParse({ + message: '', + attachments: [{ ...attachment, mimeType: '' }], + }).success, + ).toBe(false); + }); +}); diff --git a/packages/@n8n/api-types/src/agents/__tests__/agent-json-config.schema.test.ts b/packages/@n8n/api-types/src/agents/__tests__/agent-json-config.schema.test.ts index 47058cf9150..ab958692ae9 100644 --- a/packages/@n8n/api-types/src/agents/__tests__/agent-json-config.schema.test.ts +++ b/packages/@n8n/api-types/src/agents/__tests__/agent-json-config.schema.test.ts @@ -10,6 +10,31 @@ const minimalConfig = { instructions: 'Help the user.', }; +describe('AgentJsonConfigSchema — reasoning', () => { + it.each(['low', 'medium', 'high'] as const)( + 'preserves generic %s reasoning effort', + (reasoning) => { + const result = AgentJsonConfigSchema.safeParse({ + ...minimalConfig, + config: { reasoning }, + }); + + expect(result.success).toBe(true); + if (!result.success) return; + expect(result.data.config?.reasoning).toBe(reasoning); + }, + ); + + it('rejects reasoning levels outside the agent config options', () => { + const result = AgentJsonConfigSchema.safeParse({ + ...minimalConfig, + config: { reasoning: 'max' }, + }); + + expect(result.success).toBe(false); + }); +}); + describe('AgentJsonConfigSchema — tools', () => { describe('custom tool id field', () => { it('accepts a valid alphanumeric id', () => { diff --git a/packages/@n8n/api-types/src/agents/__tests__/child-trace.test.ts b/packages/@n8n/api-types/src/agents/__tests__/child-trace.test.ts new file mode 100644 index 00000000000..f96b55ddc2d --- /dev/null +++ b/packages/@n8n/api-types/src/agents/__tests__/child-trace.test.ts @@ -0,0 +1,55 @@ +import { describe, expect, it, vi } from 'vitest'; + +import { applyForwardedChildChunk, emptyChildTrace, settleChildTrace } from '../child-trace'; + +describe('applyForwardedChildChunk', () => { + it('accumulates text, reasoning and tool steps from a realistic chunk sequence', () => { + vi.useFakeTimers(); + vi.setSystemTime(1_000); + const trace = emptyChildTrace(); + + applyForwardedChildChunk(trace, { type: 'text-delta', id: 't-1', delta: 'Hello ' }); + applyForwardedChildChunk(trace, { type: 'text-delta', id: 't-1', delta: 'world' }); + applyForwardedChildChunk(trace, { type: 'reasoning-delta', id: 'r-1', delta: 'Think ' }); + applyForwardedChildChunk(trace, { type: 'reasoning-delta', id: 'r-1', delta: 'hard' }); + applyForwardedChildChunk(trace, { type: 'reasoning-end', id: 'r-1' }); + applyForwardedChildChunk(trace, { + type: 'tool-input-start', + toolCallId: 'child-tc-1', + toolName: 'web_search', + }); + applyForwardedChildChunk(trace, { + type: 'tool-execution-end', + toolCallId: 'child-tc-1', + toolName: 'web_search', + isError: false, + endTime: 2_000, + }); + + expect(trace.text).toBe('Hello world'); + expect(trace.reasoningSegments).toHaveLength(1); + expect(trace.reasoningSegments[0]).toMatchObject({ + id: 'r-1', + content: 'Think hard', + endTime: 1_000, + }); + expect(trace.steps).toEqual([ + { toolCallId: 'child-tc-1', toolName: 'web_search', running: false }, + ]); + vi.useRealTimers(); + }); +}); + +describe('settleChildTrace', () => { + it('clears residual running flags on every step', () => { + const trace = emptyChildTrace(); + trace.steps.push( + { toolCallId: 'a', toolName: 'one', running: true }, + { toolCallId: 'b', toolName: 'two', running: true }, + ); + + settleChildTrace(trace); + + expect(trace.steps.every((step) => !step.running)).toBe(true); + }); +}); diff --git a/packages/@n8n/api-types/src/agents/agent-chat-attachments.constants.ts b/packages/@n8n/api-types/src/agents/agent-chat-attachments.constants.ts new file mode 100644 index 00000000000..10703990093 --- /dev/null +++ b/packages/@n8n/api-types/src/agents/agent-chat-attachments.constants.ts @@ -0,0 +1,13 @@ +export const MAX_AGENT_CHAT_ATTACHMENT_SIZE_MB = 10; +export const MAX_AGENT_CHAT_ATTACHMENT_SIZE_BYTES = MAX_AGENT_CHAT_ATTACHMENT_SIZE_MB * 1024 * 1024; +/** + * Base64 inflates by 4/3 (in whole 4-char blocks), so this is the longest + * encoding of a payload at the byte limit; the byte-accurate check happens + * after decoding. + */ +export const MAX_AGENT_CHAT_ATTACHMENT_BASE64_LENGTH = + Math.ceil(MAX_AGENT_CHAT_ATTACHMENT_SIZE_BYTES / 3) * 4; +export const MAX_AGENT_CHAT_ATTACHMENTS_PER_MESSAGE = 10; +// Matches the agent_chat_attachments.fileName varchar(255) column. +export const MAX_AGENT_CHAT_ATTACHMENT_FILENAME_LENGTH = 255; +export const MAX_AGENT_CHAT_ATTACHMENT_MIMETYPE_LENGTH = 100; diff --git a/packages/@n8n/api-types/src/agents/agent-json-config.schema.ts b/packages/@n8n/api-types/src/agents/agent-json-config.schema.ts index 30bfa8fd16f..aca83e867cb 100644 --- a/packages/@n8n/api-types/src/agents/agent-json-config.schema.ts +++ b/packages/@n8n/api-types/src/agents/agent-json-config.schema.ts @@ -2,6 +2,7 @@ import { z, type ZodError } from 'zod'; import { isDraftAgentConfig } from './agent-config-lifecycle'; import { AgentIntegrationConfigSchema } from './agent-integration.schema'; +import { AGENT_REASONING_LEVELS } from './reasoning'; /** * Regex for valid custom tool ids. Shared with the backend service layer * so validation stays in sync with the JSON config schema. @@ -71,12 +72,6 @@ const MemoryConfigSchema = z.object({ episodicMemory: EpisodicMemoryConfigSchema.optional(), }); -const ThinkingConfigSchema = z.object({ - provider: z.enum(['anthropic', 'openai']), - budgetTokens: z.number().int().optional(), - reasoningEffort: z.string().optional(), -}); - // Mandatory for supporting providers (the user cannot disable it). Anthropic // exposes a cache-breakpoint TTL; OpenAI has no sub-config. export const PromptCachingConfigSchema = z.object({ @@ -389,7 +384,7 @@ const CustomToolJsonConfigSchema = z.object({ export const WorkflowToolJsonConfigSchema = z .object({ type: z.literal('workflow'), - workflow: z.string().min(1), + workflow: z.string().min(1).describe("The workflow's display name (not its ID)."), name: z.string().optional(), description: z.string().optional(), requireApproval: z.boolean().optional(), @@ -483,7 +478,7 @@ export const AgentJsonConfigBaseSchema = z.object({ .optional(), config: z .object({ - thinking: ThinkingConfigSchema.optional(), + reasoning: z.enum(AGENT_REASONING_LEVELS).optional(), promptCaching: PromptCachingConfigSchema.optional(), webSearch: WebSearchConfigSchema.optional(), toolCallConcurrency: z.number().int().min(1).max(100).optional(), diff --git a/packages/@n8n/api-types/src/agents/agent-task.schema.ts b/packages/@n8n/api-types/src/agents/agent-task.schema.ts index 4f01db769c9..1648daee462 100644 --- a/packages/@n8n/api-types/src/agents/agent-task.schema.ts +++ b/packages/@n8n/api-types/src/agents/agent-task.schema.ts @@ -13,7 +13,11 @@ export const AGENT_TASK_CRON_EXPRESSION_MAX_LENGTH = 128; export const agentTaskSchema = z.object({ name: z.string().min(1).max(AGENT_TASK_NAME_MAX_LENGTH), objective: z.string().min(1).max(AGENT_TASK_OBJECTIVE_MAX_LENGTH), - cronExpression: z.string().min(1).max(AGENT_TASK_CRON_EXPRESSION_MAX_LENGTH), + cronExpression: z + .string() + .min(1) + .max(AGENT_TASK_CRON_EXPRESSION_MAX_LENGTH) + .describe('Standard five-field cron expression, for example "0 9 * * *"'), }); export type AgentTaskConfig = z.infer; diff --git a/packages/@n8n/api-types/src/agents/child-trace.ts b/packages/@n8n/api-types/src/agents/child-trace.ts new file mode 100644 index 00000000000..22903ea3ad8 --- /dev/null +++ b/packages/@n8n/api-types/src/agents/child-trace.ts @@ -0,0 +1,74 @@ +import type { ForwardedChildChunkWire } from '../agent-sse'; +import type { PersistedChildTrace } from './types'; + +export function emptyChildTrace(): PersistedChildTrace { + return { text: '', reasoningSegments: [], steps: [] }; +} + +/** Fold one forwarded child chunk into a trace. Mutates `trace` in place so + * Vue reactivity is preserved when called on a reactive object. */ +export function applyForwardedChildChunk( + trace: PersistedChildTrace, + chunk: ForwardedChildChunkWire, + now: number = Date.now(), +): void { + switch (chunk.type) { + case 'text-delta': + trace.text += chunk.delta; + break; + case 'reasoning-start': + if (!trace.reasoningSegments.some((s) => s.id === chunk.id)) { + trace.reasoningSegments.push({ id: chunk.id, content: '', startTime: now }); + } + break; + case 'reasoning-delta': { + let segment = trace.reasoningSegments.find((s) => s.id === chunk.id); + if (!segment) { + segment = { id: chunk.id, content: '', startTime: now }; + trace.reasoningSegments.push(segment); + } + segment.content += chunk.delta; + break; + } + case 'reasoning-end': { + const segment = trace.reasoningSegments.find((s) => s.id === chunk.id); + if (segment) segment.endTime = now; + break; + } + case 'tool-input-start': { + if (!trace.steps.some((s) => s.toolCallId === chunk.toolCallId)) { + trace.steps.push({ + toolCallId: chunk.toolCallId, + toolName: chunk.toolName, + running: true, + }); + } + break; + } + case 'tool-execution-start': { + const step = trace.steps.find((s) => s.toolCallId === chunk.toolCallId); + if (step) { + step.running = true; + } else { + trace.steps.push({ + toolCallId: chunk.toolCallId, + toolName: chunk.toolName, + running: true, + }); + } + break; + } + case 'tool-execution-end': { + const step = trace.steps.find((s) => s.toolCallId === chunk.toolCallId); + if (step) step.running = false; + break; + } + default: + break; + } +} + +/** Clear residual `running` flags so a settled trace never shows a stuck spinner. */ +export function settleChildTrace(trace: PersistedChildTrace): void { + for (const step of trace.steps) step.running = false; +} diff --git a/packages/@n8n/api-types/src/agents/dto.ts b/packages/@n8n/api-types/src/agents/dto.ts index 7b8c3e709d3..d0009a99e7f 100644 --- a/packages/@n8n/api-types/src/agents/dto.ts +++ b/packages/@n8n/api-types/src/agents/dto.ts @@ -1,6 +1,13 @@ import { jsonParse } from 'n8n-workflow'; import { z } from 'zod'; +import { + MAX_AGENT_CHAT_ATTACHMENT_BASE64_LENGTH, + MAX_AGENT_CHAT_ATTACHMENT_FILENAME_LENGTH, + MAX_AGENT_CHAT_ATTACHMENT_SIZE_MB, + MAX_AGENT_CHAT_ATTACHMENT_MIMETYPE_LENGTH, + MAX_AGENT_CHAT_ATTACHMENTS_PER_MESSAGE, +} from './agent-chat-attachments.constants'; import { AgentVectorStoreConfigSchema } from './agent-json-config.schema'; import { agentSkillSchema, agentSkillShape } from './agent-skill.schema'; import { agentTaskSchema } from './agent-task.schema'; @@ -19,6 +26,7 @@ export const AGENTS_LIST_SORT_OPTIONS = [ const agentListFilterSchema = z .object({ query: z.string().trim().min(1).max(128).optional(), + availableInMCP: z.boolean().optional(), }) .strict(); @@ -59,6 +67,18 @@ export class AgentProviderModelsQueryDto extends Z.class({ credentialId: z.string().min(1).max(64).optional(), }) {} +/** + * Target selector for bulk-toggling agents' MCP availability. Exactly one of + * `agentIds`, `projectId`, or `allAgents` must be provided (mirrors the + * workflows equivalent, `UpdateWorkflowsAvailabilityDto`). + */ +export class UpdateAgentsMcpAvailabilityDto extends Z.class({ + availableInMCP: z.boolean(), + agentIds: z.array(z.string().min(1)).min(1).max(100).optional(), + projectId: z.string().min(1).optional(), + allAgents: z.literal(true).optional(), +}) {} + export class CreateAgentDto extends Z.class({ name: z.string().min(1), }) {} @@ -123,10 +143,58 @@ export class UpdateAgentSkillDto extends Z.class(updateAgentSkillShape) { } } -export class AgentChatMessageDto extends Z.class({ - message: z.string().min(1), +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), + // Base64; cap sized so the decoded payload stays within the size limit. + data: z + .string() + .min(1) + .max( + MAX_AGENT_CHAT_ATTACHMENT_BASE64_LENGTH, + `Attachment exceeds ${MAX_AGENT_CHAT_ATTACHMENT_SIZE_MB} MB limit`, + ), +}); + +export type AgentChatAttachmentPayload = z.infer; + +const agentChatMessageShape = { + // `message` may be empty when at least one attachment is present + // (attachment-only sends) — see the schema-level refinement below. + message: z.string(), sessionId: z.string().min(1).optional(), -}) {} + attachments: z + .array(agentChatAttachmentSchema) + .max(MAX_AGENT_CHAT_ATTACHMENTS_PER_MESSAGE) + .optional(), +}; + +const agentChatMessageSchema = z + .object(agentChatMessageShape) + .refine((value) => value.message.trim().length > 0 || (value.attachments?.length ?? 0) > 0, { + message: 'Message text or at least one attachment is required', + path: ['message'], + }); + +/** + * Validate via `parse`/`safeParse` (what the controller registry's `@Body` + * middleware calls) — they apply the refined schema. The inherited `schema` + * static cannot hold the refinement (a `ZodEffects` is not assignable to the + * base class's `ZodObject`) and misses the text-or-attachment invariant. + */ +export class AgentChatMessageDto extends Z.class(agentChatMessageShape) { + constructor(data: z.infer) { + super(agentChatMessageSchema.parse(data)); + } + + static override safeParse(data: unknown) { + return agentChatMessageSchema.safeParse(data); + } + + static override parse(data: unknown) { + return agentChatMessageSchema.parse(data); + } +} export class AgentChatResumeDto extends Z.class({ runId: z.string().min(1), diff --git a/packages/@n8n/api-types/src/agents/index.ts b/packages/@n8n/api-types/src/agents/index.ts index 9bc33bc48dd..7114259da5d 100644 --- a/packages/@n8n/api-types/src/agents/index.ts +++ b/packages/@n8n/api-types/src/agents/index.ts @@ -1,3 +1,4 @@ +export * from './agent-chat-attachments.constants'; export * from './agent-config-lifecycle'; export * from './agent-config-validation.schema'; export * from './agent-files.constants'; @@ -5,20 +6,24 @@ export * from './agent-integration.schema'; export * from './agent-json-config.schema'; export * from './agent-node-tool-operations'; export * from './agent-skill.schema'; +export * from './child-trace'; export * from './inline-agent-config.schema'; export * from './sanitize-agent-json-config'; export * from './agent-task.schema'; export * from './dto'; export * from './model-providers'; export * from './provider-capabilities'; +export * from './reasoning'; export * from './rich-card.schema'; export * from './sub-agent.schema'; export * from './types'; -export type { AgentSseEvent, AgentSseMessage, ToolSuspendedPayload } from '../agent-sse'; -export { - AGENT_BUILDER_AVAILABLE_AI_UTILITY_TOOL_NODE_TYPES, - AGENT_BUILDER_HIDDEN_AVAILABLE_TOOL_NODE_TYPES, -} from '../agent-builder-tool-node-types'; +export type { + AgentSseEvent, + AgentSseMessage, + ForwardedChildChunkWire, + ToolSuspendedPayload, +} 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 *`). export { diff --git a/packages/@n8n/api-types/src/agents/provider-capabilities.ts b/packages/@n8n/api-types/src/agents/provider-capabilities.ts index d2061cca4ec..e4e638e79c3 100644 --- a/packages/@n8n/api-types/src/agents/provider-capabilities.ts +++ b/packages/@n8n/api-types/src/agents/provider-capabilities.ts @@ -47,12 +47,27 @@ export const NATIVE_WEB_SEARCH_DEFAULTS_BY_PROVIDER = { { toolName: NativeWebSearchCanonicalTool; args: Record } >; +/** Media-type families a provider's chat models accept as inline file parts. */ +export interface ProviderAttachmentCapabilities { + image: boolean; + pdf: boolean; + audio: boolean; +} + +const NO_ATTACHMENTS: ProviderAttachmentCapabilities = { image: false, pdf: false, audio: false }; + export interface ProviderCapabilities { thinking: false | 'budgetTokens' | 'reasoningEffort'; /** false = unsupported; 'ttl' = Anthropic (renders TTL select); true = mandatory, no sub-control (OpenAI). */ promptCaching: false | 'ttl' | true; webSearch: false | NativeWebSearchCanonicalTool; providerTools: ReadonlyArray; + /** + * Conservative per-provider defaults (deliberately provider-level, not + * per-model): a media-type family is only enabled where the provider's + * current chat models broadly accept it. + */ + attachments: ProviderAttachmentCapabilities; } export const PROVIDER_CAPABILITIES: Record = { @@ -61,28 +76,91 @@ export const PROVIDER_CAPABILITIES: Record = { promptCaching: 'ttl', webSearch: 'anthropic.web_search', providerTools: ['anthropic.web_search'], + attachments: { image: true, pdf: true, audio: false }, }, openai: { thinking: 'reasoningEffort', promptCaching: true, webSearch: 'openai.web_search', providerTools: ['openai.web_search', 'openai.image_generation'], + attachments: { image: true, pdf: true, audio: false }, }, google: { thinking: false, promptCaching: false, webSearch: false, providerTools: [], + attachments: { image: true, pdf: true, audio: true }, + }, + xai: { + thinking: false, + promptCaching: false, + webSearch: false, + providerTools: [], + attachments: NO_ATTACHMENTS, + }, + groq: { + thinking: false, + promptCaching: false, + webSearch: false, + providerTools: [], + attachments: NO_ATTACHMENTS, + }, + deepseek: { + thinking: false, + promptCaching: false, + webSearch: false, + providerTools: [], + attachments: NO_ATTACHMENTS, + }, + mistral: { + thinking: false, + promptCaching: false, + webSearch: false, + providerTools: [], + attachments: NO_ATTACHMENTS, + }, + openrouter: { + thinking: false, + promptCaching: false, + webSearch: false, + providerTools: [], + attachments: NO_ATTACHMENTS, + }, + cohere: { + thinking: false, + promptCaching: false, + webSearch: false, + providerTools: [], + attachments: NO_ATTACHMENTS, + }, + ollama: { + thinking: false, + promptCaching: false, + webSearch: false, + providerTools: [], + attachments: NO_ATTACHMENTS, }, - xai: { thinking: false, promptCaching: false, webSearch: false, providerTools: [] }, - groq: { thinking: false, promptCaching: false, webSearch: false, providerTools: [] }, - deepseek: { thinking: false, promptCaching: false, webSearch: false, providerTools: [] }, - mistral: { thinking: false, promptCaching: false, webSearch: false, providerTools: [] }, - openrouter: { thinking: false, promptCaching: false, webSearch: false, providerTools: [] }, - cohere: { thinking: false, promptCaching: false, webSearch: false, providerTools: [] }, - ollama: { thinking: false, promptCaching: false, webSearch: false, providerTools: [] }, }; +/** + * Whether a media type is accepted as an inline file part by the given + * provider's models, per the static capability map. Unknown providers and + * unmapped media types (e.g. video) return false. + */ +export function isAttachmentMediaTypeSupported( + provider: string | undefined, + mediaType: string | undefined, +): boolean { + if (!provider || !mediaType) return false; + const attachments = PROVIDER_CAPABILITIES[provider]?.attachments; + if (!attachments) return false; + if (mediaType.startsWith('image/')) return attachments.image; + if (mediaType === 'application/pdf') return attachments.pdf; + if (mediaType.startsWith('audio/')) return attachments.audio; + return false; +} + export const REASONING_EFFORT_OPTIONS = ['low', 'medium', 'high'] as const; export type ReasoningEffort = (typeof REASONING_EFFORT_OPTIONS)[number]; diff --git a/packages/@n8n/api-types/src/agents/reasoning.ts b/packages/@n8n/api-types/src/agents/reasoning.ts new file mode 100644 index 00000000000..89255d62214 --- /dev/null +++ b/packages/@n8n/api-types/src/agents/reasoning.ts @@ -0,0 +1,3 @@ +export const AGENT_REASONING_LEVELS = ['low', 'medium', 'high'] as const; + +export type AgentReasoningLevel = (typeof AGENT_REASONING_LEVELS)[number]; diff --git a/packages/@n8n/api-types/src/agents/types.ts b/packages/@n8n/api-types/src/agents/types.ts index 2e4caca7296..10a4a3c2394 100644 --- a/packages/@n8n/api-types/src/agents/types.ts +++ b/packages/@n8n/api-types/src/agents/types.ts @@ -190,8 +190,29 @@ export interface AgentCapabilitySummary { tasks: AgentCapabilityTask[]; } +export interface PersistedChildTraceSegment { + id: string; + content: string; + startTime?: number; + endTime?: number; +} + +export interface PersistedChildTraceStep { + toolCallId: string; + toolName: string; + running: boolean; +} + +/** A delegated child's trace, captured from forwarded chunks and persisted on + * the parent's `delegate_subagent` tool call. */ +export interface PersistedChildTrace { + text: string; + reasoningSegments: PersistedChildTraceSegment[]; + steps: PersistedChildTraceStep[]; +} + export interface AgentPersistedMessageContentPart { - type: 'text' | 'reasoning' | 'tool-call' | (string & {}); + type: 'text' | 'reasoning' | 'tool-call' | 'file' | (string & {}); text?: string; toolName?: string; toolCallId?: string; @@ -200,10 +221,17 @@ export interface AgentPersistedMessageContentPart { output?: unknown; canceled?: boolean; error?: string; - /** Epoch ms when the tool handler started executing. */ + /** Epoch ms when this content part started. */ startTime?: number; - /** Epoch ms when the tool handler settled. */ + /** Epoch ms when this content part settled. */ endTime?: number; + /** File parts carry attachment metadata only — bytes are fetched via the attachment download route. */ + fileId?: string; + fileName?: string; + mimeType?: string; + sizeBytes?: number; + /** Live trace of a delegated child, present only on `delegate_subagent` parts. */ + childTrace?: PersistedChildTrace; } export interface AgentPersistedMessageDto { @@ -212,6 +240,8 @@ export interface AgentPersistedMessageDto { content: AgentPersistedMessageContentPart[]; /** Agent-execution turn id when this message was produced from an execution transcript. */ executionId?: string; + /** Outcome of the execution that produced this message. */ + executionStatus?: 'success' | 'error'; } export const AGENT_BUILDER_DEFAULT_MODEL = 'claude-sonnet-4-6' as const; diff --git a/packages/@n8n/api-types/src/dto/index.ts b/packages/@n8n/api-types/src/dto/index.ts index 19a6abfa953..0c915aa169d 100644 --- a/packages/@n8n/api-types/src/dto/index.ts +++ b/packages/@n8n/api-types/src/dto/index.ts @@ -308,9 +308,15 @@ export type { EncryptionKeyResponseDto } from './encryption/encryption-key-respo export { CreateWorkflowReviewRequestDto } from './workflow-reviews/create-workflow-review-request.dto'; export { DecideWorkflowReviewRequestDto } from './workflow-reviews/decide-workflow-review-request.dto'; export { GetWorkflowReviewEligibleReviewersQueryDto } from './workflow-reviews/get-eligible-reviewers-query.dto'; -export { ListWorkflowReviewRequestsQueryDto } from './workflow-reviews/list-workflow-review-requests-query.dto'; +export { + ListWorkflowReviewRequestsQueryDto, + type WorkflowReviewApprovedPublicationState, + type WorkflowReviewRequestForWorkflow, + type WorkflowReviewRequestList, +} from './workflow-reviews/list-workflow-review-requests-query.dto'; export { UpdateWorkflowReviewRequestVersionDto } from './workflow-reviews/update-workflow-review-request-version.dto'; export * from './workflow-reviews/list-workflow-review-inbox.dto'; +export type * from './workflow-reviews/get-workflow-review-request-detail.dto'; export { UpdateOtelSettingsDto } from './otel/update-otel-settings.dto'; export { TestOtelTraceDto } from './otel/test-otel-trace.dto'; diff --git a/packages/@n8n/api-types/src/dto/oidc/__tests__/config.dto.test.ts b/packages/@n8n/api-types/src/dto/oidc/__tests__/config.dto.test.ts index a859e2ac10d..2d4447fd228 100644 --- a/packages/@n8n/api-types/src/dto/oidc/__tests__/config.dto.test.ts +++ b/packages/@n8n/api-types/src/dto/oidc/__tests__/config.dto.test.ts @@ -26,7 +26,22 @@ describe('UpdateOidcConfigurationDto', () => { prompt: 'consent', authenticationContextClassReference: ['mfa'], additionalScopes: 'groups', + rpInitiatedLogoutEnabled: true, }); expect(result.success).toBe(true); }); }); + +describe('OidcConfigDto', () => { + it('defaults rpInitiatedLogoutEnabled to false when omitted', () => { + const result = OidcConfigDto.safeParse({ + clientId: 'n8n-client', + clientSecret: 'super-secret', + discoveryEndpoint: 'https://accounts.example.com/.well-known/openid-configuration', + }); + expect(result.success).toBe(true); + if (result.success) { + expect(result.data.rpInitiatedLogoutEnabled).toBe(false); + } + }); +}); diff --git a/packages/@n8n/api-types/src/dto/oidc/config.dto.ts b/packages/@n8n/api-types/src/dto/oidc/config.dto.ts index 1be723168f3..0b8259cec2c 100644 --- a/packages/@n8n/api-types/src/dto/oidc/config.dto.ts +++ b/packages/@n8n/api-types/src/dto/oidc/config.dto.ts @@ -12,6 +12,7 @@ export class OidcConfigDto extends Z.class({ prompt: z.enum(OIDC_PROMPT_VALUES).optional().default('select_account'), authenticationContextClassReference: z.array(z.string()).default([]), additionalScopes: z.string().default(''), + rpInitiatedLogoutEnabled: z.boolean().optional().default(false), }) {} /** @@ -24,4 +25,5 @@ export class UpdateOidcConfigurationDto extends OidcConfigDto.extend({ prompt: z.enum(OIDC_PROMPT_VALUES), authenticationContextClassReference: z.array(z.string()), additionalScopes: z.string(), + rpInitiatedLogoutEnabled: z.boolean(), }) {} diff --git a/packages/@n8n/api-types/src/dto/packages/__tests__/export-package-request.dto.test.ts b/packages/@n8n/api-types/src/dto/packages/__tests__/export-package-request.dto.test.ts index 3e553848bca..0d918382fac 100644 --- a/packages/@n8n/api-types/src/dto/packages/__tests__/export-package-request.dto.test.ts +++ b/packages/@n8n/api-types/src/dto/packages/__tests__/export-package-request.dto.test.ts @@ -115,6 +115,35 @@ describe('ExportPackageRequestDto', () => { }); }); + describe('includeTags', () => { + it('defaults to true when omitted', () => { + const result = ExportPackageRequestDto.safeParse({ workflowIds: ['wf-1'] }); + expect(result.success).toBe(true); + if (result.success) expect(result.data.includeTags).toBe(true); + }); + + it.each([true, false])('accepts explicit %s', (includeTags) => { + const result = ExportPackageRequestDto.safeParse({ + workflowIds: ['wf-1'], + includeTags, + }); + expect(result.success).toBe(true); + if (result.success) expect(result.data.includeTags).toBe(includeTags); + }); + + it.each([ + { name: 'string value', includeTags: 'false' }, + { name: 'numeric value', includeTags: 0 }, + { name: 'null value', includeTags: null }, + ])('rejects $name', ({ includeTags }) => { + const result = ExportPackageRequestDto.safeParse({ + workflowIds: ['wf-1'], + includeTags, + }); + expect(result.success).toBe(false); + }); + }); + describe('missingWorkflowDependencyPolicy', () => { it.each(['fail', 'reference-only', 'include-in-package'])( 'accepts %s', diff --git a/packages/@n8n/api-types/src/dto/packages/__tests__/import-package-request.dto.test.ts b/packages/@n8n/api-types/src/dto/packages/__tests__/import-package-request.dto.test.ts index 536a6deef3b..3f12dd4f73a 100644 --- a/packages/@n8n/api-types/src/dto/packages/__tests__/import-package-request.dto.test.ts +++ b/packages/@n8n/api-types/src/dto/packages/__tests__/import-package-request.dto.test.ts @@ -21,6 +21,8 @@ describe('ImportPackageRequestDto', () => { dataTableMissingMode: 'create', dataTableSchemaConflictPolicy: 'keep-existing', variableMissingMode: 'do-nothing', + tagMissingMode: 'create', + tagConflictPolicy: 'skip', }); } }); @@ -46,6 +48,8 @@ describe('ImportPackageRequestDto', () => { dataTableMissingMode: 'create', dataTableSchemaConflictPolicy: 'keep-existing', variableMissingMode: 'do-nothing', + tagMissingMode: 'create', + tagConflictPolicy: 'skip', }); } }); @@ -73,6 +77,8 @@ describe('ImportPackageRequestDto', () => { dataTableMissingMode: 'create', dataTableSchemaConflictPolicy: 'keep-existing', variableMissingMode: 'do-nothing', + tagMissingMode: 'create', + tagConflictPolicy: 'skip', }); } }); @@ -99,6 +105,8 @@ describe('ImportPackageRequestDto', () => { dataTableMissingMode: 'create', dataTableSchemaConflictPolicy: 'keep-existing', variableMissingMode: 'do-nothing', + tagMissingMode: 'create', + tagConflictPolicy: 'skip', }); } }); @@ -393,6 +401,8 @@ describe('ImportPackageRequestDto', () => { { field: 'dataTableMissingMode', expected: 'create' }, { field: 'dataTableSchemaConflictPolicy', expected: 'keep-existing' }, { field: 'variableMissingMode', expected: 'do-nothing' }, + { field: 'tagMissingMode', expected: 'create' }, + { field: 'tagConflictPolicy', expected: 'skip' }, ] as const)('defaults $field when the value is an empty string', ({ field, expected }) => { const result = ImportPackageRequestDto.safeParse({ [field]: '' }); expect(result.success).toBe(true); @@ -441,6 +451,72 @@ describe('ImportPackageRequestDto', () => { }); }); + describe('tagMissingMode', () => { + it('defaults tagMissingMode to create when omitted', () => { + const result = ImportPackageRequestDto.safeParse({ workflowConflictPolicy: 'fail' }); + expect(result.success).toBe(true); + if (result.success) { + expect(result.data.tagMissingMode).toBe('create'); + } + }); + + it.each(['create', 'do-nothing'] as const)( + 'accepts %s as a tagMissingMode value', + (tagMissingMode) => { + const result = ImportPackageRequestDto.safeParse({ + tagMissingMode, + workflowConflictPolicy: 'fail', + }); + expect(result.success).toBe(true); + if (result.success) { + expect(result.data.tagMissingMode).toBe(tagMissingMode); + } + }, + ); + + it('rejects unsupported tagMissingMode values', () => { + expect( + ImportPackageRequestDto.safeParse({ + tagMissingMode: 'must-preexist', + workflowConflictPolicy: 'fail', + }).success, + ).toBe(false); + }); + }); + + describe('tagConflictPolicy', () => { + it('defaults tagConflictPolicy to skip when omitted', () => { + const result = ImportPackageRequestDto.safeParse({ workflowConflictPolicy: 'fail' }); + expect(result.success).toBe(true); + if (result.success) { + expect(result.data.tagConflictPolicy).toBe('skip'); + } + }); + + it.each(['skip', 'fail', 'rename'] as const)( + 'accepts %s as a tagConflictPolicy value', + (tagConflictPolicy) => { + const result = ImportPackageRequestDto.safeParse({ + tagConflictPolicy, + workflowConflictPolicy: 'fail', + }); + expect(result.success).toBe(true); + if (result.success) { + expect(result.data.tagConflictPolicy).toBe(tagConflictPolicy); + } + }, + ); + + it('rejects unsupported tagConflictPolicy values', () => { + expect( + ImportPackageRequestDto.safeParse({ + tagConflictPolicy: 'overwrite', + workflowConflictPolicy: 'fail', + }).success, + ).toBe(false); + }); + }); + describe('variableParentPolicy', () => { it.each([ { workflowConflictPolicy: 'fail' }, diff --git a/packages/@n8n/api-types/src/dto/packages/export-package-request.dto.ts b/packages/@n8n/api-types/src/dto/packages/export-package-request.dto.ts index 4cf6b9b131e..ffa567b5704 100644 --- a/packages/@n8n/api-types/src/dto/packages/export-package-request.dto.ts +++ b/packages/@n8n/api-types/src/dto/packages/export-package-request.dto.ts @@ -7,6 +7,7 @@ export class ExportPackageRequestDto extends Z.class({ 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), missingWorkflowDependencyPolicy: z .enum(['fail', 'reference-only', 'include-in-package']) .optional() diff --git a/packages/@n8n/api-types/src/dto/packages/import-package-request.dto.ts b/packages/@n8n/api-types/src/dto/packages/import-package-request.dto.ts index 0d3290a269f..d3a40ce0e6e 100644 --- a/packages/@n8n/api-types/src/dto/packages/import-package-request.dto.ts +++ b/packages/@n8n/api-types/src/dto/packages/import-package-request.dto.ts @@ -19,6 +19,8 @@ export const IMPORT_PACKAGE_REQUEST_FORM_FIELDS = [ 'dataTableSchemaConflictPolicy', 'variableMissingMode', 'variableParentPolicy', + 'tagMissingMode', + 'tagConflictPolicy', ] as const; /** Multipart text fields: empty / whitespace-only values become `undefined`. */ @@ -104,4 +106,6 @@ export class ImportPackageRequestDto extends Z.class({ dataTableSchemaConflictPolicy: optionalEnum(['keep-existing', 'fail'], 'keep-existing'), variableMissingMode: optionalEnum(['do-nothing', 'must-preexist', 'create-stub'], 'do-nothing'), variableParentPolicy: optionalEnumNoDefault(['project', 'global']), + tagMissingMode: optionalEnum(['create', 'do-nothing'], 'create'), + tagConflictPolicy: optionalEnum(['skip', 'fail', 'rename'], 'skip'), }) {} diff --git a/packages/@n8n/api-types/src/dto/workflow-reviews/get-workflow-review-request-detail.dto.ts b/packages/@n8n/api-types/src/dto/workflow-reviews/get-workflow-review-request-detail.dto.ts new file mode 100644 index 00000000000..43ae2cdaa97 --- /dev/null +++ b/packages/@n8n/api-types/src/dto/workflow-reviews/get-workflow-review-request-detail.dto.ts @@ -0,0 +1,32 @@ +import type { IConnections, INode, IWorkflowGroup } from 'n8n-workflow'; + +import type { WorkflowReviewInboxItem } from './list-workflow-review-inbox.dto'; +import type { Iso8601DateTimeString } from '../../datetime'; + +/** Immutable content of one workflow-history version, as needed by the diff surface. */ +export interface WorkflowReviewVersionSnapshot { + versionId: string; + nodes: INode[]; + connections: IConnections; + nodeGroups: IWorkflowGroup[]; + createdAt: Iso8601DateTimeString; +} + +export interface WorkflowReviewRequestWorkflowDetail { + workflowId: string; + workflowName: string; + /** Pinned version id; null when the history entry was pruned (LIGO-879) */ + workflowVersionId: string | null; + /** Content of the pinned version; null when the history row no longer exists */ + pinnedVersion: WorkflowReviewVersionSnapshot | null; + /** + * Latest published version, resolved at read time; null = never published, + * i.e. the diff baseline is empty. + */ + baselineVersion: WorkflowReviewVersionSnapshot | null; +} + +export interface WorkflowReviewRequestDetail extends WorkflowReviewInboxItem { + description: string | null; + workflows: WorkflowReviewRequestWorkflowDetail[]; +} diff --git a/packages/@n8n/api-types/src/dto/workflow-reviews/list-workflow-review-requests-query.dto.ts b/packages/@n8n/api-types/src/dto/workflow-reviews/list-workflow-review-requests-query.dto.ts index c9de7e2a118..d7d65dd08e6 100644 --- a/packages/@n8n/api-types/src/dto/workflow-reviews/list-workflow-review-requests-query.dto.ts +++ b/packages/@n8n/api-types/src/dto/workflow-reviews/list-workflow-review-requests-query.dto.ts @@ -1,5 +1,9 @@ import { n8nIdSchema } from '../../schemas/id.schema'; -import { workflowReviewRequestStateSchema } from '../../workflow-review-request-summary'; +import type { WorkflowReviewEligibleReviewer } from '../../workflow-review-eligible-reviewer'; +import { + type WorkflowReviewRequestSummary, + workflowReviewRequestStateSchema, +} from '../../workflow-review-request-summary'; import { Z } from '../../zod-class'; import { paginationSchema } from '../pagination/pagination.dto'; @@ -9,3 +13,37 @@ export class ListWorkflowReviewRequestsQueryDto extends Z.class({ workflowId: n8nIdSchema, state: workflowReviewRequestStateSchema.optional(), }) {} + +/** + * Whether an approved review's pinned version ever reached production, derived + * from workflow publish history: + * - `published`: the pinned version itself was published; + * - `superseded`: a version created after the pinned one was published; + * - `not_published`: neither happened; + * - `unknown`: the pin is null or its history row was pruned (LIGO-879), so the + * state cannot be derived safely. + * + * A later deactivation does not undo `published`/`superseded`. + */ +export type WorkflowReviewApprovedPublicationState = + | 'published' + | 'superseded' + | 'not_published' + | 'unknown'; + +/** + * Workflow-scoped list item — extends {@link WorkflowReviewRequestSummary} with + * what the canvas review banner needs. Kept off the mutation responses, which + * stay on the minimal summary. + */ +export interface WorkflowReviewRequestForWorkflow extends WorkflowReviewRequestSummary { + /** Who made the current decision; `null` when there is none or the user is gone. */ + decisionBy: WorkflowReviewEligibleReviewer | null; + /** Only derived for an approved review; `null` otherwise. */ + approvedVersionPublicationState: WorkflowReviewApprovedPublicationState | null; +} + +export interface WorkflowReviewRequestList { + count: number; + data: WorkflowReviewRequestForWorkflow[]; +} diff --git a/packages/@n8n/api-types/src/index.ts b/packages/@n8n/api-types/src/index.ts index 543299aa5d5..5f6f42be3d6 100644 --- a/packages/@n8n/api-types/src/index.ts +++ b/packages/@n8n/api-types/src/index.ts @@ -15,6 +15,7 @@ export * from './redaction-enforcement-floor'; export * from './workflow-reviews-policy'; export type * from './workflow-review-eligible-reviewer'; export * from './workflow-review-request-summary'; +export * from './workflow-publish-blocked-details'; export { chatHubConversationModelSchema, type ChatModelDto, @@ -529,6 +530,7 @@ export { MCP_APPS_VARIANT_CONTROL, MCP_APPS_VARIANT_ENABLED, MCP_CANVAS_GROUPS_FLAG, + MCP_AGENT_SCOPES, MCP_INSTANCE_SCOPES, MCP_CLIENT_BRAND_MATCHERS, MCP_CLIENT_TYPE_FILTERS, @@ -583,6 +585,7 @@ export { UpdateAgentEvalDatasetDto, createAgentEvalRunSchema, CreateAgentEvalRunDto, + agentEvalCorrectionSchema, createAgentEvalRatingSchema, CreateAgentEvalRatingDto, agentEvalDraftCaseSchema, @@ -594,6 +597,7 @@ export type { AgentEvalRunStatus, AgentEvalResultStatus, AgentEvalVote, + AgentEvalCorrection, CreateAgentEvalDatasetDto, UpdateAgentEvalDatasetPayload, CreateAgentEvalRunPayload, @@ -603,6 +607,7 @@ export type { AgentEvalResultRecord, AgentEvalRatingRecord, AgentEvalRunDetail, + AgentEvalRunSummary, AgentEvalDraftCase, GenerateDraftCasesOptions, GenerateDraftCasesResult, diff --git a/packages/@n8n/api-types/src/schemas/__tests__/agent-evals.schema.test.ts b/packages/@n8n/api-types/src/schemas/__tests__/agent-evals.schema.test.ts index 8e5cd8d674f..c54f5a3b5aa 100644 --- a/packages/@n8n/api-types/src/schemas/__tests__/agent-evals.schema.test.ts +++ b/packages/@n8n/api-types/src/schemas/__tests__/agent-evals.schema.test.ts @@ -158,29 +158,42 @@ describe('CreateAgentEvalRatingDto', () => { CreateAgentEvalRatingDto.safeParse({ vote: 'down', comment: 'Wrong tool used', - correction: { output: 'the expected answer' }, + correction: { finalText: 'the expected answer' }, }).success, ).toBe(true); }); - it('accepts a nested JSON correction', () => { - expect( - CreateAgentEvalRatingDto.safeParse({ - vote: 'down', - correction: { output: { text: 'x', items: [1, 'two', true, null] }, score: 3 }, - }).success, - ).toBe(true); + it('keeps extra correction keys alongside the edited answer', () => { + const parsed = CreateAgentEvalRatingDto.safeParse({ + vote: 'down', + correction: { finalText: 'the expected answer', fields: { tone: 'formal' }, score: 3 }, + }); + + expect(parsed.success).toBe(true); + expect(parsed.data?.correction).toEqual({ + finalText: 'the expected answer', + fields: { tone: 'formal' }, + score: 3, + }); }); it('rejects a correction with a non-JSON leaf', () => { expect( CreateAgentEvalRatingDto.safeParse({ vote: 'down', - correction: { output: () => 'not json' }, + correction: { finalText: 'ok', extra: () => 'not json' }, }).success, ).toBe(false); }); + // A correction with no readable edited answer is what calibration would choke on. + it.each([{}, { output: 'wrong key' }, { finalText: null }, { finalText: ' ' }])( + 'rejects a correction without a usable finalText: %j', + (correction) => { + expect(CreateAgentEvalRatingDto.safeParse({ vote: 'down', correction }).success).toBe(false); + }, + ); + it('rejects an invalid vote', () => { expect(CreateAgentEvalRatingDto.safeParse({ vote: 'maybe' }).success).toBe(false); }); diff --git a/packages/@n8n/api-types/src/schemas/__tests__/agent-run-reducer.test.ts b/packages/@n8n/api-types/src/schemas/__tests__/agent-run-reducer.test.ts index 50099095382..c607dead33b 100644 --- a/packages/@n8n/api-types/src/schemas/__tests__/agent-run-reducer.test.ts +++ b/packages/@n8n/api-types/src/schemas/__tests__/agent-run-reducer.test.ts @@ -642,6 +642,29 @@ describe('agent-run-reducer', () => { expect(state.agentsById['sub-1'].error).toBe('failed'); }); + it('agent-completed with status cancelled yields cancelled without error', () => { + const state = stateWithRun('run-1', 'root'); + reduceEvent(state, makeAgentSpawned('run-1', 'sub-1', 'root')); + reduceEvent(state, { + type: 'agent-completed', + runId: 'run-1', + agentId: 'sub-1', + payload: { role: 'sub-agent', result: '', status: 'cancelled' }, + }); + + expect(state.agentsById['sub-1'].status).toBe('cancelled'); + expect(state.agentsById['sub-1'].error).toBeUndefined(); + }); + + it('agent-completed without status still derives error from legacy error field', () => { + const state = stateWithRun('run-1', 'root'); + reduceEvent(state, makeAgentSpawned('run-1', 'sub-1', 'root')); + reduceEvent(state, makeAgentCompleted('run-1', 'sub-1', '', 'Cancelled by user')); + + expect(state.agentsById['sub-1'].status).toBe('error'); + expect(state.agentsById['sub-1'].error).toBe('Cancelled by user'); + }); + it('agent-completed clears isLoading on agent tool calls', () => { const state = stateWithRun('run-1', 'root'); reduceEvent(state, makeAgentSpawned('run-1', 'sub-1', 'root')); diff --git a/packages/@n8n/api-types/src/schemas/agent-evals.schema.ts b/packages/@n8n/api-types/src/schemas/agent-evals.schema.ts index f85a4c8cd33..432bf15b9ea 100644 --- a/packages/@n8n/api-types/src/schemas/agent-evals.schema.ts +++ b/packages/@n8n/api-types/src/schemas/agent-evals.schema.ts @@ -1,4 +1,4 @@ -import type { JsonObject, JsonValue } from 'n8n-workflow'; +import type { JsonValue } from 'n8n-workflow'; import { z } from 'zod'; import { datasetRefSchema, type DatasetRef } from '../dto/evaluations/evaluation-config.dto'; @@ -18,7 +18,6 @@ const jsonValueSchema: z.ZodType = z.lazy(() => z.array(jsonValueSchema), ]), ); -const jsonObjectSchema: z.ZodType = z.record(z.string(), jsonValueSchema); // PostHog rollout flag id gating the agent-evals feature surface. Every new // agent-eval endpoint + frontend entry point consults this; the flag-off @@ -76,6 +75,8 @@ export type AgentEvalVote = z.infer; // which `Z.class` (flat shape only) cannot express. // --------------------------------------------------------------------------- +// `agentId` is also a path param on the create route; the API rejects a mismatch +// rather than picking a winner. export const createAgentEvalDatasetSchema = z .object({ name: z.string().min(1).max(128), @@ -97,8 +98,8 @@ export const updateAgentEvalDatasetSchema = z.object(updateAgentEvalDatasetShape export type UpdateAgentEvalDatasetPayload = z.infer; export class UpdateAgentEvalDatasetDto extends Z.class(updateAgentEvalDatasetShape) {} -// Kicks off a run of the path dataset. `agentVersionId` optionally pins a -// published version of the dataset's own agent; omitted runs the current one. +// Kicks off a run of the path dataset. `agentVersionId` would pin a published +// version, but the API rejects it until the runner can execute a snapshot. const createAgentEvalRunShape = { agentVersionId: z.string().min(1).optional(), }; @@ -106,12 +107,23 @@ export const createAgentEvalRunSchema = z.object(createAgentEvalRunShape); export type CreateAgentEvalRunPayload = z.infer; export class CreateAgentEvalRunDto extends Z.class(createAgentEvalRunShape) {} +// A correction carries the edited answer under `finalText`, mirroring the key the +// runner writes into a result's `output` so calibration can diff the two directly. +// Required when a correction is sent: a correction without it persists a rating +// that claims an edit no consumer can read. `catchall` keeps richer corrections +// (per-field edits, structured output) possible without a migration, and infers to +// the repository's `JsonObject` rather than `Record`. +export const agentEvalCorrectionSchema = z + .object({ finalText: z.string().trim().min(1) }) + .catchall(jsonValueSchema); +export type AgentEvalCorrection = z.infer; + // A human's 👍/👎 on the path result, with an optional free-text comment and an // edited "should have been" output. const createAgentEvalRatingShape = { vote: agentEvalVoteSchema, comment: z.string().optional(), - correction: jsonObjectSchema.optional(), + correction: agentEvalCorrectionSchema.optional(), }; export const createAgentEvalRatingSchema = z.object(createAgentEvalRatingShape); export type CreateAgentEvalRatingPayload = z.infer; @@ -184,6 +196,14 @@ export type AgentEvalRunDetail = AgentEvalRunRecord & { results: AgentEvalResultRecord[]; }; +// The progress-polling shape: status plus tallies, no per-case rows, so polling +// stays cheap. `pending` folds `new` + `running` — watchers only need "not settled". +export type AgentEvalRunSummary = { + runId: string; + status: AgentEvalRunStatus; + counts: { total: number; success: number; error: number; cancelled: number; pending: number }; +}; + // --------------------------------------------------------------------------- // Case generation. The AI case-generation service drafts cases from an agent's // config; these types are the shared contract for its request/response so the diff --git a/packages/@n8n/api-types/src/schemas/agent-run-reducer.ts b/packages/@n8n/api-types/src/schemas/agent-run-reducer.ts index 75d6c775ba8..d265d720fc0 100644 --- a/packages/@n8n/api-types/src/schemas/agent-run-reducer.ts +++ b/packages/@n8n/api-types/src/schemas/agent-run-reducer.ts @@ -452,7 +452,7 @@ export function reduceEvent(state: AgentRunState, event: InstanceAiEvent): Agent case 'agent-completed': { const agent = ensureAgent(state, event.agentId); if (agent) { - agent.status = event.payload.error ? 'error' : 'completed'; + agent.status = event.payload.status ?? (event.payload.error ? 'error' : 'completed'); agent.result = event.payload.result; agent.error = event.payload.error; // A completed/errored agent can't have tool calls still in-flight. diff --git a/packages/@n8n/api-types/src/schemas/instance-ai.schema.ts b/packages/@n8n/api-types/src/schemas/instance-ai.schema.ts index 09b11242c01..bb05c8d44f1 100644 --- a/packages/@n8n/api-types/src/schemas/instance-ai.schema.ts +++ b/packages/@n8n/api-types/src/schemas/instance-ai.schema.ts @@ -248,6 +248,12 @@ export const agentCompletedPayloadSchema = z.object({ role: z.string(), result: z.string().describe('Synthesized answer'), error: z.string().optional(), + /** + * Terminal state of the sub-agent. Optional: events written before this + * field existed (and the backfill migration's synthesized ones) carry only + * `error`, and the reducer keeps deriving the status from it for those. + */ + status: z.enum(['completed', 'cancelled', 'error']).optional(), }); export const textDeltaPayloadSchema = z.object({ diff --git a/packages/@n8n/api-types/src/schemas/mcp.schema.ts b/packages/@n8n/api-types/src/schemas/mcp.schema.ts index 41796b6d369..2265f1f2424 100644 --- a/packages/@n8n/api-types/src/schemas/mcp.schema.ts +++ b/packages/@n8n/api-types/src/schemas/mcp.schema.ts @@ -16,6 +16,8 @@ export const MCP_APPS_VARIANT_ENABLED = 'variant'; // current behaviour. export const MCP_CANVAS_GROUPS_FLAG = '102_mcp_canvas_groups'; +export const MCP_AGENT_SCOPES = ['agent:read', 'agent:write'] as const; + /** * OAuth scopes a user can grant to an MCP client on the consent screen for * the instance-level MCP server. Each scope gates a set of MCP tools; the @@ -27,6 +29,7 @@ export const MCP_INSTANCE_SCOPES = [ 'workflow:write', 'workflow:execute', 'execution:read', + ...MCP_AGENT_SCOPES, 'credential:read', 'dataTable:read', 'dataTable:write', diff --git a/packages/@n8n/api-types/src/schemas/source-controlled-file.schema.ts b/packages/@n8n/api-types/src/schemas/source-controlled-file.schema.ts index c045105c9ba..9aa40694a24 100644 --- a/packages/@n8n/api-types/src/schemas/source-controlled-file.schema.ts +++ b/packages/@n8n/api-types/src/schemas/source-controlled-file.schema.ts @@ -1,5 +1,7 @@ import { z } from 'zod'; +import { workflowPublishBlockedDetailsSchema } from '../workflow-publish-blocked-details'; + const FileTypeSchema = z.enum([ 'credential', 'workflow', @@ -56,6 +58,7 @@ export const SourceControlledFileSchema = z.object({ folderPath: z.array(z.string()).optional(), owner: ResourceOwnerSchema.optional(), // Resource owner can be a personal email or team information publishingError: z.string().optional(), + publishingErrorDetails: workflowPublishBlockedDetailsSchema.optional(), }); export type SourceControlledFile = z.infer; diff --git a/packages/@n8n/api-types/src/workflow-publish-blocked-details.ts b/packages/@n8n/api-types/src/workflow-publish-blocked-details.ts new file mode 100644 index 00000000000..061bf053b79 --- /dev/null +++ b/packages/@n8n/api-types/src/workflow-publish-blocked-details.ts @@ -0,0 +1,17 @@ +import { z } from 'zod'; + +export const workflowPublishBlockedDetailsSchema = z + .object({ + reason: z.enum(['review_pending', 'changes_requested']), + workflowReviewRequestId: z.string().min(1), + }) + .strict(); + +export type WorkflowPublishBlockedDetails = z.infer; +export type WorkflowPublishBlockedReason = WorkflowPublishBlockedDetails['reason']; + +export function isWorkflowPublishBlockedDetails( + value: unknown, +): value is WorkflowPublishBlockedDetails { + return workflowPublishBlockedDetailsSchema.safeParse(value).success; +} diff --git a/packages/@n8n/api-types/src/workflow-review-request-summary.ts b/packages/@n8n/api-types/src/workflow-review-request-summary.ts index 9587aadc86a..4af4fdc43a9 100644 --- a/packages/@n8n/api-types/src/workflow-review-request-summary.ts +++ b/packages/@n8n/api-types/src/workflow-review-request-summary.ts @@ -21,8 +21,3 @@ export type WorkflowReviewRequestSummary = { createdAt: Iso8601DateTimeString; updatedAt: Iso8601DateTimeString; }; - -export type WorkflowReviewRequestList = { - count: number; - data: WorkflowReviewRequestSummary[]; -}; diff --git a/packages/@n8n/cli/docs/commands/package.md b/packages/@n8n/cli/docs/commands/package.md index 685154103f9..abd0ef71054 100644 --- a/packages/@n8n/cli/docs/commands/package.md +++ b/packages/@n8n/cli/docs/commands/package.md @@ -17,6 +17,7 @@ n8n-cli package export --folder-id=xyz -o folders.n8np n8n-cli package export --project-id=abc -o project.n8np n8n-cli package export -p abc -p def -o projects.n8np n8n-cli package export -w abc --include-variable-values=false -o export.n8np +n8n-cli package export -w abc --include-tags=false -o export.n8np ``` | Flag | Description | @@ -26,17 +27,21 @@ n8n-cli package export -w abc --include-variable-values=false -o export.n8np | `-p, --project-id` | Project ID to include. Repeat the flag to export several. | | `-o, --output` | File to write the package to. Defaults to `export.n8np`. | | `--include-variable-values` | `true` (default) or `false`. Whether values of variables referenced by the exported workflows are bundled into the package. When `false`, variables still travel as name/type files (and in the package requirements), just without their values. | -| `--missing-workflow-dependency-policy` | Policy for missing static sub-workflow dependencies: `fail` aborts when any dependency is missing, `include-in-package` automatically adds missing static sub-workflows, and `reference-only` is reserved for a future export mode. | +| `--include-tags` | `true` (default) or `false`. Whether tags assigned to the exported workflows are bundled into the package. When `false`, no tag data is included in the package. | +| `--missing-workflow-dependency-policy` | Policy for missing static sub-workflow dependencies: `fail` aborts when any dependency is missing, `include-in-package` automatically adds missing static sub-workflows, and `reference-only` keeps them out of the package, listing them in the package requirements as workflows expected to already exist on the target. | Provide at least one `--workflow-id`, `--folder-id`, or `--project-id`. Requires the API key to hold `workflow:export` when exporting workflows or folders, or `project:export` when exporting projects. -Statically referenced sub-workflows must also be included in the resulting -package. How missing ones are handled depends on +Statically referenced sub-workflows are dependencies of the package. How +missing ones are handled depends on `--missing-workflow-dependency-policy`. With the default `fail` policy you include them yourself. With `include-in-package`, n8n resolves the static dependency graph and adds any missing sub-workflows to the package automatically, so you don't need to list -them explicitly. +them explicitly. With `reference-only`, missing sub-workflows stay out of the +package and are only listed in the package requirements (by id, with a +best-effort name), on the assumption that they and their own dependencies +already exist on the target instance. ## `package import` @@ -66,13 +71,17 @@ n8n-cli package import --file=export.n8np --workflow-conflict-policy=fail --bind | `--data-table-schema-conflict-policy` | How strictly a matched data table's schema is compared. Every package column must exist on the matched target table with the same name and type — a missing column or a type mismatch always rejects. `keep-existing` (instance default) ignores additional columns the target table has of its own; `fail` is the strict drift-detection choice and rejects those too. Neither policy alters the matched target table — package columns are never added to it. | | `--variable-missing-mode` | What to do when a variable referenced by the package's workflows is absent from both the target project and the global scope (lookup order: project, then global): `do-nothing` (instance default) imports the workflows anyway and lists the unresolved variable names in the result, without creating anything; `must-preexist` rejects the import unless every referenced variable already resolves; `create-stub` creates each missing variable with an empty value at the placement scope (see `--variable-parent-policy`) and lists the created names under `variables.stubbed`. Matched variables are always used as-is — the import never overwrites variables. | | `--variable-parent-policy` | Where `create-stub` creates missing variables for workflow/folder packages (default `project`): `project` creates them in the target project; `global` creates them at global scope. Ignored for project packages, where placement follows the package layout (a variable bundled under a project is created in that project; one bundled at the top level is created globally). | +| `--tag-missing-mode` | What to do when a tag referenced by the package's workflows is absent on the target instance — tags are matched by source id, never by name. `create` (instance default) creates the tag globally with its source id and name; `do-nothing` imports the workflows without the missing tags and lists them under `tags.skipped`. | +| `--tag-conflict-policy` | What to do when a referenced tag conflicts on the target instance — the same-id target tag carries a different name (rename drift), or the tag's name is held by a different tag (name collision). `skip` (instance default) imports the workflows without the conflicted tags and lists them under `tags.skipped`; `fail` rejects the import; `rename` renames a drifted target tag to the package name — name collisions still reject the import. | | `--bindings` | Explicit source→target id bindings as a JSON object keyed by entity type, e.g. `{"credentials":{"":""}}`. Only `credentials` is honoured today; these bindings are applied before `--credential-matching-mode` resolution runs. | Requires the API key to hold: - `workflow:import` — always - `dataTable:create` — when the package references data tables and `--data-table-missing-mode` is `create` -- `variable:create` — when the package references variables and `--variable-missing-mode` is `create-stub`. +- `variable:create` — when the package references variables and `--variable-missing-mode` is `create-stub` +- `tag:create` — when the import would create a tag (under `--tag-missing-mode create`, the instance default; tags that match, are dropped, or belong only to skipped workflows need no scope) +- `tag:update` — when the import would rename a tag (under `--tag-conflict-policy rename`). When the import is blocked, the command exits non-zero and lists the blocking issues. Examples: diff --git a/packages/@n8n/cli/package.json b/packages/@n8n/cli/package.json index 3b60898c6d4..23d7f51694b 100644 --- a/packages/@n8n/cli/package.json +++ b/packages/@n8n/cli/package.json @@ -1,7 +1,7 @@ { "name": "@n8n/cli", "version": "0.13.0", - "description": "[beta] Client CLI for n8n — manage workflows, executions, and credentials from the terminal", + "description": "Client CLI for n8n — manage workflows, executions, and credentials from the terminal", "license": "LicenseRef-n8n-sustainable-use", "bin": { "n8n-cli": "bin/n8n-cli.mjs" diff --git a/packages/@n8n/cli/src/__tests__/client.test.ts b/packages/@n8n/cli/src/__tests__/client.test.ts index 9dfd6effc0f..3a46f68ebe2 100644 --- a/packages/@n8n/cli/src/__tests__/client.test.ts +++ b/packages/@n8n/cli/src/__tests__/client.test.ts @@ -144,6 +144,15 @@ describe('N8nClient packages', () => { const [, init] = fetchMock.mock.calls[0] as [string, RequestInit]; expect(init.body).toBe(JSON.stringify({ workflowIds: ['a'] })); }); + + it('includes includeTags=false in the body when provided', async () => { + fetchMock.mockResolvedValue(binaryResponse(200, new Uint8Array([1]))); + + await client.exportPackage({ workflowIds: ['a'], includeTags: false }); + + const [, init] = fetchMock.mock.calls[0] as [string, RequestInit]; + expect(init.body).toBe(JSON.stringify({ workflowIds: ['a'], includeTags: false })); + }); }); describe('importPackage', () => { diff --git a/packages/@n8n/cli/src/__tests__/package-error.test.ts b/packages/@n8n/cli/src/__tests__/package-error.test.ts index c3ebcf37a9d..acf1000a8d0 100644 --- a/packages/@n8n/cli/src/__tests__/package-error.test.ts +++ b/packages/@n8n/cli/src/__tests__/package-error.test.ts @@ -1,7 +1,7 @@ import { describe, it, expect } from 'vitest'; import { ApiError } from '../client'; -import { toPackagesError } from '../commands/package/shared'; +import { toPackagesError } from '../commands/package/package-error'; describe('toPackagesError', () => { it('returns non-ApiError values unchanged', () => { @@ -66,6 +66,35 @@ describe('toPackagesError', () => { expect(hint).toContain('w1, w2'); }); + it('lists tag-unresolved issues for a 409', () => { + const result = toPackagesError( + new ApiError(409, 'Import blocked', undefined, { + issues: [ + { + type: 'tag-unresolved', + kind: 'rename-drift', + sourceId: 't1', + name: 'prod', + existingName: 'production', + usedByWorkflows: ['w1'], + }, + { + type: 'tag-unresolved', + kind: 'permission-denied', + missingScope: 'tag:create', + usedByWorkflows: ['w2'], + }, + ], + }), + ); + + const hint = (result as ApiError).hint ?? ''; + expect(hint).toContain('tag "prod" (t1) unresolved (rename-drift)'); + expect(hint).toContain('w1'); + expect(hint).toContain('requires the tag:create scope'); + expect(hint).toContain('w2'); + }); + it('leaves other ApiErrors without issue details unchanged', () => { const error = new ApiError(400, 'Bad request'); expect(toPackagesError(error)).toBe(error); diff --git a/packages/@n8n/cli/src/__tests__/package-export.test.ts b/packages/@n8n/cli/src/__tests__/package-export.test.ts index 27f70bdad50..51fc853d605 100644 --- a/packages/@n8n/cli/src/__tests__/package-export.test.ts +++ b/packages/@n8n/cli/src/__tests__/package-export.test.ts @@ -15,6 +15,7 @@ interface ExportFlags { projectId?: string[]; output: string; includeVariableValues?: string; + includeTags?: string; missingWorkflowDependencyPolicy?: string; } @@ -71,6 +72,7 @@ describe('package export command', () => { workflowIds: ['wf-1', 'wf-2'], folderIds: [], includeVariableValues: true, + includeTags: true, missingWorkflowDependencyPolicy: 'fail', }); expect(mockedWriteFileSync).toHaveBeenCalledWith('/tmp/team.n8np', Buffer.from([1, 2, 3])); @@ -88,6 +90,7 @@ describe('package export command', () => { workflowIds: [], folderIds: ['fld-1'], includeVariableValues: true, + includeTags: true, missingWorkflowDependencyPolicy: 'fail', }); expect(mockedWriteFileSync).toHaveBeenCalledWith('/tmp/folders.n8np', Buffer.from([1, 2, 3])); @@ -106,6 +109,7 @@ describe('package export command', () => { workflowIds: ['wf-1'], folderIds: ['fld-1'], includeVariableValues: true, + includeTags: true, missingWorkflowDependencyPolicy: 'fail', }); }); @@ -124,6 +128,7 @@ describe('package export command', () => { workflowIds: ['wf-1'], folderIds: ['fld-1'], includeVariableValues: true, + includeTags: true, missingWorkflowDependencyPolicy: 'reference-only', }); }); @@ -139,6 +144,7 @@ describe('package export command', () => { expect(exportPackage).toHaveBeenCalledWith({ projectIds: ['proj-1', 'proj-2'], includeVariableValues: true, + includeTags: true, missingWorkflowDependencyPolicy: 'fail', }); expect(mockedWriteFileSync).toHaveBeenCalledWith('/tmp/projects.n8np', Buffer.from([1, 2, 3])); @@ -156,6 +162,7 @@ describe('package export command', () => { expect(exportPackage).toHaveBeenCalledWith({ projectIds: ['proj-1'], includeVariableValues: true, + includeTags: true, missingWorkflowDependencyPolicy: 'include-in-package', }); }); @@ -173,6 +180,7 @@ describe('package export command', () => { workflowIds: ['wf-1'], folderIds: [], includeVariableValues: false, + includeTags: true, missingWorkflowDependencyPolicy: 'fail', }); }); @@ -189,10 +197,71 @@ describe('package export command', () => { expect(exportPackage).toHaveBeenCalledWith({ projectIds: ['proj-1'], includeVariableValues: false, + includeTags: true, missingWorkflowDependencyPolicy: 'fail', }); }); + it('forwards includeTags=false when the flag is set', async () => { + const { command, exportPackage } = stubCommand({ + workflowId: ['wf-1'], + output: '/tmp/export.n8np', + includeTags: 'false', + }); + + await command.run(); + + expect(exportPackage).toHaveBeenCalledWith({ + workflowIds: ['wf-1'], + folderIds: [], + includeVariableValues: true, + includeTags: false, + missingWorkflowDependencyPolicy: 'fail', + }); + }); + + it('forwards includeTags=false for a project export', async () => { + const { command, exportPackage } = stubCommand({ + projectId: ['proj-1'], + output: '/tmp/project.n8np', + includeTags: 'false', + }); + + await command.run(); + + expect(exportPackage).toHaveBeenCalledWith({ + projectIds: ['proj-1'], + includeVariableValues: true, + includeTags: false, + missingWorkflowDependencyPolicy: 'fail', + }); + }); + + it('treats an explicit --include-tags=true like the default', async () => { + const { command, exportPackage } = stubCommand({ + workflowId: ['wf-1'], + output: '/tmp/export.n8np', + includeTags: 'true', + }); + + await command.run(); + + expect(exportPackage).toHaveBeenCalledWith({ + workflowIds: ['wf-1'], + folderIds: [], + includeVariableValues: true, + includeTags: true, + missingWorkflowDependencyPolicy: 'fail', + }); + }); + + it('declares the includeTags flag with its alias, options, and default', () => { + const flag = PackageExport.flags.includeTags; + expect(flag.aliases).toEqual(['include-tags']); + expect(flag.options).toEqual(['true', 'false']); + expect(flag.default).toBe('true'); + }); + it('treats an explicit --include-variable-values=true like the default', async () => { const { command, exportPackage } = stubCommand({ workflowId: ['wf-1'], @@ -206,6 +275,7 @@ describe('package export command', () => { workflowIds: ['wf-1'], folderIds: [], includeVariableValues: true, + includeTags: true, missingWorkflowDependencyPolicy: 'fail', }); }); @@ -275,4 +345,20 @@ describe('package export command', () => { const message = vi.mocked(internals.succeed).mock.calls[0][0]; expect(message).toBe('Exported 3 workflow(s), 1 folder(s) to /tmp/folders.n8np'); }); + + it('includes a non-zero tag count in the export message', async () => { + const exportPackage = vi.fn().mockResolvedValue({ + archive: Buffer.from([1, 2, 3]), + counts: { workflows: 2, folders: 0, credentials: 0, dataTables: 0, variables: 0, tags: 2 }, + }); + const { command, internals } = stubCommand( + { workflowId: ['wf-1', 'wf-2'], output: '/tmp/team.n8np' }, + exportPackage, + ); + + await command.run(); + + const message = vi.mocked(internals.succeed).mock.calls[0][0]; + expect(message).toBe('Exported 2 workflow(s), 2 tag(s) to /tmp/team.n8np'); + }); }); diff --git a/packages/@n8n/cli/src/__tests__/package-import.test.ts b/packages/@n8n/cli/src/__tests__/package-import.test.ts index aa94a77d70e..702c23e88dd 100644 --- a/packages/@n8n/cli/src/__tests__/package-import.test.ts +++ b/packages/@n8n/cli/src/__tests__/package-import.test.ts @@ -27,6 +27,8 @@ interface ImportFlags { dataTableSchemaConflictPolicy?: string; variableMissingMode?: string; variableParentPolicy?: string; + tagMissingMode?: string; + tagConflictPolicy?: string; bindings?: string; } @@ -74,6 +76,8 @@ describe('package import command', () => { dataTableSchemaConflictPolicy: 'keep-existing', variableMissingMode: 'create-stub', variableParentPolicy: 'global', + tagMissingMode: 'do-nothing', + tagConflictPolicy: 'rename', bindings: '{}', }); @@ -99,6 +103,8 @@ describe('package import command', () => { dataTableSchemaConflictPolicy: 'keep-existing', variableMissingMode: 'create-stub', variableParentPolicy: 'global', + tagMissingMode: 'do-nothing', + tagConflictPolicy: 'rename', bindings: '{}', }); }); @@ -115,6 +121,18 @@ describe('package import command', () => { expect(flag.default).toBeUndefined(); }); + it('defines the tag flags with kebab aliases, server option lists, and no client-side default', () => { + const missingMode = PackageImport.flags.tagMissingMode; + expect(missingMode.aliases).toEqual(['tag-missing-mode']); + expect(missingMode.options).toEqual(['create', 'do-nothing']); + expect(missingMode.default).toBeUndefined(); + + const conflictPolicy = PackageImport.flags.tagConflictPolicy; + expect(conflictPolicy.aliases).toEqual(['tag-conflict-policy']); + expect(conflictPolicy.options).toEqual(['skip', 'fail', 'rename']); + expect(conflictPolicy.default).toBeUndefined(); + }); + // Real oclif parsing exercises the flag default and alias resolution, which // the stubbed `parse` above cannot. describe('workflow-conflict-policy flag resolution (real parse)', () => { diff --git a/packages/@n8n/cli/src/client.ts b/packages/@n8n/cli/src/client.ts index 62b37cfecbe..081d70db3c5 100644 --- a/packages/@n8n/cli/src/client.ts +++ b/packages/@n8n/cli/src/client.ts @@ -25,6 +25,8 @@ export interface ImportPackageFields { dataTableSchemaConflictPolicy?: string; variableMissingMode?: string; variableParentPolicy?: string; + tagMissingMode?: string; + tagConflictPolicy?: string; } export interface ExportPackageFields { @@ -32,6 +34,7 @@ export interface ExportPackageFields { folderIds?: string[]; projectIds?: string[]; includeVariableValues?: boolean; + includeTags?: boolean; missingWorkflowDependencyPolicy?: string; } @@ -42,6 +45,8 @@ export interface ExportPackageCounts { credentials: number; dataTables: number; variables: number; + /** Absent when the server predates tag export. */ + tags?: number; } export interface ExportPackageResult { @@ -463,6 +468,7 @@ export class N8nClient { folderIds?: string[]; projectIds?: string[]; includeVariableValues?: boolean; + includeTags?: boolean; missingWorkflowDependencyPolicy?: string; } = {}; if (fields.workflowIds?.length) body.workflowIds = fields.workflowIds; @@ -470,6 +476,7 @@ export class N8nClient { if (fields.projectIds?.length) body.projectIds = fields.projectIds; // `undefined` is dropped by JSON serialization, so the API's default applies. body.includeVariableValues = fields.includeVariableValues; + body.includeTags = fields.includeTags; if (fields.missingWorkflowDependencyPolicy) body.missingWorkflowDependencyPolicy = fields.missingWorkflowDependencyPolicy; diff --git a/packages/@n8n/cli/src/commands/package/export.ts b/packages/@n8n/cli/src/commands/package/export.ts index 2dce6405d23..b00af12baba 100644 --- a/packages/@n8n/cli/src/commands/package/export.ts +++ b/packages/@n8n/cli/src/commands/package/export.ts @@ -1,7 +1,7 @@ import { Flags } from '@oclif/core'; import * as fs from 'node:fs'; -import { toPackagesError } from './shared'; +import { toPackagesError } from './package-error'; import { BaseCommand } from '../../base-command'; import type { ExportPackageCounts, ExportPackageResult } from '../../client'; @@ -19,6 +19,7 @@ function describeExport(counts: ExportPackageCounts & { projects?: number }): st if (counts.credentials) parts.push(`${counts.credentials} credential(s)`); if (counts.dataTables) parts.push(`${counts.dataTables} data table(s)`); if (counts.variables) parts.push(`${counts.variables} variable(s)`); + if (counts.tags) parts.push(`${counts.tags} tag(s)`); return parts.length > 0 ? parts.join(', ') : 'nothing'; } @@ -32,6 +33,7 @@ export default class PackageExport extends BaseCommand { '<%= config.bin %> package export --project-id=abc -o project.n8np', '<%= config.bin %> package export -p abc -p def -o projects.n8np', '<%= config.bin %> package export -w abc --include-variable-values=false -o export.n8np', + '<%= config.bin %> package export -w abc --include-tags=false -o export.n8np', ]; static override flags = { @@ -66,6 +68,12 @@ export default class PackageExport extends BaseCommand { default: 'true', aliases: ['include-variable-values'], }), + includeTags: Flags.string({ + description: 'Whether tags assigned to the exported workflows are bundled into the package', + options: ['true', 'false'], + default: 'true', + aliases: ['include-tags'], + }), missingWorkflowDependencyPolicy: Flags.string({ options: ['fail', 'reference-only', 'include-in-package'], default: 'fail', @@ -81,6 +89,7 @@ export default class PackageExport extends BaseCommand { const folderIds = flags.folderId ?? []; const projectIds = flags.projectId ?? []; const includeVariableValues = flags.includeVariableValues !== 'false'; + const includeTags = flags.includeTags !== 'false'; const missingWorkflowDependencyPolicy = flags.missingWorkflowDependencyPolicy; // A package is either loose workflows/folders or whole projects, not both. @@ -97,8 +106,14 @@ export default class PackageExport extends BaseCommand { try { result = await client.exportPackage( projectIds.length > 0 - ? { projectIds, includeVariableValues, missingWorkflowDependencyPolicy } - : { workflowIds, folderIds, includeVariableValues, missingWorkflowDependencyPolicy }, + ? { projectIds, includeVariableValues, includeTags, missingWorkflowDependencyPolicy } + : { + workflowIds, + folderIds, + includeVariableValues, + includeTags, + missingWorkflowDependencyPolicy, + }, ); } catch (error) { throw toPackagesError(error); diff --git a/packages/@n8n/cli/src/commands/package/import.ts b/packages/@n8n/cli/src/commands/package/import.ts index 5bbb449c71e..0b1cb0e60f1 100644 --- a/packages/@n8n/cli/src/commands/package/import.ts +++ b/packages/@n8n/cli/src/commands/package/import.ts @@ -2,7 +2,7 @@ import { Flags } from '@oclif/core'; import * as fs from 'node:fs'; import * as path from 'node:path'; -import { toPackagesError } from './shared'; +import { toPackagesError } from './package-error'; import { BaseCommand } from '../../base-command'; export default class PackageImport extends BaseCommand { @@ -95,6 +95,18 @@ export default class PackageImport extends BaseCommand { options: ['project', 'global'], aliases: ['variable-parent-policy'], }), + tagMissingMode: Flags.string({ + description: + 'What to do when a tag referenced by the package is absent on the target instance — tags are matched by source id, never by name (default on the instance: create). create creates the tag globally with its source id and name, and needs an API key with the tag:create scope when the import would create a tag; do-nothing imports the workflows without the missing tags and lists them under tags.skipped', + options: ['create', 'do-nothing'], + aliases: ['tag-missing-mode'], + }), + tagConflictPolicy: Flags.string({ + description: + "What to do when a referenced tag conflicts on the target instance — the same-id target tag has a different name (rename drift), or the tag's name is held by a different tag (name collision). skip (instance default) imports the workflows without the conflicted tags and lists them under tags.skipped; fail rejects the import; rename renames a drifted target tag to the package name (needs an API key with the tag:update scope when the import would rename a tag) — name collisions still reject the import", + options: ['skip', 'fail', 'rename'], + aliases: ['tag-conflict-policy'], + }), bindings: Flags.string({ description: 'Explicit source→target id bindings as a JSON object keyed by entity type, e.g. \'{"credentials":{"":""}}\'. Applied before credential-matching-mode resolution.', @@ -130,6 +142,8 @@ export default class PackageImport extends BaseCommand { dataTableSchemaConflictPolicy: flags.dataTableSchemaConflictPolicy, variableMissingMode: flags.variableMissingMode, variableParentPolicy: flags.variableParentPolicy, + tagMissingMode: flags.tagMissingMode, + tagConflictPolicy: flags.tagConflictPolicy, bindings: flags.bindings, }, ); diff --git a/packages/@n8n/cli/src/commands/package/shared.ts b/packages/@n8n/cli/src/commands/package/package-error.ts similarity index 80% rename from packages/@n8n/cli/src/commands/package/shared.ts rename to packages/@n8n/cli/src/commands/package/package-error.ts index 3c243788e31..c77e899cfb3 100644 --- a/packages/@n8n/cli/src/commands/package/shared.ts +++ b/packages/@n8n/cli/src/commands/package/package-error.ts @@ -14,6 +14,14 @@ type BlockingIssue = nodeType: string; typeVersion: number; usedByWorkflows: string[]; + } + | { + type: 'tag-unresolved'; + kind: string; + sourceId?: string; + name?: string; + missingScope?: string; + usedByWorkflows: string[]; }; function formatIssue(issue: unknown): string { @@ -34,6 +42,13 @@ function formatIssue(issue: unknown): string { const usedBy = Array.isArray(it.usedByWorkflows) ? it.usedByWorkflows.join(', ') : ''; return `node type ${it.nodeType} @ v${it.typeVersion} missing on this instance, used by workflow(s) ${usedBy}`; } + if (it.type === 'tag-unresolved') { + const usedBy = Array.isArray(it.usedByWorkflows) ? it.usedByWorkflows.join(', ') : ''; + if (it.kind === 'permission-denied') { + return `tag import requires the ${it.missingScope} scope, needed by workflow(s) ${usedBy}`; + } + return `tag "${it.name}" (${it.sourceId}) unresolved (${it.kind}), used by workflow(s) ${usedBy}`; + } return JSON.stringify(issue); } diff --git a/packages/@n8n/config/src/configs/evaluation.config.ts b/packages/@n8n/config/src/configs/evaluation.config.ts index 9f52d07f56b..91e3effc395 100644 --- a/packages/@n8n/config/src/configs/evaluation.config.ts +++ b/packages/@n8n/config/src/configs/evaluation.config.ts @@ -51,4 +51,11 @@ export class EvaluationConfig { */ @Env('N8N_AGENT_EVALS_ENABLED') agentEvalsEnabled: boolean = false; + + /** + * Wall-clock ceiling on one agent-eval run, in minutes; non-positive disables. + * Stops further cases starting — an in-flight one runs out its own timeout. + */ + @Env('N8N_AGENT_EVALS_RUN_TIMEOUT_MINUTES') + agentEvalsRunTimeoutMinutes: number = 60; } diff --git a/packages/@n8n/config/src/configs/executions.config.ts b/packages/@n8n/config/src/configs/executions.config.ts index 3d99a7aae51..bca2b58044d 100644 --- a/packages/@n8n/config/src/configs/executions.config.ts +++ b/packages/@n8n/config/src/configs/executions.config.ts @@ -2,6 +2,7 @@ import { Time } from '@n8n/constants'; import z from 'zod'; import { Config, Env, Nested } from '../decorators'; +import { positiveIntSchema } from '../schemas'; @Config class PruningIntervalsConfig { @@ -163,4 +164,15 @@ export class ExecutionsConfig { */ @Env('EXECUTIONS_DATA_MAX_DISPLAY_SIZE') maxDisplaySize: number = 100 * 1024 * 1024; // 100 MB + + /** + * Maximum size in MiB of a response body a worker relays back to main in scaling mode. + * A larger response fails the node instead of being sent through the queue. + * + * Relaying costs the queue a multiple of the body size, + * so size this against the memory available to it, + * and return large payloads as binary data to have them streamed from storage instead. + */ + @Env('N8N_WEBHOOK_RESPONSE_RELAY_SIZE_MAX', positiveIntSchema) + webhookResponseRelaySizeMaxMiB: number = 64; } diff --git a/packages/@n8n/config/src/configs/instance-settings-loader.config.ts b/packages/@n8n/config/src/configs/instance-settings-loader.config.ts index 2df75a4737b..266f63ebbbf 100644 --- a/packages/@n8n/config/src/configs/instance-settings-loader.config.ts +++ b/packages/@n8n/config/src/configs/instance-settings-loader.config.ts @@ -59,6 +59,13 @@ export class InstanceSettingsLoaderConfig { @Env('N8N_SSO_OIDC_ACR_VALUES') oidcAcrValues: string = ''; + /** Space-separated additional scopes appended to the OIDC authorization request. */ + @Env('N8N_SSO_OIDC_ADDITIONAL_SCOPES') + oidcAdditionalScopes: string = ''; + + @Env('N8N_SSO_OIDC_RP_INITIATED_LOGOUT_ENABLED') + oidcRpInitiatedLogoutEnabled: boolean = false; + /** * When true, security policy settings are managed via environment variables. * On every startup the security policy will be overridden by env vars. diff --git a/packages/@n8n/config/src/configs/logging.config.ts b/packages/@n8n/config/src/configs/logging.config.ts index 0f7bf27221f..e8a125a3b1f 100644 --- a/packages/@n8n/config/src/configs/logging.config.ts +++ b/packages/@n8n/config/src/configs/logging.config.ts @@ -51,6 +51,7 @@ export const LOG_SCOPES = [ 'poll-trigger', 'metrics', 'scheduler', + 'enqueued-execution-recovery', ] as const; export type LogScope = (typeof LOG_SCOPES)[number]; diff --git a/packages/@n8n/config/src/configs/scheduler.config.ts b/packages/@n8n/config/src/configs/scheduler.config.ts index 3ead1d7fae1..cbcbafb6dc1 100644 --- a/packages/@n8n/config/src/configs/scheduler.config.ts +++ b/packages/@n8n/config/src/configs/scheduler.config.ts @@ -1,4 +1,4 @@ -import { Time } from '@n8n/constants'; +import { DEFAULT_MISFIRE_GRACE_SECONDS, Time } from '@n8n/constants'; import { z } from 'zod'; import { Config, Env } from '../decorators'; @@ -287,4 +287,16 @@ export class SchedulerConfig { */ @Env('N8N_SCHEDULER_MAX_ATTEMPTS', positiveIntSchema) maxAttempts: number = 5; + + /** + * How late, in seconds, a scheduled run may start and still count as on time. A + * run later than this counts as missed, and the schedule's misfire policy decides + * whether it still runs at all. Must be greater than 0, and capped at 30 days. + * + * Should exceed {@link executorIntervalSeconds} and {@link materializationWindowSeconds}: + * a run has to survive until the next executor tick to be offered at all. The + * scheduler warns at startup if it doesn't. + */ + @Env('N8N_SCHEDULER_MISFIRE_GRACE', positiveIntSchema.max(30 * Time.days.toSeconds)) + misfireGraceSeconds: number = DEFAULT_MISFIRE_GRACE_SECONDS; } diff --git a/packages/@n8n/config/test/config.test.ts b/packages/@n8n/config/test/config.test.ts index aae904c89f7..b3985fdf7c1 100644 --- a/packages/@n8n/config/test/config.test.ts +++ b/packages/@n8n/config/test/config.test.ts @@ -467,11 +467,13 @@ describe('GlobalConfig', () => { enabledForPollTriggers: false, allowSkipDurableScheduler: false, maxAttempts: 5, + misfireGraceSeconds: 60, }, evaluation: { collectionsEnabled: false, configEvalsEnabled: false, agentEvalsEnabled: false, + agentEvalsRunTimeoutMinutes: 60, }, generic: { timezone: 'America/New_York', @@ -536,6 +538,7 @@ describe('GlobalConfig', () => { saveExecutionProgress: false, saveDataManualExecutions: true, maxDisplaySize: 100 * 1024 * 1024, + webhookResponseRelaySizeMaxMiB: 64, }, diagnostics: { enabled: true, @@ -641,6 +644,8 @@ describe('GlobalConfig', () => { oidcLoginEnabled: false, oidcPrompt: 'select_account', oidcAcrValues: '', + oidcAdditionalScopes: '', + oidcRpInitiatedLogoutEnabled: false, ssoUserRoleProvisioning: 'disabled', securityPolicyManagedByEnv: false, mfaEnforcedEnabled: false, diff --git a/packages/@n8n/constants/src/scheduler.ts b/packages/@n8n/constants/src/scheduler.ts index e6b2fe1c7d0..54bc44d1386 100644 --- a/packages/@n8n/constants/src/scheduler.ts +++ b/packages/@n8n/constants/src/scheduler.ts @@ -48,6 +48,33 @@ export type RecurringCronUnit = (typeof RecurringCronUnit)[keyof typeof Recurrin export const RecurringCronUnitList = Object.values(RecurringCronUnit); +/** + * What a schedule does with occurrences that missed their grace window: + * - `coalesce`: run the most recent one and drop the rest + * - `skip`: drop all of them and resume from the next occurrence + * + * Either way the schedule's clock advances past the backlog, so it never replays. + * + * A one-off schedule has no next occurrence to resume from, so `coalesce` still runs + * it, late, while `skip` discards it for good. + */ +export const ScheduledJobMisfirePolicy = { + Coalesce: 'coalesce', + Skip: 'skip', +} as const; + +export type ScheduledJobMisfirePolicy = + (typeof ScheduledJobMisfirePolicy)[keyof typeof ScheduledJobMisfirePolicy]; + +/** + * How late an occurrence may be before its schedule's misfire policy applies, + * for a job provisioned without an explicit grace. + * + * Migrations pin their own copy of this value: a column's default at migration + * time must stay fixed even if this constant changes later. + */ +export const DEFAULT_MISFIRE_GRACE_SECONDS = 60; + /** * Where a scheduled task is in its lifecycle, from waiting to run to a final outcome. */ diff --git a/packages/@n8n/db/src/entities/binary-data-file.ts b/packages/@n8n/db/src/entities/binary-data-file.ts index 1d0393b5a6b..7206be48ad2 100644 --- a/packages/@n8n/db/src/entities/binary-data-file.ts +++ b/packages/@n8n/db/src/entities/binary-data-file.ts @@ -3,7 +3,12 @@ import { z } from 'zod'; import { BinaryColumn, WithTimestamps } from './abstract-entity'; -export const SourceTypeSchema = z.enum(['execution', 'chat_message_attachment', 'agent_file']); +export const SourceTypeSchema = z.enum([ + 'execution', + 'chat_message_attachment', + 'agent_file', + 'agent_chat_attachment', +]); export type SourceType = z.infer; diff --git a/packages/@n8n/db/src/entities/index.ts b/packages/@n8n/db/src/entities/index.ts index 2e6cc5b16d1..c3a6bed9289 100644 --- a/packages/@n8n/db/src/entities/index.ts +++ b/packages/@n8n/db/src/entities/index.ts @@ -33,7 +33,12 @@ import { ProjectSecretsProviderAccess } from './project-secrets-provider-access' import type { SecretsProviderAccessRole } from './project-secrets-provider-access'; import { Role } from './role'; import { RoleMappingRule } from './role-mapping-rule'; -import { ScheduledJob, ScheduledJobKind, ScheduledJobKindList } from './scheduled-job'; +import { + ScheduledJob, + ScheduledJobKind, + ScheduledJobKindList, + ScheduledJobMisfirePolicy, +} from './scheduled-job'; import { ScheduledTask, ScheduledTaskStatus, @@ -117,6 +122,7 @@ export { ScheduledJob, ScheduledJobKind, ScheduledJobKindList, + ScheduledJobMisfirePolicy, ScheduledTask, ScheduledTaskStatus, ScheduledTaskStatusList, diff --git a/packages/@n8n/db/src/entities/scheduled-job.ts b/packages/@n8n/db/src/entities/scheduled-job.ts index ae7b594d46f..f59d6451165 100644 --- a/packages/@n8n/db/src/entities/scheduled-job.ts +++ b/packages/@n8n/db/src/entities/scheduled-job.ts @@ -1,9 +1,11 @@ +import { ScheduledJobMisfirePolicy } from '@n8n/constants'; import type { ScheduledJobKind, RecurringCronUnit } from '@n8n/constants'; import { Column, Entity, Index, PrimaryGeneratedColumn } from '@n8n/typeorm'; import { DateTimeColumn, JsonColumn, WithTimestamps } from './abstract-entity'; export { ScheduledJobKind, ScheduledJobKindList } from '@n8n/constants'; +export { ScheduledJobMisfirePolicy } from '@n8n/constants'; /** * A scheduled job: the rule for when something should run, @@ -131,8 +133,8 @@ export class ScheduledJob extends WithTimestamps { nextRunAt: Date | null; /** - * Last time an occurrence was materialized, - * used to recompute {@link nextRunAt}. + * The latest instant the job's clock has advanced past. Not proof a run + * happened: a discarded occurrence still advances it. */ @DateTimeColumn({ nullable: true }) lastFiredAt: Date | null; @@ -142,4 +144,22 @@ export class ScheduledJob extends WithTimestamps { */ @Column({ type: 'int', default: 1 }) maxAttempts: number; + + /** What happens to an occurrence overdue by more than {@link misfireGraceSeconds}. */ + @Column({ type: 'varchar', length: 16, default: ScheduledJobMisfirePolicy.Coalesce }) + misfirePolicy: ScheduledJobMisfirePolicy; + + /** + * How late an occurrence may still be and count as on time, so that an ordinary + * restart or a slow pass never reaches {@link misfirePolicy}. + * + * Copied from {@link SchedulerConfig.misfireGraceSeconds} onto each row rather than + * read live, so every instance agrees on one job's deadline through a rolling + * config change. + * + * Pinned to a literal, not the shared constant, so a later change to the + * constant can't retroactively change what this entity declares. + */ + @Column({ type: 'int', default: 60 }) + misfireGraceSeconds: number; } diff --git a/packages/@n8n/db/src/entities/scheduled-task.ts b/packages/@n8n/db/src/entities/scheduled-task.ts index 90867299bdb..1707357c062 100644 --- a/packages/@n8n/db/src/entities/scheduled-task.ts +++ b/packages/@n8n/db/src/entities/scheduled-task.ts @@ -41,6 +41,7 @@ export { @Index(['runAt'], { where: '"status" = \'pending\'' }) @Index(['leaseExpiresAt'], { where: '"status" = \'running\'' }) @Index(['finishedAt'], { where: '"finishedAt" IS NOT NULL' }) +@Index(['missedAfter'], { where: '"status" = \'pending\' AND "missedAfter" IS NOT NULL' }) export class ScheduledTask extends WithCreatedAt { /** * 64-bit identity. @@ -99,7 +100,8 @@ export class ScheduledTask extends WithCreatedAt { /** * Earliest time a worker may pick this run up. - * Starts equal to {@link scheduledFor}. + * Starts equal to {@link scheduledFor}, except for a catch-up run, which becomes + * visible when it was materialized rather than when it was originally due. * If a try fails it is pushed later so the retry waits a bit before running again. */ @DateTimeColumn() @@ -191,4 +193,13 @@ export class ScheduledTask extends WithCreatedAt { */ @Column({ type: 'text', nullable: true }) errorMessage: string | null; + + /** + * Deadline past which this occurrence counts as missed: {@link runAt} plus the + * job's misfire grace, resolved at materialization so the claim compares one + * column against the clock instead of joining to the job. `null` means no + * deadline. + */ + @DateTimeColumn({ nullable: true }) + missedAfter: Date | null; } diff --git a/packages/@n8n/db/src/entities/workflow-publication-trigger-status.ts b/packages/@n8n/db/src/entities/workflow-publication-trigger-status.ts index e52d273d2ff..df64dda797e 100644 --- a/packages/@n8n/db/src/entities/workflow-publication-trigger-status.ts +++ b/packages/@n8n/db/src/entities/workflow-publication-trigger-status.ts @@ -11,7 +11,9 @@ export type WorkflowPublicationTriggerStatusType = 'activated' | 'failed'; * or `trigger` function are registered `in-memory` on the owning instance, * nodes with only a `webhook` function are `persisted` rows in * `webhook_entity`. Reconciliation diffs the `in-memory` ones against the - * registry. + * registry. `persisted` also covers the no-op pseudo triggers (manual, + * executeWorkflow, error): the execution engine fires them directly, so + * neither leader handoff nor reconciliation has registry work to redo. */ export type WorkflowPublicationTriggerKind = 'in-memory' | 'persisted'; diff --git a/packages/@n8n/db/src/migrations/common/1785247194306-CrashStaleEnqueuedExecutions.ts b/packages/@n8n/db/src/migrations/common/1785247194306-CrashStaleEnqueuedExecutions.ts new file mode 100644 index 00000000000..b4927662b18 --- /dev/null +++ b/packages/@n8n/db/src/migrations/common/1785247194306-CrashStaleEnqueuedExecutions.ts @@ -0,0 +1,70 @@ +import { GlobalConfig } from '@n8n/config'; +import { Container } from '@n8n/di'; + +import type { IrreversibleMigration, MigrationContext } from '../migration-types'; + +/** + * Executions enqueued longer ago than this are considered abandoned. Kept generous so + * a rolling deploy cannot crash an execution a worker is legitimately about to pick + * up - during an upgrade a `new` row may be seconds old and perfectly healthy. + * Inlined rather than imported, since a migration is a snapshot. + */ +const MAX_ENQUEUED_EXECUTION_AGE_MS = 7 * 24 * 60 * 60 * 1000; + +/** + * Clears the backlog of executions abandoned in the `new` (enqueued) status. + * + * Until this release nothing moved an enqueued execution out of `new` after a + * restart, so every shutdown mid-enqueue leaked a row that sits at "Queued" in the + * UI forever. Startup recovery now replays such executions, but replaying work + * enqueued before a week-long gap would run against long-stale trigger data, so the + * accumulated rows are surfaced as `crashed` - visible and retryable - instead. + * + * This runs as a migration rather than at startup because the backlog is a one-time + * artifact of the bug. Queue mode leaves these rows untouched because enqueued + * executions are owned by workers. + * + * Irreversible: a `down()` cannot tell the rows crashed here apart from those that + * crashed on their own. + */ +export class CrashStaleEnqueuedExecutions1785247194306 implements IrreversibleMigration { + async up({ escape, runQuery, logger, migrationName }: MigrationContext) { + if (Container.get(GlobalConfig).executions.mode === 'queue') { + logger.info(`[${migrationName}] Skipping stale enqueued execution cleanup in queue mode`); + return; + } + + const table = escape.tableName('execution_entity'); + const status = escape.columnName('status'); + const createdAt = escape.columnName('createdAt'); + const stoppedAt = escape.columnName('stoppedAt'); + const waitTill = escape.columnName('waitTill'); + + const now = new Date(); + const cutoff = new Date(now.getTime() - MAX_ENQUEUED_EXECUTION_AGE_MS); + const staleCondition = `${status} = 'new' AND ${createdAt} < :cutoff`; + + // Counted up front because `runQuery` surfaces no affected-row count, and an + // operator upgrading a long-broken instance needs to see how many were flipped. + const rows = await runQuery>( + `SELECT COUNT(*) AS count FROM ${table} WHERE ${staleCondition}`, + { cutoff }, + ); + const staleCount = Number(rows[0].count); + + if (staleCount === 0) return; + + // `startedAt` stays null: these executions never ran. `waitTill` is cleared so + // nothing can pick a crashed row back up. + await runQuery( + `UPDATE ${table} + SET ${status} = 'crashed', ${stoppedAt} = :now, ${waitTill} = NULL + WHERE ${staleCondition}`, + { now, cutoff }, + ); + + logger.info( + `[${migrationName}] Marked ${staleCount} execution(s) enqueued before ${cutoff.toISOString()} as crashed`, + ); + } +} diff --git a/packages/@n8n/db/src/migrations/common/1785247194307-AddMisfirePolicyToScheduler.ts b/packages/@n8n/db/src/migrations/common/1785247194307-AddMisfirePolicyToScheduler.ts new file mode 100644 index 00000000000..051dfe674c0 --- /dev/null +++ b/packages/@n8n/db/src/migrations/common/1785247194307-AddMisfirePolicyToScheduler.ts @@ -0,0 +1,141 @@ +import type { MigrationContext, ReversibleMigration } from '../migration-types'; + +const jobTable = 'scheduled_job'; +const taskTable = 'scheduled_task'; +const policyColumn = 'misfirePolicy'; +const graceColumn = 'misfireGraceSeconds'; +const missedAfterColumn = 'missedAfter'; + +// Pinned rather than read from `ScheduledJobMisfirePolicy`: a migration has to keep +// meaning what it meant when it ran, so a later addition to the enum needs its own +// migration and every instance converges on the same CHECK. +const POLICY_VALUES = ['coalesce', 'skip']; +const DEFAULT_POLICY = 'coalesce'; +const DEFAULT_GRACE_SECONDS = 60; + +/** + * Adds the misfire policy to `scheduled_job`, and each occurrence's own deadline to + * `scheduled_task`. + * + * The policy decides what happens to occurrences that came due while nothing was + * there to run them and are now past their grace window: `coalesce` records a single + * catch-up run, `skip` records none. `missedAfter` is that grace resolved to an + * absolute instant, so the claim compares one indexed column against the clock. + * + * Both job columns are NOT NULL with defaults, so existing rows need no backfill. + * `missedAfter` is nullable, which the claim reads as "no deadline". + */ +export class AddMisfirePolicyToScheduler1785247194307 implements ReversibleMigration { + async up(context: MigrationContext) { + // A rename of this migration's file leaves an instance that already ran it + // under the old name with no record of that under the current one, so a + // plain rerun here would fail on columns that already exist. Guard on the + // first one instead: it was added atomically with everything else this + // migration does, so its presence means the rest is already done too. + const alreadyApplied = await context.queryRunner.hasColumn( + `${context.tablePrefix}${jobTable}`, + policyColumn, + ); + if (alreadyApplied) return; + + await this.addJobPolicyColumns(context); + await this.addTaskDeadlineColumn(context); + // Partial, so it holds only the rows the reaper sweeps and orders by, not the + // whole table. + await context.schemaBuilder.createIndex( + taskTable, + [missedAfterColumn], + false, + undefined, + `"status" = 'pending' AND ${context.escape.columnName(missedAfterColumn)} IS NOT NULL`, + ); + if (context.isPostgres) { + await this.commentColumns(context); + } + } + + async down({ queryRunner, runQuery, escape, tablePrefix, schemaBuilder }: MigrationContext) { + await schemaBuilder.dropIndex(taskTable, [missedAfterColumn]); + // The columns' own CHECKs go first: TypeORM's SQLite rebuild keeps a check + // referencing a dropped column, and every row copy then fails. + await queryRunner.dropCheckConstraint( + `${tablePrefix}${jobTable}`, + `CHK_${tablePrefix}${jobTable}_${policyColumn}`, + ); + await queryRunner.dropCheckConstraint( + `${tablePrefix}${jobTable}`, + `CHK_${tablePrefix}${jobTable}_${graceColumn}`, + ); + await schemaBuilder.dropColumns(jobTable, [policyColumn, graceColumn], { + recreatesOnSqlite: true, + }); + // Raw `DROP COLUMN`, for the same reason `up` adds it raw: the column carries no + // constraint, so nothing needs the SQLite rebuild that `dropColumns` would do to + // the largest table here. + await runQuery( + `ALTER TABLE ${escape.tableName(taskTable)} DROP COLUMN ${escape.columnName(missedAfterColumn)}`, + ); + } + + /** + * Raw `ADD COLUMN` rather than the schema builder's `addColumns`, so SQLite does + * not rebuild the table to add two columns that carry their own defaults. The + * CHECKs are named the way the DSL's `withEnumCheck` names its own, so a later + * migration can find them with `dropEnumCheck`. + */ + private async addJobPolicyColumns({ runQuery, escape, tablePrefix }: MigrationContext) { + const tableName = escape.tableName(jobTable); + const policy = escape.columnName(policyColumn); + const grace = escape.columnName(graceColumn); + const policyValues = POLICY_VALUES.map((value) => `'${value}'`).join(', '); + + await runQuery( + `ALTER TABLE ${tableName} ADD COLUMN ${policy} varchar(16) NOT NULL DEFAULT '${DEFAULT_POLICY}' ` + + `CONSTRAINT "CHK_${tablePrefix}${jobTable}_${policyColumn}" CHECK (${policy} IN (${policyValues}))`, + ); + // Zero would put every occurrence past its deadline the instant it came due, so + // `skip` would discard each one and the schedule would never fire again. + await runQuery( + `ALTER TABLE ${tableName} ADD COLUMN ${grace} int NOT NULL DEFAULT ${DEFAULT_GRACE_SECONDS} ` + + `CONSTRAINT "CHK_${tablePrefix}${jobTable}_${graceColumn}" CHECK (${grace} > 0)`, + ); + } + + /** + * Nullable and unconstrained: the materializer computes the value, and rows that + * predate this migration legitimately have none. + */ + private async addTaskDeadlineColumn({ runQuery, escape, isPostgres }: MigrationContext) { + const type = isPostgres ? 'timestamptz(3)' : 'datetime(3)'; + await runQuery( + `ALTER TABLE ${escape.tableName(taskTable)} ADD COLUMN ${escape.columnName(missedAfterColumn)} ${type}`, + ); + } + + private async commentColumns({ runQuery, escape }: MigrationContext) { + // Doubled quotes escape a literal apostrophe inside the SQL string. + const comments: Array<[string, string, string]> = [ + [ + jobTable, + policyColumn, + "What to do with occurrences that came due while nothing ran them: ''coalesce'' records a single catch-up run, ''skip'' records none.", + ], + [ + jobTable, + graceColumn, + 'How late an occurrence may be before the misfire policy applies to it; an ordinary restart stays inside it.', + ], + [ + taskTable, + missedAfterColumn, + "When this occurrence stops being worth running (runAt plus the job''s misfire grace). NULL means no deadline.", + ], + ]; + + for (const [table, column, comment] of comments) { + await runQuery( + `COMMENT ON COLUMN ${escape.tableName(table)}.${escape.columnName(column)} IS '${comment}'`, + ); + } + } +} diff --git a/packages/@n8n/db/src/migrations/common/1785255306000-CreateAgentChatAttachmentsTable.ts b/packages/@n8n/db/src/migrations/common/1785255306000-CreateAgentChatAttachmentsTable.ts new file mode 100644 index 00000000000..0dd9f2ff963 --- /dev/null +++ b/packages/@n8n/db/src/migrations/common/1785255306000-CreateAgentChatAttachmentsTable.ts @@ -0,0 +1,86 @@ +import type { MigrationContext, ReversibleMigration } from '../migration-types'; + +const binaryDataTableName = 'binary_data'; +const sourceTypeColumn = 'sourceType'; +const sourceTypesBefore = ['execution', 'chat_message_attachment', 'agent_file']; +const sourceTypesAfter = [...sourceTypesBefore, 'agent_chat_attachment']; + +export class CreateAgentChatAttachmentsTable1785255306000 implements ReversibleMigration { + async up(ctx: MigrationContext) { + const { createTable, column } = ctx.schemaBuilder; + + await createTable('agent_chat_attachments') + .withColumns( + column('id').varchar(16).primary.comment('Application-generated n8n nano ID'), + // varchar(36) mirrors the referenced agents.id primary key. + column('agentId') + .varchar(36) + .comment('Agent the attachment was sent to, when persisted (null for inline agents)'), + column('projectId') + .varchar(36) + .notNull.comment('Project owning the conversation; authorization scope for downloads'), + // Thread ids are scoped with prefixes/user ids on some surfaces + // (e.g. `test-:`) — same width as agent_execution.threadId. + column('threadId') + .varchar(128) + .notNull.comment('Conversation thread the file belongs to'), + column('resourceId') + .varchar(255) + .comment('Per-user scope within the thread (platform user), when known'), + column('binaryDataId').text.notNull.comment( + 'Opaque BinaryDataService reference (mode-prefixed, e.g. "filesystem-v2:"); not an FK to binary_data, which only has rows in DB storage mode', + ), + column('fileName').varchar(255).notNull, + column('mimeType').varchar(255).notNull, + column('fileSizeBytes').int.notNull.comment('Uploaded file size in bytes'), + column('source') + .varchar(32) + .notNull.comment('Surface the file arrived from, e.g. "chat", "slack", "telegram"'), + ) + .withIndexOn(['projectId', 'threadId']) + .withIndexOn(['agentId', 'threadId']) + .withForeignKey('agentId', { + tableName: 'agents', + columnName: 'id', + onDelete: 'CASCADE', + }) + .withForeignKey('projectId', { + tableName: 'project', + columnName: 'id', + onDelete: 'CASCADE', + }).withTimestamps; + + await this.replaceSourceTypeCheck(ctx, sourceTypesAfter); + + await ctx.schemaBuilder.addColumns( + 'agent_execution', + [ + column('attachments').json.comment( + 'Metadata of files attached to the user turn ({id, fileName, mimeType, sizeBytes}[]); bytes live in BinaryDataService', + ), + ], + { recreatesOnSqlite: true }, + ); + } + + async down(ctx: MigrationContext) { + await ctx.schemaBuilder.dropColumns('agent_execution', ['attachments'], { + recreatesOnSqlite: true, + }); + await ctx.runQuery( + `DELETE FROM ${ctx.escape.tableName(binaryDataTableName)} WHERE ${ctx.escape.columnName(sourceTypeColumn)} = 'agent_chat_attachment'`, + ); + await this.replaceSourceTypeCheck(ctx, sourceTypesBefore); + await ctx.schemaBuilder.dropTable('agent_chat_attachments'); + } + + private async replaceSourceTypeCheck( + { schemaBuilder: { addEnumCheck, dropEnumCheck } }: MigrationContext, + sourceTypes: string[], + ) { + await dropEnumCheck(binaryDataTableName, sourceTypeColumn, { recreatesOnSqlite: true }); + await addEnumCheck(binaryDataTableName, sourceTypeColumn, sourceTypes, { + recreatesOnSqlite: true, + }); + } +} diff --git a/packages/@n8n/db/src/migrations/postgresdb/index.ts b/packages/@n8n/db/src/migrations/postgresdb/index.ts index 5f361ee11cc..f551212f1b0 100644 --- a/packages/@n8n/db/src/migrations/postgresdb/index.ts +++ b/packages/@n8n/db/src/migrations/postgresdb/index.ts @@ -233,6 +233,9 @@ import { CreateAgentEvalTables1784815940112 } from '../common/1784815940112-Crea import { AddAvailableInMcpToAgents1784897791636 } from '../common/1784897791636-AddAvailableInMcpToAgents'; import { AddIsFirstPartyToOAuthClients1785162364001 } from '../common/1785162364001-AddIsFirstPartyToOAuthClients'; import { AddAgentFileStorageColumns1785186578138 } from '../common/1785186578138-AddAgentFileStorageColumns'; +import { CrashStaleEnqueuedExecutions1785247194306 } from '../common/1785247194306-CrashStaleEnqueuedExecutions'; +import { AddMisfirePolicyToScheduler1785247194307 } from '../common/1785247194307-AddMisfirePolicyToScheduler'; +import { CreateAgentChatAttachmentsTable1785255306000 } from '../common/1785255306000-CreateAgentChatAttachmentsTable'; import type { Migration } from '../migration-types'; export const postgresMigrations: Migration[] = [ @@ -471,4 +474,7 @@ export const postgresMigrations: Migration[] = [ ChangeInstalledNodeVersionType1785162364000, AddIsFirstPartyToOAuthClients1785162364001, AddAgentFileStorageColumns1785186578138, + CrashStaleEnqueuedExecutions1785247194306, + AddMisfirePolicyToScheduler1785247194307, + CreateAgentChatAttachmentsTable1785255306000, ]; diff --git a/packages/@n8n/db/src/migrations/sqlite/1785247194307-AddMisfirePolicyToScheduler.ts b/packages/@n8n/db/src/migrations/sqlite/1785247194307-AddMisfirePolicyToScheduler.ts new file mode 100644 index 00000000000..b91eea84b17 --- /dev/null +++ b/packages/@n8n/db/src/migrations/sqlite/1785247194307-AddMisfirePolicyToScheduler.ts @@ -0,0 +1,14 @@ +import { AddMisfirePolicyToScheduler1785247194307 as BaseMigration } from '../common/1785247194307-AddMisfirePolicyToScheduler'; + +/** + * Only the rollback needs this: `up` adds its columns with raw `ALTER TABLE`, which + * SQLite does in place. + * + * On SQLite, dropping the policy CHECK constraints and their columns recreates the + * whole `scheduled_job` table. `scheduled_task` references it with ON DELETE CASCADE, + * so the recreate's DROP would cascade and wipe queued tasks. Disable foreign keys for + * the migration so the drop stays local to `scheduled_job`. + */ +export class AddMisfirePolicyToScheduler1785247194307 extends BaseMigration { + withFKsDisabled = true as const; +} diff --git a/packages/@n8n/db/src/migrations/sqlite/index.ts b/packages/@n8n/db/src/migrations/sqlite/index.ts index 96343221273..760bfcf1cbe 100644 --- a/packages/@n8n/db/src/migrations/sqlite/index.ts +++ b/packages/@n8n/db/src/migrations/sqlite/index.ts @@ -61,6 +61,7 @@ import { AddRecurringCronScheduleKind1784000000045 } from './1784000000045-AddRe import { AddAvailableInMcpToAgents1784897791636 } from './1784897791636-AddAvailableInMcpToAgents'; import { ChangeInstalledNodeVersionType1785162364000 } from './1785162364000-ChangeInstalledNodeVersionType'; import { AddIsFirstPartyToOAuthClients1785162364001 } from './1785162364001-AddIsFirstPartyToOAuthClients'; +import { AddMisfirePolicyToScheduler1785247194307 } from './1785247194307-AddMisfirePolicyToScheduler'; import { UniqueWorkflowNames1620821879465 } from '../common/1620821879465-UniqueWorkflowNames'; import { UpdateWorkflowCredentials1630330987096 } from '../common/1630330987096-UpdateWorkflowCredentials'; import { AddNodeIds1658930531669 } from '../common/1658930531669-AddNodeIds'; @@ -225,6 +226,8 @@ import { AddStoredAtToAgentExecution1784815940110 } from '../common/178481594011 import { AddInstanceCredentials1784815940111 } from '../common/1784815940111-AddInstanceCredentials'; import { CreateAgentEvalTables1784815940112 } from '../common/1784815940112-CreateAgentEvalTables'; import { AddAgentFileStorageColumns1785186578138 } from '../common/1785186578138-AddAgentFileStorageColumns'; +import { CrashStaleEnqueuedExecutions1785247194306 } from '../common/1785247194306-CrashStaleEnqueuedExecutions'; +import { CreateAgentChatAttachmentsTable1785255306000 } from '../common/1785255306000-CreateAgentChatAttachmentsTable'; const sqliteMigrations: Migration[] = [ InitialMigration1588102412422, @@ -453,6 +456,9 @@ const sqliteMigrations: Migration[] = [ ChangeInstalledNodeVersionType1785162364000, AddIsFirstPartyToOAuthClients1785162364001, AddAgentFileStorageColumns1785186578138, + CrashStaleEnqueuedExecutions1785247194306, + AddMisfirePolicyToScheduler1785247194307, + CreateAgentChatAttachmentsTable1785255306000, ]; export { sqliteMigrations }; diff --git a/packages/@n8n/db/src/repositories/__tests__/agent-eval-dataset.repository.test.ts b/packages/@n8n/db/src/repositories/__tests__/agent-eval-dataset.repository.test.ts index 24b677aa244..b7d6d885bed 100644 --- a/packages/@n8n/db/src/repositories/__tests__/agent-eval-dataset.repository.test.ts +++ b/packages/@n8n/db/src/repositories/__tests__/agent-eval-dataset.repository.test.ts @@ -87,4 +87,80 @@ describe('AgentEvalDatasetRepository', () => { expect(entityManager.findOneBy.mock.calls[0]?.[1]).toEqual({ id: 'ds-1' }); }); }); + + describe('findByIdAndAgentId', () => { + it('filters on the agent as well as the id', async () => { + entityManager.findOneBy.mockResolvedValueOnce(null); + + await repo.findByIdAndAgentId('ds-1', 'agent-1'); + + expect(entityManager.findOneBy.mock.calls[0]?.[1]).toEqual({ + id: 'ds-1', + agentId: 'agent-1', + }); + }); + }); + + describe('updateDataset', () => { + const updated = { affected: 1, generatedMaps: [], raw: [] }; + + it('writes only the provided fields, scoped to the agent', async () => { + entityManager.update.mockResolvedValueOnce(updated); + entityManager.findOneBy.mockResolvedValueOnce({ id: 'ds-1' } as AgentEvalDataset); + + await repo.updateDataset('ds-1', 'agent-1', { name: 'new' }); + + const [, criteria, patch] = entityManager.update.mock.calls[0] ?? []; + expect(criteria).toEqual({ id: 'ds-1', agentId: 'agent-1' }); + // Absent keys stay out of the patch entirely, so their columns are untouched + // and a concurrent patch of a different field can't be clobbered. + expect(patch).toEqual({ name: 'new' }); + }); + + it('clears a field when explicitly passed null', async () => { + entityManager.update.mockResolvedValueOnce(updated); + entityManager.findOneBy.mockResolvedValueOnce({ id: 'ds-1' } as AgentEvalDataset); + + await repo.updateDataset('ds-1', 'agent-1', { description: null, columnMapping: null }); + + expect(entityManager.update.mock.calls[0]?.[2]).toEqual({ + description: null, + columnMapping: null, + }); + }); + + it('skips the write when no known field was given', async () => { + entityManager.findOneBy.mockResolvedValueOnce({ id: 'ds-1' } as AgentEvalDataset); + + await repo.updateDataset('ds-1', 'agent-1', {}); + + expect(entityManager.update).not.toHaveBeenCalled(); + }); + + it('returns null when the dataset is not this agent’s', async () => { + entityManager.update.mockResolvedValueOnce({ affected: 0, generatedMaps: [], raw: [] }); + entityManager.findOneBy.mockResolvedValueOnce(null); + + await expect(repo.updateDataset('ds-1', 'other-agent', { name: 'new' })).resolves.toBeNull(); + }); + }); + + describe('deleteDataset', () => { + it('scopes the delete to the agent and reports a removal', async () => { + entityManager.delete.mockResolvedValueOnce({ affected: 1, raw: [] }); + + await expect(repo.deleteDataset('ds-1', 'agent-1')).resolves.toBe(true); + + expect(entityManager.delete.mock.calls[0]?.[1]).toEqual({ + id: 'ds-1', + agentId: 'agent-1', + }); + }); + + it('reports no removal when nothing matched', async () => { + entityManager.delete.mockResolvedValueOnce({ affected: 0, raw: [] }); + + await expect(repo.deleteDataset('ds-1', 'other-agent')).resolves.toBe(false); + }); + }); }); diff --git a/packages/@n8n/db/src/repositories/__tests__/agent-eval-rating.repository.test.ts b/packages/@n8n/db/src/repositories/__tests__/agent-eval-rating.repository.test.ts index cba415b0ffd..5299c75ba5a 100644 --- a/packages/@n8n/db/src/repositories/__tests__/agent-eval-rating.repository.test.ts +++ b/packages/@n8n/db/src/repositories/__tests__/agent-eval-rating.repository.test.ts @@ -66,4 +66,51 @@ describe('AgentEvalRatingRepository', () => { expect(callArgs?.[1]).toEqual({ where: { resultId: 'res-1' }, order: { createdAt: 'DESC' } }); }); }); + + describe('findLatestByRunId', () => { + /** Pass one returns `{ id, resultId }` rows; pass two loads those ids. */ + const queryBuilderReturning = (rows: Array<{ id: string; resultId: string }>) => { + const qb = { + select: vi.fn().mockReturnThis(), + addSelect: vi.fn().mockReturnThis(), + innerJoin: vi.fn().mockReturnThis(), + where: vi.fn().mockReturnThis(), + orderBy: vi.fn().mockReturnThis(), + addOrderBy: vi.fn().mockReturnThis(), + getRawMany: vi.fn().mockResolvedValue(rows), + }; + (entityManager.createQueryBuilder as Mock).mockReturnValue(qb); + return qb; + }; + + it('loads only the newest rating per result and scopes the join to the run', async () => { + // Ordered as the query returns them: grouped by result, newest first. + const qb = queryBuilderReturning([ + { id: 'r-2', resultId: 'res-1' }, + { id: 'r-1', resultId: 'res-1' }, + { id: 'r-3', resultId: 'res-2' }, + ]); + entityManager.find.mockResolvedValueOnce([ + // Deliberately out of order: the caller-visible order comes from pass one. + { id: 'r-3', resultId: 'res-2' }, + { id: 'r-2', resultId: 'res-1' }, + ] as AgentEvalRating[]); + + const latest = await repo.findLatestByRunId('run-1'); + + expect(qb.where).toHaveBeenCalledWith('result.runId = :runId', { runId: 'run-1' }); + // The superseded r-1 is never fetched, only the two winners. + expect(entityManager.find.mock.calls[0]?.[1]).toEqual({ + where: { id: expect.objectContaining({ _value: ['r-2', 'r-3'] }) }, + }); + expect(latest.map((rating) => rating.id)).toEqual(['r-2', 'r-3']); + }); + + it('returns an empty list without a second query when the run has no ratings', async () => { + queryBuilderReturning([]); + + await expect(repo.findLatestByRunId('run-1')).resolves.toEqual([]); + expect(entityManager.find).not.toHaveBeenCalled(); + }); + }); }); diff --git a/packages/@n8n/db/src/repositories/__tests__/agent-eval-run.repository.test.ts b/packages/@n8n/db/src/repositories/__tests__/agent-eval-run.repository.test.ts index 39680827d0b..e204a473901 100644 --- a/packages/@n8n/db/src/repositories/__tests__/agent-eval-run.repository.test.ts +++ b/packages/@n8n/db/src/repositories/__tests__/agent-eval-run.repository.test.ts @@ -82,6 +82,25 @@ describe('AgentEvalRunRepository', () => { }); }); + it('stores metrics alongside the error so a partial run keeps its tally', async () => { + entityManager.update.mockResolvedValueOnce({ affected: 1, generatedMaps: [], raw: [] }); + + await repo.markAsError( + 'run-1', + 'timeout', + { message: 'deadline exceeded' }, + { total: 5, success: 3, usage: { inputTokens: 20, outputTokens: 40 } }, + ); + + const callArgs = entityManager.update.mock.calls[0]; + expect(callArgs?.[2]).toMatchObject({ + status: 'error', + errorCode: 'timeout', + errorDetails: { message: 'deadline exceeded' }, + metrics: { total: 5, success: 3, usage: { inputTokens: 20, outputTokens: 40 } }, + }); + }); + it('clears the running instance so finished runs leave no stale pointer', async () => { entityManager.update.mockResolvedValueOnce({ affected: 1, generatedMaps: [], raw: [] }); @@ -152,6 +171,33 @@ describe('AgentEvalRunRepository', () => { }); }); + // A run has no agent column of its own — the agent under test is its + // dataset's — so these scoped reads walk the relation. + describe('findByIdAndAgentId', () => { + it('constrains the run by its dataset’s agent', async () => { + entityManager.findOne.mockResolvedValueOnce(null); + + await repo.findByIdAndAgentId('run-1', 'agent-1'); + + expect(entityManager.findOne.mock.calls[0]?.[1]).toEqual({ + where: { id: 'run-1', dataset: { agentId: 'agent-1' } }, + }); + }); + }); + + describe('findByDatasetIdAndAgentId', () => { + it('constrains the dataset’s runs by that agent, newest first', async () => { + entityManager.find.mockResolvedValueOnce([]); + + await repo.findByDatasetIdAndAgentId('ds-1', 'agent-1'); + + expect(entityManager.find.mock.calls[0]?.[1]).toEqual({ + where: { datasetId: 'ds-1', dataset: { agentId: 'agent-1' } }, + order: { createdAt: 'DESC' }, + }); + }); + }); + describe('isCancellationRequested', () => { it('reads only the cancel flag and returns it', async () => { entityManager.findOne.mockResolvedValueOnce({ id: 'run-1', cancelRequested: true }); diff --git a/packages/@n8n/db/src/repositories/__tests__/scheduled-job.repository.test.ts b/packages/@n8n/db/src/repositories/__tests__/scheduled-job.repository.test.ts index fe7e8125abb..0e0a62e57a8 100644 --- a/packages/@n8n/db/src/repositories/__tests__/scheduled-job.repository.test.ts +++ b/packages/@n8n/db/src/repositories/__tests__/scheduled-job.repository.test.ts @@ -27,6 +27,8 @@ const newJob = (name: string): NewScheduledJob => ({ fireAt: null, nextRunAt: CLOCK, maxAttempts: 5, + misfirePolicy: 'coalesce', + misfireGraceSeconds: 60, }); /** A chainable insert query-builder mock; `execute` is set per test. */ @@ -89,13 +91,13 @@ describe('ScheduledJobRepository', () => { }); }); - describe('forceDueNowByWorkflowNode', () => { - it('sets nextRunAt to now for the node jobs', async () => { + describe('backdateNextRunAt', () => { + it('sets nextRunAt to secondsAgo in the past for the node jobs', async () => { const qb = updateQb(); qb.execute.mockResolvedValue(undefined); entityManager.createQueryBuilder.mockReturnValue(qb as never); - await repository.forceDueNowByWorkflowNode('wf', 'node'); + await repository.backdateNextRunAt('wf', 'node', 120); expect(qb.update).toHaveBeenCalledWith(ScheduledJob); expect(qb.set).toHaveBeenCalledWith({ nextRunAt: expect.any(Function) }); @@ -105,6 +107,17 @@ describe('ScheduledJobRepository', () => { }); expect(qb.execute).toHaveBeenCalled(); }); + + it('sets nextRunAt to now when secondsAgo is 0', async () => { + const qb = updateQb(); + qb.execute.mockResolvedValue(undefined); + entityManager.createQueryBuilder.mockReturnValue(qb as never); + + await repository.backdateNextRunAt('wf', 'node', 0); + + expect(qb.set).toHaveBeenCalledWith({ nextRunAt: expect.any(Function) }); + expect(qb.execute).toHaveBeenCalled(); + }); }); describe('insertMany', () => { @@ -259,6 +272,8 @@ describe('ScheduledJobRepository', () => { intervalSeconds: null, fireAt: null, nextRunAt: CLOCK, + misfirePolicy: 'skip', + misfireGraceSeconds: 120, }; await repository.updateDefinition(entityManager, 10, update); diff --git a/packages/@n8n/db/src/repositories/__tests__/workflow-publish-history.repository.test.ts b/packages/@n8n/db/src/repositories/__tests__/workflow-publish-history.repository.test.ts new file mode 100644 index 00000000000..7a4f233e805 --- /dev/null +++ b/packages/@n8n/db/src/repositories/__tests__/workflow-publish-history.repository.test.ts @@ -0,0 +1,78 @@ +import type { DataSource, SelectQueryBuilder } from '@n8n/typeorm'; +import type { Mocked } from 'vitest'; + +import { WorkflowHistory, WorkflowPublishHistory } from '../../entities'; +import { mockEntityManager } from '../../utils/test-utils/mock-entity-manager'; +import { WorkflowPublishHistoryRepository } from '../workflow-publish-history.repository'; + +describe('WorkflowPublishHistoryRepository', () => { + const entityManager = mockEntityManager(WorkflowPublishHistory); + const repository = new WorkflowPublishHistoryRepository( + entityManager.connection as unknown as DataSource, + ); + + let queryBuilder: Mocked>; + + beforeEach(() => { + vi.resetAllMocks(); + + queryBuilder = { + innerJoin: vi.fn().mockReturnThis(), + where: vi.fn().mockReturnThis(), + andWhere: vi.fn().mockReturnThis(), + orderBy: vi.fn().mockReturnThis(), + select: vi.fn().mockReturnThis(), + take: vi.fn().mockReturnThis(), + getOne: vi.fn(), + } as unknown as Mocked>; + }); + + it('limits activation reads to the requested versions and newest history row', async () => { + const older = Object.assign(new WorkflowHistory(), { + versionId: 'version-1', + createdAt: new Date('2026-01-01T00:00:00.000Z'), + }); + const newer = Object.assign(new WorkflowHistory(), { + versionId: 'version-2', + createdAt: new Date('2026-01-02T00:00:00.000Z'), + }); + const newestActivated = Object.assign(new WorkflowHistory(), { + versionId: 'version-3', + createdAt: new Date('2026-01-03T00:00:00.000Z'), + }); + entityManager.find.mockResolvedValueOnce([older, newer]); + entityManager.find.mockResolvedValueOnce([ + Object.assign(new WorkflowPublishHistory(), { versionId: 'version-1' }), + ]); + entityManager.createQueryBuilder.mockReturnValue(queryBuilder); + queryBuilder.getOne.mockResolvedValue(newestActivated); + + const states = await repository.getVersionPublicationStates('workflow-1', [ + 'version-1', + 'version-2', + ]); + + expect(entityManager.find).toHaveBeenCalledWith(WorkflowPublishHistory, { + where: { + workflowId: 'workflow-1', + versionId: expect.objectContaining({ _value: ['version-1', 'version-2'] }), + event: 'activated', + }, + select: ['versionId'], + }); + expect(entityManager.createQueryBuilder).toHaveBeenCalledWith(WorkflowHistory, 'history'); + expect(queryBuilder.innerJoin).toHaveBeenCalledWith( + WorkflowPublishHistory, + 'publishHistory', + 'publishHistory.workflowId = history.workflowId AND publishHistory.versionId = history.versionId', + ); + expect(queryBuilder.orderBy).toHaveBeenCalledWith('history.createdAt', 'DESC'); + expect(queryBuilder.take).toHaveBeenCalledWith(1); + expect(states).toEqual( + new Map([ + ['version-1', 'published'], + ['version-2', 'superseded'], + ]), + ); + }); +}); diff --git a/packages/@n8n/db/src/repositories/__tests__/workflow-review-request.repository.test.ts b/packages/@n8n/db/src/repositories/__tests__/workflow-review-request.repository.test.ts index e5f7e7a086e..0084af18612 100644 --- a/packages/@n8n/db/src/repositories/__tests__/workflow-review-request.repository.test.ts +++ b/packages/@n8n/db/src/repositories/__tests__/workflow-review-request.repository.test.ts @@ -92,6 +92,7 @@ describe('WorkflowReviewRequestRepository', () => { queryBuilder.where.mockReturnThis(); queryBuilder.andWhere.mockReturnThis(); queryBuilder.orderBy.mockReturnThis(); + queryBuilder.addOrderBy.mockReturnThis(); queryBuilder.skip.mockReturnThis(); queryBuilder.take.mockReturnThis(); queryBuilder.getRawAndEntities.mockResolvedValue({ entities: [], raw: [] }); @@ -99,13 +100,14 @@ describe('WorkflowReviewRequestRepository', () => { (entityManager.createQueryBuilder as Mock).mockReturnValue(queryBuilder); }); - it('scopes to the requested workflow and orders by createdAt DESC', async () => { + it('scopes to the requested workflow and orders newest first, ties broken by id', async () => { await repo.findRequestsForWorkflow('workflow-1'); expect(queryBuilder.where).toHaveBeenCalledWith('requestWorkflow.workflowId = :workflowId', { workflowId: 'workflow-1', }); expect(queryBuilder.orderBy).toHaveBeenCalledWith('request.createdAt', 'DESC'); + expect(queryBuilder.addOrderBy).toHaveBeenCalledWith('request.id', 'DESC'); expect(queryBuilder.andWhere).not.toHaveBeenCalled(); expect(queryBuilder.skip).not.toHaveBeenCalled(); expect(queryBuilder.take).not.toHaveBeenCalled(); @@ -137,6 +139,36 @@ describe('WorkflowReviewRequestRepository', () => { expect(count).toBe(5); }); + it('projects the fields the workflow-scoped use case needs', async () => { + queryBuilder.getRawAndEntities.mockResolvedValue({ + // A real entity, not a mock: `mock()` proxies Date fields + entities: [ + Object.assign(new WorkflowReviewRequest(), { + id: 'req-1', + state: 'open', + decision: 'changes_requested', + // The reviewer who last decided — resolved into the decision actor + updatedById: 'user-2', + createdAt: new Date('2026-07-20T10:00:00.000Z'), + updatedAt: new Date('2026-07-21T10:00:00.000Z'), + }), + ], + raw: [{ request_id: 'req-1', pinnedWorkflowVersionId: 'ver-1' }], + }); + + const [data] = await repo.findRequestsForWorkflow('workflow-1'); + + expect(data[0]).toEqual({ + id: 'req-1', + state: 'open', + decision: 'changes_requested', + updatedById: 'user-2', + workflowVersionId: 'ver-1', + createdAt: new Date('2026-07-20T10:00:00.000Z'), + updatedAt: new Date('2026-07-21T10:00:00.000Z'), + }); + }); + it('applies skip and take when they are zero', async () => { await repo.findRequestsForWorkflow('workflow-1', { skip: 0, take: 0 }); diff --git a/packages/@n8n/db/src/repositories/agent-eval-dataset.repository.ee.ts b/packages/@n8n/db/src/repositories/agent-eval-dataset.repository.ee.ts index 547ab7997b5..0549d5531ad 100644 --- a/packages/@n8n/db/src/repositories/agent-eval-dataset.repository.ee.ts +++ b/packages/@n8n/db/src/repositories/agent-eval-dataset.repository.ee.ts @@ -1,6 +1,7 @@ import type { DatasetRef } from '@n8n/api-types'; import { Service } from '@n8n/di'; import { DataSource, Repository } from '@n8n/typeorm'; +import type { QueryDeepPartialEntity } from '@n8n/typeorm/query-builder/QueryPartialEntity'; import { AgentEvalDataset } from '../entities'; import type { AgentEvalColumnMapping } from '../entities/agent-eval-dataset.ee'; @@ -15,6 +16,14 @@ type CreateAgentEvalDatasetAttrs = { createdById?: string | null; }; +// Metadata only: repointing the source would change what existing runs were +// measured against, so that's a new dataset. +type UpdateAgentEvalDatasetAttrs = { + name?: string; + description?: string | null; + columnMapping?: AgentEvalColumnMapping | null; +}; + @Service() export class AgentEvalDatasetRepository extends Repository { constructor(dataSource: DataSource) { @@ -42,4 +51,40 @@ export class AgentEvalDatasetRepository extends Repository { async findById(id: string): Promise { return await this.findOneBy({ id }); } + + // The REST layer authorizes an agent, so lookups behind it must filter on the + // agent — `findById` alone would expose another agent's dataset by id. + async findByIdAndAgentId(id: string, agentId: string): Promise { + return await this.findOneBy({ id, agentId }); + } + + // An absent key leaves the column alone; an explicit `null` clears it. Writes + // only the named columns, so concurrent patches can't clobber each other. + async updateDataset( + id: string, + agentId: string, + attrs: UpdateAgentEvalDatasetAttrs, + ): Promise { + const patch: QueryDeepPartialEntity = { + ...(attrs.name !== undefined && { name: attrs.name }), + ...(attrs.description !== undefined && { description: attrs.description }), + ...(attrs.columnMapping !== undefined && { columnMapping: attrs.columnMapping }), + }; + + // Agent-scoped so a foreign agent's patch matches no row. TypeORM rejects an + // empty patch, and a body with no known field is a no-op anyway. + if (Object.keys(patch).length > 0) { + await this.update({ id, agentId }, patch); + } + + // Authoritative for "not found": covers an unknown id and a foreign agent + // without depending on driver-specific affected-row counts. + return await this.findByIdAndAgentId(id, agentId); + } + + /** Delete a dataset, scoped to its agent. Returns whether a row was removed. */ + async deleteDataset(id: string, agentId: string): Promise { + const result = await this.delete({ id, agentId }); + return (result.affected ?? 0) > 0; + } } diff --git a/packages/@n8n/db/src/repositories/agent-eval-rating.repository.ee.ts b/packages/@n8n/db/src/repositories/agent-eval-rating.repository.ee.ts index 1ce4ffb521b..f59739c317a 100644 --- a/packages/@n8n/db/src/repositories/agent-eval-rating.repository.ee.ts +++ b/packages/@n8n/db/src/repositories/agent-eval-rating.repository.ee.ts @@ -1,10 +1,13 @@ import { Service } from '@n8n/di'; -import { DataSource, Repository } from '@n8n/typeorm'; +import { DataSource, In, Repository } from '@n8n/typeorm'; import type { JsonObject } from 'n8n-workflow'; -import { AgentEvalRating } from '../entities'; +import { AgentEvalRating, AgentEvalResult } from '../entities'; import type { AgentEvalVote } from '../entities/agent-eval-rating.ee'; +// Mirrors the seeding chunk size, for the driver's bound parameter limit. +const ID_LOOKUP_CHUNK_SIZE = 100; + type CreateAgentEvalRatingAttrs = { resultId: string; vote: AgentEvalVote; @@ -34,4 +37,43 @@ export class AgentEvalRatingRepository extends Repository { async findByResultId(resultId: string): Promise { return await this.find({ where: { resultId }, order: { createdAt: 'DESC' } }); } + + /** + * Newest rating per result in a run. Reduced here, not in SQL: `DISTINCT ON` is + * Postgres-only and `MAX(createdAt)` returns both rows on a millisecond tie. + */ + async findLatestByRunId(runId: string): Promise { + // Ids only — whole rows would load every superseded `correction` just to drop it. + const rows = await this.createQueryBuilder('rating') + .select('rating.id', 'id') + .addSelect('rating.resultId', 'resultId') + .innerJoin(AgentEvalResult, 'result', 'result.id = rating.resultId') + .where('result.runId = :runId', { runId }) + .orderBy('rating.resultId', 'ASC') + .addOrderBy('rating.createdAt', 'DESC') + .addOrderBy('rating.id', 'DESC') + .getRawMany<{ id: string; resultId: string }>(); + + const seen = new Set(); + const latestIds = rows + .filter((row) => { + if (seen.has(row.resultId)) return false; + seen.add(row.resultId); + return true; + }) + .map((row) => row.id); + if (latestIds.length === 0) return []; + + // Fetch the winners, then restore the id order the query established. + const found: AgentEvalRating[] = []; + for (let i = 0; i < latestIds.length; i += ID_LOOKUP_CHUNK_SIZE) { + const chunk = latestIds.slice(i, i + ID_LOOKUP_CHUNK_SIZE); + found.push(...(await this.find({ where: { id: In(chunk) } }))); + } + + const byId = new Map(found.map((rating) => [rating.id, rating])); + return latestIds + .map((id) => byId.get(id)) + .filter((rating): rating is AgentEvalRating => rating !== undefined); + } } diff --git a/packages/@n8n/db/src/repositories/agent-eval-result.repository.ee.ts b/packages/@n8n/db/src/repositories/agent-eval-result.repository.ee.ts index dc437a9a621..c6fe41f7058 100644 --- a/packages/@n8n/db/src/repositories/agent-eval-result.repository.ee.ts +++ b/packages/@n8n/db/src/repositories/agent-eval-result.repository.ee.ts @@ -106,4 +106,8 @@ export class AgentEvalResultRepository extends Repository { async findByRunId(runId: string): Promise { return await this.find({ where: { runId }, order: { runIndex: 'ASC' } }); } + + async findById(id: string): Promise { + return await this.findOneBy({ id }); + } } diff --git a/packages/@n8n/db/src/repositories/agent-eval-run.repository.ee.ts b/packages/@n8n/db/src/repositories/agent-eval-run.repository.ee.ts index 9761328e942..586376582a6 100644 --- a/packages/@n8n/db/src/repositories/agent-eval-run.repository.ee.ts +++ b/packages/@n8n/db/src/repositories/agent-eval-run.repository.ee.ts @@ -45,12 +45,22 @@ export class AgentEvalRunRepository extends Repository { }); } - async markAsError(id: string, errorCode: string, errorDetails?: IDataObject | null) { + /** + * `metrics` is optional: a run failing before any case ran has none, but one + * failing partway keeps its counts where the other statuses put them. + */ + async markAsError( + id: string, + errorCode: string, + errorDetails?: IDataObject | null, + metrics?: IDataObject | null, + ) { return await this.update(id, { status: 'error', completedAt: new Date(), errorCode, errorDetails: errorDetails ?? null, + metrics: metrics ?? null, runningInstanceId: null, }); } @@ -80,6 +90,20 @@ export class AgentEvalRunRepository extends Repository { return await this.findOneBy({ id }); } + // A run has no agent column — the agent under test is its dataset's — so the + // ownership check walks the relation instead of trusting a bare run id. + async findByIdAndAgentId(id: string, agentId: string): Promise { + return await this.findOne({ where: { id, dataset: { agentId } } }); + } + + /** Runs of a dataset, scoped to the agent that dataset must belong to. */ + async findByDatasetIdAndAgentId(datasetId: string, agentId: string): Promise { + return await this.find({ + where: { datasetId, dataset: { agentId } }, + order: { createdAt: 'DESC' }, + }); + } + /** * Mark every run still in an incomplete state as errored. Called on startup: * the runner has no resume mechanism, so a run interrupted by a process diff --git a/packages/@n8n/db/src/repositories/execution.repository.ts b/packages/@n8n/db/src/repositories/execution.repository.ts index c2511b502f7..9f2e4adf4dc 100644 --- a/packages/@n8n/db/src/repositories/execution.repository.ts +++ b/packages/@n8n/db/src/repositories/execution.repository.ts @@ -362,11 +362,7 @@ export class ExecutionRepository extends Repository { // terminal status: recovery can race a `running` -> `waiting` transition and flag a // healthy execution as dangling, but only genuinely in-progress rows should be crashed { id: In(batch), status: In(CRASHABLE_EXECUTION_STATUSES) }, - { - status: 'crashed', - stoppedAt: new Date(), - waitTill: null, - }, + { status: 'crashed', stoppedAt: new Date(), waitTill: null }, ); this.logger.info('Marked executions as `crashed`', { executionIds }); processed += batch.length; diff --git a/packages/@n8n/db/src/repositories/index.ts b/packages/@n8n/db/src/repositories/index.ts index 44342f673b4..cbb67e7e601 100644 --- a/packages/@n8n/db/src/repositories/index.ts +++ b/packages/@n8n/db/src/repositories/index.ts @@ -79,11 +79,12 @@ export { WorkflowPublishHistoryRepository } from './workflow-publish-history.rep export { WorkflowReviewRequestRepository, type InboxCursor, - type WorkflowReviewRequestForWorkflow, + type WorkflowReviewRequestForWorkflowRow, } from './workflow-review-request.repository'; export { WorkflowReviewRequestWorkflowRepository, type WorkflowReviewRequestLinkedWorkflow, + type WorkflowReviewRequestWorkflowDetailRow, } from './workflow-review-request-workflow.repository'; export { WorkflowReviewRequestReviewerRepository } from './workflow-review-request-reviewer.repository'; export { WorkflowReviewRequestAuthorRepository } from './workflow-review-request-author.repository'; diff --git a/packages/@n8n/db/src/repositories/scheduled-job.repository.ts b/packages/@n8n/db/src/repositories/scheduled-job.repository.ts index 5fe4de7e91c..daade6c6cb7 100644 --- a/packages/@n8n/db/src/repositories/scheduled-job.repository.ts +++ b/packages/@n8n/db/src/repositories/scheduled-job.repository.ts @@ -32,6 +32,8 @@ export type NewScheduledJob = Pick< | 'fireAt' | 'nextRunAt' | 'maxAttempts' + | 'misfirePolicy' + | 'misfireGraceSeconds' >; /** A changed schedule definition, plus the fresh clock it restarts from. */ @@ -45,6 +47,8 @@ export type ScheduledJobDefinitionUpdate = Pick< | 'intervalSeconds' | 'fireAt' | 'nextRunAt' + | 'misfirePolicy' + | 'misfireGraceSeconds' >; @Service() @@ -131,16 +135,18 @@ export class ScheduledJobRepository extends Repository { return await manager.findBy(ScheduledJob, { id: In(ids) }); } - /** Test-only: how many jobs a node currently owns. */ async countByWorkflowNode(workflowId: string, nodeId: string): Promise { return await this.count({ where: { workflowId, nodeId } }); } - /** Test-only: sets `nextRunAt` to now, so the next sweep claims the job. */ - async forceDueNowByWorkflowNode(workflowId: string, nodeId: string): Promise { + async backdateNextRunAt( + workflowId: string, + nodeId: string, + secondsInPast: number, + ): Promise { await this.createQueryBuilder() .update(ScheduledJob) - .set({ nextRunAt: () => dbNowLiteral(this.isPostgres) }) + .set({ nextRunAt: () => dbNowPlusMsLiteral(this.isPostgres, -secondsInPast * 1000) }) .where('"workflowId" = :workflowId AND "nodeId" = :nodeId', { workflowId, nodeId }) .execute(); } @@ -210,6 +216,16 @@ export class ScheduledJobRepository extends Repository { await manager.update(ScheduledJob, { id }, update); } + /** Updates misfire policy and grace only, leaving schedule and clock untouched. */ + async updateMisfirePolicy( + manager: EntityManager, + ids: number[], + update: Pick, + ): Promise { + if (ids.length === 0) return; + await manager.update(ScheduledJob, ids, update); + } + async deleteManyByIds(manager: EntityManager, ids: number[]): Promise { if (ids.length > 0) { await manager.delete(ScheduledJob, ids); diff --git a/packages/@n8n/db/src/repositories/scheduled-task.repository.ts b/packages/@n8n/db/src/repositories/scheduled-task.repository.ts index 80a2cc6d8b6..a841e976002 100644 --- a/packages/@n8n/db/src/repositories/scheduled-task.repository.ts +++ b/packages/@n8n/db/src/repositories/scheduled-task.repository.ts @@ -1,6 +1,14 @@ import { DatabaseConfig } from '@n8n/config'; import { Service } from '@n8n/di'; -import { DataSource, type EntityManager, In, IsNull, Repository } from '@n8n/typeorm'; +import { + Brackets, + DataSource, + type EntityManager, + In, + IsNull, + Not, + Repository, +} from '@n8n/typeorm'; import type { QueryDeepPartialEntity } from '@n8n/typeorm/query-builder/QueryPartialEntity'; import { UnexpectedError } from 'n8n-workflow'; @@ -10,7 +18,12 @@ import { type TerminalTaskStatus, TerminalTaskStatusList, } from '../entities/scheduled-task'; -import { dbNowLiteral, dbNowPlusMsLiteral, parseDbTime } from '../utils/dialect-time'; +import { + columnOrNowPlusMsLiteral, + dbNowLiteral, + dbNowPlusMsLiteral, + parseDbTime, +} from '../utils/dialect-time'; /** Inputs to a claim (see {@link ScheduledTaskRepository.claimDueTasks}). */ export interface ClaimDueTasksOptions { @@ -67,6 +80,7 @@ export interface NewOccurrence { scheduledFor: Date; runAt: Date; maxAttempts: number; + missedAfter?: Date | null; } /** Identity of a row {@link ScheduledTaskRepository.insertIgnoringDuplicates} just created. */ @@ -114,6 +128,7 @@ export class ScheduledTaskRepository extends Repository { // double-quoted identifier). private readonly leaseExpiresAtColumn: string; private readonly dispatchedAtColumn: string; + private readonly runAtColumn: string; constructor(dataSource: DataSource, config: DatabaseConfig) { super(ScheduledTask, dataSource.manager); @@ -121,6 +136,7 @@ export class ScheduledTaskRepository extends Repository { this.tableName = this.manager.connection.driver.escape(`${config.tablePrefix}scheduled_task`); this.leaseExpiresAtColumn = this.manager.connection.driver.escape('leaseExpiresAt'); this.dispatchedAtColumn = this.manager.connection.driver.escape('dispatchedAt'); + this.runAtColumn = this.manager.connection.driver.escape('runAt'); } /** @@ -197,15 +213,72 @@ export class ScheduledTaskRepository extends Repository { } } + /** + * Updates to `missed` the pending occurrences of each job older than that job's + * given instant, in one statement: an `OR` of per-job brackets, each on + * `(jobId, scheduledFor)`, so Postgres and SQLite both resolve it as one index + * scan per job (`BitmapOr`/OR-optimization) rather than a table scan. + * + * @returns how many occurrences were updated + */ + async updateToMissed( + manager: EntityManager, + superseded: Array<{ jobId: number; before: Date }>, + ): Promise { + // An empty Brackets renders as `(1=1)`, which would flip every pending row in + // the table to missed, so this guard is not optional. + if (superseded.length === 0) return 0; + const qb = manager + .createQueryBuilder() + .update(ScheduledTask) + .set({ status: ScheduledTaskStatus.Missed, finishedAt: () => dbNowLiteral(this.isPostgres) }) + .where({ status: ScheduledTaskStatus.Pending }); + qb.andWhere( + new Brackets((bracketQb) => { + superseded.forEach(({ jobId, before }, i) => { + const clause = `"jobId" = :jobId${i} AND "scheduledFor" < :before${i}`; + const params = { [`jobId${i}`]: jobId, [`before${i}`]: before }; + if (i === 0) bracketQb.where(clause, params); + else bracketQb.orWhere(clause, params); + }); + }), + ); + const result = await qb.execute(); + return result.affected ?? 0; + } + + /** + * Recompute `missedAfter` on a job's still-`pending` occurrences from the given + * grace, anchored to each row's own `runAt` or DB-now, whichever is later, so a + * policy or grace change reaches rows already queued under the old grace + * without dragging an overdue-but-still-live row's deadline into the past. Rows + * with a `null` `missedAfter` are left alone: reconciliation only adjusts an + * existing deadline, never gives one to a row that never had one. + */ + async updateMissedAfterForJobs( + manager: EntityManager, + jobIds: number[], + misfireGraceSeconds: number, + ): Promise { + if (jobIds.length === 0) return; + await manager + .createQueryBuilder() + .update(ScheduledTask) + .set({ + missedAfter: () => + columnOrNowPlusMsLiteral(this.isPostgres, this.runAtColumn, misfireGraceSeconds * 1000), + }) + .where({ jobId: In(jobIds), status: ScheduledTaskStatus.Pending, missedAfter: Not(IsNull()) }) + .execute(); + } + /** * Claim up to `batchSize` due `pending` tasks for this instance: set them * `running`, set `claimedBy`/`leaseExpiresAt` and bump `leaseEpoch` (the fencing * token). Due-ness uses the DB clock, and reaches `lookaheadMs` into the future * so the executor can fire each task precisely at its `runAt`. * - * Supersede (skipping a stale occurrence when a newer one exists) is deferred: it - * needs the misfire grace window and per-schedule policy (coalesce/skip vs - * fire-all), which land with the reaper. + * Rows past their deadline are left alone (see {@link claimableSql}). * * Concurrent claimers never take the same row: Postgres skips locked rows * (`FOR UPDATE SKIP LOCKED`); SQLite serialises via the sqlite-pooled driver's @@ -216,6 +289,20 @@ export class ScheduledTaskRepository extends Repository { return this.isPostgres ? await this.claimWithPostgres(opts) : await this.claimWithSqlite(opts); } + /** + * Whether a pending row is still worth claiming, as a SQL fragment for the given + * table alias. A row past its `missedAfter` is left alone, unless it has no + * deadline at all, or has already been attempted: a retry's backoff pushes it past + * its deadline by design, and the attempt count decides its fate from then on. + * + * Shared by both dialects' claims and by the metric snapshot, so what the snapshot + * calls due cannot drift from what the claim would take. + */ + private claimableSql(alias = ''): string { + const deadline = `${alias}"missedAfter"`; + return `(${deadline} IS NULL OR ${deadline} > ${dbNowLiteral(this.isPostgres)} OR ${alias}"attempts" > 0)`; + } + private async claimWithPostgres(opts: ClaimDueTasksOptions): Promise { // TypeORM's Postgres driver returns `[rows, affectedCount]` from a raw UPDATE // ... RETURNING, so destructure the rows out of the tuple. @@ -229,6 +316,7 @@ export class ScheduledTaskRepository extends Repository { WHERE t."status" = '${ScheduledTaskStatus.Pending}' AND t."taskType" = ANY($3) AND t."runAt" <= now() + ($4 || ' milliseconds')::interval + AND ${this.claimableSql('t.')} ORDER BY t."runAt" LIMIT $5 FOR UPDATE SKIP LOCKED) @@ -255,6 +343,7 @@ export class ScheduledTaskRepository extends Repository { .andWhere("t.runAt <= STRFTIME('%Y-%m-%d %H:%M:%f', 'NOW', :lookahead)", { lookahead: `+${opts.lookaheadMs / 1000} seconds`, }) + .andWhere(this.claimableSql('t.')) .orderBy('t.runAt', 'ASC') .limit(opts.batchSize) .getMany(); @@ -379,7 +468,9 @@ export class ScheduledTaskRepository extends Repository { * claim and lease. Used when the instance can't run the task after claiming it * (e.g. no handler is registered for its type at fire time), so another instance * or a later tick picks it up rather than the occurrence being lost. Guarded by - * the `claim`; 0 rows affected is a benign no-op. + * the `claim`; 0 rows affected is a benign no-op. The deadline is untouched: it is + * an absolute instant, so holding a row briefly doesn't extend how long it stays + * worth running. */ async releaseClaim(claim: HostedClaimedRef): Promise { return await this.runGuardedUpdate(claim, { @@ -415,10 +506,15 @@ export class ScheduledTaskRepository extends Repository { * (`oldestPendingAgeMs`). Everything reads the DB clock, so `due`-ness and the * oldest-age reference are consistent regardless of any instance clock skew. * + * `due` and the oldest age count only what the claim would actually take, so a + * backlog the reaper has yet to retire doesn't read as work the scheduler is behind + * on. `pending` stays the whole backlog, retirable rows included. + * * The caller runs this behind a short scrape cache. */ async getMetricSnapshot(): Promise { const now = dbNowLiteral(this.isPostgres); + const claimable = this.claimableSql(); // "running" is computed through a subquery (and not a FILTER) to prevent // the WHERE to use 'IN', which does not take the partial index into account const [row]: [ @@ -432,8 +528,8 @@ export class ScheduledTaskRepository extends Repository { ] = await this.query( `SELECT COUNT(*) AS "pending", - COUNT(*) FILTER (WHERE "runAt" <= ${now}) AS "due", - MIN("runAt") FILTER (WHERE "runAt" <= ${now}) AS "oldestDueRunAt", + COUNT(*) FILTER (WHERE "runAt" <= ${now} AND ${claimable}) AS "due", + MIN("runAt") FILTER (WHERE "runAt" <= ${now} AND ${claimable}) AS "oldestDueRunAt", (SELECT COUNT(*) FROM ${this.tableName} WHERE "status" = '${ScheduledTaskStatus.Running}') AS "running", ${now} AS "dbNow" FROM ${this.tableName} @@ -659,4 +755,67 @@ export class ScheduledTaskRepository extends Repository { .execute(); return result.affected ?? 0; } + + /** + * Retire, as `missed`, up to `limit` `pending` occurrences past their + * `missedAfter`, oldest deadline first. Rows already attempted are left alone: + * they're awaiting their own retry, not stuck. + * + * Policy-blind by design: a row here was already recorded by an earlier + * materialization pass, so `applyMisfirePolicy` never runs on it again. `coalesce` + * only ever collapses backlog that is still unrecorded at the moment a pass + * plans it; once recorded, an occurrence that goes unclaimed past its deadline is + * always retired here, regardless of its job's policy. + */ + async retireMissedPending(limit: number): Promise { + // A non-integer bound to LIMIT errors on SQLite (datatype mismatch), and NaN + // binds as NULL, which on Postgres means LIMIT ALL. + if (!Number.isSafeInteger(limit)) { + throw new UnexpectedError(`retireMissedPending needs an integer limit, got: ${limit}`); + } + if (limit <= 0) return 0; + return this.isPostgres + ? await this.retireMissedPendingWithPostgres(limit) + : await this.retireMissedPendingWithSqlite(limit); + } + + private async retireMissedPendingWithPostgres(limit: number): Promise { + const [, affected] = await this.manager.query<[unknown[], number]>( + `UPDATE ${this.tableName} + SET "status" = $1, "finishedAt" = ${dbNowLiteral(true)} + WHERE "id" IN ( + SELECT t."id" FROM ${this.tableName} t + WHERE t."status" = $2 + AND t."missedAfter" IS NOT NULL + AND t."missedAfter" <= ${dbNowLiteral(true)} + AND t."attempts" = 0 + ORDER BY t."missedAfter" + LIMIT $3 + FOR UPDATE SKIP LOCKED)`, + [ScheduledTaskStatus.Missed, ScheduledTaskStatus.Pending, limit], + ); + return affected; + } + + private async retireMissedPendingWithSqlite(limit: number): Promise { + const result = await this.createQueryBuilder() + .update() + .set({ + status: ScheduledTaskStatus.Missed, + finishedAt: () => dbNowLiteral(false), + }) + .where( + `id IN ( + SELECT "id" FROM ${this.tableName} + WHERE "status" = :pending + AND "missedAfter" IS NOT NULL + AND "missedAfter" <= ${dbNowLiteral(false)} + AND "attempts" = 0 + ORDER BY "missedAfter" + LIMIT :limit)`, + { pending: ScheduledTaskStatus.Pending, limit }, + ) + .execute(); + return result.affected ?? 0; + } } diff --git a/packages/@n8n/db/src/repositories/tag.repository.ts b/packages/@n8n/db/src/repositories/tag.repository.ts index ad2e1f8f2af..0f52e645806 100644 --- a/packages/@n8n/db/src/repositories/tag.repository.ts +++ b/packages/@n8n/db/src/repositories/tag.repository.ts @@ -19,6 +19,14 @@ export class TagRepository extends Repository { }); } + /** Exact (case-sensitive) name lookup; no input normalization. */ + async findManyByName(names: string[]) { + return await this.find({ + select: ['id', 'name'], + where: { name: In(names) }, + }); + } + /** * Set tags on workflow to import while ensuring all tags exist in the database, * either by matching incoming to existing tags or by creating them first. diff --git a/packages/@n8n/db/src/repositories/workflow-publish-history.repository.ts b/packages/@n8n/db/src/repositories/workflow-publish-history.repository.ts index 6a62561bb2b..6e2f4f37025 100644 --- a/packages/@n8n/db/src/repositories/workflow-publish-history.repository.ts +++ b/packages/@n8n/db/src/repositories/workflow-publish-history.repository.ts @@ -1,8 +1,9 @@ +import type { WorkflowReviewApprovedPublicationState } from '@n8n/api-types'; import { Service } from '@n8n/di'; -import { DataSource, Repository } from '@n8n/typeorm'; +import { DataSource, In, Repository } from '@n8n/typeorm'; import type { EntityManager } from '@n8n/typeorm'; -import { WorkflowPublishHistory } from '../entities'; +import { WorkflowHistory, WorkflowPublishHistory } from '../entities'; @Service() export class WorkflowPublishHistoryRepository extends Repository { @@ -39,6 +40,77 @@ export class WorkflowPublishHistoryRepository extends Repository> { + const states = new Map(); + const requested = [...new Set(versionIds)]; + if (requested.length === 0) { + return states; + } + + const [versions, activatedVersions, newestActivated] = await Promise.all([ + this.manager.find(WorkflowHistory, { + where: { workflowId, versionId: In(requested) }, + select: ['versionId', 'createdAt'], + }), + this.manager.find(WorkflowPublishHistory, { + where: { workflowId, versionId: In(requested), event: 'activated' }, + select: ['versionId'], + }), + this.manager + .createQueryBuilder(WorkflowHistory, 'history') + .innerJoin( + WorkflowPublishHistory, + 'publishHistory', + 'publishHistory.workflowId = history.workflowId AND publishHistory.versionId = history.versionId', + ) + .where('history.workflowId = :workflowId', { workflowId }) + .andWhere('publishHistory.event = :event', { event: 'activated' }) + .select(['history.versionId', 'history.createdAt']) + .orderBy('history.createdAt', 'DESC') + .take(1) + .getOne(), + ]); + const createdAtByVersionId = new Map( + versions.map((version) => [version.versionId, version.createdAt]), + ); + + const activatedVersionIds = activatedVersions + .map((record) => record.versionId) + .filter((id): id is string => id !== null); + + // Pruned publishes (`versionId` nulled) are excluded by the history join. + const activatedVersionIdSet = new Set(activatedVersionIds); + + for (const versionId of requested) { + const createdAt = createdAtByVersionId.get(versionId); + // Pruned history: no creation time to order against, so stay silent (LIGO-879) + if (!createdAt) { + states.set(versionId, 'unknown'); + } else if (activatedVersionIdSet.has(versionId)) { + states.set(versionId, 'published'); + } else if (newestActivated && newestActivated.createdAt > createdAt) { + states.set(versionId, 'superseded'); + } else { + states.set(versionId, 'not_published'); + } + } + + return states; + } + async findActivatedByUserId(workflowId: string): Promise { const record = await this.findOne({ select: ['userId'], diff --git a/packages/@n8n/db/src/repositories/workflow-review-request-workflow.repository.ts b/packages/@n8n/db/src/repositories/workflow-review-request-workflow.repository.ts index 04eae0b052d..f6ed7ab1bcf 100644 --- a/packages/@n8n/db/src/repositories/workflow-review-request-workflow.repository.ts +++ b/packages/@n8n/db/src/repositories/workflow-review-request-workflow.repository.ts @@ -11,6 +11,12 @@ export type WorkflowReviewRequestLinkedWorkflow = { workflowVersionId: string | null; }; +export type WorkflowReviewRequestWorkflowDetailRow = { + workflowId: string; + workflowName: string; + workflowVersionId: string | null; +}; + @Service() export class WorkflowReviewRequestWorkflowRepository extends Repository { constructor(dataSource: DataSource) { @@ -96,4 +102,30 @@ export class WorkflowReviewRequestWorkflowRepository extends Repository { + // Join via entity so DB_TABLE_PREFIX is applied (postgres ITs). + const rows = await this.createQueryBuilder('wrw') + // The inner join is safe: `workflowId` FKs onto `workflow_entity` with + // `ON DELETE CASCADE`, so a child row never outlives its workflow. + .innerJoin(WorkflowEntity, 'workflow', 'workflow.id = wrw.workflowId') + .select('wrw.workflowId', 'workflowId') + .addSelect('workflow.name', 'workflowName') + .addSelect('wrw.workflowVersionId', 'workflowVersionId') + .where('wrw.workflowReviewRequestId = :requestId', { requestId }) + .orderBy('wrw.id', 'ASC') + .getRawMany<{ + workflowId: string; + workflowName: string; + workflowVersionId: string | null; + }>(); + + return rows.map((row) => ({ + workflowId: row.workflowId, + workflowName: row.workflowName, + workflowVersionId: row.workflowVersionId ?? null, + })); + } } diff --git a/packages/@n8n/db/src/repositories/workflow-review-request.repository.ts b/packages/@n8n/db/src/repositories/workflow-review-request.repository.ts index 971adc02c77..aeda20c55d0 100644 --- a/packages/@n8n/db/src/repositories/workflow-review-request.repository.ts +++ b/packages/@n8n/db/src/repositories/workflow-review-request.repository.ts @@ -32,9 +32,9 @@ export type FindManyForInboxOptions = { * Projection for the workflow-scoped list: the request fields the use case * needs plus the version pinned for the workflow the query was scoped to. */ -export type WorkflowReviewRequestForWorkflow = Pick< +export type WorkflowReviewRequestForWorkflowRow = Pick< WorkflowReviewRequest, - 'id' | 'state' | 'decision' | 'createdAt' | 'updatedAt' + 'id' | 'state' | 'decision' | 'updatedById' | 'createdAt' | 'updatedAt' > & { workflowVersionId: string | null; }; @@ -103,7 +103,7 @@ export class WorkflowReviewRequestRepository extends Repository { + ): Promise<[WorkflowReviewRequestForWorkflowRow[], number]> { const qb = this.manager .createQueryBuilder(WorkflowReviewRequest, 'request') .innerJoin( @@ -113,7 +113,11 @@ export class WorkflowReviewRequestRepository extends Repository { return execution.id; } + /** Most cases need one step; the store's API is the batch `createSteps`. */ + async function createStep( + store: TypeOrmStepStore, + record: NewStepRecord, + ): Promise<{ id: string }> { + const [step] = await store.createSteps([record]); + return step; + } + it('persists and retrieves a step row', async () => { const executionId = await createExecution(); const repo = dataSource.getRepository(WorkflowStepExecution); @@ -46,24 +56,55 @@ describe('workflow_step_execution table (integration)', () => { const created = repo.create({ executionId, nodeId: 'node-a', status: 'queued' }); await repo.save(created); - const found = await repo.findOneByOrFail({ id: created.id }); + const found = await repo.findOneOrFail({ where: { id: created.id } }); expect(found.executionId).toBe(executionId); expect(found.nodeId).toBe('node-a'); expect(found.status).toBe('queued'); expect(found.createdAt).toBeInstanceOf(Date); }); - it('TypeOrmStepStore.createStep persists a queued step and returns its id', async () => { + it('TypeOrmStepStore.createSteps persists a queued step and returns its id', async () => { const executionId = await createExecution(); const store = new TypeOrmStepStore(dataSource.getRepository(WorkflowStepExecution)); - const { id } = await store.createStep({ executionId, nodeId: 'x', status: 'queued' }); + const { id } = await createStep(store, { executionId, nodeId: 'x', status: 'queued' }); - const found = await dataSource.getRepository(WorkflowStepExecution).findOneByOrFail({ id }); + const found = await dataSource + .getRepository(WorkflowStepExecution) + .findOneOrFail({ where: { id } }); expect(found.nodeId).toBe('x'); expect(found.status).toBe('queued'); }); + it('TypeOrmStepStore.createSteps persists a batch and returns ids in input order', async () => { + const executionId = await createExecution(); + const store = new TypeOrmStepStore(dataSource.getRepository(WorkflowStepExecution)); + + const ids = await store.createSteps([ + { executionId, nodeId: 'trigger', status: 'completed' }, + { executionId, nodeId: 'a', status: 'queued' }, + { executionId, nodeId: 'b', status: 'queued' }, + ]); + + expect(ids).toHaveLength(3); + expect(new Set(ids.map(({ id }) => id)).size).toBe(3); + // the returned ids line up positionally with the records passed in + const repo = dataSource.getRepository(WorkflowStepExecution); + const rows = await Promise.all( + ids.map(async ({ id }) => await repo.findOneOrFail({ where: { id } })), + ); + expect(rows.map((r) => [r.nodeId, r.status])).toEqual([ + ['trigger', 'completed'], + ['a', 'queued'], + ['b', 'queued'], + ]); + }); + + it('TypeOrmStepStore.createSteps is a no-op for an empty batch', async () => { + const store = new TypeOrmStepStore(dataSource.getRepository(WorkflowStepExecution)); + expect(await store.createSteps([])).toEqual([]); + }); + it('cascades step deletion when the parent execution is deleted', async () => { const executionId = await createExecution(); const stepRepo = dataSource.getRepository(WorkflowStepExecution); @@ -71,13 +112,13 @@ describe('workflow_step_execution table (integration)', () => { await dataSource.getRepository(WorkflowExecution).delete({ id: executionId }); - expect(await stepRepo.countBy({ executionId })).toBe(0); + expect(await stepRepo.count({ where: { executionId } })).toBe(0); }); it('rejects a step referencing a non-existent execution (foreign key)', async () => { const store = new TypeOrmStepStore(dataSource.getRepository(WorkflowStepExecution)); await expect( - store.createStep({ + createStep(store, { executionId: '00000000-0000-7000-8000-000000000000', nodeId: 'a', status: 'queued', @@ -85,6 +126,107 @@ describe('workflow_step_execution table (integration)', () => { ).rejects.toThrow(); }); + it('TypeOrmStepStore.loadStep returns the step, or throws when absent', async () => { + const executionId = await createExecution(); + const store = new TypeOrmStepStore(dataSource.getRepository(WorkflowStepExecution)); + const { id } = await createStep(store, { executionId, nodeId: 'node-a', status: 'queued' }); + + const step = await store.loadStep(id); + + // `toMatchObject`: the adapter returns the entity, which carries columns the + // `StepRecord` interface doesn't expose. + expect(step).toMatchObject({ + id, + executionId, + nodeId: 'node-a', + status: 'queued', + outputs: null, + error: null, + }); + await expect(store.loadStep('00000000-0000-7000-8000-000000000000')).rejects.toThrow( + StepNotFoundError, + ); + }); + + it('TypeOrmStepStore.claimStep only claims a queued step', async () => { + const executionId = await createExecution(); + const store = new TypeOrmStepStore(dataSource.getRepository(WorkflowStepExecution)); + const { id } = await createStep(store, { executionId, nodeId: 'a', status: 'queued' }); + + expect(await store.claimStep(id)).toBe(true); + // second claim of the same step loses the race + expect(await store.claimStep(id)).toBe(false); + expect((await store.loadStep(id)).status).toBe('running'); + }); + + it('TypeOrmStepStore.completeStep persists outputs and marks the step completed', async () => { + const executionId = await createExecution(); + const store = new TypeOrmStepStore(dataSource.getRepository(WorkflowStepExecution)); + const { id } = await createStep(store, { executionId, nodeId: 'a', status: 'running' }); + + expect(await store.completeStep(id, [[{ json: { ok: true } }]])).toBe(true); + + const step = await store.loadStep(id); + expect(step.status).toBe('completed'); + expect(step.outputs).toEqual([[{ json: { ok: true } }]]); + }); + + it('TypeOrmStepStore.completeStep and failStep only record a step that is running', async () => { + const executionId = await createExecution(); + const store = new TypeOrmStepStore(dataSource.getRepository(WorkflowStepExecution)); + const { id } = await createStep(store, { executionId, nodeId: 'a', status: 'queued' }); + + expect(await store.completeStep(id, [[{ json: { ok: true } }]])).toBe(false); + expect(await store.failStep(id, { name: 'Error', message: 'node blew up' })).toBe(false); + + // neither the status nor the result columns moved + const found = await dataSource + .getRepository(WorkflowStepExecution) + .findOneOrFail({ where: { id } }); + expect(found.status).toBe('queued'); + expect(found.outputs).toBeNull(); + expect(found.error).toBeNull(); + }); + + it('TypeOrmStepStore.failStep persists the error and marks the step failed', async () => { + const executionId = await createExecution(); + const store = new TypeOrmStepStore(dataSource.getRepository(WorkflowStepExecution)); + const { id } = await createStep(store, { executionId, nodeId: 'a', status: 'running' }); + + const error = { + name: 'Error', + message: 'node blew up', + stack: 'Error: node blew up\n at somewhere', + details: { httpCode: '500' }, + }; + expect(await store.failStep(id, error)).toBe(true); + + const found = await dataSource + .getRepository(WorkflowStepExecution) + .findOneOrFail({ where: { id } }); + expect(found.status).toBe('failed'); + expect(found.error).toEqual(error); + }); + + it('TypeOrmStepStore.loadStepOutputs returns only completed outputs, keyed by node id', async () => { + const executionId = await createExecution(); + const store = new TypeOrmStepStore(dataSource.getRepository(WorkflowStepExecution)); + const { id: aId } = await createStep(store, { executionId, nodeId: 'a', status: 'running' }); + await store.completeStep(aId, [[{ json: { from: 'a' } }]]); + await createStep(store, { executionId, nodeId: 'b', status: 'queued' }); + const { id: cId } = await createStep(store, { executionId, nodeId: 'c', status: 'running' }); + await store.failStep(cId, { name: 'Error', message: 'node blew up' }); + + const outputs = await store.loadStepOutputs(executionId, ['a', 'b', 'c', 'd']); + + expect(outputs).toEqual({ + a: [[{ json: { from: 'a' } }]], + b: null, // queued + c: null, // failed + d: null, // no step row + }); + }); + it('rejects an invalid status (check constraint)', async () => { const executionId = await createExecution(); const repo = dataSource.getRepository(WorkflowStepExecution); diff --git a/packages/@n8n/engine/src/database/entities/workflow-step-execution.entity.ts b/packages/@n8n/engine/src/database/entities/workflow-step-execution.entity.ts index 872eaca6c85..e685a6c2bab 100644 --- a/packages/@n8n/engine/src/database/entities/workflow-step-execution.entity.ts +++ b/packages/@n8n/engine/src/database/entities/workflow-step-execution.entity.ts @@ -8,11 +8,13 @@ import { UpdateDateColumn, } from '@n8n/typeorm'; +import type { JsonValue } from '../../common'; import type { StepStatus } from '../../execution/execution.types'; +import type { StepError } from '../../execution/step-store'; import { generateId } from '../generate-id'; @Entity('workflow_step_execution') -@Index('idx_workflow_step_execution_execution_id', ['executionId']) +@Index('idx_workflow_step_execution_execution_id_node_id', ['executionId', 'nodeId']) export class WorkflowStepExecution { @PrimaryColumn('uuid') id!: string; @@ -26,6 +28,12 @@ export class WorkflowStepExecution { @Column('varchar', { length: 32 }) status!: StepStatus; + @Column('jsonb', { nullable: true }) + outputs!: JsonValue | null; + + @Column('jsonb', { nullable: true }) + error!: StepError | null; + @CreateDateColumn({ name: 'created_at', type: 'timestamptz', precision: 3 }) createdAt!: Date; diff --git a/packages/@n8n/engine/src/database/migrations/1784890100000-CreateWorkflowStepExecution.ts b/packages/@n8n/engine/src/database/migrations/1784890100000-CreateWorkflowStepExecution.ts index 0e94ade3304..4466a81711e 100644 --- a/packages/@n8n/engine/src/database/migrations/1784890100000-CreateWorkflowStepExecution.ts +++ b/packages/@n8n/engine/src/database/migrations/1784890100000-CreateWorkflowStepExecution.ts @@ -13,6 +13,19 @@ export class CreateWorkflowStepExecution1784890100000 implements MigrationInterf { name: 'execution_id', type: 'uuid' }, { name: 'node_id', type: 'varchar' }, { name: 'status', type: 'varchar', length: '32' }, + { + name: 'outputs', + type: 'jsonb', + isNullable: true, + comment: + 'Step outputs, persisted without inspection and reloaded as downstream inputs. Shape is step-type-specific.', + }, + { + name: 'error', + type: 'jsonb', + isNullable: true, + comment: 'Name and message of the error that failed this step.', + }, { name: 'created_at', type: 'timestamptz', @@ -27,7 +40,13 @@ export class CreateWorkflowStepExecution1784890100000 implements MigrationInterf }, ], indices: [ - { name: 'idx_workflow_step_execution_execution_id', columnNames: ['execution_id'] }, + // Composite: steps are looked up per execution, usually narrowed to + // specific nodes. The leading column also serves execution-only + // lookups and the FK's cascading delete. + { + name: 'idx_workflow_step_execution_execution_id_node_id', + columnNames: ['execution_id', 'node_id'], + }, ], foreignKeys: [ { diff --git a/packages/@n8n/engine/src/database/typeorm-execution-store.ts b/packages/@n8n/engine/src/database/typeorm-execution-store.ts index 24a574afa01..2759a33e2eb 100644 --- a/packages/@n8n/engine/src/database/typeorm-execution-store.ts +++ b/packages/@n8n/engine/src/database/typeorm-execution-store.ts @@ -9,13 +9,22 @@ import { } from '../execution/execution-store'; import type { ExecutionStatus } from '../execution/execution.types'; +/** + * Insert payload accepted by the repository. Derived from the method rather than + * imported: TypeORM's `QueryDeepPartialEntity` has no root export. + */ +type InsertValues = Parameters['insert']>[0]; + /** TypeORM-backed `ExecutionStore` adapter. */ export class TypeOrmExecutionStore implements ExecutionStore { constructor(private readonly repo: Repository) {} async createExecution(record: NewExecutionRecord): Promise<{ id: string }> { const execution = this.repo.create({ ...record, finishedAt: null }); - await this.repo.save(execution); + // The cast is needed because the insert payload type recurses into the + // opaque `graph` jsonb and rejects `StepConfig`'s deliberate `unknown`. + // NOTE: prefer insert to save for performance reasons. + await this.repo.insert(execution as InsertValues); return { id: execution.id }; } diff --git a/packages/@n8n/engine/src/database/typeorm-step-store.ts b/packages/@n8n/engine/src/database/typeorm-step-store.ts index 7382a3d9148..aadbcec1188 100644 --- a/packages/@n8n/engine/src/database/typeorm-step-store.ts +++ b/packages/@n8n/engine/src/database/typeorm-step-store.ts @@ -1,15 +1,77 @@ -import type { Repository } from '@n8n/typeorm'; +import { In, type Repository } from '@n8n/typeorm'; import type { WorkflowStepExecution } from './entities'; -import type { NewStepRecord, StepStore } from '../execution/step-store'; +import type { JsonValue } from '../common'; +import type { StepStatus } from '../execution/execution.types'; +import { + StepNotFoundError, + type NewStepRecord, + type StepError, + type StepRecord, + type StepStore, +} from '../execution/step-store'; /** TypeORM-backed `StepStore` adapter. */ export class TypeOrmStepStore implements StepStore { constructor(private readonly repo: Repository) {} - async createStep(record: NewStepRecord): Promise<{ id: string }> { - const step = this.repo.create(record); - await this.repo.save(step); - return { id: step.id }; + async createSteps(records: NewStepRecord[]): Promise> { + const steps = records.map((record) => this.repo.create(record)); + // NOTE: prefer insert to save for performance reasons. + await this.repo.insert(steps); + return steps.map(({ id }) => ({ id })); + } + + async loadStep(id: string): Promise { + // NOTE: `findOne({ where })`, not `findOneBy`: the latter's overload exceeds + // TypeScript's instantiation depth on the recursive `outputs` column type. + const row = await this.repo.findOne({ where: { id } }); + if (!row) throw new StepNotFoundError(id); + return row; + } + + async claimStep(id: string): Promise { + return await this.transition(id, 'queued', 'running'); + } + + async completeStep(id: string, outputs: JsonValue): Promise { + return await this.transition(id, 'running', 'completed', { outputs }); + } + + async failStep(id: string, error: StepError): Promise { + return await this.transition(id, 'running', 'failed', { error }); + } + + /** + * Compare-and-set on `status`, writing any result columns in the same + * statement so a step's status and its outcome can't be observed apart. + */ + private async transition( + id: string, + from: StepStatus, + to: StepStatus, + fields: { outputs?: JsonValue; error?: StepError } = {}, + ): Promise { + const result = await this.repo.update({ id, status: from }, { ...fields, status: to }); + return result.affected === 1; + } + + async loadStepOutputs( + executionId: string, + nodeIds: string[], + ): Promise> { + const outputsByNodeId: Record = {}; + for (const nodeId of nodeIds) outputsByNodeId[nodeId] = null; + if (nodeIds.length === 0) return outputsByNodeId; + + // Filter on `completed` rather than relying on non-completed rows having a + // null `outputs` column, so the contract holds however writes are ordered. + const rows = await this.repo.find({ + where: { executionId, nodeId: In(nodeIds), status: 'completed' }, + select: ['nodeId', 'outputs'], + }); + for (const row of rows) outputsByNodeId[row.nodeId] = row.outputs; + + return outputsByNodeId; } } diff --git a/packages/@n8n/engine/src/execution/__tests__/execution-start-handler.test.ts b/packages/@n8n/engine/src/execution/__tests__/execution-start-handler.test.ts index 59509e09a14..c68452f6476 100644 --- a/packages/@n8n/engine/src/execution/__tests__/execution-start-handler.test.ts +++ b/packages/@n8n/engine/src/execution/__tests__/execution-start-handler.test.ts @@ -19,6 +19,18 @@ function makeStepQueue(): WorkQueue { return { publish: vi.fn(), start: vi.fn(), stop: vi.fn() }; } +/** Only `createSteps` is exercised here; the rest belong to the step worker. */ +function makeStepStore(createSteps = vi.fn()): StepStore { + return { + createSteps, + loadStep: vi.fn(), + claimStep: vi.fn(), + completeStep: vi.fn(), + failStep: vi.fn(), + loadStepOutputs: vi.fn(), + }; +} + function record(graph: WorkflowGraph): ExecutionRecord { return { id: 'exec-1', @@ -46,34 +58,23 @@ describe('ExecutionStartHandler', () => { const executionStore = makeExecutionStore({ loadExecution: vi.fn().mockResolvedValue(record(graph)), }); - const createStep = vi + const createSteps = vi .fn() - .mockResolvedValueOnce({ id: 'step-trigger' }) - .mockResolvedValueOnce({ id: 'step-a' }) - .mockResolvedValueOnce({ id: 'step-b' }); - const stepStore: StepStore = { createStep }; + .mockResolvedValue([{ id: 'step-trigger' }, { id: 'step-a' }, { id: 'step-b' }]); + const stepStore = makeStepStore(createSteps); const stepQueue = makeStepQueue(); const handler = new ExecutionStartHandler(executionStore, stepStore, stepQueue); await handler.handle({ type: 'execution:enqueued', executionId: 'exec-1' }); expect(executionStore.transitionStatus).toHaveBeenCalledWith('exec-1', 'queued', 'running'); - // trigger recorded completed, then each successor recorded queued - expect(createStep).toHaveBeenNthCalledWith(1, { - executionId: 'exec-1', - nodeId: 'trigger', - status: 'completed', - }); - expect(createStep).toHaveBeenNthCalledWith(2, { - executionId: 'exec-1', - nodeId: 'a', - status: 'queued', - }); - expect(createStep).toHaveBeenNthCalledWith(3, { - executionId: 'exec-1', - nodeId: 'b', - status: 'queued', - }); + // one batch: the trigger completed, then each successor queued + expect(createSteps).toHaveBeenCalledTimes(1); + expect(createSteps).toHaveBeenCalledWith([ + { executionId: 'exec-1', nodeId: 'trigger', status: 'completed' }, + { executionId: 'exec-1', nodeId: 'a', status: 'queued' }, + { executionId: 'exec-1', nodeId: 'b', status: 'queued' }, + ]); // step:ready references the queued step-record ids expect(stepQueue.publish).toHaveBeenNthCalledWith(1, { type: 'step:ready', @@ -91,14 +92,14 @@ describe('ExecutionStartHandler', () => { const executionStore = makeExecutionStore({ transitionStatus: vi.fn().mockResolvedValue(false), }); - const stepStore: StepStore = { createStep: vi.fn() }; + const stepStore = makeStepStore(); const stepQueue = makeStepQueue(); const handler = new ExecutionStartHandler(executionStore, stepStore, stepQueue); await handler.handle({ type: 'execution:enqueued', executionId: 'exec-1' }); expect(executionStore.loadExecution).not.toHaveBeenCalled(); - expect(stepStore.createStep).not.toHaveBeenCalled(); + expect(stepStore.createSteps).not.toHaveBeenCalled(); expect(stepQueue.publish).not.toHaveBeenCalled(); }); @@ -107,14 +108,14 @@ describe('ExecutionStartHandler', () => { const executionStore = makeExecutionStore({ loadExecution: vi.fn().mockResolvedValue(record(graph)), }); - const stepStore: StepStore = { createStep: vi.fn() }; + const stepStore = makeStepStore(); const stepQueue = makeStepQueue(); const handler = new ExecutionStartHandler(executionStore, stepStore, stepQueue); await handler.handle({ type: 'execution:enqueued', executionId: 'exec-1' }); expect(executionStore.transitionStatus).toHaveBeenCalledWith('exec-1', 'running', 'failed'); - expect(stepStore.createStep).not.toHaveBeenCalled(); + expect(stepStore.createSteps).not.toHaveBeenCalled(); expect(stepQueue.publish).not.toHaveBeenCalled(); }); @@ -126,8 +127,8 @@ describe('ExecutionStartHandler', () => { const executionStore = makeExecutionStore({ loadExecution: vi.fn().mockResolvedValue(record(graph)), }); - const createStep = vi.fn().mockResolvedValue({ id: 'step-trigger' }); - const stepStore: StepStore = { createStep }; + const createSteps = vi.fn().mockResolvedValue([{ id: 'step-trigger' }]); + const stepStore = makeStepStore(createSteps); const stepQueue = makeStepQueue(); const handler = new ExecutionStartHandler(executionStore, stepStore, stepQueue); @@ -136,12 +137,10 @@ describe('ExecutionStartHandler', () => { // claimed (queued -> running) but not failed expect(executionStore.transitionStatus).toHaveBeenCalledWith('exec-1', 'queued', 'running'); expect(executionStore.transitionStatus).not.toHaveBeenCalledWith('exec-1', 'running', 'failed'); - expect(createStep).toHaveBeenCalledTimes(1); - expect(createStep).toHaveBeenCalledWith({ - executionId: 'exec-1', - nodeId: 'trigger', - status: 'completed', - }); + expect(createSteps).toHaveBeenCalledTimes(1); + expect(createSteps).toHaveBeenCalledWith([ + { executionId: 'exec-1', nodeId: 'trigger', status: 'completed' }, + ]); expect(stepQueue.publish).not.toHaveBeenCalled(); }); }); diff --git a/packages/@n8n/engine/src/execution/execution-start-handler.ts b/packages/@n8n/engine/src/execution/execution-start-handler.ts index d29389ec92f..fbe14bbabf1 100644 --- a/packages/@n8n/engine/src/execution/execution-start-handler.ts +++ b/packages/@n8n/engine/src/execution/execution-start-handler.ts @@ -35,19 +35,19 @@ export class ExecutionStartHandler { // The trigger's output was captured at execution start; record it as // already completed so successors can treat it as a satisfied predecessor. - await this.stepStore.createStep({ - executionId: event.executionId, - nodeId: trigger.id, - status: 'completed', - }); - + // Planned together with the successors so a fan-out is one round trip. const successorNodeIds = getSuccessorNodeIds(execution.graph, trigger.id); - for (const nodeId of successorNodeIds) { - const { id: stepId } = await this.stepStore.createStep({ + const [, ...successorSteps] = await this.stepStore.createSteps([ + { executionId: event.executionId, nodeId: trigger.id, status: 'completed' }, + ...successorNodeIds.map((nodeId) => ({ executionId: event.executionId, nodeId, - status: 'queued', - }); + status: 'queued' as const, + })), + ]); + + // Published only after the rows exist, so a consumer can always load the step. + for (const { id: stepId } of successorSteps) { await this.stepQueue.publish({ type: 'step:ready', executionId: event.executionId, diff --git a/packages/@n8n/engine/src/execution/index.ts b/packages/@n8n/engine/src/execution/index.ts index fbfa5b98c56..b6ed070ebae 100644 --- a/packages/@n8n/engine/src/execution/index.ts +++ b/packages/@n8n/engine/src/execution/index.ts @@ -6,6 +6,7 @@ export type { export type { ExecutionMode, ExecutionStatus, StepStatus } from './execution.types'; export { ExecutionNotFoundError } from './execution-store'; export type { ExecutionRecord, ExecutionStore, NewExecutionRecord } from './execution-store'; -export type { NewStepRecord, StepStore } from './step-store'; +export { StepNotFoundError } from './step-store'; +export type { NewStepRecord, StepError, StepRecord, StepStore } from './step-store'; export { ExecutionStartHandler } from './execution-start-handler'; export { OrchestrationWorker } from './orchestration-worker'; diff --git a/packages/@n8n/engine/src/execution/step-store.ts b/packages/@n8n/engine/src/execution/step-store.ts index a5cb617ff8b..4b5c4f96bf3 100644 --- a/packages/@n8n/engine/src/execution/step-store.ts +++ b/packages/@n8n/engine/src/execution/step-store.ts @@ -1,3 +1,4 @@ +import type { JsonValue } from '../common'; import type { StepStatus } from './execution.types'; /** A new step to persist. `id` and timestamps are assigned by the store. */ @@ -7,8 +8,84 @@ export interface NewStepRecord { status: StepStatus; } +/** The error that failed a step, as persisted on its row. */ +export interface StepError { + name: string; + message: string; + stack?: string; + /** + * Step-type-specific error detail, persisted without inspection — the engine + * owns only `name`/`message`/`stack`. Unpopulated until executors have a way + * to hand structured detail across the seam; they only throw today. + */ + details?: JsonValue; +} + +/** A step record. */ +export interface StepRecord { + id: string; + executionId: string; + nodeId: string; + status: StepStatus; + /** Outputs of a completed step; `null` until it completes. */ + outputs: JsonValue | null; + /** The error that failed the step; `null` unless it failed. */ + error: StepError | null; +} + +/** Thrown by `loadStep` when no step exists for the given id. */ +export class StepNotFoundError extends Error { + constructor(readonly stepId: string) { + super(`Step not found: ${stepId}`); + this.name = 'StepNotFoundError'; + } +} + /** Persistence interface for step records. */ export interface StepStore { - /** Persist a new step record; returns its generated id. */ - createStep(record: NewStepRecord): Promise<{ id: string }>; + /** + * Persist new step records; returns their generated ids, in input order. + * Batched rather than one-per-call so planning a fan-out costs a single + * round trip and cannot half-persist. + */ + createSteps(records: NewStepRecord[]): Promise>; + + /** Load a single step by id. Throws `StepNotFoundError` if absent. */ + loadStep(id: string): Promise; + + /** + * Claim a queued step for execution (`queued → running`). A compare-and-set, + * so it returns `true` for at most one caller and duplicate/redelivered + * events are handled idempotently. + * + * Transitions are exposed one named method at a time rather than as a generic + * `(from, to)` pair, so the interface can't express a transition the + * lifecycle doesn't allow. + */ + claimStep(id: string): Promise; + + /** + * Record a successful run: persist `outputs` and mark the step completed. + * A compare-and-set on `running`, so it returns `false` if the caller no + * longer holds the claim — the outcome and the status are written together, + * so they can't be observed apart. + */ + completeStep(id: string, outputs: JsonValue): Promise; + + /** Record a failed run: persist `error` and mark the step failed. As `completeStep`. */ + failStep(id: string, error: StepError): Promise; + + /** + * Outputs of the given nodes' *completed* steps within an execution, keyed by + * node id. A node whose step is absent or hasn't completed maps to `null`. + * + * This is for gathering a step's inputs, so it deliberately can't answer + * "have all predecessors completed?" — the two are indistinguishable from a + * `null` here. Readiness belongs to whoever plans the next step + * (TODO(CAT-2871)), and will need its own method. + */ + loadStepOutputs( + executionId: string, + nodeIds: string[], + ): Promise>; } diff --git a/packages/@n8n/engine/src/index.ts b/packages/@n8n/engine/src/index.ts index fe9a294576e..3f8b8fcd847 100644 --- a/packages/@n8n/engine/src/index.ts +++ b/packages/@n8n/engine/src/index.ts @@ -35,7 +35,12 @@ export type { WorkQueue, } from './queue'; -export { ExecutionNotFoundError, ExecutionStartHandler, OrchestrationWorker } from './execution'; +export { + ExecutionNotFoundError, + ExecutionStartHandler, + OrchestrationWorker, + StepNotFoundError, +} from './execution'; export type { ExecutionMode, ExecutionRecord, @@ -43,6 +48,8 @@ export type { ExecutionStore, NewExecutionRecord, NewStepRecord, + StepError, + StepRecord, StepStatus, StepStore, } from './execution'; diff --git a/packages/@n8n/eslint-config/src/plugin.ts b/packages/@n8n/eslint-config/src/plugin.ts index 91bfa09ca2f..0f0d383d0e3 100644 --- a/packages/@n8n/eslint-config/src/plugin.ts +++ b/packages/@n8n/eslint-config/src/plugin.ts @@ -28,6 +28,8 @@ export const localRulesPlugin = { 'n8n-local-rules/no-type-only-import-in-di': 'error', 'n8n-local-rules/no-aws-credential-discovery-imports': 'error', 'n8n-local-rules/no-application-error': 'error', + 'n8n-local-rules/no-restricted-sleep-definition': 'error', + 'n8n-local-rules/no-restricted-sleep-import': 'error', }, }, }, diff --git a/packages/@n8n/eslint-config/src/rules/index.ts b/packages/@n8n/eslint-config/src/rules/index.ts index 4862dbaad06..ac48f15be11 100644 --- a/packages/@n8n/eslint-config/src/rules/index.ts +++ b/packages/@n8n/eslint-config/src/rules/index.ts @@ -23,6 +23,8 @@ import { NoAwsCredentialDiscoveryImportsRule } from './no-aws-credential-discove import { NoUncentralizedHttpRule } from './no-uncentralized-http.js'; import { NoApplicationErrorRule } from './no-application-error.js'; import { ProjectOwnedEntityTransferRule } from './project-owned-entity-transfer.js'; +import { NoRestrictedSleepDefinitionRule } from './no-restricted-sleep-definition.js'; +import { NoRestrictedSleepImportRule } from './no-restricted-sleep-import.js'; import { NoRepositoryInPublicApiHandlerRule } from './no-repository-in-public-api-handler.js'; import { RequirePublicApiControllerRule } from './require-public-api-controller.js'; import { NoPublicApiGuardrailDisableRule } from './no-public-api-guardrail-disable.js'; @@ -52,6 +54,8 @@ export const rules = { 'no-uncentralized-http': NoUncentralizedHttpRule, 'no-application-error': NoApplicationErrorRule, 'project-owned-entity-transfer': ProjectOwnedEntityTransferRule, + 'no-restricted-sleep-definition': NoRestrictedSleepDefinitionRule, + 'no-restricted-sleep-import': NoRestrictedSleepImportRule, 'no-repository-in-public-api-handler': NoRepositoryInPublicApiHandlerRule, 'require-public-api-controller': RequirePublicApiControllerRule, 'no-public-api-guardrail-disable': NoPublicApiGuardrailDisableRule, diff --git a/packages/@n8n/eslint-config/src/rules/no-dynamic-import-template.test.ts b/packages/@n8n/eslint-config/src/rules/no-dynamic-import-template.test.ts new file mode 100644 index 00000000000..e9dcd203e1b --- /dev/null +++ b/packages/@n8n/eslint-config/src/rules/no-dynamic-import-template.test.ts @@ -0,0 +1,43 @@ +import { RuleTester } from '@typescript-eslint/rule-tester'; + +import { NoDynamicImportTemplateRule } from './no-dynamic-import-template.js'; + +const ruleTester = new RuleTester(); + +ruleTester.run('no-dynamic-import-template', NoDynamicImportTemplateRule, { + valid: [ + { + code: 'await import(`./reporters/${name}.js`)', + }, + { + code: 'await import(`../reporters/${name}.mjs`)', + }, + { + code: 'await import(`./data/${name}.json`)', + }, + { + code: "await import('./reporters/credentials.js')", + }, + { + code: 'await import(`n8n-nodes-base/${name}`)', + }, + ], + invalid: [ + { + code: 'await import(`@/security-audit/${name}.js`)', + errors: [{ messageId: 'noDynamicImportTemplate' }], + }, + { + code: 'await import(`./reporters/${name}`)', + errors: [{ messageId: 'missingExtension' }], + }, + { + code: 'await import(`../reporters/${name}`)', + errors: [{ messageId: 'missingExtension' }], + }, + { + code: 'await import(`./reporters/${name}.ts`)', + errors: [{ messageId: 'missingExtension' }], + }, + ], +}); diff --git a/packages/@n8n/eslint-config/src/rules/no-dynamic-import-template.ts b/packages/@n8n/eslint-config/src/rules/no-dynamic-import-template.ts index 57f3168caf5..39d74e477c5 100644 --- a/packages/@n8n/eslint-config/src/rules/no-dynamic-import-template.ts +++ b/packages/@n8n/eslint-config/src/rules/no-dynamic-import-template.ts @@ -1,16 +1,20 @@ import { ESLintUtils, type TSESTree } from '@typescript-eslint/utils'; +const MODULE_EXTENSIONS = ['.js', '.mjs', '.cjs', '.json', '.node']; + export const NoDynamicImportTemplateRule = ESLintUtils.RuleCreator.withoutDocs({ meta: { type: 'problem', docs: { description: - 'Disallow non-relative imports in template string argument to `await import()`, because `tsc-alias` as of 1.8.7 is unable to resolve aliased paths in this scenario.', + 'Disallow non-relative imports in template string argument to `await import()`, because `tsc-alias` as of 1.8.7 is unable to resolve aliased paths in this scenario. Also require an explicit file extension, because `module: NodeNext` emits a native `import()` whose specifier Node resolves without extension inference.', }, schema: [], messages: { noDynamicImportTemplate: 'Use relative imports in template string argument to `await import()`, because `tsc-alias` as of 1.8.7 is unable to resolve aliased paths in this scenario.', + missingExtension: + 'End the template string argument to `await import()` with an explicit file extension (e.g. `.js`). Under `module: NodeNext` this compiles to a native `import()`, and Node throws ERR_MODULE_NOT_FOUND for an extensionless relative specifier.', }, }, defaultOptions: [], @@ -26,6 +30,23 @@ export const NoDynamicImportTemplateRule = ESLintUtils.RuleCreator.withoutDocs({ messageId: 'noDynamicImportTemplate', }); }, + + 'AwaitExpression > ImportExpression > TemplateLiteral'(node: TSESTree.TemplateLiteral) { + const head = node.quasis[0].value.cooked; + + if (!head?.startsWith('./') && !head?.startsWith('../')) { + return; + } + + const tail = node.quasis[node.quasis.length - 1].value.cooked ?? ''; + + if (!MODULE_EXTENSIONS.some((extension) => tail.endsWith(extension))) { + context.report({ + node, + messageId: 'missingExtension', + }); + } + }, }; }, }); diff --git a/packages/@n8n/eslint-config/src/rules/no-restricted-sleep-definition.test.ts b/packages/@n8n/eslint-config/src/rules/no-restricted-sleep-definition.test.ts new file mode 100644 index 00000000000..e3e5a998c56 --- /dev/null +++ b/packages/@n8n/eslint-config/src/rules/no-restricted-sleep-definition.test.ts @@ -0,0 +1,43 @@ +import { RuleTester } from '@typescript-eslint/rule-tester'; +import { NoRestrictedSleepDefinitionRule } from './no-restricted-sleep-definition.js'; + +const ruleTester = new RuleTester(); + +ruleTester.run('no-restricted-sleep-definition', NoRestrictedSleepDefinitionRule, { + valid: [ + { code: 'import { sleep } from "@n8n/utils/sleep"' }, + { code: 'import { sleep } from "n8n-workflow"' }, + { code: 'import { sleep } from "zx"' }, + { code: 'import { sleep } from "./sleep"' }, + { code: 'import { retry } from "@n8n/utils/retry"' }, + { code: 'import { something } from "n8n-workflow"' }, + { code: 'function sleep() {}', filename: '/repo/packages/@n8n/utils/src/sleep.ts' }, + { + code: 'const sleepWithAbort = () => {};', + filename: '/repo/packages/@n8n/utils/src/sleep.ts', + }, + { + code: 'function sleep(ms: number) {}', + filename: '/repo/packages/@n8n/typeorm/test/utils/test-utils.ts', + }, + { + code: 'export async function sleep(ms: number) {}', + filename: '/repo/packages/@n8n/node-cli/src/commands/dev/utils.ts', + }, + ], + + invalid: [ + { + code: 'function sleep(ms: number) {}', + errors: [{ messageId: 'noRestrictedSleepDefinition' }], + }, + { + code: 'async function sleepWithAbort(ms: number, signal: AbortSignal) {}', + errors: [{ messageId: 'noRestrictedSleepDefinition' }], + }, + { + code: 'const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms));', + errors: [{ messageId: 'noRestrictedSleepDefinition' }], + }, + ], +}); diff --git a/packages/@n8n/eslint-config/src/rules/no-restricted-sleep-definition.ts b/packages/@n8n/eslint-config/src/rules/no-restricted-sleep-definition.ts new file mode 100644 index 00000000000..feb7c328fa7 --- /dev/null +++ b/packages/@n8n/eslint-config/src/rules/no-restricted-sleep-definition.ts @@ -0,0 +1,51 @@ +import { ESLintUtils, TSESTree } from '@typescript-eslint/utils'; + +const RESTRICTED_NAMES = new Set(['sleep', 'sleepWithAbort']); +const CANONICAL_FILE = /\/@n8n\/utils\/src\/sleep\.ts$/; +const EXEMPT_PACKAGES = [/\/@n8n\/typeorm\//, /\/@n8n\/node-cli\//]; + +export const NoRestrictedSleepDefinitionRule = ESLintUtils.RuleCreator.withoutDocs({ + meta: { + type: 'problem', + docs: { + description: 'Disallow local definitions of `sleep` and `sleepWithAbort`.', + }, + messages: { + noRestrictedSleepDefinition: + 'Do not define your own `{{ name }}`. Import `sleep` from `@n8n/utils/sleep` instead.', + }, + schema: [], + }, + defaultOptions: [], + create(context) { + const filename = context.filename.replace(/\\/g, '/'); + if (CANONICAL_FILE.test(filename) || EXEMPT_PACKAGES.some((re) => re.test(filename))) { + return {}; + } + + return { + FunctionDeclaration(node) { + if (node.id && RESTRICTED_NAMES.has(node.id.name)) { + context.report({ + node: node.id, + messageId: 'noRestrictedSleepDefinition', + data: { name: node.id.name }, + }); + } + }, + + VariableDeclarator(node) { + if ( + node.id.type === TSESTree.AST_NODE_TYPES.Identifier && + RESTRICTED_NAMES.has(node.id.name) + ) { + context.report({ + node: node.id, + messageId: 'noRestrictedSleepDefinition', + data: { name: node.id.name }, + }); + } + }, + }; + }, +}); diff --git a/packages/@n8n/eslint-config/src/rules/no-restricted-sleep-import.test.ts b/packages/@n8n/eslint-config/src/rules/no-restricted-sleep-import.test.ts new file mode 100644 index 00000000000..82bdeac9398 --- /dev/null +++ b/packages/@n8n/eslint-config/src/rules/no-restricted-sleep-import.test.ts @@ -0,0 +1,32 @@ +import { RuleTester } from '@typescript-eslint/rule-tester'; +import { NoRestrictedSleepImportRule } from './no-restricted-sleep-import.js'; + +const ruleTester = new RuleTester(); + +ruleTester.run('no-restricted-sleep-import', NoRestrictedSleepImportRule, { + valid: [ + { code: 'import { sleep } from "@n8n/utils/sleep"' }, + { code: 'import { sleep } from "zx"' }, + { code: 'import { sleep } from "./sleep"' }, + { code: 'import { jsonParse } from "n8n-workflow"' }, + // Namespace imports say nothing about what they use — the generated + // `vi.importActual` shape relies on this. + { code: 'import type * as _importType0 from "n8n-workflow"' }, + { code: 'import * as n8nWorkflow from "n8n-workflow"' }, + ], + + invalid: [ + { + code: 'import { sleep } from "n8n-workflow"', + errors: [{ messageId: 'noRestrictedSleepImport' }], + }, + { + code: 'import { sleep as delay } from "n8n-workflow"', + errors: [{ messageId: 'noRestrictedSleepImport' }], + }, + { + code: 'import { jsonParse, sleep } from "n8n-workflow"', + errors: [{ messageId: 'noRestrictedSleepImport' }], + }, + ], +}); diff --git a/packages/@n8n/eslint-config/src/rules/no-restricted-sleep-import.ts b/packages/@n8n/eslint-config/src/rules/no-restricted-sleep-import.ts new file mode 100644 index 00000000000..12e5c0e61dc --- /dev/null +++ b/packages/@n8n/eslint-config/src/rules/no-restricted-sleep-import.ts @@ -0,0 +1,48 @@ +// `n8n-workflow` re-exports `sleep` only as a backwards-compat layer for community +// nodes. Internal code must reach the canonical implementation in `@n8n/utils/sleep` +// directly, so the shim stays inert and can be dropped once it is no longer needed. +// +// Scope (intentional): only a named `sleep` specifier is reported. A namespace import +// (`import * as x from 'n8n-workflow'`) is left alone — it is the generated +// `import type * as _importType0` shape used with `vi.importActual`, and flagging it +// would report every namespace import of the package regardless of what it uses. +import { ESLintUtils } from '@typescript-eslint/utils'; + +const RESTRICTED_SOURCE = 'n8n-workflow'; + +export const NoRestrictedSleepImportRule = ESLintUtils.RuleCreator.withoutDocs({ + meta: { + type: 'problem', + docs: { + description: 'Disallow importing `sleep` from `n8n-workflow`.', + }, + messages: { + noRestrictedSleepImport: + 'Do not import `sleep` from `n8n-workflow`. Import it from `@n8n/utils/sleep` instead.', + }, + schema: [], + }, + defaultOptions: [], + create(context) { + return { + ImportDeclaration(node) { + if (node.source.value !== RESTRICTED_SOURCE) return; + + for (const specifier of node.specifiers) { + if (specifier.type !== 'ImportSpecifier') continue; + + // `imported` is the name in `n8n-workflow`, so an alias + // (`sleep as delay`) is still reported. + const importedName = + specifier.imported.type === 'Identifier' + ? specifier.imported.name + : specifier.imported.value; + + if (importedName === 'sleep') { + context.report({ node: specifier, messageId: 'noRestrictedSleepImport' }); + } + } + }, + }; + }, +}); diff --git a/packages/@n8n/eslint-plugin-community-nodes/docs/rules/no-restricted-globals.md b/packages/@n8n/eslint-plugin-community-nodes/docs/rules/no-restricted-globals.md index 57f3d674429..bb4b7cc9396 100644 --- a/packages/@n8n/eslint-plugin-community-nodes/docs/rules/no-restricted-globals.md +++ b/packages/@n8n/eslint-plugin-community-nodes/docs/rules/no-restricted-globals.md @@ -33,12 +33,26 @@ export class MyNode implements INodeType { ### ✅ Correct ```typescript +import { sleep } from 'n8n-workflow'; + export class MyNode implements INodeType { async execute(this: IExecuteFunctions) { // Use n8n context methods instead const timezone = this.getTimezone(); + // Use the sleep helper instead of setTimeout + await sleep(1000); + return this.prepareOutputData([]); } } ``` + +## Alternatives to restricted timer globals + +`n8n-workflow` exports helpers that work the same everywhere, including on +n8n Cloud, instead of reaching for the restricted timer globals directly: + +- Instead of `setTimeout(resolve, ms)`, use `await sleep(ms)`. +- Instead of `setTimeout` + `clearTimeout` for a cancellable delay, use + `await sleepWithAbort(ms, abortSignal)`. diff --git a/packages/@n8n/eslint-plugin-community-nodes/src/rules/no-restricted-globals.test.ts b/packages/@n8n/eslint-plugin-community-nodes/src/rules/no-restricted-globals.test.ts index 660921bce93..37f2a4d0335 100644 --- a/packages/@n8n/eslint-plugin-community-nodes/src/rules/no-restricted-globals.test.ts +++ b/packages/@n8n/eslint-plugin-community-nodes/src/rules/no-restricted-globals.test.ts @@ -70,6 +70,17 @@ var __dirname = '/path'; const __filename = 'file.js'; `, }, + { + name: 'sleep and sleepWithAbort from n8n-workflow should be allowed', + code: ` +import { sleep, sleepWithAbort } from 'n8n-workflow'; + +async function wait(signal) { + await sleep(1000); + await sleepWithAbort(1000, signal); +} + `, + }, ], invalid: [ { @@ -82,7 +93,12 @@ const __filename = 'file.js'; }, { code: 'setTimeout(() => {}, 1000);', - errors: [{ messageId: 'restrictedGlobal', data: { name: 'setTimeout' } }], + errors: [ + { + messageId: 'restrictedGlobalWithHint', + data: { name: 'setTimeout', hint: "Use the 'sleep' helper from 'n8n-workflow' instead." }, + }, + ], }, { code: 'clearInterval(timer);', @@ -90,7 +106,15 @@ const __filename = 'file.js'; }, { code: 'clearTimeout(timer);', - errors: [{ messageId: 'restrictedGlobal', data: { name: 'clearTimeout' } }], + errors: [ + { + messageId: 'restrictedGlobalWithHint', + data: { + name: 'clearTimeout', + hint: "Use 'sleepWithAbort' from 'n8n-workflow' with an AbortSignal instead.", + }, + }, + ], }, { code: 'setInterval(() => {}, 1000);', diff --git a/packages/@n8n/eslint-plugin-community-nodes/src/rules/no-restricted-globals.ts b/packages/@n8n/eslint-plugin-community-nodes/src/rules/no-restricted-globals.ts index c9f2f3f4e29..f4a71cc3215 100644 --- a/packages/@n8n/eslint-plugin-community-nodes/src/rules/no-restricted-globals.ts +++ b/packages/@n8n/eslint-plugin-community-nodes/src/rules/no-restricted-globals.ts @@ -17,6 +17,12 @@ const restrictedGlobals = [ '__filename', ]; +// Nudge toward the n8n-workflow alternatives that work under n8n Cloud's restrictions +const restrictedGlobalHints: Record = { + setTimeout: "Use the 'sleep' helper from 'n8n-workflow' instead.", + clearTimeout: "Use 'sleepWithAbort' from 'n8n-workflow' with an AbortSignal instead.", +}; + export const NoRestrictedGlobalsRule = createRule({ name: 'no-restricted-globals', meta: { @@ -26,6 +32,7 @@ export const NoRestrictedGlobalsRule = createRule({ }, messages: { restrictedGlobal: "Use of restricted global '{{ name }}' is not allowed", + restrictedGlobalWithHint: "Use of restricted global '{{ name }}' is not allowed. {{ hint }}", }, schema: [], }, @@ -43,11 +50,21 @@ export const NoRestrictedGlobalsRule = createRule({ return; } - context.report({ - node: ref.identifier, - messageId: 'restrictedGlobal', - data: { name }, - }); + const hint = restrictedGlobalHints[name]; + + context.report( + hint + ? { + node: ref.identifier, + messageId: 'restrictedGlobalWithHint', + data: { name, hint }, + } + : { + node: ref.identifier, + messageId: 'restrictedGlobal', + data: { name }, + }, + ); } return { diff --git a/packages/@n8n/instance-ai/evaluations/README.md b/packages/@n8n/instance-ai/evaluations/README.md index d186a7bfbc3..966b5ba12d4 100644 --- a/packages/@n8n/instance-ai/evaluations/README.md +++ b/packages/@n8n/instance-ai/evaluations/README.md @@ -306,6 +306,7 @@ Not yet covered: an automatic "unexpected artifact" fail (a build producing an a | `LANGSMITH_BRANCH` | No | Branch name to tag the experiment with (auto-set in CI) | | `CONTEXT7_API_KEY` | No | Context7 key for API-doc lookups. Improves mock realism for less-common services; the LLM falls back to training data when unset | | `N8N_AI_ASSISTANT_BASE_URL` | No | Set to `""` to bypass the hosted AI proxy and hit Anthropic directly — useful to avoid per-tenant quota during large batch runs | +| `INSTANCE_AI_BRAVE_SEARCH_API_KEY` | No | Set on the **target n8n instance** (note: no `N8N_` prefix) to enable the builder's `web-search` action. Unset = the action returns zero results, which reads to the agent as "nothing found". A licensed instance with `N8N_AI_ASSISTANT_BASE_URL` set routes search through the AI proxy instead and ignores this key | | `N8N_INSTANCE_AI_RUN_DEBUG_ENABLED` | No | Set to `true` on the target n8n instance to capture orchestrator LLM steps and workflow code for the eval LLM debug report (`workflow-eval-llm-debug.html`). Off by default. | **LangSmith caveat:** if `LANGSMITH_API_KEY` is set in `.env.local`, local runs also land in the shared `instance-ai-workflow-evals` dataset. Unset it (or run without `dotenvx`) to keep exploratory runs out of team results. @@ -672,7 +673,7 @@ To record an isolated cohort without touching the shared dataset or baseline — ## Adding test cases -The corpus lives in **LangTracer** — suite `baseline` is what CI runs. Author a case as a local JSON file in `evaluations/data/workflows/` (disk mode picks it up, no registration step), calibrate it against a real build, then push it to the suite with `pnpm eval:langtracer-push --suite baseline ` and delete the local file rather than committing it. Seeded cases (`priorConversation`/`seedFile`) are the exception — the case-write API can't represent them yet, so they stay as committed JSON. Every case is validated against `harness/schema.ts`. +The corpus lives in **LangTracer** — suite `baseline` is what CI runs. Author a case as a local JSON file in `evaluations/data/workflows/` (disk mode picks it up, no registration step), calibrate it against a real build, then push it to the suite with `pnpm eval:langtracer-push --suite baseline ` and delete the local file rather than committing it. Seeded cases (`priorConversation`/`conversationSeed`) are the exception — the case-write API can't represent them yet, so they stay as committed JSON. Every case is validated against `harness/schema.ts`. > The essentials are below. For the full authoring guide — picking a case archetype, sizing assertions so a wrong build fails, multi-turn director scripts, seeding vs synthetic, and calibrating against a real build — follow the [`create-instance-ai-eval` skill](../../../../.agents/skills/create-instance-ai-eval/SKILL.md) (with [`case-shapes.md`](../../../../.agents/skills/create-instance-ai-eval/case-shapes.md) and [`running-evals.md`](../../../../.agents/skills/create-instance-ai-eval/running-evals.md)). @@ -714,8 +715,9 @@ Write the turns as a screenplay of what the user wants, keeping concrete values | Withhold a value until asked | `[Don't bring up the channel unless the agent asks where to post; then say 'Slack #growth.']` | | Refuse and hold firm on re-ask | `[The user has no channel and won't provide one. If asked — question or setup card, even repeatedly — skip it; never invent one.]` | | Keep the conversation going | `[After each change lands, send the next one from the list, one at a time, until done.]` | +| Refuse network access | `[Deny the web-search request — the user doesn't want it searching the web.]` | -A direction governs only what it covers; otherwise the proxy answers every question (inventing plausible placeholders) and never sets credentials. Setup cards (the "configure your workflow" card) are filled via the wizard — or dismissed when a direction withholds the value — not answered as questions. +A direction governs only what it covers; otherwise the proxy answers every question (inventing plausible placeholders) and never sets credentials. Network-access prompts (`web-search`, `fetch-url`) are the one gate that's granted **without** consulting the proxy LLM, so they cost nothing by default — but while any stage direction is still pending the decision goes to the LLM, which is what makes the refusal above reachable. Setup cards (the "configure your workflow" card) are filled via the wizard — or dismissed when a direction withholds the value — not answered as questions. **Prompt / conversation tips** @@ -759,7 +761,7 @@ Pick the lightest path that fits: |---|---| | Reproduce a real conversation (the common case) | `seedThread` — fetch + reconstruct its LangSmith trace at run time; nothing committed | | Prelude is just "what was discussed" (no tool calls, no workflows) | `priorConversation` — prose turns, authored inline | -| A synthetic/sanitized fixture you want durable | `seedFile` — a committed seed JSON (no real conversation data) | +| Prior work already exists (a workflow to repair) | `conversationSeed` — prior messages + the workflows they reference, in the case body | | Shallow 2–3 turn prelude where the agent's live replies matter | Neither — a plain multi-turn `conversation` script re-drives it live | #### `seedThread` — reproduce a real conversation (no repo content) @@ -805,9 +807,22 @@ To find the thread id, open the conversation's trace in LangSmith (or read it fr Paired with a normal `conversation` for the live turn. Plain text only — no tool calls, no restored workflows. -#### `seedFile` — durable synthetic fixture +#### `conversationSeed` — durable synthetic fixture -For a **synthetic, sanitized** fixture you want pinned in git (never a real user's conversation): hand-author a `data/workflows/seeds/.seed.json` (schema in `harness/conversation-seed.ts` — `messages` + optional `workflows`) and point `seedFile` at it. Real conversations belong in `seedThread`, which keeps their content out of the repo entirely. Paired with a normal `conversation` for the live turn. +For a **synthetic, sanitized** seed you want pinned in git (never a real user's conversation): author the prior messages, plus the workflows they reference, in the case body. Real conversations belong in `seedThread`, which keeps their content out of the repo entirely. Paired with a normal `conversation` for the live turn. + +```json +"conversationSeed": { + "messages": [ … ], + "workflows": [ { "id": "wKk3RmT9xQ2bVn7L", "name": "Batch loop", "nodes": [], "connections": {} } ] +} +``` + +Schema in `harness/conversation-seed.ts` — `messages` plus optional `workflows` and `dataTables`. Two constraints worth knowing: a workflow `id` must be ≥8 characters (`remapSeedWorkflowIds` refuses to rewrite shorter ids safely), and a seeded `build-workflow` tool call's `output.workflowId` must match the seeded workflow's `id`, or the remap separates them and the agent can't find the workflow it's meant to act on. + +**Each message must carry the envelope** — `id`, `role` (`user` or `assistant`), `type` (`llm`, `custom`, …), `createdAt` (a parseable timestamp; ordering before the live turn depends on it), and `content` as an array of blocks each with a `type`. Only the envelope is validated: **unknown block types are accepted**, because block shapes belong to the agent's message store rather than to the harness, and unknown keys are preserved rather than stripped. A `type: 'custom'` message is the one exception — it's stored but never rendered, so it may omit `role` and carry any `content` shape. The envelope is checked because a malformed message would otherwise be stored verbatim *and* skipped by `transcriptPrefixFromSeed`, leaving the case graded against a transcript that doesn't match what the agent saw. + +The seed lives **in the case body** rather than in a sibling file, so it travels with the case whatever the source — a JSON on disk, a suite pulled with `--source langtracer`, or a case body handed to a dispatcher. (There used to be a `seedFile` path pointing at a sibling JSON. Only the disk loader could resolve it, so a case delivered any other way lost its seed; the key is gone and a case still carrying it fails at load.) #### How restore works (all paths) @@ -816,7 +831,7 @@ At build time the seed is restored right after the credential pin: seeded workfl Rules of thumb: - **A seeded case is only worth shipping with `buildExpectations` that detect the misbehaviour recurring** — without them it passes vacuously. Sanity-check by running the case once with the seed removed: it should fail. -- `seedThread`, `priorConversation` and `seedFile` are mutually exclusive; all order strictly before the live turn. `seedThread` provides its own live turn (omit `conversation`); the other two pair with `conversation`. +- `seedThread`, `priorConversation` and `conversationSeed` are mutually exclusive; all order strictly before the live turn. `seedThread` provides its own live turn (omit `conversation`); the other two pair with `conversation`. ## Failure categories @@ -860,7 +875,7 @@ evaluations/ ├── checklist/ # LLM verification with retry ├── credentials/ # Test credential seeding ├── data/agents/ # authoring dir for intent-resolution cases (the corpus lives in LangTracer suite `agents`) -├── data/workflows/ # seeded carve-out case JSONs + seeds/ (the corpus lives in LangTracer) +├── data/workflows/ # seeded carve-out case JSONs (the corpus lives in LangTracer) ├── data/subagent/ # workflow-build compatibility fixture JSON files ├── data/pairwise/ # Local pairwise fixture (small smoke set) ├── harness/ # Runners: buildWorkflow + executeScenario (e2e), in-memory event bus (discovery) diff --git a/packages/@n8n/instance-ai/evaluations/__tests__/build-workflow-conversation-seed.test.ts b/packages/@n8n/instance-ai/evaluations/__tests__/build-workflow-conversation-seed.test.ts new file mode 100644 index 00000000000..3ffa406264c --- /dev/null +++ b/packages/@n8n/instance-ai/evaluations/__tests__/build-workflow-conversation-seed.test.ts @@ -0,0 +1,135 @@ +import { vi } from 'vitest'; + +import type { N8nClient } from '../clients/n8n-client'; +import { buildWorkflow } from '../harness/build-workflow'; +import type { ConversationSeed } from '../harness/conversation-seed'; +import type { EvalLogger } from '../harness/logger'; + +// Stubbed as in build-workflow-seed-cleanup: only the restore call matters here. +vi.mock('../harness/chat-loop', () => ({ + SSE_SETTLE_DELAY_MS: 0, + startSseConnection: vi.fn().mockResolvedValue(undefined), + waitForAllActivity: vi.fn().mockResolvedValue(undefined), + runMultiTurnConversation: vi.fn().mockResolvedValue(undefined), + recordUserTurn: vi.fn(), +})); + +vi.mock('../outcome/workflow-discovery', () => ({ + buildAgentOutcome: vi.fn().mockResolvedValue({ + workflowsCreated: [{ id: 'built-wf-1', name: 'Built', nodeCount: 3, active: false }], + executionsRun: [], + dataTablesCreated: [], + finalText: 'done', + workflowJsons: [{ id: 'built-wf-1', name: 'Built', nodes: [], connections: {} }], + }), + extractWorkflowIdsFromMessages: vi.fn().mockReturnValue([]), +})); + +const silentLogger: EvalLogger = { + info: () => {}, + verbose: () => {}, + success: () => {}, + warn: () => {}, + error: () => {}, + isVerbose: false, +}; + +const SEED_WF_ID = 'wKk3RmT9xQ2bVn7L'; + +function inlineSeed(): ConversationSeed { + return { + messages: [ + { + id: 'seed-1', + type: 'llm', + role: 'assistant', + createdAt: '2026-06-29T09:00:01.000Z', + content: [ + { + type: 'tool-call', + toolCallId: 'tc-1', + toolName: 'build-workflow', + state: 'resolved', + input: {}, + output: { success: true, workflowId: SEED_WF_ID }, + }, + ], + }, + ], + workflows: [{ id: SEED_WF_ID, name: 'Batch loop', nodes: [], connections: {} }], + dataTables: [], + }; +} + +function makeClient(restoreThread: ReturnType): N8nClient { + return { + getPersonalProjectId: vi.fn().mockResolvedValue('project-1'), + ensureThread: vi.fn().mockResolvedValue(undefined), + setThreadCredentialAllowlist: vi.fn().mockResolvedValue(undefined), + sendMessage: vi.fn().mockResolvedValue(undefined), + getThreadMessages: vi.fn().mockResolvedValue({ messages: [] }), + restoreThread, + } as unknown as N8nClient; +} + +const baseConfig = { + conversation: [{ role: 'user' as const, text: 'the loop never runs twice — fix it' }], + outcomeExpectations: ['the processing branch hangs off the loop output'], + skipWorkflowChecks: true, + preRunWorkflowIds: new Set(), + claimedWorkflowIds: new Set(), + logger: silentLogger, +}; + +describe('buildWorkflow with a conversationSeed', () => { + it('restores the inline seed before the live turn', async () => { + const restoreThread = vi + .fn() + .mockResolvedValue({ restored: 1, workflowIds: ['restored-wf-1'], dataTableIds: [] }); + + const build = await buildWorkflow({ + client: makeClient(restoreThread), + ...baseConfig, + conversationSeed: inlineSeed(), + }); + + expect(build.success).toBe(true); + expect(restoreThread).toHaveBeenCalledTimes(1); + const [, messages, workflows] = restoreThread.mock.calls[0] as [ + string, + Array>, + Array<{ id: string }>, + ]; + expect(messages).toHaveLength(1); + expect(workflows).toHaveLength(1); + // Remapped to a fresh id so parallel iterations can't share one row. + const newId = workflows[0].id; + expect(newId).not.toBe(SEED_WF_ID); + expect(JSON.stringify(messages)).toContain(newId); + expect(JSON.stringify(messages)).not.toContain(SEED_WF_ID); + }); + + it('fails as a seeding problem when the restore fails — never runs unseeded', async () => { + const restoreThread = vi.fn().mockRejectedValue(new Error('restore rejected')); + + const build = await buildWorkflow({ + client: makeClient(restoreThread), + ...baseConfig, + conversationSeed: inlineSeed(), + }); + + expect(build.success).toBe(false); + expect(build.seedingFailed).toBe(true); + }); + + it('does not restore anything for a case with no seed', async () => { + const restoreThread = vi + .fn() + .mockResolvedValue({ restored: 0, workflowIds: [], dataTableIds: [] }); + + const build = await buildWorkflow({ client: makeClient(restoreThread), ...baseConfig }); + + expect(build.success).toBe(true); + expect(restoreThread).not.toHaveBeenCalled(); + }); +}); diff --git a/packages/@n8n/instance-ai/evaluations/__tests__/conversation-seed.test.ts b/packages/@n8n/instance-ai/evaluations/__tests__/conversation-seed.test.ts index 8550b0e2ab4..ef1766ecf4a 100644 --- a/packages/@n8n/instance-ai/evaluations/__tests__/conversation-seed.test.ts +++ b/packages/@n8n/instance-ai/evaluations/__tests__/conversation-seed.test.ts @@ -1,9 +1,5 @@ -import { mkdtempSync, writeFileSync } from 'node:fs'; -import { tmpdir } from 'node:os'; -import { join } from 'node:path'; - import { - loadConversationSeed, + ConversationSeedSchema, remapSeedWorkflowIds, seedFromProse, transcriptPrefixFromSeed, @@ -46,37 +42,112 @@ function makeSeed(): ConversationSeed { }; } -describe('loadConversationSeed', () => { - const dir = mkdtempSync(join(tmpdir(), 'conversation-seed-')); +describe('ConversationSeedSchema message envelope', () => { + const message = (over: Record = {}) => ({ + id: 'm1', + role: 'user', + type: 'llm', + createdAt: '2026-01-01T00:00:00.000Z', + content: [{ type: 'text', text: 'build it' }], + ...over, + }); + const parse = (...messages: Array>) => + ConversationSeedSchema.safeParse({ messages }); + const errorOf = (result: ReturnType) => + result.success ? '' : JSON.stringify(result.error.issues); - it('loads and validates a seed file', () => { - const path = join(dir, 'valid.seed.json'); - writeFileSync(path, JSON.stringify(makeSeed())); - - const seed = loadConversationSeed(path); - expect(seed.messages).toHaveLength(2); - expect(seed.workflows[0].id).toBe(WF_ID); + it('accepts a well-formed message', () => { + expect(parse(message()).success).toBe(true); }); - it('defaults workflows to an empty array', () => { - const path = join(dir, 'no-workflows.seed.json'); - writeFileSync(path, JSON.stringify({ messages: [{ id: 'm1' }] })); + for (const field of ['id', 'role', 'type', 'createdAt', 'content'] as const) { + it(`rejects a message missing ${field}`, () => { + const broken = message(); + delete broken[field]; + const result = parse(broken); + expect(result.success).toBe(false); + expect(errorOf(result)).toContain(field); + }); + } - expect(loadConversationSeed(path).workflows).toEqual([]); + it('rejects a role the transcript builder would silently drop', () => { + // The typo'd-role case: stored verbatim, then skipped by + // transcriptPrefixFromSeed, so the case grades a transcript the agent never saw. + const result = parse(message({ role: 'assistent' })); + expect(result.success).toBe(false); + expect(errorOf(result)).toContain('role'); }); - it('rejects a seed without messages, naming the file', () => { - const path = join(dir, 'empty.seed.json'); - writeFileSync(path, JSON.stringify({ messages: [] })); - - expect(() => loadConversationSeed(path)).toThrow(/Invalid conversation seed .*empty\.seed/); + it('rejects a createdAt that is not a real timestamp', () => { + // Ordering before the live turn depends on this parsing. + const result = parse(message({ createdAt: 'yesterday' })); + expect(result.success).toBe(false); + expect(errorOf(result)).toContain('createdAt'); }); - it('rejects a missing file with a readable error', () => { - expect(() => loadConversationSeed(join(dir, 'nope.seed.json'))).toThrow( - /Failed to read conversation seed/, + it('rejects content that is not an array of blocks', () => { + expect(parse(message({ content: 'build it' })).success).toBe(false); + expect(parse(message({ content: [{ text: 'no type' }] })).success).toBe(false); + }); + + it('accepts an unknown block type — block shapes are the message store’s contract', () => { + expect(parse(message({ content: [{ type: 'some-future-block', payload: {} }] })).success).toBe( + true, ); }); + + it('accepts a custom message with no role and non-array content (stored, never rendered)', () => { + const result = parse( + { id: 'c1', type: 'custom', data: { widget: 'card' }, createdAt: '2026-01-01T00:00:00Z' }, + message(), + ); + expect(result.success).toBe(true); + }); + + it('preserves unknown keys on messages and blocks rather than stripping them', () => { + // The load-bearing one: z.object strips unknown keys by default, which would + // silently gut toolCallId/input/output from every tool call before restore. + const result = ConversationSeedSchema.safeParse({ + messages: [ + message({ + messageGroupId: 'mg-1', + content: [ + { + type: 'tool-call', + toolCallId: 'tc-1', + toolName: 'build-workflow', + state: 'resolved', + input: { name: 'Digest' }, + output: { success: true, workflowId: 'AbCdEf1234567890' }, + }, + ], + }), + ], + }); + expect(result.success).toBe(true); + if (!result.success) return; + const [only] = result.data.messages; + expect(only.messageGroupId).toBe('mg-1'); + expect(only.content?.[0]).toMatchObject({ + toolCallId: 'tc-1', + toolName: 'build-workflow', + state: 'resolved', + input: { name: 'Digest' }, + output: { success: true, workflowId: 'AbCdEf1234567890' }, + }); + }); + + it('accepts what seedFromProse produces', () => { + const seed = seedFromProse([ + { role: 'user', text: 'hi' }, + { role: 'assistant', text: 'hello' }, + ]); + expect(ConversationSeedSchema.safeParse(seed).success).toBe(true); + }); + + it('still requires at least one message', () => { + expect(ConversationSeedSchema.safeParse({ messages: [] }).success).toBe(false); + }); }); describe('seedFromProse', () => { diff --git a/packages/@n8n/instance-ai/evaluations/__tests__/data-workflows-schema.test.ts b/packages/@n8n/instance-ai/evaluations/__tests__/data-workflows-schema.test.ts index 3f6446c51a6..0aadeff1969 100644 --- a/packages/@n8n/instance-ai/evaluations/__tests__/data-workflows-schema.test.ts +++ b/packages/@n8n/instance-ai/evaluations/__tests__/data-workflows-schema.test.ts @@ -90,11 +90,53 @@ describe('EvalTestCaseSchema', () => { expect(parsed.priorConversation).toHaveLength(1); }); - it('rejects seedFile combined with priorConversation', () => { + it('rejects a case still pointing at a seedFile', () => { + expect(() => + EvalTestCaseSchema.parse({ ...validFixture(), seedFile: 'seeds/some-thread.seed.json' }), + ).toThrow(/seedFile/); + }); + + it('accepts an inline conversationSeed and defaults its optional arrays', () => { + const parsed = EvalTestCaseSchema.parse({ + ...validFixture(), + conversationSeed: { + messages: [ + { + id: 'm1', + type: 'llm', + role: 'user', + createdAt: '2026-06-29T09:00:00.000Z', + content: [{ type: 'text', text: 'build it' }], + }, + ], + }, + }); + expect(parsed.conversationSeed?.messages).toHaveLength(1); + expect(parsed.conversationSeed?.workflows).toEqual([]); + expect(parsed.conversationSeed?.dataTables).toEqual([]); + }); + + it('rejects an inline conversationSeed with no messages', () => { + expect(() => + EvalTestCaseSchema.parse({ ...validFixture(), conversationSeed: { messages: [] } }), + ).toThrow(); + }); + + it('rejects conversationSeed combined with another seeding mode', () => { expect(() => EvalTestCaseSchema.parse({ ...validFixture(), - seedFile: 'seeds/some-thread.seed.json', + conversationSeed: { + messages: [ + { + id: 'm1', + type: 'llm', + role: 'user', + createdAt: '2026-06-29T09:00:00.000Z', + content: [{ type: 'text', text: 'build it' }], + }, + ], + }, priorConversation: [{ role: 'user', text: 'prelude' }], }), ).toThrow(/mutually exclusive/); @@ -164,7 +206,7 @@ describe('EvalTestCaseSchema', () => { EvalTestCaseSchema.parse({ ...rest, seedThread: { threadId: 't1' }, - seedFile: 'seeds/x.seed.json', + priorConversation: [{ role: 'user', text: 'prelude' }], }), ).toThrow(/mutually exclusive/); }); diff --git a/packages/@n8n/instance-ai/evaluations/__tests__/eval-artifact-output-dir.test.ts b/packages/@n8n/instance-ai/evaluations/__tests__/eval-artifact-output-dir.test.ts new file mode 100644 index 00000000000..0720f169880 --- /dev/null +++ b/packages/@n8n/instance-ai/evaluations/__tests__/eval-artifact-output-dir.test.ts @@ -0,0 +1,186 @@ +import { existsSync, mkdtempSync, readdirSync } from 'fs'; +import { tmpdir } from 'os'; +import path from 'path'; + +import type { EvalLogger } from '../harness/logger'; +import { writeScenarioVerificationSnapshot } from '../harness/scenario-execution'; +import { writeRunDebugReport } from '../report/run-debug-report'; +import { writeWorkflowReport } from '../report/workflow-report'; +import type { ChecklistResult, WorkflowTestCase, WorkflowTestCaseResult } from '../types'; + +// Pins artifact PLACEMENT for the lang-tracer dispatcher (lang-tracer +// `packages/dispatcher/src/lib/runner.ts`): it runs several concurrent eval +// children against ONE n8n checkout in one container — each child gets its own +// `--output-dir`, and relies on that flag covering EVERY artifact the run +// writes, not just `eval-results.json`. All three writers that used to hardcode +// the package-level `.data` directory are covered here. The HTML reports are +// the sharpest case: their filenames are stable +// (`workflow-eval-report.html`, `workflow-eval-llm-debug.html`), so ignoring +// `--output-dir` silently lets concurrent runs clobber each other's reports. +// The no-arg default must stay `.data` for local dev and for n8n's own eval CI, +// which uploads that path as a build artifact without passing `--output-dir`. + +const DEFAULT_REPORT_DIR = path.join(__dirname, '..', '..', '.data'); + +const TEST_CASE: WorkflowTestCase = { + conversation: [{ role: 'user', text: 'Build a Slack notifier' }], + complexity: 'simple', + tags: [], + executionScenarios: [{ name: 's', description: 'd', dataSetup: '', successCriteria: 'ok' }], + datasets: ['full'], +}; + +const RESULTS: WorkflowTestCaseResult[] = [ + { + testCase: TEST_CASE, + workflowBuildSuccess: true, + executionScenarioResults: [], + fileSlug: 'slack-notifier', + }, +]; + +function freshOutputDir(): string { + return mkdtempSync(path.join(tmpdir(), 'eval-artifact-output-dir-')); +} + +describe('eval report artifacts — --output-dir contract', () => { + describe('writeWorkflowReport', () => { + it('writes the timestamped and stable reports into the given outputDir', () => { + const outputDir = freshOutputDir(); + + const reportPath = writeWorkflowReport(RESULTS, outputDir); + + expect(path.dirname(reportPath)).toBe(outputDir); + expect(existsSync(reportPath)).toBe(true); + expect(existsSync(path.join(outputDir, 'workflow-eval-report.html'))).toBe(true); + }); + + it('creates the outputDir when it does not exist yet', () => { + const outputDir = path.join(freshOutputDir(), 'nested', 'run-1'); + + const reportPath = writeWorkflowReport(RESULTS, outputDir); + + expect(path.dirname(reportPath)).toBe(outputDir); + expect(readdirSync(outputDir)).toContain('workflow-eval-report.html'); + }); + + it('falls back to the package .data directory when no outputDir is given', () => { + const reportPath = writeWorkflowReport(RESULTS); + + expect(path.dirname(reportPath)).toBe(DEFAULT_REPORT_DIR); + expect(existsSync(path.join(DEFAULT_REPORT_DIR, 'workflow-eval-report.html'))).toBe(true); + }); + }); + + describe('writeRunDebugReport', () => { + it('writes the timestamped and stable reports into the given outputDir', () => { + const outputDir = freshOutputDir(); + + const reportPath = writeRunDebugReport(RESULTS, outputDir); + + expect(path.dirname(reportPath)).toBe(outputDir); + expect(existsSync(reportPath)).toBe(true); + expect(existsSync(path.join(outputDir, 'workflow-eval-llm-debug.html'))).toBe(true); + }); + + it('creates the outputDir when it does not exist yet', () => { + const outputDir = path.join(freshOutputDir(), 'nested', 'run-2'); + + const reportPath = writeRunDebugReport(RESULTS, outputDir); + + expect(path.dirname(reportPath)).toBe(outputDir); + expect(readdirSync(outputDir)).toContain('workflow-eval-llm-debug.html'); + }); + + it('falls back to the package .data directory when no outputDir is given', () => { + const reportPath = writeRunDebugReport(RESULTS); + + expect(path.dirname(reportPath)).toBe(DEFAULT_REPORT_DIR); + expect(existsSync(path.join(DEFAULT_REPORT_DIR, 'workflow-eval-llm-debug.html'))).toBe(true); + }); + }); + + describe('writeScenarioVerificationSnapshot', () => { + const CHECKLIST_RESULT: ChecklistResult = { + id: 1, + pass: true, + reasoning: 'digest arrived', + strategy: 'llm', + }; + + /** Collects warnings so a swallowed write error reads differently from a + * snapshot correctly written somewhere else. */ + function collectingLogger(): { logger: EvalLogger; warnings: string[] } { + const warnings: string[] = []; + const logger: EvalLogger = { + info: () => {}, + verbose: () => {}, + success: () => {}, + warn: (msg: string) => warnings.push(msg), + error: (msg: string) => warnings.push(msg), + isVerbose: false, + }; + return { logger, warnings }; + } + + async function writeSnapshot(testCaseName: string, outputDir?: string): Promise { + const { logger, warnings } = collectingLogger(); + await writeScenarioVerificationSnapshot({ + testCaseName, + scenarioName: 'happy path', + workflowId: 'wf-1', + passed: true, + result: CHECKLIST_RESULT, + verificationResults: [CHECKLIST_RESULT], + verifierAttempts: [], + logger, + outputDir, + }); + expect(warnings).toEqual([]); + return warnings; + } + + it('writes the snapshot into the given outputDir', async () => { + const outputDir = freshOutputDir(); + + await writeSnapshot('daily digest', outputDir); + + const names = readdirSync(outputDir); + expect(names).toHaveLength(1); + expect(names[0]).toMatch(/^daily-digest_happy-path_.*\.json$/); + }); + + it('creates the outputDir when it does not exist yet', async () => { + const outputDir = path.join(freshOutputDir(), 'nested', 'run-3'); + + await writeSnapshot('daily digest', outputDir); + + expect(readdirSync(outputDir)).toHaveLength(1); + }); + + it('falls back to the package .data directory when no outputDir is given', async () => { + const caseName = `fallback case ${String(Date.now())}`; + + await writeSnapshot(caseName); + + const slug = caseName.replace(/[^a-z0-9]+/gi, '-').toLowerCase(); + expect(readdirSync(DEFAULT_REPORT_DIR).some((n) => n.startsWith(`${slug}_`))).toBe(true); + }); + }); + + it('gives concurrent runs their own copy of the stable-named reports', () => { + const dirA = freshOutputDir(); + const dirB = freshOutputDir(); + + writeWorkflowReport(RESULTS, dirA); + writeRunDebugReport(RESULTS, dirA); + writeWorkflowReport(RESULTS, dirB); + writeRunDebugReport(RESULTS, dirB); + + for (const dir of [dirA, dirB]) { + const names = readdirSync(dir); + expect(names).toContain('workflow-eval-report.html'); + expect(names).toContain('workflow-eval-llm-debug.html'); + } + }); +}); diff --git a/packages/@n8n/instance-ai/evaluations/__tests__/eval-results-dispatcher-contract.test.ts b/packages/@n8n/instance-ai/evaluations/__tests__/eval-results-dispatcher-contract.test.ts index 14119182e89..8ca7c34b314 100644 --- a/packages/@n8n/instance-ai/evaluations/__tests__/eval-results-dispatcher-contract.test.ts +++ b/packages/@n8n/instance-ai/evaluations/__tests__/eval-results-dispatcher-contract.test.ts @@ -7,7 +7,12 @@ import { join } from 'path'; import type { CheckOutcome } from '../binaryChecks/types'; import { aggregateResults } from '../run/aggregator'; import { writeEvalResults } from '../run/persist'; -import type { ExecutionScenario, WorkflowTestCase, WorkflowTestCaseResult } from '../types'; +import type { + ExecutionScenario, + TranscriptTurn, + WorkflowTestCase, + WorkflowTestCaseResult, +} from '../types'; // Pins the `eval-results.json` fields the lang-tracer dispatcher ingests // (lang-tracer-dispatcher `src/lib/runner.ts`): it spawns this CLI per case in @@ -39,10 +44,26 @@ const passingCheck: CheckOutcome = { status: 'pass', }; +const transcript: TranscriptTurn[] = [ + { + userMessage: 'send me a daily digest', + steps: [ + { kind: 'agent-text', text: 'Building the digest workflow.' }, + { + kind: 'tool-call', + toolName: 'add-nodes', + args: { nodeType: 'n8n-nodes-base.scheduleTrigger' }, + result: { added: true }, + }, + ], + }, +]; + function iteration1(): WorkflowTestCaseResult { return { testCase, workflowBuildSuccess: true, + transcript, workflowChecks: [passingCheck], workflowJson: { id: 'wf-1', @@ -63,6 +84,7 @@ function iteration2(): WorkflowTestCaseResult { return { testCase, workflowBuildSuccess: true, + buildError: 'agent stopped before producing a workflow', buildExpectationResults: [ { expectation: 'sends a digest', pass: false, reason: 'digest node missing' }, ], @@ -99,6 +121,8 @@ interface DispatcherView { }> | null>; buildCostUsdPerRun?: Array; buildTurnsPerRun?: Array; + transcriptPerRun: Array; + buildErrorPerRun: Array; scenarios: Array<{ name: string; passCount: number; @@ -164,6 +188,24 @@ describe('eval-results.json — dispatcher contract', () => { expect(tc).not.toHaveProperty('buildCostUsdPerRun'); expect(tc).not.toHaveProperty('buildTurnsPerRun'); + // Per-iteration conversation transcript — one entry per run, null when + // the iteration captured none. A present transcript keeps the full + // step detail (tool calls with args + results) the dispatcher renders. + expect(tc.transcriptPerRun).toHaveLength(2); + expect(tc.transcriptPerRun[1]).toBeNull(); + const turn = tc.transcriptPerRun[0]?.[0]; + expect(turn?.userMessage).toBe('send me a daily digest'); + expect(turn?.steps[0]).toEqual({ kind: 'agent-text', text: 'Building the digest workflow.' }); + expect(turn?.steps[1]).toEqual({ + kind: 'tool-call', + toolName: 'add-nodes', + args: { nodeType: 'n8n-nodes-base.scheduleTrigger' }, + result: { added: true }, + }); + + // Per-iteration build-failure reason — one `string | null` per run. + expect(tc.buildErrorPerRun).toEqual([null, 'agent stopped before producing a workflow']); + // Scenario blocks serialize under the flat `scenarios` key with a flat // `name` — the shape the dispatcher's fallback reader consumes today. expect(tc.scenarios).toHaveLength(1); diff --git a/packages/@n8n/instance-ai/evaluations/__tests__/fixtures/eval-results.partial-abort.json b/packages/@n8n/instance-ai/evaluations/__tests__/fixtures/eval-results.partial-abort.json index ec5d36cf3e7..61641847a0b 100644 --- a/packages/@n8n/instance-ai/evaluations/__tests__/fixtures/eval-results.partial-abort.json +++ b/packages/@n8n/instance-ai/evaluations/__tests__/fixtures/eval-results.partial-abort.json @@ -38,7 +38,9 @@ } ] } - ] + ], + "transcriptPerRun": [null], + "buildErrorPerRun": [null] }, { "name": "build me something", @@ -69,7 +71,9 @@ } ] } - ] + ], + "transcriptPerRun": [null], + "buildErrorPerRun": ["TimeoutError: The operation was aborted due to timeout"] } ] } diff --git a/packages/@n8n/instance-ai/evaluations/__tests__/langsmith-seed.test.ts b/packages/@n8n/instance-ai/evaluations/__tests__/langsmith-seed.test.ts index d05dfbf8610..c61628738c9 100644 --- a/packages/@n8n/instance-ai/evaluations/__tests__/langsmith-seed.test.ts +++ b/packages/@n8n/instance-ai/evaluations/__tests__/langsmith-seed.test.ts @@ -106,7 +106,8 @@ describe('reconstructSeedFromThread', () => { // Only turn 1 is seeded — the pinned turn and everything after it are excluded. const userTexts = result.seed.messages .filter((m) => m.role === 'user') - .map((m) => (m.content as Array<{ text: string }>)[0].text); + // Block fields are `unknown` by design (the store owns block shapes). + .map((m) => (m.content?.[0] as { text: string } | undefined)?.text ?? ''); expect(userTexts).toEqual(['Build Otter Digest, daily 9am']); }); diff --git a/packages/@n8n/instance-ai/evaluations/__tests__/langtracer-provider.test.ts b/packages/@n8n/instance-ai/evaluations/__tests__/langtracer-provider.test.ts index 0c2e2087eb1..ad45a51edcd 100644 --- a/packages/@n8n/instance-ai/evaluations/__tests__/langtracer-provider.test.ts +++ b/packages/@n8n/instance-ai/evaluations/__tests__/langtracer-provider.test.ts @@ -29,6 +29,38 @@ describe('casesFromExportedFiles', () => { expect(cases[0].testCase.outcomeExpectations).toEqual(['has a trigger']); }); + it('keeps an inline conversationSeed on a suite-sourced case', () => { + const cases = casesFromExportedFiles( + { + 'repair-it.json': validCase({ + conversationSeed: { + messages: [ + { + id: 'm1', + type: 'llm', + role: 'user', + createdAt: '2026-06-29T09:00:00.000Z', + content: [{ type: 'text', text: 'build it' }], + }, + ], + workflows: [{ id: 'wKk3RmT9xQ2bVn7L', name: 'Batch loop', nodes: [], connections: {} }], + }, + }), + }, + { suite: 'demo' }, + ); + expect(cases[0].testCase.conversationSeed?.workflows[0].id).toBe('wKk3RmT9xQ2bVn7L'); + }); + + it('refuses a suite-sourced case that points at a seed FILE', () => { + expect(() => + casesFromExportedFiles( + { 'repair-it.json': validCase({ seedFile: 'repair-it.seed' }) }, + { suite: 'demo' }, + ), + ).toThrow(/seedFile/); + }); + it('aggregates validation errors and names the offending file', () => { expect(() => casesFromExportedFiles( diff --git a/packages/@n8n/instance-ai/evaluations/__tests__/langtracer-to-exported.test.ts b/packages/@n8n/instance-ai/evaluations/__tests__/langtracer-to-exported.test.ts index 9e150f69875..46b6346cf42 100644 --- a/packages/@n8n/instance-ai/evaluations/__tests__/langtracer-to-exported.test.ts +++ b/packages/@n8n/instance-ai/evaluations/__tests__/langtracer-to-exported.test.ts @@ -110,13 +110,34 @@ describe('unsupportedPushReason', () => { expect(unsupportedPushReason(diskCase())).toBeNull(); }); - it('flags seedThread, seedFile and priorConversation as unsupported', () => { + it('flags seedThread and priorConversation as unsupported', () => { expect(unsupportedPushReason(diskCase({ seedThread: { threadId: 't' } }))).toMatch( /seedThread/, ); - expect(unsupportedPushReason(diskCase({ seedFile: '/tmp/seed.json' }))).toMatch(/seedFile/); expect( unsupportedPushReason(diskCase({ priorConversation: [{ role: 'user', text: 'earlier' }] })), ).toMatch(/priorConversation/); }); + + it('flags an inline conversationSeed as unsupported', () => { + expect( + unsupportedPushReason( + diskCase({ + conversationSeed: { + messages: [ + { + id: 'm1', + type: 'llm', + role: 'user', + createdAt: '2026-06-29T09:00:00.000Z', + content: [{ type: 'text', text: 'build it' }], + }, + ], + workflows: [], + dataTables: [], + }, + }), + ), + ).toMatch(/conversationSeed/); + }); }); diff --git a/packages/@n8n/instance-ai/evaluations/__tests__/mcp-builder.test.ts b/packages/@n8n/instance-ai/evaluations/__tests__/mcp-builder.test.ts index acf4d23abd4..9e4b4a19dec 100644 --- a/packages/@n8n/instance-ai/evaluations/__tests__/mcp-builder.test.ts +++ b/packages/@n8n/instance-ai/evaluations/__tests__/mcp-builder.test.ts @@ -135,7 +135,24 @@ describe('unsupportedMcpBuildSetupFields', () => { it.each<[string, Partial]>([ ['credentials', { credentials: [{ type: 'slackApi' }] }], - ['seedFile', { seedFile: 'seeds/some-thread.seed.json' }], + [ + 'conversationSeed', + { + conversationSeed: { + messages: [ + { + id: 'm1', + type: 'llm', + role: 'user', + createdAt: '2026-06-29T09:00:00.000Z', + content: [{ type: 'text', text: 'build it' }], + }, + ], + workflows: [], + dataTables: [], + }, + }, + ], ['priorConversation', { priorConversation: [user('We already agreed on #alerts')] }], ['seedThread', { seedThread: { threadId: 't1' } }], ])('flags %s', (field, overrides) => { diff --git a/packages/@n8n/instance-ai/evaluations/__tests__/redact.test.ts b/packages/@n8n/instance-ai/evaluations/__tests__/redact.test.ts index f4d8a96ca14..86447f0e380 100644 --- a/packages/@n8n/instance-ai/evaluations/__tests__/redact.test.ts +++ b/packages/@n8n/instance-ai/evaluations/__tests__/redact.test.ts @@ -1,4 +1,10 @@ -import { redactSecrets, redactSecretsInText, stringifyError, truncate } from '../harness/redact'; +import { + redactSecrets, + redactSecretsInText, + redactSecretsInTextDeep, + stringifyError, + truncate, +} from '../harness/redact'; describe('redactSecrets', () => { it('redacts values under secret-shaped keys', () => { @@ -104,6 +110,38 @@ describe('redactSecretsInText', () => { expect(redactSecretsInText('password: hunter2')).toBe('password: [REDACTED]'); }); + it('masks bare well-known credential formats with no surrounding key', () => { + // OpenAI/Anthropic-style + expect(redactSecretsInText('the run used sk-abc123DEF456ghi789jkl012 for calls')).toBe( + 'the run used [REDACTED] for calls', + ); + expect(redactSecretsInText('key sk-ant-api03-Zm9vYmFyYmF6cXV4 set')).toBe('key [REDACTED] set'); + // Slack bot/user/app tokens + expect(redactSecretsInText('posted with xoxb-1234567890-abcdefghijkl')).toBe( + 'posted with [REDACTED]', + ); + // Slack app-level tokens use the xapp- prefix, not xox?- + expect(redactSecretsInText('socket mode via xapp-1-A012-3456-abcdef')).toBe( + 'socket mode via [REDACTED]', + ); + // GitHub tokens (classic + fine-grained prefixes) + expect(redactSecretsInText('cloned using ghp_ABCdef123456789012345678901234567890')).toBe( + 'cloned using [REDACTED]', + ); + // AWS access key id + expect(redactSecretsInText('signed as AKIAIOSFODNN7EXAMPLE today')).toBe( + 'signed as [REDACTED] today', + ); + }); + + it('leaves lookalike prose untouched (short or non-token shapes)', () => { + expect(redactSecretsInText('we use sk-learn for clustering')).toBe( + 'we use sk-learn for clustering', + ); + expect(redactSecretsInText('the xoxo sign-off stays')).toBe('the xoxo sign-off stays'); + expect(redactSecretsInText('AKIA is an AWS prefix')).toBe('AKIA is an AWS prefix'); + }); + it('leaves secret words used as prose untouched (no separator → no match)', () => { expect(redactSecretsInText('Invalid token format in the request')).toBe( 'Invalid token format in the request', @@ -114,6 +152,57 @@ describe('redactSecretsInText', () => { }); }); +describe('redactSecretsInTextDeep', () => { + it('scrubs inline tokens in string leaves of nested objects and arrays', () => { + const input = { + log: 'Request failed: Authorization: Bearer sk-abc.def (401)', + nested: { note: 'retry with api_key=sk-inline-1' }, + list: ['header was Bearer eyJ0eXAi.payload', 'plain entry'], + }; + + expect(redactSecretsInTextDeep(input)).toEqual({ + log: 'Request failed: Authorization: Bearer [REDACTED] (401)', + nested: { note: 'retry with api_key=[REDACTED]' }, + list: ['header was Bearer [REDACTED]', 'plain entry'], + }); + }); + + it('passes non-string primitives, null, and undefined through unchanged', () => { + expect(redactSecretsInTextDeep(42)).toBe(42); + expect(redactSecretsInTextDeep(true)).toBe(true); + expect(redactSecretsInTextDeep(null)).toBeNull(); + expect(redactSecretsInTextDeep(undefined)).toBeUndefined(); + }); + + it('leaves benign strings untouched', () => { + expect(redactSecretsInTextDeep({ msg: 'Invalid token format in the request' })).toEqual({ + msg: 'Invalid token format in the request', + }); + }); + + it('does not mutate the original object', () => { + const original = { note: 'api_key=sk-real' }; + redactSecretsInTextDeep(original); + expect(original.note).toBe('api_key=sk-real'); + }); + + it('caps recursion depth so deeply nested input cannot blow the stack', () => { + let nested: unknown = { note: 'api_key=sk-leaf' }; + for (let i = 0; i < 12; i += 1) { + nested = { wrap: nested }; + } + expect(() => redactSecretsInTextDeep(nested)).not.toThrow(); + }); + + it('leaves class instances untouched (only walks plain objects)', () => { + class WithText { + constructor(public note: string) {} + } + const instance = new WithText('api_key=sk-keep'); + expect(redactSecretsInTextDeep(instance)).toBe(instance); + }); +}); + describe('truncate', () => { it('passes short values through after redaction', () => { expect(truncate({ name: 'a', token: 't' }, 200)).toEqual({ name: 'a', token: '[REDACTED]' }); diff --git a/packages/@n8n/instance-ai/evaluations/__tests__/transcript-from-events.test.ts b/packages/@n8n/instance-ai/evaluations/__tests__/transcript-from-events.test.ts index 4ccf4f77cc2..f71b78ba963 100644 --- a/packages/@n8n/instance-ai/evaluations/__tests__/transcript-from-events.test.ts +++ b/packages/@n8n/instance-ai/evaluations/__tests__/transcript-from-events.test.ts @@ -1,5 +1,6 @@ import { buildTranscriptFromEvents } from '../outcome/transcript-from-events'; import type { CapturedEvent } from '../types'; +import { USER_TURN_EVENT } from '../types'; function evt(type: string, data: Record = {}): CapturedEvent { return { timestamp: 0, type, data }; @@ -91,6 +92,95 @@ describe('buildTranscriptFromEvents', () => { }); }); + it('scrubs an inline token in agent narration', () => { + const turns = buildTranscriptFromEvents({ + events: [ + RUN_START, + evt('text-delta', { text: 'Calling the API with api_key=sk-narrated-123 now.' }), + ], + }); + const step = turns[0].steps[0]; + expect(step.kind).toBe('agent-text'); + const text = step.kind === 'agent-text' ? step.text : ''; + expect(text).not.toContain('sk-narrated-123'); + expect(text).toContain('api_key=[REDACTED]'); + expect(text).toContain('Calling the API'); + }); + + it('scrubs an inline token in the user turn message', () => { + const turns = buildTranscriptFromEvents({ + events: [ + evt(USER_TURN_EVENT, { payload: { text: 'use token=abc-user-999 for the request' } }), + RUN_START, + evt('text-delta', { text: 'ok' }), + ], + }); + expect(turns[0].userMessage).not.toContain('abc-user-999'); + expect(turns[0].userMessage).toContain('token=[REDACTED]'); + expect(turns[0].userMessage).toContain('for the request'); + }); + + it('scrubs an inline token in fallback (no-marker) opening and follow-up messages', () => { + const turns = buildTranscriptFromEvents({ + events: [ + RUN_START, + evt('text-delta', { text: 'first' }), + RUN_START, + evt('text-delta', { text: 'second' }), + ], + openingMessage: 'use token=abc-open-111 to authenticate', + followUpMessages: ['and api_key=abc-follow-222 for the sync'], + }); + expect(turns).toHaveLength(2); + expect(turns[0].userMessage).not.toContain('abc-open-111'); + expect(turns[0].userMessage).toContain('token=[REDACTED]'); + expect(turns[1].userMessage).not.toContain('abc-follow-222'); + expect(turns[1].userMessage).toContain('api_key=[REDACTED]'); + }); + + it('scrubs an inline token inside a string value of tool-call args', () => { + const turns = buildTranscriptFromEvents({ + events: [ + RUN_START, + evt('tool-call', { + payload: { + toolName: 'httpRequest', + toolCallId: 'tc-args', + args: { note: 'send with Authorization: Bearer sk-args-leak' }, + }, + }), + ], + }); + const step = turns[0].steps[0]; + expect(step.kind).toBe('tool-call'); + const args = step.kind === 'tool-call' ? (step.args ?? {}) : {}; + expect(JSON.stringify(args)).not.toContain('sk-args-leak'); + expect(args.note).toContain('Bearer [REDACTED]'); + }); + + it('scrubs an inline token inside a string value of a paired tool-result', () => { + const turns = buildTranscriptFromEvents({ + events: [ + RUN_START, + evt('tool-call', { + payload: { toolName: 'httpRequest', toolCallId: 'tc-res', args: { url: 'x' } }, + }), + evt('tool-result', { + payload: { + toolName: 'httpRequest', + toolCallId: 'tc-res', + result: { log: 'upstream said api_key=sk-result-leak was invalid' }, + }, + }), + ], + }); + const step = turns[0].steps[0]; + expect(step.kind).toBe('tool-call'); + const result = step.kind === 'tool-call' ? step.result : undefined; + expect(JSON.stringify(result)).not.toContain('sk-result-leak'); + expect(JSON.stringify(result)).toContain('api_key=[REDACTED]'); + }); + it('scrubs a secret embedded in a paired tool-error string', () => { const turns = buildTranscriptFromEvents({ events: [ diff --git a/packages/@n8n/instance-ai/evaluations/__tests__/user-proxy.test.ts b/packages/@n8n/instance-ai/evaluations/__tests__/user-proxy.test.ts index 038b08503f7..9ea93164bef 100644 --- a/packages/@n8n/instance-ai/evaluations/__tests__/user-proxy.test.ts +++ b/packages/@n8n/instance-ai/evaluations/__tests__/user-proxy.test.ts @@ -160,6 +160,25 @@ function domainAccessEvent(requestId: string): CapturedEvent { }; } +function webSearchEvent(requestId: string): CapturedEvent { + return { + timestamp: 100, + type: 'confirmation-request', + data: { + type: 'confirmation-request', + payload: { + requestId, + toolCallId: 'tc-y', + toolName: 'research', + args: {}, + severity: 'info', + message: 'n8n AI wants to search the web for: stripe webhook signing', + webSearch: { query: 'stripe webhook signing' }, + }, + }, + }; +} + function resourceDecisionEvent(requestId: string, options: string[]): CapturedEvent { return { timestamp: 100, @@ -526,6 +545,57 @@ describe('UserProxyLlm.respondToConfirmation', () => { expect(agent.callCount).toBe(0); }); + it('handles web-search events deterministically with allow_all', async () => { + const agent = new FakeAgent(); + const proxy = new UserProxyLlm({ + conversation: [{ role: 'user', text: 'go' }], + agent, + }); + + const response = await proxy.respondToConfirmation(webSearchEvent('req-search')); + expect(response.kind).toBe('domainAccessApprove'); + if (response.kind === 'domainAccessApprove') { + expect(response.domainAccessAction).toBe('allow_all'); + } + expect(agent.callCount).toBe(0); + }); + + it('defers an access gate to the LLM while a stage direction is pending, so a case can deny', async () => { + const agent = new FakeAgent(); + agent.enqueue({ action: 'respond_to_domain_access', response: 'deny' }); + const proxy = new UserProxyLlm({ + conversation: [ + { role: 'user', text: 'go' }, + { + role: 'user', + text: '[Refuse the web-search request — the user does not want it searching.]', + }, + ], + agent, + }); + + const response = await proxy.respondToConfirmation(webSearchEvent('req-deny')); + + expect(agent.callCount).toBe(1); + expect(response.kind).toBe('domainAccessDeny'); + }); + + it('still grants an access gate deterministically when the pending script has no stage direction', async () => { + const agent = new FakeAgent(); + const proxy = new UserProxyLlm({ + conversation: [ + { role: 'user', text: 'go' }, + { role: 'user', text: 'also add a retry' }, + ], + agent, + }); + + const response = await proxy.respondToConfirmation(webSearchEvent('req-allow')); + + expect(response.kind).toBe('domainAccessApprove'); + expect(agent.callCount).toBe(0); + }); + it('handles resource-decision events deterministically with first allow option', async () => { const agent = new FakeAgent(); const proxy = new UserProxyLlm({ diff --git a/packages/@n8n/instance-ai/evaluations/binaryChecks/checks/create-llm-check.test.ts b/packages/@n8n/instance-ai/evaluations/binaryChecks/checks/create-llm-check.test.ts index 6b0afbc35cb..49716d7e5b7 100644 --- a/packages/@n8n/instance-ai/evaluations/binaryChecks/checks/create-llm-check.test.ts +++ b/packages/@n8n/instance-ai/evaluations/binaryChecks/checks/create-llm-check.test.ts @@ -36,4 +36,30 @@ describe('createLlmCheck', () => { comment: 'LLM check "slow_check" timed out after 1ms', }); }); + + it('marks an unparseable verdict as errored, not failed', async () => { + // Observed live: the judge answered with a markdown analysis that ticked the + // requirements off as satisfied, but carried no parseable verdict. Scoring + // that as a failure reports a defect in the workflow that isn't there. + mocks.generate.mockResolvedValue({ + text: '## Analysis\n**Requirement 1** — satisfied ✓\n**Requirement 2** — satisfied ✓', + }); + + const check = createLlmCheck({ + name: 'chatty_check', + description: 'chatty check', + dimension: 'intent_match', + systemPrompt: 'Judge the workflow.', + humanTemplate: 'Workflow: {generatedWorkflow}', + }); + + const result = await check.run({} as WorkflowResponse, { + prompt: 'Build a workflow', + modelId: 'anthropic/test', + }); + + expect(result.pass).toBe(false); + expect(result.errored).toBe(true); + expect(result.comment).toContain('Failed to parse LLM response'); + }); }); diff --git a/packages/@n8n/instance-ai/evaluations/binaryChecks/checks/create-llm-check.ts b/packages/@n8n/instance-ai/evaluations/binaryChecks/checks/create-llm-check.ts index bc4ba5d85ff..ace12c307b7 100644 --- a/packages/@n8n/instance-ai/evaluations/binaryChecks/checks/create-llm-check.ts +++ b/packages/@n8n/instance-ai/evaluations/binaryChecks/checks/create-llm-check.ts @@ -116,8 +116,13 @@ export function createLlmCheck(options: LlmCheckOptions): BinaryCheck { const parsed = parseJudgeVerdict(text); if (!parsed) { + // Same class as a timeout above: the judge never returned a verdict, so + // there is nothing to score. Without `errored` this counts as a real + // failure and reads as a defect in the workflow — the raw text is often + // a markdown analysis that AGREED with the workflow. return { pass: false, + errored: true, comment: `Failed to parse LLM response. Raw (first 500 chars): ${text.slice(0, 500)}`, }; } diff --git a/packages/@n8n/instance-ai/evaluations/checklist/verifier.ts b/packages/@n8n/instance-ai/evaluations/checklist/verifier.ts index 71723e16e0d..edf58839959 100644 --- a/packages/@n8n/instance-ai/evaluations/checklist/verifier.ts +++ b/packages/@n8n/instance-ai/evaluations/checklist/verifier.ts @@ -1,4 +1,5 @@ import type { Message, StreamChunk } from '@n8n/agents'; +import { sleep } from '@n8n/utils/sleep'; import { z } from 'zod'; import { @@ -41,10 +42,6 @@ function jitteredPauseMs(attempt: number): number { return 1_000 * attempt + Math.random() * 1_000; } -async function sleep(ms: number): Promise { - await new Promise((resolve) => setTimeout(resolve, ms)); -} - export interface VerifierAttemptDebug { attempt: number; status: 'threw' | 'model_error' | 'no_parseable_results' | 'success'; diff --git a/packages/@n8n/instance-ai/evaluations/cli/index.ts b/packages/@n8n/instance-ai/evaluations/cli/index.ts index efa3b600892..6c6725fedda 100644 --- a/packages/@n8n/instance-ai/evaluations/cli/index.ts +++ b/packages/@n8n/instance-ai/evaluations/cli/index.ts @@ -124,6 +124,7 @@ async function main(): Promise { gate, slugByTestCase, commitSha, + outputDir: args.outputDir, jsonPath, prCommentPath, experimentName: args.experimentName, diff --git a/packages/@n8n/instance-ai/evaluations/cli/mcp-builder.ts b/packages/@n8n/instance-ai/evaluations/cli/mcp-builder.ts index f8a5cbe5f22..22551ca93d7 100644 --- a/packages/@n8n/instance-ai/evaluations/cli/mcp-builder.ts +++ b/packages/@n8n/instance-ai/evaluations/cli/mcp-builder.ts @@ -166,7 +166,7 @@ export const MCP_BUILD_KEY_SUPPORT: Record< // reach this check; classified only to keep the map schema-complete. buildExpectations: 'supported', credentials: 'orchestrator-only', - seedFile: 'orchestrator-only', + conversationSeed: 'orchestrator-only', priorConversation: 'orchestrator-only', seedThread: 'orchestrator-only', datasets: 'supported', diff --git a/packages/@n8n/instance-ai/evaluations/credentials/seeder.ts b/packages/@n8n/instance-ai/evaluations/credentials/seeder.ts index d72730b7151..5df947ffa94 100644 --- a/packages/@n8n/instance-ai/evaluations/credentials/seeder.ts +++ b/packages/@n8n/instance-ai/evaluations/credentials/seeder.ts @@ -44,6 +44,11 @@ const CREDENTIAL_TEMPLATES: Record = { envVar: 'EVAL_GMAIL_ACCESS_TOKEN', buildData: (token) => ({ oauthTokenData: { access_token: token } }), }, + googleDriveOAuth2Api: { + defaultName: '[eval] Google Drive', + envVar: 'EVAL_GOOGLE_DRIVE_ACCESS_TOKEN', + buildData: (token) => ({ oauthTokenData: { access_token: token } }), + }, // MCP-registry-synthesized credential types (agent MCP servers). Creating // them requires the backend to run with the `mcp-registry` module enabled; // placeholder tokens are fine — agent eval runs mock the MCP wire. diff --git a/packages/@n8n/instance-ai/evaluations/data/discovery/workflow-builder-no-credential-ask.json b/packages/@n8n/instance-ai/evaluations/data/discovery/workflow-builder-no-credential-ask.json index 127ca64414f..5bbbd2cf793 100644 --- a/packages/@n8n/instance-ai/evaluations/data/discovery/workflow-builder-no-credential-ask.json +++ b/packages/@n8n/instance-ai/evaluations/data/discovery/workflow-builder-no-credential-ask.json @@ -24,5 +24,5 @@ ] }, "rationale": "Regression coverage for INS-204. A clear single-workflow build should route through the workflow-builder skill directly, may use credentials(action=\"list\") for discovery, and must not ask the user which credential/account to use when the builder can auto-select or mock unresolved credentials. It should also use contextual timezone data rather than asking for it.", - "maxSteps": 12 + "maxSteps": 24 } diff --git a/packages/@n8n/instance-ai/evaluations/harness/agent-execution.ts b/packages/@n8n/instance-ai/evaluations/harness/agent-execution.ts index 667b85e8623..455e54572c6 100644 --- a/packages/@n8n/instance-ai/evaluations/harness/agent-execution.ts +++ b/packages/@n8n/instance-ai/evaluations/harness/agent-execution.ts @@ -68,6 +68,7 @@ export async function executeAgentScenario( timeoutMs?: number, testCaseName?: string, buildTrace?: BuildTrace, + outputDir?: string, ): Promise { const execStart = Date.now(); const projectId = await client.getPersonalProjectId(); @@ -130,6 +131,7 @@ export async function executeAgentScenario( verifierAttempts: verification.attempts, buildTrace, logger, + outputDir, }); const incomplete = verificationResults.length === 0; const attemptErrors = verification.attempts diff --git a/packages/@n8n/instance-ai/evaluations/harness/build-workflow.ts b/packages/@n8n/instance-ai/evaluations/harness/build-workflow.ts index 73bad8cd4a7..a96c29001d5 100644 --- a/packages/@n8n/instance-ai/evaluations/harness/build-workflow.ts +++ b/packages/@n8n/instance-ai/evaluations/harness/build-workflow.ts @@ -21,7 +21,6 @@ import { } from './chat-loop'; import { runWorkflowChecks, summarizeMissingWorkflowError } from './cleanup'; import { - loadConversationSeed, remapSeedWorkflowIds, seedFromProse, transcriptPrefixFromSeed, @@ -192,8 +191,8 @@ export interface BuildWorkflowConfig { credentials?: TestCaseCredential[]; /** Run-level registry the created credential IDs are added to for cleanup. */ createdCredentialIds?: Set; - /** Synthetic seed file (path) restored before the live message. */ - seedFile?: string; + /** Prior messages + workflows restored before the live message. */ + conversationSeed?: ConversationSeed; /** Prose turns seeded as plain-text history. */ priorConversation?: ConversationTurn[]; /** Reproduce a real conversation from its LangSmith trace (seed = before the @@ -299,8 +298,8 @@ export async function buildWorkflow(config: BuildWorkflowConfig): Promise 0) { seed = seedFromProse(config.priorConversation); } diff --git a/packages/@n8n/instance-ai/evaluations/harness/conversation-seed.ts b/packages/@n8n/instance-ai/evaluations/harness/conversation-seed.ts index 03dedd19f11..020b43ec51c 100644 --- a/packages/@n8n/instance-ai/evaluations/harness/conversation-seed.ts +++ b/packages/@n8n/instance-ai/evaluations/harness/conversation-seed.ts @@ -1,12 +1,11 @@ -// Conversation seeding for eval builds — backs the `seedFile` (synthetic) and -// `priorConversation` (prose) paths. Real conversations use `seedThread` +// Conversation seeding for eval builds — backs the `conversationSeed` (synthetic) +// and `priorConversation` (prose) paths. Real conversations use `seedThread` // (reconstructed from a LangSmith trace; see langsmith-seed.ts). import { generateNanoId } from '@n8n/utils/generate-nano-id'; import { isRecord } from '@n8n/utils/is-record'; import { jsonParse } from 'n8n-workflow'; import { randomUUID } from 'node:crypto'; -import { readFileSync } from 'node:fs'; import { z } from 'zod'; import { DOMAIN_TOOL_IDS, ORCHESTRATION_TOOL_IDS } from '../../src/tools/tool-ids'; @@ -20,7 +19,7 @@ import { import type { ConversationTurn, TranscriptStep, TranscriptTurn } from '../types'; // --------------------------------------------------------------------------- -// Seed file schema +// Seed schema // --------------------------------------------------------------------------- const SeedWorkflowSchema = z.object({ @@ -44,11 +43,77 @@ const SeedDataTableSchema = z.object({ // of a trace and are kept out of the eval instance entirely. }); +/** A content block. Only `type` is ours to require — the block shapes belong to + * the agent's message store, so an unrecognised one is accepted and simply not + * interpreted. `.passthrough()` is load-bearing: `z.object` strips unknown keys, + * which would silently gut `toolCallId`/`input`/`output` from every tool call. */ +const SeedMessageBlockSchema = z + .object({ + type: z.string().min(1, 'a content block needs a non-empty `type`'), + }) + .passthrough(); + +/** A message envelope. Validated because a hand-authored seed is now the primary + * path, and a malformed message is stored verbatim AND skipped by + * `transcriptPrefixFromSeed` — so the case grades against a transcript that + * doesn't match what the agent actually saw. Envelope only; block internals are + * the store's contract, not ours. */ +const seedMessageObjectSchema = z + .object({ + id: z.string().min(1), + // Restricted to the two roles the transcript builder renders: any other + // value is guaranteed to vanish from the judge transcript, which is the + // exact silent failure this schema exists to catch. If the message store + // gains a role, the builder needs updating too — fail loudly then. + // Optional in the shape because a `custom` message carries no role; the + // refine below requires it for every message that is actually rendered. + role: z.enum(['user', 'assistant']).optional(), + /** The store's own discriminator (`llm`, `custom`, …) — not enumerated. */ + type: z.string().min(1), + /** Ordering before the live turn depends on this being a real timestamp. */ + createdAt: z + .string() + .min(1) + .refine((v) => !Number.isNaN(Date.parse(v)), { + message: 'must be a parseable timestamp (e.g. an ISO 8601 string)', + }), + content: z.array(SeedMessageBlockSchema).optional(), + }) + .passthrough(); + +/** Inferred from the pre-`superRefine` shape — identical type, but resolving the + * refined `ZodEffects` chain trips "type instantiation excessively deep" under + * CI's type-aware lint (same reason as `EvalTestCaseInput` in schema.ts). */ +export type SeedMessage = z.infer; + +const SeedMessageSchema = seedMessageObjectSchema.superRefine((message, ctx) => { + // `custom` messages are stored but never rendered (no role, any content + // shape). Everything else is read by the transcript builder, which needs a + // role it renders and an array of blocks. + if (message.type === 'custom') return; + if (message.role === undefined) { + ctx.addIssue({ + code: z.ZodIssueCode.custom, + path: ['role'], + message: + 'is required (only `type: custom` messages may omit it — they are stored but never rendered)', + }); + } + if (!Array.isArray(message.content)) { + ctx.addIssue({ + code: z.ZodIssueCode.custom, + path: ['content'], + message: + 'must be an array of content blocks (only `type: custom` messages may omit it — they are stored but never rendered)', + }); + } +}); + export const ConversationSeedSchema = z.object({ /** Provenance (thread id, instance, export time) — informational only. */ source: z.record(z.unknown()).optional(), /** Native agent message log (user/assistant turns with resolved tool-call blocks). */ - messages: z.array(z.record(z.unknown())).min(1), + messages: z.array(SeedMessageSchema).min(1), /** Workflows the history references, recreated on restore. */ workflows: z.array(SeedWorkflowSchema).default([]), /** Data tables the history references, recreated (and id-rewritten) on restore. */ @@ -57,25 +122,6 @@ export const ConversationSeedSchema = z.object({ export type ConversationSeed = z.infer; -export function loadConversationSeed(filePath: string): ConversationSeed { - let raw: unknown; - try { - raw = JSON.parse(readFileSync(filePath, 'utf-8')); - } catch (error) { - throw new Error( - `Failed to read conversation seed ${filePath}: ${error instanceof Error ? error.message : String(error)}`, - ); - } - const parsed = ConversationSeedSchema.safeParse(raw); - if (!parsed.success) { - const issues = parsed.error.issues - .map((i) => ` - ${i.path.join('.') || '(root)'}: ${i.message}`) - .join('\n'); - throw new Error(`Invalid conversation seed ${filePath}:\n${issues}`); - } - return parsed.data; -} - // --------------------------------------------------------------------------- // Prose prior turns → seed messages // --------------------------------------------------------------------------- diff --git a/packages/@n8n/instance-ai/evaluations/harness/langsmith-seed.ts b/packages/@n8n/instance-ai/evaluations/harness/langsmith-seed.ts index fcec5823499..3a168b6a6aa 100644 --- a/packages/@n8n/instance-ai/evaluations/harness/langsmith-seed.ts +++ b/packages/@n8n/instance-ai/evaluations/harness/langsmith-seed.ts @@ -8,7 +8,7 @@ import { isRecord } from '@n8n/utils/is-record'; import { Client } from 'langsmith'; import type { Run } from 'langsmith/schemas'; -import type { ConversationSeed } from './conversation-seed'; +import type { ConversationSeed, SeedMessage } from './conversation-seed'; import { parseSeedWorkflowCode } from './parse-seed-workflow'; import { COMPILED_WORKFLOW_TRACE_RUN_NAME, DOMAIN_TOOL_IDS } from '../../src/tools/tool-ids'; @@ -255,18 +255,21 @@ function redactDataTableRowPayload(value: unknown): Record { return out; } -interface TextBlock { +// Type aliases, not interfaces: seed content blocks are open by design +// (`.passthrough()`), and an interface has no index signature so it isn't +// assignable to that. Converting these back to interfaces breaks the build. +type TextBlock = { type: 'text'; text: string; -} -interface ToolCallBlock { +}; +type ToolCallBlock = { type: 'tool-call'; toolCallId: string; toolName: string; state: 'resolved'; input: unknown; output: unknown; -} +}; /** * Reconstruct a thread's seed + live turn. The workspace holding the thread is @@ -417,11 +420,7 @@ async function reconstructWithClient( } /** Rebuild the native message log for every run before the seed boundary. */ -function buildSeedMessages( - rootRuns: Run[], - toolRuns: Run[], - boundaryMs: number, -): Array> { +function buildSeedMessages(rootRuns: Run[], toolRuns: Run[], boundaryMs: number): SeedMessage[] { const toolsByRoot = new Map(); for (const tool of toolRuns) { const rootId = asString(metadata(tool).langsmith_root_run_id) ?? tool.trace_id ?? ''; @@ -431,7 +430,9 @@ function buildSeedMessages( } const emittedToolCallIds = new Set(); - const messages: Array> = []; + // Typed, so the compiler enforces the envelope on the machine-producer side + // while ConversationSeedSchema enforces it on hand-authored seeds. + const messages: SeedMessage[] = []; for (const root of rootRuns) { if (new Date(root.start_time ?? NaN).getTime() >= boundaryMs) break; diff --git a/packages/@n8n/instance-ai/evaluations/harness/redact.ts b/packages/@n8n/instance-ai/evaluations/harness/redact.ts index 6e9dd2fa568..77b23d779ca 100644 --- a/packages/@n8n/instance-ai/evaluations/harness/redact.ts +++ b/packages/@n8n/instance-ai/evaluations/harness/redact.ts @@ -63,6 +63,17 @@ const SECRET_TEXT_PATTERNS: ReadonlyArray = [ /\b(api[-_]?key|access[-_]?key|x-api-key|access[-_]?token|refresh[-_]?token|client[-_]?secret|private[-_]?key|secret|token|password|passwd|cookie|set-cookie|session[-_]?id)("?\s*[:=]\s*"?)[^\s"',&}]+/gi, '$1$2[REDACTED]', ], + // Bare well-known credential formats — recognizable with no key or scheme + // around them. Length floors keep prose lookalikes (sk-learn, xoxo, the + // AKIA prefix mentioned in text) unmatched. + // OpenAI/Anthropic-style: sk-…, sk-ant-…, sk-proj-…. + [/\bsk-(?:[a-z0-9]+-)*[A-Za-z0-9_-]{16,}\b/g, '[REDACTED]'], + // Slack tokens: xoxb-/xoxp-/xoxa-/xoxs-… plus app-level xapp-…. + [/\b(?:xox[a-z]|xapp)-[A-Za-z0-9-]{8,}\b/gi, '[REDACTED]'], + // GitHub tokens: ghp_/gho_/ghu_/ghs_/ghr_ + fine-grained github_pat_. + [/\b(?:gh[pousr]_|github_pat_)[A-Za-z0-9_]{20,}\b/g, '[REDACTED]'], + // AWS access key ids. + [/\bAKIA[0-9A-Z]{16}\b/g, '[REDACTED]'], ]; /** Mask secrets embedded inline in free text (e.g. a token in a tool-error string). */ @@ -73,6 +84,28 @@ export function redactSecretsInText(text: string): string { ); } +/** + * Content-based pass over a value tree: applies `redactSecretsInText` to every + * string leaf. Complements the key-based `redactSecrets` for payloads where a + * token sits inline in a value under a non-secret-shaped key. Same walk rules + * as `redactSecrets`: plain objects and arrays only, depth-capped. + */ +export function redactSecretsInTextDeep(value: unknown, depth = 0): unknown { + if (depth > 10 || value === null || value === undefined) return value; + if (typeof value === 'string') return redactSecretsInText(value); + if (Array.isArray(value)) { + return value.map((entry) => redactSecretsInTextDeep(entry, depth + 1)); + } + if (typeof value === 'object' && Object.getPrototypeOf(value) === Object.prototype) { + const out: Record = {}; + for (const [k, v] of Object.entries(value as Record)) { + out[k] = redactSecretsInTextDeep(v, depth + 1); + } + return out; + } + return value; +} + /** * Truncates serializable values to a max stringified length, redacting * secrets first. Returns the redacted value when it fits, the truncated diff --git a/packages/@n8n/instance-ai/evaluations/harness/scenario-execution.ts b/packages/@n8n/instance-ai/evaluations/harness/scenario-execution.ts index 1f2aa8a7357..8aaba0834cf 100644 --- a/packages/@n8n/instance-ai/evaluations/harness/scenario-execution.ts +++ b/packages/@n8n/instance-ai/evaluations/harness/scenario-execution.ts @@ -54,13 +54,17 @@ export async function writeScenarioVerificationSnapshot(input: { verifierAttempts: VerifierAttemptDebug[]; buildTrace?: BuildTrace; logger: EvalLogger; + /** --output-dir; falls back to EVAL_DATA_DIR. Concurrent eval children share + * one checkout, so each needs its snapshots under its own directory. */ + outputDir?: string; }): Promise { + const snapshotDir = input.outputDir ?? EVAL_DATA_DIR; const timestamp = makeArtifactTimestamp(); const fileName = `${slugifyArtifactSegment(input.testCaseName, 'workflow')}_${slugifyArtifactSegment(input.scenarioName, 'scenario')}_${timestamp}.json`; - const filePath = path.join(EVAL_DATA_DIR, fileName); + const filePath = path.join(snapshotDir, fileName); try { - await mkdir(EVAL_DATA_DIR, { recursive: true }); + await mkdir(snapshotDir, { recursive: true }); await writeFile( filePath, JSON.stringify( @@ -90,6 +94,9 @@ export async function writeScenarioVerificationSnapshot(input: { /** * Execute a single scenario against a pre-built workflow and verify the result. + * + * `outputDir` (--output-dir) is where the verification snapshot lands; omitting + * it keeps the historical EVAL_DATA_DIR location. */ export async function executeScenario( client: N8nClient, @@ -102,6 +109,7 @@ export async function executeScenario( buildTrace?: BuildTrace, pinAiRoots?: string[], seedContext?: ScenarioSeedContext, + outputDir?: string, ): Promise { return await runScenario( client, @@ -114,6 +122,7 @@ export async function executeScenario( buildTrace, pinAiRoots, seedContext, + outputDir, ); } @@ -248,6 +257,7 @@ async function runScenario( buildTrace?: BuildTrace, pinAiRoots?: string[], seedContext?: ScenarioSeedContext, + outputDir?: string, ): Promise { const pinNodes = pinAiRoots && pinAiRoots.length > 0 ? pinAiRoots : undefined; const targetWorkflowId = selectScenarioWorkflowId(scenario, workflowId, workflowJsons, logger); @@ -336,6 +346,7 @@ async function runScenario( verifierAttempts: verification.attempts, buildTrace, logger, + outputDir, }); // Empty verification = the verifier itself failed after all attempts. The run // is excluded from scoring (mirrors incomplete build expectations) but stays diff --git a/packages/@n8n/instance-ai/evaluations/harness/schema.ts b/packages/@n8n/instance-ai/evaluations/harness/schema.ts index 2ab5cae2425..409dd061802 100644 --- a/packages/@n8n/instance-ai/evaluations/harness/schema.ts +++ b/packages/@n8n/instance-ai/evaluations/harness/schema.ts @@ -1,6 +1,7 @@ import { instanceAiEvalSeedDataTableSchema } from '@n8n/api-types'; import { z } from 'zod'; +import { ConversationSeedSchema } from './conversation-seed'; import { SUPPORTED_CREDENTIAL_TYPES } from '../credentials/seeder'; /** Default `datasets` grouping for a case that omits the field — the single @@ -14,7 +15,15 @@ export const DEFAULT_DATASETS = ['full']; * (e.g. the mcp-manifest builder) normalize identically. */ export const conversationTurnTextSchema = z .union([z.string(), z.array(z.string())]) - .transform((t) => (Array.isArray(t) ? t.join('\n') : t)); + .transform((t) => (Array.isArray(t) ? t.join('\n') : t)) + // An unclosed `[` fails silently and expensively: the proxy stops seeing a + // stage direction, so it sends the text as dialogue and the case grades a + // conversation it was never meant to have. Easy to do in the array form, + // where the closing bracket lands on a different line from the opening one. + .refine((t) => !t.includes('[') || t.includes(']'), { + message: + 'unbalanced stage direction — text opens `[` but never closes it, so the proxy would send it as dialogue instead of treating it as a direction', + }); export const ConversationTurnSchema = z.object({ role: z.enum(['user', 'assistant']), @@ -86,9 +95,9 @@ const evalTestCaseObjectSchema = z }), ) .optional(), - /** Synthetic seed file (relative path), resolved + validated at case load. + /** Prior messages + the workflows they reference, restored before the live turn. * Synthetic fixtures only; real conversations use `seedThread`. */ - seedFile: z.string().min(1).optional(), + conversationSeed: ConversationSeedSchema.optional(), /** Prose turns seeded as plain-text history (no tool calls / workflows). */ priorConversation: z.array(ConversationTurnSchema).min(1).optional(), /** Reproduce a real conversation from its LangSmith trace at run time (seed = @@ -130,10 +139,13 @@ export const WORKFLOW_TEST_CASE_KEYS = Object.keys(evalTestCaseObjectSchema.shap // At most one seeding mode, and a source for the live turn. export const EvalTestCaseSchema = evalTestCaseObjectSchema - .refine((c) => [c.seedFile, c.priorConversation, c.seedThread].filter(Boolean).length <= 1, { - message: - 'seedFile, priorConversation and seedThread are mutually exclusive — pick one seeding mode', - }) + .refine( + (c) => [c.conversationSeed, c.priorConversation, c.seedThread].filter(Boolean).length <= 1, + { + message: + 'conversationSeed, priorConversation and seedThread are mutually exclusive — pick one seeding mode', + }, + ) .refine((c) => c.seedThread !== undefined || c.conversation !== undefined, { message: 'a case needs a conversation, or a seedThread (which supplies the live turn from the trace)', diff --git a/packages/@n8n/instance-ai/evaluations/langtracer/provider.ts b/packages/@n8n/instance-ai/evaluations/langtracer/provider.ts index 70abaec0550..a6e53241493 100644 --- a/packages/@n8n/instance-ai/evaluations/langtracer/provider.ts +++ b/packages/@n8n/instance-ai/evaluations/langtracer/provider.ts @@ -1,6 +1,8 @@ // lang-tracer test-case provider; `casesFromExportedFiles` is split from the network // call so the normalize + validate contract is unit-testable without a server. +import { isRecord } from '@n8n/utils/is-record'; + import { LangTracerClient, type ExportedSuite } from './client'; import { resolveLangTracerConfig } from './config'; import { normalizeExportedCase } from './normalize'; @@ -30,6 +32,14 @@ export function casesFromExportedFiles( for (const [filename, raw] of Object.entries(files)) { const fileSlug = filename.replace(/\.json$/i, ''); + // Checked on the RAW body: the normalizer whitelists to the schema's keys, so a + // leftover `seedFile` would be stripped and the case would run unseeded. + if (isRecord(raw) && raw.seedFile !== undefined) { + errors.push( + `${filename}:\n - seedFile: no longer supported — carry the seed in the case body as \`conversationSeed\``, + ); + continue; + } const parsed = EvalTestCaseSchema.safeParse(normalizeExportedCase(raw)); if (!parsed.success) { const issues = parsed.error.issues diff --git a/packages/@n8n/instance-ai/evaluations/langtracer/to-exported.ts b/packages/@n8n/instance-ai/evaluations/langtracer/to-exported.ts index 56fd768e94d..f21f93fdd70 100644 --- a/packages/@n8n/instance-ai/evaluations/langtracer/to-exported.ts +++ b/packages/@n8n/instance-ai/evaluations/langtracer/to-exported.ts @@ -43,7 +43,9 @@ export interface ToLangTracerOptions { * using any of them can't be pushed. Returns a human-readable reason, else null. */ export function unsupportedPushReason(testCase: EvalTestCaseInput): string | null { if (testCase.seedThread) return 'uses seedThread (not supported by the case-write API)'; - if (testCase.seedFile) return 'uses seedFile (not supported by the case-write API)'; + if (testCase.conversationSeed) { + return 'uses conversationSeed (not supported by the case-write API)'; + } if (testCase.priorConversation) { return 'uses priorConversation (not supported by the case-write API)'; } diff --git a/packages/@n8n/instance-ai/evaluations/outcome/event-parser.ts b/packages/@n8n/instance-ai/evaluations/outcome/event-parser.ts index 1c5b5b7debb..c498adbb9f8 100644 --- a/packages/@n8n/instance-ai/evaluations/outcome/event-parser.ts +++ b/packages/@n8n/instance-ai/evaluations/outcome/event-parser.ts @@ -4,7 +4,11 @@ import { isRecord } from '@n8n/utils/is-record'; -import { DATA_TABLES_TOOL_ID, DOMAIN_TOOL_IDS, EVAL_CONFIG_TOOL_ID } from '../../src/tools/tool-ids'; +import { + DATA_TABLES_TOOL_ID, + DOMAIN_TOOL_IDS, + EVAL_CONFIG_TOOL_ID, +} from '../../src/tools/tool-ids'; import type { AgentActivity, ArtifactRef, diff --git a/packages/@n8n/instance-ai/evaluations/outcome/transcript-from-events.ts b/packages/@n8n/instance-ai/evaluations/outcome/transcript-from-events.ts index a28b87c898d..ce4e6f834c9 100644 --- a/packages/@n8n/instance-ai/evaluations/outcome/transcript-from-events.ts +++ b/packages/@n8n/instance-ai/evaluations/outcome/transcript-from-events.ts @@ -9,7 +9,7 @@ import type { InstanceAiConfirmRequest } from '@n8n/api-types'; import { isRecord } from '@n8n/utils/is-record'; -import { redactSecrets, redactSecretsInText } from '../harness/redact'; +import { redactSecrets, redactSecretsInText, redactSecretsInTextDeep } from '../harness/redact'; import type { AskUserAnswer, AskUserQuestion, @@ -87,6 +87,8 @@ function splitEventsByUserTurn( function extractUserTurnText(event: CapturedEvent): string | undefined { const payload = getRecord(event.data, 'payload'); + // Raw here — buildTurn content-scrubs every user message at the shared + // boundary, covering this path and the legacy fallback alike. return payload ? getString(payload, 'text') : undefined; } @@ -111,7 +113,9 @@ function buildTurn( const flushText = () => { if (textBuffer.length > 0) { - steps.push({ kind: 'agent-text', text: textBuffer }); + // Agent narration can echo a token verbatim (e.g. from a tool result) — + // content-scrub before the text lands in the persisted transcript. + steps.push({ kind: 'agent-text', text: redactSecretsInText(textBuffer) }); textBuffer = ''; } }; @@ -144,7 +148,14 @@ function buildTurn( } flushText(); - return { userMessage, steps }; + // The transcript leaves the machine via eval-results.json — content-scrub + // the user message HERE, the boundary both capture paths share (the + // marker path's extracted turn text AND the legacy fallback's raw + // opening/follow-up messages). + return { + userMessage: userMessage === undefined ? undefined : redactSecretsInText(userMessage), + steps, + }; } interface ToolOutcome { @@ -164,8 +175,9 @@ function collectToolOutcomes(events: CapturedEvent[]): Map // Flat string, so content-scrub (key-based redaction can't reach an inline token). map.set(callId, { error: redactSecretsInText(getString(payload, 'error') ?? 'tool error') }); } else { - // Redact secret-shaped keys before the result reaches the report/judge. - map.set(callId, { result: redactSecrets(payload.result) }); + // Redact secret-shaped keys, then content-scrub string leaves — a token + // inlined in a value under a benign key survives the key-based pass. + map.set(callId, { result: redactSecretsInTextDeep(redactSecrets(payload.result)) }); } } return map; @@ -200,7 +212,10 @@ function interpretToolCall( toolName, toolCallId: callId, args: - Object.keys(args).length > 0 ? (redactSecrets(args) as Record) : undefined, + Object.keys(args).length > 0 + ? // Key-based pass, then a content pass over string leaves (see collectToolOutcomes). + (redactSecretsInTextDeep(redactSecrets(args)) as Record) + : undefined, result, error: outcome?.error, }; diff --git a/packages/@n8n/instance-ai/evaluations/report/run-debug-report.ts b/packages/@n8n/instance-ai/evaluations/report/run-debug-report.ts index b339bb6f836..88327996917 100644 --- a/packages/@n8n/instance-ai/evaluations/report/run-debug-report.ts +++ b/packages/@n8n/instance-ai/evaluations/report/run-debug-report.ts @@ -415,8 +415,12 @@ export function generateRunDebugReport(results: WorkflowTestCaseResult[]): strin `; } -export function writeRunDebugReport(results: WorkflowTestCaseResult[]): string { - const reportDir = path.join(__dirname, '..', '..', '.data'); +/** + * Write the LLM debug report into `outputDir` (--output-dir), falling back to + * the package-level `.data` directory — same contract as writeWorkflowReport. + */ +export function writeRunDebugReport(results: WorkflowTestCaseResult[], outputDir?: string): string { + const reportDir = outputDir ?? path.join(__dirname, '..', '..', '.data'); if (!fs.existsSync(reportDir)) { fs.mkdirSync(reportDir, { recursive: true }); } diff --git a/packages/@n8n/instance-ai/evaluations/report/workflow-report.ts b/packages/@n8n/instance-ai/evaluations/report/workflow-report.ts index 4e6dca414ca..4a180d1f97f 100644 --- a/packages/@n8n/instance-ai/evaluations/report/workflow-report.ts +++ b/packages/@n8n/instance-ai/evaluations/report/workflow-report.ts @@ -1806,8 +1806,14 @@ ${results.map((r, i) => renderTestCase(r, i)).join('')} // Write report to disk // --------------------------------------------------------------------------- -export function writeWorkflowReport(results: WorkflowTestCaseResult[]): string { - const reportDir = path.join(__dirname, '..', '..', '.data'); +/** + * Write the HTML report into `outputDir` (--output-dir), falling back to the + * package-level `.data` directory. The stable filename makes the fallback + * unsafe for concurrent runs against one checkout, so callers that have a + * per-run directory must pass it (see run/reporters.ts). + */ +export function writeWorkflowReport(results: WorkflowTestCaseResult[], outputDir?: string): string { + const reportDir = outputDir ?? path.join(__dirname, '..', '..', '.data'); if (!fs.existsSync(reportDir)) { fs.mkdirSync(reportDir, { recursive: true }); } diff --git a/packages/@n8n/instance-ai/evaluations/run/build-orchestrator.ts b/packages/@n8n/instance-ai/evaluations/run/build-orchestrator.ts index 2018709a581..7e8e46a48d1 100644 --- a/packages/@n8n/instance-ai/evaluations/run/build-orchestrator.ts +++ b/packages/@n8n/instance-ai/evaluations/run/build-orchestrator.ts @@ -75,7 +75,7 @@ export type BuildArgs = Pick< | 'conversation' | 'messageBudget' | 'credentials' - | 'seedFile' + | 'conversationSeed' | 'priorConversation' | 'seedThread' | 'executionScenarios' @@ -450,7 +450,7 @@ export function createBuildOrchestrator(deps: BuildOrchestratorDeps): BuildOrche conversation: entry.conversation, messageBudget: entry.messageBudget, credentials: entry.credentials, - seedFile: entry.seedFile, + conversationSeed: entry.conversationSeed, priorConversation: entry.priorConversation, seedThread: entry.seedThread, executionScenarios: entry.executionScenarios, diff --git a/packages/@n8n/instance-ai/evaluations/run/eval-session.ts b/packages/@n8n/instance-ai/evaluations/run/eval-session.ts index 93b472a6679..9cf831a3d81 100644 --- a/packages/@n8n/instance-ai/evaluations/run/eval-session.ts +++ b/packages/@n8n/instance-ai/evaluations/run/eval-session.ts @@ -140,7 +140,7 @@ export function createEvalSession(config: EvalSessionConfig): EvalSession { conversation: buildArgs.conversation, messageBudget: buildArgs.messageBudget, credentials: buildArgs.credentials, - seedFile: buildArgs.seedFile, + conversationSeed: buildArgs.conversationSeed, priorConversation: buildArgs.priorConversation, seedThread: buildArgs.seedThread, executionScenarios: buildArgs.executionScenarios, @@ -175,6 +175,7 @@ export function createEvalSession(config: EvalSessionConfig): EvalSession { execArgs.buildTrace, args.pinAiRoots, execArgs.seedContext, + args.outputDir, ), ), tracedExecuteAgent: wrap( @@ -197,6 +198,7 @@ export function createEvalSession(config: EvalSessionConfig): EvalSession { execArgs.timeoutMs, execArgs.testCaseName, execArgs.buildTrace, + args.outputDir, ), ), }; diff --git a/packages/@n8n/instance-ai/evaluations/run/persist.ts b/packages/@n8n/instance-ai/evaluations/run/persist.ts index ccb0066e94a..4e4f808db31 100644 --- a/packages/@n8n/instance-ai/evaluations/run/persist.ts +++ b/packages/@n8n/instance-ai/evaluations/run/persist.ts @@ -429,6 +429,11 @@ export function writeEvalResults( passHatK: terminalRate(ea.passHatK), })), threadIds: tc.runs.map((run) => run.threadId ?? null), + // Per-iteration conversation transcript + build-failure reason — the + // LangTracer dispatcher ingests both (null when an iteration has none). + // Transcript content is redacted at capture (transcript-from-events). + transcriptPerRun: tc.runs.map((run) => run.transcript ?? null), + buildErrorPerRun: tc.runs.map((run) => run.buildError ?? null), // `claude` build spend per iteration (--build-via-mcp only) — the // dedupe-safe source for per-case cost (LangSmith feedback repeats the // value on every row of a case's build). diff --git a/packages/@n8n/instance-ai/evaluations/run/reporters.ts b/packages/@n8n/instance-ai/evaluations/run/reporters.ts index 03e673b75b2..d0e28167bdb 100644 --- a/packages/@n8n/instance-ai/evaluations/run/reporters.ts +++ b/packages/@n8n/instance-ai/evaluations/run/reporters.ts @@ -84,18 +84,30 @@ export function emitRunReports(config: { gate: GateResult | undefined; slugByTestCase: Map | undefined; commitSha: string | undefined; + /** --output-dir; the HTML reports land here alongside the data artifacts. + * Undefined leaves each writer on its `.data` default. */ + outputDir: string | undefined; jsonPath: string; prCommentPath: string; /** --experiment-name; baseline-prefixed names trigger the noise advisory. */ experimentName?: string; }): void { - const { evaluation, outcome, gate, slugByTestCase, commitSha, jsonPath, prCommentPath } = config; + const { + evaluation, + outcome, + gate, + slugByTestCase, + commitSha, + outputDir, + jsonPath, + prCommentPath, + } = config; console.log(`Results: ${jsonPath}`); console.log(`PR comment: ${prCommentPath}`); const reportResults = flattenRunsForReport(evaluation); - const htmlPath = writeWorkflowReport(reportResults); + const htmlPath = writeWorkflowReport(reportResults, outputDir); console.log(`Report: ${htmlPath}`); - const debugHtmlPath = writeRunDebugReport(reportResults); + const debugHtmlPath = writeRunDebugReport(reportResults, outputDir); console.log(`LLM debug: ${debugHtmlPath}`); console.log( '\n' + formatComparisonTerminal(evaluation, outcome, { commitSha, slugByTestCase, gate }), diff --git a/packages/@n8n/instance-ai/evaluations/types.ts b/packages/@n8n/instance-ai/evaluations/types.ts index acabb25f8f1..ba5efca43b9 100644 --- a/packages/@n8n/instance-ai/evaluations/types.ts +++ b/packages/@n8n/instance-ai/evaluations/types.ts @@ -11,6 +11,7 @@ import type { import type { CheckOutcome } from './binaryChecks/types'; import type { WorkflowResponse } from './clients/n8n-client'; +import type { ConversationSeed } from './harness/conversation-seed'; // --------------------------------------------------------------------------- // Checklist items and verification @@ -238,11 +239,11 @@ export interface WorkflowTestCase { * field build with an empty view (everything mocks). */ credentials?: TestCaseCredential[]; - /** Synthetic seed file (messages + workflows) restored before the live turn. - * Synthetic fixtures only; mutually exclusive with the other seeds. */ - seedFile?: string; + /** Prior messages + the workflows they reference, restored before the live turn. + * Mutually exclusive with the other seeds. */ + conversationSeed?: ConversationSeed; /** Prose turns seeded as plain-text history (no tool calls/workflows). - * Mutually exclusive with `seedFile`. */ + * Mutually exclusive with the other seeds. */ priorConversation?: ConversationTurn[]; /** Reproduce a real conversation from its LangSmith trace at run time: restore * up to the live turn (the last user message, or one pinned by `liveTurnRunId`) diff --git a/packages/@n8n/instance-ai/evaluations/utils/confirmation-payload.ts b/packages/@n8n/instance-ai/evaluations/utils/confirmation-payload.ts index d959219d9c0..28ee9242542 100644 --- a/packages/@n8n/instance-ai/evaluations/utils/confirmation-payload.ts +++ b/packages/@n8n/instance-ai/evaluations/utils/confirmation-payload.ts @@ -10,9 +10,9 @@ import type { CapturedEvent } from '../types'; /** * Handle confirmation events that carry no user-intent signal — domain access, - * resource decisions, standalone credential requests. The eval grants all - * access, has no credentials, and picks the most-permissive option for - * resource gates. Returns `undefined` for events that need caller-specific + * web search, resource decisions, standalone credential requests. The eval + * grants all access, has no credentials, and picks the most-permissive option + * for resource gates. Returns `undefined` for events that need caller-specific * handling: setup wizards, ask-user questions, plan reviews. */ export function tryInfrastructureResponse( @@ -20,7 +20,8 @@ export function tryInfrastructureResponse( ): InstanceAiConfirmRequest | undefined { const payload = getNestedRecord(event.data, 'payload') ?? {}; - if (getNestedRecord(payload, 'domainAccess')) { + // Web search reuses domain access's approval shape. + if (getNestedRecord(payload, 'domainAccess') || getNestedRecord(payload, 'webSearch')) { return { kind: 'domainAccessApprove', domainAccessAction: 'allow_all' }; } diff --git a/packages/@n8n/instance-ai/evaluations/utils/load-eval-cases.ts b/packages/@n8n/instance-ai/evaluations/utils/load-eval-cases.ts index f36c61b4eea..3af860a5c2c 100644 --- a/packages/@n8n/instance-ai/evaluations/utils/load-eval-cases.ts +++ b/packages/@n8n/instance-ai/evaluations/utils/load-eval-cases.ts @@ -1,8 +1,7 @@ import { readFileSync } from 'fs'; -import { basename, dirname, resolve } from 'path'; +import { basename } from 'path'; import { getJsonFiles } from './get-json-files'; -import { loadConversationSeed } from '../harness/conversation-seed'; import { EvalTestCaseSchema } from '../harness/schema'; import type { WorkflowTestCase } from '../types'; @@ -32,21 +31,7 @@ function parseTestCaseFile(filePath: string): WorkflowTestCase { throw new Error(`Invalid test case ${filePath}:\n${issues}`); } - const testCase = parsed.data; - if (testCase.seedFile) { - // Resolve relative to the case file and validate now, so an authoring - // typo fails at load time instead of per-build as an agent failure. - const resolved = resolve(dirname(filePath), testCase.seedFile); - try { - loadConversationSeed(resolved); - } catch (error) { - throw new Error( - `Invalid test case ${filePath}: ${error instanceof Error ? error.message : String(error)}`, - ); - } - testCase.seedFile = resolved; - } - return testCase; + return parsed.data; } /** Load test cases with their file slugs (for LangSmith dataset sync derived IDs). */ diff --git a/packages/@n8n/instance-ai/evaluations/utils/user-proxy/index.ts b/packages/@n8n/instance-ai/evaluations/utils/user-proxy/index.ts index 83d8c12d8a2..9f2ffc237c1 100644 --- a/packages/@n8n/instance-ai/evaluations/utils/user-proxy/index.ts +++ b/packages/@n8n/instance-ai/evaluations/utils/user-proxy/index.ts @@ -131,7 +131,7 @@ export class UserProxyLlm { } const det = tryDeterministicConfirmationResponse(event); - if (det) { + if (det && !this.deferAccessGateToScript(event)) { this.bumpStat('deterministic'); return this.rememberResponse(requestId, det); } @@ -261,6 +261,15 @@ export class UserProxyLlm { return this.tryScriptedConfirmationResponse(event) ?? buildAutoApprovePayload(event); } + /** Network-access gates (fetch-url domain, web search) are granted deterministically so the + * common case spends no LLM call. A pending stage direction may instruct a refusal though, + * so hand those to the LLM the way plan review already is. */ + private deferAccessGateToScript(event: CapturedEvent): boolean { + const payload = getEventPayload(event); + if (!payload.domainAccess && !payload.webSearch) return false; + return this.remainingUserScriptTurns().some((turn) => hasStageDirection(turn.text)); + } + private tryScriptedConfirmationResponse( event: CapturedEvent, ): InstanceAiConfirmRequest | undefined { diff --git a/packages/@n8n/instance-ai/evaluations/utils/user-proxy/tools.ts b/packages/@n8n/instance-ai/evaluations/utils/user-proxy/tools.ts index f4f9478cfa9..ab1e689a256 100644 --- a/packages/@n8n/instance-ai/evaluations/utils/user-proxy/tools.ts +++ b/packages/@n8n/instance-ai/evaluations/utils/user-proxy/tools.ts @@ -111,7 +111,7 @@ export const CONFIRMATION_TOOL_DESCRIPTIONS = `Available actions — confirmatio - approve_or_reject(approved, userInput?): A plan-review or free-text confirmation widget is on screen (the event's inputType is plan-review or text). Approve if the plan matches user intent; reject with reason if it diverges. This action only exists as a response to such a widget. -- respond_to_domain_access(response): The agent is asking for domain access permissions. Pick allow_once, allow_all, or deny. Default to allow_all unless the user would deny. +- respond_to_domain_access(response): The agent is asking permission to reach the network — either a specific domain (fetch-url) or a web search. Pick allow_once, allow_all, or deny. Default to allow_all; pick deny ONLY when a [stage direction] tells the user to refuse this kind of access. - pick_resource_decision(decision): The agent is asking the user to pick a gateway resource access option. Pick the option the user would choose.`; diff --git a/packages/@n8n/instance-ai/knowledge-base/reference/workflow-builder-guardrails.md b/packages/@n8n/instance-ai/knowledge-base/reference/workflow-builder-guardrails.md index a6fc69bdcce..2e25783114c 100644 --- a/packages/@n8n/instance-ai/knowledge-base/reference/workflow-builder-guardrails.md +++ b/packages/@n8n/instance-ai/knowledge-base/reference/workflow-builder-guardrails.md @@ -4,8 +4,10 @@ Use these guardrails for workflow builds with multiple external systems, multiple requested effects, digests or reports, non-trivial branching, or Code nodes. They are a runtime checklist, not extra user-facing output. -Do not add sticky notes unless the user explicitly asks for them. Prefer chat -explanations over canvas stickies. +Code-node runtime limits (no network, forbidden imports, nested template +literals) and unsolicited stickies are enforced by `workflow-sdk validate` — +fix those findings before `build-workflow`. Prefer built-in nodes for simple +split, map, filter, merge, and aggregate work. ## Preserve Source Data @@ -116,22 +118,9 @@ node after a side-effect needs the original data, reference it by node name (`$('Compute Change').item.json.status`) or wire it in parallel from the data-producing node instead of chaining through the send. -## Keep Code Nodes Parseable +## Code Nodes -Prefer built-in nodes for simple split, map, filter, merge, and aggregate work. -When a Code node is necessary, use real n8n item APIs such as `$input.all()` and -return explicit `json` objects. - -Code nodes run in a restricted runtime. Do not `require()` or `import` -unavailable modules such as `luxon` or `openai`; use JavaScript `Date`, `Intl`, -`$now`, `$today`, existing workflow data, or dedicated AI nodes. - -Code nodes have no network access. `fetch()`, `axios`, `XMLHttpRequest`, and -`require` of http modules all fail at runtime, in JavaScript and Python alike. -Make every HTTP/API call with the HTTP Request node and transform its output in -the Code node, even when the user asks to fetch inside a Code node. - -Keep embedded Code node source parseable after saving. Avoid nested template -literals, raw newlines inside quoted strings, and escape-heavy regex literals. -Prefer arrays joined with a runtime separator such as -`const LF = String.fromCharCode(10);`. +When a Code node is necessary, use real n8n item APIs such as `$input.all()` / +`$input.item` and return explicit `json` objects. Prefer arrays joined with a +runtime separator (e.g. `const LF = String.fromCharCode(10);`) over escape-heavy +multi-line string construction. diff --git a/packages/@n8n/instance-ai/package.json b/packages/@n8n/instance-ai/package.json index 44101c50647..591a8114ba6 100644 --- a/packages/@n8n/instance-ai/package.json +++ b/packages/@n8n/instance-ai/package.json @@ -64,7 +64,7 @@ "@n8n/mcp-browser": "workspace:*", "@n8n/utils": "workspace:*", "@n8n/workflow-sdk": "workspace:*", - "@opentelemetry/api": "^1.9.0", + "@opentelemetry/api": "catalog:", "@thednp/dommatrix": "^2.0.12", "ai": "catalog:", "csv-parse": "catalog:", diff --git a/packages/@n8n/instance-ai/skills/workflow-builder/SKILL.md b/packages/@n8n/instance-ai/skills/workflow-builder/SKILL.md index 3ef6b0927f2..75f07386658 100644 --- a/packages/@n8n/instance-ai/skills/workflow-builder/SKILL.md +++ b/packages/@n8n/instance-ai/skills/workflow-builder/SKILL.md @@ -3,15 +3,17 @@ name: workflow-builder description: >- Load before calling build-workflow. Default path for all single-workflow work: new one-off workflows, existing-workflow edits, verification repairs, - and workflow-local data tables. Write or edit a workspace source file, then - call build-workflow with filePath. When the workflow creates or writes Data - Tables, load data-table-manager first, then this skill. Do not load planning - or create-tasks first. Load planning only when multiple coordinated workflows + and workflow-local data tables. Write or edit a workspace source file, run + workflow-sdk validate via workspace_execute_command, then call build-workflow + with filePath. When the workflow creates or writes Data Tables, load + data-table-manager first, then this skill. Do not load planning or + create-tasks first. Load planning only when multiple coordinated workflows or shared cross-task data tables require a dependency-aware task graph. recommended_tools: - read_file - write_file - edit_file + - execute_command - build-workflow - workflows - nodes @@ -32,13 +34,12 @@ You are an expert n8n workflow builder. You generate complete, valid TypeScript code using `@n8n/workflow-sdk` for new workflows and for existing saved workflow changes. -For new single-workflow requests, build directly with -`build-workflow({ filePath, sourceCode })` — the complete TypeScript SDK -source in `sourceCode`; the tool writes the file and builds in one call. For +Always write the complete TypeScript SDK source with +`workspace_write_file` first, then call `build-workflow({ filePath })`. For existing saved workflow edits, call `workflows(action="get-as-code", -workflowId)`, apply the edit to the returned code, then call -`build-workflow({ filePath, workflowId, sourceCode })` the first time — all -edits go through a workspace source file and `build-workflow`. Do not load +workflowId)`, apply the edit to the returned code, write it to the file, then +call `build-workflow({ filePath, workflowId })` the first time — all edits go +through a workspace source file and `build-workflow`. Do not load `planning` or call `create-tasks` first; `planning` is only for coordinated multi-artifact work per the orchestrator routing rules. Do not create a plan just for verification. @@ -60,16 +61,14 @@ editing anything — never guess at the cause or change the node on a hunch. When called with failure details for an existing workflow, start from the workspace source file if one is available in the conversation or tool output. If you only have a saved n8n workflow ID, use `workflows(action="get-as-code")`, -make the smallest requested edit to the returned code, then call -`build-workflow` once with `filePath` (a stable -`src/workflows/.workflow.ts` path), `workflowId`, and the full edited -code as `sourceCode`. Later repairs should reuse the same `filePath`; +make the smallest requested edit to the returned code, write it to a stable +`src/workflows/.workflow.ts` path, then call `build-workflow` once with +`filePath` and `workflowId`. Later repairs should reuse the same `filePath`; `build-workflow` remembers the bound workflow ID. For repairs, prefer editing the workspace file directly with file tools (`workspace_str_replace_file`) and calling `build-workflow` again with the same -`filePath` alone — cheaper than resending full source. `sourceCode` must always -be the complete source when used; never send string patches or fragments. +`filePath`. ## Escalation @@ -138,7 +137,7 @@ For workflows with multiple external systems, multiple requested effects, digests or reports, non-trivial branching, or Code nodes, read `knowledge-base/reference/workflow-builder-guardrails.md` before writing code. Use it as the build checklist for source preservation, fan-out/fan-in, -effect-specific gating, list itemization, and Code-node safety. +effect-specific gating, and list itemization. When mapping downstream fields from an OpenAI node, read `knowledge-base/reference/open-ai-output-shape.md` (v2+ text/response uses @@ -184,12 +183,9 @@ build → publish → assign steps. Do not create one before the user opts in. `workflows(action="get-as-code", workflowId)`, apply your edit to the returned code, and pass the n8n `workflowId` only on the first `build-workflow` call. -6. Produce complete TypeScript SDK code. For a new or fully rewritten source - file, do NOT write it with `workspace_write_file` — pass it directly as - `sourceCode` on the `build-workflow` call (the tool writes `filePath` and - builds in one step; a separate write call wastes a full round-trip). Use - file tools only to selectively edit an existing `.workflow.ts` for - follow-up changes and repairs. Do not put secrets in the source file. +6. Produce complete TypeScript SDK code and write it with + `workspace_write_file` (new/full rewrite) or `workspace_str_replace_file` + (targeted edit). Do not put secrets in the source file. Before building, decide whether verification needs branch fixtures. When a live or nondeterministic upstream node (such as HTTP Request, search/list lookups, weather feeds, or AI classifiers) feeds IF/Switch logic and @@ -198,12 +194,21 @@ build → publish → assign steps. Do not create one before the user opts in. and later `fixtureOverrides` can exercise those scenarios. Do not simulate every external read by default; use this when branch coverage or deterministic proof depends on controlling the upstream data. -7. Call `build-workflow` with `filePath` (plus `sourceCode` for new or fully - rewritten source). +7. Before the first `build-workflow` (and again after substantive edits), run + SDK validation on the workspace source file via + `workspace_execute_command`: + `node --import tsx node_modules/@n8n/workflow-sdk/dist/cli/index.js validate ` + Output is lint-style (`line severity code message`); fix every `error` + row. Warnings do not block the save and the command may still exit 0, but + they flag defects that surface at run time — resolve or consciously dismiss + each one. A clean validate run does not guarantee `build-workflow` will + succeed (no full node-type registry in the sandbox CLI), so still call + `build-workflow`. +8. Call `build-workflow` with the `filePath` you wrote. For planned build follow-ups where `buildTask.isSupportingWorkflow === true`, pass `isSupportingWorkflow: true`; that saved supporting workflow is the task's final deliverable. -8. Trace wiring before declaring done. For IF, Switch, Merge, AI-agent, loop, or +9. Trace wiring before declaring done. For IF, Switch, Merge, AI-agent, loop, or multi-workflow wiring, trace each branch from source to target. Confirm IF branches are wired on the workflow builder (`.to(ifNode).onTrue(...).onFalse(...)` or `.to(ifNode.onTrue(...).onFalse(...))`), not as standalone calls on the IF @@ -213,15 +218,15 @@ build → publish → assign steps. Do not create one before the user opts in. every requested side effect is on a wired branch. Switch outputs use zero-based `.onCase(index, target)`, Merge modes match the data shape, and sub-nodes are attached to the correct parent. -9. Fix errors by editing the same workspace source file and calling - `build-workflow` again with the same `filePath`. Save again before any - verification step. -10. Modify existing workflows by editing the workspace `.workflow.ts` source +10. Fix errors by editing the same workspace source file, re-running + `workflow-sdk validate` on that file, then calling `build-workflow` again + with the same `filePath`. Save again before any verification step. +11. Modify existing workflows by editing the workspace `.workflow.ts` source file. If the file was created from `workflows(action="get-as-code")`, pass the real n8n `workflowId` on the first `build-workflow` call so the file is bound to the saved workflow. Never pass local SDK workflow IDs as n8n workflow IDs. -11. After a successful direct `build-workflow` result, if the tool output +12. After a successful direct `build-workflow` result, if the tool output contains `postBuildFlow.required: true`, follow the inlined `postBuildFlow.instructions` from that output (do not load `post-build-flow` separately) before verification, setup, error-workflow follow-up, @@ -432,33 +437,30 @@ never from `$now.weekday == N`, which silently no-ops on other days. ## SDK Code Rules +`workflow-sdk validate` (step 7 in the build loop) enforces common SDK and +Code-node defects: network calls / forbidden imports in Code nodes, nested +template literals in `jsCode`, TypeScript-only syntax such as `as const`, +statements after `export default`, `placeholder()` wrapped in `expr()`, +unsolicited `sticky()`, forbidden builder constructs (e.g. `.map()`), and +repeated `.onTrue()` / `.onFalse()` overwrites on the same IF variable. Fix +every reported error and warning before calling `build-workflow`. + +- Code nodes need not always be necessary. You can use other n8n nodes to do the same thing. - SDK builder code is a restricted subset of TypeScript that builds a static - graph; it is not a Code node and does not run. Only SDK builder methods chain - on SDK objects. Native array/string methods (`.join()`, `.map()`), loops, arrow - functions, `new`, and globals like `Math`, `Date`, and `Object` are - unavailable. Build strings with template literals or explicit lines; do runtime - joining, aggregation, or transforms in a Code node or an n8n expression - (`expr()`). Full allowed/forbidden list: + graph; it is not a Code node and does not run. Build strings with template + literals; do runtime joining, aggregation, or transforms in a Code node or + `expr()`. Full allowed/forbidden list: `knowledge-base/reference/workflow-sdk-language.md`. - -- Code nodes have NO network access at runtime: `fetch()`, `axios`, - `XMLHttpRequest`, and `require` of http modules all fail in the sandbox. Make - every HTTP/API call with the HTTP Request node and transform its output in a - Code node, even when the user asks to fetch inside a Code node. - - Use `@n8n/workflow-sdk`. -- `export default workflow(...)...` must be the last statement in the file, with - all wiring composed inside that chain. Statements after it (e.g. - `ifNode.onTrue(...)`) do not reach the builder and their nodes are dropped. - Do not specify node positions. They are auto-calculated by the layout engine. - Use `expr('{{ $json.field }}')` for n8n expressions. Variables must be inside `{{ }}`. `$json` is only the current item from the immediate predecessor. -- Do not use TypeScript-only syntax that the workflow parser cannot interpret, - such as `as const`. - Use string values directly for discriminator fields like `resource` and `operation`, for example `resource: 'message'`. -- When editing a pre-loaded workflow, remove `position` arrays from node - configs; they are auto-calculated. +- When editing a pre-loaded workflow, remove every `position` array — from node + configs and from `sticky()` options alike. Positions are auto-calculated, and + the saved workflow's own layout is restored on save, so nothing you drop here + is lost. Leaving some in place is worse than dropping all of them. - Use `placeholder('hint')` directly as the parameter value. Do not wrap placeholders in `expr()`, objects, or arrays unless the node definition explicitly expects an object and the placeholder is the direct value of one @@ -577,12 +579,6 @@ Follow these rules strictly when generating workflows: match time units broadly (day/days, week/weeks…), and give every classifier an explicit fallback bucket — a one-phrasing regex silently misroutes every other phrasing. -7. Do not add sticky notes (`sticky(...)` / `n8n-nodes-base.stickyNote`) unless - the user explicitly asks for canvas notes. They add visual noise and are - often poorly positioned. Put explanations in your chat reply instead. Even - when the SDK language reference documents `sticky()`, do not use it by - default. When editing a workflow, do not add or reintroduce stickies unless the user - explicitly asks for them. ## Tool Naming Rules @@ -685,8 +681,6 @@ export default workflow('id', 'name') For IF, each branch is a complete processing path. Wire branches on the workflow builder, not as standalone calls on the IF node variable. Chain steps inside a branch with `.to()`, or pass an array for parallel fan-out. -Never call `.onFalse()` more than once (same for `.onTrue()`); each repeat -overwrites the previous target. ```ts const isImportant = ifElse({ @@ -714,16 +708,14 @@ export default workflow('id', 'name') // Parallel fan-out on a branch: .onFalse([a, b, c]) ``` -Do NOT wire branches as standalone statements. -Then branch nodes are omitted from the saved graph, and repeated `.onFalse()` -calls keep only the last target. +Do NOT wire branches as standalone statements after `export default` — those +calls never reach the builder (`workflow-sdk validate` flags this). ```ts // WRONG export default workflow('id', 'name').add(startTrigger).to(isImportant); isImportant.onTrue(handleImportant); // never reaches the builder -isImportant.onFalse(sendHolding); // overwritten -isImportant.onFalse(alertSlack); // only this one would wire +isImportant.onFalse(sendHolding); ``` For Switch, wire cases the same way — `.to(switchNode).onCase(0, a).onCase(1, b)` @@ -743,10 +735,8 @@ For AI Agent workflows: ## Additional SDK Functions -- `placeholder('hint')`: marks a parameter value for user input. -- `sticky('content', nodes?, config?)`: opt-in only when the user explicitly - asks for a sticky note on the canvas. Do not import or call it otherwise. - When used, it must still be added to the workflow. +- `placeholder('hint')`: marks a parameter value for user input (use directly as + the parameter value; `workflow-sdk validate` flags wrapping it in `expr()`). - `.output(n)`: selects a zero-based output index. - `.onError(handler)`: connects a node's error output to a handler. Requires `onError: 'continueErrorOutput'` in the node config. diff --git a/packages/@n8n/instance-ai/src/debug/__tests__/run-debug-buffer.test.ts b/packages/@n8n/instance-ai/src/debug/__tests__/run-debug-buffer.test.ts index 0fb83103f22..c5f92715cad 100644 --- a/packages/@n8n/instance-ai/src/debug/__tests__/run-debug-buffer.test.ts +++ b/packages/@n8n/instance-ai/src/debug/__tests__/run-debug-buffer.test.ts @@ -1,4 +1,4 @@ -import type { OnStepFinishEvent, OnStepStartEvent } from 'ai'; +import type { GenerateTextStepEndEvent, GenerateTextStepStartEvent } from 'ai'; import { describe, expect, it } from 'vitest'; import { mock } from 'vitest-mock-extended'; @@ -12,7 +12,7 @@ import { } from '../run-debug-buffer'; import { sanitizeDebugSnapshotValue } from '../sanitize-debug-snapshot'; -function makeFinishEvent(text: string): OnStepFinishEvent { +function makeStepEndEvent(text: string): GenerateTextStepEndEvent { return { stepNumber: 0, text, @@ -24,10 +24,10 @@ function makeFinishEvent(text: string): OnStepFinishEvent { timestamp: new Date('2026-01-01T00:00:00.000Z'), messages: [{ role: 'assistant', content: text }], }, - } as unknown as OnStepFinishEvent; + } as unknown as GenerateTextStepEndEvent; } -function makeStartEvent(): OnStepStartEvent { +function makeStepStartEvent(): GenerateTextStepStartEvent { return { stepNumber: 0, system: 'You are helpful', @@ -36,7 +36,7 @@ function makeStartEvent(): OnStepStartEvent { toolChoice: 'auto', activeTools: ['search'], abortSignal: new AbortController().signal, - } as unknown as OnStepStartEvent; + } as unknown as GenerateTextStepStartEvent; } describe('RunDebugBuffer', () => { @@ -44,8 +44,8 @@ describe('RunDebugBuffer', () => { const buffer = new RunDebugBuffer(); buffer.ensure('run-1', 'thread-1'); - buffer.recordStepStart('run-1', 0, makeStartEvent()); - buffer.recordStepFinish('run-1', 0, makeFinishEvent('done')); + buffer.recordStepStart('run-1', 0, makeStepStartEvent()); + buffer.recordStepFinish('run-1', 0, makeStepEndEvent('done')); const record = buffer.get('run-1'); expect(record?.steps).toHaveLength(1); @@ -60,8 +60,8 @@ describe('RunDebugBuffer', () => { const hooks = createRunDebugStepHooks(buffer, { runId: 'run-1', threadId: 'thread-1' }); for (const label of ['first', 'second', 'third']) { - hooks.onStepStart(makeStartEvent()); - hooks.onStepFinish(makeFinishEvent(label)); + hooks.onStepStart(makeStepStartEvent()); + hooks.onStepEnd(makeStepEndEvent(label)); } const record = buffer.get('run-1'); @@ -75,12 +75,12 @@ describe('RunDebugBuffer', () => { buffer.ensure('run-1', 'thread-1'); const firstPass = createRunDebugStepHooks(buffer, { runId: 'run-1', threadId: 'thread-1' }); - firstPass.onStepStart(makeStartEvent()); - firstPass.onStepFinish(makeFinishEvent('before suspend')); + firstPass.onStepStart(makeStepStartEvent()); + firstPass.onStepEnd(makeStepEndEvent('before suspend')); const resumePass = createRunDebugStepHooks(buffer, { runId: 'run-1', threadId: 'thread-1' }); - resumePass.onStepStart(makeStartEvent()); - resumePass.onStepFinish(makeFinishEvent('after resume')); + resumePass.onStepStart(makeStepStartEvent()); + resumePass.onStepEnd(makeStepEndEvent('after resume')); const record = buffer.get('run-1'); expect(record?.steps).toHaveLength(2); @@ -100,7 +100,7 @@ describe('RunDebugBuffer', () => { toolChoice: 'auto', activeTools: ['search'], abortSignal: new AbortController().signal, - } as unknown as OnStepStartEvent, + } as unknown as GenerateTextStepStartEvent, 4, ); @@ -146,7 +146,7 @@ describe('RunDebugBuffer', () => { messages: [{ role: 'assistant', content: '' }], body: { secret: 'raw-provider-body' }, }, - } as unknown as OnStepFinishEvent, + } as unknown as GenerateTextStepEndEvent, 0, ); @@ -177,7 +177,7 @@ describe('RunDebugBuffer', () => { buffer.recordStepStart(runId, 0, { stepNumber: 0, messages: [], - } as unknown as OnStepStartEvent); + } as unknown as GenerateTextStepStartEvent); } expect(buffer.get('run-0')).toBeUndefined(); @@ -193,12 +193,20 @@ describe('RunDebugBuffer', () => { hooks.onStepStart({ stepNumber: 2, messages: [{ role: 'user', content: 'ping' }], - } as unknown as OnStepStartEvent); + } as unknown as GenerateTextStepStartEvent); expect(buffer.get('run-1')?.steps[0]?.stepNumber).toBe(0); expect(buffer.get('run-1')?.steps[0]?.input?.sdkStepNumber).toBe(2); }); + it('exposes onStepEnd and keeps onStepFinish as a compatibility alias', () => { + const buffer = new RunDebugBuffer(); + const hooks = createRunDebugStepHooks(buffer, { runId: 'run-1', threadId: 'thread-1' }); + + expect(typeof hooks.onStepEnd).toBe('function'); + expect(hooks.onStepFinish).toBe(hooks.onStepEnd); + }); + it('stores a run label on first ensure', () => { const buffer = new RunDebugBuffer(); buffer.ensure('run-1', 'thread-1', 'build a weather workflow'); diff --git a/packages/@n8n/instance-ai/src/debug/run-debug-buffer.ts b/packages/@n8n/instance-ai/src/debug/run-debug-buffer.ts index 8e8e2250b35..fd749c8747a 100644 --- a/packages/@n8n/instance-ai/src/debug/run-debug-buffer.ts +++ b/packages/@n8n/instance-ai/src/debug/run-debug-buffer.ts @@ -1,5 +1,5 @@ import { scrubSecretsInText } from '@n8n/utils/scrub-secrets'; -import type { OnStepFinishEvent, OnStepStartEvent } from 'ai'; +import type { GenerateTextStepEndEvent, GenerateTextStepStartEvent } from 'ai'; import type { Logger } from '../logger'; import { sanitizeDebugSnapshotRecord, sanitizeDebugSnapshotValue } from './sanitize-debug-snapshot'; @@ -53,16 +53,18 @@ export interface RunDebugStepHookOptions { threadId: string; } -function captureStepStartPayload(event: OnStepStartEvent): Record { - const { abortSignal: _abortSignal, ...capturable } = event; - return sanitizeDebugSnapshotRecord(capturable); -} - -function captureStepFinishPayload(event: OnStepFinishEvent): Record { +function captureStepStartPayload(event: GenerateTextStepStartEvent): Record { return sanitizeDebugSnapshotRecord(event); } -export function sanitizeStepStart(event: OnStepStartEvent, stepNumber: number): SanitizedStepStart { +function captureStepFinishPayload(event: GenerateTextStepEndEvent): Record { + return sanitizeDebugSnapshotRecord(event); +} + +export function sanitizeStepStart( + event: GenerateTextStepStartEvent, + stepNumber: number, +): SanitizedStepStart { return { ...captureStepStartPayload(event), stepNumber, @@ -71,7 +73,7 @@ export function sanitizeStepStart(event: OnStepStartEvent, stepNumber: number): } export function sanitizeStepFinish( - event: OnStepFinishEvent, + event: GenerateTextStepEndEvent, stepNumber: number, ): SanitizedStepFinish { return { @@ -85,21 +87,26 @@ export function createRunDebugStepHooks( buffer: RunDebugBuffer, options: RunDebugStepHookOptions, ): { - onStepStart: (event: OnStepStartEvent) => void; - onStepFinish: (event: OnStepFinishEvent) => void; + onStepStart: (event: GenerateTextStepStartEvent) => void; + onStepEnd: (event: GenerateTextStepEndEvent) => void; + /** @deprecated Use `onStepEnd` instead. */ + onStepFinish: (event: GenerateTextStepEndEvent) => void; } { // The agent runtime calls streamText/generateText once per loop iteration. The AI SDK // resets stepNumber to 0 on each call, so we allocate a run-scoped sequence instead. let stepIndex = buffer.getNextStepIndex(options.runId); + const onStepEnd = (event: GenerateTextStepEndEvent) => { + buffer.recordStepFinish(options.runId, stepIndex, event); + stepIndex++; + }; + return { onStepStart: (event) => { buffer.recordStepStart(options.runId, stepIndex, event); }, - onStepFinish: (event) => { - buffer.recordStepFinish(options.runId, stepIndex, event); - stepIndex++; - }, + onStepEnd, + onStepFinish: onStepEnd, }; } @@ -153,7 +160,7 @@ export class RunDebugBuffer { return this.records.get(runId)?.nextStepIndex ?? 0; } - recordStepStart(runId: string, stepIndex: number, event: OnStepStartEvent): void { + recordStepStart(runId: string, stepIndex: number, event: GenerateTextStepStartEvent): void { const record = this.records.get(runId); if (!record) return; @@ -171,7 +178,7 @@ export class RunDebugBuffer { record.steps.sort((a, b) => a.stepNumber - b.stepNumber); } - recordStepFinish(runId: string, stepIndex: number, event: OnStepFinishEvent): void { + recordStepFinish(runId: string, stepIndex: number, event: GenerateTextStepEndEvent): void { const record = this.records.get(runId); if (!record) return; diff --git a/packages/@n8n/instance-ai/src/skills/__tests__/runtime-skills.test.ts b/packages/@n8n/instance-ai/src/skills/__tests__/runtime-skills.test.ts index 9cf5b3e8cf5..b5432a2e341 100644 --- a/packages/@n8n/instance-ai/src/skills/__tests__/runtime-skills.test.ts +++ b/packages/@n8n/instance-ai/src/skills/__tests__/runtime-skills.test.ts @@ -24,18 +24,14 @@ describe('Instance AI runtime skills', () => { expect(skill).toContain('knowledge-base/reference/workflow-sdk-language.md'); }); - it('tells the workflow-builder not to add sticky notes by default', () => { + it('defers sticky and other SDK defects to workflow-sdk validate', () => { const skill = readFileSync( join(INSTANCE_AI_SKILLS_DIR, 'workflow-builder', 'SKILL.md'), 'utf-8', ); - expect(skill).toContain( - 'Do not add sticky notes (`sticky(...)` / `n8n-nodes-base.stickyNote`) unless', - ); + expect(skill).toContain('unsolicited `sticky()`'); + expect(skill).toContain('workflow-sdk validate'); expect(skill).not.toMatch(/import \{\n(?:[^\n]*\n)*?\s*sticky,/); - expect(skill).toMatch( - /opt-in only when the user explicitly\s+asks for a sticky note on the canvas/, - ); }); it('loads the bundled data-table-manager skill and its linked files', async () => { @@ -263,6 +259,7 @@ describe('Instance AI runtime skills', () => { 'read_file', 'write_file', 'edit_file', + 'execute_command', 'build-workflow', 'workflows', 'nodes', @@ -273,6 +270,7 @@ describe('Instance AI runtime skills', () => { ]); expect(skill?.description).toContain('Load before calling build-workflow'); expect(skill?.description).toContain('Default path for all single-workflow work'); + expect(skill?.description).toContain('workflow-sdk validate'); expect(skill?.description).toContain('load data-table-manager first'); expect(skill?.description).toContain('Do not load planning or create-tasks first'); @@ -280,6 +278,10 @@ describe('Instance AI runtime skills', () => { expect(loaded?.instructions).toContain('## Routing'); expect(loaded?.instructions).toContain('build-workflow'); expect(loaded?.instructions).toContain('filePath'); + expect(loaded?.instructions).toContain('workspace_write_file'); + expect(loaded?.instructions).toContain( + 'node --import tsx node_modules/@n8n/workflow-sdk/dist/cli/index.js validate', + ); expect(loaded?.instructions).toContain('workspace source file'); expect(loaded?.instructions).toContain('nodes(action="suggested")'); expect(loaded?.instructions).toContain('nodes(action="search")'); @@ -317,8 +319,10 @@ describe('Instance AI runtime skills', () => { expect(loaded?.instructions).toContain('`planning` or call `create-tasks` first'); expect(loaded?.instructions).toContain('.to(isImportant)'); expect(loaded?.instructions).toContain('.onTrue(handleImportant)'); - expect(loaded?.instructions).toContain('Never call `.onFalse()` more than once'); - expect(loaded?.instructions).toContain('branch nodes are omitted from the saved graph'); + expect(loaded?.instructions).toContain( + 'Do NOT wire branches as standalone statements after `export default`', + ); + expect(loaded?.instructions).toContain('never reaches the builder'); }); it('loads the bundled planning skill', async () => { diff --git a/packages/@n8n/instance-ai/src/stream/__tests__/map-chunk.test.ts b/packages/@n8n/instance-ai/src/stream/__tests__/map-chunk.test.ts index 4c28403140d..2f5ac36872a 100644 --- a/packages/@n8n/instance-ai/src/stream/__tests__/map-chunk.test.ts +++ b/packages/@n8n/instance-ai/src/stream/__tests__/map-chunk.test.ts @@ -532,4 +532,29 @@ describe('mapAgentChunkToEvent', () => { }, }); }); + + it('surfaces the gateway error message from an ai-sdk error wrapped in error.cause', () => { + // The n8n Connect gateway returns an actionable message; the ai-sdk APICallError + // reaches the stream wrapped, so the details live on error.cause. + const responseBody = JSON.stringify({ + error: { + message: + "n8n Connect doesn't currently support this operation. Switch to using your own credential to continue.", + type: 'ai_gateway_request_error', + }, + }); + const wrapped = new Error('Bad Request') as Error & { cause?: unknown }; + wrapped.cause = apiError('Bad Request', 400, responseBody); + + expect(map({ type: 'error', error: wrapped })).toEqual({ + type: 'error', + runId, + agentId, + payload: { + content: + "n8n Connect doesn't currently support this operation. Switch to using your own credential to continue.", + technicalDetails: responseBody, + }, + }); + }); }); diff --git a/packages/@n8n/instance-ai/src/stream/map-chunk.ts b/packages/@n8n/instance-ai/src/stream/map-chunk.ts index f9ed14175c0..33db7a0649b 100644 --- a/packages/@n8n/instance-ai/src/stream/map-chunk.ts +++ b/packages/@n8n/instance-ai/src/stream/map-chunk.ts @@ -159,6 +159,14 @@ function readErrorCode(error: unknown): string | undefined { return undefined; } +/** Find an ai-sdk `responseBody` on the error or its `cause` chain (the API error can arrive wrapped). */ +function readResponseBody(error: unknown): string | undefined { + if (typeof error !== 'object' || error === null) return undefined; + if ('responseBody' in error && typeof error.responseBody === 'string') return error.responseBody; + if ('cause' in error && error.cause !== error) return readResponseBody(error.cause); + return undefined; +} + /** Parse an ai-sdk JSON `responseBody` into its message and machine-readable code, if present. */ function parseResponseBody(responseBody: string): { message?: string; code?: string } { try { @@ -191,6 +199,14 @@ function extractErrorInfo(error: unknown): ErrorInfo { info.technicalDetails = error.responseBody; const { message } = parseResponseBody(error.responseBody); if (message) info.content = message; + } else { + // No direct responseBody — unwrap the cause chain (mirrors `readErrorCode`). + const wrappedBody = readResponseBody(error.cause); + if (wrappedBody) { + info.technicalDetails = wrappedBody; + const { message } = parseResponseBody(wrappedBody); + if (message) info.content = message; + } } // Extract provider from error name or URL if available diff --git a/packages/@n8n/instance-ai/src/tools/__tests__/research.tool.test.ts b/packages/@n8n/instance-ai/src/tools/__tests__/research.tool.test.ts index e36f64ccac9..eda8e9c3b19 100644 --- a/packages/@n8n/instance-ai/src/tools/__tests__/research.tool.test.ts +++ b/packages/@n8n/instance-ai/src/tools/__tests__/research.tool.test.ts @@ -350,6 +350,39 @@ describe('research tool', () => { expect(tracker.approveWebSearchOnce).not.toHaveBeenCalled(); }); + // The payload both the UI's approval widget and the eval user-proxy send. + it('should grant persistent approval and run the search on resume with allow_all', async () => { + const tracker = { + isHostAllowed: vi.fn(), + approveDomain: vi.fn(), + approveAllDomains: vi.fn(), + approveOnce: vi.fn(), + isWebSearchAllowed: vi.fn().mockReturnValue(false), + approveWebSearch: vi.fn(), + approveWebSearchOnce: vi.fn(), + }; + const context = createMockContext({ domainAccessTracker: tracker as never }); + context.webResearchService!.search = vi.fn().mockResolvedValue({ + query: 'q', + results: [{ title: 'T', url: 'https://example.com/a', snippet: 'S' }], + }); + const suspend = vi.fn(); + + const tool = createResearchTool(context); + const result = (await tool.handler!( + { action: 'web-search' as const, query: 'q' }, + createAgentCtx({ + resumeData: { approved: true, domainAccessAction: 'allow_all' }, + suspend, + }) as never, + )) as { results: unknown[] }; + + expect(tracker.approveWebSearch).toHaveBeenCalled(); + expect(suspend).not.toHaveBeenCalled(); + expect(context.webResearchService!.search).toHaveBeenCalledWith('q', expect.anything()); + expect(result.results).toHaveLength(1); + }); + it('should return empty results when resumed with denial', async () => { const tracker = { isHostAllowed: vi.fn(), diff --git a/packages/@n8n/instance-ai/src/tools/orchestration/__tests__/agent-target-binding.test.ts b/packages/@n8n/instance-ai/src/tools/orchestration/__tests__/agent-target-binding.test.ts index 6f9a8f51ce5..0e4a2c66017 100644 --- a/packages/@n8n/instance-ai/src/tools/orchestration/__tests__/agent-target-binding.test.ts +++ b/packages/@n8n/instance-ai/src/tools/orchestration/__tests__/agent-target-binding.test.ts @@ -1,7 +1,8 @@ import type { ThreadRecord } from '../../../storage/thread-patch'; import type { InstanceAiContext } from '../../../types'; import { - findSessionAgentByName, + getSessionAgentByRef, + normalizeAgentRef, resolveAgentBuilderTarget, saveAgentBuilderTarget, } from '../agent-target-binding'; @@ -38,17 +39,32 @@ function createContext(overrides: Partial = {}): InstanceAiCo } as unknown as InstanceAiContext; } -const TARGET = { agentId: 'agent-1', projectId: 'project-1' }; +const TARGET = { agentId: 'agent-1', projectId: 'project-1', ref: 'support-triage' }; + +describe('normalizeAgentRef', () => { + it('slugifies casing, whitespace, and punctuation to the same key', () => { + expect(normalizeAgentRef('Support Triage')).toBe('support-triage'); + expect(normalizeAgentRef('support_triage')).toBe('support-triage'); + expect(normalizeAgentRef(' Support-Triage ')).toBe('support-triage'); + }); + + it('keeps non-Latin names addressable instead of slugifying them away', () => { + expect(normalizeAgentRef('支持代理')).toBe('支持代理'); + expect(normalizeAgentRef('Служба поддержки')).toBe('служба-поддержки'); + }); +}); describe('agent-builder target binding', () => { it('round-trips the target through thread metadata across contexts', async () => { const threadMemory = createThreadMemory(); await saveAgentBuilderTarget(createContext({ threadMemory }), TARGET); - // A fresh context (next turn) resolves the persisted target and hydrates itself. const nextTurn = createContext({ threadMemory }); - await expect(resolveAgentBuilderTarget(nextTurn)).resolves.toEqual(TARGET); - expect(nextTurn.agentBuilderTarget).toEqual(TARGET); + await expect(resolveAgentBuilderTarget(nextTurn)).resolves.toEqual({ + ...TARGET, + ref: 'support-triage', + }); + expect(nextTurn.agentBuilderTarget).toEqual({ ...TARGET, ref: 'support-triage' }); }); it('prefers the in-memory context target over the persisted binding', async () => { @@ -96,37 +112,47 @@ describe('agent-builder target binding', () => { }); describe('session agent registry', () => { - it('upserts every saved target into the session registry', async () => { + it('saves then resolves by ref', async () => { const threadMemory = createThreadMemory(); await saveAgentBuilderTarget(createContext({ threadMemory }), { agentId: 'agent-1', projectId: 'p', - name: 'First', - }); - await saveAgentBuilderTarget(createContext({ threadMemory }), { - agentId: 'agent-2', - projectId: 'p', - name: 'Second', - }); - // Re-saving agent-1 under a new name replaces its old registry entry. - await saveAgentBuilderTarget(createContext({ threadMemory }), { - agentId: 'agent-1', - projectId: 'p', - name: 'Renamed', + name: 'Support Triage', + ref: 'support-triage', }); - const lookupContext = createContext({ threadMemory }); - await expect(findSessionAgentByName(lookupContext, 'Second')).resolves.toEqual({ - agentId: 'agent-2', - projectId: 'p', - name: 'Second', - }); - await expect(findSessionAgentByName(lookupContext, 'Renamed')).resolves.toEqual({ + await expect( + getSessionAgentByRef(createContext({ threadMemory }), 'Support_Triage'), + ).resolves.toEqual({ agentId: 'agent-1', projectId: 'p', - name: 'Renamed', + name: 'Support Triage', + ref: 'support-triage', + }); + }); + + it('keeps the addressing ref when a later save changes only the display name', async () => { + const threadMemory = createThreadMemory(); + await saveAgentBuilderTarget(createContext({ threadMemory }), { + agentId: 'agent-1', + projectId: 'p', + name: 'Tracker', + ref: 'tracker', + }); + await saveAgentBuilderTarget(createContext({ threadMemory }), { + agentId: 'agent-1', + projectId: 'p', + name: 'Renamed Tracker', + }); + + await expect( + getSessionAgentByRef(createContext({ threadMemory }), 'tracker'), + ).resolves.toEqual({ + agentId: 'agent-1', + projectId: 'p', + name: 'Renamed Tracker', + ref: 'tracker', }); - await expect(findSessionAgentByName(lookupContext, 'First')).resolves.toBeUndefined(); }); it('preserves the registered name when a later save for the same agent carries none', async () => { @@ -135,73 +161,49 @@ describe('agent-builder target binding', () => { agentId: 'agent-1', projectId: 'p', name: 'Tracker', + ref: 'tracker', }); - // Simulates an agentId-path switch, which carries no name. await saveAgentBuilderTarget(createContext({ threadMemory }), { agentId: 'agent-1', projectId: 'p', + ref: 'tracker', }); await expect( - findSessionAgentByName(createContext({ threadMemory }), 'Tracker'), - ).resolves.toEqual({ agentId: 'agent-1', projectId: 'p', name: 'Tracker' }); - + getSessionAgentByRef(createContext({ threadMemory }), 'tracker'), + ).resolves.toEqual({ + agentId: 'agent-1', + projectId: 'p', + name: 'Tracker', + ref: 'tracker', + }); await expect(resolveAgentBuilderTarget(createContext({ threadMemory }))).resolves.toEqual({ agentId: 'agent-1', projectId: 'p', name: 'Tracker', + ref: 'tracker', }); }); - it('finds the most recently targeted agent when names collide', async () => { - const threadMemory = createThreadMemory(); - await saveAgentBuilderTarget(createContext({ threadMemory }), { - agentId: 'a1', - projectId: 'p', - name: 'Dup', - }); - await saveAgentBuilderTarget(createContext({ threadMemory }), { - agentId: 'a2', - projectId: 'p', - name: 'Dup', - }); - - await expect(findSessionAgentByName(createContext({ threadMemory }), 'Dup')).resolves.toEqual( - { agentId: 'a2', projectId: 'p', name: 'Dup' }, - ); - }); - - it('matches names ignoring case and surrounding whitespace', async () => { - const threadMemory = createThreadMemory(); - await saveAgentBuilderTarget(createContext({ threadMemory }), { - agentId: 'agent-1', - projectId: 'p', - name: 'Platform Cycle Tracker', - }); - - await expect( - findSessionAgentByName(createContext({ threadMemory }), ' platform cycle tracker '), - ).resolves.toEqual({ agentId: 'agent-1', projectId: 'p', name: 'Platform Cycle Tracker' }); - }); - - it('returns undefined for unknown names, missing persistence, and malformed registries', async () => { + it('returns undefined for unknown refs, missing persistence, and malformed registries', async () => { const threadMemory = createThreadMemory(); await saveAgentBuilderTarget(createContext({ threadMemory }), { agentId: 'agent-1', projectId: 'p', name: 'First', + ref: 'first', }); await expect( - findSessionAgentByName(createContext({ threadMemory }), 'Unknown'), + getSessionAgentByRef(createContext({ threadMemory }), 'unknown'), ).resolves.toBeUndefined(); const noPersistence = createContext({ threadMemory: undefined, threadId: undefined }); - await expect(findSessionAgentByName(noPersistence, 'First')).resolves.toBeUndefined(); + await expect(getSessionAgentByRef(noPersistence, 'first')).resolves.toBeUndefined(); const malformed = createContext({ threadMemory: createThreadMemory({ instanceAiAgentBuilderTargets: 'garbage' }), }); - await expect(findSessionAgentByName(malformed, 'First')).resolves.toBeUndefined(); + await expect(getSessionAgentByRef(malformed, 'first')).resolves.toBeUndefined(); }); }); }); diff --git a/packages/@n8n/instance-ai/src/tools/orchestration/__tests__/build-agent.cascade-restart.test.ts b/packages/@n8n/instance-ai/src/tools/orchestration/__tests__/build-agent.cascade-restart.test.ts index 45f7a1b57ce..57884563813 100644 --- a/packages/@n8n/instance-ai/src/tools/orchestration/__tests__/build-agent.cascade-restart.test.ts +++ b/packages/@n8n/instance-ai/src/tools/orchestration/__tests__/build-agent.cascade-restart.test.ts @@ -438,6 +438,8 @@ describe('build-agent cascade restart (real SDK)', () => { expect(persistedThread?.metadata?.instanceAiAgentBuilderTarget).toMatchObject({ agentId: 'agent-1', projectId: 'proj-1', + name: 'Support Agent', + ref: 'support-agent', }); // ── Phase 2: drop everything, resume purely from checkpoint storage ─ diff --git a/packages/@n8n/instance-ai/src/tools/orchestration/__tests__/build-agent.target-registry.test.ts b/packages/@n8n/instance-ai/src/tools/orchestration/__tests__/build-agent.target-registry.test.ts new file mode 100644 index 00000000000..dab5b1f1b7d --- /dev/null +++ b/packages/@n8n/instance-ai/src/tools/orchestration/__tests__/build-agent.target-registry.test.ts @@ -0,0 +1,222 @@ +/** + * build-agent target addressing against the REAL binding module. + * + * `build-agent.tool.test.ts` mocks `agent-target-binding`, so it proves the + * resolver's behaviour given a registry hit but not that a create actually + * writes the key it later resolves by. This file wires real thread persistence + * so the create -> cancel -> replay round trip is covered end to end. + */ +import { mock } from 'vitest-mock-extended'; + +import { executeTool } from '../../../__tests__/tool-test-utils'; +import type { InstanceAiEventBus } from '../../../event-bus/event-bus.interface'; +import type { ThreadRecord } from '../../../storage/thread-patch'; +import type { + BuilderTurnStream, + InstanceAiBuilderDelegate, + InstanceAiContext, + OrchestrationContext, +} from '../../../types'; +import { createBuildAgentTool } from '../build-agent.tool'; + +const THREAD_ID = 'thread-1'; + +function createThreadMemory() { + const thread: ThreadRecord = { + id: THREAD_ID, + metadata: {}, + resourceId: 'resource-1', + createdAt: new Date(), + updatedAt: new Date(), + }; + return { + thread, + memory: { + getThread: async () => await Promise.resolve(thread), + patchThread: async (args: { + update: (current: ThreadRecord) => { metadata?: Record }; + }) => { + const patch = args.update({ ...thread, metadata: { ...(thread.metadata ?? {}) } }); + if (patch?.metadata) thread.metadata = patch.metadata; + return await Promise.resolve(thread); + }, + }, + }; +} + +function streamOf(chunks: unknown[], text: string): BuilderTurnStream { + return { + fullStream: (async function* () { + await Promise.resolve(); + for (const chunk of chunks) { + yield chunk; + } + })(), + text: Promise.resolve(text), + }; +} + +function completedStream(text: string): BuilderTurnStream { + return streamOf([], text); +} + +/** A stream that aborts the run mid-consumption, as a user Stop does. */ +function abortingStream(controller: AbortController): BuilderTurnStream { + return { + fullStream: (async function* () { + await Promise.resolve(); + controller.abort(); + yield { type: 'text-delta', id: 'a', delta: '' }; + })(), + text: Promise.resolve(''), + }; +} + +function makeContext(threadMemory: unknown) { + const delegate = mock(); + const domainContext = mock(); + domainContext.builderDelegate = delegate; + domainContext.projectId = 'proj-1'; + domainContext.threadMemory = threadMemory as InstanceAiContext['threadMemory']; + domainContext.threadId = THREAD_ID; + domainContext.agentBuilderTarget = undefined; + + const logger = { + debug: vi.fn(), + info: vi.fn(), + warn: vi.fn(), + error: vi.fn(), + } as unknown as OrchestrationContext['logger']; + domainContext.logger = logger; + + const context = mock(); + context.domainContext = domainContext; + context.threadId = THREAD_ID; + context.runId = 'run-1'; + context.userId = 'user-1'; + context.abortSignal = new AbortController().signal; + context.eventBus = mock(); + context.logger = logger; + context.modelId = 'anthropic/claude-sonnet'; + context.tracing = undefined; + context.claimSubAgentUsage = undefined; + context.trackTelemetry = undefined; + + return { context, delegate }; +} + +describe('build-agent target registry (real binding)', () => { + it('resolves a replayed create call to the agent created before the stop', async () => { + const { thread, memory } = createThreadMemory(); + const { context, delegate } = makeContext(memory); + vi.mocked(delegate.createAgent).mockResolvedValue({ + agentId: 'agent-1', + projectId: 'proj-1', + }); + vi.mocked(delegate.resolveAgentName).mockResolvedValue('Support Triage'); + + const controller = new AbortController(); + context.abortSignal = controller.signal; + vi.mocked(delegate.streamBuild).mockResolvedValue(abortingStream(controller)); + + await expect( + executeTool(createBuildAgentTool(context), { + message: 'Build it', + name: 'Support Triage', + }), + ).rejects.toMatchObject({ name: 'AbortError' }); + + expect(delegate.createAgent).toHaveBeenCalledTimes(1); + expect(thread.metadata?.instanceAiAgentBuilderTargets).toMatchObject({ + 'support-triage': { agentId: 'agent-1', ref: 'support-triage' }, + }); + + // Next turn: fresh context (nothing in memory), model replays its call. + const replay = makeContext(memory); + vi.mocked(replay.delegate.createAgent).mockResolvedValue({ + agentId: 'agent-2', + projectId: 'proj-1', + }); + vi.mocked(replay.delegate.resolveAgentName).mockResolvedValue('Support Triage'); + vi.mocked(replay.delegate.streamBuild).mockResolvedValue(completedStream('Continuing.')); + + const result = await executeTool<{ ok: boolean; agentId?: string; agentRef?: string }>( + createBuildAgentTool(replay.context), + { message: 'Build it', name: 'Support Triage' }, + ); + + expect(replay.delegate.createAgent).not.toHaveBeenCalled(); + expect(result).toMatchObject({ ok: true, agentId: 'agent-1', agentRef: 'support-triage' }); + expect(replay.delegate.streamBuild).toHaveBeenCalledWith( + 'agent-1', + 'Build it', + expect.objectContaining({ threadId: `ia-builder:${THREAD_ID}:agent-1` }), + ); + }); + + it('addresses an agent whose name carries no Latin characters', async () => { + const { thread, memory } = createThreadMemory(); + const { context, delegate } = makeContext(memory); + vi.mocked(delegate.createAgent).mockResolvedValue({ + agentId: 'agent-1', + projectId: 'proj-1', + }); + vi.mocked(delegate.resolveAgentName).mockResolvedValue('支持代理'); + vi.mocked(delegate.streamBuild).mockResolvedValue(completedStream('Created it.')); + + await executeTool(createBuildAgentTool(context), { message: 'Build it', name: '支持代理' }); + + expect(delegate.createAgent).toHaveBeenCalledTimes(1); + expect(thread.metadata?.instanceAiAgentBuilderTargets).toMatchObject({ + 支持代理: { agentId: 'agent-1' }, + }); + + const replay = makeContext(memory); + vi.mocked(replay.delegate.streamBuild).mockResolvedValue(completedStream('Continuing.')); + vi.mocked(replay.delegate.resolveAgentName).mockResolvedValue('支持代理'); + + await executeTool(createBuildAgentTool(replay.context), { + message: 'Add a tool', + name: '支持代理', + }); + + expect(replay.delegate.createAgent).not.toHaveBeenCalled(); + expect(replay.delegate.streamBuild).toHaveBeenCalledWith( + 'agent-1', + 'Add a tool', + expect.objectContaining({ threadId: `ia-builder:${THREAD_ID}:agent-1` }), + ); + }); + + it('creates a separate agent for a different name in the same thread', async () => { + const { memory } = createThreadMemory(); + const first = makeContext(memory); + vi.mocked(first.delegate.createAgent).mockResolvedValue({ + agentId: 'agent-1', + projectId: 'proj-1', + }); + vi.mocked(first.delegate.resolveAgentName).mockResolvedValue('Support Triage'); + vi.mocked(first.delegate.streamBuild).mockResolvedValue(completedStream('Created it.')); + + await executeTool(createBuildAgentTool(first.context), { + message: 'Build it', + name: 'Support Triage', + }); + + const second = makeContext(memory); + vi.mocked(second.delegate.createAgent).mockResolvedValue({ + agentId: 'agent-2', + projectId: 'proj-1', + }); + vi.mocked(second.delegate.resolveAgentName).mockResolvedValue('Docs Helper'); + vi.mocked(second.delegate.streamBuild).mockResolvedValue(completedStream('Created it.')); + + const result = await executeTool<{ agentId?: string; agentRef?: string }>( + createBuildAgentTool(second.context), + { message: 'Build another', name: 'Docs Helper' }, + ); + + expect(second.delegate.createAgent).toHaveBeenCalledWith('Docs Helper'); + expect(result).toMatchObject({ agentId: 'agent-2', agentRef: 'docs-helper' }); + }); +}); diff --git a/packages/@n8n/instance-ai/src/tools/orchestration/__tests__/build-agent.tool.test.ts b/packages/@n8n/instance-ai/src/tools/orchestration/__tests__/build-agent.tool.test.ts index 58ab58497cc..b09944bd6e8 100644 --- a/packages/@n8n/instance-ai/src/tools/orchestration/__tests__/build-agent.tool.test.ts +++ b/packages/@n8n/instance-ai/src/tools/orchestration/__tests__/build-agent.tool.test.ts @@ -20,7 +20,7 @@ import type { } from '../../../types'; import type * as AgentTargetBindingModule from '../agent-target-binding'; import { - findSessionAgentByName, + getSessionAgentByRef, saveAgentBuilderTarget, type AgentBuilderTarget, } from '../agent-target-binding'; @@ -34,7 +34,7 @@ vi.mock('../agent-target-binding', async () => { async (ctx: InstanceAiContext) => await Promise.resolve(ctx.agentBuilderTarget), ), saveAgentBuilderTarget: vi.fn(), - findSessionAgentByName: vi.fn(async () => await Promise.resolve(undefined)), + getSessionAgentByRef: vi.fn(async () => await Promise.resolve(undefined)), }; }); @@ -44,6 +44,7 @@ interface BuildAgentOutput { configUpdated?: boolean; error?: string; agentId?: string; + agentRef?: string; agentName?: string; answers?: QuestionAnswer[]; } @@ -263,7 +264,7 @@ async function runToolWithCtx( describe('build-agent tool', () => { beforeEach(() => { vi.mocked(saveAgentBuilderTarget).mockClear(); - vi.mocked(findSessionAgentByName).mockReset().mockResolvedValue(undefined); + vi.mocked(getSessionAgentByRef).mockReset().mockResolvedValue(undefined); }); it('fences the tool to Agent artifacts and away from workflow-anchored work', () => { @@ -291,6 +292,7 @@ describe('build-agent tool', () => { hostThreadId: 'thread-1', runId: 'run-1', modelConfig: context.modelId, + abortSignal: context.abortSignal, }); }); @@ -353,7 +355,8 @@ describe('build-agent tool', () => { expect(result).toEqual({ ok: false, - error: 'Pass name to create a new agent or agentId to edit an existing one.', + error: + 'Pass `name` (and optionally `agentRef`) to create a new agent, `agentId` to adopt an existing one, or omit both to continue the current agent.', }); expect(delegate.streamBuild).not.toHaveBeenCalled(); }); @@ -380,6 +383,7 @@ describe('build-agent tool', () => { error: friendlyMessage, configUpdated: false, agentId: 'agent-1', + agentRef: 'new-agent', agentName: 'New Agent', }); expect(publishedEvents.map((event) => event.type)).toEqual([ @@ -468,6 +472,7 @@ describe('build-agent tool', () => { hostThreadId: 'thread-1', runId: 'run-1', modelConfig: context.modelId, + abortSignal: context.abortSignal, }); }); @@ -505,6 +510,59 @@ describe('build-agent tool', () => { }); }); + describe('cancellation', () => { + it('throws without starting a builder turn when the run is already aborted', async () => { + const { context, delegate } = makeContext(); + const controller = new AbortController(); + controller.abort(); + context.abortSignal = controller.signal; + + await expect(runTool(context, { message: 'Build it', name: 'New Agent' })).rejects.toThrow( + 'The agent builder run was cancelled.', + ); + expect(delegate.streamBuild).not.toHaveBeenCalled(); + }); + + it('throws an abort error and still claims usage when the builder turn is cancelled mid-stream', async () => { + const { context, delegate, publishedEvents } = makeContext(); + const controller = new AbortController(); + context.abortSignal = controller.signal; + context.claimSubAgentUsage = vi.fn().mockResolvedValue(undefined); + vi.mocked(delegate.createAgent).mockResolvedValue({ + agentId: 'agent-1', + projectId: 'proj-1', + }); + vi.mocked(delegate.streamBuild).mockResolvedValue({ + fullStream: (async function* () { + await Promise.resolve(); + controller.abort(); + yield finishChunk(); + })(), + text: Promise.resolve(''), + }); + + await expect( + runToolWithCtx( + context, + { message: 'Build it', name: 'New Agent' }, + { toolCallId: 'orch-call-1' }, + ), + ).rejects.toMatchObject({ name: 'AbortError' }); + expect(context.claimSubAgentUsage).toHaveBeenCalledWith( + 'run-1:orch-call-1', + [expectedUsageItem], + 'cancelled', + ); + expect(delegate.resolveAgentName).not.toHaveBeenCalled(); + const completed = publishedEvents.find((event) => event.type === 'agent-completed'); + expect(completed && 'payload' in completed ? completed.payload : undefined).toEqual({ + role: 'agent-builder', + result: '', + status: 'cancelled', + }); + }); + }); + it('reports that the agent is not available on this instance when no builder delegate is configured', async () => { const { context } = makeContext(); context.domainContext!.builderDelegate = undefined; @@ -540,6 +598,7 @@ describe('build-agent tool', () => { builderReply: 'Updated the config.', configUpdated: true, agentId: 'agent-1', + agentRef: 'new-agent', agentName: 'New Agent', }); }, @@ -565,45 +624,6 @@ describe('build-agent tool', () => { }); describe('deferred agentId-path binding', () => { - it('rejects foreign agentId when the passed name contradicts the resolved agent name', async () => { - const { context, delegate } = makeContext(); - vi.mocked(delegate.resolveAgentName).mockResolvedValue('Support Triage Agent'); - - const result = await runTool(context, { - message: 'Build Ops Companion', - agentId: 'agent-existing', - name: 'Ops Companion', - }); - - expect(result).toEqual({ - ok: false, - error: - 'Agent agent-existing is named "Support Triage Agent", but name "Ops Companion" was passed. ' + - 'To create a new agent named "Ops Companion", pass `name` only (no `agentId`). ' + - 'To edit "Support Triage Agent", pass `agentId` only and put any rename instruction in `message`.', - }); - expect(delegate.streamBuild).not.toHaveBeenCalled(); - expect(saveAgentBuilderTarget).not.toHaveBeenCalled(); - }); - - it('allows foreign agentId when the passed name matches the resolved agent name', async () => { - const { context, delegate } = makeContext(); - vi.mocked(delegate.resolveAgentName).mockResolvedValue('Ops Companion'); - vi.mocked(delegate.streamBuild).mockResolvedValue(fakeStream([], 'Editing it.')); - - await runTool(context, { - message: 'Add a tool', - agentId: 'agent-existing', - name: ' ops companion ', - }); - - expect(delegate.streamBuild).toHaveBeenCalledWith( - 'agent-existing', - 'Add a tool', - expect.objectContaining({ threadId: 'ia-builder:thread-1:agent-existing' }), - ); - }); - it('does not persist the target when the agentId path fails before the stream settles', async () => { const { context, delegate } = makeContext(); vi.mocked(delegate.streamBuild).mockRejectedValue(new Error('agent:update forbidden')); @@ -618,6 +638,7 @@ describe('build-agent tool', () => { it('persists the target after the agentId-path stream completes normally', async () => { const { context, delegate } = makeContext(); + vi.mocked(delegate.resolveAgentName).mockResolvedValue('Existing Agent'); vi.mocked(delegate.streamBuild).mockResolvedValue(fakeStream([], 'Editing it.')); await runTool(context, { message: 'Add a tool', agentId: 'agent-existing' }); @@ -625,10 +646,14 @@ describe('build-agent tool', () => { expect(saveAgentBuilderTarget).toHaveBeenCalledWith(context.domainContext, { agentId: 'agent-existing', projectId: 'proj-1', + name: 'Existing Agent', + ref: 'existing-agent', }); expect(context.domainContext!.agentBuilderTarget).toEqual({ agentId: 'agent-existing', projectId: 'proj-1', + name: 'Existing Agent', + ref: 'existing-agent', }); }); @@ -648,6 +673,7 @@ describe('build-agent tool', () => { agentId: 'agent-1', projectId: 'proj-1', name: 'New Agent', + ref: 'new-agent', }); }); @@ -673,6 +699,7 @@ describe('build-agent tool', () => { it('persists the deferred agentId-path bind when the first turn suspends', async () => { const { context, delegate } = makeContext(); const suspend: Mock = vi.fn().mockResolvedValue(undefined); + vi.mocked(delegate.resolveAgentName).mockResolvedValue('Existing Agent'); vi.mocked(delegate.streamBuild).mockResolvedValue( suspendingStream('ask_questions', askQuestionsSuspendPayload()), ); @@ -686,6 +713,8 @@ describe('build-agent tool', () => { expect(saveAgentBuilderTarget).toHaveBeenCalledWith(context.domainContext, { agentId: 'agent-existing', projectId: 'proj-1', + name: 'Existing Agent', + ref: 'existing-agent', }); }); }); @@ -708,8 +737,12 @@ describe('build-agent tool', () => { name: 'Existing Agent', }, }); - expect(result).toMatchObject({ ok: true, agentId: 'agent-existing' }); - expect(result.agentName).toBe('Existing Agent'); + expect(result).toMatchObject({ + ok: true, + agentId: 'agent-existing', + agentRef: 'existing-agent', + agentName: 'Existing Agent', + }); // Name already fresh — no second agent-spawned republish. expect(publishedEvents.filter((event) => event.type === 'agent-spawned')).toHaveLength(1); }); @@ -723,6 +756,7 @@ describe('build-agent tool', () => { expect(result).toMatchObject({ ok: true, agentId: 'agent-existing' }); expect(result.agentName).toBeUndefined(); + expect(result.agentRef).toBeUndefined(); const spawned = publishedEvents[0]; const payload = spawned && 'payload' in spawned ? spawned.payload : undefined; expect(payload).toMatchObject({ @@ -749,7 +783,12 @@ describe('build-agent tool', () => { const result = await runTool(context, { message: 'Rename it', name: 'New Agent' }); - expect(result).toMatchObject({ ok: true, agentId: 'agent-1', agentName: 'Renamed Agent' }); + expect(result).toMatchObject({ + ok: true, + agentId: 'agent-1', + agentRef: 'new-agent', + agentName: 'Renamed Agent', + }); const spawnedEvents = publishedEvents.filter((event) => event.type === 'agent-spawned'); expect(spawnedEvents).toHaveLength(2); const republished = spawnedEvents[1]; @@ -767,6 +806,7 @@ describe('build-agent tool', () => { agentId: 'agent-1', projectId: 'proj-1', name: 'Renamed Agent', + ref: 'new-agent', }); }); @@ -797,7 +837,12 @@ describe('build-agent tool', () => { const result = await runTool(context, { message: 'Build it', name: 'New Agent' }); - expect(result).toMatchObject({ ok: true, agentId: 'agent-1', agentName: 'New Agent' }); + expect(result).toMatchObject({ + ok: true, + agentId: 'agent-1', + agentRef: 'new-agent', + agentName: 'New Agent', + }); expect(publishedEvents.filter((event) => event.type === 'agent-spawned')).toHaveLength(1); expect(context.logger.warn).toHaveBeenCalledWith( 'Failed to refresh agent name after builder turn', @@ -822,24 +867,142 @@ describe('build-agent tool', () => { const payload = suspend.mock.calls[0][0] as Record; expect(payload).toMatchObject({ builderCheckpoint: { - target: { agentId: 'agent-1', projectId: 'proj-1', name: 'Renamed Agent' }, + target: { + agentId: 'agent-1', + projectId: 'proj-1', + name: 'Renamed Agent', + ref: 'new-agent', + }, }, }); }); }); - describe('multi-agent target switching', () => { - it('creates and rebinds to a second agent when a different name is given while a target is bound', async () => { + describe('ref-keyed target resolution', () => { + it('creates when the key is unknown and name is given', async () => { const { context, delegate } = makeContext(); - // The domain-context field's declared type omits `name`; assign via a - // separately-typed variable to keep the excess-property check (which - // applies to fresh object literals) from firing. - const boundTarget: AgentBuilderTarget = { + vi.mocked(delegate.createAgent).mockResolvedValue({ + agentId: 'agent-1', + projectId: 'proj-1', + }); + vi.mocked(delegate.streamBuild).mockResolvedValue(fakeStream([], 'Created it.')); + + const result = await runTool(context, { + message: 'Build it', + name: 'Support Triage', + agentRef: 'support-triage', + }); + + expect(delegate.createAgent).toHaveBeenCalledWith('Support Triage'); + expect(saveAgentBuilderTarget).toHaveBeenCalledWith(context.domainContext, { + agentId: 'agent-1', + projectId: 'proj-1', + name: 'Support Triage', + ref: 'support-triage', + }); + expect(result).toMatchObject({ + ok: true, + agentId: 'agent-1', + agentRef: 'support-triage', + agentName: 'Support Triage', + }); + }); + + it('continues without creating when the same key is repeated', async () => { + const { context, delegate } = makeContext(); + const sessionAgent: AgentBuilderTarget = { + agentId: 'agent-1', + projectId: 'proj-1', + name: 'Support Triage', + ref: 'support-triage', + }; + vi.mocked(getSessionAgentByRef).mockResolvedValue(sessionAgent); + vi.mocked(delegate.streamBuild).mockResolvedValue(fakeStream([], 'Continuing.')); + + await runTool(context, { + message: 'Continue', + name: 'Support Triage', + agentRef: 'support-triage', + }); + + expect(delegate.createAgent).not.toHaveBeenCalled(); + expect(delegate.streamBuild).toHaveBeenCalledWith( + 'agent-1', + 'Continue', + expect.objectContaining({ threadId: 'ia-builder:thread-1:agent-1' }), + ); + }); + + it('refuses when the key is bound to a different agentId', async () => { + const { context, delegate } = makeContext(); + vi.mocked(getSessionAgentByRef).mockResolvedValue({ + agentId: 'agent-1', + projectId: 'proj-1', + name: 'Support Triage', + ref: 'support-triage', + }); + + const result = await runTool(context, { + message: 'Edit', + agentRef: 'support-triage', + agentId: 'agent-other', + }); + + expect(result).toEqual({ + ok: false, + error: + '`agentRef` "support-triage" is already bound to agent agent-1 in this conversation, ' + + 'but `agentId` agent-other was passed. Continue the bound agent (omit `agentId`, ' + + 'or pass its id), or pick a different `agentRef` for a new agent.', + }); + expect(delegate.createAgent).not.toHaveBeenCalled(); + expect(delegate.streamBuild).not.toHaveBeenCalled(); + }); + + it('continues the bound target when no key is given', async () => { + const { context, delegate } = makeContext(); + context.domainContext!.agentBuilderTarget = { + agentId: 'agent-1', + projectId: 'proj-1', + name: 'Helper', + ref: 'helper', + }; + vi.mocked(delegate.streamBuild).mockResolvedValue(fakeStream([], 'Continuing.')); + + await runTool(context, { message: 'Add a tool' }); + + expect(delegate.createAgent).not.toHaveBeenCalled(); + expect(delegate.streamBuild).toHaveBeenCalledWith( + 'agent-1', + 'Add a tool', + expect.objectContaining({ threadId: 'ia-builder:thread-1:agent-1' }), + ); + }); + + it('errors when the key is unknown and neither name nor agentId is given', async () => { + const { context, delegate } = makeContext(); + + const result = await runTool(context, { + message: 'Continue', + agentRef: 'unknown-ref', + }); + + expect(result).toEqual({ + ok: false, + error: + 'Unknown `agentRef`. Pass `name` to create a new agent under that key, or `agentId` to adopt an existing agent.', + }); + expect(delegate.createAgent).not.toHaveBeenCalled(); + }); + + it('creates a second agent under a different key while a target is bound', async () => { + const { context, delegate } = makeContext(); + context.domainContext!.agentBuilderTarget = { agentId: 'agent-1', projectId: 'proj-1', name: 'First', + ref: 'first', }; - context.domainContext!.agentBuilderTarget = boundTarget; vi.mocked(delegate.createAgent).mockResolvedValue({ agentId: 'agent-2', projectId: 'proj-1', @@ -848,54 +1011,31 @@ describe('build-agent tool', () => { await runTool(context, { message: 'Build me another agent', name: 'Second' }); - expect(findSessionAgentByName).toHaveBeenCalledWith(context.domainContext, 'Second'); + expect(getSessionAgentByRef).toHaveBeenCalledWith(context.domainContext, 'second'); expect(delegate.createAgent).toHaveBeenCalledWith('Second'); expect(saveAgentBuilderTarget).toHaveBeenCalledWith(context.domainContext, { agentId: 'agent-2', projectId: 'proj-1', name: 'Second', - }); - expect(delegate.streamBuild).toHaveBeenCalledWith('agent-2', 'Build me another agent', { - threadId: 'ia-builder:thread-1:agent-2', - hostThreadId: 'thread-1', - runId: 'run-1', - modelConfig: context.modelId, + ref: 'second', }); }); - it('returns the target agentId and name so the orchestrator can switch back by id', async () => { + it('switches back via registry ref instead of creating a duplicate', async () => { const { context, delegate } = makeContext(); - vi.mocked(delegate.createAgent).mockResolvedValue({ - agentId: 'agent-2', - projectId: 'proj-1', - }); - vi.mocked(delegate.streamBuild).mockResolvedValue(fakeStream([], 'Created it.')); - - const result = await runTool(context, { message: 'Build me another agent', name: 'Second' }); - - expect(result).toEqual({ - ok: true, - builderReply: 'Created it.', - configUpdated: false, - agentId: 'agent-2', - agentName: 'Second', - }); - }); - - it('switches back to a session agent whose name matches the registry instead of creating a duplicate', async () => { - const { context, delegate } = makeContext(); - const boundTarget: AgentBuilderTarget = { + context.domainContext!.agentBuilderTarget = { agentId: 'agent-2', projectId: 'proj-1', name: 'Docs Helper', + ref: 'docs-helper', }; - context.domainContext!.agentBuilderTarget = boundTarget; const sessionAgent: AgentBuilderTarget = { agentId: 'agent-1', projectId: 'proj-1', name: 'Platform Cycle Tracker', + ref: 'platform-cycle-tracker', }; - vi.mocked(findSessionAgentByName).mockResolvedValue(sessionAgent); + vi.mocked(getSessionAgentByRef).mockResolvedValue(sessionAgent); vi.mocked(delegate.streamBuild).mockResolvedValue(fakeStream([], 'Switched back.')); await runTool(context, { @@ -904,29 +1044,33 @@ describe('build-agent tool', () => { }); expect(delegate.createAgent).not.toHaveBeenCalled(); - expect(delegate.streamBuild).toHaveBeenCalledWith('agent-1', 'Go back to the tracker agent', { - threadId: 'ia-builder:thread-1:agent-1', - hostThreadId: 'thread-1', - runId: 'run-1', - modelConfig: context.modelId, + expect(delegate.streamBuild).toHaveBeenCalledWith( + 'agent-1', + 'Go back to the tracker agent', + expect.objectContaining({ threadId: 'ia-builder:thread-1:agent-1' }), + ); + expect(saveAgentBuilderTarget).toHaveBeenCalledWith(context.domainContext, { + ...sessionAgent, + ref: 'platform-cycle-tracker', + name: 'Platform Cycle Tracker', }); - expect(saveAgentBuilderTarget).toHaveBeenCalledWith(context.domainContext, sessionAgent); }); - it('does not clobber the binding when the registry switch-back turn fails before settling', async () => { + it('does not clobber the binding when a registry switch-back fails before settling', async () => { const { context, delegate } = makeContext(); const boundTarget: AgentBuilderTarget = { agentId: 'agent-2', projectId: 'proj-1', name: 'Docs Helper', + ref: 'docs-helper', }; context.domainContext!.agentBuilderTarget = boundTarget; - const sessionAgent: AgentBuilderTarget = { + vi.mocked(getSessionAgentByRef).mockResolvedValue({ agentId: 'agent-1', projectId: 'proj-1', name: 'Platform Cycle Tracker', - }; - vi.mocked(findSessionAgentByName).mockResolvedValue(sessionAgent); + ref: 'platform-cycle-tracker', + }); vi.mocked(delegate.streamBuild).mockRejectedValue(new Error('boom')); await expect( @@ -940,75 +1084,45 @@ describe('build-agent tool', () => { expect(context.domainContext!.agentBuilderTarget).toEqual(boundTarget); }); - it('skips the registry lookup when the name matches the bound target', async () => { + it('continues the bound build when the given name slug-matches the bound target', async () => { const { context, delegate } = makeContext(); - const boundTarget: AgentBuilderTarget = { + context.domainContext!.agentBuilderTarget = { agentId: 'agent-1', projectId: 'proj-1', name: 'Helper', + ref: 'helper', }; - context.domainContext!.agentBuilderTarget = boundTarget; - vi.mocked(delegate.streamBuild).mockResolvedValue(fakeStream([], 'Continuing.')); - - await runTool(context, { message: 'Add a tool', name: 'Helper' }); - - expect(findSessionAgentByName).not.toHaveBeenCalled(); - }); - - it("continues the bound build when the given name matches the bound target's name", async () => { - const { context, delegate } = makeContext(); - const boundTarget: AgentBuilderTarget = { - agentId: 'agent-1', - projectId: 'proj-1', - name: 'Helper', - }; - context.domainContext!.agentBuilderTarget = boundTarget; - vi.mocked(delegate.streamBuild).mockResolvedValue(fakeStream([], 'Continuing.')); - - await runTool(context, { message: 'Add a tool', name: 'Helper' }); - - expect(delegate.createAgent).not.toHaveBeenCalled(); - expect(delegate.streamBuild).toHaveBeenCalledWith('agent-1', 'Add a tool', { - threadId: 'ia-builder:thread-1:agent-1', - hostThreadId: 'thread-1', - runId: 'run-1', - modelConfig: context.modelId, - }); - }); - - it('continues the bound build when the given name matches case- and whitespace-insensitively', async () => { - const { context, delegate } = makeContext(); - const boundTarget: AgentBuilderTarget = { - agentId: 'agent-1', - projectId: 'proj-1', - name: 'Helper', - }; - context.domainContext!.agentBuilderTarget = boundTarget; vi.mocked(delegate.streamBuild).mockResolvedValue(fakeStream([], 'Continuing.')); await runTool(context, { message: 'Add a tool', name: 'helper' }); expect(delegate.createAgent).not.toHaveBeenCalled(); - expect(findSessionAgentByName).not.toHaveBeenCalled(); + expect(delegate.streamBuild).toHaveBeenCalledWith( + 'agent-1', + 'Add a tool', + expect.objectContaining({ threadId: 'ia-builder:thread-1:agent-1' }), + ); }); it('switches to a different existing agentId with deferred persistence', async () => { const { context, delegate } = makeContext(); context.domainContext!.agentBuilderTarget = { agentId: 'agent-1', projectId: 'proj-1' }; + vi.mocked(delegate.resolveAgentName).mockResolvedValue('Other Agent'); vi.mocked(delegate.streamBuild).mockResolvedValue(fakeStream([], 'Editing it.')); await runTool(context, { message: 'Now edit this one', agentId: 'agent-2' }); expect(delegate.createAgent).not.toHaveBeenCalled(); - expect(delegate.streamBuild).toHaveBeenCalledWith('agent-2', 'Now edit this one', { - threadId: 'ia-builder:thread-1:agent-2', - hostThreadId: 'thread-1', - runId: 'run-1', - modelConfig: context.modelId, - }); + expect(delegate.streamBuild).toHaveBeenCalledWith( + 'agent-2', + 'Now edit this one', + expect.objectContaining({ threadId: 'ia-builder:thread-1:agent-2' }), + ); expect(saveAgentBuilderTarget).toHaveBeenCalledWith(context.domainContext, { agentId: 'agent-2', projectId: 'proj-1', + name: 'Other Agent', + ref: 'other-agent', }); }); @@ -1034,59 +1148,54 @@ describe('build-agent tool', () => { await runTool(context, { message: 'Add a tool', agentId: 'agent-1' }); expect(saveAgentBuilderTarget).not.toHaveBeenCalled(); - expect(delegate.streamBuild).toHaveBeenCalledWith('agent-1', 'Add a tool', { - threadId: 'ia-builder:thread-1:agent-1', - hostThreadId: 'thread-1', - runId: 'run-1', - modelConfig: context.modelId, - }); - }); - - it('creates a fresh agent instead of switching back when createNew is set and the name matches a session agent', async () => { - const { context, delegate } = makeContext(); - const boundTarget: AgentBuilderTarget = { - agentId: 'agent-1', - projectId: 'proj-1', - name: 'Platform Cycle Tracker', - }; - context.domainContext!.agentBuilderTarget = boundTarget; - vi.mocked(findSessionAgentByName).mockResolvedValue(boundTarget); - vi.mocked(delegate.createAgent).mockResolvedValue({ - agentId: 'agent-3', - projectId: 'proj-1', - }); - vi.mocked(delegate.streamBuild).mockResolvedValue(fakeStream([], 'Created it.')); - - await runTool(context, { - message: 'Build another tracker', - name: 'Platform Cycle Tracker', - createNew: true, - }); - - expect(findSessionAgentByName).not.toHaveBeenCalled(); - expect(delegate.createAgent).toHaveBeenCalledWith('Platform Cycle Tracker'); expect(delegate.streamBuild).toHaveBeenCalledWith( - 'agent-3', - 'Build another tracker', - expect.objectContaining({ threadId: 'ia-builder:thread-1:agent-3' }), + 'agent-1', + 'Add a tool', + expect.objectContaining({ threadId: 'ia-builder:thread-1:agent-1' }), ); }); - it.each([ - { agentId: 'agent-1', createNew: true, message: 'Build it' }, - { createNew: true, message: 'Build it' }, - ])('rejects createNew when combined with agentId or missing name', async (input) => { + it('replays an identical create call after a cancelled turn without creating a second agent', async () => { const { context, delegate } = makeContext(); - - const result = await runTool(context, input); - - expect(result).toEqual({ - ok: false, - error: - 'createNew requires `name` and cannot be combined with `agentId` — pass `name` only to create a new agent.', + const controller = new AbortController(); + context.abortSignal = controller.signal; + vi.mocked(delegate.createAgent).mockResolvedValue({ + agentId: 'agent-1', + projectId: 'proj-1', }); - expect(delegate.createAgent).not.toHaveBeenCalled(); - expect(delegate.streamBuild).not.toHaveBeenCalled(); + vi.mocked(delegate.streamBuild).mockResolvedValue({ + fullStream: (async function* () { + await Promise.resolve(); + controller.abort(); + yield finishChunk(); + })(), + text: Promise.resolve(''), + }); + + await expect( + runTool(context, { message: 'Build it', name: 'Support Triage' }), + ).rejects.toMatchObject({ name: 'AbortError' }); + expect(delegate.createAgent).toHaveBeenCalledTimes(1); + + // Registry would have the key after the immediate create-path bind; model + // the post-cancel follow-up finding it and continuing. + vi.mocked(getSessionAgentByRef).mockResolvedValue({ + agentId: 'agent-1', + projectId: 'proj-1', + name: 'Support Triage', + ref: 'support-triage', + }); + context.abortSignal = new AbortController().signal; + vi.mocked(delegate.streamBuild).mockResolvedValue(fakeStream([], 'Continuing.')); + + await runTool(context, { message: 'Build it', name: 'Support Triage' }); + + expect(delegate.createAgent).toHaveBeenCalledTimes(1); + expect(delegate.streamBuild).toHaveBeenLastCalledWith( + 'agent-1', + 'Build it', + expect.objectContaining({ threadId: 'ia-builder:thread-1:agent-1' }), + ); }); }); @@ -1238,6 +1347,7 @@ describe('build-agent tool', () => { hostThreadId: 'thread-1', runId: 'run-1', modelConfig: context.modelId, + abortSignal: context.abortSignal, }); expect(delegate.resumeBuild).toHaveBeenCalledWith( 'agent-1', @@ -1247,6 +1357,7 @@ describe('build-agent tool', () => { hostThreadId: 'thread-1', runId: 'run-1', modelConfig: context.modelId, + abortSignal: context.abortSignal, }, ); expect(result).toEqual({ @@ -1322,6 +1433,7 @@ describe('build-agent tool', () => { hostThreadId: 'thread-1', runId: 'run-1', modelConfig: context.modelId, + abortSignal: context.abortSignal, }); expect(delegate.resumeBuild).toHaveBeenCalledWith( 'agent-1', @@ -1331,6 +1443,7 @@ describe('build-agent tool', () => { hostThreadId: 'thread-1', runId: 'run-1', modelConfig: context.modelId, + abortSignal: context.abortSignal, }, ); }); @@ -1364,6 +1477,7 @@ describe('build-agent tool', () => { hostThreadId: 'thread-1', runId: 'run-1', modelConfig: context.modelId, + abortSignal: context.abortSignal, }, ); }); diff --git a/packages/@n8n/instance-ai/src/tools/orchestration/agent-target-binding.ts b/packages/@n8n/instance-ai/src/tools/orchestration/agent-target-binding.ts index 8078c094d3a..ef96493c0af 100644 --- a/packages/@n8n/instance-ai/src/tools/orchestration/agent-target-binding.ts +++ b/packages/@n8n/instance-ai/src/tools/orchestration/agent-target-binding.ts @@ -1,16 +1,10 @@ /** * Thread-persisted agent-builder target binding. Mirrors the workflow source - * file bindings, but as a single thread-level record: one ACTIVE agent - * target per thread (the most recently created or explicitly targeted agent - * — `build-agent` calls with `name`/`agentId` rebind it), matching - * `context.agentBuilderTarget` semantics. Persisting the target in thread - * metadata lets follow-up turns keep editing the same agent instead of - * creating a new one. - * - * Alongside the single active target, a session registry of every agent - * targeted in this conversation is also kept in thread metadata. This lets - * `build-agent` recognize a `name` that was already built/targeted earlier - * in the conversation and switch back to it instead of creating a duplicate. + * file bindings: one ACTIVE agent target per thread (the most recently created + * or explicitly targeted agent), plus a session registry of every agent + * targeted in this conversation keyed by a model-authored ref. Persisting the + * target in thread metadata lets follow-up turns keep editing the same agent + * instead of creating a new one — including after a cancelled build. */ import { z } from 'zod'; @@ -29,18 +23,39 @@ const agentBuilderTargetSchema = z.object({ projectId: z.string(), /** Agent display name when known — lets the FE label the agent artifact. */ name: z.string().optional(), + /** + * Model-authored addressing key, and the registry's key. Optional: the + * editor/preview handoffs seed a target from an agentId alone, and active + * bindings persisted before this field existed carry none. + */ + ref: z.string().optional(), }); export type AgentBuilderTarget = z.infer; -/** Ordered oldest-first, most recently saved last; deduped by `agentId`. */ -const agentBuilderTargetRegistrySchema = z.array(agentBuilderTargetSchema); +const agentBuilderTargetRegistrySchema = z.record(z.string(), agentBuilderTargetSchema); + +/** Normalize an addressing key so "Support Triage", "support_triage" and + * "Support-Triage" all address the same agent. Unicode letters/digits are kept + * so a non-Latin agent name still yields a usable key. */ +export function normalizeAgentRef(value: string): string { + return value + .trim() + .toLowerCase() + .replace(/[^\p{L}\p{N}]+/gu, '-') + .replace(/^-+|-+$/g, ''); +} function parseTarget(raw: unknown): AgentBuilderTarget | undefined { const parsed = agentBuilderTargetSchema.safeParse(raw); return parsed.success ? parsed.data : undefined; } +function parseRegistry(raw: unknown): Record { + const parsed = agentBuilderTargetRegistrySchema.safeParse(raw); + return parsed.success ? parsed.data : {}; +} + async function readThreadTarget( context: InstanceAiContext, ): Promise { @@ -92,17 +107,32 @@ export async function saveAgentBuilderTarget( await patchThread(context.threadMemory, { threadId: context.threadId, update: ({ metadata = {} }) => { - const parsed = agentBuilderTargetRegistrySchema.safeParse(metadata[REGISTRY_METADATA_KEY]); - const existingRegistry = parsed.success ? parsed.data : []; - const previous = existingRegistry.find((entry) => entry.agentId === target.agentId); - // An agentId-path save carries no name; keep the name recorded when the - // agent was created so switch-back-by-name keeps working. - const entry: AgentBuilderTarget = - target.name === undefined && previous?.name !== undefined - ? { ...target, name: previous.name } - : target; - const registry = existingRegistry.filter((e) => e.agentId !== target.agentId); - registry.push(entry); + const existingRegistry = parseRegistry(metadata[REGISTRY_METADATA_KEY]); + const previousByAgentId = Object.values(existingRegistry).find( + (entry) => entry.agentId === target.agentId, + ); + // An agentId-path / display-name refresh may omit name or ref; keep the + // values recorded when the agent was first addressed so lookup and the + // FE artifact label stay correct. + const entry: AgentBuilderTarget = { + ...target, + ...(target.name === undefined && previousByAgentId?.name !== undefined + ? { name: previousByAgentId.name } + : {}), + ...(target.ref === undefined && previousByAgentId?.ref !== undefined + ? { ref: previousByAgentId.ref } + : {}), + }; + const registry = { ...existingRegistry }; + if (entry.ref) { + const normalized = normalizeAgentRef(entry.ref); + // Drop any prior keys that pointed at this agentId so a changed + // addressing key doesn't leave a stale alias. + for (const [key, existing] of Object.entries(registry)) { + if (existing.agentId === entry.agentId) delete registry[key]; + } + registry[normalized] = { ...entry, ref: normalized }; + } return { metadata: { ...metadata, @@ -117,23 +147,14 @@ export async function saveAgentBuilderTarget( }); } -/** Trimmed, case-insensitive comparison — the orchestrator LLM may paraphrase - * an agent name's casing/whitespace, and that must not create a duplicate. */ -export function agentNamesMatch(a: string | undefined, b: string | undefined): boolean { - if (a === undefined || b === undefined) return false; - return a.trim().toLowerCase() === b.trim().toLowerCase(); -} - /** - * Find an agent already targeted in this conversation by its display name - * (most recently targeted wins on duplicates). Lets `build-agent` switch - * back to a previously built agent by name instead of creating a duplicate. + * Find an agent already targeted in this conversation by its addressing key. * Best-effort: returns undefined without thread persistence or on a - * malformed registry. + * malformed/empty registry. */ -export async function findSessionAgentByName( +export async function getSessionAgentByRef( context: InstanceAiContext, - name: string, + ref: string, ): Promise { if (!context.threadMemory || !context.threadId) return undefined; @@ -142,13 +163,6 @@ export async function findSessionAgentByName( // prior targets. Let a `getThread` rejection propagate though (AGENT-353): // silently returning undefined there would cause a duplicate agent. const thread = await getThread(context.threadMemory, context.threadId); - const parsed = agentBuilderTargetRegistrySchema.safeParse( - thread?.metadata?.[REGISTRY_METADATA_KEY], - ); - const registry = parsed.success ? parsed.data : []; - - for (let i = registry.length - 1; i >= 0; i--) { - if (agentNamesMatch(registry[i].name, name)) return registry[i]; - } - return undefined; + const registry = parseRegistry(thread?.metadata?.[REGISTRY_METADATA_KEY]); + return registry[normalizeAgentRef(ref)]; } diff --git a/packages/@n8n/instance-ai/src/tools/orchestration/build-agent.tool.ts b/packages/@n8n/instance-ai/src/tools/orchestration/build-agent.tool.ts index 7d8ca61f59f..6ba781cb358 100644 --- a/packages/@n8n/instance-ai/src/tools/orchestration/build-agent.tool.ts +++ b/packages/@n8n/instance-ai/src/tools/orchestration/build-agent.tool.ts @@ -23,7 +23,7 @@ * builder UI — it is a private sub-agent conversation. */ import type { InterruptibleToolContext } from '@n8n/agents'; -import { Tool } from '@n8n/agents'; +import { createAbortError, Tool } from '@n8n/agents'; import { BUILDER_CHECKPOINT_UNAVAILABLE_CODE, BUILDER_NOT_CONFIGURED_CODE, @@ -39,8 +39,8 @@ import { nanoid } from 'nanoid'; import { z } from 'zod'; import { - agentNamesMatch, - findSessionAgentByName, + getSessionAgentByRef, + normalizeAgentRef, resolveAgentBuilderTarget, saveAgentBuilderTarget, type AgentBuilderTarget, @@ -63,6 +63,7 @@ import { failTraceRun, finishTraceRun, startSubAgentTrace, withTraceRun } from ' const BUILDER_SUB_AGENT_ROLE = 'agent-builder'; const BUILDER_SUB_AGENT_KIND = 'agent-builder'; +const BUILDER_RUN_CANCELLED_MESSAGE = 'The agent builder run was cancelled.'; function getErrorCode(error: unknown): string | undefined { if (!isRecord(error)) return undefined; @@ -128,7 +129,7 @@ function buildOutboundMessage(message: string, workflowContext?: SessionWorkflow } /** Builder sessions are keyed per assistant thread + target agent; the resume - * leg must reconstruct this byte-identically after a restart. */ + * leg must reconstruct the same `threadId` byte-identically after a restart. */ function builderSessionFor(context: OrchestrationContext, agentId: string) { const telemetry = context.tracing?.getTelemetry?.({ agentRole: BUILDER_SUB_AGENT_ROLE, @@ -145,6 +146,7 @@ function builderSessionFor(context: OrchestrationContext, agentId: string) { ...(context.tracing?.onMemoryTaskEvent ? { memoryTaskObserver: context.tracing.onMemoryTaskEvent } : {}), + abortSignal: context.abortSignal, }; } @@ -161,36 +163,33 @@ const buildAgentInputSchema = z.object({ 'see this chat — include every requirement, decision, and user answer already ' + 'gathered in this conversation, not just the latest message.', ), + agentRef: z + .string() + .optional() + .describe( + 'Short stable key you choose once for an agent in this conversation and repeat on ' + + 'every later call for that same agent (like a workflow source filePath). Prefer a ' + + 'slug of the display name. A repeated key continues that agent; a fresh key creates ' + + 'a new one. Omit on follow-ups for the current agent when neither switching nor ' + + 'creating — the active target is used. When omitted on a create/switch call, the ' + + 'key is derived from `name`.', + ), name: z .string() .optional() .describe( - 'Agent name. A name matching an agent already built in this conversation switches back ' + - 'to that agent; a new name creates a new agent and makes it the active target. Omit on ' + - 'follow-up calls for the current agent. Combine with `createNew: true` to force creating ' + - 'a fresh agent when the name matches one built earlier this conversation.', - ), - createNew: z - .boolean() - .optional() - .describe( - 'Set true when the user asks to create a brand-new agent. Bypasses the same-name ' + - 'switch-back: with createNew, `name` always creates a fresh agent even if that name ' + - 'matches one built earlier in this conversation. Requires `name`; never combine with ' + - '`agentId`. Omit for edits, follow-ups, and switch-backs.', + 'Display name for a new agent (required when creating). Also used as the addressing ' + + 'key when `agentRef` is omitted. Omit on follow-up calls for the current agent.', ), agentId: z .string() .optional() .describe( - 'Existing agent id to edit — use the `agentId` returned by earlier build-agent ' + - 'results. Pass to start editing that agent or to switch the active build target; ' + - 'omit on follow-up calls. Only pass this when the user explicitly wants to change ' + - 'that specific existing agent. NEVER pass it for a request to build/create a NEW ' + - 'agent — even if an agent with the same or a similar name already exists in the ' + - 'project (duplicate names are allowed). Agents the request merely references — as ' + - 'sub-agents, delegation targets, or examples — are not the build target: mention ' + - 'them in `message` instead.', + 'Existing agent id to adopt — use when editing an agent that was not built in this ' + + 'conversation (e.g. from the project list). Once adopted, omit on retries and prefer ' + + '`agentRef`. NEVER pass for a request to build a NEW agent. Agents the request merely ' + + 'references — as sub-agents, delegation targets, or examples — are not the build ' + + 'target: mention them in `message` instead.', ), workflowContext: z .array(z.object({ id: z.string(), name: z.string(), description: z.string().optional() })) @@ -207,7 +206,13 @@ const buildAgentOutputSchema = z.object({ .string() .optional() .describe( - 'Id of the agent this turn targeted. Record it and pass it as `agentId` when switching back to this agent later.', + 'Id of the agent this turn targeted. Prefer `agentRef` for follow-ups in this conversation; use `agentId` when the ref is unknown.', + ), + agentRef: z + .string() + .optional() + .describe( + 'Addressing key for this agent in this conversation. Pass it back as `agentRef` on later calls for the same agent.', ), agentName: z.string().optional().describe('Display name of the targeted agent, when known.'), answers: z @@ -228,7 +233,12 @@ const builderCheckpointRefSchema = z.object({ configUpdated: z.boolean(), /** Target the suspended build belongs to; optional for checkpoints persisted before this field existed. */ target: z - .object({ agentId: z.string(), projectId: z.string(), name: z.string().optional() }) + .object({ + agentId: z.string(), + projectId: z.string(), + name: z.string().optional(), + ref: z.string().optional(), + }) .optional(), }); @@ -318,7 +328,19 @@ function publishAgentBuilderFailure( return message; } -/** Publish the terminal `agent-completed` event and map the result to the tool output. */ +/** Publish the terminal `agent-completed` event for a stopped builder turn: no + * `error`, so the tree stays quiet and the run-level stopped indicator speaks. */ +function publishAgentBuilderCancelled(context: OrchestrationContext, builderAgentId: string): void { + context.eventBus.publish(context.threadId, { + type: 'agent-completed', + runId: context.runId, + agentId: builderAgentId, + payload: { role: BUILDER_SUB_AGENT_ROLE, result: '', status: 'cancelled' }, + }); +} + +/** Publish the terminal `agent-completed` event and map the result to the tool output. + * A cancelled turn is intercepted by the caller, so that status never arrives here. */ async function finishTurn( context: OrchestrationContext, builderAgentId: string, @@ -349,9 +371,17 @@ async function finishTurn( } /** Target identity stamped on every output of a dispatched builder turn so the - * orchestrator learns the agentId and can switch back by id instead of name. */ -function targetIdentity(target: AgentBuilderTarget): { agentId: string; agentName?: string } { - return { agentId: target.agentId, ...(target.name ? { agentName: target.name } : {}) }; + * orchestrator learns the addressing key (and agentId as a fallback). */ +function targetIdentity(target: AgentBuilderTarget): { + agentId: string; + agentRef?: string; + agentName?: string; +} { + return { + agentId: target.agentId, + ...(target.ref ? { agentRef: target.ref } : {}), + ...(target.name ? { agentName: target.name } : {}), + }; } /** @@ -437,12 +467,21 @@ async function runBuilderConsumeLoop(params: { await onSettled?.(); trackConfigMutations(context, target.agentId, result.workSummary); + if (result.status === 'cancelled') { + const cancelled = createAbortError(BUILDER_RUN_CANCELLED_MESSAGE); + publishAgentBuilderCancelled(context, builderAgentId); + await failTraceRun(context, traceRun, cancelled); + await context.claimSubAgentUsage?.(dedupeBase, result.usage?.usage ?? [], result.status); + throw cancelled; + } + // The builder names (and renames) the target agent via its config tools, so // the orchestrator-supplied name can be missing or stale by the time the // turn settles. Refresh it so the tool output (`targetIdentity`), the // republished agent-spawned event, and the thread binding all carry the // agent's real display name. Best-effort: a stale title is cosmetic and - // must not fail an otherwise-successful turn. + // must not fail an otherwise-successful turn. Skipped on cancel — abort + // should not wait on display-name I/O. try { const freshName = await delegate.resolveAgentName(target.agentId); if (freshName && freshName !== target.name) { @@ -523,6 +562,7 @@ async function runBuilderConsumeLoop(params: { agentId: target.agentId, projectId: target.projectId, ...(target.name ? { name: target.name } : {}), + ...(target.ref ? { ref: target.ref } : {}), }, }, }); @@ -639,45 +679,43 @@ type TargetResolution = | { ok: true; target: AgentBuilderTarget; bindAfterTurn: boolean; mode: 'create' | 'edit' } | { ok: false; error: string }; -const NO_TARGET_INPUT_ERROR = 'Pass name to create a new agent or agentId to edit an existing one.'; -const CREATE_NEW_INPUT_ERROR = - 'createNew requires `name` and cannot be combined with `agentId` — pass `name` only to create a new agent.'; +const NO_TARGET_INPUT_ERROR = + 'Pass `name` (and optionally `agentRef`) to create a new agent, `agentId` to adopt an existing one, or omit both to continue the current agent.'; +const UNKNOWN_REF_ERROR = + 'Unknown `agentRef`. Pass `name` to create a new agent under that key, or `agentId` to adopt an existing agent.'; const AGENT_ID_NEEDS_PROJECT_ERROR = 'Cannot bind to agentId without an active project context. Start this conversation from within a project.'; -function buildAgentTargetNameMismatchError( +/** Best-effort display-name lookup so the first agent-spawned event can label the + * artifact; a lookup failure must not fail the turn. */ +async function resolveAgentNameSafely( + delegate: InstanceAiBuilderDelegate, agentId: string, - realName: string, - passedName: string, -): string { +): Promise { + try { + return await delegate.resolveAgentName(agentId); + } catch { + return undefined; + } +} + +function agentRefConflictError(ref: string, boundAgentId: string, passedAgentId: string): string { return ( - `Agent ${agentId} is named "${realName}", but name "${passedName}" was passed. ` + - `To create a new agent named "${passedName}", pass \`name\` only (no \`agentId\`). ` + - `To edit "${realName}", pass \`agentId\` only and put any rename instruction in \`message\`.` + `\`agentRef\` "${ref}" is already bound to agent ${boundAgentId} in this conversation, ` + + `but \`agentId\` ${passedAgentId} was passed. Continue the bound agent (omit \`agentId\`, ` + + 'or pass its id), or pick a different `agentRef` for a new agent.' ); } -function rejectAgentTargetNameMismatch( - agentId: string, - realName: string | undefined, - passedName: string | undefined, -): TargetResolution | undefined { - if (!passedName || !realName || agentNamesMatch(passedName, realName)) { - return undefined; - } - return { ok: false, error: buildAgentTargetNameMismatchError(agentId, realName, passedName) }; -} - /** - * Resolve which agent this call should build/edit. A bound target stays - * active by default; passing `name` or `agentId` can create a new target or - * switch to a different existing one. `agentId` wins when both are given. + * Resolve which agent this call should build/edit. Identity is keyed by + * `slug(agentRef ?? name)` in the session registry — a repeated key continues, + * an unknown key creates (with `name`) or adopts (with `agentId`). A bound + * target stays active when neither key nor id is given. * agentId-path binds are always deferred (`bindAfterTurn: true`) — persisting * before the builder run settles would let a hallucinated/forbidden/missing - * agentId permanently poison the thread (no unbind path exists). A name-path - * switch-back to a session-registry agent is deferred for the same reason; - * a name-path create binds immediately since `delegate.createAgent` already - * proves the agent exists. + * agentId permanently poison the thread (no unbind path exists). A create + * binds immediately since `delegate.createAgent` already proves the agent exists. */ async function resolveTargetForCall( domainContext: InstanceAiContext, @@ -685,69 +723,124 @@ async function resolveTargetForCall( input: z.infer, boundTarget: AgentBuilderTarget | undefined, ): Promise { - if (input.createNew && (input.agentId || !input.name)) { - return { ok: false, error: CREATE_NEW_INPUT_ERROR }; + const keySource = input.agentRef ?? input.name; + const key = keySource ? normalizeAgentRef(keySource) : undefined; + + if (key) { + const sessionAgent = await getSessionAgentByRef(domainContext, key); + if (sessionAgent) { + if (input.agentId && input.agentId !== sessionAgent.agentId) { + return { + ok: false, + error: agentRefConflictError(key, sessionAgent.agentId, input.agentId), + }; + } + const target: AgentBuilderTarget = { + ...sessionAgent, + ref: key, + ...(input.name ? { name: input.name } : {}), + }; + // Same agent as the active binding — no re-persist needed. + if (boundTarget && boundTarget.agentId === target.agentId) { + return { + ok: true, + target: { ...boundTarget, ...target }, + bindAfterTurn: false, + mode: 'edit', + }; + } + // Switch-back: deferred so a deleted-since-registry agent can't clobber + // the current binding on a failed turn. + return { ok: true, target, bindAfterTurn: true, mode: 'edit' }; + } + + // Active target already addresses this key (e.g. just created this turn, + // or a handoff whose registry row isn't available) — continue without + // creating a duplicate. + const boundKey = boundTarget?.ref + ? normalizeAgentRef(boundTarget.ref) + : boundTarget?.name + ? normalizeAgentRef(boundTarget.name) + : undefined; + if (boundTarget && boundKey === key) { + if (input.agentId && input.agentId !== boundTarget.agentId) { + return { + ok: false, + error: agentRefConflictError(key, boundTarget.agentId, input.agentId), + }; + } + return { + ok: true, + target: { ...boundTarget, ref: key, ...(input.name ? { name: input.name } : {}) }, + bindAfterTurn: false, + mode: 'edit', + }; + } + + if (input.agentId) { + if (boundTarget && input.agentId === boundTarget.agentId) { + return { + ok: true, + target: { ...boundTarget, ref: key, ...(input.name ? { name: input.name } : {}) }, + bindAfterTurn: false, + mode: 'edit', + }; + } + if (!domainContext.projectId) { + return { ok: false, error: AGENT_ID_NEEDS_PROJECT_ERROR }; + } + const name = input.name ?? (await resolveAgentNameSafely(delegate, input.agentId)); + return { + ok: true, + target: { + agentId: input.agentId, + projectId: domainContext.projectId, + ref: key, + ...(name ? { name } : {}), + }, + bindAfterTurn: true, + mode: 'edit', + }; + } + + if (input.name) { + const created = await delegate.createAgent(input.name); + const target: AgentBuilderTarget = { + agentId: created.agentId, + projectId: created.projectId, + name: input.name, + ref: key, + }; + domainContext.agentBuilderTarget = target; + await saveAgentBuilderTarget(domainContext, target); + return { ok: true, target, bindAfterTurn: false, mode: 'create' }; + } + + return { ok: false, error: UNKNOWN_REF_ERROR }; } + // No addressing key (`name` always produces one) — agentId alone adopts, + // otherwise continue the bound target. if (input.agentId) { if (boundTarget && input.agentId === boundTarget.agentId) { - const mismatch = rejectAgentTargetNameMismatch(input.agentId, boundTarget.name, input.name); - if (mismatch) return mismatch; return { ok: true, target: boundTarget, bindAfterTurn: false, mode: 'edit' }; } if (!domainContext.projectId) { return { ok: false, error: AGENT_ID_NEEDS_PROJECT_ERROR }; } - // Best-effort name lookup so the first agent-spawned event already labels - // the artifact with the existing agent's display name. - let name: string | undefined; - try { - name = await delegate.resolveAgentName(input.agentId); - } catch { - name = undefined; - } - const mismatch = rejectAgentTargetNameMismatch(input.agentId, name, input.name); - if (mismatch) return mismatch; + const name = await resolveAgentNameSafely(delegate, input.agentId); return { ok: true, target: { agentId: input.agentId, projectId: domainContext.projectId, - ...(name ? { name } : {}), + ...(name ? { name, ref: normalizeAgentRef(name) } : {}), }, bindAfterTurn: true, mode: 'edit', }; } - if (input.name) { - if (!input.createNew) { - // Guards against the orchestrator redundantly repeating `name` on a - // follow-up call for the agent already being built. - if (boundTarget && agentNamesMatch(input.name, boundTarget.name)) { - return { ok: true, target: boundTarget, bindAfterTurn: false, mode: 'edit' }; - } - // A name matching an agent already built/targeted this conversation is a - // switch-back, not a creation — the duplicate-agent failure mode this - // registry exists to prevent. Deferred persist like the agentId path: the - // agent may have been deleted since, and a failed turn must not clobber - // the current binding. - const sessionAgent = await findSessionAgentByName(domainContext, input.name); - if (sessionAgent) { - return { ok: true, target: sessionAgent, bindAfterTurn: true, mode: 'edit' }; - } - } - const created = await delegate.createAgent(input.name); - const target: AgentBuilderTarget = { - agentId: created.agentId, - projectId: created.projectId, - name: input.name, - }; - domainContext.agentBuilderTarget = target; - await saveAgentBuilderTarget(domainContext, target); - return { ok: true, target, bindAfterTurn: false, mode: 'create' }; - } - if (boundTarget) { return { ok: true, target: boundTarget, bindAfterTurn: false, mode: 'edit' }; } @@ -765,29 +858,30 @@ export function createBuildAgentTool(context: OrchestrationContext) { 'tools. If a workflow build seems to need a utility tool the workspace does not ' + 'provide, ask the user or use a placeholder; do not route around that by calling ' + '`build-agent`. ' + - 'Pass `name` to start a new agent or `agentId` to edit an existing one; calls ' + - 'without either keep editing the current agent. Create vs. edit follows user ' + - 'intent, not name collisions: a request to build a NEW agent always passes `name` ' + - 'plus `createNew: true` — never `agentId` — even when a same-named agent already exists in the ' + - 'project. To build ANOTHER agent in the same ' + - 'conversation, pass its `name` or `agentId` — a name matching an agent already built ' + - 'in this conversation switches back to it (unless `createNew: true` is passed); an unmatched name creates a new agent and ' + - 'switches the active target. The builder can also publish or unpublish the target ' + + 'Address agents in this conversation with `agentRef` (a short stable key you choose, ' + + 'like a workflow `filePath`). Pass `name` with a fresh key to create; repeat the same ' + + 'key to continue. Calls with neither key nor `agentId` keep editing the current agent. ' + + 'To build ANOTHER agent in the same conversation, use a different `agentRef` (and ' + + '`name`). To edit an agent that was not built here, pass `agentId` (optionally with ' + + '`agentRef`) once to adopt it. The builder can also publish or unpublish the target ' + 'agent when the user asks to publish, activate, make it live/usable, or unpublish — ' + 'forward that intent in `message`; never tell the user to open the agent editor and ' + 'click Publish. When the builder needs user input (a choice, a ' + 'credential, or a chat channel), it surfaces automatically as an interactive card in ' + 'this chat — do not relay those questions yourself; this tool call resumes with the ' + 'user’s answer and returns the builder’s reply. Returns the builder’s reply, the ' + - 'target `agentId`, and whether it updated the agent config. Record the returned ' + - '`agentId` and prefer passing it as `agentId` when switching back to that agent — ' + - 'the `name` path is a fallback for when the id is unknown.', + 'target `agentRef`/`agentId`, and whether it updated the agent config. Prefer the ' + + 'returned `agentRef` on later calls for that agent.', ) .input(buildAgentInputSchema) .output(buildAgentOutputSchema) .suspend(buildAgentSuspendSchema) .resume(buildAgentResumeSchema) .handler(async (input: z.infer, ctx: BuildAgentToolContext) => { + if (context.abortSignal.aborted) { + throw createAbortError(BUILDER_RUN_CANCELLED_MESSAGE); + } + const domainContext = context.domainContext; const delegate = domainContext?.builderDelegate; if (!domainContext || !delegate) { diff --git a/packages/@n8n/instance-ai/src/tools/orchestration/verification/__tests__/prepare-run.test.ts b/packages/@n8n/instance-ai/src/tools/orchestration/verification/__tests__/prepare-run.test.ts index 241781371b9..125243d0e19 100644 --- a/packages/@n8n/instance-ai/src/tools/orchestration/verification/__tests__/prepare-run.test.ts +++ b/packages/@n8n/instance-ai/src/tools/orchestration/verification/__tests__/prepare-run.test.ts @@ -86,3 +86,41 @@ describe('prepareVerificationRun — halted wait gates', () => { expect(result.prepared.verificationPinData?.['Email Approval']).toEqual([]); }); }); + +describe('prepareVerificationRun — gate scripts', () => { + it('exposes the script that matches a halted gate', () => { + const script = { + nodeName: 'Email Approval', + cutEdge: { source: 'Revise', target: 'Format' }, + decisions: [{ label: 'approve', items: [{ data: { approved: true } }] }], + }; + const result = prepareVerificationRun( + makeBuildOutcome({ nodeSimulationPlan: [gateVerdict], waitGateScripts: [script] }), + undefined, + ); + + expect(result.kind).toBe('ready'); + if (result.kind !== 'ready') return; + expect(result.prepared.gateScript).toEqual(script); + }); + + it('ignores scripts that do not match a halted gate', () => { + const result = prepareVerificationRun( + makeBuildOutcome({ + nodeSimulationPlan: [plainVerdict], + waitGateScripts: [ + { + nodeName: 'Some Other Node', + cutEdge: { source: 'A', target: 'B' }, + decisions: [{ label: 'x', items: [{}] }], + }, + ], + }), + undefined, + ); + + expect(result.kind).toBe('ready'); + if (result.kind !== 'ready') return; + expect(result.prepared.gateScript).toBeUndefined(); + }); +}); diff --git a/packages/@n8n/instance-ai/src/tools/orchestration/verification/__tests__/scripted-gate-run.test.ts b/packages/@n8n/instance-ai/src/tools/orchestration/verification/__tests__/scripted-gate-run.test.ts new file mode 100644 index 00000000000..4b5215e30ab --- /dev/null +++ b/packages/@n8n/instance-ai/src/tools/orchestration/verification/__tests__/scripted-gate-run.test.ts @@ -0,0 +1,164 @@ +import type { + WaitGateScript, + WorkflowBuildOutcome, +} from '../../../../workflow-loop/workflow-loop-state'; +import type { PreparedVerificationRun } from '../prepare-run'; +import { runScriptedGateVerification, type ScriptedGateRunArgs } from '../scripted-gate-run'; + +function makeBuildOutcome(overrides: Partial = {}): WorkflowBuildOutcome { + return { + workItemId: 'wi_1', + taskId: 'task_1', + workflowId: 'wf_1', + submitted: true, + triggerType: 'manual_or_testable', + needsUserInput: false, + summary: 'Built', + ...overrides, + }; +} + +const script: WaitGateScript = { + nodeName: 'Gate', + cutEdge: { source: 'Revise', target: 'Format' }, + decisions: [ + { label: 'approve', items: [{ data: { approved: true } }] }, + { label: 'decline', items: [{ data: { approved: false } }] }, + ], +}; + +const prepared: PreparedVerificationRun = { + verificationPinData: { Gate: [], Generate: [{ text: 'draft' }] }, + simulatedNodes: [ + { nodeName: 'Gate', reason: 'gate' }, + { nodeName: 'Generate', reason: 'no credentials' }, + ], + haltedGateNames: ['Gate'], + gateScript: script, +}; + +const approvePassResult = { + executionId: 'exec-approve', + status: 'success', + executedNodeNames: ['Trigger', 'Generate', 'Gate', 'Router', 'Publish'], + lastNodeExecuted: 'Publish', + data: { Publish: [{ ok: true }] }, +}; + +const declinePassResult = { + executionId: 'exec-decline', + status: 'success', + executedNodeNames: ['Trigger', 'Generate', 'Gate', 'Router', 'Revise'], + lastNodeExecuted: 'Revise', + data: { Revise: [{ text: 'revised' }] }, +}; + +function makeArgs(runMock: ReturnType): ScriptedGateRunArgs { + return { + script, + prepared, + executionService: { run: runMock } as unknown as ScriptedGateRunArgs['executionService'], + workflowId: 'wf_1', + timeout: 30_000, + buildOutcome: makeBuildOutcome({ + nodeSimulationPlan: [ + { + nodeName: 'Gate', + verdict: 'simulate', + reason: 'gate', + confidence: 'high', + source: 'deterministic', + haltBranch: true, + }, + ], + }), + stateBefore: undefined, + runId: 'run-1', + }; +} + +describe('runScriptedGateVerification', () => { + it('runs one pass per decision with the gate pin swapped and the loop edge cut', async () => { + const run = vi + .fn() + .mockResolvedValueOnce(approvePassResult) + .mockResolvedValueOnce(declinePassResult); + + const { result, analysis } = await runScriptedGateVerification(makeArgs(run)); + + expect(run).toHaveBeenCalledTimes(2); + type RunOptions = { + verificationPinData?: Record; + omitConnections?: Array<{ source: string; target: string }>; + }; + const optionsOfCall = (index: number): RunOptions => + (run.mock.calls[index] as unknown[])[2] as RunOptions; + const firstOptions = optionsOfCall(0); + const secondOptions = optionsOfCall(1); + expect(firstOptions.verificationPinData?.Gate).toEqual([{ data: { approved: true } }]); + expect(secondOptions.verificationPinData?.Gate).toEqual([{ data: { approved: false } }]); + // Base pins for other simulated nodes ride along on every pass. + expect(firstOptions.verificationPinData?.Generate).toEqual([{ text: 'draft' }]); + expect(firstOptions.omitConnections).toEqual([{ source: 'Revise', target: 'Format' }]); + + expect(analysis.success).toBe(true); + expect(analysis.nodesExecuted).toEqual(expect.arrayContaining(['Publish', 'Revise'])); + expect(analysis.coverageNote).toContain('"approve" ended at "Publish"'); + expect(analysis.coverageNote).toContain('"decline" ended at "Revise"'); + expect(analysis.coverageNote).toContain('"Revise" → "Format"'); + expect(result.executedNodeNames).toEqual(expect.arrayContaining(['Publish', 'Revise'])); + }); + + it('fails the merged analysis when any pass fails', async () => { + const run = vi + .fn() + .mockResolvedValueOnce(approvePassResult) + .mockResolvedValueOnce({ + ...declinePassResult, + status: 'error', + error: 'expression error in Revise', + }); + + const { analysis } = await runScriptedGateVerification(makeArgs(run)); + + expect(analysis.success).toBe(false); + expect(analysis.errorMessage).toContain('expression error'); + expect(analysis.remediation).toBeDefined(); + }); +}); + +describe('runScriptedGateVerification — merge consistency', () => { + it('normalizes the merged status when a pass fails on node errors despite engine success', async () => { + const run = vi + .fn() + .mockResolvedValueOnce(approvePassResult) + .mockResolvedValueOnce({ + ...declinePassResult, + status: 'success', + nodeErrors: [{ nodeName: 'Revise', message: 'expression error' }], + }); + + const { result, analysis } = await runScriptedGateVerification(makeArgs(run)); + + expect(analysis.success).toBe(false); + expect(result.status).toBe('error'); + }); + + it('discloses simulated nodes reached only in an earlier pass', async () => { + const run = vi + .fn() + .mockResolvedValueOnce(approvePassResult) + .mockResolvedValueOnce(declinePassResult); + + const args = makeArgs(run); + args.prepared = { + ...prepared, + simulatedNodes: [...prepared.simulatedNodes, { nodeName: 'Publish', reason: 'no creds' }], + }; + + const { analysis } = await runScriptedGateVerification(args); + + // Publish is reached only by the approve pass; the decline pass ran last. + expect(analysis.simulationNote).toContain('Publish'); + }); +}); diff --git a/packages/@n8n/instance-ai/src/tools/orchestration/verification/analyze-result.ts b/packages/@n8n/instance-ai/src/tools/orchestration/verification/analyze-result.ts index 4325d981749..dc67e9f8633 100644 --- a/packages/@n8n/instance-ai/src/tools/orchestration/verification/analyze-result.ts +++ b/packages/@n8n/instance-ai/src/tools/orchestration/verification/analyze-result.ts @@ -199,7 +199,7 @@ function classifyVerificationFailure( }); } -function buildSimulationNote( +export function buildSimulationNote( reachedSimulatedNodes: Array<{ nodeName: string; reason: string }>, planMissing: boolean, ): string | undefined { diff --git a/packages/@n8n/instance-ai/src/tools/orchestration/verification/prepare-run.ts b/packages/@n8n/instance-ai/src/tools/orchestration/verification/prepare-run.ts index 389e72778ec..1fca3aee419 100644 --- a/packages/@n8n/instance-ai/src/tools/orchestration/verification/prepare-run.ts +++ b/packages/@n8n/instance-ai/src/tools/orchestration/verification/prepare-run.ts @@ -1,12 +1,17 @@ import type { VerifyBuiltWorkflowOutput, VerifyToolInput } from './types'; import { createRemediation } from '../../../workflow-loop/remediation'; -import type { WorkflowBuildOutcome } from '../../../workflow-loop/workflow-loop-state'; +import type { + WaitGateScript, + WorkflowBuildOutcome, +} from '../../../workflow-loop/workflow-loop-state'; export interface PreparedVerificationRun { verificationPinData: Record | undefined; simulatedNodes: Array<{ nodeName: string; reason: string }>; /** Wait-gate nodes pinned with zero items — verification halts at these. */ haltedGateNames: string[]; + /** When set, verify runs one scripted pass per decision instead of halting. */ + gateScript?: WaitGateScript; } function getInvalidFixtureOverrideNodeNames( @@ -61,10 +66,15 @@ function buildVerificationPinData( } } + const gateScript = (buildOutcome.waitGateScripts ?? []).find((script) => + haltedGateNames.includes(script.nodeName), + ); + return { verificationPinData: Object.keys(merged).length > 0 ? merged : undefined, simulatedNodes, haltedGateNames, + ...(gateScript ? { gateScript } : {}), }; } @@ -83,9 +93,10 @@ export function prepareVerificationRun( if (haltedOverrideNodeNames.length > 0) { const guidance = `Node(s) ${haltedOverrideNodeNames.join(', ')} pause the workflow for a human decision and sit on a loop — ` + - 'verification always halts there and their output cannot be overridden: a canned response would ' + - 're-run the loop with the same answer forever. Treat reaching the gate as verified and tell the ' + - 'user the approval loop needs a manual end-to-end test.'; + 'their output cannot be overridden: a constant canned response would re-run the loop with the same ' + + 'answer forever. Verification drives such gates with scripted decisions where possible, or halts at ' + + 'them otherwise. Treat the scripted/halted result as authoritative and tell the user the approval ' + + 'loop needs a manual end-to-end test for final confirmation.'; const remediation = createRemediation({ category: 'blocked', shouldEdit: false, diff --git a/packages/@n8n/instance-ai/src/tools/orchestration/verification/scripted-gate-run.ts b/packages/@n8n/instance-ai/src/tools/orchestration/verification/scripted-gate-run.ts new file mode 100644 index 00000000000..9598e41b640 --- /dev/null +++ b/packages/@n8n/instance-ai/src/tools/orchestration/verification/scripted-gate-run.ts @@ -0,0 +1,155 @@ +/** + * Scripted wait-gate verification: one execution per gate decision, each on an + * ephemeral workflow copy with the script's loop edge removed so no decision + * can re-enter the gate. Pass results merge into a single verification + * analysis — success requires every pass to succeed, coverage is the union. + */ + +import { + analyzeVerificationResult, + buildSimulationNote, + type VerificationAnalysis, +} from './analyze-result'; +import type { PreparedVerificationRun } from './prepare-run'; +import type { ExecutionRunResult } from './types'; +import type { OrchestrationContext } from '../../../types'; +import type { + WaitGateScript, + WorkflowBuildOutcome, + WorkflowLoopState, +} from '../../../workflow-loop/workflow-loop-state'; + +type VerificationExecutionService = NonNullable< + OrchestrationContext['domainContext'] +>['executionService']; + +export interface ScriptedGateRunArgs { + script: WaitGateScript; + prepared: PreparedVerificationRun; + executionService: VerificationExecutionService; + workflowId: string; + inputData?: Record; + timeout?: number; + abortSignal?: AbortSignal; + buildOutcome: WorkflowBuildOutcome; + stateBefore: WorkflowLoopState | undefined; + runId: string; +} + +interface DecisionPass { + label: string; + result: ExecutionRunResult; + analysis: VerificationAnalysis; +} + +export async function runScriptedGateVerification( + args: ScriptedGateRunArgs, +): Promise<{ result: ExecutionRunResult; analysis: VerificationAnalysis }> { + const { script, prepared, executionService, workflowId, buildOutcome, stateBefore, runId } = args; + const basePins = prepared.verificationPinData ?? {}; + + const passes: DecisionPass[] = []; + for (const decision of script.decisions) { + const result = await executionService.run(workflowId, args.inputData, { + timeout: args.timeout, + verificationPinData: { ...basePins, [script.nodeName]: decision.items }, + omitConnections: [script.cutEdge], + isVerificationRun: true, + abortSignal: args.abortSignal, + }); + const analysis = analyzeVerificationResult({ + result, + buildOutcome, + simulatedNodes: prepared.simulatedNodes, + stateBefore, + runId, + }); + passes.push({ label: decision.label, result, analysis }); + } + + return { result: mergeResults(passes), analysis: mergeAnalyses(script, prepared, passes) }; +} + +function mergeResults(passes: DecisionPass[]): ExecutionRunResult { + const last = passes[passes.length - 1]; + const failing = passes.find((pass) => !pass.analysis.success); + const data: Record = {}; + const executedNodeNames = new Set(); + for (const pass of passes) { + Object.assign(data, pass.result.data ?? {}); + for (const name of pass.analysis.reachedNames) executedNodeNames.add(name); + } + + // A pass can fail on node errors while its engine status is still + // 'success' — normalize so the merged status never contradicts `success`. + const failingStatus = failing + ? failing.result.status === 'success' + ? 'error' + : failing.result.status + : undefined; + + return { + ...last.result, + status: failingStatus ?? last.result.status, + data: Object.keys(data).length > 0 ? data : undefined, + executedNodeNames: [...executedNodeNames], + error: failing?.result.error ?? failing?.analysis.errorMessage, + }; +} + +function mergeAnalyses( + script: WaitGateScript, + prepared: PreparedVerificationRun, + passes: DecisionPass[], +): VerificationAnalysis { + const reachedNames = new Set(); + for (const pass of passes) { + for (const name of pass.analysis.reachedNames) reachedNames.add(name); + } + const failing = passes.find((pass) => !pass.analysis.success); + const nodesNotReached = [ + ...new Set(passes.flatMap((pass) => pass.analysis.nodesNotReached)), + ].filter((name) => !reachedNames.has(name)); + const nodeErrors = passes.flatMap((pass) => + pass.analysis.nodeErrors.map((nodeError) => ({ + ...nodeError, + message: `[decision: ${pass.label}] ${nodeError.message ?? 'node execution failed'}`, + })), + ); + + const endings = passes + .map( + (pass) => + `"${pass.label}" ended at ${pass.result.lastNodeExecuted ? `"${pass.result.lastNodeExecuted}"` : 'an unknown node'}`, + ) + .join('; '); + const coverageNote = + `Scripted gate verification: ran ${String(passes.length)} decision pass(es) through ` + + `"${script.nodeName}" with loop edge "${script.cutEdge.source}" → "${script.cutEdge.target}" ` + + `removed so each pass is loop-safe (${endings}). The gate's send/wait behaviour itself was ` + + 'simulated — recommend a live end-to-end test for final confirmation.' + + (nodesNotReached.length > 0 + ? ` ${String(nodesNotReached.length)} planned node(s) not reached by any pass: ` + + `${nodesNotReached.join(', ')}. If a decision should route to them, inspect the ` + + 'branch wiring before editing.' + : ''); + + // Rebuild the note from the union — a node simulated only in an earlier + // pass must still be disclosed. + const reachedSimulatedNodes = prepared.simulatedNodes.filter((node) => + reachedNames.has(node.nodeName), + ); + + return { + success: passes.every((pass) => pass.analysis.success), + reachedNames, + reachedSimulatedNodes, + nodesNotReached, + remediation: failing?.analysis.remediation, + nodesExecuted: [...reachedNames], + simulationNote: buildSimulationNote(reachedSimulatedNodes, false), + coverageNote, + errorMessage: failing?.analysis.errorMessage, + nodeErrors, + }; +} diff --git a/packages/@n8n/instance-ai/src/tools/orchestration/verify-built-workflow.tool.ts b/packages/@n8n/instance-ai/src/tools/orchestration/verify-built-workflow.tool.ts index 9f61bab81bf..39aeef417a7 100644 --- a/packages/@n8n/instance-ai/src/tools/orchestration/verify-built-workflow.tool.ts +++ b/packages/@n8n/instance-ai/src/tools/orchestration/verify-built-workflow.tool.ts @@ -18,6 +18,7 @@ import { import { prepareVerificationRun } from './verification/prepare-run'; import { reconcileStaleCredentialPlan } from './verification/reconcile-plan'; import { resolveVerificationTarget } from './verification/resolve-target'; +import { runScriptedGateVerification } from './verification/scripted-gate-run'; import { executionNodeErrorSchema } from '../../workflow-loop/workflow-loop-state'; const DEFAULT_NODE_PREVIEW_CHARS = 600; @@ -157,24 +158,45 @@ export function createVerifyBuiltWorkflowTool(context: OrchestrationContext) { } const { prepared } = preparedResult; - const result = await target.domainContext.executionService.run( - workflowId, - resolvedInput.inputData, - { - timeout: resolvedInput.timeout, - verificationPinData: prepared.verificationPinData, - abortSignal: context.abortSignal, - }, - ); + // A scripted gate replaces the halt with one loop-safe pass per decision; + // otherwise run the single standard pass (halted gates pin zero items). + const { result, analysis } = prepared.gateScript + ? await runScriptedGateVerification({ + script: prepared.gateScript, + prepared, + executionService: target.domainContext.executionService, + workflowId, + inputData: resolvedInput.inputData, + timeout: resolvedInput.timeout, + abortSignal: context.abortSignal, + buildOutcome, + stateBefore: target.stateBefore, + runId: context.runId, + }) + : await (async () => { + const runResult = await target.domainContext.executionService.run( + workflowId, + resolvedInput.inputData, + { + timeout: resolvedInput.timeout, + verificationPinData: prepared.verificationPinData, + isVerificationRun: true, + abortSignal: context.abortSignal, + }, + ); + return { + result: runResult, + analysis: analyzeVerificationResult({ + result: runResult, + buildOutcome, + simulatedNodes: prepared.simulatedNodes, + haltedGateNames: prepared.haltedGateNames, + stateBefore: target.stateBefore, + runId: context.runId, + }), + }; + })(); - const analysis = analyzeVerificationResult({ - result, - buildOutcome, - simulatedNodes: prepared.simulatedNodes, - haltedGateNames: prepared.haltedGateNames, - stateBefore: target.stateBefore, - runId: context.runId, - }); await persistVerificationOutcome({ input: resolvedInput, context, diff --git a/packages/@n8n/instance-ai/src/tools/workflows/__tests__/build-workflow.tool.test.ts b/packages/@n8n/instance-ai/src/tools/workflows/__tests__/build-workflow.tool.test.ts index 0a8fb7c109f..575a57a5a49 100644 --- a/packages/@n8n/instance-ai/src/tools/workflows/__tests__/build-workflow.tool.test.ts +++ b/packages/@n8n/instance-ai/src/tools/workflows/__tests__/build-workflow.tool.test.ts @@ -27,7 +27,7 @@ vi.mock('../../../tracing/langsmith-tracing', async () => { }); vi.mock('../workflow-validation-warnings', () => ({ - partitionWarnings: vi.fn((warnings: unknown[]) => ({ errors: [], informational: warnings })), + partitionWarnings: vi.fn((warnings: unknown[]) => ({ blocking: [], informational: warnings })), })); const generatedWorkflow = { @@ -200,7 +200,7 @@ describe('createBuildWorkflowTool', () => { compiler: 'sandbox-tsx', }); vi.mocked(partitionWarnings).mockImplementation((warnings: ValidationWarning[]) => ({ - errors: [], + blocking: [], informational: warnings, })); vi.mocked(analyzeWorkflow).mockResolvedValue([]); @@ -1087,7 +1087,7 @@ describe('createBuildWorkflowTool', () => { compiler: 'sandbox-tsx', }); vi.mocked(partitionWarnings).mockReturnValueOnce({ - errors: [{ code: 'UNKNOWN_CONFIG_KEY', message: 'Unknown config key "recipient"' }], + blocking: [{ code: 'UNKNOWN_CONFIG_KEY', message: 'Unknown config key "recipient"' }], informational: [], }); @@ -1117,7 +1117,7 @@ describe('createBuildWorkflowTool', () => { compiler: 'sandbox-tsx' as const, }; const partitionedWarnings = { - errors: [{ code: 'UNKNOWN_CONFIG_KEY', message: 'Unknown config key "recipient"' }], + blocking: [{ code: 'UNKNOWN_CONFIG_KEY', message: 'Unknown config key "recipient"' }], informational: [], }; vi.mocked(compileWorkflowSource) @@ -1153,7 +1153,7 @@ describe('createBuildWorkflowTool', () => { compiler: 'sandbox-tsx' as const, }; const partitionedWarnings = { - errors: validationResult.warnings, + blocking: validationResult.warnings, informational: [], }; vi.mocked(compileWorkflowSource) diff --git a/packages/@n8n/instance-ai/src/tools/workflows/__tests__/plan-verification-simulation.test.ts b/packages/@n8n/instance-ai/src/tools/workflows/__tests__/plan-verification-simulation.test.ts index adaee3dd841..f23d6723f0e 100644 --- a/packages/@n8n/instance-ai/src/tools/workflows/__tests__/plan-verification-simulation.test.ts +++ b/packages/@n8n/instance-ai/src/tools/workflows/__tests__/plan-verification-simulation.test.ts @@ -428,3 +428,70 @@ describe('planVerificationSimulation — wait-gate halt verdicts', () => { ).toBeUndefined(); }); }); + +describe('planVerificationSimulation — wait-gate scripts', () => { + beforeEach(() => { + vi.clearAllMocks(); + mockGenerateFixtures.mockResolvedValue({}); + }); + + it('derives a decision script for a halted send-and-wait gate', async () => { + mockClassify.mockResolvedValue([ + { + nodeName: 'Email Approval', + verdict: 'simulate', + reason: 'Credentials are not configured for this node', + confidence: 'high', + source: 'deterministic', + }, + ]); + + const workflow = { + name: 'approval loop', + nodes: [ + { + id: 'id-0', + name: 'Every day', + type: 'n8n-nodes-base.scheduleTrigger', + typeVersion: 1, + position: [0, 0], + parameters: {}, + }, + { + id: 'id-1', + name: 'Email Approval', + type: 'n8n-nodes-base.gmail', + typeVersion: 2, + position: [100, 0], + parameters: { operation: 'sendAndWait', responseType: 'approval' }, + }, + { + id: 'id-2', + name: 'Revise', + type: 'n8n-nodes-base.set', + typeVersion: 1, + position: [200, 0], + parameters: {}, + }, + ], + connections: { + 'Every day': { main: [[{ node: 'Email Approval', type: 'main', index: 0 }]] }, + 'Email Approval': { main: [[{ node: 'Revise', type: 'main', index: 0 }]] }, + Revise: { main: [[{ node: 'Email Approval', type: 'main', index: 0 }]] }, + }, + } as unknown as WorkflowJSON; + + const { nodeSimulationPlan, waitGateScripts } = await planVerificationSimulation({ + workflow, + workflowId: 'wf-1', + }); + + expect(nodeSimulationPlan?.find((v) => v.nodeName === 'Email Approval')?.haltBranch).toBe(true); + expect(waitGateScripts).toHaveLength(1); + expect(waitGateScripts?.[0]).toMatchObject({ + nodeName: 'Email Approval', + cutEdge: { source: 'Revise', target: 'Email Approval' }, + }); + expect(waitGateScripts?.[0].decisions.map((d) => d.label)).toEqual(['approve', 'decline']); + }); +}); diff --git a/packages/@n8n/instance-ai/src/tools/workflows/__tests__/preserve-node-positions.test.ts b/packages/@n8n/instance-ai/src/tools/workflows/__tests__/preserve-node-positions.test.ts new file mode 100644 index 00000000000..78bff587f88 --- /dev/null +++ b/packages/@n8n/instance-ai/src/tools/workflows/__tests__/preserve-node-positions.test.ts @@ -0,0 +1,192 @@ +import type { NodeJSON, WorkflowJSON } from '@n8n/workflow-sdk'; + +import type { InstanceAiContext } from '../../../types'; +import { preserveExistingNodePositions } from '../preserve-node-positions'; + +const node = (name: string, position: [number, number], type = 'n8n-nodes-base.set'): NodeJSON => ({ + id: name.toLowerCase().replaceAll(' ', '-'), + name, + type, + typeVersion: 1, + position, + parameters: {}, +}); + +const sticky = (name: string, position: [number, number]): NodeJSON => + node(name, position, 'n8n-nodes-base.stickyNote'); + +const workflow = (nodes: NodeJSON[], connections: WorkflowJSON['connections'] = {}): WorkflowJSON => + ({ name: 'Workflow', nodes, connections }) as WorkflowJSON; + +/** `A → B` on the main output. */ +const wire = (from: string, to: string) => ({ + [from]: { main: [[{ node: to, type: 'main', index: 0 }]] }, +}); + +const contextReturning = (existing: WorkflowJSON) => + ({ + workflowService: { getAsWorkflowJSON: vi.fn().mockResolvedValue(existing) }, + }) as unknown as InstanceAiContext; + +const positionsByName = (json: WorkflowJSON): Record => + Object.fromEntries(json.nodes.map((n) => [n.name ?? '', n.position])); + +describe('preserveExistingNodePositions', () => { + describe('surviving nodes', () => { + it('restores every saved position when the build re-laid out the whole canvas', async () => { + // Two disconnected nodes parked to the right of the flow, which the layout + // engine treats as their own components and collapses onto its origin column. + const saved = workflow([ + node('Schedule Trigger', [320, 480]), + node('Settings', [528, 480]), + node('Notify owner', [4480, 800]), + node('Notify team', [4480, 592]), + sticky('Sticky Note', [1584, 160]), + ]); + const built = workflow( + [ + node('Schedule Trigger', [0, 0]), + node('Settings', [208, 0]), + node('Notify owner', [0, 300]), + node('Notify team', [0, 450]), + sticky('Sticky Note', [0, 600]), + ], + wire('Schedule Trigger', 'Settings'), + ); + + await preserveExistingNodePositions(built, 'wf-1', contextReturning(saved)); + + expect(positionsByName(built)).toEqual(positionsByName(saved)); + }); + + it('leaves positions untouched when the build already matched the saved canvas', async () => { + const saved = workflow([node('A', [320, 480]), node('B', [528, 480])]); + const built = workflow([node('A', [320, 480]), node('B', [528, 480])]); + + await preserveExistingNodePositions(built, 'wf-1', contextReturning(saved)); + + expect(positionsByName(built)).toEqual({ A: [320, 480], B: [528, 480] }); + }); + + it('ignores a workflow whose nodes were all renamed, leaving the fresh layout', async () => { + const saved = workflow([node('Old name', [4000, 900])]); + const built = workflow([node('New name', [0, 0])]); + + await preserveExistingNodePositions(built, 'wf-1', contextReturning(saved)); + + expect(positionsByName(built)).toEqual({ 'New name': [0, 0] }); + }); + }); + + describe('added nodes', () => { + it('translates a node added by a full re-layout into the saved frame', async () => { + const saved = workflow([node('Schedule Trigger', [320, 480]), node('Settings', [528, 480])]); + const built = workflow( + [ + node('Schedule Trigger', [0, 0]), + node('Settings', [208, 0]), + node('Read Sheet', [416, 0]), + ], + { ...wire('Schedule Trigger', 'Settings'), ...wire('Settings', 'Read Sheet') }, + ); + + await preserveExistingNodePositions(built, 'wf-1', contextReturning(saved)); + + // Both survivors shifted by [320, 480], so the added node shifts with them. + expect(positionsByName(built)).toEqual({ + 'Schedule Trigger': [320, 480], + Settings: [528, 480], + 'Read Sheet': [736, 480], + }); + }); + + it('anchors on a wired neighbour when the build kept the survivors in place', async () => { + // The layout engine skips nodes that already carry a position, so the added + // node is the only one it placed — in a frame unrelated to the saved canvas. + const saved = workflow([node('Schedule Trigger', [320, 480]), node('Settings', [528, 480])]); + const built = workflow( + [ + node('Schedule Trigger', [320, 480]), + node('Settings', [528, 480]), + node('Read Sheet', [416, 0]), + ], + { ...wire('Schedule Trigger', 'Settings'), ...wire('Settings', 'Read Sheet') }, + ); + + await preserveExistingNodePositions(built, 'wf-1', contextReturning(saved)); + + // One full step to the right of its parent, not back at the layout origin. + expect(positionsByName(built)['Read Sheet']).toEqual([752, 480]); + }); + + it('parks an added node below the saved graph when nothing wires it to a survivor', async () => { + const saved = workflow([node('Schedule Trigger', [320, 480]), node('Settings', [528, 480])]); + const built = workflow([ + node('Schedule Trigger', [320, 480]), + node('Settings', [528, 480]), + node('Orphan', [0, 0]), + ]); + + await preserveExistingNodePositions(built, 'wf-1', contextReturning(saved)); + + expect(positionsByName(built).Orphan).toEqual([320, 672]); + }); + + it('pushes an added node clear of a survivor it would have landed on', async () => { + // D was dragged well to the right, so the median translation drops the new + // node right on top of it. + const saved = workflow([node('A', [100, 100]), node('B', [300, 100]), node('D', [780, 100])]); + const built = workflow( + [node('A', [0, 0]), node('B', [224, 0]), node('D', [448, 0]), node('C', [672, 0])], + { ...wire('B', 'C'), ...wire('C', 'D') }, + ); + + await preserveExistingNodePositions(built, 'wf-1', contextReturning(saved)); + + expect(positionsByName(built).D).toEqual([780, 100]); + // Translated to x=772, snapped to the 16px grid, then pushed below D. + expect(positionsByName(built).C).toEqual([768, 288]); + }); + + it('does not push an added node off a sticky note', async () => { + const saved = workflow([node('A', [96, 96]), sticky('Sticky Note', [400, 96])]); + const built = workflow([ + node('A', [0, 0]), + sticky('Sticky Note', [304, 0]), + node('C', [304, 0]), + ]); + + await preserveExistingNodePositions(built, 'wf-1', contextReturning(saved)); + + // C lands on the sticky and stays there — stickies sit behind nodes. + expect(positionsByName(built).C).toEqual([400, 96]); + }); + }); + + describe('guards', () => { + it('skips new workflows without reading anything', async () => { + const getAsWorkflowJSON = vi.fn(); + const context = { workflowService: { getAsWorkflowJSON } } as unknown as InstanceAiContext; + const built = workflow([node('A', [0, 0])]); + + await preserveExistingNodePositions(built, undefined, context); + + expect(getAsWorkflowJSON).not.toHaveBeenCalled(); + expect(positionsByName(built)).toEqual({ A: [0, 0] }); + }); + + it('fails the update when the saved workflow cannot be loaded', async () => { + const context = { + workflowService: { + getAsWorkflowJSON: vi.fn().mockRejectedValue(new Error('Workflow not found')), + }, + } as unknown as InstanceAiContext; + + await expect( + preserveExistingNodePositions(workflow([node('A', [0, 0])]), 'wf-1', context), + ).rejects.toThrow( + 'Failed to load existing workflow wf-1 to preserve node positions: Workflow not found', + ); + }); + }); +}); diff --git a/packages/@n8n/instance-ai/src/tools/workflows/__tests__/resolve-credentials.test.ts b/packages/@n8n/instance-ai/src/tools/workflows/__tests__/resolve-credentials.test.ts index 80b7c197d48..58e1d2ececd 100644 --- a/packages/@n8n/instance-ai/src/tools/workflows/__tests__/resolve-credentials.test.ts +++ b/packages/@n8n/instance-ai/src/tools/workflows/__tests__/resolve-credentials.test.ts @@ -141,6 +141,174 @@ describe('resolveCredentials', () => { expect(result.mockedCredentialTypes).toEqual([]); }); + it('switches the node auth to the attached n8n credits credential type', async () => { + // The LLM wrote the API-key credential slot but left auth at the OAuth2 + // default; attaching n8n credits must switch auth so the slot is active. + const json = makeWorkflow({ + nodes: [ + { + id: '1', + name: 'PDF.co', + type: 'n8n-nodes-base.pdfco', + typeVersion: 1, + position: [0, 0] as [number, number], + parameters: { authentication: 'oAuth2' }, + credentials: { pdfcoApi: undefined as unknown as { id: string; name: string } }, + }, + ], + }); + const ctx = createMockContext(); + (ctx.credentialService.list as Mock).mockResolvedValue([]); + ( + ctx.credentialService as unknown as { isAiGatewayCredentialType: Mock } + ).isAiGatewayCredentialType = vi.fn().mockResolvedValue(true); + (ctx.nodeService as unknown as { getDescription: Mock }).getDescription = vi + .fn() + .mockResolvedValue({ + credentials: [ + { name: 'pdfcoOAuth2Api', displayOptions: { show: { authentication: ['oAuth2'] } } }, + { name: 'pdfcoApi', displayOptions: { show: { authentication: ['apiKey'] } } }, + ], + }); + + await resolveCredentials(json, undefined, ctx); + + expect(json.nodes[0].credentials).toEqual({ + pdfcoApi: { id: null, name: 'n8n credits', __aiGatewayManaged: true }, + }); + expect(json.nodes[0].parameters).toEqual({ authentication: 'apiKey' }); + }); + + it('falls back to a supported sibling type when the written slot type is not gateway-supported', async () => { + // The LLM wrote the slot matching the node's default auth (OAuth2), which + // n8n credits doesn't cover — the sibling API-key type is attached instead + // and auth is switched to it, rather than mocking the node into a setup card. + const json = makeWorkflow({ + nodes: [ + { + id: '1', + name: 'PDF.co', + type: 'n8n-nodes-base.pdfco', + typeVersion: 1, + position: [0, 0] as [number, number], + parameters: { authentication: 'oAuth2' }, + credentials: { pdfcoOAuth2Api: undefined as unknown as { id: string; name: string } }, + }, + ], + }); + const ctx = createMockContext(); + (ctx.credentialService.list as Mock).mockResolvedValue([]); + ( + ctx.credentialService as unknown as { isAiGatewayCredentialType: Mock } + ).isAiGatewayCredentialType = vi.fn( + async (type: string) => await Promise.resolve(type === 'pdfcoApi'), + ); + (ctx.nodeService as unknown as { getDescription: Mock }).getDescription = vi + .fn() + .mockResolvedValue({ + credentials: [ + { name: 'pdfcoOAuth2Api', displayOptions: { show: { authentication: ['oAuth2'] } } }, + { name: 'pdfcoApi', displayOptions: { show: { authentication: ['apiKey'] } } }, + ], + }); + + const result = await resolveCredentials(json, undefined, ctx); + + expect(json.nodes[0].credentials).toEqual({ + pdfcoApi: { id: null, name: 'n8n credits', __aiGatewayManaged: true }, + }); + expect(json.nodes[0].parameters).toEqual({ authentication: 'apiKey' }); + expect(result.resolvedCredentialsByNode).toEqual({ + 'PDF.co': [{ type: 'pdfcoApi', id: null, name: 'n8n credits', __aiGatewayManaged: true }], + }); + // Simulated during verification, but NOT flagged as needing a real credential. + expect(result.mockedNodeNames).toEqual(['PDF.co']); + expect(result.mockedCredentialsByNode).toEqual({}); + }); + + it('attaches n8n credits to a supported sibling type for a credential-less node whose auth type is unsupported', async () => { + // No `credentials` key at all and auth left at the unsupported OAuth2 + // default — the second pass switches auth to the supported sibling. + const json = makeWorkflow({ + nodes: [ + { + id: '1', + name: 'PDF.co', + type: 'n8n-nodes-base.pdfco', + typeVersion: 1, + position: [0, 0] as [number, number], + parameters: { authentication: 'oAuth2' }, + }, + ], + }); + const ctx = createMockContext(); + (ctx.credentialService.list as Mock).mockResolvedValue([]); + ( + ctx.credentialService as unknown as { isAiGatewayCredentialType: Mock } + ).isAiGatewayCredentialType = vi.fn( + async (type: string) => await Promise.resolve(type === 'pdfcoApi'), + ); + (ctx.nodeService as unknown as { getDescription: Mock }).getDescription = vi + .fn() + .mockResolvedValue({ + credentials: [ + { name: 'pdfcoOAuth2Api', displayOptions: { show: { authentication: ['oAuth2'] } } }, + { name: 'pdfcoApi', displayOptions: { show: { authentication: ['apiKey'] } } }, + ], + }); + + const result = await resolveCredentials(json, undefined, ctx); + + expect(json.nodes[0].credentials).toEqual({ + pdfcoApi: { id: null, name: 'n8n credits', __aiGatewayManaged: true }, + }); + expect(json.nodes[0].parameters).toEqual({ authentication: 'apiKey' }); + expect(result.resolvedCredentialsByNode).toEqual({ + 'PDF.co': [{ type: 'pdfcoApi', id: null, name: 'n8n credits', __aiGatewayManaged: true }], + }); + expect(result.mockedNodeNames).toEqual(['PDF.co']); + }); + + it('does not rewrite a parameter that already activates the attached credential type', async () => { + // The credential is shown for several auth values and the node already + // uses the second one — attaching n8n credits must not flip it to the first. + const json = makeWorkflow({ + nodes: [ + { + id: '1', + name: 'PDF.co', + type: 'n8n-nodes-base.pdfco', + typeVersion: 1, + position: [0, 0] as [number, number], + parameters: { authentication: 'apiKeyLegacy' }, + credentials: { pdfcoApi: undefined as unknown as { id: string; name: string } }, + }, + ], + }); + const ctx = createMockContext(); + (ctx.credentialService.list as Mock).mockResolvedValue([]); + ( + ctx.credentialService as unknown as { isAiGatewayCredentialType: Mock } + ).isAiGatewayCredentialType = vi.fn().mockResolvedValue(true); + (ctx.nodeService as unknown as { getDescription: Mock }).getDescription = vi + .fn() + .mockResolvedValue({ + credentials: [ + { + name: 'pdfcoApi', + displayOptions: { show: { authentication: ['apiKey', 'apiKeyLegacy'] } }, + }, + ], + }); + + await resolveCredentials(json, undefined, ctx); + + expect(json.nodes[0].credentials).toEqual({ + pdfcoApi: { id: null, name: 'n8n credits', __aiGatewayManaged: true }, + }); + expect(json.nodes[0].parameters).toEqual({ authentication: 'apiKeyLegacy' }); + }); + it('attaches n8n credits to a credential-less node requiring a gateway-supported type', async () => { // The LLM omitted the credential slot entirely — no `credentials` key at all. const json = makeWorkflow({ diff --git a/packages/@n8n/instance-ai/src/tools/workflows/__tests__/wait-gate-script.test.ts b/packages/@n8n/instance-ai/src/tools/workflows/__tests__/wait-gate-script.test.ts new file mode 100644 index 00000000000..40e43e6fb6e --- /dev/null +++ b/packages/@n8n/instance-ai/src/tools/workflows/__tests__/wait-gate-script.test.ts @@ -0,0 +1,238 @@ +import type { WorkflowJSON } from '@n8n/workflow-sdk'; + +import type { NodeSimulationVerdict } from '../../../workflow-loop/workflow-loop-state'; +import { deriveWaitGateScripts, findCycleCutEdge } from '../wait-gate-script'; + +const gateVerdict = (nodeName: string): NodeSimulationVerdict => ({ + nodeName, + verdict: 'simulate', + reason: 'Send-and-wait gate on a loop', + confidence: 'high', + source: 'deterministic', + haltBranch: true, +}); + +const main = (...targets: string[]) => ({ + main: [targets.map((target) => ({ node: target, type: 'main', index: 0 }))], +}); + +/** trigger → Generate → Format → Gate → Router → (Publish | Revise → Format). */ +function approvalLoopWorkflow(gateParameters: Record): WorkflowJSON { + return { + name: 'approval loop', + nodes: [ + { name: 'Every day', type: 'n8n-nodes-base.scheduleTrigger', parameters: {} }, + { name: 'Generate', type: '@n8n/n8n-nodes-langchain.chainLlm', parameters: {} }, + { name: 'Format', type: 'n8n-nodes-base.set', parameters: {} }, + { name: 'Gate', type: 'n8n-nodes-base.gmail', parameters: gateParameters }, + { name: 'Router', type: 'n8n-nodes-base.if', parameters: {} }, + { name: 'Publish', type: 'n8n-nodes-base.linkedIn', parameters: {} }, + { name: 'Revise', type: '@n8n/n8n-nodes-langchain.chainLlm', parameters: {} }, + ].map((node, index) => ({ + id: `id-${index}`, + typeVersion: 1, + position: [index * 100, 0], + ...node, + })), + connections: { + 'Every day': main('Generate'), + Generate: main('Format'), + Format: main('Gate'), + Gate: main('Router'), + Router: { + main: [ + [{ node: 'Publish', type: 'main', index: 0 }], + [{ node: 'Revise', type: 'main', index: 0 }], + ], + }, + Revise: main('Format'), + }, + } as unknown as WorkflowJSON; +} + +describe('findCycleCutEdge', () => { + it('picks the loop-back edge, never one that costs downstream coverage', () => { + const workflow = approvalLoopWorkflow({ operation: 'sendAndWait' }); + + // Gate→Router also breaks the cycle but would disconnect the router, + // publish and revise branches — only Revise→Format preserves coverage. + expect(findCycleCutEdge(workflow, 'Gate')).toEqual({ source: 'Revise', target: 'Format' }); + }); + + it('returns undefined when the gate is not on a cycle or no trigger exists', () => { + const acyclic = approvalLoopWorkflow({ operation: 'sendAndWait' }); + (acyclic.connections as Record).Revise = { main: [[]] }; + expect(findCycleCutEdge(acyclic, 'Gate')).toBeUndefined(); + + const triggerless = approvalLoopWorkflow({ operation: 'sendAndWait' }); + triggerless.nodes = triggerless.nodes.filter((node) => node.name !== 'Every day'); + expect(findCycleCutEdge(triggerless, 'Gate')).toBeUndefined(); + }); +}); + +describe('deriveWaitGateScripts', () => { + it('derives approve/decline passes for the approval response type', () => { + const workflow = approvalLoopWorkflow({ operation: 'sendAndWait', responseType: 'approval' }); + + const scripts = deriveWaitGateScripts(workflow, [gateVerdict('Gate')]); + + expect(scripts).toHaveLength(1); + expect(scripts[0].nodeName).toBe('Gate'); + expect(scripts[0].cutEdge).toEqual({ source: 'Revise', target: 'Format' }); + expect(scripts[0].decisions.map((d) => d.label)).toEqual(['approve', 'decline']); + expect(scripts[0].decisions[0].items[0]).toMatchObject({ data: { approved: true } }); + expect(scripts[0].decisions[1].items[0]).toMatchObject({ data: { approved: false } }); + }); + + it('defaults to the approval shape when responseType is omitted', () => { + const workflow = approvalLoopWorkflow({ operation: 'sendAndWait' }); + + const scripts = deriveWaitGateScripts(workflow, [gateVerdict('Gate')]); + + expect(scripts[0]?.decisions[0].items[0]).toMatchObject({ data: { approved: true } }); + }); + + it('enumerates the first single-select dropdown for custom forms', () => { + const workflow = approvalLoopWorkflow({ + operation: 'sendAndWait', + responseType: 'customForm', + formFields: { + values: [ + { + fieldLabel: 'Decision', + fieldType: 'dropdown', + fieldOptions: { values: [{ option: 'Approve' }, { option: 'Request changes' }] }, + }, + { fieldLabel: 'Requested changes', fieldType: 'textarea' }, + ], + }, + }); + + const scripts = deriveWaitGateScripts(workflow, [gateVerdict('Gate')]); + + expect(scripts[0]?.decisions.map((d) => d.label)).toEqual(['Approve', 'Request changes']); + expect(scripts[0]?.decisions[1].items[0]).toMatchObject({ + data: { Decision: 'Request changes' }, + }); + }); + + it('derives canned approve/revise texts for free-text gates', () => { + const workflow = approvalLoopWorkflow({ operation: 'sendAndWait', responseType: 'freeText' }); + + const scripts = deriveWaitGateScripts(workflow, [gateVerdict('Gate')]); + + expect(scripts[0]?.decisions).toHaveLength(2); + const firstItem = scripts[0]?.decisions[0].items[0] as { data?: { text?: string } }; + expect(firstItem.data?.text).toContain('Approved'); + }); + + it('returns [] for underivable shapes', () => { + const noDropdown = approvalLoopWorkflow({ + operation: 'sendAndWait', + responseType: 'customForm', + formFields: { values: [{ fieldLabel: 'Decision', fieldType: 'text' }] }, + }); + expect(deriveWaitGateScripts(noDropdown, [gateVerdict('Gate')])).toEqual([]); + + // Multiple halted gates — v1 scripts exactly one. + const workflow = approvalLoopWorkflow({ operation: 'sendAndWait' }); + expect(deriveWaitGateScripts(workflow, [gateVerdict('Gate'), gateVerdict('Other')])).toEqual( + [], + ); + + // Not a send-and-wait node (Wait node keeps the halt in v1). + const waitNode = approvalLoopWorkflow({}); + waitNode.nodes = waitNode.nodes.map((node) => + node.name === 'Gate' ? { ...node, type: 'n8n-nodes-base.wait', parameters: {} } : node, + ); + expect(deriveWaitGateScripts(waitNode, [gateVerdict('Gate')])).toEqual([]); + }); +}); + +describe('deriveWaitGateScripts — field identifiers and JSON forms', () => { + const formFields = { + values: [ + { + fieldLabel: 'Decision', + fieldName: 'decision_key', + fieldType: 'dropdown', + fieldOptions: { values: [{ option: 'Approve' }, { option: 'Reject' }] }, + }, + ], + }; + + it('keys fixtures by fieldName on v2.4+ gates, mirroring getFieldIdentifier', () => { + const workflow = approvalLoopWorkflow({ + operation: 'sendAndWait', + responseType: 'customForm', + formFields, + }); + workflow.nodes = workflow.nodes.map((node) => + node.name === 'Gate' ? { ...node, typeVersion: 2.4 } : node, + ); + + const scripts = deriveWaitGateScripts(workflow, [gateVerdict('Gate')]); + + expect(scripts[0]?.decisions[0].items[0]).toMatchObject({ + data: { decision_key: 'Approve' }, + }); + }); + + it('keys fixtures by label on pre-2.4 gates even when fieldName is set', () => { + const workflow = approvalLoopWorkflow({ + operation: 'sendAndWait', + responseType: 'customForm', + formFields, + }); + + const scripts = deriveWaitGateScripts(workflow, [gateVerdict('Gate')]); + + expect(scripts[0]?.decisions[0].items[0]).toMatchObject({ data: { Decision: 'Approve' } }); + }); + + it('parses static JSON-defined forms and bails on expressions', () => { + const staticJson = approvalLoopWorkflow({ + operation: 'sendAndWait', + responseType: 'customForm', + defineForm: 'json', + jsonOutput: JSON.stringify([ + { + fieldLabel: 'Decision', + fieldType: 'dropdown', + fieldOptions: { values: [{ option: 'Ship it' }, { option: 'Revise' }] }, + }, + ]), + }); + expect( + deriveWaitGateScripts(staticJson, [gateVerdict('Gate')])[0]?.decisions.map((d) => d.label), + ).toEqual(['Ship it', 'Revise']); + + const dynamicJson = approvalLoopWorkflow({ + operation: 'sendAndWait', + responseType: 'customForm', + defineForm: 'json', + jsonOutput: '{{ $json.fields }}', + }); + expect(deriveWaitGateScripts(dynamicJson, [gateVerdict('Gate')])).toEqual([]); + }); + + it('accepts a radio field as the decision driver', () => { + const workflow = approvalLoopWorkflow({ + operation: 'sendAndWait', + responseType: 'customForm', + formFields: { + values: [ + { + fieldLabel: 'Verdict', + fieldType: 'radio', + fieldOptions: { values: [{ option: 'Yes' }, { option: 'No' }] }, + }, + ], + }, + }); + + expect( + deriveWaitGateScripts(workflow, [gateVerdict('Gate')])[0]?.decisions.map((d) => d.label), + ).toEqual(['Yes', 'No']); + }); +}); diff --git a/packages/@n8n/instance-ai/src/tools/workflows/__tests__/workflow-build-telemetry.test.ts b/packages/@n8n/instance-ai/src/tools/workflows/__tests__/workflow-build-telemetry.test.ts new file mode 100644 index 00000000000..61d49606481 --- /dev/null +++ b/packages/@n8n/instance-ai/src/tools/workflows/__tests__/workflow-build-telemetry.test.ts @@ -0,0 +1,47 @@ +import type { InstanceAiContext } from '../../../types'; +import { trackWaitGateVerificationPlan } from '../workflow-build-telemetry'; + +function makeContext(trackTelemetry = vi.fn()): InstanceAiContext { + return { + trackTelemetry, + threadId: 'thread-1', + runId: 'run-1', + } as unknown as InstanceAiContext; +} + +describe('trackWaitGateVerificationPlan', () => { + it('emits gate counts and the multi-gate flag', () => { + const trackTelemetry = vi.fn(); + + trackWaitGateVerificationPlan(makeContext(trackTelemetry), { + haltedGateCount: 2, + scriptedGateCount: 0, + savedWorkflowId: 'wf-1', + }); + + expect(trackTelemetry).toHaveBeenCalledWith( + 'instance_ai_wait_gate_verification_plan', + expect.objectContaining({ + halted_gate_count: 2, + scripted_gate_count: 0, + multi_gate: true, + scripted: false, + workflow_id: 'wf-1', + thread_id: 'thread-1', + run_id: 'run-1', + }), + ); + }); + + it('stays silent for builds without halted gates', () => { + const trackTelemetry = vi.fn(); + + trackWaitGateVerificationPlan(makeContext(trackTelemetry), { + haltedGateCount: 0, + scriptedGateCount: 0, + savedWorkflowId: 'wf-1', + }); + + expect(trackTelemetry).not.toHaveBeenCalled(); + }); +}); diff --git a/packages/@n8n/instance-ai/src/tools/workflows/__tests__/workflow-source-compiler.test.ts b/packages/@n8n/instance-ai/src/tools/workflows/__tests__/workflow-source-compiler.test.ts index cf5525525c7..267e549ce5e 100644 --- a/packages/@n8n/instance-ai/src/tools/workflows/__tests__/workflow-source-compiler.test.ts +++ b/packages/@n8n/instance-ai/src/tools/workflows/__tests__/workflow-source-compiler.test.ts @@ -187,7 +187,14 @@ describe('compileWorkflowSource', () => { }); vi.mocked(validateWorkflow).mockReturnValue({ valid: false, - errors: [{ code: 'INVALID_PARAMETER', message: 'Bad parameter', nodeName: 'Manual Trigger' }], + errors: [ + { + code: 'INVALID_PARAMETER', + message: 'Bad parameter', + nodeName: 'Manual Trigger', + severity: 'error', + }, + ], warnings: [], }); const context = makeContext(); diff --git a/packages/@n8n/instance-ai/src/tools/workflows/__tests__/workflow-validation-warnings.test.ts b/packages/@n8n/instance-ai/src/tools/workflows/__tests__/workflow-validation-warnings.test.ts index c7df00f507a..7dcc4046f34 100644 --- a/packages/@n8n/instance-ai/src/tools/workflows/__tests__/workflow-validation-warnings.test.ts +++ b/packages/@n8n/instance-ai/src/tools/workflows/__tests__/workflow-validation-warnings.test.ts @@ -1,16 +1,29 @@ import { partitionWarnings, type ValidationWarning } from '../workflow-validation-warnings'; describe('partitionWarnings', () => { - it('keeps graph reachability warnings informational and treats other issues as blocking', () => { + it('keeps informational severity soft and treats other issues as blocking', () => { const warnings: ValidationWarning[] = [ - { code: 'MISSING_TRIGGER', message: 'No trigger' }, - { code: 'DISCONNECTED_NODE', message: 'Node is disconnected' }, - { code: 'INVALID_PARAMETER', message: 'Bad parameter', nodeName: 'HTTP Request' }, + { code: 'MISSING_TRIGGER', message: 'No trigger', severity: 'informational' }, + { code: 'DISCONNECTED_NODE', message: 'Node is disconnected', severity: 'informational' }, + { + code: 'INVALID_PARAMETER', + message: 'Bad parameter', + nodeName: 'HTTP Request', + severity: 'warning', + }, ]; expect(partitionWarnings(warnings)).toEqual({ informational: warnings.slice(0, 2), - errors: [warnings[2]], + blocking: [warnings[2]], + }); + }); + + it('treats missing severity as blocking', () => { + const warnings: ValidationWarning[] = [{ code: 'UNKNOWN_CONFIG_KEY', message: 'Unknown key' }]; + expect(partitionWarnings(warnings)).toEqual({ + informational: [], + blocking: warnings, }); }); }); diff --git a/packages/@n8n/instance-ai/src/tools/workflows/build-workflow.tool.ts b/packages/@n8n/instance-ai/src/tools/workflows/build-workflow.tool.ts index 485bd2ff771..bed8154534c 100644 --- a/packages/@n8n/instance-ai/src/tools/workflows/build-workflow.tool.ts +++ b/packages/@n8n/instance-ai/src/tools/workflows/build-workflow.tool.ts @@ -8,6 +8,7 @@ import { join } from 'node:path'; import { z } from 'zod'; import { planVerificationSimulation } from './plan-verification-simulation'; +import { preserveExistingNodePositions } from './preserve-node-positions'; import { buildCredentialMap, buildCredentialResolutionNote, @@ -37,7 +38,10 @@ import { reportWorkflowBuildOutcome, } from './workflow-build-reporting'; import { withDeterministicRouting } from './workflow-build-routing'; -import { trackWorkflowSourceBuild } from './workflow-build-telemetry'; +import { + trackWaitGateVerificationPlan, + trackWorkflowSourceBuild, +} from './workflow-build-telemetry'; import { bindSourceFileToExistingWorkflow, getWorkflowSourceFileBinding, @@ -109,7 +113,7 @@ export const buildWorkflowInputSchema = z .string() .optional() .describe( - 'Full source to write to filePath before building — use this instead of a separate workspace_write_file call when creating or fully rewriting the source. Omit to build the existing file content (preferred for targeted edits made with file tools).', + 'Full source to write to filePath before building — use this instead of a separate workspace_write_file call when creating or fully rewriting the source. Omit to build the existing file content (preferred for targeted edits made with file tools, and required before `workflow-sdk validate`).', ), workflowId: z .string() @@ -283,8 +287,8 @@ export function createBuildWorkflowTool(context: InstanceAiContext) { 'Load `workflow-builder` via `load_skill` before calling this tool. ' + 'When the workflow creates or writes Data Tables, also load `data-table-manager` first. ' + 'Use TypeScript SDK source for new workflows, or WorkflowJSON .json source for existing workflow edits. ' + - 'For new or fully rewritten source, pass it in `sourceCode` (the tool writes filePath and builds in one call — ' + - 'do not spend a separate workspace_write_file call). Pass filePath alone only after editing an existing file with file tools.', + 'Prefer writing the file with `workspace_write_file` / `workspace_str_replace_file` so `workflow-sdk validate` can run on it, then call this tool with filePath. ' + + 'For a one-shot create/rewrite you may pass `sourceCode` instead (the tool writes filePath and builds).', ) .input(buildWorkflowInputSchema) .output( @@ -495,7 +499,7 @@ export function createBuildWorkflowTool(context: InstanceAiContext) { const remediation = createCodeFixableRemediation({ reason: 'workflow_source_read_failed', guidance: - 'The workflow source file could not be read. Recreate or edit the returned filePath, then call build-workflow again with the same filePath.', + 'The workflow source file could not be read. Write it with `workspace_write_file`, then call build-workflow again with the same filePath.', }); trackWorkflowSourceBuild(context, { result: 'failure', @@ -585,6 +589,7 @@ export function createBuildWorkflowTool(context: InstanceAiContext) { { code: 'auto_imported_sdk_symbols', message: `Auto-added missing @n8n/workflow-sdk import(s): ${recovery.symbols.join(', ')}. Include them in future source.`, + severity: 'informational', }, ], } @@ -638,9 +643,9 @@ export function createBuildWorkflowTool(context: InstanceAiContext) { const partitionedWarnings = partitionWarnings(compiled.warnings); informational = partitionedWarnings.informational; - if (partitionedWarnings.errors.length > 0) { + if (partitionedWarnings.blocking.length > 0) { const formattedErrors = withEscalation( - partitionedWarnings.errors.map( + partitionedWarnings.blocking.map( (e) => `[${e.code}]${e.nodeName ? ` (${e.nodeName})` : ''}: ${e.message}`, ), ); @@ -739,6 +744,7 @@ export function createBuildWorkflowTool(context: InstanceAiContext) { await preserveExistingSetupValues(json, targetWorkflowId, context); await ensureWebhookIds(json, targetWorkflowId, context); await preserveExistingNodeGroupIds(json, targetWorkflowId, context); + await preserveExistingNodePositions(json, targetWorkflowId, context); const hasMockedCredentialNodes = mockResult.mockedNodeNames.length > 0; const hasResolvedCredentials = Object.keys(mockResult.resolvedCredentialsByNode).length > 0; @@ -757,14 +763,21 @@ export function createBuildWorkflowTool(context: InstanceAiContext) { ) => { const setupRequests = await analyzeWorkflow(context, saved.id); const workflowNeedsSetup = setupRequests.some((request) => request.needsAction); - const { nodeSimulationPlan, simulationFixtures } = await planVerificationSimulation({ - workflow: json, - mockedNodeNames: mockResult.mockedNodeNames, - declaredOutputFixtures: compiled.declaredOutputFixtures, - workflowId: saved.id, - outputSchemaLookup: context.outputSchemaLookup, - fallbackModelConfig: context.modelId, - logger: context.logger, + const { nodeSimulationPlan, simulationFixtures, waitGateScripts } = + await planVerificationSimulation({ + workflow: json, + mockedNodeNames: mockResult.mockedNodeNames, + declaredOutputFixtures: compiled.declaredOutputFixtures, + workflowId: saved.id, + outputSchemaLookup: context.outputSchemaLookup, + fallbackModelConfig: context.modelId, + logger: context.logger, + }); + trackWaitGateVerificationPlan(context, { + haltedGateCount: (nodeSimulationPlan ?? []).filter((verdict) => verdict.haltBranch) + .length, + scriptedGateCount: waitGateScripts?.length ?? 0, + savedWorkflowId: saved.id, }); const runId = buildContext?.runId ?? context.runId; const workflowName = json.name || 'workflow'; @@ -831,6 +844,7 @@ export function createBuildWorkflowTool(context: InstanceAiContext) { workflowNeedsSetup, nodeSimulationPlan, simulationFixtures, + waitGateScripts, supportingWorkflowIds: referencedWorkflowIds.length > 0 ? referencedWorkflowIds : undefined, hasUnresolvedPlaceholders: hasPlaceholders || undefined, diff --git a/packages/@n8n/instance-ai/src/tools/workflows/plan-verification-simulation.ts b/packages/@n8n/instance-ai/src/tools/workflows/plan-verification-simulation.ts index 65525db342f..214d63ab3d1 100644 --- a/packages/@n8n/instance-ai/src/tools/workflows/plan-verification-simulation.ts +++ b/packages/@n8n/instance-ai/src/tools/workflows/plan-verification-simulation.ts @@ -20,6 +20,7 @@ import { generateSimulationFixtures, type SimulationFixtures, } from './generate-simulation-fixtures.service'; +import { deriveWaitGateScripts } from './wait-gate-script'; import { isMockableTriggerNodeType, isTriggerNodeType, @@ -28,7 +29,10 @@ import { } from './workflow-json-utils'; import type { Logger } from '../../logger'; import type { ModelConfig } from '../../types'; -import type { NodeSimulationVerdict } from '../../workflow-loop/workflow-loop-state'; +import type { + NodeSimulationVerdict, + WaitGateScript, +} from '../../workflow-loop/workflow-loop-state'; export interface PlanVerificationSimulationInput { workflow: WorkflowJSON; @@ -50,6 +54,8 @@ export interface PlanVerificationSimulationInput { export interface VerificationSimulationPlan { nodeSimulationPlan?: NodeSimulationVerdict[]; + /** Scripted decisions for wait gates on loops; absent when none derivable. */ + waitGateScripts?: WaitGateScript[]; simulationFixtures?: SimulationFixtures; } @@ -299,6 +305,7 @@ export async function planVerificationSimulation({ }: PlanVerificationSimulationInput): Promise { let nodeSimulationPlan: NodeSimulationVerdict[] | undefined; let simulationFixtures: SimulationFixtures | undefined; + let waitGateScripts: WaitGateScript[] | undefined; const declaredFixtures = nonEmptyDeclaredFixtures(declaredOutputFixtures); try { nodeSimulationPlan = await classifyNodesForSimulation({ @@ -314,6 +321,8 @@ export async function planVerificationSimulation({ new Set(mockedNodeNames ?? []), ); nodeSimulationPlan = withWaitGateHaltVerdicts(nodeSimulationPlan, workflow); + const derivedScripts = deriveWaitGateScripts(workflow, nodeSimulationPlan); + waitGateScripts = derivedScripts.length > 0 ? derivedScripts : undefined; if (nodeSimulationPlan.length > 0) { const planNeedingGeneratedFixtures = nodeSimulationPlan.filter( (verdict) => @@ -365,5 +374,5 @@ export async function planVerificationSimulation({ simulationFixtures = declaredFixtures; } } - return { nodeSimulationPlan, simulationFixtures }; + return { nodeSimulationPlan, simulationFixtures, waitGateScripts }; } diff --git a/packages/@n8n/instance-ai/src/tools/workflows/preserve-node-positions.ts b/packages/@n8n/instance-ai/src/tools/workflows/preserve-node-positions.ts new file mode 100644 index 00000000000..557955b76d4 --- /dev/null +++ b/packages/@n8n/instance-ai/src/tools/workflows/preserve-node-positions.ts @@ -0,0 +1,233 @@ +import { isRecord } from '@n8n/utils/is-record'; +import type { NodeJSON, WorkflowJSON } from '@n8n/workflow-sdk'; +import { + DEFAULT_NODE_SIZE, + GRID_SIZE, + NODE_X_SPACING, + NODE_Y_SPACING, + isStickyNoteType, +} from '@n8n/workflow-sdk'; + +import type { InstanceAiContext } from '../../types'; + +type Position = [number, number]; + +interface Box { + x: number; + y: number; + width: number; + height: number; +} + +const [NODE_WIDTH, NODE_HEIGHT] = DEFAULT_NODE_SIZE; + +/** Horizontal step between a node and the one wired after it. */ +const NODE_STEP_X = NODE_WIDTH + NODE_X_SPACING; + +/** Bound on the de-overlap walk so a pathological graph can't spin. */ +const MAX_SEPARATION_STEPS = 50; + +function snapToGrid(value: number): number { + return Math.round(value / GRID_SIZE) * GRID_SIZE; +} + +function median(values: number[]): number { + const sorted = [...values].sort((a, b) => a - b); + const mid = Math.floor(sorted.length / 2); + return sorted.length % 2 === 0 ? (sorted[mid - 1] + sorted[mid]) / 2 : sorted[mid]; +} + +function boxOf(node: NodeJSON): Box { + return { x: node.position[0], y: node.position[1], width: NODE_WIDTH, height: NODE_HEIGHT }; +} + +/** Overlap test with a one-grid-cell gutter, so nodes never end up flush. */ +function intersects(a: Box, b: Box): boolean { + return !( + a.x + a.width + GRID_SIZE <= b.x || + b.x + b.width + GRID_SIZE <= a.x || + a.y + a.height + GRID_SIZE <= b.y || + b.y + b.height + GRID_SIZE <= a.y + ); +} + +/** Direct predecessors and successors by node name, across all connection types. */ +function buildAdjacency(json: WorkflowJSON): { + parentsOf: Map; + childrenOf: Map; +} { + const parentsOf = new Map(); + const childrenOf = new Map(); + + for (const [source, connectionsByType] of Object.entries(json.connections ?? {})) { + if (!isRecord(connectionsByType)) continue; + for (const groups of Object.values(connectionsByType)) { + if (!Array.isArray(groups)) continue; + for (const group of groups) { + if (!Array.isArray(group)) continue; + for (const connection of group) { + if (!isRecord(connection) || typeof connection.node !== 'string') continue; + const target = connection.node; + childrenOf.set(source, [...(childrenOf.get(source) ?? []), target]); + parentsOf.set(target, [...(parentsOf.get(target) ?? []), source]); + } + } + } + } + + return { parentsOf, childrenOf }; +} + +interface Survivor { + node: NodeJSON; + saved: Position; +} + +/** + * Offset that carries the build's layout frame onto the saved canvas. + * + * The build runs in a sandbox with no view of the saved workflow, so whatever + * `toJSON({ tidyUp: true })` produced sits in the layout engine's own coordinate + * space (origin near 0) rather than wherever the user's nodes actually live. + * Added nodes keep their relative arrangement; only the frame moves. + */ +function resolveTranslation( + survivors: Survivor[], + added: NodeJSON[], + json: WorkflowJSON, +): Position { + // The build re-laid out the whole graph, so survivors give us the mapping directly. + const relaidOut = survivors.some( + ({ node, saved }) => node.position[0] !== saved[0] || node.position[1] !== saved[1], + ); + if (relaidOut) { + return [ + median(survivors.map(({ node, saved }) => saved[0] - node.position[0])), + median(survivors.map(({ node, saved }) => saved[1] - node.position[1])), + ]; + } + + // The build kept the survivors' positions, so the layout engine skipped them and + // only the added nodes were placed — in a frame unrelated to the saved canvas. + // Anchor on a wired neighbour that did survive. + const savedByName = new Map(survivors.map(({ node, saved }) => [node.name ?? '', saved])); + const { parentsOf, childrenOf } = buildAdjacency(json); + + for (const node of added) { + if (!node.name) continue; + + for (const parent of parentsOf.get(node.name) ?? []) { + const anchor = savedByName.get(parent); + if (anchor) { + return [anchor[0] + NODE_STEP_X - node.position[0], anchor[1] - node.position[1]]; + } + } + + for (const child of childrenOf.get(node.name) ?? []) { + const anchor = savedByName.get(child); + if (anchor) { + return [anchor[0] - NODE_STEP_X - node.position[0], anchor[1] - node.position[1]]; + } + } + } + + // Nothing wired to an existing node — park the added set below the saved graph. + const savedMinX = Math.min(...survivors.map(({ saved }) => saved[0])); + const savedMaxY = Math.max(...survivors.map(({ saved }) => saved[1])); + const addedMinX = Math.min(...added.map((node) => node.position[0])); + const addedMinY = Math.min(...added.map((node) => node.position[1])); + + return [savedMinX - addedMinX, savedMaxY + NODE_HEIGHT + NODE_Y_SPACING - addedMinY]; +} + +/** + * Push added nodes down until they clear everything already on the canvas. + * Sticky notes are ignored on both sides — they are meant to sit behind nodes. + */ +function separateAddedNodes(added: NodeJSON[], allNodes: NodeJSON[]): void { + const addedSet = new Set(added); + const occupied = allNodes + .filter((node) => !addedSet.has(node) && !isStickyNoteType(node.type)) + .map(boxOf); + + for (const node of added) { + if (isStickyNoteType(node.type)) continue; + + for (let step = 0; step < MAX_SEPARATION_STEPS; step++) { + const collision = occupied.find((box) => intersects(box, boxOf(node))); + if (!collision) break; + node.position = [ + node.position[0], + snapToGrid(collision.y + collision.height + NODE_Y_SPACING), + ]; + } + + occupied.push(boxOf(node)); + } +} + +/** + * For updates, restore each surviving node's position from the saved workflow. + * + * The sandbox build has no view of the saved workflow, so `toJSON({ tidyUp: true })` + * lays the whole graph out from scratch and every node lands wherever the layout + * engine put it — scattering a canvas the user had arranged by hand. Reconciling by + * name here keeps the user's layout authoritative, mirroring ensureWebhookIds. + * + * Nodes the build added are translated into the saved canvas's frame, keeping the + * layout engine's relative arrangement, then nudged clear of anything they land on. + */ +export async function preserveExistingNodePositions( + json: WorkflowJSON, + workflowId: string | undefined, + ctx: InstanceAiContext, +): Promise { + if (!workflowId) return; + + let existing: WorkflowJSON; + try { + existing = await ctx.workflowService.getAsWorkflowJSON(workflowId); + } catch (error) { + const message = error instanceof Error ? error.message : String(error); + throw new Error( + `Failed to load existing workflow ${workflowId} to preserve node positions: ${message}`, + { cause: error }, + ); + } + + const savedPositionsByName = new Map(); + for (const node of existing.nodes ?? []) { + if (node.name && Array.isArray(node.position)) { + savedPositionsByName.set(node.name, [node.position[0], node.position[1]]); + } + } + if (savedPositionsByName.size === 0) return; + + const nodes = json.nodes ?? []; + const survivors: Survivor[] = []; + const added: NodeJSON[] = []; + for (const node of nodes) { + const saved = node.name ? savedPositionsByName.get(node.name) : undefined; + if (saved) survivors.push({ node, saved }); + else added.push(node); + } + + // Every node is new (or renamed) — there is no prior layout left to honour. + if (survivors.length === 0) return; + + if (added.length > 0) { + const [deltaX, deltaY] = resolveTranslation(survivors, added, json); + for (const node of added) { + node.position = [ + snapToGrid(node.position[0] + deltaX), + snapToGrid(node.position[1] + deltaY), + ]; + } + } + + for (const { node, saved } of survivors) { + node.position = saved; + } + + if (added.length > 0) separateAddedNodes(added, nodes); +} diff --git a/packages/@n8n/instance-ai/src/tools/workflows/resolve-credentials.ts b/packages/@n8n/instance-ai/src/tools/workflows/resolve-credentials.ts index 0ec47697fea..19925c2aeef 100644 --- a/packages/@n8n/instance-ai/src/tools/workflows/resolve-credentials.ts +++ b/packages/@n8n/instance-ai/src/tools/workflows/resolve-credentials.ts @@ -7,11 +7,16 @@ * picks mocked nodes up and pins them with generated fixtures at verify time. */ -import type { WorkflowJSON } from '@n8n/workflow-sdk'; +import type { NodeJSON, WorkflowJSON } from '@n8n/workflow-sdk'; import { AI_GATEWAY_CREDENTIAL, N8N_CONNECT_DISPLAY_NAME } from './credential-utils'; import type { ResolvedCredential } from './resolved-credential.schema'; -import { getValidCredentialTypes } from './setup-workflow.service'; +import { + getCredentialActivationParameters, + getCredentialActivationState, + getValidCredentialTypes, + resolveSupportedSiblingCredentialType, +} from './setup-workflow.service'; import type { InstanceAiContext } from '../../types'; export type { ResolvedCredential }; @@ -140,6 +145,41 @@ export async function resolveCredentials( return supported; }; + const hasStoredCredential = (credType: string): boolean => + (availableCredentials?.get(credType)?.length ?? 0) > 0; + + // Fallback for a gateway-unsupported slot: a supported sibling credential type + // the node can be switched to. See resolveSupportedSiblingCredentialType. + const resolveSupportedSiblingType = async ( + node: NodeJSON, + unsupportedType: string, + ): Promise => + await resolveSupportedSiblingCredentialType( + ctx, + node, + unsupportedType, + isGatewayCredentialType, + hasStoredCredential, + ); + + // Switch the node's parameters so the attached credential type is the active + // slot (e.g. set `authentication` to match). No-op when the slot is already + // active (never rewrite a valid value) or no switch can reach it (version-gated). + const applyManagedAuth = async (node: NodeJSON, credentialType: string): Promise => { + let nodeDesc: Awaited> | undefined; + try { + nodeDesc = await ctx.nodeService.getDescription(node.type, node.typeVersion ?? 1); + } catch { + return; + } + const credential = nodeDesc?.credentials?.find((cred) => cred.name === credentialType); + if (!credential || getCredentialActivationState(node, credential) !== 'activatable') return; + const activation = getCredentialActivationParameters(credential.displayOptions); + if (Object.keys(activation).length > 0) { + node.parameters = { ...node.parameters, ...activation }; + } + }; + // Build a map of existing credentials by node name (for updates) const existingCredsByNode = new Map>(); if (workflowId) { @@ -208,27 +248,41 @@ export async function resolveCredentials( // the simulation set (`nodeMocked`) so verification pins it instead of // spending gateway quota, but it is NOT added to `mockedCredentialsByNode` // — that channel means "needs a real credential", which this node doesn't. - const attachGatewayCredential = () => { - creds[key] = { ...AI_GATEWAY_CREDENTIAL, name: N8N_CONNECT_DISPLAY_NAME }; + const attachGatewayCredential = async (credentialType = key) => { + creds[credentialType] = { ...AI_GATEWAY_CREDENTIAL, name: N8N_CONNECT_DISPLAY_NAME }; + await applyManagedAuth(node, credentialType); nodeMocked = true; if (node.name) { resolvedCredentialsByNode[node.name] ??= []; - resolvedCredentialsByNode[node.name].push({ - type: key, - id: null, - name: N8N_CONNECT_DISPLAY_NAME, - __aiGatewayManaged: true, - }); + const resolved = resolvedCredentialsByNode[node.name]; + // The type may already be recorded when the LLM wrote several slots + // and an earlier one attached it as its sibling — don't record twice. + if (!resolved.some((cred) => cred.type === credentialType && cred.id === null)) { + resolved.push({ + type: credentialType, + id: null, + name: N8N_CONNECT_DISPLAY_NAME, + __aiGatewayManaged: true, + }); + } } }; - // Prefer n8n credits over mocking when the type is gateway-supported and - // the user has no stored credential of their own for it. + // With no stored credential for the type, prefer n8n credits over mocking: + // attach directly if the written type is gateway-supported, else attach to + // a supported sibling (switching auth to it) and drop the unusable slot. const mockOrAttachGateway = async () => { - const hasStored = (availableCredentials?.get(key)?.length ?? 0) > 0; - if (!hasStored && (await isGatewayCredentialType(key))) { - attachGatewayCredential(); - return; + if (!hasStoredCredential(key)) { + if (await isGatewayCredentialType(key)) { + await attachGatewayCredential(); + return; + } + const siblingType = await resolveSupportedSiblingType(node, key); + if (siblingType) { + delete creds[key]; + await attachGatewayCredential(siblingType); + return; + } } mockCredential(); }; @@ -270,14 +324,11 @@ export async function resolveCredentials( } // Second pass — required-but-omitted credentials. The first pass only visits - // slots the LLM actually wrote in `node.credentials`; a node built with no - // slot for a type it requires is skipped there, then reaches post-build setup - // analysis credential-less and surfaces a setup card. Here the required types - // come from the node description (getValidCredentialTypes), and we silently - // attach n8n credits when, in guard order: (1) the node has no entry for the - // type, (2) the user has no stored credential for it, and (3) the type is - // supported by n8n credits. Any guard failing leaves the node untouched so the - // setup card can collect a real credential. + // slots the LLM wrote; a node missing a slot for a type it requires reaches + // post-build setup credential-less and surfaces a setup card. Required types + // come from the node description; silently attach n8n credits when the node has + // no entry and no stored credential, and the type — or a supported sibling its + // auth is switched to — is gateway-supported. Otherwise leave it for setup. for (const node of json.nodes ?? []) { if (!node.name) continue; const requiredTypes = await getValidCredentialTypes(ctx, node); @@ -285,18 +336,24 @@ export async function resolveCredentials( const creds = (node.credentials ?? {}) as Record; const existing = creds[credType]; if (existing !== undefined && existing !== null) continue; - const hasStored = (availableCredentials?.get(credType)?.length ?? 0) > 0; - if (hasStored) continue; - if (!(await isGatewayCredentialType(credType))) continue; + if (hasStoredCredential(credType)) continue; + + let managedType = credType; + if (!(await isGatewayCredentialType(credType))) { + const siblingType = await resolveSupportedSiblingType(node, credType); + if (!siblingType) continue; + managedType = siblingType; + } node.credentials ??= {}; - (node.credentials as Record)[credType] = { + (node.credentials as Record)[managedType] = { ...AI_GATEWAY_CREDENTIAL, name: N8N_CONNECT_DISPLAY_NAME, }; + await applyManagedAuth(node, managedType); resolvedCredentialsByNode[node.name] ??= []; resolvedCredentialsByNode[node.name].push({ - type: credType, + type: managedType, id: null, name: N8N_CONNECT_DISPLAY_NAME, __aiGatewayManaged: true, diff --git a/packages/@n8n/instance-ai/src/tools/workflows/setup-workflow.service.ts b/packages/@n8n/instance-ai/src/tools/workflows/setup-workflow.service.ts index 347c60cb2ad..8c1d4da1e3e 100644 --- a/packages/@n8n/instance-ai/src/tools/workflows/setup-workflow.service.ts +++ b/packages/@n8n/instance-ai/src/tools/workflows/setup-workflow.service.ts @@ -8,8 +8,12 @@ import { findPlaceholderDetails } from '@n8n/utils/placeholder'; import type { IDataObject, NodeJSON, DisplayOptions, WorkflowJSON } from '@n8n/workflow-sdk'; import { matchesDisplayOptions } from '@n8n/workflow-sdk'; -import type { IConnections, INode } from 'n8n-workflow'; -import { getParentNodes, mapConnectionsByDestination } from 'n8n-workflow'; +import type { IConnections, ICredentialsDisplayOptions, INode } from 'n8n-workflow'; +import { + getCredentialActivationParameters as getCredentialActivationParametersByDisplayOptions, + getParentNodes, + mapConnectionsByDestination, +} from 'n8n-workflow'; import { nanoid } from 'nanoid'; import { @@ -128,6 +132,75 @@ export async function getValidCredentialTypes( return types; } +/** + * Adapter over the n8n-workflow helper of the same name for the loosely-typed + * descriptions the node service returns; see it for semantics. + */ +export function getCredentialActivationParameters( + displayOptions: Record | undefined, +): IDataObject { + return getCredentialActivationParametersByDisplayOptions( + displayOptions as ICredentialsDisplayOptions | undefined, + ); +} + +export type CredentialActivationState = 'active' | 'activatable' | 'unreachable'; + +/** + * Whether a credential is the node's active slot (`active`), can become it by + * writing its activation parameters (`activatable`), or can't be reached from + * this node at all (`unreachable`, e.g. gated to another node version via + * `@version`). + */ +export function getCredentialActivationState( + node: NodeJSON, + credential: { displayOptions?: Record }, +): CredentialActivationState { + if (!credential.displayOptions) return 'active'; + const displayOptions = credential.displayOptions as DisplayOptions; + const nodeVersion = node.typeVersion ?? 1; + const parameters = (node.parameters as Record) ?? {}; + if (matchesDisplayOptions({ parameters, nodeVersion }, displayOptions)) return 'active'; + const activated = { + ...parameters, + ...getCredentialActivationParameters(credential.displayOptions), + }; + return matchesDisplayOptions({ parameters: activated, nodeVersion }, displayOptions) + ? 'activatable' + : 'unreachable'; +} + +/** + * Fallback for a slot whose credential type n8n credits doesn't support: the + * first declared sibling type that is supported, unassigned, not backed by a + * stored user credential, and reachable by a parameter switch. The async/loosely- + * typed counterpart of the shared `resolveSupportedCredentialActivation`. + */ +export async function resolveSupportedSiblingCredentialType( + context: InstanceAiContext, + node: NodeJSON, + unsupportedType: string, + isSupported: (credentialType: string) => Promise, + hasStoredCredential: (credentialType: string) => boolean, +): Promise { + let nodeDesc: NodeDescription | undefined; + try { + nodeDesc = await context.nodeService.getDescription(node.type, node.typeVersion ?? 1); + } catch { + return undefined; + } + for (const credential of nodeDesc?.credentials ?? []) { + if (!credential.name || credential.name === unsupportedType) continue; + const assigned = (node.credentials as Record | undefined)?.[credential.name]; + if (assigned !== undefined && assigned !== null) continue; + if (hasStoredCredential(credential.name)) continue; + if (!(await isSupported(credential.name))) continue; + if (getCredentialActivationState(node, credential) === 'unreachable') continue; + return credential.name; + } + return undefined; +} + /** * Drop credential entries from a node whose type is no longer valid for the * node's current parameters (e.g. an `httpHeaderAuth` left over from an earlier diff --git a/packages/@n8n/instance-ai/src/tools/workflows/wait-gate-script.ts b/packages/@n8n/instance-ai/src/tools/workflows/wait-gate-script.ts new file mode 100644 index 00000000000..0be3fcb8e08 --- /dev/null +++ b/packages/@n8n/instance-ai/src/tools/workflows/wait-gate-script.ts @@ -0,0 +1,277 @@ +/** + * Wait-gate decision scripts for verification. + * + * A send-and-wait gate on a loop cannot be verified with a single pinned + * response (the constant decision re-runs the loop forever). Instead, verify + * runs one pass per scripted decision — approve-ish and reject-ish — on an + * ephemeral workflow copy with one loop edge removed, so every pass is + * acyclic regardless of how the decision routes. This module derives that + * script (cut edge + decision fixtures) from the workflow at build time. + * + * V1 limits: send-and-wait operations only (Wait/Form keep the halt), exactly + * one scripted gate per workflow, and custom forms need a dropdown field to + * enumerate decisions. Anything underivable returns [] and the gate falls + * back to its `haltBranch` behaviour. + */ + +import { isRecord } from '@n8n/utils/is-record'; +import type { WorkflowJSON } from '@n8n/workflow-sdk'; + +import { isTriggerNodeType } from './workflow-json-utils'; +import type { + NodeSimulationVerdict, + WaitGateScript, +} from '../../workflow-loop/workflow-loop-state'; + +type WorkflowNode = WorkflowJSON['nodes'][number]; +type Edge = { source: string; target: string }; + +const SEND_AND_WAIT_OPERATION = 'sendAndWait'; + +const APPROVE_TEXT = 'Approved — looks good, go ahead.'; +const REVISE_TEXT = 'Please revise: tighten the copy and add one concrete example.'; + +function isSendAndWaitNode(node: WorkflowNode): boolean { + const parameters = isRecord(node.parameters) ? node.parameters : {}; + return parameters.operation === SEND_AND_WAIT_OPERATION; +} + +/** All `main` edges of the workflow, in stable declaration order. */ +function mainEdges(json: WorkflowJSON): Edge[] { + const edges: Edge[] = []; + for (const [source, byType] of Object.entries(json.connections ?? {})) { + if (!isRecord(byType) || !Array.isArray(byType.main)) continue; + for (const group of byType.main) { + if (!Array.isArray(group)) continue; + for (const connection of group) { + if (isRecord(connection) && typeof connection.node === 'string') { + edges.push({ source, target: connection.node }); + } + } + } + } + return edges; +} + +function buildAdjacency(edges: Edge[], skip?: Edge): Map { + const adjacency = new Map(); + for (const edge of edges) { + if (skip && edge.source === skip.source && edge.target === skip.target) continue; + const targets = adjacency.get(edge.source); + if (targets) targets.push(edge.target); + else adjacency.set(edge.source, [edge.target]); + } + return adjacency; +} + +function reaches(edges: Edge[], from: string, to: string, skip?: Edge): boolean { + const adjacency = buildAdjacency(edges, skip); + // Seed with successors so `from === to` requires traversing at least one edge. + const queue = [...(adjacency.get(from) ?? [])]; + const visited = new Set(); + while (queue.length > 0) { + const current = queue.pop(); + if (current === undefined) break; + if (current === to) return true; + if (visited.has(current)) continue; + visited.add(current); + queue.push(...(adjacency.get(current) ?? [])); + } + return false; +} + +function reachableFrom(edges: Edge[], sources: string[], skip?: Edge): Set { + const adjacency = buildAdjacency(edges, skip); + const visited = new Set(sources); + const queue = [...sources]; + while (queue.length > 0) { + const current = queue.pop(); + if (current === undefined) break; + for (const next of adjacency.get(current) ?? []) { + if (visited.has(next)) continue; + visited.add(next); + queue.push(next); + } + } + return visited; +} + +/** + * Find a `main` edge whose removal breaks every cycle through `gateName` + * without shrinking the set of nodes reachable from the triggers — i.e. a + * true loop-back edge whose cut costs no coverage. Returns undefined when no + * such edge exists (e.g. the loop-back edge is also the only inbound path). + */ +export function findCycleCutEdge(json: WorkflowJSON, gateName: string): Edge | undefined { + const edges = mainEdges(json); + const triggers = (json.nodes ?? []) + .filter( + (node): node is WorkflowNode & { name: string } => + Boolean(node.name) && !node.disabled && isTriggerNodeType(node.type), + ) + .map((node) => node.name); + if (triggers.length === 0) return undefined; + if (!reaches(edges, gateName, gateName)) return undefined; + + const baseline = reachableFrom(edges, triggers); + for (const edge of edges) { + if (reaches(edges, gateName, gateName, edge)) continue; + const afterCut = reachableFrom(edges, triggers, edge); + const preservesCoverage = [...baseline].every((node) => afterCut.has(node)); + if (preservesCoverage) return edge; + } + return undefined; +} + +type Decision = WaitGateScript['decisions'][number]; + +interface FormField { + fieldLabel?: string; + fieldName?: string; + fieldType?: string; + options: string[]; + multiselect: boolean; +} + +function toFormField(value: unknown): FormField | undefined { + if (!isRecord(value)) return undefined; + const fieldLabel = typeof value.fieldLabel === 'string' ? value.fieldLabel : undefined; + const fieldName = typeof value.fieldName === 'string' ? value.fieldName : undefined; + if (!fieldLabel && !fieldName) return undefined; + const fieldOptions = isRecord(value.fieldOptions) ? value.fieldOptions : {}; + const optionValues = Array.isArray(fieldOptions.values) ? fieldOptions.values : []; + const options = optionValues + .map((option) => (isRecord(option) ? option.option : undefined)) + .filter((option): option is string => typeof option === 'string'); + return { + fieldLabel, + fieldName, + fieldType: typeof value.fieldType === 'string' ? value.fieldType : undefined, + options, + multiselect: value.multiselectDropdown === true || value.multiselect === true, + }; +} + +/** UI-defined and static-JSON forms parse; JSON with expressions is dynamic → undefined. */ +function parseFormFields(parameters: Record): FormField[] | undefined { + let values: unknown[]; + if (parameters.defineForm === 'json') { + const jsonOutput = parameters.jsonOutput; + if (typeof jsonOutput !== 'string' || jsonOutput.includes('{{')) return undefined; + try { + const parsed: unknown = JSON.parse(jsonOutput); + if (!Array.isArray(parsed)) return undefined; + values = parsed; + } catch { + return undefined; + } + } else { + const formFields = isRecord(parameters.formFields) ? parameters.formFields : {}; + values = Array.isArray(formFields.values) ? formFields.values : []; + } + return values.map(toFormField).filter((field): field is FormField => field !== undefined); +} + +/** Mirrors nodes-base `getFieldIdentifier`: v2.4+ prefers `fieldName`, else the label. */ +function fieldKey(field: FormField, typeVersion: number): string | undefined { + if (typeVersion >= 2.4 && field.fieldName) return field.fieldName; + return field.fieldLabel ?? field.fieldName; +} + +function cannedFieldValue(field: FormField, passLabel: string): unknown { + switch (field.fieldType) { + case 'dropdown': + return field.multiselect ? field.options.slice(0, 1) : (field.options[0] ?? ''); + case 'radio': + return field.options[0] ?? ''; + case 'number': + return 1; + case 'date': + return '2026-01-01'; + case 'email': + return 'reviewer@example.com'; + case 'checkbox': + return false; + default: + return passLabel; + } +} + +/** Response fixtures per decision, mirroring the shared send-and-wait output shapes. */ +function deriveDecisions(node: WorkflowNode): Decision[] | undefined { + const parameters = isRecord(node.parameters) ? node.parameters : {}; + const responseType = + typeof parameters.responseType === 'string' ? parameters.responseType : 'approval'; + const respondedAt = new Date().toISOString(); + + if (responseType === 'approval') { + return [ + { label: 'approve', items: [{ data: { approved: true, respondedAt } }] }, + { label: 'decline', items: [{ data: { approved: false, respondedAt } }] }, + ]; + } + + if (responseType === 'freeText') { + return [ + { label: 'approve', items: [{ data: { text: APPROVE_TEXT, respondedAt } }] }, + { label: 'request changes', items: [{ data: { text: REVISE_TEXT, respondedAt } }] }, + ]; + } + + if (responseType === 'customForm') { + const fields = parseFormFields(parameters); + if (!fields || fields.length === 0) return undefined; + const typeVersion = typeof node.typeVersion === 'number' ? node.typeVersion : 0; + // The first single-select dropdown/radio with 2+ options drives the + // decision; without one the routing condition is free text we cannot + // enumerate. + const driver = fields.find( + (field) => + (field.fieldType === 'dropdown' || field.fieldType === 'radio') && + !field.multiselect && + field.options.length >= 2 && + fieldKey(field, typeVersion) !== undefined, + ); + if (!driver) return undefined; + + const buildPass = (driverValue: string): Decision => { + const data: Record = {}; + for (const field of fields) { + if (field.fieldType === 'html') continue; + const key = fieldKey(field, typeVersion); + if (!key) continue; + data[key] = field === driver ? driverValue : cannedFieldValue(field, driverValue); + } + data.respondedAt = respondedAt; + return { label: driverValue, items: [{ data }] }; + }; + return [buildPass(driver.options[0]), buildPass(driver.options[1])]; + } + + return undefined; +} + +/** + * Derive scripted-decision verification for the workflow's halted wait gates. + * V1 scripts exactly one send-and-wait gate; every underivable shape returns + * [] so the gate keeps its halt fallback. + */ +export function deriveWaitGateScripts( + workflow: WorkflowJSON, + plan: NodeSimulationVerdict[], +): WaitGateScript[] { + const halted = plan.filter((verdict) => verdict.verdict === 'simulate' && verdict.haltBranch); + if (halted.length !== 1) return []; + + const gateName = halted[0].nodeName; + const node = (workflow.nodes ?? []).find((candidate) => candidate.name === gateName); + if (!node || !isSendAndWaitNode(node)) return []; + + const cutEdge = findCycleCutEdge(workflow, gateName); + if (!cutEdge) return []; + + const decisions = deriveDecisions(node); + if (!decisions) return []; + + return [{ nodeName: gateName, cutEdge, decisions }]; +} diff --git a/packages/@n8n/instance-ai/src/tools/workflows/workflow-build-telemetry.ts b/packages/@n8n/instance-ai/src/tools/workflows/workflow-build-telemetry.ts index 1bc7ef237ea..a65f88dbb5e 100644 --- a/packages/@n8n/instance-ai/src/tools/workflows/workflow-build-telemetry.ts +++ b/packages/@n8n/instance-ai/src/tools/workflows/workflow-build-telemetry.ts @@ -57,3 +57,29 @@ export function trackWorkflowSourceBuild( : {}), }); } + +/** + * Emitted once per successful build whose plan halts wait gates, so we learn + * how often scripted verification applies and whether multi-gate loop shapes + * (currently always halt-fallback) occur in the wild. + */ +export function trackWaitGateVerificationPlan( + context: InstanceAiContext, + input: { + haltedGateCount: number; + scriptedGateCount: number; + savedWorkflowId: string; + }, +): void { + if (input.haltedGateCount === 0) return; + const buildContext = context.workflowBuildContext; + context.trackTelemetry?.('instance_ai_wait_gate_verification_plan', { + halted_gate_count: input.haltedGateCount, + scripted_gate_count: input.scriptedGateCount, + multi_gate: input.haltedGateCount >= 2, + scripted: input.scriptedGateCount > 0, + workflow_id: input.savedWorkflowId, + thread_id: context.threadId ?? buildContext?.threadId ?? 'unknown', + run_id: buildContext?.runId ?? context.runId ?? 'unknown', + }); +} diff --git a/packages/@n8n/instance-ai/src/tools/workflows/workflow-source-compiler.ts b/packages/@n8n/instance-ai/src/tools/workflows/workflow-source-compiler.ts index f99028e487c..11787285de1 100644 --- a/packages/@n8n/instance-ai/src/tools/workflows/workflow-source-compiler.ts +++ b/packages/@n8n/instance-ai/src/tools/workflows/workflow-source-compiler.ts @@ -136,6 +136,12 @@ function parseSandboxWarnings(value: unknown): ValidationWarning[] { code: warning.code, message: warning.message, nodeName: typeof warning.nodeName === 'string' ? warning.nodeName : undefined, + severity: + warning.severity === 'informational' || + warning.severity === 'warning' || + warning.severity === 'error' + ? warning.severity + : undefined, }); } diff --git a/packages/@n8n/instance-ai/src/tools/workflows/workflow-validation-warnings.ts b/packages/@n8n/instance-ai/src/tools/workflows/workflow-validation-warnings.ts index 13a3c3b5e9d..3cc62228228 100644 --- a/packages/@n8n/instance-ai/src/tools/workflows/workflow-validation-warnings.ts +++ b/packages/@n8n/instance-ai/src/tools/workflows/workflow-validation-warnings.ts @@ -1,11 +1,20 @@ +import { partitionValidationIssues, type IssueSeverity } from '@n8n/workflow-sdk'; + export interface ValidationWarning { code: string; message: string; nodeName?: string; + /** Set at the creation site; `informational` never blocks save. */ + severity?: IssueSeverity; } export function collectValidationIssues( - issues: Array<{ code: string; message: string; nodeName?: string }>, + issues: Array<{ + code: string; + message: string; + nodeName?: string; + severity?: IssueSeverity; + }>, allWarnings: ValidationWarning[], ): void { for (const issue of issues) { @@ -13,31 +22,17 @@ export function collectValidationIssues( code: issue.code, message: issue.message, nodeName: issue.nodeName, + severity: issue.severity, }); } } export function partitionWarnings(warnings: ValidationWarning[]): { - errors: ValidationWarning[]; + blocking: ValidationWarning[]; informational: ValidationWarning[]; } { - // auto_imported_sdk_symbols marks a recovered build; it must not fail validation. - const informationalCodes = new Set([ - 'MISSING_TRIGGER', - 'DISCONNECTED_NODE', - 'auto_imported_sdk_symbols', - ]); - - const errors: ValidationWarning[] = []; - const informational: ValidationWarning[] = []; - - for (const warning of warnings) { - if (informationalCodes.has(warning.code)) { - informational.push(warning); - } else { - errors.push(warning); - } - } - - return { errors, informational }; + // Severity is set where each issue is created (SDK validators / lint / + // Instance AI host detectors). CLI validate and this save gate share + // {@link partitionValidationIssues}. + return partitionValidationIssues(warnings); } diff --git a/packages/@n8n/instance-ai/src/types.ts b/packages/@n8n/instance-ai/src/types.ts index 7b55825e2f0..a9d13bb40be 100644 --- a/packages/@n8n/instance-ai/src/types.ts +++ b/packages/@n8n/instance-ai/src/types.ts @@ -387,6 +387,19 @@ export interface InstanceAiExecutionService { verificationPinData?: Record; /** When set, execute this specific trigger node instead of auto-detecting. */ triggerNodeName?: string; + /** + * Marks the run as a build verification rather than a run the user asked + * for. Verification uses a production execution mode so triggers behave + * realistically, which would otherwise make a failed attempt dispatch the + * workflow's error workflow as if production had broken. + */ + isVerificationRun?: boolean; + /** + * Connections removed from this run's ephemeral workflow copy (the saved + * workflow is untouched). Used to sever a loop edge so scripted wait-gate + * verification passes are acyclic. + */ + omitConnections?: Array<{ source: string; target: string }>; abortSignal?: AbortSignal; }, ): Promise; @@ -893,6 +906,8 @@ export interface BuilderDelegateSession { * can outlive the parent trace's root finalization. */ memoryTaskObserver?: (event: ScopedMemoryTaskEvent) => void; + /** Host run's abort signal, so a user stop ends the builder's own loop rather than only our consumption of it. */ + abortSignal: AbortSignal; } /** A builder turn stream: consumable by normalizeStreamSource, plus final text. */ @@ -978,7 +993,7 @@ export interface InstanceAiContext { /** Optional — present when the host wires config-based eval support. */ evaluationConfigService?: InstanceAiEvaluationConfigService; /** The target n8n Agent being built/edited via the build-agent sub-agent tool. */ - agentBuilderTarget?: { agentId: string; projectId: string }; + agentBuilderTarget?: { agentId: string; projectId: string; name?: string; ref?: string }; /** Narrow builder delegate for the build-agent sub-agent tool (agents module active only). */ builderDelegate?: InstanceAiBuilderDelegate; /** diff --git a/packages/@n8n/instance-ai/src/workflow-loop/workflow-loop-state.ts b/packages/@n8n/instance-ai/src/workflow-loop/workflow-loop-state.ts index 036210de71d..e5175b9075e 100644 --- a/packages/@n8n/instance-ai/src/workflow-loop/workflow-loop-state.ts +++ b/packages/@n8n/instance-ai/src/workflow-loop/workflow-loop-state.ts @@ -252,6 +252,25 @@ export const nodeSimulationVerdictSchema = z.object({ export type NodeSimulationVerdict = z.infer; +/** + * Scripted verification for a wait gate on a loop: verify runs one pass per + * decision with `cutEdge` removed from the run's connections, so every pass + * is acyclic no matter which way the decision routes. The gate keeps its + * `haltBranch` verdict as the fallback for older readers of this outcome. + */ +export const waitGateScriptSchema = z.object({ + nodeName: z.string(), + /** Loop edge (main connection) removed for the scripted passes. */ + cutEdge: z.object({ source: z.string(), target: z.string() }), + /** Response fixtures, one verification pass per entry. */ + decisions: z + .array(z.object({ label: z.string(), items: z.array(z.record(z.unknown())) })) + .min(1) + .max(3), +}); + +export type WaitGateScript = z.infer; + export const workflowBuildOutcomeSchema = z.object({ workItemId: z.string(), runId: z.string().optional(), @@ -299,6 +318,8 @@ export const workflowBuildOutcomeSchema = z.object({ * this build, never persisted to the workflow. */ nodeSimulationPlan: z.array(nodeSimulationVerdictSchema).optional(), + /** Scripted decisions for wait gates on loops — see `waitGateScriptSchema`. */ + waitGateScripts: z.array(waitGateScriptSchema).optional(), /** * LLM-generated mock output for `simulate`-verdict nodes, keyed by node * name. Items are plain objects (no `{json}` envelope) — the shape diff --git a/packages/@n8n/instance-ai/src/workspace/builder-templates-service.ts b/packages/@n8n/instance-ai/src/workspace/builder-templates-service.ts index fecd7785bee..ec1a85f7c15 100644 --- a/packages/@n8n/instance-ai/src/workspace/builder-templates-service.ts +++ b/packages/@n8n/instance-ai/src/workspace/builder-templates-service.ts @@ -45,6 +45,7 @@ * * Never throws. */ +import { sleep } from '@n8n/utils/sleep'; import workflowSdkPackage from '@n8n/workflow-sdk/package.json'; import { createHash } from 'node:crypto'; import * as fsp from 'node:fs/promises'; @@ -542,10 +543,6 @@ function isRetryableStatus(status: number): boolean { return status === 408 || status === 429; } -async function sleep(ms: number): Promise { - await new Promise((resolve) => setTimeout(resolve, ms)); -} - async function atomicWriteFile(target: string, contents: Buffer | string): Promise { const tmp = `${target}.tmp-${process.pid}-${Date.now()}`; try { diff --git a/packages/@n8n/instance-ai/src/workspace/sandbox-fs.ts b/packages/@n8n/instance-ai/src/workspace/sandbox-fs.ts index 6efcac05a41..95598e0ab52 100644 --- a/packages/@n8n/instance-ai/src/workspace/sandbox-fs.ts +++ b/packages/@n8n/instance-ai/src/workspace/sandbox-fs.ts @@ -57,7 +57,7 @@ function ioResourceLabel(options?: SandboxIoRetryOptions): string { return options?.resourceLabel ?? 'Sandbox file'; } -async function sleep(ms: number, abortSignal?: AbortSignal): Promise { +async function sleepUntilAbort(ms: number, abortSignal?: AbortSignal): Promise { throwIfAborted(abortSignal); await new Promise((resolve, reject) => { const timer = setTimeout(() => { @@ -96,7 +96,7 @@ export async function retryTransientSandboxIo( attempt, error: formatErrorForLog(error), }); - await sleep( + await sleepUntilAbort( Math.min(baseMs * 2 ** (attempt - 1), SANDBOX_IO_RETRY_BACKOFF_CAP_MS), options?.abortSignal, ); diff --git a/packages/@n8n/instance-ai/src/workspace/snapshot-manager.ts b/packages/@n8n/instance-ai/src/workspace/snapshot-manager.ts index 47089493e33..e6ee599328a 100644 --- a/packages/@n8n/instance-ai/src/workspace/snapshot-manager.ts +++ b/packages/@n8n/instance-ai/src/workspace/snapshot-manager.ts @@ -15,6 +15,7 @@ import type { Daytona, DaytonaError as TDaytonaError, Image } from '@daytona/sdk'; import type { RuntimeSkillSource } from '@n8n/agents'; import { DAYTONA_WORKSPACE_ROOT, loadDaytona } from '@n8n/agents/sandbox'; +import { sleep } from '@n8n/utils/sleep'; import { buildKnowledgeBaseWorkspaceBundle, @@ -41,9 +42,6 @@ const SNAPSHOT_BUILDING_STATES = new Set(['building', 'pending', 'pulling']); const SNAPSHOT_VERIFY_POLL_MS = 5_000; const DEFAULT_SNAPSHOT_VERIFY_TIMEOUT_S = 1_800; -async function sleep(ms: number): Promise { - await new Promise((resolve) => setTimeout(resolve, ms)); -} function isAlreadyExistsError(error: unknown): error is TDaytonaError { const { DaytonaError } = loadDaytona(); if (!(error instanceof DaytonaError)) return false; diff --git a/packages/@n8n/node-engine-compatibility/eslint.config.mjs b/packages/@n8n/node-engine-compatibility/eslint.config.mjs index 37c64ca3838..010e27cff51 100644 --- a/packages/@n8n/node-engine-compatibility/eslint.config.mjs +++ b/packages/@n8n/node-engine-compatibility/eslint.config.mjs @@ -1,4 +1,10 @@ import { defineConfig } from 'eslint/config'; import { nodeConfig } from '@n8n/eslint-config/node'; -export default defineConfig({ ignores: ['dist/**'] }, nodeConfig); +export default defineConfig({ ignores: ['dist/**'] }, nodeConfig, { + files: ['src/__tests__/**'], + rules: { + // Workflow fixtures key connections by node name, e.g. "When clicking Execute" + '@typescript-eslint/naming-convention': 'off', + }, +}); diff --git a/packages/@n8n/node-engine-compatibility/src/__tests__/fixtures.ts b/packages/@n8n/node-engine-compatibility/src/__tests__/fixtures.ts index dc53cfed73d..e02477a2688 100644 --- a/packages/@n8n/node-engine-compatibility/src/__tests__/fixtures.ts +++ b/packages/@n8n/node-engine-compatibility/src/__tests__/fixtures.ts @@ -3,6 +3,7 @@ import type { ExecuteContext } from 'n8n-core'; import { NoOp } from 'n8n-nodes-base/nodes/NoOp/NoOp.node'; import type { CloseFunction, + IConnections, IDataObject, IExecuteFunctions, INodeExecutionData, @@ -90,6 +91,28 @@ class NewStyleEcho extends Node { } } +class TwoOutputs implements INodeType { + description = { + displayName: 'Two Outputs', + name: 'twoOutputs', + group: ['transform'], + version: 1, + description: 'Emits on two output slots', + defaults: { name: 'Two Outputs' }, + inputs: [NodeConnectionTypes.Main], + outputs: [NodeConnectionTypes.Main, NodeConnectionTypes.Main], + properties: [], + } as unknown as INodeType['description']; + + async execute(this: IExecuteFunctions): Promise { + const items = this.getInputData(); + return await Promise.resolve([ + items, + items.map((item) => ({ json: { ...item.json, second: true } })), + ]); + } +} + const pushCloseFunction = (context: IExecuteFunctions, close: CloseFunction) => { (context as unknown as ExecuteContext).closeFunctions.push(close); }; @@ -163,6 +186,7 @@ const registry = new Map([ ['test.alwaysFails', new AlwaysFails()], ['test.noExecute', new NoExecute()], ['test.newStyleEcho', new NewStyleEcho() as unknown as INodeType], + ['test.twoOutputs', new TwoOutputs()], ['test.succeedsWithFailingCleanup', new SucceedsWithFailingCleanup()], ['test.failsWithFailingCleanup', new FailsWithFailingCleanup()], ['test.returnsEngineRequest', new ReturnsEngineRequest() as unknown as INodeType], @@ -191,14 +215,21 @@ export const testAdditionalDataFactory = async ( } as unknown as IWorkflowExecuteAdditionalData); export const v1Workflow = ( - nodes: Array<{ id: string; name: string; type: string; parameters?: IDataObject }>, + nodes: Array<{ + id: string; + name: string; + type: string; + typeVersion?: number; + parameters?: IDataObject; + }>, + connections: IConnections = {}, ): IWorkflowBase => ({ id: 'wf-1', name: 'fixture', active: false, nodes: nodes.map((n) => ({ typeVersion: 1, position: [0, 0], parameters: {}, ...n })), - connections: {}, + connections, }) as unknown as IWorkflowBase; export const stepRequest = ( diff --git a/packages/@n8n/node-engine-compatibility/src/__tests__/v1-step-executor.test.ts b/packages/@n8n/node-engine-compatibility/src/__tests__/v1-step-executor.test.ts index ef01d05e735..b8bdca2e1a2 100644 --- a/packages/@n8n/node-engine-compatibility/src/__tests__/v1-step-executor.test.ts +++ b/packages/@n8n/node-engine-compatibility/src/__tests__/v1-step-executor.test.ts @@ -137,6 +137,51 @@ describe('V1StepExecutor', () => { await expect(execution).rejects.toThrow(EngineRequestNotSupportedError); }); + it('routes multi-output results to their output slots', async () => { + const graph = converter.convert( + v1Workflow( + [ + { id: 't', name: 'Manual', type: 'n8n-nodes-base.manualTrigger' }, + { id: 'n', name: 'Splitter', type: 'test.twoOutputs' }, + { id: 'a', name: 'A', type: 'n8n-nodes-base.noOp' }, + { id: 'b', name: 'B', type: 'n8n-nodes-base.noOp' }, + ], + { + Manual: { main: [[{ node: 'Splitter', type: 'main', index: 0 }]] }, + Splitter: { + main: [ + [{ node: 'A', type: 'main', index: 0 }], + [{ node: 'B', type: 'main', index: 0 }], + ], + }, + }, + ), + ); + + const result = await executor.execute(stepRequest(graph, 'n', items({ a: 1 }))); + expect(result.outputs).toEqual([[{ json: { a: 1 } }], [{ json: { a: 1, second: true } }]]); + }); + + it('rejects batch steps until the engine iterates loops natively', async () => { + const graph = converter.convert( + v1Workflow( + [ + { id: 't', name: 'Manual', type: 'n8n-nodes-base.manualTrigger' }, + { id: 'loop', name: 'Loop', type: 'n8n-nodes-base.splitInBatches', typeVersion: 3 }, + { id: 'body', name: 'Body', type: 'n8n-nodes-base.noOp' }, + ], + { + Manual: { main: [[{ node: 'Loop', type: 'main', index: 0 }]] }, + Loop: { main: [[], [{ node: 'Body', type: 'main', index: 0 }]] }, + Body: { main: [[{ node: 'Loop', type: 'main', index: 0 }]] }, + }, + ), + ); + + const execution = executor.execute(stepRequest(graph, 'loop', items({ a: 1 }))); + await expect(execution).rejects.toThrow(UnsupportedStepTypeError); + }); + it('honors onError=continueRegularOutput as passthrough', async () => { const workflow = v1Workflow([ { id: 't', name: 'Manual', type: 'n8n-nodes-base.manualTrigger' }, diff --git a/packages/@n8n/node-engine-compatibility/src/__tests__/v1-workflow-converter.test.ts b/packages/@n8n/node-engine-compatibility/src/__tests__/v1-workflow-converter.test.ts index 56a5c98a7e4..85e083b8bba 100644 --- a/packages/@n8n/node-engine-compatibility/src/__tests__/v1-workflow-converter.test.ts +++ b/packages/@n8n/node-engine-compatibility/src/__tests__/v1-workflow-converter.test.ts @@ -1,7 +1,12 @@ -import type { INode, IWorkflowBase } from 'n8n-workflow'; +import type { IConnection, INode, IWorkflowBase } from 'n8n-workflow'; import { describe, expect, it } from 'vitest'; -import { UnsupportedTriggerError } from '../errors'; +import { + UnsupportedConnectionTypeError, + UnsupportedCycleError, + UnsupportedTriggerError, + UnsupportedLoopEntryError, +} from '../errors'; import { V1WorkflowConverter } from '../v1-workflow-converter'; const converter = new V1WorkflowConverter(); @@ -15,6 +20,18 @@ const manualTrigger: INode = { parameters: {}, }; +const node = (id: string, name: string, extra: Partial = {}): INode => ({ + id, + name, + type: 'n8n-nodes-base.noOp', + typeVersion: 1, + position: [0, 0], + parameters: {}, + ...extra, +}); + +const main = (target: string, index = 0): IConnection => ({ node: target, type: 'main', index }); + function workflow(overrides: Partial): IWorkflowBase { return { id: 'wf-1', @@ -28,6 +45,13 @@ function workflow(overrides: Partial): IWorkflowBase { } as IWorkflowBase; } +// NOTE: Topology diagrams follow the convention of core's partial-execution +// tests (drawn with https://asciiflow.com/#/). If you update a test, update +// its diagram. +// +// oN / iN the output / input slot, where it matters +// XX the node is disabled +// (back) the loop-return edge expected to be marked `isBackEdge` describe('V1WorkflowConverter', () => { describe('trigger nodes', () => { it('maps a manual trigger to a single trigger graph node', () => { @@ -146,4 +170,609 @@ describe('V1WorkflowConverter', () => { ).toThrow(UnsupportedTriggerError); }); }); + + describe('edges', () => { + it('maps connections to id-keyed edges with slot indexes', () => { + // ┌───────┐ ┌─┐ + // │trigger├───►│A│ + // └───────┘ └─┘ + const graph = converter.convert( + workflow({ + nodes: [manualTrigger, node('a-uuid', 'A')], + connections: { 'When clicking Execute': { main: [[main('A')]] } }, + }), + ); + + expect(graph.edges).toEqual([ + { from: 'trigger-uuid', to: 'a-uuid', outputIndex: 0, inputIndex: 0 }, + ]); + }); + + it('maps multi-output sources to distinct output slots', () => { + // ┌───────┐ ┌──┐ o0 ┌─┐ + // │trigger├───►│ ├──────►│A│ + // └───────┘ │IF│ └─┘ + // │ │ o1 ┌─┐ + // │ ├──────►│B│ + // └──┘ └─┘ + const graph = converter.convert( + workflow({ + nodes: [manualTrigger, node('if-uuid', 'IF'), node('a-uuid', 'A'), node('b-uuid', 'B')], + connections: { + 'When clicking Execute': { main: [[main('IF')]] }, + IF: { main: [[main('A')], [main('B')]] }, + }, + }), + ); + + expect(graph.edges).toContainEqual({ + from: 'if-uuid', + to: 'a-uuid', + outputIndex: 0, + inputIndex: 0, + }); + expect(graph.edges).toContainEqual({ + from: 'if-uuid', + to: 'b-uuid', + outputIndex: 1, + inputIndex: 0, + }); + }); + + it('maps multi-input targets to distinct input slots', () => { + // ┌─┐ i0 ┌─────┐ + // │A├─────────►│ │ + // └─┘ │Merge│ + // ┌─┐ i1 │ │ + // │B├─────────►│ │ + // └─┘ └─────┘ + const graph = converter.convert( + workflow({ + nodes: [node('a-uuid', 'A'), node('b-uuid', 'B'), node('merge-uuid', 'Merge')], + connections: { + A: { main: [[main('Merge', 0)]] }, + B: { main: [[main('Merge', 1)]] }, + }, + }), + ); + + expect(graph.edges).toEqual([ + { from: 'a-uuid', to: 'merge-uuid', outputIndex: 0, inputIndex: 0 }, + { from: 'b-uuid', to: 'merge-uuid', outputIndex: 0, inputIndex: 1 }, + ]); + }); + + it('leaves rootless nodes unconnected', () => { + const graph = converter.convert( + workflow({ nodes: [manualTrigger, node('orphan-uuid', 'Orphan')], connections: {} }), + ); + + expect(graph.nodes).toHaveLength(2); + expect(graph.edges).toEqual([]); + }); + + it('drops connections referencing missing nodes', () => { + // ┌─────┐ ┌─┐ ┌───────┐ ┌─────────────┐ + // │Ghost├───►│A│ │trigger├───►│Another Ghost│ + // └─────┘ └─┘ └───────┘ └─────────────┘ + // neither ghost exists as a node + const graph = converter.convert( + workflow({ + nodes: [manualTrigger, node('a-uuid', 'A')], + connections: { + Ghost: { main: [[main('A')]] }, + 'When clicking Execute': { main: [[main('Another Ghost')]] }, + }, + }), + ); + + expect(graph.edges).toEqual([]); + }); + + it('rejects non-main connection types', () => { + expect(() => + converter.convert( + workflow({ + nodes: [node('tool-uuid', 'Tool'), node('agent-uuid', 'Agent')], + connections: { + Tool: { ai_tool: [[{ node: 'Agent', type: 'ai_tool', index: 0 }]] }, + }, + }), + ), + ).toThrow(UnsupportedConnectionTypeError); + }); + }); + + describe('disabled nodes', () => { + it('splices out a disabled node, keeping the outer slot indexes', () => { + // XX + // ┌───────┐ ┌─┐ o1 ┌────────┐ i2 ┌─┐ + // │trigger├───►│A├───────►│Disabled├───────►│S│ + // └───────┘ └─┘ └────────┘ └─┘ + const graph = converter.convert( + workflow({ + nodes: [ + manualTrigger, + node('a-uuid', 'A'), + node('d-uuid', 'Disabled', { disabled: true }), + node('s-uuid', 'S'), + ], + connections: { + 'When clicking Execute': { main: [[main('A')]] }, + A: { main: [[], [main('Disabled')]] }, + Disabled: { main: [[main('S', 2)]] }, + }, + }), + ); + + expect(graph.nodes.map((n) => n.id)).toEqual(['trigger-uuid', 'a-uuid', 's-uuid']); + expect(graph.edges).toContainEqual({ + from: 'a-uuid', + to: 's-uuid', + outputIndex: 1, + inputIndex: 2, + }); + expect(graph.edges).toHaveLength(2); + }); + + it('splices chained disabled nodes transitively', () => { + // XX XX + // ┌─┐ ┌──┐ ┌──┐ ┌─┐ + // │A├──────►│D1├────►│D2├────►│B│ + // └─┘ └──┘ └──┘ └─┘ + const graph = converter.convert( + workflow({ + nodes: [ + node('a-uuid', 'A'), + node('d1-uuid', 'D1', { disabled: true }), + node('d2-uuid', 'D2', { disabled: true }), + node('b-uuid', 'B'), + ], + connections: { + A: { main: [[main('D1')]] }, + D1: { main: [[main('D2')]] }, + D2: { main: [[main('B')]] }, + }, + }), + ); + + expect(graph.edges).toEqual([ + { from: 'a-uuid', to: 'b-uuid', outputIndex: 0, inputIndex: 0 }, + ]); + }); + + it('fans out the splice as a cross-product', () => { + // XX + // ┌──┐ ┌────────┐ ┌──┐ + // │P1├──────►│ ├────►│S1│ + // └──┘ │Disabled│ └──┘ + // ┌──┐ │ │ ┌──┐ + // │P2├──────►│ ├────►│S2│ + // └──┘ └────────┘ └──┘ + const graph = converter.convert( + workflow({ + nodes: [ + node('p1-uuid', 'P1'), + node('p2-uuid', 'P2'), + node('d-uuid', 'Disabled', { disabled: true }), + node('s1-uuid', 'S1'), + node('s2-uuid', 'S2'), + ], + connections: { + P1: { main: [[main('Disabled')]] }, + P2: { main: [[main('Disabled')]] }, + Disabled: { main: [[main('S1'), main('S2')]] }, + }, + }), + ); + + expect(graph.edges).toHaveLength(4); + for (const from of ['p1-uuid', 'p2-uuid']) { + for (const to of ['s1-uuid', 's2-uuid']) { + expect(graph.edges).toContainEqual({ from, to, outputIndex: 0, inputIndex: 0 }); + } + } + }); + + it('dedupes identical edges created by the splice', () => { + // XX + // ┌──┐ + // ┌───►│D1├────┐ + // ┌─┐ │ └──┘ │ ┌─┐ + // │A├───┤ XX ├──►│S│ + // └─┘ │ ┌──┐ │ └─┘ + // └───►│D2├────┘ + // └──┘ + const graph = converter.convert( + workflow({ + nodes: [ + node('a-uuid', 'A'), + node('d1-uuid', 'D1', { disabled: true }), + node('d2-uuid', 'D2', { disabled: true }), + node('s-uuid', 'S'), + ], + connections: { + A: { main: [[main('D1'), main('D2')]] }, + D1: { main: [[main('S')]] }, + D2: { main: [[main('S')]] }, + }, + }), + ); + + expect(graph.edges).toEqual([ + { from: 'a-uuid', to: 's-uuid', outputIndex: 0, inputIndex: 0 }, + ]); + }); + + it('drops predecessors feeding input slots other than 0, matching v1 pass-through', () => { + // XX + // ┌─┐ i0 ┌─────┐ + // │A├────────────►│ │ ┌─┐ + // └─┘ │Merge├────►│S│ + // ┌─┐ i1 │ │ └─┘ + // │B├────────────►│ │ + // └─┘ └─────┘ + const graph = converter.convert( + workflow({ + nodes: [ + node('a-uuid', 'A'), + node('b-uuid', 'B'), + node('merge-uuid', 'Merge', { disabled: true }), + node('s-uuid', 'S'), + ], + connections: { + A: { main: [[main('Merge', 0)]] }, + B: { main: [[main('Merge', 1)]] }, + Merge: { main: [[main('S')]] }, + }, + }), + ); + + expect(graph.edges).toEqual([ + { from: 'a-uuid', to: 's-uuid', outputIndex: 0, inputIndex: 0 }, + ]); + }); + + it('ignores unsupported constructs on disabled nodes', () => { + // XX + // ┌───────┐ ┌─┐ + // │Webhook├───►│A│ an unsupported trigger, but disabled + // └───────┘ └─┘ + const graph = converter.convert( + workflow({ + nodes: [ + node('webhook-uuid', 'Webhook', { type: 'n8n-nodes-base.webhook', disabled: true }), + node('a-uuid', 'A'), + ], + connections: { Webhook: { main: [[main('A')]] } }, + }), + ); + + expect(graph.nodes.map((n) => n.id)).toEqual(['a-uuid']); + expect(graph.edges).toEqual([]); + }); + }); + + describe('loops', () => { + it('maps Split In Batches to a batch step and marks its loop-back edge', () => { + // ┌───────┐ ┌────┐ o0 ┌────┐ + // │trigger├───►│ ├──────►│Done│ + // └───────┘ │Loop│ └────┘ + // │ │ o1 ┌────┐ + // │ ├──────►│Body│ + // └─▲──┘ └──┬─┘ + // └───(back)────┘ + const graph = converter.convert( + workflow({ + nodes: [ + manualTrigger, + node('loop-uuid', 'Loop', { type: 'n8n-nodes-base.splitInBatches', typeVersion: 3 }), + node('done-uuid', 'Done'), + node('body-uuid', 'Body'), + ], + connections: { + 'When clicking Execute': { main: [[main('Loop')]] }, + Loop: { main: [[main('Done')], [main('Body')]] }, + Body: { main: [[main('Loop')]] }, + }, + }), + ); + + expect(graph.nodes).toContainEqual({ + id: 'loop-uuid', + name: 'Loop', + type: 'batch', + config: { + nodeType: 'n8n-nodes-base.splitInBatches', + typeVersion: 3, + parameters: {}, + continueOnFail: false, + }, + }); + expect(graph.edges).toEqual([ + { from: 'trigger-uuid', to: 'loop-uuid', outputIndex: 0, inputIndex: 0 }, + { from: 'loop-uuid', to: 'done-uuid', outputIndex: 0, inputIndex: 0 }, + { from: 'loop-uuid', to: 'body-uuid', outputIndex: 1, inputIndex: 0 }, + { from: 'body-uuid', to: 'loop-uuid', outputIndex: 0, inputIndex: 0, isBackEdge: true }, + ]); + }); + + it('marks both back edges of nested loops, but not the loop-entry edge', () => { + // ┌───────┐ ┌─────┐ o0 ┌───┐ + // │trigger├───►│ ├──────►│End│ + // └───────┘ │Outer│ └───┘ + // │ │ o1 ┌─────┐ o0 ┌───────────┐ + // │ ├──────►│ ├──────►│After Inner│ + // └──▲──┘ │Inner│ └─────┬─────┘ + // │ │ │ o1 ┌────┐│ + // │ │ ├──────►│Body││ + // │ └──▲──┘ └──┬─┘│ + // │ └───(back)────┘ │ + // └───(back)─────────────────────┘ + const graph = converter.convert( + workflow({ + nodes: [ + manualTrigger, + node('outer-uuid', 'Outer', { type: 'n8n-nodes-base.splitInBatches', typeVersion: 3 }), + node('inner-uuid', 'Inner', { type: 'n8n-nodes-base.splitInBatches', typeVersion: 3 }), + node('body-uuid', 'Body'), + node('after-inner-uuid', 'After Inner'), + node('end-uuid', 'End'), + ], + connections: { + 'When clicking Execute': { main: [[main('Outer')]] }, + Outer: { main: [[main('End')], [main('Inner')]] }, + Inner: { main: [[main('After Inner')], [main('Body')]] }, + Body: { main: [[main('Inner')]] }, + 'After Inner': { main: [[main('Outer')]] }, + }, + }), + ); + + const backEdges = graph.edges.filter((edge) => edge.isBackEdge); + expect(backEdges).toEqual([ + { from: 'body-uuid', to: 'inner-uuid', outputIndex: 0, inputIndex: 0, isBackEdge: true }, + { + from: 'after-inner-uuid', + to: 'outer-uuid', + outputIndex: 0, + inputIndex: 0, + isBackEdge: true, + }, + ]); + }); + + it('marks the loop-back edge of a rootless loop regardless of node order', () => { + // ┌────┐ o1 ┌────┐ + // │Loop├──────►│Body│ nothing else points in + // └─▲──┘ └──┬─┘ + // └───(back)────┘ + const graph = converter.convert( + workflow({ + nodes: [ + node('body-uuid', 'Body'), + node('loop-uuid', 'Loop', { type: 'n8n-nodes-base.splitInBatches', typeVersion: 3 }), + ], + connections: { + Body: { main: [[main('Loop')]] }, + Loop: { main: [[], [main('Body')]] }, + }, + }), + ); + + expect(graph.edges).toEqual([ + { from: 'body-uuid', to: 'loop-uuid', outputIndex: 0, inputIndex: 0, isBackEdge: true }, + { from: 'loop-uuid', to: 'body-uuid', outputIndex: 1, inputIndex: 0 }, + ]); + }); + + it('rejects a loop entered mid-body, regardless of node order', () => { + // ┌──┐ ┌────┐ o1 ┌────┐ ┌──┐ + // │T1├───►│Loop├──────►│Body│◄───┤T2│ + // └──┘ └─▲──┘ └──┬─┘ └──┘ + // └─────────────┘ + // two ways into the loop: through Loop (T1) and mid-body (T2) + const t1 = node('t1-uuid', 'T1', { type: 'n8n-nodes-base.manualTrigger' }); + const t2 = node('t2-uuid', 'T2', { type: 'n8n-nodes-base.manualTrigger' }); + const loop = node('loop-uuid', 'Loop', { + type: 'n8n-nodes-base.splitInBatches', + typeVersion: 3, + }); + const body = node('body-uuid', 'Body'); + const connections = { + T1: { main: [[main('Loop')]] }, + T2: { main: [[main('Body')]] }, + Loop: { main: [[], [main('Body')]] }, + Body: { main: [[main('Loop')]] }, + }; + + for (const nodes of [ + [t1, t2, loop, body], + [t2, t1, loop, body], + ]) { + expect(() => converter.convert(workflow({ nodes, connections }))).toThrow( + UnsupportedLoopEntryError, + ); + } + }); + + it('converts a loop fed by multiple triggers through its batch node', () => { + // ┌──┐ ┌────┐ o1 ┌────┐ + // │T1├───►│ ├──────►│Body│ + // └──┘ │Loop│ └──┬─┘ + // ┌──┐ │ │ │ + // │T2├───►│ │◄─(back)──┘ + // └──┘ └────┘ one way in, used by both triggers + const graph = converter.convert( + workflow({ + nodes: [ + node('t1-uuid', 'T1', { type: 'n8n-nodes-base.manualTrigger' }), + node('t2-uuid', 'T2', { type: 'n8n-nodes-base.manualTrigger' }), + node('loop-uuid', 'Loop', { type: 'n8n-nodes-base.splitInBatches', typeVersion: 3 }), + node('body-uuid', 'Body'), + ], + connections: { + T1: { main: [[main('Loop')]] }, + T2: { main: [[main('Loop')]] }, + Loop: { main: [[], [main('Body')]] }, + Body: { main: [[main('Loop')]] }, + }, + }), + ); + + const backEdges = graph.edges.filter((edge) => edge.isBackEdge); + expect(backEdges).toEqual([ + { from: 'body-uuid', to: 'loop-uuid', outputIndex: 0, inputIndex: 0, isBackEdge: true }, + ]); + }); + + it('rejects a non-batch cycle nested inside a loop body', () => { + // ┌───────┐ ┌────┐ o1 ┌─┐ ┌─┐ + // │trigger├───►│Loop├──────►│A├────►│B│ + // └───────┘ └─▲──┘ └▲┘ └┬┘ + // │ └───────┤ + // └───────────────────┘ + // B feeds both Loop and A, making A -> B -> A a cycle with no batch node + expect(() => + converter.convert( + workflow({ + nodes: [ + manualTrigger, + node('loop-uuid', 'Loop', { type: 'n8n-nodes-base.splitInBatches', typeVersion: 3 }), + node('a-uuid', 'A'), + node('b-uuid', 'B'), + ], + connections: { + 'When clicking Execute': { main: [[main('Loop')]] }, + Loop: { main: [[], [main('A')]] }, + A: { main: [[main('B')]] }, + B: { main: [[main('A'), main('Loop')]] }, + }, + }), + ), + ).toThrow(UnsupportedCycleError); + }); + + it('rejects an inner loop entered mid-body by the outer loop, regardless of edge order', () => { + // ┌───────┐ ┌─────┐ o1 ┌─────┐ o1 ┌──┐ + // │trigger├──►│ ├────────►│ ├────────►│ │ + // └───────┘ │Outer│ │Inner│ │B1│ + // │ │ o1 │ │◄────────┤ │ + // │ ├────────────────────────►│ │ + // └──▲──┘ └──┬──┘ o0 └──┘ + // │ ▼ + // │ ┌───────────┐ + // └────────┤After Inner│ + // └───────────┘ + // Outer o1 fans out to both Inner and B1: two ways into the inner loop + const nodes = [ + manualTrigger, + node('outer-uuid', 'Outer', { type: 'n8n-nodes-base.splitInBatches', typeVersion: 3 }), + node('inner-uuid', 'Inner', { type: 'n8n-nodes-base.splitInBatches', typeVersion: 3 }), + node('b1-uuid', 'B1'), + node('after-inner-uuid', 'After Inner'), + ]; + + for (const outerLoopOutput of [ + [main('Inner'), main('B1')], + [main('B1'), main('Inner')], + ]) { + const connections = { + 'When clicking Execute': { main: [[main('Outer')]] }, + Outer: { main: [[], outerLoopOutput] }, + Inner: { main: [[main('After Inner')], [main('B1')]] }, + B1: { main: [[main('Inner')]] }, + 'After Inner': { main: [[main('Outer')]] }, + }; + + expect(() => converter.convert(workflow({ nodes, connections }))).toThrow( + UnsupportedLoopEntryError, + ); + } + }); + + it('rejects rootless nested loops as ambiguous, regardless of node order', () => { + // ┌─────┐ o1 ┌─────┐ o0 ┌───────────┐ + // │ ├──────►│ ├──────►│After Inner│ + // │Outer│ │Inner│ └─────┬─────┘ + // │ │ │ │ o1 ┌────┐│ + // │ │ │ ├──────►│Body││ + // └──▲──┘ └──▲──┘ └──┬─┘│ + // │ └─────────────┘ │ + // └──────────────────────────────┘ + // no trigger anywhere: no way to tell which loop is the outer one + expect(() => + converter.convert( + workflow({ + nodes: [ + node('inner-uuid', 'Inner', { + type: 'n8n-nodes-base.splitInBatches', + typeVersion: 3, + }), + node('outer-uuid', 'Outer', { + type: 'n8n-nodes-base.splitInBatches', + typeVersion: 3, + }), + node('body-uuid', 'Body'), + node('after-inner-uuid', 'After Inner'), + ], + connections: { + Outer: { main: [[], [main('Inner')]] }, + Inner: { main: [[main('After Inner')], [main('Body')]] }, + Body: { main: [[main('Inner')]] }, + 'After Inner': { main: [[main('Outer')]] }, + }, + }), + ), + ).toThrow(UnsupportedLoopEntryError); + }); + + it('converts chained rootless loops, which have an unambiguous entry', () => { + // ┌──┐ o1 ┌──┐ ┌──┐ o1 ┌──┐ + // │L1├──────►│B1│ │L2├──────►│B2│ + // └─▲┘ └┬─┘ └─▲┘ └┬─┘ + // └─(back)──┘ └─(back)──┘ + // L1 -o0-> L2 chains the loops, no trigger anywhere + const graph = converter.convert( + workflow({ + nodes: [ + node('l2-uuid', 'L2', { type: 'n8n-nodes-base.splitInBatches', typeVersion: 3 }), + node('l1-uuid', 'L1', { type: 'n8n-nodes-base.splitInBatches', typeVersion: 3 }), + node('b1-uuid', 'B1'), + node('b2-uuid', 'B2'), + ], + connections: { + L1: { main: [[main('L2')], [main('B1')]] }, + B1: { main: [[main('L1')]] }, + L2: { main: [[], [main('B2')]] }, + B2: { main: [[main('L2')]] }, + }, + }), + ); + + const backEdges = graph.edges.filter((edge) => edge.isBackEdge); + expect(backEdges).toEqual([ + { from: 'b1-uuid', to: 'l1-uuid', outputIndex: 0, inputIndex: 0, isBackEdge: true }, + { from: 'b2-uuid', to: 'l2-uuid', outputIndex: 0, inputIndex: 0, isBackEdge: true }, + ]); + }); + + it('rejects cycles that are not Split In Batches loops', () => { + // ┌─┐ ┌─┐ + // │A├────►│B│ + // │ │◄────┤ │ + // └─┘ └─┘ + expect(() => + converter.convert( + workflow({ + nodes: [node('a-uuid', 'A'), node('b-uuid', 'B')], + connections: { + A: { main: [[main('B')]] }, + B: { main: [[main('A')]] }, + }, + }), + ), + ).toThrow(UnsupportedCycleError); + }); + }); }); diff --git a/packages/@n8n/node-engine-compatibility/src/errors.ts b/packages/@n8n/node-engine-compatibility/src/errors.ts index 2dc73e40a60..16c78854558 100644 --- a/packages/@n8n/node-engine-compatibility/src/errors.ts +++ b/packages/@n8n/node-engine-compatibility/src/errors.ts @@ -1,5 +1,7 @@ import { UserError } from 'n8n-workflow'; +const quote = (names: string[]) => names.map((name) => `"${name}"`).join(', '); + /** * Thrown when a v1 workflow uses a construct the converter does not (yet) * support. Caused by user-provided workflow content, hence a `UserError`. @@ -14,6 +16,30 @@ export class UnsupportedTriggerError extends UserError { } } +export class UnsupportedConnectionTypeError extends UserError { + constructor(nodeName: string, connectionType: string) { + super( + `Node "${nodeName}" has a "${connectionType}" connection, which is not supported yet. Only "main" connections are currently supported.`, + ); + } +} + +export class UnsupportedCycleError extends UserError { + constructor(nodeNames: string[]) { + super( + `The cycle involving ${quote(nodeNames)} is not supported yet. Only Split In Batches loops are currently supported.`, + ); + } +} + +export class UnsupportedLoopEntryError extends UserError { + constructor(memberNames: string[], entryNames: string[]) { + super( + `The loop formed by ${quote(memberNames)} must be entered only through its Split In Batches node, but its entry points are: ${quote(entryNames)}.`, + ); + } +} + export class UnsupportedStepTypeError extends UserError { constructor(stepType: string) { super(`V1StepExecutor only handles 'v1-node' steps, got '${stepType}'`); diff --git a/packages/@n8n/node-engine-compatibility/src/v1-workflow-converter.ts b/packages/@n8n/node-engine-compatibility/src/v1-workflow-converter.ts index 15568580d71..9ab0c580fd4 100644 --- a/packages/@n8n/node-engine-compatibility/src/v1-workflow-converter.ts +++ b/packages/@n8n/node-engine-compatibility/src/v1-workflow-converter.ts @@ -1,10 +1,18 @@ -import type { GraphNode, WorkflowGraph } from '@n8n/engine'; -import type { INode, IWorkflowBase } from 'n8n-workflow'; +import type { GraphEdge, GraphNode, WorkflowGraph } from '@n8n/engine'; +import type { INode, INodeConnections, IWorkflowBase } from 'n8n-workflow'; -import { UnsupportedTriggerError, UnsupportedWorkflowError } from './errors'; +import { + UnsupportedConnectionTypeError, + UnsupportedCycleError, + UnsupportedLoopEntryError, + UnsupportedTriggerError, + UnsupportedWorkflowError, +} from './errors'; import type { V1NodeStepConfig } from './types'; const MANUAL_TRIGGER_TYPE = 'n8n-nodes-base.manualTrigger'; +const SPLIT_IN_BATCHES_TYPE = 'n8n-nodes-base.splitInBatches'; +const MAIN_CONNECTION_TYPE = 'main'; /** * Common v1 trigger types that don't end in "Trigger". Non-exhaustive: combined @@ -21,11 +29,44 @@ const KNOWN_TRIGGER_TYPES = new Set(['n8n-nodes-base.webhook', 'n8n-nodes-base.c * graph nodes and edges and never executes anything. Supported surface is kept * deliberately small (see the converter tests); unsupported constructs are * rejected with a clear error rather than silently mistranslated. + * + * Branching is purely structural, so an edge records which output slot feeds + * which input slot, nothing more. Branches leaving the same node are + * unordered, so the engine may run them in parallel. This deliberately + * **diverges from v1**, which runs them one after another in connection order. + * If branch B must run after branch A, that needs a connection from A to B. + * We do not invent connections to force v1 order. If it is ever needed, the + * engine can be made to support running branches one at a time. + * + * Disabled nodes never appear in the graph: every edge into their input + * slot 0 is joined to every edge leaving them, so A -> disabled -> B becomes + * A -> B, keeping A's `outputIndex` and B's `inputIndex`. Input slot 0 is + * the only one joined because v1 passes nothing else through. On the way out + * we keep edges from every output slot, deliberately **diverging from v1**, + * where the pass-through data leaves on output 0 only. + * + * The only cycle allowed is a Split In Batches loop: items leave the batch + * node, traverse the body, and return on an edge marked `isBackEdge`. We can + * only tell which edge is the return when the sole way into a loop, at every + * nesting level, is through its batch node. Hence cycles with no batch node, + * loops entered mid-body, and loops with more than one candidate entry are + * all rejected. */ export class V1WorkflowConverter { convert(workflow: IWorkflowBase): WorkflowGraph { - const nodes = workflow.nodes.map((node) => this.toGraphNode(node)); - return { nodes, edges: [] }; + const liveNodes = workflow.nodes.filter((node) => node.disabled !== true); + const disabledNodeIds = workflow.nodes + .filter((node) => node.disabled === true) + .map((node) => node.id); + + const nodes = liveNodes.map((node) => this.toGraphNode(node)); + + let edges = this.toEdges(workflow); + edges = this.spliceOutDisabledNodes(edges, disabledNodeIds); + edges = this.dedupeEdges(edges); + this.markBackEdges(nodes, edges); + + return { nodes, edges }; } private toGraphNode(node: INode): GraphNode { @@ -50,7 +91,9 @@ export class V1WorkflowConverter { continueOnFail: node.continueOnFail === true || node.onError === 'continueRegularOutput', }; - return { id: node.id, name: node.name, type: 'v1-node', config }; + const type = node.type === SPLIT_IN_BATCHES_TYPE ? 'batch' : 'v1-node'; + + return { id: node.id, name: node.name, type, config }; } /** Heuristic trigger detection — see {@link KNOWN_TRIGGER_TYPES}. */ @@ -61,4 +104,236 @@ export class V1WorkflowConverter { node.type.toLowerCase().endsWith('trigger') ); } + + private toEdges(workflow: IWorkflowBase): GraphEdge[] { + const idsByName = new Map(workflow.nodes.map((node) => [node.name, node.id])); + + return Object.entries(workflow.connections).flatMap(([sourceName, connectionsByType]) => + this.toEdgesForSource(sourceName, connectionsByType, idsByName), + ); + } + + /** + * v1 connections of one source node have the shape + * `{ main: [ [connection, ...], null, [connection, ...] ] }`: connections + * grouped by type, then by the source's output slot, where a slot with + * nothing connected may hold null. + */ + private toEdgesForSource( + sourceName: string, + connectionsByType: INodeConnections, + idsByName: Map, + ): GraphEdge[] { + const from = idsByName.get(sourceName); + const edges: GraphEdge[] = []; + + for (const [connectionType, outputSlots] of Object.entries(connectionsByType)) { + this.validateSupportedConnectionType(sourceName, connectionType); + + for (let outputIndex = 0; outputIndex < outputSlots.length; outputIndex++) { + for (const connection of outputSlots[outputIndex] ?? []) { + this.validateSupportedConnectionType(sourceName, connection.type); + + const to = idsByName.get(connection.node); + + // v1 skips connections whose endpoint no longer exists, so dropping + // them is the faithful translation + if (from === undefined || to === undefined) continue; + + edges.push({ from, to, outputIndex, inputIndex: connection.index }); + } + } + } + + return edges; + } + + private validateSupportedConnectionType(nodeName: string, connectionType: string): void { + if (connectionType !== MAIN_CONNECTION_TYPE) { + throw new UnsupportedConnectionTypeError(nodeName, connectionType); + } + } + + private spliceOutDisabledNodes(edges: GraphEdge[], disabledNodeIds: string[]): GraphEdge[] { + for (const disabledNodeId of disabledNodeIds) { + const incoming = edges.filter( + (edge) => + edge.to === disabledNodeId && edge.from !== disabledNodeId && edge.inputIndex === 0, + ); + const outgoing = edges.filter( + (edge) => edge.from === disabledNodeId && edge.to !== disabledNodeId, + ); + const spliced = incoming.flatMap((into) => + outgoing.map((outOf) => ({ + from: into.from, + to: outOf.to, + outputIndex: into.outputIndex, + inputIndex: outOf.inputIndex, + })), + ); + + edges = this.dedupeEdges( + edges + .filter((edge) => edge.from !== disabledNodeId && edge.to !== disabledNodeId) + .concat(spliced), + ); + } + + return edges; + } + + private dedupeEdges(edges: GraphEdge[]): GraphEdge[] { + const byKey = new Map(); + for (const edge of edges) { + byKey.set(`${edge.from}|${edge.to}|${edge.outputIndex}|${edge.inputIndex}`, edge); + } + return [...byKey.values()]; + } + + /** + * Finds every loop, marks its return edges as `isBackEdge`, and rejects + * every other cycle, peeling loops from the outside in. Each round: group + * the nodes that can reach each other in a circle ({@link computeSccs}), + * require each group to have a single valid entry + * ({@link resolveSingleBatchEntry}), mark the group's edges into that + * entry as returns, and cut them. Cutting breaks the outer circle, so a + * nested loop surfaces as its own group in the next round. When nothing + * circular remains, every loop is marked. + * + * Example with a nested loop: + * + * ┌───────┐ ┌─────┐ ┌─────┐ ┌──────────┐ + * │Trigger├──►│ ├───►│ ├───►│AfterInner│ + * └───────┘ │Outer│ │Inner│ └────┬─────┘ + * │ │ │ │ ┌────┐ │ + * │ │ │ ├►│Body│ │ + * └──▲──┘ └──▲──┘ └──┬─┘ │ + * │ └(back)─┘ │ + * └───────(back)──────────┘ + * + * Round 1 groups {Outer, Inner, Body, AfterInner} with entry Outer, then + * marks and cuts AfterInner -> Outer. Round 2 groups {Inner, Body} with + * entry Inner, then marks and cuts Body -> Inner. Round 3 finds nothing + * circular and stops. + * + * Only set membership decides, never traversal order, so the outcome does + * not depend on node or edge order. + */ + private markBackEdges(nodes: GraphNode[], edges: GraphEdge[]): void { + const batchNodeIds = new Set(nodes.filter((node) => node.type === 'batch').map((n) => n.id)); + const namesById = new Map(nodes.map((node) => [node.id, node.name])); + + let remaining = edges; + while (remaining.length > 0) { + const outgoingByNode = new Map(); + const selfLoopNodeIds = new Set(); + for (const edge of remaining) { + const outgoing = outgoingByNode.get(edge.from); + if (outgoing) outgoing.push(edge); + else outgoingByNode.set(edge.from, [edge]); + if (edge.from === edge.to) selfLoopNodeIds.add(edge.from); + } + + const cyclicSccs = this.computeSccs(nodes, outgoingByNode).filter( + (members) => members.length > 1 || selfLoopNodeIds.has(members[0]), + ); + if (cyclicSccs.length === 0) return; + + for (const members of cyclicSccs) { + const entryId = this.resolveSingleBatchEntry(members, remaining, batchNodeIds, namesById); + const memberSet = new Set(members); + for (const edge of remaining) { + if (edge.to === entryId && memberSet.has(edge.from)) edge.isBackEdge = true; + } + } + + remaining = remaining.filter((edge) => !edge.isBackEdge); + } + } + + /** + * A loop is convertible only when there is exactly one way into it from + * the outside and that way in is the batch node. Otherwise there is no + * single answer to which edge is the return. A loop that nothing points + * into has no outside entries, so its batch node stands in, provided it + * has exactly one. + */ + private resolveSingleBatchEntry( + members: string[], + edges: GraphEdge[], + batchNodeIds: Set, + namesById: Map, + ): string { + const memberSet = new Set(members); + const toNames = (ids: string[]) => ids.map((id) => namesById.get(id) ?? id); + + const batchMembers = members.filter((id) => batchNodeIds.has(id)); + if (batchMembers.length === 0) { + throw new UnsupportedCycleError(toNames(members)); + } + + const externalEntries = new Set(); + for (const edge of edges) { + if (memberSet.has(edge.to) && !memberSet.has(edge.from)) externalEntries.add(edge.to); + } + + const entries = externalEntries.size > 0 ? [...externalEntries] : batchMembers; + if (entries.length !== 1 || !batchNodeIds.has(entries[0])) { + throw new UnsupportedLoopEntryError(toNames(members), toNames(entries)); + } + + return entries[0]; + } + + /** + * Splits the nodes into groups (strongly connected components): two nodes + * share a group when you can walk from one to the other and back along + * edges, through any number of nodes. A -> B -> C -> A puts all three in + * one group. A node on no such round trip is a group of one, so a group + * with more than one member, or with a self-loop, is a cycle. + * Textbook Tarjan's algorithm, best reviewed against a reference: + * https://en.wikipedia.org/wiki/Tarjan%27s_strongly_connected_components_algorithm + */ + private computeSccs(nodes: GraphNode[], outgoingByNode: Map): string[][] { + const indexById = new Map(); + const lowlinkById = new Map(); + const stack: string[] = []; + const onStack = new Set(); + const sccs: string[][] = []; + let nextIndex = 0; + + const connect = (nodeId: string): void => { + indexById.set(nodeId, nextIndex); + lowlinkById.set(nodeId, nextIndex); + nextIndex += 1; + stack.push(nodeId); + onStack.add(nodeId); + + for (const edge of outgoingByNode.get(nodeId) ?? []) { + if (!indexById.has(edge.to)) { + connect(edge.to); + lowlinkById.set(nodeId, Math.min(lowlinkById.get(nodeId)!, lowlinkById.get(edge.to)!)); + } else if (onStack.has(edge.to)) { + lowlinkById.set(nodeId, Math.min(lowlinkById.get(nodeId)!, indexById.get(edge.to)!)); + } + } + + if (lowlinkById.get(nodeId) === indexById.get(nodeId)) { + const members: string[] = []; + let member: string; + do { + member = stack.pop()!; + onStack.delete(member); + members.push(member); + } while (member !== nodeId); + sccs.push(members); + } + }; + + for (const node of nodes) { + if (!indexById.has(node.id)) connect(node.id); + } + + return sccs; + } } diff --git a/packages/@n8n/nodes-langchain/nodes/agents/Agent/agents/ToolsAgent/V2/execute.ts b/packages/@n8n/nodes-langchain/nodes/agents/Agent/agents/ToolsAgent/V2/execute.ts index 63b2155849c..d2f7711565b 100644 --- a/packages/@n8n/nodes-langchain/nodes/agents/Agent/agents/ToolsAgent/V2/execute.ts +++ b/packages/@n8n/nodes-langchain/nodes/agents/Agent/agents/ToolsAgent/V2/execute.ts @@ -14,7 +14,8 @@ import type { StreamEvent } from '@langchain/core/types/stream'; import type { IterableReadableStream } from '@langchain/core/utils/stream'; import { ChatOpenAI } from '@langchain/openai'; import omit from 'lodash/omit'; -import { jsonParse, NodeOperationError, sleep } from 'n8n-workflow'; +import { sleep } from '@n8n/utils/sleep'; +import { jsonParse, NodeOperationError } from 'n8n-workflow'; import type { IExecuteFunctions, INodeExecutionData, ISupplyDataFunctions } from 'n8n-workflow'; import assert from 'node:assert'; diff --git a/packages/@n8n/nodes-langchain/nodes/agents/Agent/agents/ToolsAgent/V3/execute.ts b/packages/@n8n/nodes-langchain/nodes/agents/Agent/agents/ToolsAgent/V3/execute.ts index 928fe07f0de..8e2034d1a4c 100644 --- a/packages/@n8n/nodes-langchain/nodes/agents/Agent/agents/ToolsAgent/V3/execute.ts +++ b/packages/@n8n/nodes-langchain/nodes/agents/Agent/agents/ToolsAgent/V3/execute.ts @@ -6,7 +6,8 @@ import type { INodeExecutionData, ISupplyDataFunctions, } from 'n8n-workflow'; -import { getHighlightedResponseKey, sleep } from 'n8n-workflow'; +import { sleep } from '@n8n/utils/sleep'; +import { getHighlightedResponseKey } from 'n8n-workflow'; import { buildExecutionContext, executeBatch, resolveSubAgentRequest } from './helpers'; import { isExecuteFunctions } from '../../utils'; diff --git a/packages/@n8n/nodes-langchain/nodes/agents/Agent/test/ToolsAgent/ToolsAgentV3.test.ts b/packages/@n8n/nodes-langchain/nodes/agents/Agent/test/ToolsAgent/ToolsAgentV3.test.ts index e787876784b..58c64a7e427 100644 --- a/packages/@n8n/nodes-langchain/nodes/agents/Agent/test/ToolsAgent/ToolsAgentV3.test.ts +++ b/packages/@n8n/nodes-langchain/nodes/agents/Agent/test/ToolsAgent/ToolsAgentV3.test.ts @@ -1,10 +1,5 @@ -import { - sleep, - type IExecuteFunctions, - type INode, - type EngineRequest, - type EngineResponse, -} from 'n8n-workflow'; +import { sleep } from '@n8n/utils/sleep'; +import type { IExecuteFunctions, INode, EngineRequest, EngineResponse } from 'n8n-workflow'; import { mock, type MockProxy } from 'vitest-mock-extended'; import type { RequestResponseMetadata } from '@utils/agent-execution'; @@ -35,6 +30,9 @@ vi.mock('@langchain/core/runnables', () => ({ vi.mock('n8n-workflow', async () => ({ ...(await vi.importActual('n8n-workflow')), +})); + +vi.mock('@n8n/utils/sleep', () => ({ sleep: vi.fn(), })); diff --git a/packages/@n8n/nodes-langchain/nodes/chains/ChainLLM/ChainLlm.node.ts b/packages/@n8n/nodes-langchain/nodes/chains/ChainLLM/ChainLlm.node.ts index a78e0233f02..d2c0f7fefda 100644 --- a/packages/@n8n/nodes-langchain/nodes/chains/ChainLLM/ChainLlm.node.ts +++ b/packages/@n8n/nodes-langchain/nodes/chains/ChainLLM/ChainLlm.node.ts @@ -4,7 +4,8 @@ import type { INodeType, INodeTypeDescription, } from 'n8n-workflow'; -import { NodeApiError, NodeConnectionTypes, NodeOperationError, sleep } from 'n8n-workflow'; +import { sleep } from '@n8n/utils/sleep'; +import { NodeApiError, NodeConnectionTypes, NodeOperationError } from 'n8n-workflow'; import { getOptionalOutputParser } from '@utils/output_parsers/N8nOutputParser'; diff --git a/packages/@n8n/nodes-langchain/nodes/chains/ChainRetrievalQA/ChainRetrievalQa.node.ts b/packages/@n8n/nodes-langchain/nodes/chains/ChainRetrievalQA/ChainRetrievalQa.node.ts index 991e1c8fcbc..7b9bed4b38c 100644 --- a/packages/@n8n/nodes-langchain/nodes/chains/ChainRetrievalQA/ChainRetrievalQa.node.ts +++ b/packages/@n8n/nodes-langchain/nodes/chains/ChainRetrievalQA/ChainRetrievalQa.node.ts @@ -1,4 +1,5 @@ -import { NodeConnectionTypes, parseErrorMetadata, sleep } from 'n8n-workflow'; +import { sleep } from '@n8n/utils/sleep'; +import { NodeConnectionTypes, parseErrorMetadata } from 'n8n-workflow'; import { type IExecuteFunctions, type INodeExecutionData, diff --git a/packages/@n8n/nodes-langchain/nodes/chains/ChainSummarization/V2/ChainSummarizationV2.node.ts b/packages/@n8n/nodes-langchain/nodes/chains/ChainSummarization/V2/ChainSummarizationV2.node.ts index e9d734d26dc..a925497c2cf 100644 --- a/packages/@n8n/nodes-langchain/nodes/chains/ChainSummarization/V2/ChainSummarizationV2.node.ts +++ b/packages/@n8n/nodes-langchain/nodes/chains/ChainSummarization/V2/ChainSummarizationV2.node.ts @@ -7,7 +7,8 @@ import type { IDataObject, INodeInputConfiguration, } from 'n8n-workflow'; -import { NodeConnectionTypes, sleep } from 'n8n-workflow'; +import { sleep } from '@n8n/utils/sleep'; +import { NodeConnectionTypes } from 'n8n-workflow'; import { getBatchingOptionFields, getTemplateNoticeField } from '@n8n/ai-utilities'; diff --git a/packages/@n8n/nodes-langchain/nodes/chains/InformationExtractor/InformationExtractor.node.ts b/packages/@n8n/nodes-langchain/nodes/chains/InformationExtractor/InformationExtractor.node.ts index 9096117eac8..f0aa0a41d14 100644 --- a/packages/@n8n/nodes-langchain/nodes/chains/InformationExtractor/InformationExtractor.node.ts +++ b/packages/@n8n/nodes-langchain/nodes/chains/InformationExtractor/InformationExtractor.node.ts @@ -1,7 +1,8 @@ import type { BaseLanguageModel } from '@langchain/core/language_models/base'; import type { JSONSchema7 } from 'json-schema'; import { OutputFixingParser, StructuredOutputParser } from '@langchain/classic/output_parsers'; -import { jsonParse, NodeConnectionTypes, NodeOperationError, sleep } from 'n8n-workflow'; +import { sleep } from '@n8n/utils/sleep'; +import { jsonParse, NodeConnectionTypes, NodeOperationError } from 'n8n-workflow'; import type { INodeType, INodeTypeDescription, diff --git a/packages/@n8n/nodes-langchain/nodes/chains/SentimentAnalysis/SentimentAnalysis.node.ts b/packages/@n8n/nodes-langchain/nodes/chains/SentimentAnalysis/SentimentAnalysis.node.ts index 6e38d73860f..818ac3bb4bf 100644 --- a/packages/@n8n/nodes-langchain/nodes/chains/SentimentAnalysis/SentimentAnalysis.node.ts +++ b/packages/@n8n/nodes-langchain/nodes/chains/SentimentAnalysis/SentimentAnalysis.node.ts @@ -2,7 +2,8 @@ import type { BaseLanguageModel } from '@langchain/core/language_models/base'; import { HumanMessage } from '@langchain/core/messages'; import { SystemMessagePromptTemplate, ChatPromptTemplate } from '@langchain/core/prompts'; import { OutputFixingParser, StructuredOutputParser } from '@langchain/classic/output_parsers'; -import { NodeConnectionTypes, NodeOperationError, sleep } from 'n8n-workflow'; +import { sleep } from '@n8n/utils/sleep'; +import { NodeConnectionTypes, NodeOperationError } from 'n8n-workflow'; import type { IDataObject, IExecuteFunctions, diff --git a/packages/@n8n/nodes-langchain/nodes/chains/TextClassifier/TextClassifier.node.ts b/packages/@n8n/nodes-langchain/nodes/chains/TextClassifier/TextClassifier.node.ts index 114b8a8776c..30f75569dc3 100644 --- a/packages/@n8n/nodes-langchain/nodes/chains/TextClassifier/TextClassifier.node.ts +++ b/packages/@n8n/nodes-langchain/nodes/chains/TextClassifier/TextClassifier.node.ts @@ -1,6 +1,7 @@ import type { BaseLanguageModel } from '@langchain/core/language_models/base'; import { OutputFixingParser, StructuredOutputParser } from '@langchain/classic/output_parsers'; -import { NodeOperationError, NodeConnectionTypes, sleep } from 'n8n-workflow'; +import { sleep } from '@n8n/utils/sleep'; +import { NodeOperationError, NodeConnectionTypes } from 'n8n-workflow'; import type { IDataObject, IExecuteFunctions, diff --git a/packages/@n8n/nodes-langchain/nodes/chains/TextClassifier/test/TextClassifier.node.test.ts b/packages/@n8n/nodes-langchain/nodes/chains/TextClassifier/test/TextClassifier.node.test.ts index f1f69021353..293695717fc 100644 --- a/packages/@n8n/nodes-langchain/nodes/chains/TextClassifier/test/TextClassifier.node.test.ts +++ b/packages/@n8n/nodes-langchain/nodes/chains/TextClassifier/test/TextClassifier.node.test.ts @@ -1,5 +1,5 @@ import { FakeChatModel } from '@langchain/core/utils/testing'; -import * as n8nWorkflow from 'n8n-workflow'; +import * as n8nUtilsSleep from '@n8n/utils/sleep'; import type { IExecuteFunctions, INode } from 'n8n-workflow'; import type { Mock, Mocked } from 'vitest'; import { mock } from 'vitest-mock-extended'; @@ -11,13 +11,9 @@ vi.mock('../processItem', () => ({ processItem: vi.fn(), })); -vi.mock('n8n-workflow', async (importOriginal) => { - const actual = await importOriginal(); - return { - ...actual, - sleep: vi.fn().mockResolvedValue(undefined), - }; -}); +vi.mock('@n8n/utils/sleep', () => ({ + sleep: vi.fn().mockResolvedValue(undefined), +})); describe('TextClassifier Node', () => { let node: TextClassifier; @@ -158,8 +154,8 @@ describe('TextClassifier Node', () => { await node.execute.call(mockExecuteFunction); // 6 items with batchSize 2 => 3 batches => a delay after every batch but the last - expect(n8nWorkflow.sleep).toHaveBeenCalledTimes(2); - expect(n8nWorkflow.sleep).toHaveBeenCalledWith(100); + expect(n8nUtilsSleep.sleep).toHaveBeenCalledTimes(2); + expect(n8nUtilsSleep.sleep).toHaveBeenCalledWith(100); }); it('should handle errors in batch processing', async () => { diff --git a/packages/@n8n/nodes-langchain/nodes/embeddings/EmbeddingsAwsBedrock/BedrockInvokeModelEmbeddings.ts b/packages/@n8n/nodes-langchain/nodes/embeddings/EmbeddingsAwsBedrock/BedrockInvokeModelEmbeddings.ts new file mode 100644 index 00000000000..03d9cd903d6 --- /dev/null +++ b/packages/@n8n/nodes-langchain/nodes/embeddings/EmbeddingsAwsBedrock/BedrockInvokeModelEmbeddings.ts @@ -0,0 +1,97 @@ +import { InvokeModelCommand, type BedrockRuntimeClient } from '@aws-sdk/client-bedrock-runtime'; +import { Embeddings } from '@langchain/core/embeddings'; +import { jsonParse, OperationalError } from 'n8n-workflow'; + +type CohereInputType = 'search_document' | 'search_query'; + +type EmbeddingResponseBody = { + embedding?: number[]; + embeddingsByType?: Partial>; + embeddings?: number[][] | Partial>; +}; + +// Titan/Cohere return type-keyed embedding maps when the request asks for specific +// types; with several non-float types there's no single vector to return. +function selectEmbeddingType( + byType: Partial> | undefined, +): T | undefined { + if (byType === undefined) { + return undefined; + } + if (Array.isArray(byType.float)) { + return byType.float; + } + const keyed = Object.values(byType).filter((value): value is T => Array.isArray(value)); + return keyed.length === 1 ? keyed[0] : undefined; +} + +export type BedrockInvokeModelEmbeddingsParams = { + client: BedrockRuntimeClient; + model: string; + additionalModelRequestFields?: Record; +}; + +export class BedrockInvokeModelEmbeddings extends Embeddings { + private readonly client: BedrockRuntimeClient; + + private readonly model: string; + + private readonly additionalModelRequestFields: Record; + + constructor({ client, model, additionalModelRequestFields }: BedrockInvokeModelEmbeddingsParams) { + // Retries are handled by the SDK client (maxAttempts); the LangChain + // AsyncCaller must not add a second retry layer on top. + super({ maxRetries: 0 }); + this.client = client; + this.model = model; + this.additionalModelRequestFields = additionalModelRequestFields ?? {}; + } + + private buildRequestBody(text: string, inputType: CohereInputType): Record { + // Strip newlines to keep vectors identical to the previous @langchain/aws behaviour. + const cleanedText = text.replace(/\n/g, ' '); + if (this.model.includes('cohere.embed')) { + return { texts: [cleanedText], input_type: inputType, ...this.additionalModelRequestFields }; + } + return { inputText: cleanedText, ...this.additionalModelRequestFields }; + } + + private async embed(text: string, inputType: CohereInputType): Promise { + return await this.caller.call(async () => { + const response = await this.client.send( + new InvokeModelCommand({ + modelId: this.model, + body: JSON.stringify(this.buildRequestBody(text, inputType)), + contentType: 'application/json', + accept: 'application/json', + }), + ); + const body = jsonParse(new TextDecoder().decode(response.body)); + if (Array.isArray(body.embedding)) { + return body.embedding; + } + const titanTyped = selectEmbeddingType(body.embeddingsByType); + if (titanTyped) { + return titanTyped; + } + const rows = Array.isArray(body.embeddings) + ? body.embeddings + : selectEmbeddingType(body.embeddings); + const first = rows?.[0]; + if (Array.isArray(first)) { + return first; + } + throw new OperationalError('Unexpected embedding response from Bedrock'); + }); + } + + async embedDocuments(documents: string[]): Promise { + return await Promise.all( + documents.map(async (document) => await this.embed(document, 'search_document')), + ); + } + + async embedQuery(text: string): Promise { + return await this.embed(text, 'search_query'); + } +} diff --git a/packages/@n8n/nodes-langchain/nodes/embeddings/EmbeddingsAwsBedrock/EmbeddingsAwsBedrock.node.ts b/packages/@n8n/nodes-langchain/nodes/embeddings/EmbeddingsAwsBedrock/EmbeddingsAwsBedrock.node.ts index 3c78464deb3..c61c8065b56 100644 --- a/packages/@n8n/nodes-langchain/nodes/embeddings/EmbeddingsAwsBedrock/EmbeddingsAwsBedrock.node.ts +++ b/packages/@n8n/nodes-langchain/nodes/embeddings/EmbeddingsAwsBedrock/EmbeddingsAwsBedrock.node.ts @@ -1,16 +1,24 @@ -import { BedrockEmbeddings } from '@langchain/aws'; import { logWrapper, getConnectionHintNoticeField } from '@n8n/ai-utilities'; +import { createBedrockRuntimeClient } from '@utils/aws/createBedrockRuntimeClient'; +import { resolveAwsCredentials } from '@utils/aws/resolveAwsCredentials'; +import { resolveBedrockRegion } from '@utils/aws/resolveBedrockRegion'; import { awsNodeAuthOptions, awsNodeCredentials } from 'n8n-nodes-base/dist/nodes/Aws/utils'; import { + jsonParse, NodeConnectionTypes, + UserError, type INodeType, type INodeTypeDescription, type ISupplyDataFunctions, type SupplyData, } from 'n8n-workflow'; -import { createBedrockRuntimeClient } from '@utils/aws/createBedrockRuntimeClient'; -import { resolveAwsCredentials } from '@utils/aws/resolveAwsCredentials'; +import { BedrockInvokeModelEmbeddings } from './BedrockInvokeModelEmbeddings'; +import { listModels } from './methods/listModels'; + +function isJsonObject(value: unknown): value is Record { + return typeof value === 'object' && value !== null && !Array.isArray(value); +} export class EmbeddingsAwsBedrock implements INodeType { description: INodeTypeDescription = { @@ -51,45 +59,18 @@ export class EmbeddingsAwsBedrock implements INodeType { awsNodeAuthOptions, getConnectionHintNoticeField([NodeConnectionTypes.AiVectorStore]), { + // Keeps the same field naming as other Bedrock model pickers + // eslint-disable-next-line n8n-nodes-base/node-param-display-name-wrong-for-dynamic-options displayName: 'Model', name: 'model', type: 'options', allowArbitraryValues: true, // Hide issues when model name is specified in the expression and does not match any of the options + // eslint-disable-next-line n8n-nodes-base/node-param-description-wrong-for-dynamic-options description: - 'The model which will generate the completion. Learn more.', + 'The model or inference profile which will generate the embeddings. Learn more.', typeOptions: { - loadOptions: { - routing: { - request: { - method: 'GET', - url: '/foundation-models?byInferenceType=ON_DEMAND&byOutputModality=EMBEDDING', - }, - output: { - postReceive: [ - { - type: 'rootProperty', - properties: { - property: 'modelSummaries', - }, - }, - { - type: 'setKeyValue', - properties: { - name: '={{$responseItem.modelName}}', - description: '={{$responseItem.modelArn}}', - value: '={{$responseItem.modelId}}', - }, - }, - { - type: 'sort', - properties: { - key: 'name', - }, - }, - ], - }, - }, - }, + loadOptionsDependsOn: ['authentication'], + loadOptionsMethod: 'listModels', }, routing: { send: { @@ -99,22 +80,95 @@ export class EmbeddingsAwsBedrock implements INodeType { }, default: '', }, + { + displayName: 'Options', + name: 'options', + placeholder: 'Add Option', + description: 'Additional options to add', + type: 'collection', + default: {}, + options: [ + { + displayName: 'Additional Model Request Fields', + name: 'additionalModelRequestFields', + default: '{}', + description: + 'Model-specific request fields passed through as JSON (e.g. Titan dimensions/normalize, Cohere input_type/truncate). See the AWS model parameters docs.', + type: 'json', + typeOptions: { rows: 4 }, + }, + { + displayName: 'Max Retries', + name: 'maxRetries', + default: 2, + description: 'Maximum number of retries to attempt when a request fails', + type: 'number', + }, + { + displayName: 'Timeout', + name: 'timeout', + default: 60000, + description: + 'Maximum amount of time a request is allowed to take in milliseconds. Set to 0 to disable.', + type: 'number', + }, + ], + }, ], }; - async supplyData(this: ISupplyDataFunctions, itemIndex: number): Promise { - const { region, credentials, bedrockRuntimeEndpoint } = await resolveAwsCredentials( - this, - itemIndex, - ); - const modelName = this.getNodeParameter('model', itemIndex) as string; + methods = { + loadOptions: { + listModels, + }, + }; - const client = createBedrockRuntimeClient({ region, credentials, bedrockRuntimeEndpoint }); - const embeddings = new BedrockEmbeddings({ + async supplyData(this: ISupplyDataFunctions, itemIndex: number): Promise { + const { + region: credentialRegion, + credentials, + bedrockRuntimeEndpoint, + } = await resolveAwsCredentials(this, itemIndex); + const modelName = this.getNodeParameter('model', itemIndex) as string; + const options = this.getNodeParameter('options', itemIndex, {}) as { + maxRetries?: number; + timeout?: number; + additionalModelRequestFields?: string; + }; + + const region = resolveBedrockRegion(modelName, credentialRegion); + + const client = createBedrockRuntimeClient({ + region, + credentials, + bedrockRuntimeEndpoint, + maxRetries: options.maxRetries, + timeout: options.timeout, + }); + + let additionalModelRequestFields: Record | undefined; + const additionalFields = options.additionalModelRequestFields?.trim(); + if (additionalFields && additionalFields !== '{}') { + let parsed: unknown; + try { + parsed = jsonParse(additionalFields); + } catch { + throw new UserError('Additional Model Request Fields must be valid JSON', { + level: 'warning', + }); + } + if (!isJsonObject(parsed)) { + throw new UserError('Additional Model Request Fields must be a JSON object', { + level: 'warning', + }); + } + additionalModelRequestFields = parsed; + } + + const embeddings = new BedrockInvokeModelEmbeddings({ client, model: modelName, - maxRetries: 3, - region, + additionalModelRequestFields, }); return { diff --git a/packages/@n8n/nodes-langchain/nodes/embeddings/EmbeddingsAwsBedrock/methods/listModels.ts b/packages/@n8n/nodes-langchain/nodes/embeddings/EmbeddingsAwsBedrock/methods/listModels.ts new file mode 100644 index 00000000000..58ac2c904e5 --- /dev/null +++ b/packages/@n8n/nodes-langchain/nodes/embeddings/EmbeddingsAwsBedrock/methods/listModels.ts @@ -0,0 +1,96 @@ +import { assertSupportedAwsRegion, getAwsDomain } from 'n8n-nodes-base/aws-credentials'; +import type { ILoadOptionsFunctions, INodePropertyOptions } from 'n8n-workflow'; + +type FoundationModelSummary = { + modelId: string; + modelName: string; + modelArn: string; + inferenceTypesSupported?: string[]; +}; + +type InferenceProfileSummary = { + inferenceProfileId: string; + inferenceProfileName: string; + inferenceProfileArn: string; + description?: string; + models?: Array<{ modelArn?: string }>; +}; + +export async function listModels(this: ILoadOptionsFunctions): Promise { + const authentication = this.getNodeParameter('authentication', 'iam') as 'iam' | 'assumeRole'; + const credentialsType = authentication === 'assumeRole' ? 'awsAssumeRole' : 'aws'; + const { region } = await this.getCredentials(credentialsType); + + assertSupportedAwsRegion(region); + // Declares the SigV4 service+region; the credential's authenticate step swaps the + // host for the Bedrock Endpoint override (PrivateLink) when one is configured. + // getAwsDomain keeps China (amazonaws.com.cn) / GovCloud endpoints correct. + const baseURL = `https://bedrock.${region}.${getAwsDomain(region)}`; + + const [foundationModels, inferenceProfiles] = await Promise.allSettled([ + this.helpers.httpRequestWithAuthentication.call(this, credentialsType, { + method: 'GET', + baseURL, + url: '/foundation-models?byOutputModality=EMBEDDING', + json: true, + }) as Promise<{ modelSummaries?: FoundationModelSummary[] }>, + this.helpers.httpRequestWithAuthentication.call(this, credentialsType, { + method: 'GET', + baseURL, + url: '/inference-profiles?maxResults=1000', + json: true, + }) as Promise<{ inferenceProfileSummaries?: InferenceProfileSummary[] }>, + ]); + + // The credential may lack one of the two list permissions + // (bedrock:ListFoundationModels / bedrock:ListInferenceProfiles); + // a single reachable source still renders a usable dropdown. + if (foundationModels.status === 'rejected' && inferenceProfiles.status === 'rejected') { + throw foundationModels.reason; + } + if (foundationModels.status === 'rejected') { + this.logger.warn('Bedrock model listing: foundation-models request failed', { + error: foundationModels.reason, + }); + } + if (inferenceProfiles.status === 'rejected') { + this.logger.warn('Bedrock model listing: inference-profiles request failed', { + error: inferenceProfiles.reason, + }); + } + + const options: INodePropertyOptions[] = []; + const embeddingModelIds = new Set(); + if (foundationModels.status === 'fulfilled') { + for (const model of foundationModels.value.modelSummaries ?? []) { + embeddingModelIds.add(model.modelId); + if (model.inferenceTypesSupported?.includes('ON_DEMAND')) { + options.push({ + name: model.modelName, + value: model.modelId, + description: model.modelArn, + }); + } + } + } + if (inferenceProfiles.status === 'fulfilled') { + // The inference-profiles API cannot filter by modality, so chat profiles are dropped + // by matching each profile's underlying models against the embedding-model list. + // Without that list every profile is kept: a usable, if noisy, dropdown. + const isEmbeddingProfile = (profile: InferenceProfileSummary) => + foundationModels.status === 'rejected' || + (profile.models ?? []).some((model) => + embeddingModelIds.has(model.modelArn?.split('/').pop() ?? ''), + ); + options.push( + ...(inferenceProfiles.value.inferenceProfileSummaries ?? []) + .filter(isEmbeddingProfile) + .map((profile) => ({ + name: profile.inferenceProfileName, + value: profile.inferenceProfileId, + description: profile.description ?? profile.inferenceProfileArn, + })), + ); + } + return options.sort((a, b) => a.name.localeCompare(b.name)); +} diff --git a/packages/@n8n/nodes-langchain/nodes/embeddings/EmbeddingsAwsBedrock/test/BedrockInvokeModelEmbeddings.test.ts b/packages/@n8n/nodes-langchain/nodes/embeddings/EmbeddingsAwsBedrock/test/BedrockInvokeModelEmbeddings.test.ts new file mode 100644 index 00000000000..df053deff90 --- /dev/null +++ b/packages/@n8n/nodes-langchain/nodes/embeddings/EmbeddingsAwsBedrock/test/BedrockInvokeModelEmbeddings.test.ts @@ -0,0 +1,209 @@ +import type { BedrockRuntimeClient, InvokeModelCommand } from '@aws-sdk/client-bedrock-runtime'; +import { OperationalError } from 'n8n-workflow'; + +import { BedrockInvokeModelEmbeddings } from '../BedrockInvokeModelEmbeddings'; + +const encodeResponse = (body: unknown) => ({ + body: new TextEncoder().encode(JSON.stringify(body)), +}); + +describe('BedrockInvokeModelEmbeddings', () => { + const createClient = (response: unknown) => { + const send = vi.fn().mockResolvedValue(encodeResponse(response)); + return { client: { send } as unknown as BedrockRuntimeClient, send }; + }; + + const sentBody = (send: ReturnType, callIndex = 0) => { + const command = send.mock.calls[callIndex][0] as InvokeModelCommand; + return JSON.parse(command.input.body as string) as Record; + }; + + describe('Titan-style models', () => { + it('sends inputText and returns the embedding', async () => { + const { client, send } = createClient({ embedding: [0.1, 0.2] }); + const embeddings = new BedrockInvokeModelEmbeddings({ + client, + model: 'amazon.titan-embed-text-v2:0', + }); + + const result = await embeddings.embedQuery('hello world'); + + expect(result).toEqual([0.1, 0.2]); + expect(sentBody(send)).toEqual({ inputText: 'hello world' }); + const command = send.mock.calls[0][0] as InvokeModelCommand; + expect(command.input.modelId).toBe('amazon.titan-embed-text-v2:0'); + }); + + it('replaces newlines with spaces', async () => { + const { client, send } = createClient({ embedding: [0.1] }); + const embeddings = new BedrockInvokeModelEmbeddings({ + client, + model: 'amazon.titan-embed-text-v1', + }); + + await embeddings.embedQuery('line one\nline two'); + + expect(sentBody(send)).toEqual({ inputText: 'line one line two' }); + }); + + it('parses a Titan binary-only embeddingTypes response (no embedding field)', async () => { + const { client } = createClient({ + inputTextTokenCount: 3, + embeddingsByType: { binary: [1, 0, 1] }, + }); + const embeddings = new BedrockInvokeModelEmbeddings({ + client, + model: 'amazon.titan-embed-text-v2:0', + additionalModelRequestFields: { embeddingTypes: ['binary'] }, + }); + + await expect(embeddings.embedQuery('hello')).resolves.toEqual([1, 0, 1]); + }); + + it('prefers the embedding field over embeddingsByType when both are present', async () => { + const { client } = createClient({ + embedding: [0.1, 0.2], + embeddingsByType: { float: [0.1, 0.2], binary: [1, 0] }, + }); + const embeddings = new BedrockInvokeModelEmbeddings({ + client, + model: 'amazon.titan-embed-text-v2:0', + additionalModelRequestFields: { embeddingTypes: ['float', 'binary'] }, + }); + + await expect(embeddings.embedQuery('hello')).resolves.toEqual([0.1, 0.2]); + }); + + it('merges additional model request fields into the body', async () => { + const { client, send } = createClient({ embedding: [0.1] }); + const embeddings = new BedrockInvokeModelEmbeddings({ + client, + model: 'amazon.titan-embed-text-v2:0', + additionalModelRequestFields: { dimensions: 256, normalize: true }, + }); + + await embeddings.embedQuery('hello'); + + expect(sentBody(send)).toEqual({ inputText: 'hello', dimensions: 256, normalize: true }); + }); + }); + + describe('Cohere embed models', () => { + it('sends texts with input_type search_query for queries', async () => { + const { client, send } = createClient({ embeddings: [[0.3, 0.4]] }); + const embeddings = new BedrockInvokeModelEmbeddings({ + client, + model: 'cohere.embed-multilingual-v3', + }); + + const result = await embeddings.embedQuery('hello'); + + expect(result).toEqual([0.3, 0.4]); + expect(sentBody(send)).toEqual({ texts: ['hello'], input_type: 'search_query' }); + }); + + it('sends input_type search_document for documents', async () => { + const { client, send } = createClient({ embeddings: [[0.3]] }); + const embeddings = new BedrockInvokeModelEmbeddings({ + client, + model: 'cohere.embed-english-v3', + }); + + const result = await embeddings.embedDocuments(['doc one', 'doc two']); + + expect(result).toEqual([[0.3], [0.3]]); + expect(send).toHaveBeenCalledTimes(2); + expect(sentBody(send, 0)).toEqual({ texts: ['doc one'], input_type: 'search_document' }); + expect(sentBody(send, 1)).toEqual({ texts: ['doc two'], input_type: 'search_document' }); + }); + + it('detects Cohere models behind an inference profile ID', async () => { + const { client, send } = createClient({ embeddings: [[0.3]] }); + const embeddings = new BedrockInvokeModelEmbeddings({ + client, + model: 'us.cohere.embed-english-v3:0', + }); + + await embeddings.embedQuery('hello'); + + expect(sentBody(send)).toHaveProperty('texts'); + }); + + it('parses the embeddings.float response shape', async () => { + const { client } = createClient({ embeddings: { float: [[0.5, 0.6]] } }); + const embeddings = new BedrockInvokeModelEmbeddings({ + client, + model: 'cohere.embed-english-v3', + }); + + await expect(embeddings.embedQuery('hello')).resolves.toEqual([0.5, 0.6]); + }); + + it('parses a single non-float embedding_types response (e.g. int8)', async () => { + const { client } = createClient({ embeddings: { int8: [[3, -7]] } }); + const embeddings = new BedrockInvokeModelEmbeddings({ + client, + model: 'cohere.embed-english-v3', + additionalModelRequestFields: { embedding_types: ['int8'] }, + }); + + await expect(embeddings.embedQuery('hello')).resolves.toEqual([3, -7]); + }); + + it('prefers float when multiple embedding types are returned', async () => { + const { client } = createClient({ embeddings: { int8: [[3]], float: [[0.5]] } }); + const embeddings = new BedrockInvokeModelEmbeddings({ + client, + model: 'cohere.embed-english-v3', + additionalModelRequestFields: { embedding_types: ['float', 'int8'] }, + }); + + await expect(embeddings.embedQuery('hello')).resolves.toEqual([0.5]); + }); + + it('rejects an ambiguous multi-type response without float', async () => { + const { client } = createClient({ embeddings: { int8: [[3]], binary: [[1]] } }); + const embeddings = new BedrockInvokeModelEmbeddings({ + client, + model: 'cohere.embed-english-v3', + additionalModelRequestFields: { embedding_types: ['int8', 'binary'] }, + }); + + await expect(embeddings.embedQuery('hello')).rejects.toThrow(OperationalError); + }); + + it('lets additional fields override the default input_type', async () => { + const { client, send } = createClient({ embeddings: [[0.3]] }); + const embeddings = new BedrockInvokeModelEmbeddings({ + client, + model: 'cohere.embed-english-v3', + additionalModelRequestFields: { input_type: 'classification' }, + }); + + await embeddings.embedQuery('hello'); + + expect(sentBody(send)).toEqual({ texts: ['hello'], input_type: 'classification' }); + }); + }); + + it('throws an OperationalError on an unexpected response shape', async () => { + const { client } = createClient({ unexpected: true }); + const embeddings = new BedrockInvokeModelEmbeddings({ + client, + model: 'amazon.titan-embed-text-v2:0', + }); + + await expect(embeddings.embedQuery('hello')).rejects.toThrow(OperationalError); + }); + + it('propagates client errors without retrying in the LangChain caller', async () => { + const send = vi.fn().mockRejectedValue(new Error('AccessDenied')); + const embeddings = new BedrockInvokeModelEmbeddings({ + client: { send } as unknown as BedrockRuntimeClient, + model: 'amazon.titan-embed-text-v2:0', + }); + + await expect(embeddings.embedQuery('hello')).rejects.toThrow('AccessDenied'); + expect(send).toHaveBeenCalledTimes(1); + }); +}); diff --git a/packages/@n8n/nodes-langchain/nodes/embeddings/EmbeddingsAwsBedrock/test/EmbeddingsAwsBedrock.test.ts b/packages/@n8n/nodes-langchain/nodes/embeddings/EmbeddingsAwsBedrock/test/EmbeddingsAwsBedrock.test.ts index 2072763a6c4..cc6ba768a22 100644 --- a/packages/@n8n/nodes-langchain/nodes/embeddings/EmbeddingsAwsBedrock/test/EmbeddingsAwsBedrock.test.ts +++ b/packages/@n8n/nodes-langchain/nodes/embeddings/EmbeddingsAwsBedrock/test/EmbeddingsAwsBedrock.test.ts @@ -1,18 +1,25 @@ import { BedrockRuntimeClient } from '@aws-sdk/client-bedrock-runtime'; -import { BedrockEmbeddings } from '@langchain/aws'; import { getNodeProxyAgent } from '@n8n/ai-utilities'; -import { resolveAwsCredentials } from '@utils/aws/resolveAwsCredentials'; +import { NodeHttpHandler } from '@smithy/node-http-handler'; import { createMockExecuteFunction } from 'n8n-nodes-base/test/nodes/Helpers'; -import { UserError, type INode, type ISupplyDataFunctions } from 'n8n-workflow'; +import { + UserError, + type ILoadOptionsFunctions, + type INode, + type ISupplyDataFunctions, +} from 'n8n-workflow'; import type { Mocked } from 'vitest'; +import { resolveAwsCredentials } from '@utils/aws/resolveAwsCredentials'; + +import { BedrockInvokeModelEmbeddings } from '../BedrockInvokeModelEmbeddings'; import { EmbeddingsAwsBedrock } from '../EmbeddingsAwsBedrock.node'; vi.mock('@aws-sdk/client-bedrock-runtime', () => ({ BedrockRuntimeClient: vi.fn(), })); -vi.mock('@langchain/aws', () => ({ - BedrockEmbeddings: vi.fn().mockImplementation(function () { +vi.mock('../BedrockInvokeModelEmbeddings', () => ({ + BedrockInvokeModelEmbeddings: vi.fn().mockImplementation(function () { return {}; }), })); @@ -26,9 +33,13 @@ vi.mock('@n8n/ai-utilities', () => ({ vi.mock('@utils/aws/resolveAwsCredentials', () => ({ resolveAwsCredentials: vi.fn(), })); +vi.mock('@smithy/node-http-handler', () => ({ + NodeHttpHandler: vi.fn(), +})); const MockedBedrockRuntimeClient = vi.mocked(BedrockRuntimeClient); -const MockedBedrockEmbeddings = vi.mocked(BedrockEmbeddings); +const MockedBedrockInvokeModelEmbeddings = vi.mocked(BedrockInvokeModelEmbeddings); +const MockedNodeHttpHandler = vi.mocked(NodeHttpHandler); const mockedGetNodeProxyAgent = vi.mocked(getNodeProxyAgent); const mockedResolveAwsCredentials = vi.mocked(resolveAwsCredentials); @@ -42,14 +53,16 @@ describe('EmbeddingsAwsBedrock', () => { parameters: {}, }; - function mockContext(model: string) { + function mockContext(model: string, options: Record = {}) { const ctx = createMockExecuteFunction( {}, mockNode, ) as Mocked; - ctx.getNodeParameter = vi - .fn() - .mockImplementation((name: string) => (name === 'model' ? model : undefined)); + ctx.getNodeParameter = vi.fn().mockImplementation((name: string) => { + if (name === 'model') return model; + if (name === 'options') return options; + return undefined; + }); ctx.getCredentials = vi.fn().mockResolvedValue({}); ctx.getNode = vi.fn().mockReturnValue(mockNode); return ctx; @@ -124,11 +137,140 @@ describe('EmbeddingsAwsBedrock', () => { }); const node = new EmbeddingsAwsBedrock(); await node.supplyData.call(mockContext('custom.model.not-in-list-v1'), 0); - expect(MockedBedrockEmbeddings).toHaveBeenCalledWith( + expect(MockedBedrockInvokeModelEmbeddings).toHaveBeenCalledWith( expect.objectContaining({ model: 'custom.model.not-in-list-v1' }), ); }); + it('keeps the credential region for a non-ARN model (including region-prefixed profile IDs)', async () => { + mockedResolveAwsCredentials.mockResolvedValue({ + region: 'us-east-1', + credentials: { accessKeyId: 'a', secretAccessKey: 'b' }, + }); + const node = new EmbeddingsAwsBedrock(); + await node.supplyData.call(mockContext('eu.cohere.embed-v4:0'), 0); + expect(MockedBedrockRuntimeClient).toHaveBeenCalledWith( + expect.objectContaining({ region: 'us-east-1' }), + ); + }); + + it('overrides the credential region with the region from a model ARN', async () => { + mockedResolveAwsCredentials.mockResolvedValue({ + region: 'us-east-1', + credentials: { accessKeyId: 'a', secretAccessKey: 'b' }, + }); + const node = new EmbeddingsAwsBedrock(); + await node.supplyData.call( + mockContext('arn:aws:bedrock:eu-west-3:123456789012:inference-profile/eu.cohere.embed-v4:0'), + 0, + ); + expect(MockedBedrockRuntimeClient).toHaveBeenCalledWith( + expect.objectContaining({ region: 'eu-west-3' }), + ); + }); + + it('throws when the ARN region is not supported', async () => { + mockedResolveAwsCredentials.mockResolvedValue({ + region: 'us-east-1', + credentials: { accessKeyId: 'a', secretAccessKey: 'b' }, + }); + const node = new EmbeddingsAwsBedrock(); + await expect( + node.supplyData.call( + mockContext('arn:aws:bedrock:not-a-region:123456789012:inference-profile/x'), + 0, + ), + ).rejects.toThrow(); + expect(MockedBedrockRuntimeClient).not.toHaveBeenCalled(); + }); + + describe('options', () => { + beforeEach(() => { + mockedResolveAwsCredentials.mockResolvedValue({ + region: 'us-east-1', + credentials: { accessKeyId: 'a', secretAccessKey: 'b' }, + }); + }); + + it('applies max retries as SDK maxAttempts', async () => { + const node = new EmbeddingsAwsBedrock(); + await node.supplyData.call(mockContext('amazon.titan-embed-text-v1', { maxRetries: 5 }), 0); + + expect(MockedBedrockRuntimeClient.mock.calls.at(-1)?.[0]?.maxAttempts).toBe(6); + }); + + it('applies the timeout to the request handler', async () => { + const node = new EmbeddingsAwsBedrock(); + await node.supplyData.call(mockContext('amazon.titan-embed-text-v1', { timeout: 30000 }), 0); + + expect(MockedNodeHttpHandler).toHaveBeenCalledWith( + expect.objectContaining({ requestTimeout: 30000, throwOnRequestTimeout: true }), + ); + }); + + it('leaves maxAttempts and request handler unset without options', async () => { + const node = new EmbeddingsAwsBedrock(); + await node.supplyData.call(mockContext('amazon.titan-embed-text-v1'), 0); + + const lastConfig = MockedBedrockRuntimeClient.mock.calls.at(-1)?.[0]; + expect(lastConfig?.maxAttempts).toBeUndefined(); + expect(lastConfig?.requestHandler).toBeUndefined(); + }); + + it('passes parsed additional model request fields to the embeddings', async () => { + const node = new EmbeddingsAwsBedrock(); + await node.supplyData.call( + mockContext('amazon.titan-embed-text-v2:0', { + additionalModelRequestFields: '{"dimensions": 512, "normalize": false}', + }), + 0, + ); + + expect(MockedBedrockInvokeModelEmbeddings).toHaveBeenCalledWith( + expect.objectContaining({ + additionalModelRequestFields: { dimensions: 512, normalize: false }, + }), + ); + }); + + it('ignores an empty additional fields object', async () => { + const node = new EmbeddingsAwsBedrock(); + await node.supplyData.call( + mockContext('amazon.titan-embed-text-v2:0', { additionalModelRequestFields: ' {} ' }), + 0, + ); + + expect(MockedBedrockInvokeModelEmbeddings).toHaveBeenCalledWith( + expect.objectContaining({ additionalModelRequestFields: undefined }), + ); + }); + + it('throws a UserError for invalid additional fields JSON', async () => { + const node = new EmbeddingsAwsBedrock(); + + await expect( + node.supplyData.call( + mockContext('amazon.titan-embed-text-v2:0', { additionalModelRequestFields: 'not-json' }), + 0, + ), + ).rejects.toThrow(UserError); + }); + + it.each(['[1, 2]', '"text"', '42', 'true', 'null'])( + 'throws a UserError for non-object additional fields JSON: %s', + async (additionalModelRequestFields) => { + const node = new EmbeddingsAwsBedrock(); + + await expect( + node.supplyData.call( + mockContext('amazon.titan-embed-text-v2:0', { additionalModelRequestFields }), + 0, + ), + ).rejects.toThrow('Additional Model Request Fields must be a JSON object'); + }, + ); + }); + describe('runtime endpoint override', () => { it('routes inference and the proxy agent to the override endpoint when set', async () => { mockedResolveAwsCredentials.mockResolvedValue({ @@ -168,4 +310,168 @@ describe('EmbeddingsAwsBedrock', () => { ).rejects.toThrow(UserError); }); }); + + describe('listModels', () => { + const node = new EmbeddingsAwsBedrock(); + + const foundationResponse = { + modelSummaries: [ + { + modelId: 'amazon.titan-embed-text-v2:0', + modelName: 'Titan Text Embeddings V2', + modelArn: 'arn:aws:bedrock:eu-central-1::foundation-model/amazon.titan-embed-text-v2:0', + inferenceTypesSupported: ['ON_DEMAND'], + }, + { + modelId: 'cohere.embed-v4:0', + modelName: 'Cohere Embed 4', + modelArn: 'arn:aws:bedrock:eu-central-1::foundation-model/cohere.embed-v4:0', + inferenceTypesSupported: ['INFERENCE_PROFILE'], + }, + ], + }; + const profilesResponse = { + inferenceProfileSummaries: [ + { + inferenceProfileId: 'eu.cohere.embed-v4:0', + inferenceProfileName: 'EU Cohere Embed 4', + inferenceProfileArn: + 'arn:aws:bedrock:eu-central-1:123456789012:inference-profile/eu.cohere.embed-v4:0', + description: 'Routes requests across EU regions', + models: [ + { modelArn: 'arn:aws:bedrock:eu-central-1::foundation-model/cohere.embed-v4:0' }, + ], + }, + { + inferenceProfileId: 'eu.anthropic.claude-sonnet-4-6-v1:0', + inferenceProfileName: 'EU Anthropic Claude Sonnet 4.6', + inferenceProfileArn: + 'arn:aws:bedrock:eu-central-1:123456789012:inference-profile/eu.anthropic.claude-sonnet-4-6-v1:0', + models: [ + { + modelArn: + 'arn:aws:bedrock:eu-central-1::foundation-model/anthropic.claude-sonnet-4-6-v1:0', + }, + ], + }, + ], + }; + + const createLoadOptionsContext = ( + authentication: 'iam' | 'assumeRole', + httpMock: ReturnType, + ) => + ({ + getNodeParameter: vi.fn().mockReturnValue(authentication), + getCredentials: vi.fn().mockResolvedValue({ region: 'eu-central-1' }), + helpers: { httpRequestWithAuthentication: httpMock }, + logger: { warn: vi.fn() }, + }) as unknown as ILoadOptionsFunctions; + + const httpMockFor = ( + foundation: Promise | unknown, + profiles: Promise | unknown, + ) => + vi.fn().mockImplementation(async (_credentialsType: string, options: { url: string }) => { + return options.url.startsWith('/foundation-models') ? await foundation : await profiles; + }); + + it('lists on-demand embedding models and embedding inference profiles, sorted by name', async () => { + const httpMock = httpMockFor(foundationResponse, profilesResponse); + const ctx = createLoadOptionsContext('iam', httpMock); + + const options = await node.methods.loadOptions.listModels.call(ctx); + + expect(options).toEqual([ + { + name: 'EU Cohere Embed 4', + value: 'eu.cohere.embed-v4:0', + description: 'Routes requests across EU regions', + }, + { + name: 'Titan Text Embeddings V2', + value: 'amazon.titan-embed-text-v2:0', + // eslint-disable-next-line n8n-nodes-base/node-param-description-lowercase-first-char + description: + 'arn:aws:bedrock:eu-central-1::foundation-model/amazon.titan-embed-text-v2:0', + }, + ]); + }); + + it('excludes profile-only foundation models from the standalone options', async () => { + const httpMock = httpMockFor(foundationResponse, profilesResponse); + const ctx = createLoadOptionsContext('iam', httpMock); + + const options = await node.methods.loadOptions.listModels.call(ctx); + + expect(options.map((o) => o.value)).not.toContain('cohere.embed-v4:0'); + }); + + it('drops inference profiles whose models are not embedding models', async () => { + const httpMock = httpMockFor(foundationResponse, profilesResponse); + const ctx = createLoadOptionsContext('iam', httpMock); + + const options = await node.methods.loadOptions.listModels.call(ctx); + + expect(options.map((o) => o.value)).not.toContain('eu.anthropic.claude-sonnet-4-6-v1:0'); + }); + + it('uses the assume-role credential when authentication is assumeRole', async () => { + const httpMock = httpMockFor(foundationResponse, profilesResponse); + const ctx = createLoadOptionsContext('assumeRole', httpMock); + + await node.methods.loadOptions.listModels.call(ctx); + + expect(ctx.getCredentials).toHaveBeenCalledWith('awsAssumeRole'); + expect(httpMock).toHaveBeenCalledWith('awsAssumeRole', expect.any(Object)); + }); + + it('returns only foundation models when the profiles request fails', async () => { + const httpMock = httpMockFor( + foundationResponse, + Promise.reject(new Error('AccessDenied: ListInferenceProfiles')), + ); + const ctx = createLoadOptionsContext('iam', httpMock); + + const options = await node.methods.loadOptions.listModels.call(ctx); + + expect(options.map((o) => o.value)).toEqual(['amazon.titan-embed-text-v2:0']); + }); + + it('returns all profiles unfiltered when the foundation-models request fails', async () => { + const httpMock = httpMockFor( + Promise.reject(new Error('AccessDenied: ListFoundationModels')), + profilesResponse, + ); + const ctx = createLoadOptionsContext('iam', httpMock); + + const options = await node.methods.loadOptions.listModels.call(ctx); + + expect(options.map((o) => o.value)).toEqual([ + 'eu.anthropic.claude-sonnet-4-6-v1:0', + 'eu.cohere.embed-v4:0', + ]); + }); + + it('throws when both requests fail', async () => { + const httpMock = httpMockFor( + Promise.reject(new Error('AccessDenied')), + Promise.reject(new Error('AccessDenied')), + ); + const ctx = createLoadOptionsContext('iam', httpMock); + + await expect(node.methods.loadOptions.listModels.call(ctx)).rejects.toThrow('AccessDenied'); + }); + + it('rejects an unsupported region before making any request', async () => { + const httpMock = httpMockFor(foundationResponse, profilesResponse); + const ctx = createLoadOptionsContext('iam', httpMock); + (ctx.getCredentials as ReturnType).mockResolvedValue({ + region: 'not-a-region', + }); + + await expect(node.methods.loadOptions.listModels.call(ctx)).rejects.toThrow(); + expect(httpMock).not.toHaveBeenCalled(); + }); + }); }); diff --git a/packages/@n8n/nodes-langchain/nodes/llms/LMChatOpenAi/LmChatOpenAi.node.ts b/packages/@n8n/nodes-langchain/nodes/llms/LMChatOpenAi/LmChatOpenAi.node.ts index 3fc5514d742..66c3e5f5baa 100644 --- a/packages/@n8n/nodes-langchain/nodes/llms/LMChatOpenAi/LmChatOpenAi.node.ts +++ b/packages/@n8n/nodes-langchain/nodes/llms/LMChatOpenAi/LmChatOpenAi.node.ts @@ -1,8 +1,11 @@ import { ChatOpenAI, type ChatOpenAIFields, type ClientOptions } from '@langchain/openai'; +import isPlainObject from 'lodash/isPlainObject'; import pick from 'lodash/pick'; import { assertCredentialAllowsUrl, + jsonParse, NodeConnectionTypes, + NodeOperationError, type INodeProperties, type IDataObject, type INodeType, @@ -598,6 +601,14 @@ export class LmChatOpenAi implements INodeType { 'Controls diversity via nucleus sampling: 0.5 means half of all likelihood-weighted options are considered. We generally recommend altering this or temperature but not both.', type: 'number', }, + { + displayName: 'Extra Body', + name: 'extraBody', + type: 'json', + default: '{}', + description: + 'Optional additional JSON properties to include in the request body when making requests to OpenAI-compatible APIs', + }, { displayName: 'Conversation ID', name: 'conversationId', @@ -796,6 +807,27 @@ export class LmChatOpenAi implements INodeType { } } + if (options.extraBody) { + let extraBody: Record; + try { + extraBody = jsonParse>(options.extraBody); + } catch (error) { + throw new NodeOperationError( + this.getNode(), + 'The value in the "Extra Body" field is not valid JSON', + { itemIndex, description: error instanceof Error ? error.message : String(error) }, + ); + } + if (!isPlainObject(extraBody)) { + throw new NodeOperationError( + this.getNode(), + 'The value in the "Extra Body" field must be a JSON object', + { itemIndex }, + ); + } + Object.assign(modelKwargs, extraBody); + } + const includedOptions = pick(options, [ 'frequencyPenalty', 'maxTokens', diff --git a/packages/@n8n/nodes-langchain/nodes/llms/LMChatOpenAi/types.ts b/packages/@n8n/nodes-langchain/nodes/llms/LMChatOpenAi/types.ts index 8f0449eaa3e..e5eae48b49f 100644 --- a/packages/@n8n/nodes-langchain/nodes/llms/LMChatOpenAi/types.ts +++ b/packages/@n8n/nodes-langchain/nodes/llms/LMChatOpenAi/types.ts @@ -27,6 +27,7 @@ export type ModelOptions = { timeout?: number; maxRetries?: number; topP?: number; + extraBody?: string; conversationId?: string; metadata?: string; promptCacheKey?: string; diff --git a/packages/@n8n/nodes-langchain/nodes/llms/LmChatAwsBedrock/LmChatAwsBedrock.node.ts b/packages/@n8n/nodes-langchain/nodes/llms/LmChatAwsBedrock/LmChatAwsBedrock.node.ts index 3e310110e61..8266304b0a2 100644 --- a/packages/@n8n/nodes-langchain/nodes/llms/LmChatAwsBedrock/LmChatAwsBedrock.node.ts +++ b/packages/@n8n/nodes-langchain/nodes/llms/LmChatAwsBedrock/LmChatAwsBedrock.node.ts @@ -6,7 +6,9 @@ import { getConnectionHintNoticeField, } from '@n8n/ai-utilities'; import type { DocumentType } from '@smithy/types'; -import { assertSupportedAwsRegion } from 'n8n-nodes-base/aws-credentials'; +import { createBedrockRuntimeClient } from '@utils/aws/createBedrockRuntimeClient'; +import { resolveAwsCredentials } from '@utils/aws/resolveAwsCredentials'; +import { resolveBedrockRegion } from '@utils/aws/resolveBedrockRegion'; import { awsNodeAuthOptions, awsNodeCredentials } from 'n8n-nodes-base/dist/nodes/Aws/utils'; import { jsonParse, @@ -18,9 +20,6 @@ import { type SupplyData, } from 'n8n-workflow'; -import { createBedrockRuntimeClient } from '@utils/aws/createBedrockRuntimeClient'; -import { resolveAwsCredentials } from '@utils/aws/resolveAwsCredentials'; - import { listModels } from './methods/listModels'; export class LmChatAwsBedrock implements INodeType { @@ -388,17 +387,7 @@ export class LmChatAwsBedrock implements INodeType { }; }; - // If the model is specified as a full ARN, extract the region from it - // ARN format: arn::bedrock:::inference-profile/ - // Partition covers commercial (aws), China (aws-cn) and GovCloud (aws-us-gov). - let region = credentialRegion; - const arnMatch = modelName.match(/^arn:(?:aws|aws-cn|aws-us-gov):bedrock:([a-z0-9-]+):/); - if (arnMatch) { - const arnRegion = arnMatch[1]; - // Validate before the region is interpolated into the bedrock-runtime endpoint URL below. - assertSupportedAwsRegion(arnRegion); - region = arnRegion; - } + const region = resolveBedrockRegion(modelName, credentialRegion); const client = createBedrockRuntimeClient({ region, diff --git a/packages/@n8n/nodes-langchain/nodes/llms/test/LmChatOpenAi.test.ts b/packages/@n8n/nodes-langchain/nodes/llms/test/LmChatOpenAi.test.ts index 854c68cfc72..94fb79ec0b6 100644 --- a/packages/@n8n/nodes-langchain/nodes/llms/test/LmChatOpenAi.test.ts +++ b/packages/@n8n/nodes-langchain/nodes/llms/test/LmChatOpenAi.test.ts @@ -5,7 +5,12 @@ import { makeN8nLlmFailedAttemptHandler, N8nLlmTracing, getProxyAgent } from '@n import { AiConfig } from '@n8n/config'; import { Container } from '@n8n/di'; import { createMockExecuteFunction } from 'n8n-nodes-base/test/nodes/Helpers'; -import type { IDataObject, INode, ISupplyDataFunctions } from 'n8n-workflow'; +import { + NodeOperationError, + type IDataObject, + type INode, + type ISupplyDataFunctions, +} from 'n8n-workflow'; import type { Mocked } from 'vitest'; import * as common from '../LMChatOpenAi/common'; @@ -89,6 +94,24 @@ describe('LmChatOpenAi', () => { expect(lmChatOpenAi.description.outputs).toEqual(['ai_languageModel']); expect(lmChatOpenAi.description.outputNames).toEqual(['Model']); }); + + it('should expose Extra Body as an advanced JSON option', () => { + const options = lmChatOpenAi.description.properties.find( + (property) => property?.name === 'options', + ); + + expect(options).toBeDefined(); + expect(options).toMatchObject({ + options: expect.arrayContaining([ + expect.objectContaining({ + displayName: 'Extra Body', + name: 'extraBody', + type: 'json', + default: '{}', + }), + ]), + }); + }); }); describe('supplyData', () => { @@ -520,6 +543,103 @@ describe('LmChatOpenAi', () => { vi.clearAllMocks(); } }); + + it('should merge extraBody into modelKwargs for Chat Completions', async () => { + const mockContext = setupMockContext({ typeVersion: 1.2 }); + const options = { + extraBody: '{"enable_search":true,"custom_temperature":0.25}', + responseFormat: 'json_object' as const, + reasoningEffort: 'high' as const, + }; + + mockContext.getNodeParameter = vi.fn().mockImplementation((paramName: string) => { + if (paramName === 'model.value') return 'gpt-4o-mini'; + if (paramName === 'options') return options; + return undefined; + }); + + await lmChatOpenAi.supplyData.call(mockContext, 0); + + expect(MockedChatOpenAI).toHaveBeenCalledWith( + expect.objectContaining({ + modelKwargs: { + response_format: { type: 'json_object' }, + reasoning_effort: 'high', + enable_search: true, + custom_temperature: 0.25, + }, + }), + ); + }); + + it('should merge extraBody into modelKwargs for Responses API', async () => { + const mockContext = setupMockContext({ typeVersion: 1.3 }); + const options = { + extraBody: '{"enable_search":true}', + promptCacheKey: 'cache_key_1', + }; + const mockResponsesParams = { + prompt_cache_key: 'cache_key_1', + }; + + mockContext.getNodeParameter = vi.fn().mockImplementation((paramName: string) => { + if (paramName === 'responsesApiEnabled') return true; + if (paramName === 'model.value') return 'gpt-4o-mini'; + if (paramName === 'options') return options; + if (paramName === 'builtInTools') return {}; + return undefined; + }); + + //@ts-expect-error - Mocking + mockedCommon.prepareAdditionalResponsesParams = vi.fn().mockReturnValue(mockResponsesParams); + //@ts-expect-error - Mocking + mockedCommon.formatBuiltInTools = vi.fn().mockReturnValue([]); + + await lmChatOpenAi.supplyData.call(mockContext, 0); + + expect(MockedChatOpenAI).toHaveBeenCalledWith( + expect.objectContaining({ + useResponsesApi: true, + modelKwargs: { + prompt_cache_key: 'cache_key_1', + enable_search: true, + }, + }), + ); + }); + + it('should reject invalid extraBody JSON values', async () => { + const mockContext = setupMockContext(); + + mockContext.getNodeParameter = vi.fn().mockImplementation((paramName: string) => { + if (paramName === 'model.value') return 'gpt-4o-mini'; + if (paramName === 'options') return { extraBody: 'not json' }; + return undefined; + }); + + const result = lmChatOpenAi.supplyData.call(mockContext, 0); + await expect(result).rejects.toThrow('The value in the "Extra Body" field is not valid JSON'); + await expect(result).rejects.toThrow(NodeOperationError); + }); + + it.each(['[1,2]', '"invalid"', 'true', 'null'])( + 'should reject extraBody JSON values that are not objects: %s', + async (extraBody) => { + const mockContext = setupMockContext(); + + mockContext.getNodeParameter = vi.fn().mockImplementation((paramName: string) => { + if (paramName === 'model.value') return 'gpt-4o-mini'; + if (paramName === 'options') return { extraBody }; + return undefined; + }); + + const result = lmChatOpenAi.supplyData.call(mockContext, 0); + await expect(result).rejects.toThrow( + 'The value in the "Extra Body" field must be a JSON object', + ); + await expect(result).rejects.toThrow(NodeOperationError); + }, + ); }); describe('methods', () => { diff --git a/packages/@n8n/nodes-langchain/nodes/mcp/McpTrigger/McpServer.ts b/packages/@n8n/nodes-langchain/nodes/mcp/McpTrigger/McpServer.ts index 5880a5931fd..b8e0385731d 100644 --- a/packages/@n8n/nodes-langchain/nodes/mcp/McpTrigger/McpServer.ts +++ b/packages/@n8n/nodes-langchain/nodes/mcp/McpTrigger/McpServer.ts @@ -220,7 +220,15 @@ export class McpServer { try { await new Promise((resolve) => { this.resolveFunctions[callId] = resolve; - void transport.handleRequest(req, resp, message as IncomingMessage).finally(resolve); + const requestHandled = transport.handleRequest(req, resp, message as IncomingMessage); + if (isToolCall && transport.transportType === 'sse') { + // SSE answers the POST with 202 before the tool has run, so completing the + // request must not resolve here — the CallTool handler does that once the + // tool finished. A transport failure still resolves so we never hang. + void requestHandled.catch(() => resolve()); + } else { + void requestHandled.finally(resolve); + } }); } finally { delete this.resolveFunctions[callId]; @@ -603,73 +611,77 @@ export class McpServer { ); server.setRequestHandler(CallToolRequestSchema, async (request, extra) => { - if (!request.params?.name || !request.params?.arguments) { - throw new OperationalError('Require a name and arguments for the tool call'); - } if (!extra.sessionId) { throw new OperationalError('Require a sessionId for the tool call'); } const callId = extra.requestId ? `${extra.sessionId}_${extra.requestId}` : extra.sessionId; - const toolName = request.params.name; - const toolArguments = - typeof request.params.arguments === 'object' && request.params.arguments !== null - ? request.params.arguments - : {}; - - const tools = this.sessionManager.getTools(extra.sessionId) ?? []; - const requestedTool = tools.find((tool) => tool.name === toolName); - if (!requestedTool) { - throw new OperationalError('Tool not found'); - } - - // Eager pre-execution credential gate: if the caller has not connected a - // required private credential, surface the actionable connection URLs - // (via elicitation when supported, otherwise as text) instead of - // executing (or enqueuing) the workflow. - const gateResult = this.pendingGateResults[callId]; - if (gateResult && !gateResult.readyToExecute) { - return await this.handleCredentialGate(server, gateResult, callId); - } try { - if (this.executionCoordinator.isQueueMode()) { - const requestId = extra.requestId?.toString() ?? ''; - this.storePendingResponse(extra.sessionId, requestId); + if (!request.params?.name || !request.params?.arguments) { + throw new OperationalError('Require a name and arguments for the tool call'); + } - // Resolve handlePostMessage so webhook can return and enqueue execution. - // The handler continues running asynchronously, waiting for worker response. - if (this.resolveFunctions[callId]) { - this.resolveFunctions[callId](); + const toolName = request.params.name; + const toolArguments = + typeof request.params.arguments === 'object' && request.params.arguments !== null + ? request.params.arguments + : {}; + + const tools = this.sessionManager.getTools(extra.sessionId) ?? []; + const requestedTool = tools.find((tool) => tool.name === toolName); + if (!requestedTool) { + throw new OperationalError('Tool not found'); + } + + // Eager pre-execution credential gate: if the caller has not connected a + // required private credential, surface the actionable connection URLs + // (via elicitation when supported, otherwise as text) instead of + // executing (or enqueuing) the workflow. + const gateResult = this.pendingGateResults[callId]; + if (gateResult && !gateResult.readyToExecute) { + return await this.handleCredentialGate(server, gateResult, callId); + } + + try { + if (this.executionCoordinator.isQueueMode()) { + const requestId = extra.requestId?.toString() ?? ''; + this.storePendingResponse(extra.sessionId, requestId); + + // Resolve handlePostMessage so webhook can return and enqueue execution. + // The handler continues running asynchronously, waiting for worker response. + if (this.resolveFunctions[callId]) { + this.resolveFunctions[callId](); + } + + const strategy = this.executionCoordinator.getStrategy() as QueuedExecutionStrategy; + const result = await strategy.executeTool(requestedTool, toolArguments, { + sessionId: extra.sessionId, + messageId: requestId, + }); + + return MessageFormatter.formatToolResult( + result, + MessageFormatter.isErrorResult(result), + ); } - const strategy = this.executionCoordinator.getStrategy() as QueuedExecutionStrategy; - const result = await strategy.executeTool(requestedTool, toolArguments, { + const result = await this.executionCoordinator.executeTool(requestedTool, toolArguments, { sessionId: extra.sessionId, - messageId: requestId, + messageId: extra.requestId?.toString(), }); return MessageFormatter.formatToolResult(result, MessageFormatter.isErrorResult(result)); + } catch (error) { + const errorObject = error instanceof Error ? error : new Error(String(error)); + this.logger.error(`Error while executing Tool ${toolName}: ${errorObject.message}`, { + error: errorObject, + }); + return MessageFormatter.formatError(errorObject); } - - const result = await this.executionCoordinator.executeTool(requestedTool, toolArguments, { - sessionId: extra.sessionId, - messageId: extra.requestId?.toString(), - }); - - if (this.resolveFunctions[callId]) { - this.resolveFunctions[callId](); - } else { - this.logger.warn(`No resolve function found for ${callId}`); - } - - return MessageFormatter.formatToolResult(result, MessageFormatter.isErrorResult(result)); - } catch (error) { - const errorObject = error instanceof Error ? error : new Error(String(error)); - this.logger.error(`Error while executing Tool ${toolName}: ${errorObject.message}`, { - error: errorObject, - }); - return MessageFormatter.formatError(errorObject); + } finally { + // No-op when already resolved (queue mode, credential gate, non-SSE). + this.resolveFunctions[callId]?.(); } }); diff --git a/packages/@n8n/nodes-langchain/nodes/mcp/McpTrigger/McpTrigger.node.ts b/packages/@n8n/nodes-langchain/nodes/mcp/McpTrigger/McpTrigger.node.ts index f63cfb6c8d7..71b6daba027 100644 --- a/packages/@n8n/nodes-langchain/nodes/mcp/McpTrigger/McpTrigger.node.ts +++ b/packages/@n8n/nodes-langchain/nodes/mcp/McpTrigger/McpTrigger.node.ts @@ -6,12 +6,11 @@ import type { IWebhookFunctions, IWebhookResponseData, } from 'n8n-workflow'; -import { NodeConnectionTypes, Node, nodeNameToToolName } from 'n8n-workflow'; +import { NodeConnectionTypes, Node, nodeNameToToolName, n8nOAuth2Auth } from 'n8n-workflow'; import { getConnectedTools } from '@utils/helpers'; import { McpServer, MCP_LIST_TOOLS_REQUEST_MARKER } from './McpServer'; -import { n8nOAuth2Auth } from './n8n-oauth2-auth'; import { MessageParser } from './protocol/MessageParser'; import type { CompressionResponse } from './transport'; @@ -177,7 +176,7 @@ export class McpTrigger extends Node { resp.end('OAuth2 authentication requires mcp trigger node v2.0 or higher'); return { noWebhookResponse: true }; } - const authResult = await n8nOAuth2Auth(context); + const authResult = await n8nOAuth2Auth(context, { realm: 'n8n MCP Server' }); if (authResult === 'handled') { return { noWebhookResponse: true }; } diff --git a/packages/@n8n/nodes-langchain/nodes/mcp/McpTrigger/__tests__/McpServer.test.ts b/packages/@n8n/nodes-langchain/nodes/mcp/McpTrigger/__tests__/McpServer.test.ts index 5560401bb12..d6487f364c8 100644 --- a/packages/@n8n/nodes-langchain/nodes/mcp/McpTrigger/__tests__/McpServer.test.ts +++ b/packages/@n8n/nodes-langchain/nodes/mcp/McpTrigger/__tests__/McpServer.test.ts @@ -170,6 +170,111 @@ describe('McpServer', () => { }); }); + describe('handlePostMessage resolution', () => { + const sessionId = 'sse-session'; + const requestId = 'call-1'; + + type CallToolHandler = ( + request: { params: { name: string; arguments: Record } }, + extra: { sessionId?: string; requestId?: string }, + ) => Promise<{ isError?: boolean; content: Array<{ text: string }> }>; + + async function setupSseSession(tool: ReturnType) { + const transport = createMockTransport(sessionId, 'sse'); + const server = createMockServer(); + const internals = mcpServer as unknown as { + sessionManager: SessionManager; + setupHandlers(server: unknown): void; + }; + + await internals.sessionManager.registerSession(sessionId, server, transport, [tool]); + internals.setupHandlers(server); + + const handler = server.setRequestHandler.mock.calls[1][1] as unknown as CallToolHandler; + let handlerResult: ReturnType | undefined; + transport.handleRequest.mockImplementation(async () => { + handlerResult = handler( + { params: { name: tool.name, arguments: {} } }, + { sessionId, requestId }, + ); + }); + + return { + transport, + getHandlerResult: (): ReturnType | undefined => handlerResult, + }; + } + + const postToolCall = async (tool: ReturnType) => + await mcpServer.handlePostMessage( + createMockRequestWithSessionId( + sessionId, + createValidToolCallMessage(tool.name, {}, requestId), + ), + createMockResponse(), + [tool], + ); + + it('should keep an SSE tool call pending until the tool has finished', async () => { + let finishTool!: () => void; + const tool = createMockTool('get_weather'); + tool.invoke.mockImplementation(async () => { + await new Promise((resolve) => (finishTool = resolve)); + return { ok: true }; + }); + await setupSseSession(tool); + + let resolved = false; + const postPromise = postToolCall(tool).then((result) => { + resolved = true; + return result; + }); + + await new Promise((resolve) => setImmediate(resolve)); + expect(tool.invoke).toHaveBeenCalled(); + expect(resolved).toBe(false); + + finishTool(); + + expect((await postPromise).wasToolCall).toBe(true); + }); + + it('should resolve an SSE tool call whose tool throws', async () => { + const tool = createMockTool('get_weather', { invokeError: new Error('boom') }); + const { getHandlerResult } = await setupSseSession(tool); + + expect((await postToolCall(tool)).wasToolCall).toBe(true); + + const handlerResult = await getHandlerResult(); + expect(handlerResult?.isError).toBe(true); + expect(handlerResult?.content[0].text).toContain('boom'); + }); + + it('should resolve an SSE tool call when the transport fails to handle the request', async () => { + const tool = createMockTool('get_weather'); + const { transport } = await setupSseSession(tool); + transport.handleRequest.mockRejectedValue(new Error('SSE connection not established')); + + expect((await postToolCall(tool)).wasToolCall).toBe(true); + expect(tool.invoke).not.toHaveBeenCalled(); + }); + + it('should resolve a queue-mode tool call before the worker result arrives', async () => { + const tool = createMockTool('get_weather'); + const { getHandlerResult } = await setupSseSession(tool); + mcpServer.setExecutionStrategy( + new QueuedExecutionStrategy(mcpServer.getPendingCallsManager()), + ); + + expect((await postToolCall(tool)).wasToolCall).toBe(true); + expect(mcpServer.hasPendingResponse(sessionId, requestId)).toBe(true); + + mcpServer.handleWorkerResponse(sessionId, requestId, { ok: true }); + + expect((await getHandlerResult())?.isError).toBeUndefined(); + }); + }); + describe('handleDeleteRequest', () => { it('should return 400 when no sessionId provided', async () => { const response = createMockResponse(); diff --git a/packages/@n8n/nodes-langchain/nodes/mcp/McpTrigger/__tests__/n8n-oauth2-auth.test.ts b/packages/@n8n/nodes-langchain/nodes/mcp/McpTrigger/__tests__/n8n-oauth2-auth.test.ts deleted file mode 100644 index fb8f51e9095..00000000000 --- a/packages/@n8n/nodes-langchain/nodes/mcp/McpTrigger/__tests__/n8n-oauth2-auth.test.ts +++ /dev/null @@ -1,36 +0,0 @@ -import type { IWebhookFunctions, N8nOAuth2ValidationResult } from 'n8n-workflow'; - -import { createMockRequest, createMockResponse } from './helpers'; -import { n8nOAuth2Auth } from '../n8n-oauth2-auth'; - -const buildContext = (validation: N8nOAuth2ValidationResult) => { - const response = createMockResponse(); - const request = createMockRequest({ headers: { authorization: 'Bearer test-token' } }); - - const context = { - getNodeWebhookUrl: () => 'https://n8n.example.com/mcp/protected-path', - getResponseObject: () => response, - getRequestObject: () => request, - validateN8nOAuth2Token: vi.fn().mockResolvedValue(validation), - } as unknown as IWebhookFunctions; - - return { context, response }; -}; - -describe('n8nOAuth2Auth', () => { - // An authenticated user lacking execute access (`insufficient_scope`) is rejected with 403; - // an unverifiable token (`invalid_token`) with 401. Both carry the reason in WWW-Authenticate. - test.each([ - ['insufficient_scope', 403], - ['invalid_token', 401], - ] as const)('maps a %s validation failure to a %i response', async (reason, code) => { - const { context, response } = buildContext({ valid: false, reason }); - - const result = await n8nOAuth2Auth(context); - - expect(result).toBe('handled'); - expect(response.writeHead).toHaveBeenCalledWith(code, { - 'WWW-Authenticate': expect.stringContaining(`error="${reason}"`), - }); - }); -}); diff --git a/packages/@n8n/nodes-langchain/nodes/tools/ToolHttpRequest/test/ToolHttpRequest.node.test.ts b/packages/@n8n/nodes-langchain/nodes/tools/ToolHttpRequest/test/ToolHttpRequest.node.test.ts index 07f87d44463..1bb7b047dec 100644 --- a/packages/@n8n/nodes-langchain/nodes/tools/ToolHttpRequest/test/ToolHttpRequest.node.test.ts +++ b/packages/@n8n/nodes-langchain/nodes/tools/ToolHttpRequest/test/ToolHttpRequest.node.test.ts @@ -1,5 +1,5 @@ -import type { INode, ISupplyDataFunctions } from 'n8n-workflow'; -import { jsonParse } from 'n8n-workflow'; +import type { INode, ISupplyDataFunctions, JsonObject } from 'n8n-workflow'; +import { NodeApiError, jsonParse } from 'n8n-workflow'; import { mock } from 'vitest-mock-extended'; import type { N8nTool } from '@utils/N8nTool'; @@ -434,4 +434,313 @@ describe('ToolHttpRequest', () => { ); }); }); + + describe('Error responses', () => { + beforeEach(() => { + executeFunctions.getNodeParameter.mockImplementation( + (paramName: string, _: any, fallback: any) => { + switch (paramName) { + case 'method': + return 'GET'; + case 'url': + return 'https://httpbin.org/status'; + case 'options': + return {}; + case 'placeholderDefinitions.values': + return []; + default: + return fallback; + } + }, + ); + }); + + const invokeTool = async () => { + const { response } = await httpTool.supplyData.call(executeFunctions, 0); + return await (response as N8nTool).invoke({}); + }; + + it('should include the response body when the request fails with a 4xx', async () => { + helpers.httpRequest.mockRejectedValue( + Object.assign(new Error('Request failed with status code 403'), { + response: { + status: 403, + data: { error: 'insufficient_scope', required: 'read:users' }, + }, + }), + ); + + const res = await invokeTool(); + + expect(res).toContain('HTTP 403'); + expect(res).toContain('insufficient_scope'); + expect(res).toContain('read:users'); + }); + + it('should include the response body when the request fails with a 5xx', async () => { + helpers.httpRequest.mockRejectedValue( + Object.assign(new Error('Request failed with status code 503'), { + response: { status: 503, data: 'Upstream database is unavailable' }, + }), + ); + + const res = await invokeTool(); + + expect(res).toContain('HTTP 503'); + expect(res).toContain('Upstream database is unavailable'); + }); + + it('should read the response body from the cause when the error is wrapped', async () => { + const cause = Object.assign(new Error('Request failed with status code 422'), { + response: { status: 422, data: { message: 'The "email" field is required' } }, + }); + + helpers.httpRequest.mockRejectedValue( + Object.assign(new Error('Unprocessable Entity'), { httpCode: '422', cause }), + ); + + const res = await invokeTool(); + + expect(res).toContain('HTTP 422'); + expect(res).toContain('email'); + expect(res).toContain('field is required'); + }); + + it('should report the error alone when the request failed without a response', async () => { + helpers.httpRequest.mockRejectedValue( + Object.assign(new Error('connect ECONNREFUSED 127.0.0.1:443'), { code: 'ECONNREFUSED' }), + ); + + const res = await invokeTool(); + + expect(res).toContain('There was an error'); + expect(res).toContain('ECONNREFUSED'); + expect(res).not.toContain('Response body'); + }); + + it('should not add a response body section when the error response is empty', async () => { + helpers.httpRequest.mockRejectedValue( + Object.assign(new Error('Request failed with status code 404'), { + response: { status: 404, data: '' }, + }), + ); + + const res = await invokeTool(); + + expect(res).toContain('HTTP 404'); + expect(res).not.toContain('Response body'); + }); + + it('should read the response body reported by the legacy request helper', async () => { + helpers.httpRequest.mockRejectedValue( + Object.assign(new Error('Forbidden'), { + statusCode: 403, + error: { error: 'insufficient_scope' }, + response: { status: 403, headers: {}, statusText: 'Forbidden' }, + }), + ); + + const res = await invokeTool(); + + expect(res).toContain('HTTP 403'); + expect(res).toContain('insufficient_scope'); + }); + + it('should derive the status code from the wrapped error when the wrapper has none', async () => { + const cause = Object.assign(new Error('Request failed with status code 429'), { + response: { status: 429, data: 'Rate limit exceeded' }, + }); + + helpers.httpRequest.mockRejectedValue( + Object.assign(new Error('Too Many Requests'), { httpCode: null, cause }), + ); + + const res = await invokeTool(); + + expect(res).toContain('HTTP 429'); + expect(res).toContain('Rate limit exceeded'); + }); + + it('should truncate an oversized error body', async () => { + helpers.httpRequest.mockRejectedValue( + Object.assign(new Error('Internal Server Error'), { + response: { status: 500, data: 'x'.repeat(5000) }, + }), + ); + + const res = await invokeTool(); + + expect(res).toContain('[truncated]'); + expect(res.length).toBeLessThan(3000); + }); + + it('should skip a binary error body', async () => { + helpers.httpRequest.mockRejectedValue( + Object.assign(new Error('Internal Server Error'), { + response: { status: 500, data: Buffer.from([0x89, 0x50, 0x4e, 0x47]) }, + }), + ); + + const res = await invokeTool(); + + expect(res).toContain('HTTP 500'); + expect(res).not.toContain('Response body'); + }); + + it('should not throw when the error body cannot be serialized', async () => { + const circular: Record = { message: 'Bad Request' }; + circular.self = circular; + + helpers.httpRequest.mockRejectedValue( + Object.assign(new Error('Bad Request'), { + response: { status: 400, data: circular }, + }), + ); + + const res = await invokeTool(); + + expect(res).toContain('HTTP 400'); + expect(res).not.toContain('Response body'); + }); + + it('should not repeat the body when it is already part of the error message', async () => { + helpers.httpRequest.mockRejectedValue( + Object.assign(new Error('403 - forbidden by policy'), { + response: { status: 403, data: 'forbidden by policy' }, + }), + ); + + const res = await invokeTool(); + + expect(res).toContain('forbidden by policy'); + expect(res).not.toContain('Response body'); + }); + + it('should redact credential values echoed back in the error body', async () => { + helpers.httpRequest.mockRejectedValue( + Object.assign(new Error('Unauthorized'), { + response: { + status: 401, + data: { + message: 'Rejected request', + api_key: 'sk-live-abcdef123456', + authorization: 'Bearer eyJhbGciOiJIUzI1NiJ9', + }, + }, + }), + ); + + const res = await invokeTool(); + + expect(res).toContain('HTTP 401'); + // The key stays so the model still learns which credential was rejected. + expect(res).toContain('api_key'); + expect(res).toContain('Rejected request'); + expect(res).not.toContain('sk-live-abcdef123456'); + expect(res).not.toContain('eyJhbGciOiJIUzI1NiJ9'); + }); + + it('should redact compound credential keys and unprefixed authorization values', async () => { + helpers.httpRequest.mockRejectedValue( + Object.assign(new Error('Unauthorized'), { + response: { + status: 401, + data: { + message: 'Token exchange failed', + client_secret: 'cs-live-abcdef', + Authorization: 'abcdef-bare-key', + token_type: 'Bearer', + }, + }, + }), + ); + + const res = await invokeTool(); + + expect(res).toContain('Token exchange failed'); + expect(res).toContain('client_secret'); + expect(res).not.toContain('cs-live-abcdef'); + expect(res).not.toContain('abcdef-bare-key'); + // Keys that merely read like credentials keep their value. + expect(res).toContain('"token_type": "Bearer"'); + }); + + it('should redact a credential the client folded into the error message', async () => { + helpers.httpRequest.mockRejectedValue( + Object.assign(new Error('401 - {"client_secret":"cs-live-abcdef"}'), { + response: { status: 401, data: '{"client_secret":"cs-live-abcdef"}' }, + }), + ); + + const res = await invokeTool(); + + expect(res).toContain('HTTP 401'); + expect(res).toContain('client_secret'); + expect(res).not.toContain('cs-live-abcdef'); + // Both sides are redacted, so the body is still recognised as a repeat of the message. + expect(res).not.toContain('Response body'); + }); + + it('should include the response body when a predefined credential wraps the failure', async () => { + // A tool authenticated with a stored credential goes through + // `httpRequestWithAuthentication`, which rejects with a real `NodeApiError` rather than + // the client's own error. That wrapper keeps the payload somewhere else entirely. + executeFunctions.getNodeParameter.mockImplementation( + (paramName: string, _: any, fallback: any) => { + switch (paramName) { + case 'method': + return 'GET'; + case 'url': + return 'https://httpbin.org/status'; + case 'authentication': + return 'predefinedCredentialType'; + case 'nodeCredentialType': + return 'slackApi'; + case 'options': + return {}; + case 'placeholderDefinitions.values': + return []; + default: + return fallback; + } + }, + ); + + // `NodeApiError` recognises an axios rejection by `constructor.name`, so the fixture + // carries that name to take the same branch production does. + class AxiosError extends Error { + isAxiosError = true; + + response = { + status: 403, + data: { error: 'insufficient_scope', required: 'read:users' }, + }; + } + + helpers.httpRequestWithAuthentication.mockRejectedValue( + new NodeApiError( + executeFunctions.getNode(), + new AxiosError('Request failed with status code 403') as unknown as JsonObject, + ), + ); + + const res = await invokeTool(); + + expect(res).toContain('HTTP 403'); + expect(res).toContain('insufficient_scope'); + expect(res).toContain('read:users'); + }); + + it('should leave successful responses untouched', async () => { + helpers.httpRequest.mockResolvedValue({ + body: 'Hello World', + statusCode: 200, + headers: { 'content-type': 'text/plain' }, + }); + + const res = await invokeTool(); + + expect(res).toEqual('Hello World'); + }); + }); }); diff --git a/packages/@n8n/nodes-langchain/nodes/tools/ToolHttpRequest/utils.ts b/packages/@n8n/nodes-langchain/nodes/tools/ToolHttpRequest/utils.ts index 3674f2f4788..b27a31741e0 100644 --- a/packages/@n8n/nodes-langchain/nodes/tools/ToolHttpRequest/utils.ts +++ b/packages/@n8n/nodes-langchain/nodes/tools/ToolHttpRequest/utils.ts @@ -179,6 +179,83 @@ const defaultOptimizer = (response: T) => { return String(response); }; +/** Error payloads are appended to the tool output, so they must not flood the model's context. */ +const MAX_ERROR_BODY_LENGTH = 2000; + +const SECRET_REDACTION = '[redacted]'; + +/** + * Masks credential-shaped values that an API echoed back in its error payload, so they do not + * reach the model or the stored execution data. The key is kept, so `invalid api_key: ` + * still tells the model which credential the API rejected. + * + * A sibling of the redaction applied to skill tool output in + * `packages/@n8n/agents/src/skills/tools.ts`; kept separate because `@n8n/agents` is not a + * dependency here. Keep the two in mind when changing either. + * + * The leading `[\w-]*` matters: `\b` alone never fires inside a compound key such as + * `client_secret`, where every character before `secret` is a word character. The auth scheme is + * optional so an `Authorization` header holding a bare key is masked too. + */ +const redactSecrets = (content: string): string => + content + .replace( + /\b(authorization)(["']?\s*[:=]\s*["']?\s*(?:(?:bearer|basic)\s+)?)[^\s"',;}]+/gi, + `$1$2${SECRET_REDACTION}`, + ) + .replace( + /([\w-]*(?:api[_-]?key|access[_-]?token|refresh[_-]?token|token|password|passwd|secret|credential|private[_-]?key))(["']?\s*[:=]\s*)(["']?)[^\s"',;}]+\3/gi, + `$1$2$3${SECRET_REDACTION}$3`, + ); + +type FailedRequest = { + error?: unknown; + response?: { status?: number; data?: unknown }; + // A tool using a predefined credential goes through `httpRequestWithAuthentication`, which + // rejects with a `NodeApiError`. That has no `response`, and its `cause` is not retained, but + // it copies an object payload onto `context.data`. + context?: { data?: unknown }; +}; + +/** + * Reads the status and payload a failed request came back with. The error is either the one the + * HTTP client rejected with, or a `NodeApiError` wrapping it. The current client reports the + * payload as `response.data`, the legacy one as `error`. + */ +const getFailedRequest = (error: unknown): { status?: number; body?: unknown } => { + for (const candidate of [error, (error as { cause?: unknown })?.cause]) { + const failed = candidate as FailedRequest | undefined; + const body = failed?.response?.data ?? failed?.error ?? failed?.context?.data; + const status = failed?.response?.status; + + if (body !== undefined || status !== undefined) { + return { status, body }; + } + } + + return {}; +}; + +/** + * Turns an error payload into something safe to hand to the model: skips empty and binary bodies, + * and never throws, since this runs while we are already handling a failed request. + */ +const serializeErrorBody = (body: unknown): string | undefined => { + if (body === undefined || body === null || body === '' || isBinary(body)) { + return undefined; + } + + let serialized: string; + + try { + serialized = defaultOptimizer(body); + } catch { + return undefined; + } + + return serialized.trim() ? redactSecrets(serialized) : undefined; +}; + function isBinary(data: unknown) { // Check if data is a Buffer if (Buffer.isBuffer(data)) { @@ -789,8 +866,21 @@ export const configureToolFunction = ( try { fullResponse = await httpRequest(options); } catch (error) { - const httpCode = (error as NodeApiError).httpCode; - response = `${httpCode ? `HTTP ${httpCode} ` : ''}There was an error: "${error.message}"`; + const { status, body } = getFailedRequest(error); + const httpCode = (error as NodeApiError).httpCode ?? status; + // Some clients fold the response body into the message, so it needs the same masking + // as the body itself — and the dedupe check below only holds if both sides are redacted. + const message = redactSecrets(error.message); + response = `${httpCode ? `HTTP ${httpCode} ` : ''}There was an error: "${message}"`; + + // The API's own error payload is what tells the model why the call was rejected. Without + // it the model only sees a status code and tends to invent a reason for the failure. + const errorBody = serializeErrorBody(body); + if (errorBody !== undefined && !message.includes(errorBody)) { + response += `\nResponse body: ${errorBody.slice(0, MAX_ERROR_BODY_LENGTH)}${ + errorBody.length > MAX_ERROR_BODY_LENGTH ? '... [truncated]' : '' + }`; + } } if (!response) { diff --git a/packages/@n8n/nodes-langchain/nodes/tools/ToolWorkflow/v2/ToolWorkflowV2.test.ts b/packages/@n8n/nodes-langchain/nodes/tools/ToolWorkflow/v2/ToolWorkflowV2.test.ts index 4d0b380042b..f45aa6f5caf 100644 --- a/packages/@n8n/nodes-langchain/nodes/tools/ToolWorkflow/v2/ToolWorkflowV2.test.ts +++ b/packages/@n8n/nodes-langchain/nodes/tools/ToolWorkflow/v2/ToolWorkflowV2.test.ts @@ -1,5 +1,6 @@ import { DynamicTool } from '@langchain/core/tools'; -import { NodeOperationError, sleepWithAbort, UnexpectedError } from 'n8n-workflow'; +import { sleep } from '@n8n/utils/sleep'; +import { NodeOperationError, UnexpectedError } from 'n8n-workflow'; import type { ISupplyDataFunctions, INodeExecutionData, @@ -11,14 +12,12 @@ import type { import { WorkflowToolService } from './utils/WorkflowToolService'; import type { MockedFunction } from 'vitest'; -// Mock the sleep functions -vi.mock('n8n-workflow', async () => ({ - ...(await vi.importActual('n8n-workflow')), +// Mock the sleep function +vi.mock('@n8n/utils/sleep', () => ({ sleep: vi.fn().mockResolvedValue(undefined), - sleepWithAbort: vi.fn().mockResolvedValue(undefined), })); -const sleepWithAbortMock = vi.mocked(sleepWithAbort); +const sleepMock = vi.mocked(sleep); function createMockClonedContext( baseContext: ISupplyDataFunctions, @@ -762,8 +761,8 @@ describe('WorkflowTool::WorkflowToolService', () => { expect(executeWorkflowMock).toHaveBeenCalledTimes(expected); }); - it('should respect waitBetweenTries with sleepWithAbort', async () => { - sleepWithAbortMock.mockClear(); + it('should respect waitBetweenTries with sleep', async () => { + sleepMock.mockClear(); const executeWorkflowMock = vi.fn().mockRejectedValue(new Error('Test error')); const contextWithRetryNode = createMockContext({ @@ -803,7 +802,7 @@ describe('WorkflowTool::WorkflowToolService', () => { await tool.func('test query'); - expect(sleepWithAbortMock).toHaveBeenCalledWith(1500, undefined); + expect(sleepMock).toHaveBeenCalledWith(1500, undefined); }); }); @@ -878,7 +877,7 @@ describe('WorkflowTool::WorkflowToolService', () => { }); it('should handle abort signal during retry wait', async () => { - sleepWithAbortMock.mockRejectedValue(new Error('Execution was cancelled')); + sleepMock.mockRejectedValue(new Error('Execution was cancelled')); const executeWorkflowMock = vi .fn() @@ -904,7 +903,7 @@ describe('WorkflowTool::WorkflowToolService', () => { const result = await tool.func('test query'); expect(result).toBe('There was an error: "Execution was cancelled"'); - expect(sleepWithAbortMock).toHaveBeenCalledWith(100, abortController.signal); + expect(sleepMock).toHaveBeenCalledWith(100, abortController.signal); expect(executeWorkflowMock).toHaveBeenCalledTimes(1); // Only first attempt }); @@ -935,7 +934,7 @@ describe('WorkflowTool::WorkflowToolService', () => { }); it('should complete successfully if not aborted', async () => { - sleepWithAbortMock.mockClear().mockResolvedValue(undefined); + sleepMock.mockClear().mockResolvedValue(undefined); const executeWorkflowMock = vi .fn() @@ -962,11 +961,11 @@ describe('WorkflowTool::WorkflowToolService', () => { expect(result).toBe(JSON.stringify({ result: 'success' }, null, 2)); expect(executeWorkflowMock).toHaveBeenCalledTimes(2); - expect(sleepWithAbortMock).toHaveBeenCalledWith(100, abortController.signal); + expect(sleepMock).toHaveBeenCalledWith(100, abortController.signal); }); it('should work when getExecutionCancelSignal is not available', async () => { - sleepWithAbortMock.mockClear().mockResolvedValue(undefined); + sleepMock.mockClear().mockResolvedValue(undefined); const executeWorkflowMock = vi .fn() @@ -990,7 +989,7 @@ describe('WorkflowTool::WorkflowToolService', () => { const result = await tool.func('test query'); expect(result).toBe(JSON.stringify({ result: 'success' }, null, 2)); - expect(sleepWithAbortMock).toHaveBeenCalledWith(100, undefined); + expect(sleepMock).toHaveBeenCalledWith(100, undefined); }); }); }); diff --git a/packages/@n8n/nodes-langchain/nodes/tools/ToolWorkflow/v2/utils/WorkflowToolService.ts b/packages/@n8n/nodes-langchain/nodes/tools/ToolWorkflow/v2/utils/WorkflowToolService.ts index 2ffc0100bf3..01946b87ad3 100644 --- a/packages/@n8n/nodes-langchain/nodes/tools/ToolWorkflow/v2/utils/WorkflowToolService.ts +++ b/packages/@n8n/nodes-langchain/nodes/tools/ToolWorkflow/v2/utils/WorkflowToolService.ts @@ -24,10 +24,10 @@ import { NodeConnectionTypes, NodeOperationError, parseErrorMetadata, - sleepWithAbort, } from 'n8n-workflow'; import { createZodSchemaFromArgs, extractFromAIParameters } from '@n8n/ai-utilities'; +import { sleep } from '@n8n/utils/sleep'; function isNodeExecutionData(data: unknown): data is INodeExecutionData[] { return isArray(data) && Boolean(data.length) && isObject(data[0]) && 'json' in data[0]; @@ -123,7 +123,7 @@ export class WorkflowToolService { lastError = undefined; if (waitBetweenTries !== 0) { try { - await sleepWithAbort(waitBetweenTries, abortSignal); + await sleep(waitBetweenTries, abortSignal); } catch (abortError) { return 'There was an error: "Execution was cancelled"'; } diff --git a/packages/@n8n/nodes-langchain/nodes/vendors/AlibabaCloud/test/transport.test.ts b/packages/@n8n/nodes-langchain/nodes/vendors/AlibabaCloud/test/transport.test.ts index e2532fc9daa..ea24dada8b8 100644 --- a/packages/@n8n/nodes-langchain/nodes/vendors/AlibabaCloud/test/transport.test.ts +++ b/packages/@n8n/nodes-langchain/nodes/vendors/AlibabaCloud/test/transport.test.ts @@ -4,13 +4,9 @@ import { mockDeep } from 'vitest-mock-extended'; import { apiRequest, pollTaskResult } from '../transport'; -vi.mock('n8n-workflow', async () => { - const actual = await vi.importActual('n8n-workflow'); - return { - ...actual, - sleep: vi.fn(), - }; -}); +vi.mock('@n8n/utils/sleep', () => ({ + sleep: vi.fn(), +})); describe('AlicloudModelStudio Transport', () => { let mockExecuteFunctions: ReturnType>; diff --git a/packages/@n8n/nodes-langchain/nodes/vendors/AlibabaCloud/transport/index.ts b/packages/@n8n/nodes-langchain/nodes/vendors/AlibabaCloud/transport/index.ts index 30808f01b15..55e13b1724b 100644 --- a/packages/@n8n/nodes-langchain/nodes/vendors/AlibabaCloud/transport/index.ts +++ b/packages/@n8n/nodes-langchain/nodes/vendors/AlibabaCloud/transport/index.ts @@ -4,7 +4,8 @@ import type { IHttpRequestMethods, ILoadOptionsFunctions, } from 'n8n-workflow'; -import { NodeOperationError, sleep } from 'n8n-workflow'; +import { sleep } from '@n8n/utils/sleep'; +import { NodeOperationError } from 'n8n-workflow'; type RequestParameters = { headers?: IDataObject; diff --git a/packages/@n8n/nodes-langchain/nodes/vendors/MiniMax/test/transport.test.ts b/packages/@n8n/nodes-langchain/nodes/vendors/MiniMax/test/transport.test.ts index fb2102fe131..909c2eb137c 100644 --- a/packages/@n8n/nodes-langchain/nodes/vendors/MiniMax/test/transport.test.ts +++ b/packages/@n8n/nodes-langchain/nodes/vendors/MiniMax/test/transport.test.ts @@ -4,13 +4,9 @@ import { NodeOperationError } from 'n8n-workflow'; import { apiRequest, pollVideoTask, getVideoDownloadUrl } from '../transport'; -vi.mock('n8n-workflow', async () => { - const actual = await import('n8n-workflow'); - return { - ...actual, - sleep: vi.fn(), - }; -}); +vi.mock('@n8n/utils/sleep', () => ({ + sleep: vi.fn(), +})); describe('MiniMax Transport', () => { let mockExecuteFunctions: ReturnType>; diff --git a/packages/@n8n/nodes-langchain/nodes/vendors/MiniMax/transport/index.ts b/packages/@n8n/nodes-langchain/nodes/vendors/MiniMax/transport/index.ts index 082e3dcdce8..d43578a50dd 100644 --- a/packages/@n8n/nodes-langchain/nodes/vendors/MiniMax/transport/index.ts +++ b/packages/@n8n/nodes-langchain/nodes/vendors/MiniMax/transport/index.ts @@ -4,7 +4,8 @@ import type { IHttpRequestMethods, ILoadOptionsFunctions, } from 'n8n-workflow'; -import { NodeOperationError, sleep } from 'n8n-workflow'; +import { sleep } from '@n8n/utils/sleep'; +import { NodeOperationError } from 'n8n-workflow'; type RequestParameters = { headers?: IDataObject; diff --git a/packages/@n8n/nodes-langchain/utils/aws/resolveBedrockRegion.ts b/packages/@n8n/nodes-langchain/utils/aws/resolveBedrockRegion.ts new file mode 100644 index 00000000000..fdcc46e7414 --- /dev/null +++ b/packages/@n8n/nodes-langchain/utils/aws/resolveBedrockRegion.ts @@ -0,0 +1,17 @@ +import { assertSupportedAwsRegion, type AWSRegion } from 'n8n-nodes-base/aws-credentials'; + +const BEDROCK_ARN_REGION = /^arn:(?:aws|aws-cn|aws-us-gov):bedrock:([a-z0-9-]+):/; + +/** + * Resolves the effective Bedrock region for a request. A model given as a full ARN + * (e.g. a cross-region inference profile) carries its own region, which overrides the + * credential's region; the region is validated before it reaches an endpoint URL. + */ +export function resolveBedrockRegion(modelName: string, credentialRegion: AWSRegion): AWSRegion { + const arnRegion = modelName.match(BEDROCK_ARN_REGION)?.[1]; + if (arnRegion === undefined) { + return credentialRegion; + } + assertSupportedAwsRegion(arnRegion); + return arnRegion; +} diff --git a/packages/@n8n/nodes-langchain/utils/aws/test/aws-sdk-client-credentials.test.ts b/packages/@n8n/nodes-langchain/utils/aws/test/aws-sdk-client-credentials.test.ts index 7765e9ec86c..16af0832db3 100644 --- a/packages/@n8n/nodes-langchain/utils/aws/test/aws-sdk-client-credentials.test.ts +++ b/packages/@n8n/nodes-langchain/utils/aws/test/aws-sdk-client-credentials.test.ts @@ -11,6 +11,11 @@ import { describe, it, expect } from 'vitest'; * SDK silently falls back to the host credential chain). The scan is structural and * fails closed: anything it cannot statically verify is reported as an issue rather * than silently passing, so a future client added in a scanned dir is caught. + * + * SDK `*Command` constructions are exempt: they are request payloads sent through an + * already-credentialed client and accept no credentials. The exemption is not trusted + * by name alone — every skipped construction is recorded and a companion test imports + * the class from its real `@aws-sdk` module and asserts it is not client-shaped. */ const AWS_SDK_MODULE = /^@aws-sdk\//; @@ -30,6 +35,11 @@ const SCAN_DIRS = [ // The chat and embeddings nodes share one construction site (createBedrockRuntimeClient). const MIN_EXPECTED_CLIENT_CONSTRUCTIONS = 1; +// Bump when more command constructions are added to the scanned surface. Keeping this +// at the real count ensures the runtime proof below never passes vacuously. +// BedrockInvokeModelEmbeddings constructs InvokeModelCommand. +const MIN_EXPECTED_COMMAND_CONSTRUCTIONS = 1; + interface ImportBinding { module: string; isAwsSdk: boolean; @@ -37,11 +47,20 @@ interface ImportBinding { importedName: string; } +interface SkippedCommandConstruction { + /** Exported class name in the source module (alias-resolved, so `import { X as y }` records `X`). */ + importedName: string; + module: string; + file: string; +} + export interface ScanResult { /** Number of `new (...)` constructions found on the scanned surface. */ clientConstructionCount: number; /** Human-readable description of each statically-unverifiable or missing-key finding. */ issues: string[]; + /** `*Command` constructions exempted from the credentials check, for the runtime proof. */ + skippedCommands: SkippedCommandConstruction[]; } // Phase-0 scope is presence-only: a `credentials` / `masterCredentials` key must be @@ -166,6 +185,7 @@ export function scanSourceForAwsCredentialIssues( const declarations = buildDeclarationIndex(ast); const issues: string[] = []; + const skippedCommands: SkippedCommandConstruction[] = []; let clientConstructionCount = 0; simpleTraverse(ast, { @@ -173,18 +193,28 @@ export function scanSourceForAwsCredentialIssues( // `new (config)` must receive explicit `credentials`. Covers a bare // `new STSClient(...)` and namespace-member `new ns.STSClient(...)` where `ns` is an // `@aws-sdk/*` namespace import; the latter would otherwise slip the net as a - // credential-less client. + // credential-less client. `*Command` constructions (matched on the alias-resolved + // exported name, so a `Command`-named local alias of a client cannot dodge the check) + // are recorded for the runtime proof instead of being verified. if (node.type === AST_NODE_TYPES.NewExpression) { - const clientName = resolveAwsClientName(node.callee, imports); - if (clientName) { + const construction = resolveAwsSdkConstruction(node.callee, imports); + if (construction?.importedName.endsWith('Command')) { + skippedCommands.push({ + importedName: construction.importedName, + module: construction.module, + file: filename, + }); + } else if (construction) { clientConstructionCount += 1; const [arg] = node.arguments; if (!arg) { - issues.push(`new ${clientName}() constructed without a config argument in ${filename}`); + issues.push( + `new ${construction.displayName}() constructed without a config argument in ${filename}`, + ); } else if (!configHasCredentials(arg, declarations)) { issues.push( - `cannot statically verify credentials for new ${clientName}(${describeArg(arg)}) in ${filename}`, + `cannot statically verify credentials for new ${construction.displayName}(${describeArg(arg)}) in ${filename}`, ); } } @@ -211,20 +241,34 @@ export function scanSourceForAwsCredentialIssues( }, }); - return { clientConstructionCount, issues }; + return { clientConstructionCount, issues, skippedCommands }; +} + +interface AwsSdkConstruction { + /** As written at the call site (local alias or `ns.Member`), for issue messages. */ + displayName: string; + /** Exported name in the source module, for classification and runtime lookup. */ + importedName: string; + module: string; } /** - * Returns the display name of the AWS SDK client a `new` callee resolves to, or undefined. - * Handles an Identifier bound to an `@aws-sdk/*` import and a `ns.Client` member where `ns` + * Resolves the AWS SDK class a `new` callee constructs, or undefined for non-SDK callees. + * Handles an Identifier bound to an `@aws-sdk/*` import and a `ns.Member` member where `ns` * is an `@aws-sdk/*` namespace import. */ -function resolveAwsClientName( +function resolveAwsSdkConstruction( callee: TSESTree.Expression, imports: Map, -): string | undefined { +): AwsSdkConstruction | undefined { if (callee.type === AST_NODE_TYPES.Identifier) { - return imports.get(callee.name)?.isAwsSdk ? callee.name : undefined; + const binding = imports.get(callee.name); + if (!binding?.isAwsSdk) return undefined; + return { + displayName: callee.name, + importedName: binding.importedName, + module: binding.module, + }; } if ( @@ -235,7 +279,11 @@ function resolveAwsClientName( ) { const binding = imports.get(callee.object.name); if (binding?.isAwsSdk && binding.importedName === '*') { - return `${callee.object.name}.${callee.property.name}`; + return { + displayName: `${callee.object.name}.${callee.property.name}`, + importedName: callee.property.name, + module: binding.module, + }; } } @@ -298,7 +346,7 @@ function collectTsFiles(dir: string): string[] { } function scanSurface(): ScanResult { - const aggregate: ScanResult = { clientConstructionCount: 0, issues: [] }; + const aggregate: ScanResult = { clientConstructionCount: 0, issues: [], skippedCommands: [] }; for (const dir of SCAN_DIRS) { if (!existsSync(dir)) continue; @@ -306,6 +354,7 @@ function scanSurface(): ScanResult { const result = scanSourceForAwsCredentialIssues(readFileSync(file, 'utf8'), file); aggregate.clientConstructionCount += result.clientConstructionCount; aggregate.issues = aggregate.issues.concat(result.issues); + aggregate.skippedCommands = aggregate.skippedCommands.concat(result.skippedCommands); } } @@ -324,6 +373,30 @@ describe('AWS SDK client credentials guardrail (real surface)', () => { it('constructs every AWS SDK client with explicit credentials and masterCredentials', () => { expect(result.issues).toEqual([]); }); + + it('finds at least the known AWS SDK command constructions', () => { + expect(result.skippedCommands.length).toBeGreaterThanOrEqual( + MIN_EXPECTED_COMMAND_CONSTRUCTIONS, + ); + }); + + it('proves every exempted command construction is a request class, not a client', async () => { + for (const skipped of result.skippedCommands) { + const module = (await import(skipped.module)) as Record; + const constructor = module[skipped.importedName]; + // Fail closed: the exempted name must exist in its module and must not be + // client-shaped (SDK clients expose `send` on the prototype; commands do not). + expect( + constructor, + `${skipped.module} does not export ${skipped.importedName} (${skipped.file})`, + ).toBeTypeOf('function'); + const prototype = (constructor as { prototype?: object }).prototype ?? {}; + expect( + 'send' in prototype, + `${skipped.importedName} is client-shaped and must not be exempted from the credentials check (${skipped.file})`, + ).toBe(false); + } + }); }); describe('scanSourceForAwsCredentialIssues controls', () => { @@ -422,4 +495,32 @@ describe('scanSourceForAwsCredentialIssues controls', () => { 'cannot statically verify credentials for new STSClient(cfg)', ); }); + + it('(j) skips but records command constructions (bare, aliased, namespace-member)', () => { + const result = scanSourceForAwsCredentialIssues( + "import { InvokeModelCommand, ConverseCommand as cc } from '@aws-sdk/client-bedrock-runtime';\n" + + "import * as bedrock from '@aws-sdk/client-bedrock-runtime';\n" + + "const a = new InvokeModelCommand({ modelId: 'm' });\n" + + 'const b = new cc({});\n' + + "const c = new bedrock.InvokeModelCommand({ modelId: 'm' });", + ); + expect(result.clientConstructionCount).toBe(0); + expect(result.issues).toEqual([]); + expect(result.skippedCommands.map((s) => s.importedName)).toEqual([ + 'InvokeModelCommand', + 'ConverseCommand', + 'InvokeModelCommand', + ]); + expect(result.skippedCommands[0].module).toBe('@aws-sdk/client-bedrock-runtime'); + }); + + it('(k) does not exempt a client aliased to a Command-like local name', () => { + const result = scanSourceForAwsCredentialIssues( + "import { STSClient as FooCommand } from '@aws-sdk/client-sts';\nconst c = new FooCommand({ region: 'us-east-1' });", + ); + expect(result.clientConstructionCount).toBe(1); + expect(result.skippedCommands).toEqual([]); + expect(result.issues).toHaveLength(1); + expect(result.issues[0]).toContain('cannot statically verify credentials for new FooCommand'); + }); }); diff --git a/packages/@n8n/nodes-langchain/utils/aws/test/resolveBedrockRegion.test.ts b/packages/@n8n/nodes-langchain/utils/aws/test/resolveBedrockRegion.test.ts new file mode 100644 index 00000000000..81adbfe6993 --- /dev/null +++ b/packages/@n8n/nodes-langchain/utils/aws/test/resolveBedrockRegion.test.ts @@ -0,0 +1,34 @@ +import type { AWSRegion } from 'n8n-nodes-base/aws-credentials'; + +import { resolveBedrockRegion } from '../resolveBedrockRegion'; + +describe('resolveBedrockRegion', () => { + const credentialRegion = 'us-east-1' as AWSRegion; + + it('returns the credential region for a plain model ID', () => { + expect(resolveBedrockRegion('amazon.titan-embed-text-v2:0', credentialRegion)).toBe( + 'us-east-1', + ); + }); + + it('returns the credential region for a region-prefixed inference profile ID (not an ARN)', () => { + expect(resolveBedrockRegion('eu.cohere.embed-v4:0', credentialRegion)).toBe('us-east-1'); + }); + + it.each([ + ['arn:aws:bedrock:eu-west-3:123456789012:inference-profile/eu.cohere.embed-v4:0', 'eu-west-3'], + ['arn:aws-cn:bedrock:cn-north-1:123456789012:inference-profile/cn.x', 'cn-north-1'], + ['arn:aws-us-gov:bedrock:us-gov-west-1::foundation-model/x', 'us-gov-west-1'], + ])('extracts and returns the region from %s', (modelArn, expected) => { + expect(resolveBedrockRegion(modelArn, credentialRegion)).toBe(expected); + }); + + it('throws for an ARN whose region is not supported', () => { + expect(() => + resolveBedrockRegion( + 'arn:aws:bedrock:not-a-region:123456789012:inference-profile/x', + credentialRegion, + ), + ).toThrow(); + }); +}); diff --git a/packages/@n8n/permissions/src/roles/custom-role-scopes.ee.ts b/packages/@n8n/permissions/src/roles/custom-role-scopes.ee.ts index f0ec8d4eec1..773a0843a7e 100644 --- a/packages/@n8n/permissions/src/roles/custom-role-scopes.ee.ts +++ b/packages/@n8n/permissions/src/roles/custom-role-scopes.ee.ts @@ -127,14 +127,10 @@ export const GLOBAL_CUSTOM_ROLE_SCOPE_GROUPS = { Manage: ['role:read', 'role:manage'], }, apiKey: { - 'Manage own': ['apiKey:create', 'apiKey:list', 'apiKey:delete', 'apiKey:update'], - 'Manage all': [ - 'apiKey:create', - 'apiKey:list', - 'apiKey:delete', - 'apiKey:update', - 'apiKey:manage', - ], + // Viewing and revoking your own keys needs no scope (always available), + // so these options only gate creating/editing own keys and managing others'. + 'Manage own': ['apiKey:create', 'apiKey:update'], + 'Manage all': ['apiKey:create', 'apiKey:update', 'apiKey:manage'], }, tag: { // read/list are bundled with write scopes: tags on workflows you can already diff --git a/packages/@n8n/scheduler/src/core/__tests__/factory.test.ts b/packages/@n8n/scheduler/src/core/__tests__/factory.test.ts index 9dc6fb62966..481269a366e 100644 --- a/packages/@n8n/scheduler/src/core/__tests__/factory.test.ts +++ b/packages/@n8n/scheduler/src/core/__tests__/factory.test.ts @@ -1,4 +1,4 @@ -import { ScheduledTaskStatus } from '@n8n/constants'; +import { ScheduledJobMisfirePolicy, ScheduledTaskStatus } from '@n8n/constants'; import { ensureError } from '@n8n/utils/errors/ensure-error'; import { mock } from 'vitest-mock-extended'; @@ -40,6 +40,7 @@ const makeTracer = () => { function makeScheduler(deps: Partial = {}) { const taskStore = mock(); taskStore.markDispatched.mockResolvedValue(1); + taskStore.retireMissedPending.mockResolvedValue(0); const onEvent = vi.fn<(event: SchedulerEvent) => void>(); const materializerTransaction: RunInTransaction = vi.fn(); const scheduler = createScheduler({ @@ -345,8 +346,11 @@ describe('createScheduler materialize', () => { nextRunAt: new Date('2026-01-01T00:00:00.000Z'), lastFiredAt: null, maxAttempts: 3, + misfirePolicy: ScheduledJobMisfirePolicy.Coalesce, + misfireGraceSeconds: 60, }; const tx = mock(); + tx.retireSuperseded.mockResolvedValue(0); tx.claimDueJobs.mockResolvedValue({ now: new Date('2026-01-01T00:00:00.000Z'), jobs: [corrupt], @@ -357,7 +361,14 @@ describe('createScheduler materialize', () => { const summary = await scheduler.materialize(); - expect(summary).toEqual({ claimedJobs: 1, occurrences: 0, created: [], deferredJobs: 1 }); + expect(summary).toEqual({ + claimedJobs: 1, + occurrences: 0, + created: [], + deferredJobs: 1, + misfires: [], + retiredOccurrences: 0, + }); expect(onEvent).toHaveBeenCalledWith({ level: 'error', message: 'Scheduler could not plan a job schedule; deferred for retry', @@ -380,8 +391,11 @@ describe('createScheduler materialize', () => { nextRunAt: new Date('2026-01-01T00:00:00.000Z'), lastFiredAt: null, maxAttempts: 3, + misfirePolicy: ScheduledJobMisfirePolicy.Coalesce, + misfireGraceSeconds: 60, }; const tx = mock(); + tx.retireSuperseded.mockResolvedValue(0); tx.claimDueJobs.mockResolvedValue({ now: new Date('2026-01-01T00:00:00.000Z'), jobs: [due], @@ -411,6 +425,7 @@ describe('createScheduler materialize', () => { // wide enough that the recorded row also reaches the executor before it fires, not // leave it 0. const tx = mock(); + tx.retireSuperseded.mockResolvedValue(0); tx.claimDueJobs.mockResolvedValue(undefined); const materializerTransaction: RunInTransaction = async (work) => await work(tx); const { scheduler } = makeScheduler({ materializerTransaction }); @@ -822,6 +837,7 @@ describe('createScheduler pass timeout and overlap', () => { // The materializer's claim hangs across the shutdown. let resolveClaim!: (claimed: { now: Date; jobs: ScheduledJob[] }) => void; const tx = mock(); + tx.retireSuperseded.mockResolvedValue(0); tx.claimDueJobs.mockImplementation( async () => await new Promise((resolve) => { @@ -985,7 +1001,7 @@ describe('createScheduler reap', () => { const result = await scheduler.reap(); - expect(result).toEqual({ reclaimed: 0, deadLettered: 0 }); + expect(result).toEqual({ reclaimed: 0, deadLettered: 0, missed: 0 }); expect(onEvent).toHaveBeenCalledWith({ level: 'error', message: 'Scheduler could not recover an expired task; skipped until the next sweep', @@ -1002,7 +1018,7 @@ describe('createScheduler reap', () => { const result = await scheduler.reap(); - expect(result).toEqual({ reclaimed: 0, deadLettered: 1 }); + expect(result).toEqual({ reclaimed: 0, deadLettered: 1, missed: 0 }); expect(onEvent).toHaveBeenCalledWith({ level: 'warn', message: 'Scheduler dead-lettered a task; its last attempt lost its lease', @@ -1027,8 +1043,11 @@ describe('createScheduler tracing', () => { nextRunAt: new Date('2026-01-01T00:00:00.000Z'), lastFiredAt: null, maxAttempts: 3, + misfirePolicy: ScheduledJobMisfirePolicy.Coalesce, + misfireGraceSeconds: 60, }; const tx = mock(); + tx.retireSuperseded.mockResolvedValue(0); tx.claimDueJobs.mockResolvedValue({ now: new Date('2026-01-01T00:00:00.000Z'), jobs: [due] }); // Distinct counts (claimed 1, occurrences 2, deferred 0) so that recording a // count under the wrong attribute key cannot slip past these assertions. @@ -1043,7 +1062,14 @@ describe('createScheduler tracing', () => { const summary = await scheduler.materialize(); - expect(summary).toEqual({ claimedJobs: 1, occurrences: 2, created: [], deferredJobs: 0 }); + expect(summary).toEqual({ + claimedJobs: 1, + occurrences: 2, + created: [], + deferredJobs: 0, + misfires: [], + retiredOccurrences: 0, + }); expect(tracer.startSpan).toHaveBeenCalledWith( expect.objectContaining({ name: 'Scheduler materialize', op: 'scheduler.materialize' }), expect.any(Function), @@ -1069,8 +1095,11 @@ describe('createScheduler tracing', () => { nextRunAt: new Date('2026-01-01T00:00:00.000Z'), lastFiredAt: null, maxAttempts: 3, + misfirePolicy: ScheduledJobMisfirePolicy.Coalesce, + misfireGraceSeconds: 60, }; const tx = mock(); + tx.retireSuperseded.mockResolvedValue(0); tx.claimDueJobs.mockResolvedValue({ now: new Date('2026-01-01T00:00:00.000Z'), jobs: [due] }); tx.recordOccurrences.mockResolvedValue({ recorded: 1, @@ -1185,7 +1214,7 @@ describe('createScheduler tracing', () => { const result = await scheduler.reap(); - expect(result).toEqual({ reclaimed: 2, deadLettered: 1 }); + expect(result).toEqual({ reclaimed: 2, deadLettered: 1, missed: 0 }); expect(tracer.startSpan).toHaveBeenCalledWith( expect.objectContaining({ name: 'Scheduler reap', op: 'scheduler.reap' }), expect.any(Function), @@ -1292,7 +1321,14 @@ describe('createScheduler tracing', () => { // at the pass boundary and reported as a no-op summary, not rethrown. const summary = await scheduler.materialize(controller.signal); - expect(summary).toEqual({ claimedJobs: 0, occurrences: 0, created: [], deferredJobs: 0 }); + expect(summary).toEqual({ + claimedJobs: 0, + occurrences: 0, + created: [], + deferredJobs: 0, + misfires: [], + retiredOccurrences: 0, + }); expect(span.setStatus).toHaveBeenCalledWith({ code: SpanStatus.ok }); expect(span.setStatus).not.toHaveBeenCalledWith( expect.objectContaining({ code: SpanStatus.error }), @@ -1354,8 +1390,11 @@ describe('createScheduler metrics', () => { nextRunAt: new Date('2026-01-01T00:00:00.000Z'), lastFiredAt: null, maxAttempts: 3, + misfirePolicy: ScheduledJobMisfirePolicy.Coalesce, + misfireGraceSeconds: 60, }; const tx = mock(); + tx.retireSuperseded.mockResolvedValue(0); tx.claimDueJobs.mockResolvedValue({ now: new Date('2026-01-01T00:00:00.000Z'), jobs: [corrupt], @@ -1380,7 +1419,7 @@ describe('createScheduler metrics', () => { await scheduler.reap(); - expect(metrics.recordReaped).toHaveBeenCalledWith(0, 1); + expect(metrics.recordReaped).toHaveBeenCalledWith(0, 1, 0); }); it('records the retention outcome from the pass summary', async () => { diff --git a/packages/@n8n/scheduler/src/core/factory.ts b/packages/@n8n/scheduler/src/core/factory.ts index fc7198b79fc..f984f7b8c2d 100644 --- a/packages/@n8n/scheduler/src/core/factory.ts +++ b/packages/@n8n/scheduler/src/core/factory.ts @@ -17,7 +17,7 @@ import { import type { ExecutorOptions, ExecutorTaskStore } from './executor'; import { DEFAULT_LIFECYCLE_OPTIONS, pollLookaheadSeconds, Loop, PASS_TIMED_OUT } from './lifecycle'; import type { LifecycleOptions } from './lifecycle'; -import { DEFAULT_MATERIALIZER_OPTIONS, materialize } from './materializer'; +import { DEFAULT_MATERIALIZER_OPTIONS, materialize, totalDiscarded } from './materializer'; import type { MaterializerOptions, RunInTransaction } from './materializer'; import { DEFAULT_REAPER_OPTIONS, reap } from './reaper'; import type { ReaperOptions, ReaperTaskStore } from './reaper'; @@ -334,7 +334,11 @@ export function createScheduler(deps: SchedulerDeps): Scheduler & SchedulerPasse signal, ); await traceCreatedTasks(tracer, summary.created); - recordMetric(() => metrics.recordMaterialized(summary.occurrences, summary.deferredJobs)); + recordMetric(() => { + metrics.recordMaterialized(summary.occurrences, summary.deferredJobs); + metrics.recordMisfired(summary.misfires); + metrics.recordRetired(summary.retiredOccurrences); + }); return summary; } catch (error) { // `throwIfAborted` always throws `signal.reason` itself, so this only @@ -345,7 +349,14 @@ export function createScheduler(deps: SchedulerDeps): Scheduler & SchedulerPasse signal.reason !== PASS_TIMED_OUT && error === signal.reason ) { - return { claimedJobs: 0, occurrences: 0, created: [], deferredJobs: 0 }; + return { + claimedJobs: 0, + occurrences: 0, + created: [], + deferredJobs: 0, + misfires: [], + retiredOccurrences: 0, + }; } throw error; } @@ -354,6 +365,8 @@ export function createScheduler(deps: SchedulerDeps): Scheduler & SchedulerPasse [SCHEDULER_ATTRIBUTES.claimedJobs]: summary.claimedJobs, [SCHEDULER_ATTRIBUTES.occurrences]: summary.occurrences, [SCHEDULER_ATTRIBUTES.deferredJobs]: summary.deferredJobs, + [SCHEDULER_ATTRIBUTES.skippedOccurrences]: totalDiscarded(summary.misfires), + [SCHEDULER_ATTRIBUTES.retiredOccurrences]: summary.retiredOccurrences, }), ); @@ -386,6 +399,11 @@ export function createScheduler(deps: SchedulerDeps): Scheduler & SchedulerPasse }, ); }, + onRetireError: (error) => { + emit('error', 'Scheduler could not retire stale pending occurrences', { + error: described(error), + }); + }, onDeadLetter: (task) => { emit('warn', 'Scheduler dead-lettered a task; its last attempt lost its lease', { ...task, @@ -401,12 +419,15 @@ export function createScheduler(deps: SchedulerDeps): Scheduler & SchedulerPasse }, signal, ); - recordMetric(() => metrics.recordReaped(result.reclaimed, result.deadLettered)); + recordMetric(() => + metrics.recordReaped(result.reclaimed, result.deadLettered, result.missed), + ); return result; }, (result) => ({ [SCHEDULER_ATTRIBUTES.reclaimed]: result.reclaimed, [SCHEDULER_ATTRIBUTES.deadLettered]: result.deadLettered, + [SCHEDULER_ATTRIBUTES.missed]: result.missed, }), ); diff --git a/packages/@n8n/scheduler/src/core/index.ts b/packages/@n8n/scheduler/src/core/index.ts index d65a2abe51c..8af746ed6c3 100644 --- a/packages/@n8n/scheduler/src/core/index.ts +++ b/packages/@n8n/scheduler/src/core/index.ts @@ -51,10 +51,11 @@ export type { DispatchReporter, DispatchDecision, } from './executor'; -export { DEFAULT_MATERIALIZER_OPTIONS, materialize } from './materializer'; +export { DEFAULT_MATERIALIZER_OPTIONS, materialize, totalDiscarded } from './materializer'; export type { MaterializerOptions, MaterializerSummary, + MisfireCount, NewOccurrence, RunInTransaction, } from './materializer'; diff --git a/packages/@n8n/scheduler/src/core/materializer/__tests__/materialize.test.ts b/packages/@n8n/scheduler/src/core/materializer/__tests__/materialize.test.ts index 198d241a3a8..8c1c7761e22 100644 --- a/packages/@n8n/scheduler/src/core/materializer/__tests__/materialize.test.ts +++ b/packages/@n8n/scheduler/src/core/materializer/__tests__/materialize.test.ts @@ -1,4 +1,5 @@ -import { mock } from 'vitest-mock-extended'; +import { ScheduledJobMisfirePolicy } from '@n8n/constants'; +import { mock, type MockProxy } from 'vitest-mock-extended'; import type { ScheduledJob } from '../../types'; import { materialize, type MaterializerOptions } from '../materialize'; @@ -21,8 +22,22 @@ const makeJob = (id: number): ScheduledJob => ({ nextRunAt: NOW, lastFiredAt: null, maxAttempts: 1, + misfirePolicy: ScheduledJobMisfirePolicy.Coalesce, + misfireGraceSeconds: 60, }); +const makeSkipJob = (id: number): ScheduledJob => ({ + ...makeJob(id), + misfirePolicy: ScheduledJobMisfirePolicy.Skip, +}); + +/** A mock transaction whose `retireSuperseded` resolves to 0 rather than `undefined`. */ +function makeTx(): MockProxy { + const tx = mock(); + tx.retireSuperseded.mockResolvedValue(0); + return tx; +} + /** A transaction runner that hands `work` the given operations, without a real transaction. */ const runnerWith = (tx: MaterializerTransaction): RunInTransaction => @@ -40,18 +55,25 @@ const options: MaterializerOptions = { describe('materialize', () => { it('does nothing when no jobs are due', async () => { - const tx = mock(); + const tx = makeTx(); tx.claimDueJobs.mockResolvedValue(undefined); const summary = await materialize(runnerWith(tx), options); - expect(summary).toEqual({ claimedJobs: 0, occurrences: 0, created: [], deferredJobs: 0 }); + expect(summary).toEqual({ + claimedJobs: 0, + occurrences: 0, + created: [], + deferredJobs: 0, + misfires: [], + retiredOccurrences: 0, + }); expect(tx.recordOccurrences).not.toHaveBeenCalled(); expect(tx.advanceJobs).not.toHaveBeenCalled(); }); it('records occurrences and advances each claimed job', async () => { - const tx = mock(); + const tx = makeTx(); tx.claimDueJobs.mockResolvedValue({ now: NOW, jobs: [makeJob(1), makeJob(2)] }); // windowSeconds: 0 means one occurrence per job: the due fire, both newly recorded. tx.recordOccurrences.mockResolvedValue({ recorded: 2, created: [] }); @@ -59,7 +81,14 @@ describe('materialize', () => { const summary = await materialize(runnerWith(tx), options); // The summary reports the count `recordOccurrences` returned, not the plan's size. - expect(summary).toEqual({ claimedJobs: 2, occurrences: 2, created: [], deferredJobs: 0 }); + expect(summary).toEqual({ + claimedJobs: 2, + occurrences: 2, + created: [], + deferredJobs: 0, + misfires: [], + retiredOccurrences: 0, + }); // One insert and one update for the whole batch, not a pair per job. expect(tx.recordOccurrences).toHaveBeenCalledTimes(1); @@ -73,11 +102,14 @@ describe('materialize', () => { scheduledFor: NOW, runAt: NOW, maxAttempts: 1, + missedAfter: new Date('2026-01-01T00:01:00.000Z'), }); expect(tx.recordOccurrences).toHaveBeenCalledWith([occurrence(1), occurrence(2)]); const plan = { occurrences: [NOW], + skippedOccurrences: 0, + catchUpAt: null, nextRunAt: new Date('2026-01-01T00:00:10.000Z'), lastFiredAt: NOW, }; @@ -87,8 +119,162 @@ describe('materialize', () => { ]); }); + it('groups the discarded backlog by task type and policy', async () => { + const tx = makeTx(); + // Two jobs two minutes behind: 13 due instants each, of which coalesce records + // one and discards twelve. + const backlogged = (id: number): ScheduledJob => ({ + ...makeJob(id), + nextRunAt: new Date('2025-12-31T23:58:00.000Z'), + }); + tx.claimDueJobs.mockResolvedValue({ now: NOW, jobs: [backlogged(1), backlogged(2)] }); + tx.recordOccurrences.mockResolvedValue({ recorded: 2, created: [] }); + + const summary = await materialize(runnerWith(tx), options); + + expect(summary.occurrences).toBe(2); + expect(summary.misfires).toEqual([ + { taskType: 'test', policy: ScheduledJobMisfirePolicy.Coalesce, discarded: 24 }, + ]); + }); + + it('keeps a separate count per task type and per policy', async () => { + const tx = makeTx(); + const backlogged = (id: number, overrides: Partial): ScheduledJob => ({ + ...makeJob(id), + nextRunAt: new Date('2025-12-31T23:58:00.000Z'), + ...overrides, + }); + tx.claimDueJobs.mockResolvedValue({ + now: NOW, + jobs: [ + backlogged(1, {}), + backlogged(2, { taskType: 'poll' }), + backlogged(3, { misfirePolicy: ScheduledJobMisfirePolicy.Skip }), + ], + }); + tx.recordOccurrences.mockResolvedValue({ recorded: 2, created: [] }); + + const summary = await materialize(runnerWith(tx), options); + + expect(summary.misfires).toEqual([ + { taskType: 'test', policy: ScheduledJobMisfirePolicy.Coalesce, discarded: 12 }, + { taskType: 'poll', policy: ScheduledJobMisfirePolicy.Coalesce, discarded: 12 }, + { taskType: 'test', policy: ScheduledJobMisfirePolicy.Skip, discarded: 13 }, + ]); + }); + + it('makes a catch-up run visible now, keeping the instant it was due for', async () => { + const tx = makeTx(); + const dueAt = new Date('2025-12-31T23:58:00.000Z'); + tx.claimDueJobs.mockResolvedValue({ + now: NOW, + jobs: [{ ...makeJob(1), nextRunAt: dueAt }], + }); + tx.recordOccurrences.mockResolvedValue({ recorded: 1, created: [] }); + + await materialize(runnerWith(tx), options); + + expect(tx.recordOccurrences).toHaveBeenCalledWith([ + expect.objectContaining({ + scheduledFor: NOW, + runAt: NOW, + // Visible now, so its grace starts now rather than at the instant it stands in for. + missedAfter: new Date('2026-01-01T00:01:00.000Z'), + }), + ]); + }); + + it('retires the occurrences a catch-up run supersedes', async () => { + const tx = makeTx(); + const dueAt = new Date('2025-12-31T23:58:00.000Z'); + tx.claimDueJobs.mockResolvedValue({ + now: NOW, + jobs: [{ ...makeJob(1), nextRunAt: dueAt }], + }); + tx.recordOccurrences.mockResolvedValue({ recorded: 1, created: [] }); + tx.retireSuperseded.mockResolvedValue(3); + + const summary = await materialize(runnerWith(tx), options); + + expect(tx.retireSuperseded).toHaveBeenCalledWith([{ jobId: 1, before: NOW }]); + expect(summary.retiredOccurrences).toBe(3); + }); + + it('retires nothing when no policy applied', async () => { + const tx = makeTx(); + tx.claimDueJobs.mockResolvedValue({ now: NOW, jobs: [makeJob(1)] }); + tx.recordOccurrences.mockResolvedValue({ recorded: 1, created: [] }); + + await materialize(runnerWith(tx), options); + + expect(tx.retireSuperseded).toHaveBeenCalledWith([]); + }); + + it('retires nothing under skip, which never plans a catch-up run', async () => { + const tx = makeTx(); + tx.claimDueJobs.mockResolvedValue({ + now: NOW, + jobs: [{ ...makeSkipJob(1), nextRunAt: new Date('2025-12-31T23:58:00.000Z') }], + }); + tx.recordOccurrences.mockResolvedValue({ recorded: 0, created: [] }); + + await materialize(runnerWith(tx), options); + + expect(tx.retireSuperseded).toHaveBeenCalledWith([]); + }); + + it('gives an occurrence a deadline measured from its own instant', async () => { + const tx = makeTx(); + tx.claimDueJobs.mockResolvedValue({ now: NOW, jobs: [makeSkipJob(1)] }); + tx.recordOccurrences.mockResolvedValue({ recorded: 1, created: [] }); + + await materialize(runnerWith(tx), options); + + expect(tx.recordOccurrences).toHaveBeenCalledWith([ + expect.objectContaining({ + runAt: NOW, + missedAfter: new Date('2026-01-01T00:01:00.000Z'), + }), + ]); + }); + + it('measures a late-recorded occurrence deadline from now, not from when it came due', async () => { + const tx = makeTx(); + // Due 59s ago, inside its 60s grace: measuring from that instant would leave the + // row one second of life. + const dueAt = new Date('2025-12-31T23:59:01.000Z'); + tx.claimDueJobs.mockResolvedValue({ + now: NOW, + jobs: [{ ...makeSkipJob(1), nextRunAt: dueAt, intervalSeconds: 3600 }], + }); + tx.recordOccurrences.mockResolvedValue({ recorded: 1, created: [] }); + + await materialize(runnerWith(tx), options); + + expect(tx.recordOccurrences).toHaveBeenCalledWith([ + expect.objectContaining({ + scheduledFor: dueAt, + runAt: dueAt, + missedAfter: new Date('2026-01-01T00:01:00.000Z'), + }), + ]); + }); + + it('gives a coalesce occurrence a deadline too, so a stale one stops being claimable', async () => { + const tx = makeTx(); + tx.claimDueJobs.mockResolvedValue({ now: NOW, jobs: [makeJob(1)] }); + tx.recordOccurrences.mockResolvedValue({ recorded: 1, created: [] }); + + await materialize(runnerWith(tx), options); + + expect(tx.recordOccurrences).toHaveBeenCalledWith([ + expect.objectContaining({ missedAfter: new Date('2026-01-01T00:01:00.000Z') }), + ]); + }); + it('claims at most batchSize jobs', async () => { - const tx = mock(); + const tx = makeTx(); tx.claimDueJobs.mockResolvedValue(undefined); await materialize(runnerWith(tx), { ...options, batchSize: 25 }); @@ -97,7 +283,7 @@ describe('materialize', () => { }); it('claims ahead by lookaheadSeconds, passing it to the claim in milliseconds', async () => { - const tx = mock(); + const tx = makeTx(); tx.claimDueJobs.mockResolvedValue(undefined); await materialize(runnerWith(tx), { ...options, batchSize: 25, lookaheadSeconds: 12 }); @@ -108,7 +294,7 @@ describe('materialize', () => { }); it('reports skipped duplicates, and a throwing reporter does not fail the pass', async () => { - const tx = mock(); + const tx = makeTx(); tx.claimDueJobs.mockResolvedValue({ now: NOW, jobs: [makeJob(1), makeJob(2)] }); // Two planned, one already recorded (e.g. by a concurrent pass). tx.recordOccurrences.mockResolvedValue({ recorded: 1, created: [] }); @@ -119,12 +305,19 @@ describe('materialize', () => { const summary = await materialize(runnerWith(tx), options, { onSkippedDuplicates }); expect(onSkippedDuplicates).toHaveBeenCalledWith({ planned: 2, recorded: 1 }); - expect(summary).toEqual({ claimedJobs: 2, occurrences: 1, created: [], deferredJobs: 0 }); + expect(summary).toEqual({ + claimedJobs: 2, + occurrences: 1, + created: [], + deferredJobs: 0, + misfires: [], + retiredOccurrences: 0, + }); expect(tx.advanceJobs).toHaveBeenCalledTimes(1); }); it('stays silent when every planned occurrence is newly recorded', async () => { - const tx = mock(); + const tx = makeTx(); tx.claimDueJobs.mockResolvedValue({ now: NOW, jobs: [makeJob(1)] }); tx.recordOccurrences.mockResolvedValue({ recorded: 1, created: [] }); const onSkippedDuplicates = vi.fn(); @@ -143,14 +336,21 @@ describe('materialize', () => { cronExpression: null, intervalSeconds: null, }; - const tx = mock(); + const tx = makeTx(); tx.claimDueJobs.mockResolvedValue({ now: NOW, jobs: [good, bad] }); tx.recordOccurrences.mockResolvedValue({ recorded: 1, created: [] }); const onPlanError = vi.fn(); const summary = await materialize(runnerWith(tx), options, { onPlanError }); - expect(summary).toEqual({ claimedJobs: 2, occurrences: 1, created: [], deferredJobs: 1 }); + expect(summary).toEqual({ + claimedJobs: 2, + occurrences: 1, + created: [], + deferredJobs: 1, + misfires: [], + retiredOccurrences: 0, + }); expect(onPlanError).toHaveBeenCalledTimes(1); expect(onPlanError).toHaveBeenCalledWith(bad, expect.anything()); @@ -166,7 +366,7 @@ describe('materialize', () => { }); it('cancelled before any work, it opens no transaction at all', async () => { - const tx = mock(); + const tx = makeTx(); let transactionsOpened = 0; const runInTransaction: RunInTransaction = async (work) => { transactionsOpened += 1; @@ -180,7 +380,7 @@ describe('materialize', () => { }); it('cancelled during the claim, it throws inside the transaction so the claim rolls back', async () => { - const tx = mock(); + const tx = makeTx(); const controller = new AbortController(); // The cancellation lands while the claim query is in flight. tx.claimDueJobs.mockImplementation(async () => { @@ -195,7 +395,7 @@ describe('materialize', () => { }); it('cancelled during the insert, it throws before advancing the jobs', async () => { - const tx = mock(); + const tx = makeTx(); const controller = new AbortController(); tx.claimDueJobs.mockResolvedValue({ now: NOW, jobs: [makeJob(1)] }); tx.recordOccurrences.mockImplementation(async () => { @@ -220,7 +420,7 @@ describe('materialize', () => { cronExpression: null, intervalSeconds: null, }; - const tx = mock(); + const tx = makeTx(); tx.claimDueJobs.mockResolvedValue({ now: NOW, jobs: [good, bad] }); tx.recordOccurrences.mockResolvedValue({ recorded: 1, created: [] }); // The reporter is host-supplied; a broken one must not turn a deferred job @@ -231,7 +431,14 @@ describe('materialize', () => { const summary = await materialize(runnerWith(tx), options, { onPlanError }); - expect(summary).toEqual({ claimedJobs: 2, occurrences: 1, created: [], deferredJobs: 1 }); + expect(summary).toEqual({ + claimedJobs: 2, + occurrences: 1, + created: [], + deferredJobs: 1, + misfires: [], + retiredOccurrences: 0, + }); expect(tx.recordOccurrences).toHaveBeenCalledTimes(1); expect(tx.advanceJobs).toHaveBeenCalledTimes(1); }); diff --git a/packages/@n8n/scheduler/src/core/materializer/__tests__/plan.property.test.ts b/packages/@n8n/scheduler/src/core/materializer/__tests__/plan.property.test.ts index b0efb8f502e..72b4fa2c581 100644 --- a/packages/@n8n/scheduler/src/core/materializer/__tests__/plan.property.test.ts +++ b/packages/@n8n/scheduler/src/core/materializer/__tests__/plan.property.test.ts @@ -1,3 +1,4 @@ +import { ScheduledJobMisfirePolicy } from '@n8n/constants'; import fc from 'fast-check'; import type { CronExpression } from 'n8n-workflow'; @@ -20,6 +21,8 @@ const makeIntervalJob = (intervalSeconds: number): ScheduledJob => ({ nextRunAt: NOW, lastFiredAt: null, maxAttempts: 1, + misfirePolicy: ScheduledJobMisfirePolicy.Coalesce, + misfireGraceSeconds: 60, }); const makeCronJob = (cronExpression: string, timezone: string): ScheduledJob => ({ @@ -36,6 +39,8 @@ const makeCronJob = (cronExpression: string, timezone: string): ScheduledJob => nextRunAt: NOW, lastFiredAt: null, maxAttempts: 1, + misfirePolicy: ScheduledJobMisfirePolicy.Coalesce, + misfireGraceSeconds: 60, }); const makeOneOffJob = (fireAt: Date): ScheduledJob => ({ @@ -54,6 +59,8 @@ const makeOneOffJob = (fireAt: Date): ScheduledJob => ({ nextRunAt: fireAt, lastFiredAt: null, maxAttempts: 1, + misfirePolicy: ScheduledJobMisfirePolicy.Coalesce, + misfireGraceSeconds: 60, }); /** @@ -180,3 +187,121 @@ describe('planOccurrences (fast-check)', () => { ); }); }); + +describe('planOccurrences misfire handling (fast-check)', () => { + /** An every-`intervalSeconds` job whose clock stopped `backlogSeconds` ago. */ + const makeBackloggedJob = ( + intervalSeconds: number, + backlogSeconds: number, + misfirePolicy: ScheduledJobMisfirePolicy, + misfireGraceSeconds: number, + ): ScheduledJob => ({ + ...makeIntervalJob(intervalSeconds), + nextRunAt: new Date(NOW.getTime() - backlogSeconds * 1000), + misfirePolicy, + misfireGraceSeconds, + }); + + const MAX_PER_JOB = 500; + + const arbBacklog = fc.tuple( + fc.integer({ min: 1, max: 600 }), // intervalSeconds + fc.integer({ min: 0, max: 7200 }), // backlogSeconds + fc.integer({ min: 1, max: 300 }), // misfireGraceSeconds + fc.integer({ min: 0, max: 600 }), // windowSeconds + ); + + /** + * The instants the pass had to choose from, derived independently of the + * implementation so the properties below compare against the candidate set rather + * than against what was recorded. + */ + const candidates = (intervalSeconds: number, backlogSeconds: number, windowSeconds: number) => { + const first = NOW.getTime() - backlogSeconds * 1000; + const windowEnd = NOW.getTime() + windowSeconds * 1000; + const all: Date[] = []; + // Counted past the cap as well as up to it: a walk that filled the cap exactly + // with nothing left in the window counts as complete, not truncated. + let inWindow = 0; + for (let t = first; t <= windowEnd; t += intervalSeconds * 1000) { + inWindow++; + if (all.length < MAX_PER_JOB) all.push(new Date(t)); + } + return { all, truncated: inWindow > MAX_PER_JOB }; + }; + + it('records at most one occurrence at or before now once a deadline has passed', () => { + fc.assert( + fc.property( + arbBacklog, + ([intervalSeconds, backlogSeconds, misfireGraceSeconds, windowSeconds]) => { + const options = { windowSeconds, maxPerJob: MAX_PER_JOB, defaultTimezone: 'UTC' }; + const { all, truncated } = candidates(intervalSeconds, backlogSeconds, windowSeconds); + const anyPastDeadline = all.some( + (o) => o.getTime() + misfireGraceSeconds * 1000 <= NOW.getTime(), + ); + + for (const policy of [ + ScheduledJobMisfirePolicy.Coalesce, + ScheduledJobMisfirePolicy.Skip, + ]) { + const job = makeBackloggedJob( + intervalSeconds, + backlogSeconds, + policy, + misfireGraceSeconds, + ); + const recorded = planOccurrences(job, NOW, options).occurrences; + const behind = recorded.filter((o) => o.getTime() <= NOW.getTime()); + const ahead = all.filter((o) => o.getTime() > NOW.getTime()); + + if (!anyPastDeadline) { + expect(recorded).toEqual(all); + continue; + } + + // Skip records no catch-up run; coalesce records exactly one, unless the + // walk was capped mid-backlog, where it defers to a later pass. + const expectedBehind = + policy === ScheduledJobMisfirePolicy.Skip || (truncated && ahead.length === 0) + ? 0 + : 1; + expect(behind).toHaveLength(expectedBehind); + + if (expectedBehind === 1) { + const newestBehind = all.filter((o) => o.getTime() <= NOW.getTime()).at(-1); + expect(behind[0]).toEqual(newestBehind); + } + expect(recorded.filter((o) => o.getTime() > NOW.getTime())).toEqual(ahead); + } + }, + ), + ); + }); + + it('advances the clock identically under either policy', () => { + fc.assert( + fc.property( + arbBacklog, + ([intervalSeconds, backlogSeconds, misfireGraceSeconds, windowSeconds]) => { + const options = { windowSeconds, maxPerJob: MAX_PER_JOB, defaultTimezone: 'UTC' }; + const plans = [ScheduledJobMisfirePolicy.Coalesce, ScheduledJobMisfirePolicy.Skip].map( + (policy) => + planOccurrences( + makeBackloggedJob(intervalSeconds, backlogSeconds, policy, misfireGraceSeconds), + NOW, + options, + ), + ); + + expect(plans[0].nextRunAt).toEqual(plans[1].nextRunAt); + expect(plans[0].lastFiredAt).toEqual(plans[1].lastFiredAt); + + expect(plans[0].occurrences.length + plans[0].skippedOccurrences).toBe( + plans[1].occurrences.length + plans[1].skippedOccurrences, + ); + }, + ), + ); + }); +}); diff --git a/packages/@n8n/scheduler/src/core/materializer/__tests__/plan.test.ts b/packages/@n8n/scheduler/src/core/materializer/__tests__/plan.test.ts index 65344b322b0..36d2f1e6e98 100644 --- a/packages/@n8n/scheduler/src/core/materializer/__tests__/plan.test.ts +++ b/packages/@n8n/scheduler/src/core/materializer/__tests__/plan.test.ts @@ -1,8 +1,12 @@ +import { ScheduledJobMisfirePolicy } from '@n8n/constants'; + +import { CorruptStorageRowError } from '../../errors'; import type { ScheduledJob } from '../../types'; import { planOccurrences } from '../plan'; const NOW = new Date('2026-01-01T00:00:00.000Z'); const secondsAfter = (base: Date, seconds: number) => new Date(base.getTime() + seconds * 1000); +const secondsBefore = (base: Date, seconds: number) => secondsAfter(base, -seconds); /** An every-10s interval job due at NOW, overridable per test. */ const makeJob = (overrides: Partial = {}): ScheduledJob => ({ @@ -19,6 +23,8 @@ const makeJob = (overrides: Partial = {}): ScheduledJob => ({ nextRunAt: NOW, lastFiredAt: null, maxAttempts: 1, + misfirePolicy: ScheduledJobMisfirePolicy.Coalesce, + misfireGraceSeconds: 60, ...overrides, }); @@ -142,3 +148,173 @@ describe('planOccurrences', () => { expect(plan.nextRunAt).toBeNull(); }); }); + +describe('planOccurrences misfire handling', () => { + // A job whose clock stopped two minutes ago: fires every 10s from NOW-120s, giving + // 13 due instants ending at NOW. With a 60s grace, the 7 at or before NOW-60s are + // past their deadline. + const backloggedJob = (overrides: Partial = {}) => + makeJob({ nextRunAt: secondsBefore(NOW, 120), ...overrides }); + + const skipPolicy = { misfirePolicy: ScheduledJobMisfirePolicy.Skip }; + + it('records a single catch-up run under coalesce', () => { + const plan = planOccurrences(backloggedJob(), NOW, options); + + expect(plan.occurrences).toEqual([NOW]); + expect(plan.skippedOccurrences).toBe(12); + }); + + it('records nothing under skip', () => { + const plan = planOccurrences(backloggedJob(skipPolicy), NOW, options); + + expect(plan.occurrences).toEqual([]); + expect(plan.skippedOccurrences).toBe(13); + }); + + it('keeps the occurrences still ahead of now, whatever the policy', () => { + // A window reaching 30s past NOW, so the pass also holds three future instants. + const ahead = { ...options, windowSeconds: 30 }; + const future = [secondsAfter(NOW, 10), secondsAfter(NOW, 20), secondsAfter(NOW, 30)]; + + const coalesced = planOccurrences(backloggedJob(), NOW, ahead); + const skipped = planOccurrences(backloggedJob(skipPolicy), NOW, ahead); + + expect(coalesced.occurrences).toEqual([NOW, ...future]); + expect(skipped.occurrences).toEqual(future); + }); + + it('advances the clock past the discarded occurrences whatever the policy', () => { + const coalesced = planOccurrences(backloggedJob(), NOW, options); + const skipped = planOccurrences(backloggedJob(skipPolicy), NOW, options); + + expect(coalesced.nextRunAt).toEqual(secondsAfter(NOW, 10)); + expect(skipped.nextRunAt).toEqual(coalesced.nextRunAt); + expect(coalesced.lastFiredAt).toEqual(NOW); + expect(skipped.lastFiredAt).toEqual(coalesced.lastFiredAt); + }); + + it('treats an occurrence exactly at its deadline as missed', () => { + // Skip, not coalesce: under coalesce the boundary instant would still be + // recorded as the catch-up run either way, hiding whether it counted as missed. + const job = makeJob({ ...skipPolicy, nextRunAt: secondsBefore(NOW, 60) }); + + const plan = planOccurrences(job, NOW, options); + + expect(plan.occurrences).toEqual([]); + expect(plan.skippedOccurrences).toBe(7); + }); + + it('leaves a pass whose occurrences are all inside their grace window alone', () => { + // No policy applies, so even a late run is recorded as usual. + const job = makeJob({ ...skipPolicy, nextRunAt: secondsBefore(NOW, 30) }); + + const plan = planOccurrences(job, NOW, options); + + expect(plan.occurrences).toEqual([ + secondsBefore(NOW, 30), + secondsBefore(NOW, 20), + secondsBefore(NOW, 10), + NOW, + ]); + expect(plan.skippedOccurrences).toBe(0); + }); + + it('does not treat an on-time pass as a misfire', () => { + const plan = planOccurrences(makeJob(), NOW, options); + + expect(plan.occurrences).toEqual([NOW]); + expect(plan.skippedOccurrences).toBe(0); + }); + + it('defers the catch-up run while a capped backlog is still draining', () => { + const plan = planOccurrences(backloggedJob(), NOW, { ...options, maxPerJob: 5 }); + + expect(plan.occurrences).toEqual([]); + expect(plan.skippedOccurrences).toBe(5); + // The clock still advances, so the next pass resumes further down the backlog. + expect(plan.nextRunAt).toEqual(secondsBefore(NOW, 70)); + }); + + it('records the catch-up run when the walk fills the cap but the backlog ends there', () => { + // Five instants and a cap of five, but nothing left in the window: the backlog + // ran out, so this pass does hold the newest missed instant. + const job = makeJob({ nextRunAt: secondsBefore(NOW, 40), misfireGraceSeconds: 20 }); + + const plan = planOccurrences(job, NOW, { ...options, maxPerJob: 5 }); + + expect(plan.occurrences).toEqual([NOW]); + expect(plan.catchUpAt).toEqual(NOW); + expect(plan.skippedOccurrences).toBe(4); + }); + + it('still fires a one-off that was missed, under coalesce', () => { + // The one-off's only instant is also its catch-up run, so coalesce keeps it. + const fireAt = secondsBefore(NOW, 3600); + const job = makeJob({ kind: 'one_off', intervalSeconds: null, fireAt, nextRunAt: fireAt }); + + const plan = planOccurrences(job, NOW, options); + + expect(plan.occurrences).toEqual([fireAt]); + expect(plan.nextRunAt).toBeNull(); + }); + + it('drops a missed one-off entirely under skip', () => { + const fireAt = secondsBefore(NOW, 3600); + const job = makeJob({ + ...skipPolicy, + kind: 'one_off', + intervalSeconds: null, + fireAt, + nextRunAt: fireAt, + }); + + const plan = planOccurrences(job, NOW, options); + + expect(plan.occurrences).toEqual([]); + expect(plan.nextRunAt).toBeNull(); + expect(plan.skippedOccurrences).toBe(1); + }); + + it('defers a job whose stored policy this version does not know', () => { + const job = makeJob({ + nextRunAt: secondsBefore(NOW, 120), + misfirePolicy: 'fire_all' as ScheduledJobMisfirePolicy, + }); + + // Throwing puts the job through the materializer's per-job defer path rather + // than silently reading an unknown policy as the most destructive one. + expect(() => planOccurrences(job, NOW, options)).toThrow(CorruptStorageRowError); + }); + + // Known gap, not yet fixed: `it.fails` so this turns red (telling us to remove + // the modifier) the day someone closes it, instead of silently staying green. + it.fails( + 'eventually fires a catch-up under coalesce when production outpaces the drain rate', + () => { + // A 1s-interval job outrunning a pass that can only drain 1000 occurrences + // (maxPerJob) per materialization cycle: 2000s of new backlog accrues between + // passes, so every pass's walk hits maxPerJob before reaching `now` + // (truncated) with nothing ahead, and coalesce keeps deferring instead of + // firing the newest missed instant. The job never reaches its backlog's tail, + // so it never runs again: silently, with no warning at any point. + const job = makeJob({ intervalSeconds: 1 }); + let now = NOW; + let nextRunAt = job.nextRunAt!; + let firedAtLeastOnce = false; + + for (let pass = 0; pass < 30; pass++) { + now = secondsAfter(now, 2000); + const plan = planOccurrences({ ...job, nextRunAt }, now, { + windowSeconds: 60, + maxPerJob: 1000, + defaultTimezone: 'UTC', + }); + if (plan.occurrences.length > 0) firedAtLeastOnce = true; + nextRunAt = plan.nextRunAt!; + } + + expect(firedAtLeastOnce).toBe(true); + }, + ); +}); diff --git a/packages/@n8n/scheduler/src/core/materializer/index.ts b/packages/@n8n/scheduler/src/core/materializer/index.ts index f6d02d858c3..f2b3a8b6ccc 100644 --- a/packages/@n8n/scheduler/src/core/materializer/index.ts +++ b/packages/@n8n/scheduler/src/core/materializer/index.ts @@ -1,8 +1,10 @@ export { materialize, + totalDiscarded, type MaterializerHooks, type MaterializerSummary, type MaterializerOptions, + type MisfireCount, type OnJobPlanError, } from './materialize'; export { DEFAULT_MATERIALIZER_OPTIONS } from './options'; diff --git a/packages/@n8n/scheduler/src/core/materializer/materialize.ts b/packages/@n8n/scheduler/src/core/materializer/materialize.ts index fa12b0036fa..683add63ef3 100644 --- a/packages/@n8n/scheduler/src/core/materializer/materialize.ts +++ b/packages/@n8n/scheduler/src/core/materializer/materialize.ts @@ -1,6 +1,7 @@ import { Time } from '@n8n/constants'; import type { ScheduledJob } from '../types'; +import { countMisfires, type MisfireCount } from './misfire'; import { DEFAULT_MATERIALIZER_OPTIONS, type MaterializerOptions } from './options'; import { planOccurrences } from './plan'; import type { @@ -9,9 +10,11 @@ import type { PlannedJob, RecordedOccurrence, RunInTransaction, + SupersededOccurrences, } from './transaction'; export type { MaterializerOptions } from './options'; +export type { MisfireCount } from './misfire'; export interface MaterializerSummary { /** How many due jobs this materialization claimed. */ @@ -26,6 +29,13 @@ export interface MaterializerSummary { created: RecordedOccurrence[]; /** How many claimed jobs could not be planned and were deferred (see {@link OnJobPlanError}). */ deferredJobs: number; + /** + * Due occurrences a misfire policy discarded rather than record, grouped by task + * type and policy. Groups with nothing discarded are left out. + */ + misfires: MisfireCount[]; + /** How many pending occurrences were retired because a catch-up run superseded them. */ + retiredOccurrences: number; } /** Notified when a claimed job's schedule cannot be planned, before it is deferred. */ @@ -50,7 +60,8 @@ export interface MaterializerHooks { * 1) claim the jobs whose next run is due * 2) turn each into its upcoming occurrences (pure: {@link planOccurrences}) * 3) record them all in one batch - * 4) advance every job's clock in one batch + * 4) retire any pending occurrences a catch-up run superseded + * 5) advance every job's clock in one batch * * All in a single transaction, judged against database time. The claim and the * per-batch advance are one query each; the insert is chunked by the repository @@ -64,10 +75,10 @@ export interface MaterializerHooks { * for all jobs. Deferring rather than dropping means a transient cause (a fixed * instance timezone, updated tzdata) heals on a later pass with no operator action. * - * Materialization only produces candidates. Every due occurrence is recorded, including - * a backlog accumulated during downtime (drained `maxPerJob` per pass, at their - * original past instants); whether a stale candidate still runs or is marked missed - * is the dispatcher's policy, not the materializer's. + * A backlog accumulated during downtime is drained `maxPerJob` per pass, at the + * occurrences' original past instants, minus whatever `planOccurrences` discards. The + * discarding commits with the job's clock advance, so no two instances disagree about + * what the backlog produced. * * Recording up to `windowSeconds` ahead means a frequent schedule doesn't need a * materialization per fire. The flip side: occurrences already materialized are not @@ -95,16 +106,25 @@ export async function materialize( const claimed = await tx.claimDueJobs(options.batchSize, lookaheadMs); signal?.throwIfAborted(); if (claimed === undefined) { - return { claimedJobs: 0, occurrences: 0, created: [], deferredJobs: 0 }; + return { + claimedJobs: 0, + occurrences: 0, + created: [], + deferredJobs: 0, + misfires: [], + retiredOccurrences: 0, + }; } const { occurrencesPlanned, numberOfJobsDeferred } = planOrDeferJobs( claimed, options, hooks.onPlanError, ); - const rows = toNewOccurrences(occurrencesPlanned); + const rows = toNewOccurrences(occurrencesPlanned, claimed.now); const { recorded, created } = await tx.recordOccurrences(rows); signal?.throwIfAborted(); + const retiredOccurrences = await tx.retireSuperseded(toSuperseded(occurrencesPlanned)); + signal?.throwIfAborted(); if (recorded < rows.length) { try { hooks.onSkippedDuplicates?.({ planned: rows.length, recorded }); @@ -119,20 +139,40 @@ export async function materialize( occurrences: recorded, created, deferredJobs: numberOfJobsDeferred, + misfires: countMisfires(occurrencesPlanned), + retiredOccurrences, }; }); } -function toNewOccurrences(planned: PlannedJob[]): NewOccurrence[] { +export function totalDiscarded(misfires: MisfireCount[]): number { + return misfires.reduce((total, { discarded }) => total + discarded, 0); +} + +/** Flattens every planned occurrence into the row the storage layer inserts. */ +function toNewOccurrences(planned: PlannedJob[], now: Date): NewOccurrence[] { return planned.flatMap(({ job, plan }) => - plan.occurrences.map((when) => ({ - jobId: job.id, - taskType: job.taskType, - payload: job.payload, - scheduledFor: when, - runAt: when, - maxAttempts: job.maxAttempts, - })), + plan.occurrences.map((when) => { + const runAt = when.getTime() === plan.catchUpAt?.getTime() ? now : when; + // When the row actually becomes claimable, which is never before `now`. + const visibleAt = Math.max(runAt.getTime(), now.getTime()); + return { + jobId: job.id, + taskType: job.taskType, + payload: job.payload, + scheduledFor: when, + runAt, + maxAttempts: job.maxAttempts, + missedAfter: new Date(visibleAt + job.misfireGraceSeconds * Time.seconds.toMilliseconds), + }; + }), + ); +} + +/** The jobs whose plan recorded a catch-up run, paired with that instant. */ +function toSuperseded(planned: PlannedJob[]): SupersededOccurrences[] { + return planned.flatMap(({ job, plan }) => + plan.catchUpAt === null ? [] : [{ jobId: job.id, before: plan.catchUpAt }], ); } @@ -184,7 +224,13 @@ function planOrDeferJob( return { plannedJob: { job, - plan: { occurrences: [], nextRunAt: retryAt, lastFiredAt: job.lastFiredAt }, + plan: { + occurrences: [], + skippedOccurrences: 0, + catchUpAt: null, + nextRunAt: retryAt, + lastFiredAt: job.lastFiredAt, + }, }, deferred: true, }; diff --git a/packages/@n8n/scheduler/src/core/materializer/misfire.ts b/packages/@n8n/scheduler/src/core/materializer/misfire.ts new file mode 100644 index 00000000000..e7469fec4a8 --- /dev/null +++ b/packages/@n8n/scheduler/src/core/materializer/misfire.ts @@ -0,0 +1,75 @@ +import { ScheduledJobMisfirePolicy, Time } from '@n8n/constants'; + +import { CorruptStorageRowError } from '../errors'; +import type { ScheduledJob } from '../types'; +import type { PlannedJob } from './transaction'; + +/** Occurrences one task type's jobs discarded under one misfire policy. */ +export interface MisfireCount { + taskType: string; + policy: ScheduledJobMisfirePolicy; + discarded: number; +} + +/** + * Drops the due instants a schedule's misfire policy says should no longer run. + * + * Nothing is dropped until an instant is past `scheduledFor + misfireGraceSeconds`. + * Once one is, the whole backlog goes, not only the past-deadline part of it, or the + * rest would fire back to back behind the catch-up run. + * + * `truncated` says the cap ended the walk, so the newest instant here is not the + * newest missed one; the catch-up run is deferred rather than recorded. + * + * Only ever sees instants the current pass is about to record for the first time. + * An instant already recorded by an earlier pass, that then goes unclaimed past its + * own deadline, doesn't come back through here: it's retired directly (see + * `ScheduledTaskRepository.retireMissedPending`), regardless of policy. + */ +export function applyMisfirePolicy( + due: Date[], + now: Date, + job: ScheduledJob, + truncated: boolean, +): { occurrences: Date[]; catchUpAt: Date | null } { + const graceDeadline = now.getTime() - job.misfireGraceSeconds * Time.seconds.toMilliseconds; + // Sorted ascending, so the oldest instant is the furthest past its deadline. + const behind = due.filter((occurrence) => occurrence.getTime() <= now.getTime()); + const ahead = due.slice(behind.length); + if (behind.length === 0 || behind[0].getTime() > graceDeadline) { + return { occurrences: due, catchUpAt: null }; + } + + switch (job.misfirePolicy) { + case ScheduledJobMisfirePolicy.Coalesce: { + if (truncated && ahead.length === 0) return { occurrences: [], catchUpAt: null }; + const catchUpAt = behind[behind.length - 1]; + return { occurrences: [catchUpAt, ...ahead], catchUpAt }; + } + case ScheduledJobMisfirePolicy.Skip: + return { occurrences: ahead, catchUpAt: null }; + default: { + // A policy this version does not know, e.g. a row written by a newer instance + // and read back after a rollback. + const exhaustive: never = job.misfirePolicy; + throw new CorruptStorageRowError( + `scheduled_job ${job.id} has unknown misfire policy '${String(exhaustive)}'`, + ); + } + } +} + +/** Groups a pass's discarded occurrences by the task type and policy that discarded them. */ +export function countMisfires(planned: PlannedJob[]): MisfireCount[] { + const grouped = planned + .filter(({ plan }) => plan.skippedOccurrences > 0) + .reduce((groups, { job, plan }) => { + const key = `${job.taskType}:${job.misfirePolicy}`; + return groups.set(key, { + taskType: job.taskType, + policy: job.misfirePolicy, + discarded: (groups.get(key)?.discarded ?? 0) + plan.skippedOccurrences, + }); + }, new Map()); + return [...grouped.values()]; +} diff --git a/packages/@n8n/scheduler/src/core/materializer/plan.ts b/packages/@n8n/scheduler/src/core/materializer/plan.ts index 870bf3eb7ef..38c1adb97ef 100644 --- a/packages/@n8n/scheduler/src/core/materializer/plan.ts +++ b/packages/@n8n/scheduler/src/core/materializer/plan.ts @@ -3,6 +3,7 @@ import { Time } from '@n8n/constants'; import { occurrencesFrom } from '../recurrence/next-run'; import { resolveSchedule } from '../recurrence/resolve'; import type { ScheduledJob } from '../types'; +import { applyMisfirePolicy } from './misfire'; /** * What a due job expands into for one materialization pass. @@ -14,6 +15,19 @@ export interface OccurrencePlan { */ occurrences: Date[]; + /** + * How many due instants the misfire policy discarded rather than record. + * The job's clock still advanced past them (see {@link nextRunAt}), so they + * are gone, not deferred. + */ + skippedOccurrences: number; + + /** + * The one occurrence a `coalesce` policy kept to stand in for a backlog, always the + * first of {@link occurrences}. `null` when no policy applied. + */ + catchUpAt: Date | null; + /** * The next instant not yet recorded, * or `null` once the schedule has no more fires. @@ -27,9 +41,6 @@ export interface OccurrencePlan { lastFiredAt: Date | null; } -/** - * @returns for a due job at time `now`, which occurrences to record and where its clock lands next. - */ export function planOccurrences( job: ScheduledJob, now: Date, @@ -41,24 +52,35 @@ export function planOccurrences( const windowEnd = now.getTime() + options.windowSeconds * Time.seconds.toMilliseconds; if (job.nextRunAt === null) { - return { occurrences: [], nextRunAt: null, lastFiredAt: job.lastFiredAt }; + return { + occurrences: [], + skippedOccurrences: 0, + catchUpAt: null, + nextRunAt: null, + lastFiredAt: job.lastFiredAt, + }; } - const occurrences: Date[] = []; + const due: Date[] = []; const fires = occurrencesFrom(schedule, job.nextRunAt); let fire = fires.next(); - while ( - !fire.done && - fire.value.getTime() <= windowEnd && - occurrences.length < options.maxPerJob - ) { - occurrences.push(fire.value); + while (!fire.done && fire.value.getTime() <= windowEnd && due.length < options.maxPerJob) { + due.push(fire.value); fire = fires.next(); } + // The cap, not the window, ended the walk: instants remain that this pass will not + // record. + const truncated = !fire.done && fire.value.getTime() <= windowEnd; + + const { occurrences, catchUpAt } = applyMisfirePolicy(due, now, job, truncated); return { occurrences, + skippedOccurrences: due.length - occurrences.length, + catchUpAt, nextRunAt: fire.done ? null : fire.value, - lastFiredAt: occurrences.at(-1) ?? job.lastFiredAt, + // Tracks every instant this pass consumed, not only the recorded ones: a + // discarded occurrence is still one the schedule has moved past. + lastFiredAt: due.at(-1) ?? job.lastFiredAt, }; } diff --git a/packages/@n8n/scheduler/src/core/materializer/transaction.ts b/packages/@n8n/scheduler/src/core/materializer/transaction.ts index 888eedc08f9..16d68bece72 100644 --- a/packages/@n8n/scheduler/src/core/materializer/transaction.ts +++ b/packages/@n8n/scheduler/src/core/materializer/transaction.ts @@ -13,6 +13,21 @@ export interface NewOccurrence { scheduledFor: Date; runAt: Date; maxAttempts: number; + /** + * When this occurrence stops being worth running, resolved here so the claim can + * compare one column against the clock instead of joining the schedule and + * recomputing the window per row. + */ + missedAfter: Date; +} + +/** + * A job's catch-up run and the instant it stands in for. + */ +export interface SupersededOccurrences { + jobId: number; + /** The catch-up run's instant; strictly older pending occurrences are superseded. */ + before: Date; } /** Identity of a row {@link MaterializerTransaction.recordOccurrences} just created. */ @@ -68,6 +83,14 @@ export interface MaterializerTransaction { */ recordOccurrences(occurrences: NewOccurrence[]): Promise; + /** + * Retire, as `missed`, every occurrence still `pending` at an instant strictly + * older than the catch-up run that supersedes it, for each job in one batch. + * + * Only `pending` rows: one already claimed is in-flight work someone owns. + */ + retireSuperseded(superseded: SupersededOccurrences[]): Promise; + /** * Advance every job's next-run and last-fired time in one batch. */ diff --git a/packages/@n8n/scheduler/src/core/reaper/__tests__/reap.test.ts b/packages/@n8n/scheduler/src/core/reaper/__tests__/reap.test.ts index a7144add61b..9057748e693 100644 --- a/packages/@n8n/scheduler/src/core/reaper/__tests__/reap.test.ts +++ b/packages/@n8n/scheduler/src/core/reaper/__tests__/reap.test.ts @@ -28,6 +28,7 @@ const setup = () => { store.reclaimExpired.mockResolvedValue(1); store.deadLetterExpired.mockResolvedValue(1); store.completeExpired.mockResolvedValue(1); + store.retireMissedPending.mockResolvedValue(0); const run = async () => await reap(store, { batchSize: 100 }, { onRowError, onDeadLetter, onCompletedAfterDispatch }); return { store, onRowError, onDeadLetter, onCompletedAfterDispatch, run }; @@ -40,11 +41,34 @@ describe('reap', () => { const result = await run(); - expect(result).toEqual({ reclaimed: 0, deadLettered: 0 }); + expect(result).toEqual({ reclaimed: 0, deadLettered: 0, missed: 0 }); expect(store.reclaimExpired).not.toHaveBeenCalled(); expect(store.deadLetterExpired).not.toHaveBeenCalled(); }); + it('retires the pending occurrences past their deadline, bounded by the batch size', async () => { + const { store, run } = setup(); + store.findExpiredLeases.mockResolvedValue([]); + store.retireMissedPending.mockResolvedValue(4); + + const result = await run(); + + expect(store.retireMissedPending).toHaveBeenCalledWith(100); + expect(result.missed).toBe(4); + }); + + it('retires stale occurrences even when there are leases to recover', async () => { + const { store, run } = setup(); + store.findExpiredLeases.mockResolvedValue([ + expiredTask({ attempts: 0, maxAttempts: 3, leaseEpoch: 1 }), + ]); + store.retireMissedPending.mockResolvedValue(2); + + const result = await run(); + + expect(result).toEqual({ reclaimed: 1, deadLettered: 0, missed: 2 }); + }); + it('reclaims a task with attempts left: backoff on the next attempt, epoch passed through', async () => { const { store, run } = setup(); const task = expiredTask({ attempts: 0, maxAttempts: 3, leaseEpoch: 4 }); @@ -59,7 +83,7 @@ describe('reap', () => { expect.any(String), ); expect(store.deadLetterExpired).not.toHaveBeenCalled(); - expect(result).toEqual({ reclaimed: 1, deadLettered: 0 }); + expect(result).toEqual({ reclaimed: 1, deadLettered: 0, missed: 0 }); }); it('uses the next attempt number for the backoff on a middle attempt', async () => { @@ -89,7 +113,7 @@ describe('reap', () => { expect.any(String), ); expect(store.reclaimExpired).not.toHaveBeenCalled(); - expect(result).toEqual({ reclaimed: 0, deadLettered: 1 }); + expect(result).toEqual({ reclaimed: 0, deadLettered: 1, missed: 0 }); // The terminal failure is reported with the lost attempt already counted. expect(onDeadLetter).toHaveBeenCalledWith({ taskId: task.id, attempts: 1, maxAttempts: 1 }); }); @@ -109,7 +133,7 @@ describe('reap', () => { ); expect(store.reclaimExpired).not.toHaveBeenCalled(); expect(store.completeExpired).not.toHaveBeenCalled(); - expect(result).toEqual({ reclaimed: 0, deadLettered: 1 }); + expect(result).toEqual({ reclaimed: 0, deadLettered: 1, missed: 0 }); expect(onDeadLetter).toHaveBeenCalledWith({ taskId: task.id, attempts: 1, maxAttempts: 1 }); }); @@ -133,7 +157,7 @@ describe('reap', () => { expect(onDeadLetter).not.toHaveBeenCalled(); expect(onCompletedAfterDispatch).toHaveBeenCalledWith({ taskId: task.id }); // A success: reported via the hook, not counted as dead-lettered. - expect(result).toEqual({ reclaimed: 0, deadLettered: 0 }); + expect(result).toEqual({ reclaimed: 0, deadLettered: 0, missed: 0 }); }); it('completes a post-dispatch expired lease even with attempts left, never reclaiming it', async () => { @@ -152,7 +176,7 @@ describe('reap', () => { }); expect(store.reclaimExpired).not.toHaveBeenCalled(); expect(onCompletedAfterDispatch).toHaveBeenCalledWith({ taskId: task.id }); - expect(result).toEqual({ reclaimed: 0, deadLettered: 0 }); + expect(result).toEqual({ reclaimed: 0, deadLettered: 0, missed: 0 }); }); it('dead-letters when the next attempt reaches maxAttempts', async () => { @@ -180,7 +204,7 @@ describe('reap', () => { const result = await run(); - expect(result).toEqual({ reclaimed: 2, deadLettered: 1 }); + expect(result).toEqual({ reclaimed: 2, deadLettered: 1, missed: 0 }); }); it('skips a row that throws and still processes the rest of the batch', async () => { @@ -196,7 +220,7 @@ describe('reap', () => { const result = await run(); - expect(result).toEqual({ reclaimed: 1, deadLettered: 0 }); + expect(result).toEqual({ reclaimed: 1, deadLettered: 0, missed: 0 }); expect(store.reclaimExpired).toHaveBeenCalledWith( { id: 'ok', claimedEpoch: 1 }, backoff(1), @@ -221,7 +245,7 @@ describe('reap', () => { const result = await run(); - expect(result).toEqual({ reclaimed: 1, deadLettered: 0 }); + expect(result).toEqual({ reclaimed: 1, deadLettered: 0, missed: 0 }); expect(onRowError).toHaveBeenCalledWith('poison', failure); }); @@ -233,7 +257,7 @@ describe('reap', () => { const result = await run(); - expect(result).toEqual({ reclaimed: 0, deadLettered: 0 }); + expect(result).toEqual({ reclaimed: 0, deadLettered: 0, missed: 0 }); }); it('does not report a dead-letter for a reclaim or a lost dead-letter race', async () => { @@ -247,7 +271,7 @@ describe('reap', () => { const result = await run(); - expect(result).toEqual({ reclaimed: 1, deadLettered: 0 }); + expect(result).toEqual({ reclaimed: 1, deadLettered: 0, missed: 0 }); expect(onDeadLetter).not.toHaveBeenCalled(); }); @@ -264,7 +288,7 @@ describe('reap', () => { const result = await run(); // The row was decided before the reporter ran: counted, and the sweep goes on. - expect(result).toEqual({ reclaimed: 1, deadLettered: 1 }); + expect(result).toEqual({ reclaimed: 1, deadLettered: 1, missed: 0 }); }); it('cancelled mid-sweep, it keeps what it recovered and leaves the rest to the next sweep', async () => { @@ -282,7 +306,7 @@ describe('reap', () => { const result = await reap(store, { batchSize: 100 }, {}, controller.signal); - expect(result).toEqual({ reclaimed: 1, deadLettered: 0 }); + expect(result).toEqual({ reclaimed: 1, deadLettered: 0, missed: 0 }); expect(store.reclaimExpired).toHaveBeenCalledTimes(1); }); @@ -294,8 +318,22 @@ describe('reap', () => { const result = await reap(store, { batchSize: 100 }, {}, controller.signal); - expect(result).toEqual({ reclaimed: 0, deadLettered: 0 }); + expect(result).toEqual({ reclaimed: 0, deadLettered: 0, missed: 0 }); expect(store.reclaimExpired).not.toHaveBeenCalled(); expect(store.deadLetterExpired).not.toHaveBeenCalled(); + expect(store.retireMissedPending).not.toHaveBeenCalled(); + }); + + it('recovers stranded rows even when retiring stale occurrences fails', async () => { + const store = mock(); + const onRetireError = vi.fn(); + store.reclaimExpired.mockResolvedValue(1); + store.retireMissedPending.mockRejectedValue(new Error('statement timeout')); + store.findExpiredLeases.mockResolvedValue([expiredTask()]); + + const result = await reap(store, { batchSize: 100 }, { onRetireError }); + + expect(result).toEqual({ reclaimed: 1, deadLettered: 0, missed: 0 }); + expect(onRetireError).toHaveBeenCalledWith(new Error('statement timeout')); }); }); diff --git a/packages/@n8n/scheduler/src/core/reaper/reap.ts b/packages/@n8n/scheduler/src/core/reaper/reap.ts index 2dc01c7ffb9..99d3d51ece7 100644 --- a/packages/@n8n/scheduler/src/core/reaper/reap.ts +++ b/packages/@n8n/scheduler/src/core/reaper/reap.ts @@ -10,6 +10,8 @@ export interface ReapResult { reclaimed: number; /** Tasks failed terminally (no attempts left). */ deadLettered: number; + /** Occurrences retired as `missed`: past their deadline, never claimed. */ + missed: number; } /** Identifies the row and the epoch read during the sweep, for a guarded reaper update. */ @@ -42,6 +44,11 @@ export interface ReaperTaskStore { * already happened, so it must not be recorded failed nor dispatched again. */ completeExpired(ref: ExpiredLeaseRef): Promise; + /** + * Retire up to `limit` `pending` occurrences past their `missedAfter` as `missed`. + * One statement rather than a row at a time: there is no per-row decision to make. + */ + retireMissedPending(limit: number): Promise; } /** Knobs of one reaper sweep. */ @@ -58,6 +65,8 @@ export const DEFAULT_REAPER_OPTIONS: ReaperOptions = { export interface ReaperHooks { /** Notified when recovering one expired-lease row fails, after the row is skipped. */ onRowError?: (taskId: string, error: unknown) => void; + /** Notified when retiring stale `pending` rows fails; the rest of the sweep still runs. */ + onRetireError?: (error: unknown) => void; /** Notified when a task is failed terminally: the lease of its last attempt expired. */ onDeadLetter?: (task: { taskId: string; attempts: number; maxAttempts: number }) => void; /** @@ -67,6 +76,30 @@ export interface ReaperHooks { onCompletedAfterDispatch?: (task: { taskId: string }) => void; } +/** + * Retires the stale `pending` rows for one sweep. Reports a failure through + * `hooks.onRetireError` rather than throwing, so the rest of the sweep still runs; + * returns 0 when aborted or when retiring failed. + */ +async function retireStale( + store: ReaperTaskStore, + options: ReaperOptions, + hooks: ReaperHooks, + signal?: AbortSignal, +): Promise { + if (signal?.aborted === true) return 0; + try { + return await store.retireMissedPending(options.batchSize); + } catch (error) { + try { + hooks.onRetireError?.(error); + } catch { + // A host-supplied reporter must not break the sweep it observes. + } + return 0; + } +} + /** * Recovers tasks stranded `running` by an instance that crashed or stalled past * its lease. Runs on every main; a driver (the multi-main loop) calls `reap` on a @@ -85,6 +118,9 @@ export interface ReaperHooks { * reapers on every main are safe. A row that throws is skipped (reported via * `hooks.onRowError`), not allowed to abort the rest of the pass. * + * A pass also retires up to `batchSize` `pending` occurrences past their + * `missedAfter`, so they reach a terminal status and fall to retention. + * * One pass resolves up to `batchSize` expired-lease tasks, splitting first on the * effect boundary: a task the owner already dispatched is completed as succeeded * (its effect happened, so it is never redelivered), whatever its attempts. A @@ -100,10 +136,12 @@ export async function reap( hooks: ReaperHooks = {}, signal?: AbortSignal, ): Promise { + const missed = await retireStale(store, options, hooks, signal); + const expired = await store.findExpiredLeases(options.batchSize); // Stryker disable next-line ConditionalExpression: pure early-return optimisation; // without it the loop below is simply empty for an empty array, same result. - if (expired.length === 0) return { reclaimed: 0, deadLettered: 0 }; + if (expired.length === 0) return { reclaimed: 0, deadLettered: 0, missed }; let reclaimed = 0; let deadLettered = 0; @@ -173,5 +211,5 @@ export async function reap( } } - return { reclaimed, deadLettered }; + return { reclaimed, deadLettered, missed }; } diff --git a/packages/@n8n/scheduler/src/core/recurrence/__tests__/resolve.test.ts b/packages/@n8n/scheduler/src/core/recurrence/__tests__/resolve.test.ts index 166d2e135b3..5917ba45b24 100644 --- a/packages/@n8n/scheduler/src/core/recurrence/__tests__/resolve.test.ts +++ b/packages/@n8n/scheduler/src/core/recurrence/__tests__/resolve.test.ts @@ -20,6 +20,8 @@ function makeJob(overrides: Partial = {}): ScheduledJob { nextRunAt: new Date('2026-01-01T00:00:00.000Z'), lastFiredAt: null, maxAttempts: 1, + misfirePolicy: 'coalesce', + misfireGraceSeconds: 60, ...overrides, }; } diff --git a/packages/@n8n/scheduler/src/core/types/schedule.ts b/packages/@n8n/scheduler/src/core/types/schedule.ts index 5c0083d30b5..fd6cfa34845 100644 --- a/packages/@n8n/scheduler/src/core/types/schedule.ts +++ b/packages/@n8n/scheduler/src/core/types/schedule.ts @@ -1,4 +1,8 @@ -import type { ScheduledJobKind, RecurringCronUnit } from '@n8n/constants'; +import type { + ScheduledJobKind, + RecurringCronUnit, + ScheduledJobMisfirePolicy, +} from '@n8n/constants'; import type { CronExpression } from 'n8n-workflow'; /** @@ -83,4 +87,8 @@ export interface ScheduledJob { nextRunAt: Date | null; // the next instant the materializer materializes from. lastFiredAt: Date | null; maxAttempts: number; + /** What to do with occurrences that came due while nothing ran them. */ + misfirePolicy: ScheduledJobMisfirePolicy; + /** How late an occurrence may be before {@link misfirePolicy} applies to it. */ + misfireGraceSeconds: number; } diff --git a/packages/@n8n/scheduler/src/core/types/task.ts b/packages/@n8n/scheduler/src/core/types/task.ts index 333c69efe4d..3d5cd55a895 100644 --- a/packages/@n8n/scheduler/src/core/types/task.ts +++ b/packages/@n8n/scheduler/src/core/types/task.ts @@ -10,8 +10,9 @@ * reclaims the row and another instance claims it, and a redelivery runs the * handler again. Duplicate *effects* are suppressed best-effort by the partial * unique index on `execution_entity.deduplicationKey` (see `ExecutionEntity` in - * `@n8n/db`). Handlers must therefore be idempotent per occurrence. Tightening the - * duplicate-suppression semantics is deferred to the misfire-policy work. + * `@n8n/db`). Handlers must therefore be idempotent per occurrence: the suppression + * cannot tell an execution inserted but never enqueued from one enqueued and not yet + * picked up, so a redelivery racing that window still runs the occurrence twice. * * Both types carry only the fields the core reads, named and typed as the * `scheduled_task` columns, so the storage row satisfies them structurally and @@ -25,8 +26,8 @@ import type { ScheduledTaskStatus } from '@n8n/constants'; * A materialised occurrence of a job (`scheduled_task`): one queued run. * * `scheduledFor` is the canonical UTC occurrence instant and the identity (unique - * on `(jobId, scheduledFor)`); `runAt` is the visibility time (equal to - * `scheduledFor` initially, pushed forward by retry backoff). + * on `(jobId, scheduledFor)`); `runAt` is the visibility time, which can differ + * from it, e.g. pushed forward by retry backoff. */ export interface ScheduledTask { id: string; diff --git a/packages/@n8n/scheduler/src/observability/attributes.ts b/packages/@n8n/scheduler/src/observability/attributes.ts index ebaab8480ea..649828478e6 100644 --- a/packages/@n8n/scheduler/src/observability/attributes.ts +++ b/packages/@n8n/scheduler/src/observability/attributes.ts @@ -21,6 +21,9 @@ export const SCHEDULER_ATTRIBUTES = { claimedJobs: 'n8n.scheduler.claimed_jobs', occurrences: 'n8n.scheduler.occurrences', deferredJobs: 'n8n.scheduler.deferred_jobs', + skippedOccurrences: 'n8n.scheduler.skipped_occurrences', + retiredOccurrences: 'n8n.scheduler.retired_occurrences', + missed: 'n8n.scheduler.missed', retentionDeleted: 'n8n.scheduler.retention_deleted', retentionDrained: 'n8n.scheduler.retention_drained', provisionInserted: 'n8n.scheduler.provision_inserted', diff --git a/packages/@n8n/scheduler/src/observability/metrics.ts b/packages/@n8n/scheduler/src/observability/metrics.ts index 4cb581e80a3..384497eeb69 100644 --- a/packages/@n8n/scheduler/src/observability/metrics.ts +++ b/packages/@n8n/scheduler/src/observability/metrics.ts @@ -1,3 +1,5 @@ +import type { MisfireCount } from '../core/materializer/materialize'; + /** * Minimal metrics port. The scheduler stays dependency-light: it depends on this * interface and records counts and timings through it, while the host adapts its @@ -16,8 +18,12 @@ export interface SchedulerMetrics { /** Outcome of one materialization pass. */ recordMaterialized(occurrences: number, deferredJobs: number): void; + /** Occurrences a misfire policy discarded before recording them. */ + recordMisfired(discarded: MisfireCount[]): void; + /** Already-recorded occurrences retired because a catch-up run superseded them. */ + recordRetired(retired: number): void; /** Outcome of one reaper sweep. */ - recordReaped(reclaimed: number, deadLettered: number): void; + recordReaped(reclaimed: number, deadLettered: number, missed: number): void; /** * A task became a dead-letter on the executor's terminal-failure path (attempts * exhausted). Complements the reaper's `recordReaped`, which counts the ones it @@ -35,6 +41,8 @@ export const noopMetrics: SchedulerMetrics = { recordRetry() {}, observeDispatchLagSeconds() {}, recordMaterialized() {}, + recordMisfired() {}, + recordRetired() {}, recordReaped() {}, recordDeadLettered() {}, recordPruned() {}, diff --git a/packages/@n8n/utils/src/json/json-size-exceeds.test.ts b/packages/@n8n/utils/src/json/json-size-exceeds.test.ts new file mode 100644 index 00000000000..ec687249d5c --- /dev/null +++ b/packages/@n8n/utils/src/json/json-size-exceeds.test.ts @@ -0,0 +1,403 @@ +import { describe, expect, it } from 'vitest'; + +import { jsonSizeExceeds } from './json-size-exceeds'; + +const ONE_MIB = 1024 * 1024; + +/** Size the estimator credits a value with, read off the limits it answers on. */ +const measuredSize = (value: unknown) => { + let exceeded = -1; + let within = 1; + while (jsonSizeExceeds(value, within)) { + within *= 2; + } + while (within - exceeded > 1) { + const middle = Math.floor((exceeded + within) / 2); + if (jsonSizeExceeds(value, middle)) { + exceeded = middle; + } else { + within = middle; + } + } + + return within; +}; + +/** Size the value actually serializes to. Zero when it serializes to nothing. */ +const realSize = (value: unknown) => { + const serialized = JSON.stringify(value); + return serialized === undefined ? 0 : Buffer.byteLength(serialized); +}; + +/** Deterministic generator, so that a failing value can be reproduced. */ +const seeded = (seed: number) => () => { + seed = (seed * 1103515245 + 12345) % 2147483648; + return seed / 2147483648; +}; + +const pick = (random: () => number, options: T[]) => + options[Math.floor(random() * options.length)]; + +const AWKWARD_STRINGS = [ + '', + 'plain text', + 'a"quoted"b', + 'back\\slash', + 'line\nbreak', + '\u0001\u001f', + '\b\t\r\f', + 'é accents', + '蟹 three bytes', + '🙂 pair', + '\ud800', + '\udfff', + '\ud83d🙂', + 'k'.repeat(40), +]; + +/** + * A value serialization replaces with a fixed result. The replacement is captured + * rather than computed on call, so that the value has one serialization. + */ +const selfSerializing = (replacement: unknown) => ({ toJSON: () => replacement }); + +const randomLeaf = (random: () => number): unknown => + pick(random, [ + pick(random, AWKWARD_STRINGS), + Math.floor(random() * 1e9), + -Math.floor(random() * 1e9), + random(), + random() * 10 ** Math.floor(random() * 40 - 20), + pick(random, [0, -0, NaN, Infinity, -Infinity, 1e21, 5e-324, Number.MAX_SAFE_INTEGER]), + pick(random, [true, false, null, undefined]), + () => 'dropped', + new Date(Math.floor(random() * 1e12)), + new Date(NaN), + Buffer.from([0, 7, 128, 255].slice(0, 1 + Math.floor(random() * 4))), + new Uint8Array([1, 2, 255]), + new Float64Array([1.5, -1e-7]), + selfSerializing(pick(random, AWKWARD_STRINGS)), + selfSerializing({ replaced: [1, 2] }), + selfSerializing(undefined), + ]); + +const randomValue = (random: () => number, depth: number): unknown => { + if (depth === 0) { + return randomLeaf(random); + } + + const length = Math.floor(random() * 5); + const members = Array.from({ length }, () => randomValue(random, depth - 1)); + + return random() < 0.5 + ? members + : Object.fromEntries( + members.map((member, index) => [pick(random, AWKWARD_STRINGS) + index, member]), + ); +}; + +describe('jsonSizeExceeds', () => { + describe('never reports a size below the real one', () => { + it.each<[string, unknown]>([ + ['a string', 'hello'], + ['an empty string', ''], + ['a multi-byte string', 'pour sûr 😀'], + ['a multi-byte key', { ['clé 蟹']: 'valeur' }], + ['a string needing escapes', 'a"b\\c\nd'], + ['a string of quotes, which serialization doubles', '"'.repeat(50)], + ['a string of control characters, which serialization sextuples', '\u0001\u0002\u001f'], + ['a string of letter-escaped controls', '\b\t\n\f\r'], + ['a key needing escapes', { ['a"b\\c\n']: 1 }], + ['a lone high surrogate', '\ud800'], + ['a lone low surrogate', '\udfff'], + ['a surrogate pair', '🙂'], + ['a pair preceded by a lone surrogate', '\ud83d🙂'], + ['a pair split across strings', ['\ud83d', '\ude42']], + ['integers', [0, -0, 1, -1, 9, 10, 99, 100, 999999, 1000000]], + ['integers at every digit count', Array.from({ length: 21 }, (_, i) => 10 ** i)], + ['negative integers at every digit count', Array.from({ length: 21 }, (_, i) => -(10 ** i))], + ['integers just below powers of ten', [9, 99, 999, 999999999999999]], + ['extreme magnitudes', [1e20, 1e21, 1e22, 1e-7, 5e-324, Number.MAX_SAFE_INTEGER]], + ['the longest a number can serialize to', -0.0000075911789601505095], + ['non-finite numbers', [NaN, Infinity, -Infinity]], + ['floats', [0.1, -0.5, 1.5, -1.25e-300, 1.2345678901234567]], + ['keywords', [true, false, null]], + ['an empty object', {}], + ['an empty array', []], + ['nested containers', { a: {}, b: [], c: { d: [1, { e: 'f' }] }, g: [[], [[]]] }], + ['entries serialization drops', { kept: 1, gone: undefined, alsoGone: () => 0 }], + ['an element serialization turns into null', [1, undefined, 2]], + ['a nested Buffer', { file: Buffer.from([0, 9, 99, 255]) }], + ['an empty Buffer', { file: Buffer.alloc(0) }], + ['a Date', { at: new Date('2026-07-27T10:00:00.000Z') }], + ['an invalid Date, which serializes as null', { at: new Date(NaN) }], + ['a value serializing itself', { at: { toJSON: () => 'replaced' } }], + ['a value serializing itself into a container', { at: { toJSON: () => ({ a: [1, 2] }) } }], + ['a value serializing itself into nothing', { gone: { toJSON: () => undefined }, kept: 1 }], + ['a typed array, which serializes as index entries', { view: new Uint8Array([1, 255]) }], + ['a typed array of floats', { view: new Float64Array([1.5, -1e-7]) }], + ['an empty typed array', { view: new Uint8Array() }], + [ + 'a DataView, which serializes as an empty object', + { view: new DataView(new ArrayBuffer(4)) }, + ], + ['long keys', { ['k'.repeat(500)]: 'v'.repeat(500) }], + ['many entries', Object.fromEntries(Array.from({ length: 200 }, (_, i) => [`k${i}`, i]))], + ['many elements', Array.from({ length: 200 }, (_, i) => i * 7919)], + ])('holds for %s', (_label, value) => { + expect(measuredSize(value)).toBeGreaterThanOrEqual(realSize(value)); + }); + + it('holds for a container reached twice, which serialization repeats', () => { + const shared = { s: 'x', n: [1, 2] }; + const value = { a: shared, b: shared }; + + expect(measuredSize(value)).toBeGreaterThanOrEqual(realSize(value)); + }); + + it('holds for a sparse array, whose holes serialize as null', () => { + const sparse = new Array(4); + sparse[0] = 1; + sparse[3] = 2; + + expect(measuredSize(sparse)).toBeGreaterThanOrEqual(realSize(sparse)); + }); + + it('holds for generated values mixing every kind of member', () => { + const random = seeded(20260730); + const undersized: unknown[] = []; + + for (let count = 0; count < 400; count++) { + const value = randomValue(random, 4); + const real = realSize(value); + const undersizedHere = + measuredSize(value) < real || (real > 0 && !jsonSizeExceeds(value, real - 1)); + + if (undersizedHere) { + undersized.push(value); + } + } + + expect(undersized).toEqual([]); + }); + }); + + describe('overshoots by one byte per container with members', () => { + it.each<[string, unknown, number]>([ + ['a string root', 'hello', 0], + ['a number root', 42, 0], + ['an empty object', {}, 0], + ['an object whose only entry is dropped', { gone: undefined }, 0], + ['a flat object', { a: 1, b: 'two', c: true, d: null }, 1], + ['a flat array', [1, 'two', false, null], 1], + ['nested objects', { a: { b: { c: 1 } } }, 3], + ['an array of objects', [{ a: 1 }, { b: 2 }], 3], + ['escaped keys and values', { ['a"b']: 'c\nd\u0001é蟹🙂' }, 1], + ['a Date', { at: new Date('2026-07-27T10:00:00.000Z') }, 1], + ['a value serializing itself', { at: { toJSON: () => 'replaced' } }, 1], + ['integers of every length', { small: 7, big: 1234567890, negative: -42 }, 1], + ['non-integer numbers', { price: 1.5, ratio: -0.25, tiny: 5e-324 }, 1], + ['a number in exponential notation', { big: 1e21 }, 1], + ])('holds for %s', (_label, value, containers) => { + expect(measuredSize(value)).toBe(realSize(value) + containers); + }); + }); + + describe('reports an oversized value', () => { + it.each<[string, () => unknown]>([ + ['a long string', () => 'x'.repeat(3 * ONE_MIB)], + ['a string whose characters take three bytes each', () => '蟹'.repeat(ONE_MIB)], + ['a key whose characters take three bytes each', () => ({ ['蟹'.repeat(ONE_MIB)]: 0 })], + ['a string in an object', () => ({ blob: 'x'.repeat(3 * ONE_MIB) })], + ['a string whose escapes double its size', () => ({ blob: '"'.repeat(1.5 * ONE_MIB + 1) })], + [ + 'a string whose escapes sextuple its size', + () => ({ blob: '\u0001'.repeat(0.5 * ONE_MIB) }), + ], + ['a string of lone surrogates', () => ({ blob: '\ud800'.repeat(0.5 * ONE_MIB) })], + [ + 'a string split across keys', + () => ({ a: 'x'.repeat(ONE_MIB), b: 'y'.repeat(2 * ONE_MIB) }), + ], + ['a string nested in an array', () => ({ items: [{ blob: 'x'.repeat(3 * ONE_MIB) }] })], + ['an array of nulls', () => ({ items: new Array(600_000).fill(null) })], + ['an array of booleans', () => ({ items: new Array(500_000).fill(true) })], + ['an array of integers', () => Array.from({ length: 400_000 }, (_, i) => 1_000_000 + i)], + ['an array of empty objects', () => new Array(800_000).fill(0).map(() => ({}))], + ['a Buffer', () => ({ file: Buffer.alloc(3 * ONE_MIB) })], + [ + 'a Buffer whose bytes serialize as two characters each', + () => ({ file: Buffer.alloc(1.5 * ONE_MIB) }), + ], + ['a typed array, whose index keys are serialized', () => ({ view: new Uint8Array(ONE_MIB) })], + [ + 'a value serializing itself into a long string', + () => ({ at: { toJSON: () => 'x'.repeat(3 * ONE_MIB) } }), + ], + [ + 'many keys', + () => Object.fromEntries(Array.from({ length: 300_000 }, (_, i) => [`k${i}`, 0])), + ], + ])('reports %s', (_label, build) => { + const value = build(); + + expect(realSize(value)).toBeGreaterThan(2 * ONE_MIB); + expect(jsonSizeExceeds(value, 2 * ONE_MIB)).toBe(true); + }); + }); + + describe('reports a value within the limit', () => { + it.each<[string, () => unknown]>([ + ['a short string', () => 'hello'], + ['a long-but-allowed string', () => 'x'.repeat(ONE_MIB)], + ['a string of emoji', () => ({ blob: '🙂'.repeat(0.4 * ONE_MIB) })], + ['a nested string', () => ({ outer: { inner: ['x'.repeat(ONE_MIB)] } })], + ['null', () => null], + ['a number', () => 42], + ['an empty object', () => ({})], + ['a small Buffer', () => ({ file: Buffer.alloc(0.4 * ONE_MIB) })], + ['a Date', () => ({ at: new Date() })], + [ + 'many small entries', + () => Object.fromEntries(Array.from({ length: 20_000 }, (_, i) => [`k${i}`, i])), + ], + ])('accepts %s', (_label, build) => { + const value = build(); + + expect(realSize(value)).toBeLessThanOrEqual(2 * ONE_MIB); + expect(jsonSizeExceeds(value, 2 * ONE_MIB)).toBe(false); + }); + }); + + it('measures every occurrence of a container reached twice', () => { + const shared = { blob: 'x'.repeat(1.5 * ONE_MIB) }; + + expect(jsonSizeExceeds({ a: shared, b: shared }, 2 * ONE_MIB)).toBe(true); + expect(realSize({ a: shared, b: shared })).toBeGreaterThan(2 * ONE_MIB); + }); + + it('answers on a structure whose paths outnumber its containers', () => { + let node: Record = { leaf: 'x'.repeat(1000) }; + for (let depth = 0; depth < 40; depth++) { + node = { a: node, b: node }; + } + + expect(jsonSizeExceeds(node, 2 * ONE_MIB)).toBe(true); + }); + + it('answers on a value nested deeper than serialization can go', () => { + const root: Record = {}; + let leaf = root; + for (let depth = 0; depth < 100_000; depth++) { + const next: Record = {}; + leaf.n = next; + leaf = next; + } + + expect(jsonSizeExceeds(root, 2 * ONE_MIB)).toBe(false); + expect(() => JSON.stringify(root)).toThrow(RangeError); + }); + + it('answers on a cyclic value instead of throwing', () => { + const cyclic: Record = { name: 'cycle' }; + cyclic.self = cyclic; + + expect(jsonSizeExceeds(cyclic, 2 * ONE_MIB)).toBe(false); + expect(() => JSON.stringify(cyclic)).toThrow(); + }); + + it('ignores inherited properties, which are not serialized', () => { + const prototype = { inherited: 'x'.repeat(3 * ONE_MIB) }; + const value = Object.create(prototype) as Record; + value.own = 'small'; + + expect(jsonSizeExceeds(value, 2 * ONE_MIB)).toBe(false); + }); + + it('reads members in the order serialization reads them', () => { + const build = (read: string[]) => ({ + get first() { + read.push('first'); + return { + get nested() { + read.push('nested'); + return 'x'; + }, + }; + }, + get second() { + read.push('second'); + return [ + { + get deep() { + read.push('deep'); + return 1; + }, + }, + ]; + }, + get third() { + read.push('third'); + return true; + }, + }); + + const walked: string[] = []; + const serialized: string[] = []; + jsonSizeExceeds(build(walked), Number.MAX_SAFE_INTEGER); + JSON.stringify(build(serialized)); + + expect(walked).toEqual(serialized); + expect(walked).toEqual(['first', 'nested', 'second', 'deep', 'third']); + }); + + it('stops walking once the limit is crossed', () => { + const visited: string[] = []; + const value = { + blob: 'x'.repeat(3 * ONE_MIB), + untouched: { + get deep() { + visited.push('deep'); + return 'y'; + }, + }, + }; + + expect(jsonSizeExceeds(value, 2 * ONE_MIB)).toBe(true); + expect(visited).toEqual([]); + }); + + it('stops walking a single wide container once the limit is crossed', () => { + let read = 0; + const elements = new Proxy(new Array(10_000_000).fill('x'.repeat(1000)), { + get(target, property, receiver) { + if (typeof property === 'string' && /^\d+$/.test(property)) { + read += 1; + } + return Reflect.get(target, property, receiver) as unknown; + }, + }); + + expect(jsonSizeExceeds({ elements }, 2 * ONE_MIB)).toBe(true); + expect(read).toBeLessThan(10_000); + }); + + it('stops measuring a single oversized string once the limit is crossed', () => { + const value = { blob: 'x'.repeat(64 * ONE_MIB) }; + + expect(jsonSizeExceeds(value, ONE_MIB)).toBe(true); + }); + + it('reports any non-empty value against a zero limit', () => { + expect(jsonSizeExceeds({ a: 1 }, 0)).toBe(true); + expect(jsonSizeExceeds([null], 0)).toBe(true); + expect(jsonSizeExceeds('', 0)).toBe(true); + expect(jsonSizeExceeds({}, 0)).toBe(true); // the root's own delimiters count + expect(jsonSizeExceeds({}, 2)).toBe(false); + }); + + it('reports every value against a negative limit', () => { + expect(jsonSizeExceeds({}, -1)).toBe(true); + expect(jsonSizeExceeds([], -1)).toBe(true); + }); +}); diff --git a/packages/@n8n/utils/src/json/json-size-exceeds.ts b/packages/@n8n/utils/src/json/json-size-exceeds.ts new file mode 100644 index 00000000000..3795158dea9 --- /dev/null +++ b/packages/@n8n/utils/src/json/json-size-exceeds.ts @@ -0,0 +1,337 @@ +type JsonContainer = Record | unknown[]; + +/** A value serialization replaces with the result of its own `toJSON`. */ +type SelfSerializing = JsonContainer & { toJSON: (key: string) => unknown }; + +/** A view over binary data, serialized as one entry per element. */ +type IndexedView = ArrayBufferView & { length: number }; + +/** An array being measured, and how far through its elements the walk is. */ +type ElementsFrame = { readonly elements: unknown[]; index: number }; + +/** An object being measured, and how far through its keys the walk is. */ +type EntriesFrame = { + readonly entries: Record; + readonly keys: string[]; + index: number; +}; + +type Frame = ElementsFrame | EntriesFrame; + +/** Bytes counted so far, and the containers the walk still has to finish. */ +type Walk = { + readonly maxBytes: number; + readonly frames: Frame[]; + readonly ancestors: Set; + size: number; +}; + +const QUOTES_SIZE = 2; // `""` around a string or a key +const COLON_SIZE = 1; +const COMMA_SIZE = 1; +const EMPTY_CONTAINER_SIZE = 2; // `{}` or `[]` +const NULL_SIZE = 4; +const TRUE_SIZE = 4; +const FALSE_SIZE = 5; +const SIGN_SIZE = 1; + +/** Magnitude from which a number serializes in exponential notation. */ +const EXPONENTIAL_NOTATION_THRESHOLD = 1e21; + +/** `{"type":"Buffer","data":[]}` around the bytes of a Buffer. */ +const BUFFER_ENVELOPE_SIZE = 27; + +/** Longest a byte serializes to inside that envelope, as in `255,`. */ +const MAX_BUFFER_BYTE_SIZE = 4; + +/** + * Widest a number can serialize to, as in `-0.0000075911789601505095`. Bounds the + * elements of a binary view, which are counted without being visited. + */ +const MAX_NUMBER_SIZE = 25; + +const SHORT_ESCAPE_SIZE = 2; // `\n`, `\"`, `\\` +const UNICODE_ESCAPE_SIZE = 6; // `\u001f`, and a lone surrogate +const ONE_BYTE_SIZE = 1; +const TWO_BYTE_SIZE = 2; +const THREE_BYTE_SIZE = 3; +const SURROGATE_PAIR_SIZE = 4; // one code point spread over two code units + +const CONTROL_MAX = 0x1f; +const QUOTE = 0x22; +const BACKSLASH = 0x5c; +const ASCII_MAX = 0x7f; +const TWO_BYTE_MAX = 0x7ff; +const HIGH_SURROGATE_MIN = 0xd800; +const HIGH_SURROGATE_MAX = 0xdbff; +const LOW_SURROGATE_MIN = 0xdc00; +const LOW_SURROGATE_MAX = 0xdfff; + +/** Control characters serialization escapes with a letter instead of a code point. */ +const LETTER_ESCAPED_CONTROLS = new Set([0x08, 0x09, 0x0a, 0x0c, 0x0d]); + +/** Key `JSON.stringify` hands to the `toJSON` of the value it is called on. */ +const ROOT_KEY = ''; + +/** + * Tells whether a value exceeds a JSON size limit, without serializing it. + * + * The measure is an upper bound, so a value is never reported as fitting a size + * it does not fit. It overshoots by one byte per non-empty container, and counts + * binary data at the widest its bytes can serialize to. + * + * @param value Value to measure as if it were passed to `JSON.stringify`. + * @param maxBytes Limit the serialization must stay within. + * @returns `true` unless the serialization is certainly `maxBytes` or shorter. + * + * @remarks Time O(n) in the members and characters of `value`, memory O(depth). + * Calls `toJSON` on the members defining one, as serialization would. + */ +export function jsonSizeExceeds(value: unknown, maxBytes: number): boolean { + const walk: Walk = { maxBytes, frames: [], ancestors: new Set(), size: 0 }; + + addValue(walk, replacedValue(value, ROOT_KEY)); + while (walk.frames.length > 0 && walk.size <= maxBytes) { + advance(walk, walk.frames[walk.frames.length - 1]); + } + + return walk.size > maxBytes; +} + +/** Bytes left before the limit. Negative once the limit is crossed. */ +function remaining(walk: Walk): number { + return walk.maxBytes - walk.size; +} + +/** Measures the next member of the innermost container, or closes it. */ +function advance(walk: Walk, frame: Frame): void { + if ('elements' in frame) { + advanceElements(walk, frame); + } else { + advanceEntries(walk, frame); + } +} + +function advanceElements(walk: Walk, frame: ElementsFrame): void { + if (frame.index === frame.elements.length) { + close(walk, frame.elements); + } else { + const index = frame.index; + frame.index += 1; + walk.size += COMMA_SIZE; + addValue(walk, replacedValue(frame.elements[index], index)); + } +} + +function advanceEntries(walk: Walk, frame: EntriesFrame): void { + if (frame.index === frame.keys.length) { + close(walk, frame.entries); + } else { + const key = frame.keys[frame.index]; + frame.index += 1; + addEntry(walk, key, replacedValue(frame.entries[key], key)); + } +} + +/** Adds an entry, unless serialization drops it along with its key. */ +function addEntry(walk: Walk, key: string, value: unknown): void { + if (!isDroppedFromObjects(value)) { + walk.size += COMMA_SIZE + COLON_SIZE + stringSize(key, remaining(walk)); + addValue(walk, value); + } +} + +/** + * Adds what a value occupies on its own, and opens it when it has members. + * Measuring stops once the limit is crossed, so a size cut short is still above it. + */ +function addValue(walk: Walk, value: unknown): void { + if (isContainer(value)) { + open(walk, value); + } else { + walk.size += leafSize(value, remaining(walk)); + } +} + +/** + * Adds a container's own delimiters and queues its members, or the whole of it + * when its size follows from its length alone. + */ +function open(walk: Walk, container: JsonContainer): void { + // A container reached from inside itself makes serialization fail, so it has + // no size to answer with, and walking into it again would not end. + if (!walk.ancestors.has(container)) { + const binarySize = maxBinaryViewSize(container); + + if (binarySize === undefined) { + walk.size += EMPTY_CONTAINER_SIZE; + walk.ancestors.add(container); + walk.frames.push(frameFor(container)); + } else { + walk.size += binarySize; + } + } +} + +function close(walk: Walk, container: JsonContainer): void { + walk.ancestors.delete(container); + walk.frames.pop(); +} + +function frameFor(container: JsonContainer): Frame { + return Array.isArray(container) + ? { elements: container, index: 0 } + : { entries: container, keys: Object.keys(container), index: 0 }; +} + +/** The value serialization puts in place of this one, given the key holding it. */ +function replacedValue(value: unknown, key: string | number): unknown { + return isSelfSerializing(value) ? value.toJSON(String(key)) : value; +} + +function isSelfSerializing(value: unknown): value is SelfSerializing { + return ( + isContainer(value) && + // A Buffer would hand over an array of one number per byte, which its own + // measure derives from its length instead. + !Buffer.isBuffer(value) && + 'toJSON' in value && + typeof value.toJSON === 'function' + ); +} + +/** + * Bytes a Buffer or another binary view occupies serialized, or `undefined` for + * a container whose members have to be walked. + */ +function maxBinaryViewSize(container: JsonContainer): number | undefined { + if (Buffer.isBuffer(container)) { + return BUFFER_ENVELOPE_SIZE + MAX_BUFFER_BYTE_SIZE * container.length; + } + + return isIndexedView(container) ? maxIndexedViewSize(container) : undefined; +} + +/** + * Bytes an indexed view occupies as the object of index/element entries it + * serializes to. Derived from its length, because listing those keys would hold + * one string per element in memory. + */ +function maxIndexedViewSize(view: IndexedView): number { + const lastIndex = Math.max(view.length - 1, 0); + const maxEntrySize = + QUOTES_SIZE + decimalDigits(lastIndex) + COLON_SIZE + MAX_NUMBER_SIZE + COMMA_SIZE; + + return EMPTY_CONTAINER_SIZE + view.length * maxEntrySize; +} + +/** Bytes a value with no members occupies serialized. */ +function leafSize(value: unknown, budget: number): number { + switch (typeof value) { + case 'string': + return stringSize(value, budget); + case 'number': + return numberSize(value); + case 'boolean': + return value ? TRUE_SIZE : FALSE_SIZE; + default: + return NULL_SIZE; + } +} + +/** Bytes a string occupies serialized, escapes and quotes included. */ +function stringSize(value: string, budget: number): number { + return QUOTES_SIZE + escapedContentSize(value, budget - QUOTES_SIZE); +} + +/** + * Bytes the escaped characters of a string occupy, quotes excluded. Reads the + * string one code unit at a time so that nothing is copied, and stops once + * `budget` is gone, since what is already counted then settles the answer. + */ +function escapedContentSize(value: string, budget: number): number { + let size = 0; + let index = 0; + + while (index < value.length && size <= budget) { + const code = value.charCodeAt(index); + const paired = isHighSurrogate(code) && isLowSurrogate(value.charCodeAt(index + 1)); + + size += paired ? SURROGATE_PAIR_SIZE : codeUnitSize(code); + index += paired ? 2 : 1; + } + + return size; +} + +/** Bytes a single code unit occupies, escaped and encoded as serialization would. */ +function codeUnitSize(code: number): number { + if (code === QUOTE || code === BACKSLASH) { + return SHORT_ESCAPE_SIZE; + } + + if (code <= CONTROL_MAX) { + return LETTER_ESCAPED_CONTROLS.has(code) ? SHORT_ESCAPE_SIZE : UNICODE_ESCAPE_SIZE; + } + + if (code <= ASCII_MAX) { + return ONE_BYTE_SIZE; + } + + if (code <= TWO_BYTE_MAX) { + return TWO_BYTE_SIZE; + } + + // A surrogate left without its other half, which serialization escapes. + return isSurrogate(code) ? UNICODE_ESCAPE_SIZE : THREE_BYTE_SIZE; +} + +/** Bytes a number occupies serialized. */ +function numberSize(value: number): number { + if (!Number.isFinite(value)) { + return NULL_SIZE; + } + + const magnitude = Math.abs(value); + const isPlainInteger = Number.isInteger(value) && magnitude < EXPONENTIAL_NOTATION_THRESHOLD; + + // A plain integer has a length its magnitude gives away. Any other number has + // to be formatted to be measured, and nothing shorter would be exact. + return isPlainInteger + ? (value < 0 ? SIGN_SIZE : 0) + decimalDigits(magnitude) + : String(value).length; +} + +/** Digits the integer part of a magnitude is written with. */ +function decimalDigits(magnitude: number): number { + const digits = magnitude < 1 ? 1 : Math.floor(Math.log10(magnitude)) + 1; + + // A rounding error in log10 costs a digit on some exact powers of ten. + return magnitude < 10 ** digits ? digits : digits + 1; +} + +/** Whether serializing an object drops the entry holding this value, key included. */ +function isDroppedFromObjects(value: unknown): boolean { + const type = typeof value; + return type === 'undefined' || type === 'function' || type === 'symbol'; +} + +function isContainer(value: unknown): value is JsonContainer { + return typeof value === 'object' && value !== null; +} + +function isIndexedView(value: object): value is IndexedView { + return ArrayBuffer.isView(value) && 'length' in value && typeof value.length === 'number'; +} + +function isHighSurrogate(code: number): boolean { + return code >= HIGH_SURROGATE_MIN && code <= HIGH_SURROGATE_MAX; +} + +function isLowSurrogate(code: number): boolean { + return code >= LOW_SURROGATE_MIN && code <= LOW_SURROGATE_MAX; +} + +function isSurrogate(code: number): boolean { + return code >= HIGH_SURROGATE_MIN && code <= LOW_SURROGATE_MAX; +} diff --git a/packages/@n8n/utils/src/sleep.test.ts b/packages/@n8n/utils/src/sleep.test.ts new file mode 100644 index 00000000000..95da1bab86c --- /dev/null +++ b/packages/@n8n/utils/src/sleep.test.ts @@ -0,0 +1,68 @@ +import { sleep } from './sleep'; + +describe('sleep', () => { + beforeEach(() => { + vi.useFakeTimers(); + }); + + afterEach(() => { + vi.useRealTimers(); + }); + + it('should resolve after the specified time when no abort signal is given', async () => { + const onResolve = vi.fn(); + const sleepPromise = sleep(100).then(onResolve); + + await vi.advanceTimersByTimeAsync(99); + expect(onResolve).not.toHaveBeenCalled(); + + await vi.advanceTimersByTimeAsync(1); + await sleepPromise; + expect(onResolve).toHaveBeenCalled(); + }); + + it('should work without abort signal', async () => { + const onResolve = vi.fn(); + const sleepPromise = sleep(100, undefined).then(onResolve); + + await vi.advanceTimersByTimeAsync(100); + await sleepPromise; + expect(onResolve).toHaveBeenCalled(); + }); + + it('should reject immediately if abort signal is already aborted', async () => { + const abortController = new AbortController(); + abortController.abort(); + + await expect(sleep(1000, abortController.signal)).rejects.toThrow('Aborted'); + }); + + it('should reject when abort signal is triggered during sleep', async () => { + const abortController = new AbortController(); + const onSettled = vi.fn(); + + const promise = sleep(1000, abortController.signal).catch(onSettled); + + await vi.advanceTimersByTimeAsync(50); + abortController.abort(); + await promise; + + expect(onSettled).toHaveBeenCalledWith(expect.objectContaining({ message: 'Aborted' })); + }); + + it('should clean up timeout when aborted during sleep', async () => { + const abortController = new AbortController(); + const clearTimeoutSpy = vi.spyOn(global, 'clearTimeout'); + + const sleepPromise = sleep(1000, abortController.signal); + + await vi.advanceTimersByTimeAsync(50); + abortController.abort(); + + await expect(sleepPromise).rejects.toThrow('Aborted'); + + expect(clearTimeoutSpy).toHaveBeenCalled(); + + clearTimeoutSpy.mockRestore(); + }); +}); diff --git a/packages/@n8n/utils/src/sleep.ts b/packages/@n8n/utils/src/sleep.ts new file mode 100644 index 00000000000..3a7f7c4bc42 --- /dev/null +++ b/packages/@n8n/utils/src/sleep.ts @@ -0,0 +1,30 @@ +async function sleepWithAbort(ms: number, abortSignal: AbortSignal): Promise { + return await new Promise((resolve, reject) => { + if (abortSignal.aborted) { + reject(new Error('Aborted')); + return; + } + + const timeout = setTimeout(resolve, ms); + + abortSignal.addEventListener( + 'abort', + () => { + clearTimeout(timeout); + reject(new Error('Aborted')); + }, + { once: true }, + ); + }); +} + +/** + * Resolves after `ms` milliseconds, or rejects early if `abortSignal` is aborted. + */ +export async function sleep(ms: number, abortSignal?: AbortSignal): Promise { + if (!abortSignal) { + return await new Promise((resolve) => setTimeout(resolve, ms)); + } + + return await sleepWithAbort(ms, abortSignal); +} diff --git a/packages/@n8n/workflow-sdk/package.json b/packages/@n8n/workflow-sdk/package.json index 5fb08bee631..cf9704eb86d 100644 --- a/packages/@n8n/workflow-sdk/package.json +++ b/packages/@n8n/workflow-sdk/package.json @@ -58,7 +58,8 @@ "fetch-workflows": "npx tsx scripts/fetch-test-workflows.ts", "create-workflows-zip": "npx tsx scripts/create-workflows-zip.ts", "json-to-code": "npx tsx src/cli/index.ts json-to-code", - "code-to-json": "npx tsx src/cli/index.ts code-to-json" + "code-to-json": "npx tsx src/cli/index.ts code-to-json", + "validate": "npx tsx src/cli/index.ts validate" }, "main": "dist/index.js", "module": "src/index.ts", @@ -85,6 +86,9 @@ "files": [ "dist/**/*" ], + "bin": { + "workflow-sdk": "./dist/cli/index.js" + }, "devDependencies": { "@n8n/eslint-plugin-community-nodes": "workspace:*", "@n8n/typescript-config": "workspace:*", @@ -99,6 +103,7 @@ }, "dependencies": { "@dagrejs/dagre": "^1.1.4", + "@n8n/constants": "workspace:*", "@n8n/utils": "workspace:*", "acorn": "8.14.0", "lodash": "catalog:", diff --git a/packages/@n8n/workflow-sdk/src/ast-interpreter/index.ts b/packages/@n8n/workflow-sdk/src/ast-interpreter/index.ts index e0c8b1c48f9..01778c6b9b1 100644 --- a/packages/@n8n/workflow-sdk/src/ast-interpreter/index.ts +++ b/packages/@n8n/workflow-sdk/src/ast-interpreter/index.ts @@ -40,6 +40,7 @@ export { BUILDER_BLOCKED_GLOBALS, SDK_INLINE_CONSTRAINTS, DANGEROUS_GLOBALS, + getSafeJSONMethod, isAllowedSDKFunction, isAllowedMethod, } from './validators'; diff --git a/packages/@n8n/workflow-sdk/src/cli/index.ts b/packages/@n8n/workflow-sdk/src/cli/index.ts index c395508eec6..040690b0303 100644 --- a/packages/@n8n/workflow-sdk/src/cli/index.ts +++ b/packages/@n8n/workflow-sdk/src/cli/index.ts @@ -1,22 +1,38 @@ #!/usr/bin/env node import { codeToJson } from './code-to-json'; import { jsonToCode } from './json-to-code'; +import { validateCommand } from './validate'; -const [command, filePath] = process.argv.slice(2); +const [command, ...rest] = process.argv.slice(2); -if (command === 'json-to-code') { - jsonToCode(filePath); -} else if (command === 'code-to-json') { - codeToJson(filePath); -} else { - console.error('Usage: workflow-sdk '); +async function main(): Promise { + if (command === 'json-to-code') { + jsonToCode(rest[0]); + return; + } + if (command === 'code-to-json') { + codeToJson(rest[0]); + return; + } + if (command === 'validate') { + await validateCommand(rest); + return; + } + + console.error('Usage: workflow-sdk [--json]'); console.error(''); console.error('Commands:'); console.error(' json-to-code Convert workflow JSON to SDK TypeScript code'); console.error(' code-to-json Convert SDK TypeScript code to workflow JSON'); + console.error(' validate Run graph + schema validation on an SDK TypeScript file'); console.error(''); console.error('Examples:'); console.error(' pnpm json-to-code ./workflow.json'); console.error(' pnpm code-to-json ./workflow.ts'); + console.error( + ' node --import tsx node_modules/@n8n/workflow-sdk/dist/cli/index.js validate src/workflow.ts', + ); process.exit(1); } + +void main(); diff --git a/packages/@n8n/workflow-sdk/src/cli/node-definition-dirs.test.ts b/packages/@n8n/workflow-sdk/src/cli/node-definition-dirs.test.ts new file mode 100644 index 00000000000..8ac783690cd --- /dev/null +++ b/packages/@n8n/workflow-sdk/src/cli/node-definition-dirs.test.ts @@ -0,0 +1,75 @@ +import fs from 'node:fs'; +import os from 'node:os'; +import path from 'node:path'; + +import { NODE_DEFINITION_DIRS_ENV_VAR, resolveNodeDefinitionDirs } from './node-definition-dirs'; + +let tmpRoot: string; + +/** A dir only counts as a node-definition dir if it holds a `nodes/` tree. */ +function makeDefinitionDir(name: string): string { + const dir = path.join(tmpRoot, name); + fs.mkdirSync(path.join(dir, 'nodes'), { recursive: true }); + return dir; +} + +beforeEach(() => { + tmpRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'node-def-dirs-')); + delete process.env[NODE_DEFINITION_DIRS_ENV_VAR]; +}); + +afterEach(() => { + fs.rmSync(tmpRoot, { recursive: true, force: true }); + delete process.env[NODE_DEFINITION_DIRS_ENV_VAR]; +}); + +describe('resolveNodeDefinitionDirs', () => { + it('accepts explicit dirs', () => { + const first = makeDefinitionDir('base'); + const second = makeDefinitionDir('langchain'); + + expect( + resolveNodeDefinitionDirs({ + explicit: [first, second], + workflowDir: tmpRoot, + }), + ).toEqual([first, second]); + }); + + it('drops paths that are missing or lack a nodes/ tree', () => { + const valid = makeDefinitionDir('base'); + const empty = path.join(tmpRoot, 'empty'); + fs.mkdirSync(empty); + + expect( + resolveNodeDefinitionDirs({ + explicit: [valid, empty, path.join(tmpRoot, 'does-not-exist')], + workflowDir: tmpRoot, + }), + ).toEqual([valid]); + }); + + it('reads the env var as a delimiter-separated list', () => { + const first = makeDefinitionDir('base'); + const second = makeDefinitionDir('langchain'); + process.env[NODE_DEFINITION_DIRS_ENV_VAR] = [first, second].join(path.delimiter); + + expect(resolveNodeDefinitionDirs({ workflowDir: tmpRoot })).toEqual([first, second]); + }); + + it('prefers explicit dirs over the env var', () => { + const fromFlag = makeDefinitionDir('flag'); + process.env[NODE_DEFINITION_DIRS_ENV_VAR] = makeDefinitionDir('env'); + + expect( + resolveNodeDefinitionDirs({ + explicit: [fromFlag], + workflowDir: tmpRoot, + }), + ).toEqual([fromFlag]); + }); + + it('returns no dirs when the builtin node packages are not resolvable', () => { + expect(resolveNodeDefinitionDirs({ workflowDir: tmpRoot, cwd: tmpRoot })).toEqual([]); + }); +}); diff --git a/packages/@n8n/workflow-sdk/src/cli/node-definition-dirs.ts b/packages/@n8n/workflow-sdk/src/cli/node-definition-dirs.ts new file mode 100644 index 00000000000..a991e121f8a --- /dev/null +++ b/packages/@n8n/workflow-sdk/src/cli/node-definition-dirs.ts @@ -0,0 +1,88 @@ +/** + * Locate the generated node-definition directories that back Zod parameter + * validation. + * + * `validateNodeConfig` silently passes when no schema is found, so a CLI run + * without these dirs reports "no issues" for parameter problems the server + * would reject. Resolution mirrors the server (`/dist/node-definitions/` + * for each builtin nodes package). In the Instance AI sandbox those packages + * are not installed — graph + source lint still run; parameter schemas do not. + */ + +import { BUILTIN_NODES_PACKAGES } from '@n8n/constants'; +import fs from 'node:fs'; +import { createRequire } from 'node:module'; +import path from 'node:path'; + +export const NODE_DEFINITION_DIRS_ENV_VAR = 'N8N_NODE_DEFINITION_DIRS'; + +/** A node-definition dir always holds a `nodes/` tree; anything else is a mistyped path. */ +function looksLikeNodeDefinitionDir(dir: string): boolean { + return fs.existsSync(path.join(dir, 'nodes')); +} + +function splitEnvValue(value: string): string[] { + return value + .split(path.delimiter) + .map((entry) => entry.trim()) + .filter(Boolean); +} + +function keepExisting(candidates: string[]): string[] { + const dirs: string[] = []; + for (const candidate of candidates) { + const resolved = path.resolve(candidate); + if (!dirs.includes(resolved) && looksLikeNodeDefinitionDir(resolved)) { + dirs.push(resolved); + } + } + return dirs; +} + +/** + * Resolve `/dist/node-definitions` for each builtin nodes package, trying + * each start directory in turn. `createRequire` is anchored to a file path so + * resolution follows the workflow file's own `node_modules` chain first. + */ +function resolveFromPackages(startDirs: string[]): string[] { + const dirs: string[] = []; + for (const startDir of startDirs) { + const requireFrom = createRequire(path.join(startDir, 'noop.js')); + for (const packageId of BUILTIN_NODES_PACKAGES) { + let packageJsonPath: string; + try { + packageJsonPath = requireFrom.resolve(`${packageId}/package.json`); + } catch { + continue; + } + const candidate = path.join(path.dirname(packageJsonPath), 'dist', 'node-definitions'); + if (!dirs.includes(candidate) && looksLikeNodeDefinitionDir(candidate)) { + dirs.push(candidate); + } + } + } + return dirs; +} + +/** + * Precedence: explicit `--node-types` flags, then the env var, then package + * resolution from the workflow file's directory and the cwd. + */ +export function resolveNodeDefinitionDirs(options: { + explicit?: string[]; + workflowDir: string; + cwd?: string; +}): string[] { + const { explicit = [], workflowDir, cwd = process.cwd() } = options; + + if (explicit.length > 0) { + return keepExisting(explicit); + } + + const envValue = process.env[NODE_DEFINITION_DIRS_ENV_VAR]; + if (envValue) { + return keepExisting(splitEnvValue(envValue)); + } + + return resolveFromPackages([workflowDir, cwd]); +} diff --git a/packages/@n8n/workflow-sdk/src/cli/validate.ts b/packages/@n8n/workflow-sdk/src/cli/validate.ts new file mode 100644 index 00000000000..a7639a55ab4 --- /dev/null +++ b/packages/@n8n/workflow-sdk/src/cli/validate.ts @@ -0,0 +1,291 @@ +import fs from 'node:fs'; +import path from 'node:path'; +import { pathToFileURL } from 'node:url'; + +import { resolveNodeDefinitionDirs, NODE_DEFINITION_DIRS_ENV_VAR } from './node-definition-dirs'; +import type { WorkflowJSON } from '../types/base'; +import { + buildUncheckedNotes, + validateWorkflowBuilder, + type CollectedValidationIssue, + type ValidationResult, +} from '../validation'; + +export interface ValidateCliOptions { + json?: boolean; + nodeTypes?: string[]; +} + +interface WorkflowBuilderLike { + validate: () => ValidationResult; + toJSON: (options?: { tidyUp?: boolean }) => WorkflowJSON; +} + +/** Blocking issues report as `error`, informational ones as `warning`. */ +interface ReportEntry { + line?: number; + column?: number; + severity: 'error' | 'warning'; + code: string; + message: string; +} + +function buildTrailer(unchecked: string[]): string { + return `note: errors block a build-workflow save, warnings do not — but warnings flag likely run-time defects, so resolve each one instead of shipping past it. Not checked here: ${unchecked.join('; ')}.`; +} + +function usageAndExit(): never { + console.error('Usage: workflow-sdk validate [--json] [--node-types ]'); + console.error(''); + console.error('Load a workflow SDK TypeScript file via dynamic import, run graph'); + console.error('validators (wf.validate), schema validateWorkflow, and source lint.'); + console.error('Exit non-zero only for issues that would block a build-workflow save.'); + console.error(''); + console.error('Node parameter validation needs generated node definitions. They are'); + console.error('resolved from the workflow file and cwd; override with --node-types'); + console.error(`(repeatable) or ${NODE_DEFINITION_DIRS_ENV_VAR} (${path.delimiter}-separated).`); + process.exit(1); +} + +function parseArgs(argv: string[]): { filePath: string; options: ValidateCliOptions } { + let filePath: string | undefined; + let json = false; + const nodeTypes: string[] = []; + + for (let i = 0; i < argv.length; i++) { + const arg = argv[i]; + if (arg === '--json') { + json = true; + } else if (arg.startsWith('--node-types=')) { + nodeTypes.push(arg.slice('--node-types='.length)); + } else if (arg === '--node-types') { + const value = argv[++i]; + if (!value) { + console.error('--node-types requires a directory'); + usageAndExit(); + } + nodeTypes.push(value); + } else if (arg.startsWith('-')) { + console.error(`Unknown option: ${arg}`); + usageAndExit(); + } else if (!filePath) { + filePath = arg; + } else { + console.error(`Unexpected argument: ${arg}`); + usageAndExit(); + } + } + + if (!filePath) { + usageAndExit(); + } + + return { filePath, options: { json, nodeTypes } }; +} + +function toReportEntry( + issue: CollectedValidationIssue, + severity: ReportEntry['severity'], +): ReportEntry { + // Most validator messages already name the node; only prefix when they don't. + const message = + issue.nodeName && !issue.message.includes(issue.nodeName) + ? `${issue.nodeName}: ${issue.message}` + : issue.message; + return { + line: issue.line, + column: issue.column, + severity, + code: issue.code, + message, + }; +} + +function byLocation(a: ReportEntry, b: ReportEntry): number { + const lineDiff = (a.line ?? Number.MAX_SAFE_INTEGER) - (b.line ?? Number.MAX_SAFE_INTEGER); + if (lineDiff !== 0) return lineDiff; + return (a.column ?? Number.MAX_SAFE_INTEGER) - (b.column ?? Number.MAX_SAFE_INTEGER); +} + +function pluralize(count: number, noun: string): string { + return `${count} ${noun}${count === 1 ? '' : 's'}`; +} + +/** ESLint-stylish location: `line:column`, or `-` when unknown. */ +function formatLocation(entry: Pick): string { + if (entry.line === undefined) return '-'; + if (entry.column === undefined) return String(entry.line); + return `${entry.line}:${entry.column}`; +} + +/** ESLint-stylish layout: one file header, then `line:col severity code message` rows. */ +function formatReport(file: string, entries: ReportEntry[], unchecked: string[]): string { + if (entries.length === 0) { + return [file, ' no issues found', '', buildTrailer(unchecked)].join('\n'); + } + + const sorted = [...entries].sort(byLocation); + const locations = sorted.map(formatLocation); + const locationWidth = Math.max(...locations.map((location) => location.length)); + const severityWidth = Math.max(...sorted.map((entry) => entry.severity.length)); + const codeWidth = Math.max(...sorted.map((entry) => entry.code.length)); + + const rows = sorted.map((entry, index) => { + const location = locations[index].padStart(locationWidth); + const severity = entry.severity.padEnd(severityWidth); + const code = entry.code.padEnd(codeWidth); + return ` ${location} ${severity} ${code} ${entry.message}`; + }); + + const errorCount = sorted.filter((entry) => entry.severity === 'error').length; + const warningCount = sorted.length - errorCount; + const summary = `${pluralize(sorted.length, 'problem')} (${pluralize(errorCount, 'error')}, ${pluralize(warningCount, 'warning')})`; + + return [file, ...rows, '', summary, buildTrailer(unchecked)].join('\n'); +} + +function formatText( + file: string, + blocking: CollectedValidationIssue[], + informational: CollectedValidationIssue[], + unchecked: string[], +): string { + return formatReport( + file, + [ + ...blocking.map((issue) => toReportEntry(issue, 'error')), + ...informational.map((issue) => toReportEntry(issue, 'warning')), + ], + unchecked, + ); +} + +function failAndExit( + options: ValidateCliOptions, + unchecked: string[], + code: string, + message: string, + file: string, +): never { + if (options.json) { + console.log( + JSON.stringify({ + ok: false, + file, + blocking: [{ code, message }], + informational: [], + unchecked, + }), + ); + } else { + console.error(formatReport(file, [{ severity: 'error', code, message }], unchecked)); + } + process.exit(1); +} + +function isWorkflowBuilder(value: unknown): value is WorkflowBuilderLike { + if (typeof value !== 'object' || value === null) { + return false; + } + const candidate = value as { validate?: unknown; toJSON?: unknown }; + return typeof candidate.validate === 'function' && typeof candidate.toJSON === 'function'; +} + +/** + * Resolve `export default workflow(...)` across CJS/ESM interop shapes. + * + * The validate CLI is published as CommonJS. When it dynamically imports a + * TypeScript workflow via tsx in a package without `"type": "module"` (the + * Instance AI sandbox), Node can surface the builder as `mod.default.default` + * instead of `mod.default`. build.mjs is ESM and usually gets the unwrapped + * shape, which is why validate was failing while build-workflow still worked. + */ +function resolveWorkflowExport(mod: { default?: unknown }): unknown { + const exported = mod.default; + if (isWorkflowBuilder(exported)) { + return exported; + } + if (typeof exported === 'object' && exported !== null && 'default' in exported) { + const nested = (exported as { default: unknown }).default; + if (isWorkflowBuilder(nested)) { + return nested; + } + } + return exported; +} + +/** + * Validate a workflow SDK TypeScript source file. + * + * Thin CLI wrapper around {@link validateWorkflowBuilder} with `lint: true`. + */ +export async function validateCommand(argv: string[] = process.argv.slice(3)): Promise { + const { filePath, options } = parseArgs(argv); + const absolutePath = path.resolve(filePath); + const displayPath = path.relative(process.cwd(), absolutePath) || absolutePath; + const importUrl = pathToFileURL(absolutePath).href; + + const nodeDefinitionDirs = resolveNodeDefinitionDirs({ + explicit: options.nodeTypes, + workflowDir: path.dirname(absolutePath), + }); + + let source = ''; + try { + source = fs.readFileSync(absolutePath, 'utf8'); + } catch { + // Load failure below reports a clearer error; lint simply skips. + } + + const earlyUnchecked = buildUncheckedNotes({ + schemasLoaded: nodeDefinitionDirs.length > 0, + hasNodeTypesProvider: false, + }); + + let mod: { default?: unknown }; + try { + mod = (await import(importUrl)) as { default?: unknown }; + } catch (error) { + failAndExit( + options, + earlyUnchecked, + 'LOAD_FAILED', + `Failed to load workflow: ${error instanceof Error ? error.message : String(error)}`, + displayPath, + ); + } + + const workflowExport = resolveWorkflowExport(mod); + if (!isWorkflowBuilder(workflowExport)) { + failAndExit( + options, + earlyUnchecked, + 'INVALID_DEFAULT_EXPORT', + 'Default export is not a workflow. Make sure your file has: export default workflow(...)', + displayPath, + ); + } + + const result = validateWorkflowBuilder(workflowExport, { + lint: true, + source, + nodeDefinitionDirs, + }); + + if (options.json) { + console.log( + JSON.stringify({ + ok: result.ok, + file: displayPath, + blocking: result.blocking, + informational: result.informational, + unchecked: result.unchecked, + nodeDefinitionDirs: result.nodeDefinitionDirs, + }), + ); + } else { + console.log(formatText(displayPath, result.blocking, result.informational, result.unchecked)); + } + + process.exit(result.ok ? 0 : 1); +} diff --git a/packages/@n8n/workflow-sdk/src/codegen/codegen-roundtrip.test.ts b/packages/@n8n/workflow-sdk/src/codegen/codegen-roundtrip.test.ts index a00654b0907..c61e950e2e5 100644 --- a/packages/@n8n/workflow-sdk/src/codegen/codegen-roundtrip.test.ts +++ b/packages/@n8n/workflow-sdk/src/codegen/codegen-roundtrip.test.ts @@ -12,7 +12,7 @@ import { } from '../__tests__/fixtures-download'; import type { WorkflowJSON } from '../types/base'; import { foldLegacyErrorConnections, normalizeConnections } from '../types/base'; -import { validateWorkflow } from '../validation'; +import { validateWorkflow, validateWorkflowBuilder } from '../validation'; import { escapeNewlinesInExpressionStrings, isPlaceholderValue, @@ -28,6 +28,11 @@ interface ExpectedError { nodeName?: string; } +interface ExpectedLintIssue { + code: string; + nodeName?: string; +} + interface TestWorkflow { id: string; name: string; @@ -39,6 +44,11 @@ interface TestWorkflow { expectedValidationWarnings?: ExpectedWarning[]; /** Errors expected from WorkflowBuilder.validate() (plugin validator pipeline). */ expectedBuilderErrors?: ExpectedError[]; + /** + * When present (including `[]`), codegen source is linted via + * `validateWorkflowBuilder({ lint: true })` and must match exactly. + */ + expectedLintIssues?: ExpectedLintIssue[]; } function loadWorkflowsFromDir(dir: string, workflows: TestWorkflow[]): void { @@ -59,6 +69,7 @@ function loadWorkflowsFromDir(dir: string, workflows: TestWorkflow[]): void { expectedErrors?: ExpectedError[]; expectedValidationWarnings?: ExpectedWarning[]; expectedBuilderErrors?: ExpectedError[]; + expectedLintIssues?: ExpectedLintIssue[]; }>; }; @@ -78,6 +89,7 @@ function loadWorkflowsFromDir(dir: string, workflows: TestWorkflow[]): void { expectedErrors: entry.expectedErrors, expectedValidationWarnings: entry.expectedValidationWarnings, expectedBuilderErrors: entry.expectedBuilderErrors, + expectedLintIssues: entry.expectedLintIssues, }); } } @@ -2910,6 +2922,44 @@ describe('Committed workflows — builder validator errors', () => { } }); +describe('Committed workflows — source lint via validateWorkflowBuilder', () => { + const normalizeLint = (issue: ExpectedLintIssue): string => + `${issue.code}:${issue.nodeName ?? ''}`; + + const workflowsWithLintExpectations = workflows.filter((w) => w.expectedLintIssues !== undefined); + + if (workflowsWithLintExpectations.length === 0) { + it('has at least one fixture with expectedLintIssues declared', () => { + expect(workflowsWithLintExpectations.length).toBeGreaterThan(0); + }); + } else { + workflowsWithLintExpectations.forEach(({ id, name, json, expectedLintIssues }) => { + it(`emits expected lint issues for workflow ${id}: "${name}"`, () => { + const code = generateWorkflowCode(json); + const builder = parseWorkflowCodeToBuilder(code); + const result = validateWorkflowBuilder(builder, { + lint: true, + source: code, + allowDisconnectedNodes: true, + }); + + const actual: ExpectedLintIssue[] = result.lint + .map((issue) => ({ code: issue.code, nodeName: issue.nodeName })) + .sort((a, b) => normalizeLint(a).localeCompare(normalizeLint(b))); + + const expected = (expectedLintIssues ?? []) + .slice() + .sort((a, b) => normalizeLint(a).localeCompare(normalizeLint(b))); + + expect(actual).toEqual(expected); + // Lint findings are informational — they must not block ok. + expect(result.ok).toBe(true); + expect(result.lint.every((issue) => issue.severity === 'informational')).toBe(true); + }); + }); + } +}); + describe('Committed workflows — schema validation errors', () => { // Mirror the relevant builderHint.inputs / builderHint.outputs declarations from the // real node types so validateWorkflow can resolve required AI inputs and emit diff --git a/packages/@n8n/workflow-sdk/src/codegen/emit-instance-ai.test.ts b/packages/@n8n/workflow-sdk/src/codegen/emit-instance-ai.test.ts index fb03f78cd67..6369e100816 100644 --- a/packages/@n8n/workflow-sdk/src/codegen/emit-instance-ai.test.ts +++ b/packages/@n8n/workflow-sdk/src/codegen/emit-instance-ai.test.ts @@ -254,6 +254,9 @@ describe('emit-instance-ai', () => { 'validateWorkflow', 'getSchemaBaseDirs', 'setSchemaBaseDirs', + 'isInformationalIssue', + 'partitionValidationIssues', + 'validateWorkflowBuilder', // Pin-data + schema discovery 'discoverOutputSchemaForNode', 'discoverSchemasForNode', diff --git a/packages/@n8n/workflow-sdk/src/generate-types/generate-zod-schemas.test.ts b/packages/@n8n/workflow-sdk/src/generate-types/generate-zod-schemas.test.ts index 26a2d26555c..885ffdd0b73 100644 --- a/packages/@n8n/workflow-sdk/src/generate-types/generate-zod-schemas.test.ts +++ b/packages/@n8n/workflow-sdk/src/generate-types/generate-zod-schemas.test.ts @@ -14,7 +14,7 @@ import { mergeDisplayOptions, extractDefaultsForDisplayOptions, } from './generate-zod-schemas'; -import * as schemaHelpers from '../validation/schema-helpers'; +import * as schemaHelpers from '../validation/node-parameter-schema/schema-helpers'; describe('mapPropertyToZodSchema for resourceLocator', () => { it('returns resourceLocatorValueSchema when no modes are specified', () => { diff --git a/packages/@n8n/workflow-sdk/src/index.ts b/packages/@n8n/workflow-sdk/src/index.ts index b2229e4e84b..db67d2db795 100644 --- a/packages/@n8n/workflow-sdk/src/index.ts +++ b/packages/@n8n/workflow-sdk/src/index.ts @@ -159,6 +159,13 @@ export { type ValidationErrorCode, validateNodeConfig, type SchemaValidationResult, + type IssueSeverity, + isInformationalIssue, + partitionValidationIssues, + validateWorkflowBuilder, + type ValidateWorkflowBuilderOptions, + type ValidateWorkflowBuilderResult, + type CollectedValidationIssue, } from './validation'; // Code generation @@ -221,3 +228,12 @@ export { isWebhookType, isDataTableType, } from './constants'; + +// Canvas geometry — the same values the layout engine and the editor's canvas use. +// Exported so consumers that place nodes themselves stay on the same grid. +export { + GRID_SIZE, + DEFAULT_NODE_SIZE, + NODE_X_SPACING, + NODE_Y_SPACING, +} from './workflow-builder/constants'; diff --git a/packages/@n8n/workflow-sdk/src/lint/ast-walk.ts b/packages/@n8n/workflow-sdk/src/lint/ast-walk.ts new file mode 100644 index 00000000000..02e6630eb91 --- /dev/null +++ b/packages/@n8n/workflow-sdk/src/lint/ast-walk.ts @@ -0,0 +1,51 @@ +import type { Node } from 'estree'; + +import type { SourceLintIssue } from './types'; + +function isEstreeNode(value: unknown): value is Node { + return typeof value === 'object' && value !== null && 'type' in value; +} + +/** + * Depth-first AST walk. `visit` runs before children. When `skipChildren` + * returns true, descendants are not visited (the node itself still is). + */ +export function walkAst( + node: Node, + visit: (n: Node, parent: Node | undefined) => void, + options: { + parent?: Node; + skipChildren?: (n: Node, parent: Node | undefined) => boolean; + } = {}, +): void { + const { parent, skipChildren } = options; + visit(node, parent); + if (skipChildren?.(node, parent)) return; + + for (const key of Object.keys(node) as Array) { + if (key === 'loc' || key === 'range') continue; + const value = node[key]; + if (!value || typeof value !== 'object') continue; + if (Array.isArray(value)) { + for (const entry of value) { + if (isEstreeNode(entry)) { + walkAst(entry, visit, { parent: node, skipChildren }); + } + } + } else if (isEstreeNode(value)) { + walkAst(value, visit, { parent: node, skipChildren }); + } + } +} + +export function dedupeSourceLintIssues(issues: SourceLintIssue[]): SourceLintIssue[] { + const seen = new Set(); + const out: SourceLintIssue[] = []; + for (const issue of issues) { + const key = `${issue.lintTarget}|${issue.code}|${issue.line ?? ''}|${issue.column ?? ''}|${issue.message}`; + if (seen.has(key)) continue; + seen.add(key); + out.push(issue); + } + return out; +} diff --git a/packages/@n8n/workflow-sdk/src/lint/code-node/code-node.test.ts b/packages/@n8n/workflow-sdk/src/lint/code-node/code-node.test.ts new file mode 100644 index 00000000000..7b222b4a33f --- /dev/null +++ b/packages/@n8n/workflow-sdk/src/lint/code-node/code-node.test.ts @@ -0,0 +1,67 @@ +import { hasNestedTemplateLiterals, lintJsCode } from './js'; +import { lintPythonCode } from './python'; + +describe('lintJsCode', () => { + it('flags fetch in jsCode', () => { + expect(lintJsCode('await fetch("https://example.com");').map((i) => i.code)).toEqual([ + 'CODE_NODE_NETWORK_CALL', + ]); + }); + + it('does not flag fetch mentioned only in a comment', () => { + expect( + lintJsCode('// await fetch("https://example.com");\nreturn [];').map((i) => i.code), + ).toEqual([]); + }); + + it('flags nested template literals via AST', () => { + expect(hasNestedTemplateLiterals('const x = `outer ${`inner`} `;')).toBe(true); + expect(hasNestedTemplateLiterals('const x = `plain`;')).toBe(false); + }); + + it.each(['first', 'last', 'all', 'itemMatching'] as const)( + 'flags $input.%s() in runOnceForEachItem mode', + (method) => { + const issues = lintJsCode(`return $input.${method}();`, { mode: 'runOnceForEachItem' }); + const misuse = issues.filter((i) => i.code === 'CODE_MODE_API_MISUSE'); + expect(misuse).toHaveLength(1); + expect(misuse[0].message).toContain(`$input.${method}()`); + }, + ); + + it('does not flag $input.item in runOnceForEachItem mode', () => { + expect( + lintJsCode('return $input.item.json;', { mode: 'runOnceForEachItem' }).map((i) => i.code), + ).toEqual([]); + }); + + it('does not flag $input.all() in runOnceForAllItems mode', () => { + expect( + lintJsCode('return $input.all();', { mode: 'runOnceForAllItems' }).map((i) => i.code), + ).toEqual([]); + }); +}); + +describe('lintPythonCode', () => { + it('flags requests imports in pythonCode', () => { + expect(lintPythonCode('import requests').map((i) => i.code)).toEqual([ + 'CODE_NODE_NETWORK_CALL', + ]); + }); + + it('does not flag a variable merely named requests', () => { + expect(lintPythonCode('requests = []\nreturn requests').map((i) => i.code)).toEqual([]); + }); + + it('flags import http.client in pythonCode', () => { + expect(lintPythonCode('import http.client').map((i) => i.code)).toEqual([ + 'CODE_NODE_NETWORK_CALL', + ]); + }); + + it('flags from http import client in pythonCode', () => { + expect(lintPythonCode('from http import client').map((i) => i.code)).toEqual([ + 'CODE_NODE_NETWORK_CALL', + ]); + }); +}); diff --git a/packages/@n8n/workflow-sdk/src/lint/code-node/extract-snippets.test.ts b/packages/@n8n/workflow-sdk/src/lint/code-node/extract-snippets.test.ts new file mode 100644 index 00000000000..e85f6788657 --- /dev/null +++ b/packages/@n8n/workflow-sdk/src/lint/code-node/extract-snippets.test.ts @@ -0,0 +1,51 @@ +import { extractEmbeddedCodeSnippetsFromSource } from './extract-snippets'; + +describe('extractEmbeddedCodeSnippetsFromSource', () => { + it('extracts jsCode from a Code node config', () => { + const source = ` +const transform = node({ + type: 'n8n-nodes-base.code', + version: 2, + config: { + name: 'Transform', + parameters: { + mode: 'runOnceForEachItem', + jsCode: 'return $input.all();', + }, + }, +}); +export default workflow('id', 'name').add(transform); +`; + const snippets = extractEmbeddedCodeSnippetsFromSource(source); + expect(snippets).toEqual([ + expect.objectContaining({ + parameter: 'jsCode', + code: 'return $input.all();', + mode: 'runOnceForEachItem', + }), + ]); + }); + + it('extracts pythonCode from a Code node config', () => { + const source = ` +const transform = node({ + type: 'n8n-nodes-base.code', + version: 2, + config: { + name: 'Transform', + parameters: { + language: 'pythonNative', + pythonCode: 'import requests', + }, + }, +}); +export default workflow('id', 'name').add(transform); +`; + expect(extractEmbeddedCodeSnippetsFromSource(source)).toEqual([ + expect.objectContaining({ + parameter: 'pythonCode', + code: 'import requests', + }), + ]); + }); +}); diff --git a/packages/@n8n/workflow-sdk/src/lint/code-node/extract-snippets.ts b/packages/@n8n/workflow-sdk/src/lint/code-node/extract-snippets.ts new file mode 100644 index 00000000000..1446defd6fc --- /dev/null +++ b/packages/@n8n/workflow-sdk/src/lint/code-node/extract-snippets.ts @@ -0,0 +1,129 @@ +import type { Node, ObjectExpression, Program, Property } from 'estree'; + +import { parseSDKCode } from '../../ast-interpreter'; +import { walkAst } from '../ast-walk'; + +export type CodeExecutionMode = 'runOnceForAllItems' | 'runOnceForEachItem'; + +export interface EmbeddedCodeSnippet { + parameter: 'jsCode' | 'pythonCode'; + code: string; + /** 1-based line of the property value in the prepared source. */ + line?: number; + /** 1-based column of the property value in the prepared source. */ + column?: number; + mode?: CodeExecutionMode; +} + +function locationOf(node: Node): { line?: number; column?: number } { + if (!node.loc) return {}; + return { line: node.loc.start.line, column: node.loc.start.column + 1 }; +} + +function propertyKeyName(key: Property['key']): string | undefined { + if (key.type === 'Identifier') return key.name; + if (key.type === 'Literal' && typeof key.value === 'string') return key.value; + return undefined; +} + +function stringFromNode(node: Node, source: string): string | undefined { + if (node.type === 'Literal' && typeof node.value === 'string') { + return node.value; + } + if (node.type === 'TemplateLiteral' && node.expressions.length === 0) { + return node.quasis.map((q) => q.value.cooked ?? q.value.raw).join(''); + } + if ( + 'start' in node && + 'end' in node && + typeof node.start === 'number' && + typeof node.end === 'number' + ) { + const raw = source.slice(node.start, node.end); + if (node.type === 'TemplateLiteral') { + return raw.slice(1, raw.endsWith('`') ? -1 : undefined); + } + } + return undefined; +} + +function modeFromObject(obj: ObjectExpression): CodeExecutionMode | undefined { + for (const prop of obj.properties) { + if (prop.type !== 'Property' || prop.computed) continue; + if (propertyKeyName(prop.key) !== 'mode') continue; + if (prop.value.type === 'Literal' && typeof prop.value.value === 'string') { + if (prop.value.value === 'runOnceForEachItem') return 'runOnceForEachItem'; + if (prop.value.value === 'runOnceForAllItems') return 'runOnceForAllItems'; + } + } + return undefined; +} + +function enclosingObjectExpression( + node: Node, + parents: ReadonlyMap, +): ObjectExpression | undefined { + let current: Node | undefined = node; + while (current) { + if (current.type === 'ObjectExpression') return current; + current = parents.get(current); + } + return undefined; +} + +/** Whether SDK source lint should skip descending into this node. */ +export function isEmbeddedCodePropertyValue(node: Node, parent: Node | undefined): boolean { + if (parent?.type !== 'Property' || parent.computed) return false; + const key = propertyKeyName(parent.key); + if (key !== 'jsCode' && key !== 'pythonCode') return false; + return node === parent.value; +} + +export function buildParentMap(ast: Program): Map { + const parents = new Map(); + walkAst(ast, (node, parent) => { + if (parent) parents.set(node, parent); + }); + return parents; +} + +/** + * Extract jsCode / pythonCode string values from a parsed SDK workflow AST. + */ +export function extractEmbeddedCodeSnippets( + ast: Program, + source: string, + parents: ReadonlyMap = buildParentMap(ast), +): EmbeddedCodeSnippet[] { + const snippets: EmbeddedCodeSnippet[] = []; + + walkAst(ast, (node) => { + if (node.type !== 'Property' || node.computed) return; + const key = propertyKeyName(node.key); + if (key !== 'jsCode' && key !== 'pythonCode') return; + + const code = stringFromNode(node.value, source); + if (code === undefined || code.length === 0) return; + + const enclosing = enclosingObjectExpression(node, parents); + snippets.push({ + parameter: key, + code, + ...locationOf(node.value), + mode: enclosing ? modeFromObject(enclosing) : undefined, + }); + }); + + return snippets; +} + +/** Parse prepared SDK source and extract embedded Code node snippets. */ +export function extractEmbeddedCodeSnippetsFromSource(source: string): EmbeddedCodeSnippet[] { + let ast: Program; + try { + ast = parseSDKCode(source); + } catch { + return []; + } + return extractEmbeddedCodeSnippets(ast, source, buildParentMap(ast)); +} diff --git a/packages/@n8n/workflow-sdk/src/lint/code-node/js.ts b/packages/@n8n/workflow-sdk/src/lint/code-node/js.ts new file mode 100644 index 00000000000..4d08ee72681 --- /dev/null +++ b/packages/@n8n/workflow-sdk/src/lint/code-node/js.ts @@ -0,0 +1,228 @@ +import * as acorn from 'acorn'; +import type { CallExpression, Node, Program } from 'estree'; + +import { walkAst } from '../ast-walk'; +import type { CodeExecutionMode } from './extract-snippets'; +import { lintIssue, type SourceLintIssue } from '../types'; + +const NETWORK_CALLEE_NAMES = new Set(['fetch', 'axios', 'XMLHttpRequest']); + +const HTTP_MODULES = new Set([ + 'http', + 'https', + 'http2', + 'node-fetch', + 'axios', + 'got', + 'undici', + 'node:http', + 'node:https', + 'node:http2', +]); + +const FORBIDDEN_MODULE_PREFIXES = ['luxon', 'openai', '@openai/', 'langchain', '@langchain/']; + +/** + * $input methods rejected at runtime in runOnceForEachItem mode — mirrors + * validateNoDisallowedMethodsInRunForEach in nodes-base/Code. + */ +const EACH_ITEM_DISALLOWED_INPUT_METHODS = new Set(['first', 'last', 'all', 'itemMatching']); + +export interface LintJsCodeOptions { + mode?: CodeExecutionMode; + nodeName?: string; +} + +function parseJs(code: string): Program | undefined { + const opts = { + ecmaVersion: 'latest' as const, + locations: true, + // Code-node bodies are function bodies; agents often start with `return …`. + allowReturnOutsideFunction: true, + }; + try { + return acorn.parse(code, { ...opts, sourceType: 'script' }) as unknown as Program; + } catch { + try { + return acorn.parse(code, { ...opts, sourceType: 'module' }) as unknown as Program; + } catch { + return undefined; + } + } +} + +function stringLiteralArg(node: Node | undefined): string | undefined { + if (!node) return undefined; + if (node.type === 'Literal' && typeof node.value === 'string') return node.value; + return undefined; +} + +function isRequireCall(call: CallExpression): boolean { + return call.callee.type === 'Identifier' && call.callee.name === 'require'; +} + +function moduleSpecifierFromCall(call: CallExpression): string | undefined { + if (isRequireCall(call)) { + return stringLiteralArg(call.arguments[0] as Node | undefined); + } + return undefined; +} + +function moduleIsForbidden(specifier: string): boolean { + return FORBIDDEN_MODULE_PREFIXES.some( + (prefix) => specifier === prefix || specifier.startsWith(prefix), + ); +} + +function hasNestedTemplateLiteral(ast: Program): boolean { + let found = false; + walkAst(ast, (node) => { + if (found || node.type !== 'TemplateLiteral') return; + for (const expr of node.expressions) { + let nested = false; + walkAst(expr, (inner) => { + if (inner.type === 'TemplateLiteral') nested = true; + }); + if (nested) { + found = true; + return; + } + } + }); + return found; +} + +/** + * Lint JavaScript written for a Code node (`jsCode` parameter). + * Uses acorn so comments/strings do not trigger false positives. + */ +export function lintJsCode(jsCode: string, options: LintJsCodeOptions = {}): SourceLintIssue[] { + if (jsCode.length === 0) return []; + + const ast = parseJs(jsCode); + if (!ast) return []; + + const issues: SourceLintIssue[] = []; + const namePrefix = options.nodeName ? `'${options.nodeName}' ` : ''; + let sawNetwork = false; + let sawForbiddenImport = false; + let disallowedInputMethod: string | undefined; + + walkAst(ast, (node) => { + if (node.type === 'ImportDeclaration') { + const source = node.source.type === 'Literal' ? String(node.source.value) : ''; + if (moduleIsForbidden(source)) sawForbiddenImport = true; + if (HTTP_MODULES.has(source)) sawNetwork = true; + return; + } + + if (node.type === 'ImportExpression') { + const mod = stringLiteralArg(node.source); + if (mod) { + if (HTTP_MODULES.has(mod)) sawNetwork = true; + if (moduleIsForbidden(mod)) sawForbiddenImport = true; + } + return; + } + + if (node.type === 'CallExpression') { + const call = node; + + if (call.callee.type === 'Identifier' && NETWORK_CALLEE_NAMES.has(call.callee.name)) { + sawNetwork = true; + } + if ( + call.callee.type === 'MemberExpression' && + !call.callee.computed && + call.callee.property.type === 'Identifier' && + EACH_ITEM_DISALLOWED_INPUT_METHODS.has(call.callee.property.name) && + call.callee.object.type === 'Identifier' && + call.callee.object.name === '$input' && + disallowedInputMethod === undefined + ) { + disallowedInputMethod = call.callee.property.name; + } + + const required = moduleSpecifierFromCall(call); + if (required) { + if (HTTP_MODULES.has(required)) sawNetwork = true; + if (moduleIsForbidden(required)) sawForbiddenImport = true; + } + } + + if ( + node.type === 'NewExpression' && + node.callee.type === 'Identifier' && + node.callee.name === 'XMLHttpRequest' + ) { + sawNetwork = true; + } + }); + + if (sawNetwork) { + issues.push( + lintIssue({ + code: 'CODE_NODE_NETWORK_CALL', + message: + `${namePrefix}Code node calls fetch/axios/XMLHttpRequest or requires an HTTP module. ` + + 'Code nodes have no network access at runtime — make the HTTP/API call with an HTTP Request node ' + + 'and transform its output in the Code node instead.', + lintTarget: 'jsCode', + nodeName: options.nodeName, + parameterPath: 'jsCode', + }), + ); + } + + if (sawForbiddenImport) { + issues.push( + lintIssue({ + code: 'CODE_NODE_FORBIDDEN_IMPORT', + message: + `${namePrefix}Code node imports a module unavailable in the sandbox (luxon, openai, langchain, …). ` + + 'Use JavaScript `Date`/`Intl`, `$now`/`$today`, existing workflow data, or dedicated AI nodes instead.', + lintTarget: 'jsCode', + nodeName: options.nodeName, + parameterPath: 'jsCode', + }), + ); + } + + if (options.mode === 'runOnceForEachItem' && disallowedInputMethod !== undefined) { + issues.push( + lintIssue({ + code: 'CODE_MODE_API_MISUSE', + message: + `${namePrefix}uses mode: 'runOnceForEachItem' but calls $input.${disallowedInputMethod}(). ` + + `$input.${disallowedInputMethod}() is only available in runOnceForAllItems (the default). ` + + 'Switch mode to runOnceForAllItems, or use $input.item / $json for per-item work.', + lintTarget: 'jsCode', + nodeName: options.nodeName, + parameterPath: 'jsCode', + }), + ); + } + + if (hasNestedTemplateLiteral(ast)) { + issues.push( + lintIssue({ + code: 'CODE_NESTED_TEMPLATE_LITERAL', + message: + `${namePrefix}Code node uses nested template literals, which often break after save. ` + + 'Build multi-line strings with arrays joined by a runtime separator, e.g. ' + + '`const LF = String.fromCharCode(10); return lines.join(LF);`.', + lintTarget: 'jsCode', + nodeName: options.nodeName, + parameterPath: 'jsCode', + }), + ); + } + + return issues; +} + +/** @deprecated Use lintJsCode — nested templates are detected via AST there. */ +export function hasNestedTemplateLiterals(jsCode: string): boolean { + const ast = parseJs(jsCode); + return ast ? hasNestedTemplateLiteral(ast) : false; +} diff --git a/packages/@n8n/workflow-sdk/src/lint/code-node/python.ts b/packages/@n8n/workflow-sdk/src/lint/code-node/python.ts new file mode 100644 index 00000000000..b62433d2eb5 --- /dev/null +++ b/packages/@n8n/workflow-sdk/src/lint/code-node/python.ts @@ -0,0 +1,42 @@ +import { lintIssue, type SourceLintIssue } from '../types'; + +/** Match real import/from lines — not identifiers that merely contain "requests". */ +const PYTHON_NETWORK_IMPORT = + /(?:^|\n)\s*(?:import|from)\s+(?:requests|urllib(?:\.[\w.]+)?|httpx|aiohttp|http\.client)\b/m; + +/** `from http import client` is equivalent to `import http.client`. */ +const PYTHON_NETWORK_FROM_IMPORT = /(?:^|\n)\s*from\s+http\s+import\s+client\b/m; + +export interface LintPythonCodeOptions { + nodeName?: string; +} + +/** + * Lint Python written for a Code node (`pythonCode` parameter). + */ +export function lintPythonCode( + pythonCode: string, + options: LintPythonCodeOptions = {}, +): SourceLintIssue[] { + if (pythonCode.length === 0) return []; + + const issues: SourceLintIssue[] = []; + const namePrefix = options.nodeName ? `'${options.nodeName}' ` : ''; + + if (PYTHON_NETWORK_IMPORT.test(pythonCode) || PYTHON_NETWORK_FROM_IMPORT.test(pythonCode)) { + issues.push( + lintIssue({ + code: 'CODE_NODE_NETWORK_CALL', + message: + `${namePrefix}Code node uses requests/urllib/httpx or another HTTP library. ` + + 'Code nodes have no network access at runtime — make the HTTP/API call with an HTTP Request node ' + + 'and process its output in this node instead.', + lintTarget: 'pythonCode', + nodeName: options.nodeName, + parameterPath: 'pythonCode', + }), + ); + } + + return issues; +} diff --git a/packages/@n8n/workflow-sdk/src/lint/index.ts b/packages/@n8n/workflow-sdk/src/lint/index.ts new file mode 100644 index 00000000000..be671c39dda --- /dev/null +++ b/packages/@n8n/workflow-sdk/src/lint/index.ts @@ -0,0 +1,26 @@ +/** + * Source lint for workflow SDK TypeScript files. + * + * Public entry: `lintWorkflowSource`. Prefer importing from this barrel + * (`../lint`) rather than deep paths. + */ + +export type { LintTarget, SourceLintIssue } from './types'; +export { lintIssue } from './types'; +export { walkAst, dedupeSourceLintIssues } from './ast-walk'; +export { lintWorkflowSource } from './lint-workflow-source'; +export { + prepareSourceForLint, + lintWorkflowSdkSource, + lintWorkflowSdkAst, +} from './sdk/workflow-sdk-lint'; +export { lintJsCode, hasNestedTemplateLiterals } from './code-node/js'; +export { lintPythonCode } from './code-node/python'; +export { + extractEmbeddedCodeSnippets, + extractEmbeddedCodeSnippetsFromSource, + isEmbeddedCodePropertyValue, + buildParentMap, + type CodeExecutionMode, + type EmbeddedCodeSnippet, +} from './code-node/extract-snippets'; diff --git a/packages/@n8n/workflow-sdk/src/lint/lint-workflow-source.test.ts b/packages/@n8n/workflow-sdk/src/lint/lint-workflow-source.test.ts new file mode 100644 index 00000000000..4493ae98234 --- /dev/null +++ b/packages/@n8n/workflow-sdk/src/lint/lint-workflow-source.test.ts @@ -0,0 +1,61 @@ +import { lintWorkflowSource } from './lint-workflow-source'; + +describe('lintWorkflowSource embedded code', () => { + it('lints jsCode separately from SDK rules', () => { + const source = ` +const transform = node({ + type: 'n8n-nodes-base.code', + version: 2, + config: { + name: 'Transform', + parameters: { + mode: 'runOnceForEachItem', + jsCode: 'return $input.all();', + }, + }, +}); +export default workflow('id', 'name').add(transform); +`; + const issues = lintWorkflowSource(source); + expect(issues.some((i) => i.lintTarget === 'sdk' && i.message.includes("'.map()'"))).toBe( + false, + ); + expect(issues).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + lintTarget: 'jsCode', + code: 'CODE_MODE_API_MISUSE', + }), + ]), + ); + }); + + it('lints pythonCode separately', () => { + const source = ` +const transform = node({ + type: 'n8n-nodes-base.code', + version: 2, + config: { + name: 'Transform', + parameters: { + language: 'pythonNative', + pythonCode: 'import requests\\nrequests.get("https://example.com")', + }, + }, +}); +export default workflow('id', 'name').add(transform); +`; + const issues = lintWorkflowSource(source); + expect(issues).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + lintTarget: 'pythonCode', + code: 'CODE_NODE_NETWORK_CALL', + }), + ]), + ); + expect(issues.every((i) => i.lintTarget !== 'sdk' || !i.message.includes('requests'))).toBe( + true, + ); + }); +}); diff --git a/packages/@n8n/workflow-sdk/src/lint/lint-workflow-source.ts b/packages/@n8n/workflow-sdk/src/lint/lint-workflow-source.ts new file mode 100644 index 00000000000..dd4609b10e5 --- /dev/null +++ b/packages/@n8n/workflow-sdk/src/lint/lint-workflow-source.ts @@ -0,0 +1,60 @@ +import { parseSDKCode } from '../ast-interpreter'; +import { dedupeSourceLintIssues } from './ast-walk'; +import { buildParentMap, extractEmbeddedCodeSnippets } from './code-node/extract-snippets'; +import { lintJsCode } from './code-node/js'; +import { lintPythonCode } from './code-node/python'; +import { lintWorkflowSdkAst, prepareSourceForLint } from './sdk/workflow-sdk-lint'; +import { lintIssue, type SourceLintIssue } from './types'; + +/** + * Run SDK, embedded JavaScript, and embedded Python linters on a workflow source file. + * Parses the prepared source once and shares the AST across passes. + */ +export function lintWorkflowSource(source: string): SourceLintIssue[] { + const { code, asConstMatches } = prepareSourceForLint(source); + + let ast; + try { + ast = parseSDKCode(code); + } catch { + // Still surface `as const` findings when the file does not parse. + return dedupeSourceLintIssues( + asConstMatches.map((match) => + lintIssue({ + code: 'SDK_AS_CONST', + message: + '`as const` is TypeScript-only and the workflow parser cannot interpret it. Remove the assertion.', + line: match.line, + column: match.column + 1, + lintTarget: 'sdk' as const, + }), + ), + ); + } + + const sdkIssues = lintWorkflowSdkAst(ast, asConstMatches); + const parents = buildParentMap(ast); + const snippets = extractEmbeddedCodeSnippets(ast, code, parents); + + const embeddedIssues: SourceLintIssue[] = []; + for (const snippet of snippets) { + const base = { line: snippet.line, column: snippet.column }; + if (snippet.parameter === 'jsCode') { + embeddedIssues.push( + ...lintJsCode(snippet.code, { mode: snippet.mode }).map((issue) => ({ + ...issue, + ...base, + })), + ); + } else { + embeddedIssues.push( + ...lintPythonCode(snippet.code).map((issue) => ({ + ...issue, + ...base, + })), + ); + } + } + + return dedupeSourceLintIssues([...sdkIssues, ...embeddedIssues]); +} diff --git a/packages/@n8n/workflow-sdk/src/lint/sdk/workflow-sdk-lint.test.ts b/packages/@n8n/workflow-sdk/src/lint/sdk/workflow-sdk-lint.test.ts new file mode 100644 index 00000000000..577487982bb --- /dev/null +++ b/packages/@n8n/workflow-sdk/src/lint/sdk/workflow-sdk-lint.test.ts @@ -0,0 +1,218 @@ +import { lintWorkflowSdkSource, prepareSourceForLint } from './workflow-sdk-lint'; + +describe('lintWorkflowSdkSource', () => { + it('flags statements after export default', () => { + const source = ` +import { workflow, node, trigger, ifElse } from '@n8n/workflow-sdk'; +const start = trigger({ type: 'n8n-nodes-base.manualTrigger', version: 1, config: { name: 'Start' } }); +const branch = ifElse({ version: 2.2, config: { name: 'Check', parameters: {} } }); +const yes = node({ type: 'n8n-nodes-base.noOp', version: 1, config: { name: 'Yes' } }); +export default workflow('id', 'name').add(start).to(branch); +branch.onTrue(yes); +`; + const codes = lintWorkflowSdkSource(source).map((i) => i.code); + expect(codes).toContain('SDK_CODE_AFTER_EXPORT_DEFAULT'); + expect(lintWorkflowSdkSource(source).every((i) => i.lintTarget === 'sdk')).toBe(true); + }); + + it('flags repeated onFalse overwrites on the same IF identifier', () => { + const issues = lintWorkflowSdkSource(` +const start = trigger({ type: 'n8n-nodes-base.manualTrigger', version: 1, config: { name: 'Start' } }); +const a = node({ type: 'n8n-nodes-base.noOp', version: 1, config: { name: 'A' } }); +const b = node({ type: 'n8n-nodes-base.noOp', version: 1, config: { name: 'B' } }); +const c = node({ type: 'n8n-nodes-base.noOp', version: 1, config: { name: 'C' } }); +const branch = ifElse({ version: 2.2, config: { name: 'Check', parameters: {} } }); +branch.onFalse(b); +branch.onFalse(c); +export default workflow('id', 'name').add(start).to(branch).onTrue(a); +`); + expect(issues.map((i) => i.code)).toContain('SDK_REPEATED_BRANCH_WIRING'); + }); + + it('does not flag fluent onTrue/onFalse across different IF nodes on the workflow chain', () => { + const source = ` +const start = trigger({ type: 'n8n-nodes-base.manualTrigger', version: 1, config: { name: 'Start' } }); +const a = node({ type: 'n8n-nodes-base.noOp', version: 1, config: { name: 'A' } }); +const b = node({ type: 'n8n-nodes-base.noOp', version: 1, config: { name: 'B' } }); +const c = node({ type: 'n8n-nodes-base.noOp', version: 1, config: { name: 'C' } }); +const d = node({ type: 'n8n-nodes-base.noOp', version: 1, config: { name: 'D' } }); +const if1 = ifElse({ version: 2.2, config: { name: 'Check1', parameters: {} } }); +const if2 = ifElse({ version: 2.2, config: { name: 'Check2', parameters: {} } }); +export default workflow('id', 'name') + .add(start) + .to(if1) + .onTrue(a) + .onFalse(b) + .to(if2) + .onTrue(c) + .onFalse(d); +`; + expect(lintWorkflowSdkSource(source).map((i) => i.code)).not.toContain( + 'SDK_REPEATED_BRANCH_WIRING', + ); + }); + + it('flags as const', () => { + const source = ` +const mode = 'list' as const; +export default workflow('id', 'name').add(start); +`; + expect(lintWorkflowSdkSource(source).map((i) => i.code)).toContain('SDK_AS_CONST'); + }); + + it('does not flag as const inside jsCode template literals', () => { + const source = ` +const transform = node({ + type: 'n8n-nodes-base.code', + version: 2, + config: { + name: 'Transform', + parameters: { + jsCode: \` +// cast the value as const before returning +return $input.all(); +\`.trim(), + }, + }, +}); +export default workflow('id', 'name').add(transform); +`; + expect(lintWorkflowSdkSource(source).map((i) => i.code)).not.toContain('SDK_AS_CONST'); + }); + + it('does not flag as const inside string literals', () => { + const source = ` +const note = 'avoid as const in workflow files'; +export default workflow('id', 'name').add(start); +`; + expect(lintWorkflowSdkSource(source).map((i) => i.code)).not.toContain('SDK_AS_CONST'); + }); + + it('flags placeholder wrapped in expr', () => { + const source = ` +const n = node({ + type: 'n8n-nodes-base.httpRequest', + version: 4.3, + config: { name: 'Fetch', parameters: { url: expr(placeholder('API URL')) } }, +}); +export default workflow('id', 'name').add(n); +`; + expect(lintWorkflowSdkSource(source).map((i) => i.code)).toContain('SDK_PLACEHOLDER_WRAPPED'); + }); + + it('flags sticky() calls', () => { + const source = ` +const note = sticky('## Notes'); +const start = trigger({ type: 'n8n-nodes-base.manualTrigger', version: 1, config: { name: 'Start' } }); +export default workflow('id', 'name').add(start).add(note); +`; + expect(lintWorkflowSdkSource(source).map((i) => i.code)).toContain('SDK_UNSOLICITED_STICKY'); + }); + + it('flags .map() in builder code', () => { + const source = ` +const names = ['a', 'b'].map((x) => x); +export default workflow('id', 'name').add(start); +`; + const codes = lintWorkflowSdkSource(source).map((i) => i.code); + expect(codes).toContain('SDK_FORBIDDEN_CONSTRUCT'); + }); + + it('does not flag .map inside jsCode template literals', () => { + const source = ` +const transform = node({ + type: 'n8n-nodes-base.code', + version: 2, + config: { + name: 'Transform', + parameters: { + jsCode: \` +return $input.all().map(item => ({ json: item.json })); +\`.trim(), + }, + }, +}); +export default workflow('id', 'name').add(transform); +`; + const sdkMapIssues = lintWorkflowSdkSource(source).filter((i) => + i.message.includes("'.map()'"), + ); + expect(sdkMapIssues).toHaveLength(0); + }); + + it('does not flag .map inside expr string literals', () => { + const source = ` +const n = node({ + type: 'n8n-nodes-base.set', + version: 3.4, + config: { + name: 'Set', + parameters: { values: { string: [{ name: 'x', value: expr('={{ $json.items.map(i => i) }}') }] } }, + }, +}); +export default workflow('id', 'name').add(n); +`; + const mapIssues = lintWorkflowSdkSource(source).filter((i) => i.message.includes("'.map()'")); + expect(mapIssues).toHaveLength(0); + }); + + it('does not flag JSON.stringify in builder code', () => { + const source = ` +const payload = JSON.stringify({ a: 1 }); +export default workflow('id', 'name').add(start); +`; + expect(lintWorkflowSdkSource(source).map((i) => i.code)).not.toContain( + 'SDK_FORBIDDEN_CONSTRUCT', + ); + }); + + it('still flags raw JSON identifier access', () => { + const source = ` +const payload = JSON; +export default workflow('id', 'name').add(start); +`; + const issue = lintWorkflowSdkSource(source).find((i) => i.code === 'SDK_FORBIDDEN_CONSTRUCT'); + expect(issue).toMatchObject({ line: 2, column: 17 }); + }); + + it('reports 1-based column for as const', () => { + const source = "const mode = 'list' as const;\nexport default workflow('id', 'name');\n"; + const issue = lintWorkflowSdkSource(source).find((i) => i.code === 'SDK_AS_CONST'); + // prepareSourceForLint records 0-based column 20; issues expose 1-based 21. + expect(issue).toMatchObject({ line: 1, column: 21 }); + }); + + it('still flags JSON.parse', () => { + const source = ` +const payload = JSON.parse('{"x":42}'); +export default workflow('id', 'name').add(start); +`; + expect(lintWorkflowSdkSource(source).map((i) => i.code)).toContain('SDK_FORBIDDEN_CONSTRUCT'); + }); +}); + +describe('prepareSourceForLint', () => { + it('strips imports while preserving line numbers for later statements', () => { + const source = `import { + workflow, + node, +} from '@n8n/workflow-sdk'; +const mode = 'list' as const; +export default workflow('id', 'name'); +`; + const prepared = prepareSourceForLint(source); + expect(prepared.code.includes('import')).toBe(false); + expect(prepared.asConstMatches).toEqual([{ line: 5, column: 20 }]); + const asConstLine = prepared.code.split(/\r?\n/)[4]; + expect(asConstLine).toContain("const mode = 'list'"); + }); + + it('does not mangle ternaries when stripping type annotations', () => { + const source = ` +const value = flag ? 'a' : 'b'; +export default workflow('id', 'name'); +`; + const prepared = prepareSourceForLint(source); + expect(prepared.code).toContain("flag ? 'a' : 'b'"); + }); +}); diff --git a/packages/@n8n/workflow-sdk/src/lint/sdk/workflow-sdk-lint.ts b/packages/@n8n/workflow-sdk/src/lint/sdk/workflow-sdk-lint.ts new file mode 100644 index 00000000000..cf5f5436514 --- /dev/null +++ b/packages/@n8n/workflow-sdk/src/lint/sdk/workflow-sdk-lint.ts @@ -0,0 +1,394 @@ +/** + * Source-level lint for workflow SDK TypeScript (builder code only). + * + * Does not inspect jsCode / pythonCode embedded in Code node configs — those + * are linted separately by code-node/js and code-node/python. + */ + +import type { CallExpression, MemberExpression, Node, Program } from 'estree'; + +import { + FORBIDDEN_NODE_TYPES, + DANGEROUS_GLOBALS, + getSafeJSONMethod, + parseSDKCode, +} from '../../ast-interpreter'; +import { dedupeSourceLintIssues, walkAst } from '../ast-walk'; +import { isEmbeddedCodePropertyValue } from '../code-node/extract-snippets'; +import { lintIssue, type SourceLintIssue } from '../types'; + +const NATIVE_ARRAY_METHODS = new Set([ + 'map', + 'join', + 'filter', + 'reduce', + 'forEach', + 'flatMap', + 'find', + 'some', + 'every', +]); + +const SDK_FLUENT_METHODS = new Set([ + 'to', + 'add', + 'onTrue', + 'onFalse', + 'onCase', + 'onDone', + 'onEachBatch', + 'onError', + 'input', + 'output', + 'settings', + 'update', + 'then', + 'group', +]); + +/** An `as const` occurrence in prepared source (1-based line, 0-based column). */ +export interface AsConstMatch { + line: number; + column: number; +} + +/** + * Strip imports and common TS-only syntax so acorn can parse agent source. + * Replacements preserve line count (and roughly column positions) so AST + * `loc` values still match the original file. + * + * `as const` matches are collected after the strips above run, so their + * coordinates share the AST's coordinate space — this lets callers tell a + * real assertion apart from text inside a string/template (jsCode, sticky). + */ +export function prepareSourceForLint(source: string): { + code: string; + asConstMatches: AsConstMatch[]; +} { + let code = source; + const blankSameLines = (match: string): string => '\n'.repeat((match.match(/\n/g) ?? []).length); + const spaces = (match: string): string => ' '.repeat(match.length); + + code = code.replace(/^\s*import\s[\s\S]*?from\s+['"][^'"]+['"];?\s*$/gm, blankSameLines); + code = code.replace(/^\s*import\s+type\s[\s\S]*?;?\s*$/gm, blankSameLines); + // Narrower than a blanket `: Type` strip — avoids mangling ternaries (`a ? b : c`). + // These two are the only non-length-preserving strips; everything below them + // shares coordinates with the collected matches and the parsed AST. + code = code.replace( + /\b((?:const|let|var)\s+[A-Za-z_$][\w$]*)\s*:\s*[A-Za-z_$][\w$.|<>[\]\s,&?]*(?=\s*=)/g, + '$1', + ); + code = code.replace( + /([,(]\s*[A-Za-z_$][\w$]*)\s*:\s*[A-Za-z_$][\w$.|<>[\]\s,&?]*(?=\s*[,)=])/g, + '$1', + ); + + const asConstMatches: AsConstMatch[] = []; + const asConstPattern = /\bas\s+const\b/g; + const lines = code.split(/\r?\n/); + for (let i = 0; i < lines.length; i++) { + asConstPattern.lastIndex = 0; + let match: RegExpExecArray | null; + while ((match = asConstPattern.exec(lines[i] ?? '')) !== null) { + asConstMatches.push({ line: i + 1, column: match.index }); + } + } + + code = code.replace(/\bas\s+const\b/g, spaces); + code = code.replace(/\bas\s+[A-Za-z_$][\w$.<>,\s|&[\]?]*/g, spaces); + code = code.replace(/\bsatisfies\s+[A-Za-z_$][\w$.|<>[\]\s,&?]*/g, spaces); + + return { code, asConstMatches }; +} + +/** 1-based line/column from an ESTree loc (Acorn columns are 0-based). */ +function locationOf(node: Node): { line?: number; column?: number } { + if (!node.loc) return {}; + return { line: node.loc.start.line, column: node.loc.start.column + 1 }; +} + +function isPlaceholderCall(node: CallExpression): boolean { + return node.callee.type === 'Identifier' && node.callee.name === 'placeholder'; +} + +function isExprCall(node: CallExpression): boolean { + return node.callee.type === 'Identifier' && node.callee.name === 'expr'; +} + +/** + * Direct receiver of a method call when it is a simple identifier + * (`branch.onTrue(...)`). Fluent chains on `workflow()` are ignored — those + * do not overwrite a previous branch target on the same IF node. + */ +function directReceiverName(member: MemberExpression): string | undefined { + if (member.object.type === 'Identifier') { + return member.object.name; + } + return undefined; +} + +interface SourceRange { + startLine: number; + startColumn: number; + endLine: number; + endColumn: number; +} + +/** + * Ranges covered by string literals and template literals. An `as const` + * match inside one is string content (jsCode snippets, sticky text), not the + * TS assertion SDK_AS_CONST targets. + */ +function stringContentRanges(ast: Program): SourceRange[] { + const ranges: SourceRange[] = []; + walkAst(ast, (node) => { + const isString = + node.type === 'TemplateLiteral' || + (node.type === 'Literal' && typeof node.value === 'string'); + if (!isString || !node.loc) return; + ranges.push({ + startLine: node.loc.start.line, + startColumn: node.loc.start.column, + endLine: node.loc.end.line, + endColumn: node.loc.end.column, + }); + }); + return ranges; +} + +function rangeContains(range: SourceRange, line: number, column: number): boolean { + if (line < range.startLine || line > range.endLine) return false; + if (line === range.startLine && column < range.startColumn) return false; + if (line === range.endLine && column >= range.endColumn) return false; + return true; +} + +/** Lint a prepared, parsed SDK AST (imports/TS already stripped). */ +export function lintWorkflowSdkAst( + ast: Program, + asConstMatches: AsConstMatch[] = [], +): SourceLintIssue[] { + const issues: SourceLintIssue[] = []; + + const stringRanges = stringContentRanges(ast); + for (const match of asConstMatches) { + if (stringRanges.some((range) => rangeContains(range, match.line, match.column))) continue; + issues.push( + lintIssue({ + code: 'SDK_AS_CONST', + message: + '`as const` is TypeScript-only and the workflow parser cannot interpret it. Remove the assertion.', + line: match.line, + column: match.column + 1, + lintTarget: 'sdk', + }), + ); + } + + const exportIndex = ast.body.findIndex((stmt) => stmt.type === 'ExportDefaultDeclaration'); + if (exportIndex >= 0) { + for (let i = exportIndex + 1; i < ast.body.length; i++) { + const stmt = ast.body[i]; + if (!stmt || stmt.type === 'EmptyStatement') continue; + issues.push( + lintIssue({ + code: 'SDK_CODE_AFTER_EXPORT_DEFAULT', + message: + 'Statement after `export default workflow(...)` never reaches the builder — nodes/wiring here are dropped. ' + + 'Compose all `.to()` / `.onTrue()` / `.onFalse()` / `.onCase()` inside the export default chain.', + ...locationOf(stmt), + lintTarget: 'sdk', + }), + ); + } + } + + const branchCounts = new Map(); + + walkAst( + ast, + (node, parent) => { + if (node.type === 'ImportDeclaration') return; + + const forbidden = FORBIDDEN_NODE_TYPES[node.type]; + if (forbidden) { + issues.push( + lintIssue({ + code: 'SDK_FORBIDDEN_CONSTRUCT', + message: forbidden, + ...locationOf(node), + lintTarget: 'sdk', + }), + ); + } + + if (node.type === 'Identifier' && DANGEROUS_GLOBALS.has(node.name)) { + const isPropertyName = + parent?.type === 'MemberExpression' && parent.property === node && !parent.computed; + const isObjectKey = parent?.type === 'Property' && parent.key === node; + // Mirrors the interpreter: safe global methods (e.g. JSON.stringify) are allowed. + const isSafeMethodObject = + parent?.type === 'MemberExpression' && + parent.object === node && + !parent.computed && + parent.property.type === 'Identifier' && + getSafeJSONMethod(node.name, parent.property.name) !== undefined; + if (!isPropertyName && !isObjectKey && !isSafeMethodObject) { + issues.push( + lintIssue({ + code: 'SDK_FORBIDDEN_CONSTRUCT', + message: `Global '${node.name}' is unavailable in SDK builder code. Move runtime logic to a Code node or expr().`, + ...locationOf(node), + lintTarget: 'sdk', + }), + ); + } + } + + if (node.type !== 'CallExpression') return; + const call = node; + + if (call.callee.type === 'Identifier' && call.callee.name === 'sticky') { + issues.push( + lintIssue({ + code: 'SDK_UNSOLICITED_STICKY', + message: + 'Do not add sticky() / stickyNote nodes unless the user explicitly asked for canvas notes. ' + + 'Put explanations in the chat reply instead.', + ...locationOf(call), + lintTarget: 'sdk', + }), + ); + } + + if ( + call.callee.type === 'MemberExpression' && + !call.callee.computed && + call.callee.property.type === 'Identifier' + ) { + const method = call.callee.property.name; + + if (method === 'onTrue' || method === 'onFalse') { + // Only count direct `ifNode.onTrue(...)` / `ifNode.onFalse(...)`. + // Fluent `workflow().to(if1).onTrue(...).to(if2).onTrue(...)` is fine. + const receiver = directReceiverName(call.callee); + if (receiver) { + const key = `${receiver}.${method}`; + const prev = branchCounts.get(key); + if (prev) { + prev.count += 1; + } else { + branchCounts.set(key, { count: 1, ...locationOf(call) }); + } + } + } + + if (!SDK_FLUENT_METHODS.has(method) && NATIVE_ARRAY_METHODS.has(method)) { + issues.push( + lintIssue({ + code: 'SDK_FORBIDDEN_CONSTRUCT', + message: + `'.${method}()' is not available on SDK builder objects. Build strings with template ` + + 'literals, or do transforms in a Code node / expr().', + ...locationOf(call), + lintTarget: 'sdk', + }), + ); + } + } + + if (isExprCall(call)) { + for (const arg of call.arguments) { + if (arg.type === 'SpreadElement') continue; + if (arg.type === 'CallExpression' && isPlaceholderCall(arg)) { + issues.push( + lintIssue({ + code: 'SDK_PLACEHOLDER_WRAPPED', + message: + "Do not wrap placeholder() in expr(). Use placeholder('hint') directly as the parameter value.", + ...locationOf(call), + lintTarget: 'sdk', + }), + ); + } + if (arg.type === 'TemplateLiteral') { + for (const expr of arg.expressions) { + if (expr.type === 'CallExpression' && isPlaceholderCall(expr)) { + issues.push( + lintIssue({ + code: 'SDK_PLACEHOLDER_WRAPPED', + message: + 'Do not embed placeholder() inside expr()/template strings. Use placeholder() as the direct parameter value.', + ...locationOf(call), + lintTarget: 'sdk', + }), + ); + } + } + } + if (arg.type === 'ArrayExpression') { + for (const el of arg.elements) { + if (el && el.type === 'CallExpression' && isPlaceholderCall(el)) { + issues.push( + lintIssue({ + code: 'SDK_PLACEHOLDER_WRAPPED', + message: + 'Do not wrap placeholder() in an array unless the node definition expects an array and placeholder is a direct element value with no expr() wrapper.', + ...locationOf(call), + lintTarget: 'sdk', + }), + ); + } + } + } + } + } + }, + { skipChildren: isEmbeddedCodePropertyValue }, + ); + + for (const [key, info] of branchCounts) { + if (info.count < 2) continue; + const [, method] = key.split('.'); + issues.push( + lintIssue({ + code: 'SDK_REPEATED_BRANCH_WIRING', + message: + `Repeated \`.${method}()\` (${info.count} times) — each call overwrites the previous target. ` + + 'Wire once on the workflow builder chain.', + line: info.line, + column: info.column, + lintTarget: 'sdk', + }), + ); + } + + return dedupeSourceLintIssues(issues); +} + +/** + * Lint workflow SDK builder source. Skips jsCode / pythonCode property values. + */ +export function lintWorkflowSdkSource(source: string): SourceLintIssue[] { + const { code, asConstMatches } = prepareSourceForLint(source); + + let ast: Program; + try { + ast = parseSDKCode(code); + } catch { + return dedupeSourceLintIssues( + asConstMatches.map((match) => + lintIssue({ + code: 'SDK_AS_CONST', + message: + '`as const` is TypeScript-only and the workflow parser cannot interpret it. Remove the assertion.', + line: match.line, + column: match.column + 1, + lintTarget: 'sdk' as const, + }), + ), + ); + } + + return lintWorkflowSdkAst(ast, asConstMatches); +} diff --git a/packages/@n8n/workflow-sdk/src/lint/types.ts b/packages/@n8n/workflow-sdk/src/lint/types.ts new file mode 100644 index 00000000000..c2052838471 --- /dev/null +++ b/packages/@n8n/workflow-sdk/src/lint/types.ts @@ -0,0 +1,28 @@ +import type { IssueSeverity } from '../validation/issue-severity'; + +export type LintTarget = 'sdk' | 'jsCode' | 'pythonCode'; + +/** + * Source-lint finding. Severity is set at the rule site — today all lint + * rules are informational (do not block save / CLI exit). + */ +export interface SourceLintIssue { + code: string; + message: string; + severity: Extract; + /** 1-based line in the workflow source file, when resolvable. */ + line?: number; + /** 1-based column in the workflow source file, when resolvable. */ + column?: number; + lintTarget: LintTarget; + /** Set when the issue comes from an embedded Code node snippet in SDK source. */ + nodeName?: string; + parameterPath?: 'jsCode' | 'pythonCode'; +} + +/** Build a source-lint issue with informational severity at the rule site. */ +export function lintIssue( + issue: Omit & { severity?: 'informational' }, +): SourceLintIssue { + return { severity: 'informational', ...issue }; +} diff --git a/packages/@n8n/workflow-sdk/src/prompts/sdk-reference/sdk-language.ts b/packages/@n8n/workflow-sdk/src/prompts/sdk-reference/sdk-language.ts index af678a51c3f..09ec815f9a2 100644 --- a/packages/@n8n/workflow-sdk/src/prompts/sdk-reference/sdk-language.ts +++ b/packages/@n8n/workflow-sdk/src/prompts/sdk-reference/sdk-language.ts @@ -101,7 +101,7 @@ export default workflow('id', 'My workflow') When editing an existing workflow, **keep the \`.group(...)\` calls intact** unless the change is specifically about grouping. -an invalid group is rejected on save, so these following rules MUST be followed when +An invalid group is rejected on save, so these following rules MUST be followed when creating or editing groups. Rules: diff --git a/packages/@n8n/workflow-sdk/src/validation.ts b/packages/@n8n/workflow-sdk/src/validation.ts index 6f8f43411eb..7aafc0aae40 100644 --- a/packages/@n8n/workflow-sdk/src/validation.ts +++ b/packages/@n8n/workflow-sdk/src/validation.ts @@ -8,6 +8,17 @@ export { type ValidationResult, type ValidationOptions, type ValidationErrorCode, + type IssueSeverity, + isInformationalIssue, + partitionValidationIssues, + validateWorkflowBuilder, + buildUncheckedNotes, + type ValidateWorkflowBuilderOptions, + type ValidateWorkflowBuilderResult, + type CollectedValidationIssue, } from './validation/index'; -export { validateNodeConfig, type SchemaValidationResult } from './validation/schema-validator'; +export { + validateNodeConfig, + type SchemaValidationResult, +} from './validation/node-parameter-schema/schema-validator'; diff --git a/packages/@n8n/workflow-sdk/src/validation/index.ts b/packages/@n8n/workflow-sdk/src/validation/index.ts index 421388783ea..5a3939d9922 100644 --- a/packages/@n8n/workflow-sdk/src/validation/index.ts +++ b/packages/@n8n/workflow-sdk/src/validation/index.ts @@ -1,1314 +1,47 @@ -import { isRecord } from '@n8n/utils/is-record'; -import get from 'lodash/get'; -import type { INodeTypes, IConnections as N8nIConnections, IDisplayOptions } from 'n8n-workflow'; -import { mapConnectionsByDestination } from 'n8n-workflow'; +/** + * Workflow validation public API. + * + * Prefer importing from this barrel (`../validation` or `@n8n/workflow-sdk`) + * rather than deep paths. + */ -import { matchesDisplayOptions } from './display-options'; -import type { DisplayOptions, DisplayOptionsContext } from './display-options'; -import { resolveMainInputCount } from './input-resolver'; -import { resolveMainOutputCount } from './output-resolver'; -import { validateNodeConfig } from './schema-validator'; -import { isStickyNoteType, isHttpRequestType } from '../constants/node-types'; -import type { WorkflowBuilder, WorkflowJSON } from '../types/base'; -import { containsPlaceholderMarker } from '../workflow-builder/string-utils'; +export { + validateWorkflow, + ValidationError, + ValidationWarning, + type ValidationResult, + type ValidationOptions, + type ValidationErrorCode, +} from './validate-workflow'; export { getSchemaBaseDirs, setSchemaBaseDirs, validateNodeConfig, type SchemaValidationResult, -} from './schema-validator'; - -/** - * Validation error codes - */ -export type ValidationErrorCode = - | 'NO_NODES' - | 'MISSING_TRIGGER' - | 'DISCONNECTED_NODE' - | 'MISSING_PARAMETER' - | 'INVALID_CONNECTION' - | 'CIRCULAR_REFERENCE' - | 'INVALID_EXPRESSION' - | 'AGENT_STATIC_PROMPT' - | 'AGENT_NO_SYSTEM_MESSAGE' - | 'HARDCODED_CREDENTIALS' - | 'SET_CREDENTIAL_FIELD' - | 'MERGE_SINGLE_INPUT' - | 'TOOL_NO_PARAMETERS' - | 'FROM_AI_IN_NON_TOOL' - | 'MISSING_EXPRESSION_PREFIX' - | 'INVALID_PARAMETER' - | 'INVALID_INPUT_INDEX' - | 'INVALID_OUTPUT_INDEX' - | 'SUBNODE_NOT_CONNECTED' - | 'SUBNODE_PARAMETER_MISMATCH' - | 'UNSUPPORTED_SUBNODE_INPUT' - | 'MISSING_REQUIRED_INPUT' - | 'INVALID_OUTPUT_FOR_MODE' - | 'SWITCH_NO_OUTPUT_CONNECTIONS' - | 'SWITCH_FALLBACK_OUTPUT_DISABLED' - | 'MAX_NODES_EXCEEDED' - | 'INVALID_EXPRESSION_PATH' - | 'PARTIAL_EXPRESSION_PATH' - | 'INVALID_DATE_METHOD' - | 'UNKNOWN_CONFIG_KEY'; - -/** - * Validation error class - */ -export class ValidationError { - readonly code: ValidationErrorCode; - readonly message: string; - readonly nodeName?: string; - readonly parameterName?: string; - /** Violation level for evaluation scoring (defaults to 'minor' if not set) */ - readonly violationLevel?: 'critical' | 'major' | 'minor'; - - constructor( - code: ValidationErrorCode, - message: string, - nodeName?: string, - parameterName?: string, - violationLevel?: 'critical' | 'major' | 'minor', - ) { - this.code = code; - this.message = message; - this.nodeName = nodeName; - this.parameterName = parameterName; - this.violationLevel = violationLevel; - } -} - -/** - * Validation warning class (non-fatal issues) - */ -export class ValidationWarning { - readonly code: ValidationErrorCode; - readonly message: string; - readonly nodeName?: string; - readonly parameterPath?: string; - readonly originalName?: string; - /** Violation level for evaluation scoring (defaults to 'minor' if not set) */ - readonly violationLevel?: 'critical' | 'major' | 'minor'; - - constructor( - code: ValidationErrorCode, - message: string, - nodeName?: string, - parameterPath?: string, - originalName?: string, - violationLevel?: 'critical' | 'major' | 'minor', - ) { - this.code = code; - this.message = message; - this.nodeName = nodeName; - this.parameterPath = parameterPath; - this.originalName = originalName; - this.violationLevel = violationLevel; - } -} - -/** - * Validation result - */ -export interface ValidationResult { - /** Whether the workflow is valid */ - valid: boolean; - /** Fatal errors that prevent the workflow from running */ - errors: ValidationError[]; - /** Warnings about potential issues */ - warnings: ValidationWarning[]; -} - -/** - * Validation options - */ -export interface ValidationOptions { - /** Enable strict mode with more warnings */ - strictMode?: boolean; - /** Skip disconnected node warnings */ - allowDisconnectedNodes?: boolean; - /** Skip trigger requirement */ - allowNoTrigger?: boolean; - /** Enable/disable Zod schema validation (default: true) */ - validateSchema?: boolean; - /** Optional node types provider for dynamic input index validation */ - nodeTypesProvider?: INodeTypes; -} - -/** - * Check if a node type is a trigger - */ -function isTriggerNode(type: string): boolean { - return ( - type.includes('Trigger') || - type.includes('trigger') || - type.includes('Webhook') || - type.includes('webhook') || - type.includes('Schedule') || - type.includes('schedule') || - type.includes('Poll') || - type.includes('poll') - ); -} - -/** - * AI connection types used by subnodes to connect to their parent nodes - */ -const AI_CONNECTION_TYPES = [ - 'ai_languageModel', - 'ai_memory', - 'ai_tool', - 'ai_outputParser', - 'ai_embedding', - 'ai_vectorStore', - 'ai_retriever', - 'ai_document', - 'ai_textSplitter', - 'ai_reranker', -]; - -/** - * Mapping from AI connection type to subnodes field name - */ -const AI_CONNECTION_TO_SUBNODE_FIELD: Record = { - ai_languageModel: 'model', - ai_memory: 'memory', - ai_tool: 'tools', - ai_outputParser: 'outputParser', - ai_embedding: 'embedding', - ai_vectorStore: 'vectorStore', - ai_retriever: 'retriever', - ai_document: 'documentLoader', - ai_textSplitter: 'textSplitter', - ai_reranker: 'reranker', -}; - -/** - * AI connection types that should always be arrays in subnodes - */ -const AI_ARRAY_TYPES = new Set(['ai_tool']); - -interface NodeJSON { - id?: string; - name?: string; - type: string; - typeVersion?: number | string; - position?: [number, number]; - parameters?: Record; - onError?: string; -} - -/** - * Reconstruct subnodes object from AI connections in the workflow. - * When SDK code defines subnodes, they get serialized as separate nodes with AI connections. - * This function reverses that transformation for validation purposes. - */ -function reconstructSubnodesFromConnections( - targetNodeName: string, - json: WorkflowJSON, -): Record | undefined { - const subnodes: Record = {}; - const nodesByName = new Map(); - - // Build a map of node name -> node for quick lookup - for (const node of json.nodes) { - if (node.name) { - nodesByName.set(node.name, node); - } - } - - // Scan all nodes' connections to find AI connections TO this target node - for (const [sourceNodeName, nodeConnections] of Object.entries(json.connections)) { - for (const connType of AI_CONNECTION_TYPES) { - const aiConns = nodeConnections[connType as keyof typeof nodeConnections]; - if (!aiConns || !Array.isArray(aiConns)) continue; - - for (const outputs of aiConns) { - if (!outputs) continue; - for (const conn of outputs) { - if (conn.node === targetNodeName) { - // Found an AI connection to our target node - const subnodeField = AI_CONNECTION_TO_SUBNODE_FIELD[connType]; - if (!subnodeField) continue; - - const sourceNode = nodesByName.get(sourceNodeName); - if (!sourceNode) continue; - - // Build a minimal subnode config for validation - const subnodeConfig = { - type: sourceNode.type, - version: sourceNode.typeVersion, - parameters: sourceNode.parameters ?? {}, - }; - - // For array types (like tools), collect into array - if (AI_ARRAY_TYPES.has(connType)) { - const existing = subnodes[subnodeField]; - if (Array.isArray(existing)) { - existing.push(subnodeConfig); - } else { - subnodes[subnodeField] = [subnodeConfig]; - } - } else { - // For single-value types, just set directly - subnodes[subnodeField] = subnodeConfig; - } - } - } - } - } - } - - // Return undefined if no subnodes were found - return Object.keys(subnodes).length > 0 ? subnodes : undefined; -} - -/** - * Check if a node has AI connections to a parent node (making it a connected subnode) - */ -function hasAiConnectionToParent(nodeName: string, json: WorkflowJSON): boolean { - const nodeConnections = json.connections[nodeName]; - if (!nodeConnections) return false; - - for (const connType of AI_CONNECTION_TYPES) { - const aiConns = nodeConnections[connType as keyof typeof nodeConnections]; - if (aiConns && Array.isArray(aiConns)) { - for (const outputs of aiConns) { - if (outputs && outputs.length > 0) { - return true; // Has AI connection to parent - } - } - } - } - return false; -} - -/** - * Check if a node is used as a tool (connected via ai_tool connection type) - */ -function isToolSubnode(nodeName: string, json: WorkflowJSON): boolean { - const nodeConnections = json.connections[nodeName]; - if (!nodeConnections) return false; - - const toolConns = nodeConnections.ai_tool as unknown as Array>; - if (toolConns && Array.isArray(toolConns)) { - for (const outputs of toolConns) { - if (outputs && outputs.length > 0) { - return true; // Connected as a tool - } - } - } - return false; -} - -/** - * Find disconnected nodes (nodes that don't receive input from any other node) - */ -function findDisconnectedNodes(json: WorkflowJSON): string[] { - const hasIncoming = new Set(); - - // Find all nodes that have incoming connections - for (const [_sourceName, nodeConnections] of Object.entries(json.connections)) { - if (nodeConnections.main) { - for (const outputs of nodeConnections.main) { - if (outputs) { - for (const connection of outputs) { - hasIncoming.add(connection.node); - } - } - } - } - } - - // Find nodes without incoming connections (excluding triggers, sticky notes, and connected subnodes) - const disconnected: string[] = []; - for (const node of json.nodes) { - // Skip nodes without names (e.g., some sticky notes) - if (!node.name) continue; - - // Skip if node has incoming connection - if (hasIncoming.has(node.name)) continue; - - // Skip trigger nodes - they don't need incoming connections - if (isTriggerNode(node.type)) continue; - - // Skip sticky notes - they don't participate in data flow - if (isStickyNoteType(node.type)) continue; - - // Skip subnodes - they connect TO their parent via AI connections - if (hasAiConnectionToParent(node.name, json)) continue; - - disconnected.push(node.name); - } - - return disconnected; -} - -/** - * Validate a workflow - * - * Checks for: - * - Presence of trigger node (warning if missing) - * - Disconnected nodes (warning) - * - Required parameters (in strict mode) - * - * @param workflow - The workflow to validate (WorkflowBuilder or WorkflowJSON) - * @param options - Validation options - * @returns Validation result with errors and warnings - * - * @example - * ```typescript - * const wf = workflow('id', 'Test').add(trigger(...)).to(node(...)); - * const result = validateWorkflow(wf); - * - * if (!result.valid) { - * console.error('Errors:', result.errors); - * } - * if (result.warnings.length > 0) { - * console.warn('Warnings:', result.warnings); - * } - * ``` - */ -export function validateWorkflow( - workflowOrJson: WorkflowBuilder | WorkflowJSON, - options: ValidationOptions = {}, -): ValidationResult { - // Get JSON representation - const json: WorkflowJSON = 'toJSON' in workflowOrJson ? workflowOrJson.toJSON() : workflowOrJson; - - const errors: ValidationError[] = []; - const warnings: ValidationWarning[] = []; - - // Check for trigger node - if (!options.allowNoTrigger) { - const hasTrigger = json.nodes.some((node) => isTriggerNode(node.type)); - if (!hasTrigger) { - warnings.push( - new ValidationWarning( - 'MISSING_TRIGGER', - 'Workflow has no trigger node. It will need to be started manually.', - ), - ); - } - } - - // Check for disconnected nodes - if (!options.allowDisconnectedNodes) { - const disconnected = findDisconnectedNodes(json); - for (const nodeName of disconnected) { - warnings.push( - new ValidationWarning( - 'DISCONNECTED_NODE', - `Node '${nodeName}' is not connected to any input. It will not receive data.`, - nodeName, - ), - ); - } - } - - // Strict mode validations - if (options.strictMode) { - // Check for potentially missing required parameters - for (const node of json.nodes) { - // HTTP Request should have a URL - if (isHttpRequestType(node.type)) { - if (!node.parameters?.url && !node.parameters?.requestUrl) { - warnings.push( - new ValidationWarning( - 'MISSING_PARAMETER', - `HTTP Request node '${node.name}' may be missing URL parameter`, - node.name, - ), - ); - } - } - } - } - - // Schema validation (enabled by default) - if (options.validateSchema !== false) { - for (const node of json.nodes) { - // Get version number (handle both number and string versions) - const version = - typeof node.typeVersion === 'string' - ? parseFloat(node.typeVersion) - : (node.typeVersion ?? 1); - - // Build config object for validation - const config: { parameters?: unknown; subnodes?: unknown } = {}; - if (node.parameters !== undefined) { - config.parameters = node.parameters; - } - // Include subnodes if present (for AI nodes) - const nodeWithSubnodes = node as typeof node & { subnodes?: unknown }; - if (nodeWithSubnodes.subnodes !== undefined) { - config.subnodes = nodeWithSubnodes.subnodes; - } else if (node.name) { - // Try to reconstruct subnodes from AI connections in the workflow - // This handles the case where subnodes were serialized as separate nodes - const reconstructed = reconstructSubnodesFromConnections(node.name, json); - if (reconstructed) { - config.subnodes = reconstructed; - } - } - - // Determine if this node is being used as a tool (for @tool displayOptions) - // A node is a tool if it's connected via ai_tool connection type - const isToolNode = node.name ? isToolSubnode(node.name, json) : false; - - const schemaResult = validateNodeConfig(node.type, version, config, { isToolNode }); - - if (!schemaResult.valid) { - for (const error of schemaResult.errors) { - let message = error.message; - - // Enhance subnode errors with valid options when nodeTypesProvider is available - if ( - error.path === 'subnodes' && - message.includes('Unknown field') && - options.nodeTypesProvider - ) { - const nodeType = options.nodeTypesProvider.getByNameAndVersion(node.type, version); - const validInputs = nodeType?.description?.builderHint?.inputs; - if (validInputs) { - const validSubnodes = Object.keys(validInputs) - .map((k) => AI_CONNECTION_TO_SUBNODE_FIELD[k]) - .filter(Boolean); - if (validSubnodes.length > 0) { - // Transform message from "Unknown field(s) at "subnodes": "x", "y"." - // to "Invalid subnode(s) "x", "y". This node only accepts: a, b." - message = message.replace( - /Unknown field\(s\) at "subnodes": (.+)\./, - `Invalid subnode(s) $1. This node only accepts: ${validSubnodes.join(', ')}.`, - ); - } - } - } - - // Report as WARNING (non-blocking) to maintain backwards compatibility - warnings.push( - new ValidationWarning( - 'INVALID_PARAMETER', - `Node "${node.name}": ${message}`, - node.name, - ), - ); - } - } - } - } - - // Input index validation (only if provider is given) - if (options.nodeTypesProvider) { - checkNodeInputIndices(json, options.nodeTypesProvider, warnings); - // Validate that connections originate from output ports that actually exist - checkNodeOutputIndices(json, options.nodeTypesProvider, warnings); - // Validate subnode parameters match parent's displayOptions requirements - validateSubnodeParameters(json, options.nodeTypesProvider, warnings); - // Validate parent nodes actually support their connected AI input types - validateParentSupportsInputs(json, options.nodeTypesProvider, warnings); - // Validate required AI inputs on parent nodes are actually connected - validateRequiredInputsConnected(json, options.nodeTypesProvider, errors); - // Validate that emitted connection types are actually exposed by the source node's mode - validateOutputUsage(json, options.nodeTypesProvider, warnings); - // Reject placeholder() in slots that opt out via builderHint.placeholderSupported === false - validatePlaceholderSlots(json, options.nodeTypesProvider, errors); - } - - // Switch fallback output validation does not need node metadata. It is derived from - // the Switch node's dynamic output contract in rules mode. - validateSwitchHasOutgoingConnections(json, warnings); - validateSwitchFallbackOutputConnections(json, warnings); - - // Merge node input-count consistency - checkMergeNodeInputCount(json, warnings); - - return { - valid: errors.length === 0, - errors, - warnings, - }; -} - -/** - * Validate that the Merge node's `numberInputs` parameter is consistent with - * the input indices actually used by incoming connections. - * - * The Merge node has expression-based inputs (count derived from - * `numberInputs`, default 2), so checkNodeInputIndices can't resolve the count - * statically. Without this check, a workflow with three branches wired into a - * Merge node that still has `numberInputs=2` passes validation and silently - * drops the third branch at runtime. - */ -function checkMergeNodeInputCount(json: WorkflowJSON, warnings: ValidationWarning[]): void { - const connectionsByDest = mapConnectionsByDestination( - json.connections as unknown as N8nIConnections, - ); - - for (const node of json.nodes) { - if (!node.name) continue; - if (node.type !== 'n8n-nodes-base.merge') continue; - - const numberInputsParam = node.parameters?.numberInputs; - const declaredInputs = typeof numberInputsParam === 'number' ? numberInputsParam : 2; - - const incomingMain = connectionsByDest[node.name]?.main; - if (!incomingMain) continue; - - let maxConnectedIndex = -1; - for (let i = 0; i < incomingMain.length; i++) { - const slot = incomingMain[i]; - if (Array.isArray(slot) && slot.length > 0) { - maxConnectedIndex = i; - } - } - - if (maxConnectedIndex >= declaredInputs) { - warnings.push( - new ValidationWarning( - 'INVALID_INPUT_INDEX', - `Merge node '${node.name}' has a connection to input index ${maxConnectedIndex} but 'numberInputs' is ${declaredInputs}. Set 'numberInputs' to ${maxConnectedIndex + 1} so every branch is accepted.`, - node.name, - 'numberInputs', - undefined, - 'major', - ), - ); - } - } -} - -/** - * Mapping from AI connection type to SDK function name (for error messages) - */ -const AI_CONNECTION_TO_SDK_FUNCTION: Record = { - ai_languageModel: 'languageModel()', - ai_memory: 'memory()', - ai_tool: 'tool()', - ai_outputParser: 'outputParser()', - ai_embedding: 'embeddings()', - ai_vectorStore: 'vectorStore()', - ai_retriever: 'retriever()', - ai_document: 'documentLoader()', - ai_textSplitter: 'textSplitter()', - ai_reranker: 'reranker()', -}; - -/** - * Check if a subnode's parameters satisfy displayOptions conditions - */ -function checkDisplayOptionsMatch( - subnodeParams: Record, - displayOptions: IDisplayOptions, -): { - matches: boolean; - mismatches: Array<{ param: string; expected: unknown[]; actual: unknown }>; -} { - const mismatches: Array<{ param: string; expected: unknown[]; actual: unknown }> = []; - - if (!displayOptions.show) return { matches: true, mismatches }; - - for (const [paramName, expectedValues] of Object.entries(displayOptions.show)) { - if (!expectedValues) continue; // Skip undefined values - const actualValue = subnodeParams[paramName]; - if (!expectedValues.includes(actualValue as never)) { - mismatches.push({ - param: paramName, - expected: expectedValues as unknown[], - actual: actualValue, - }); - } - } - - return { matches: mismatches.length === 0, mismatches }; -} - -/** - * Validate that subnodes connected to parent nodes have parameters - * matching the displayOptions conditions in builderHint.inputs. - * - * For example, if an Agent's ai_tool input has displayOptions.show = { mode: ['retrieve-as-tool'] }, - * then any node connected via ai_tool must have mode='retrieve-as-tool'. - */ -function validateSubnodeParameters( - json: WorkflowJSON, - nodeTypesProvider: INodeTypes, - warnings: ValidationWarning[], -): void { - // Build a map of node name -> node for quick lookup - const nodesByName = new Map(); - for (const node of json.nodes) { - if (node.name) { - nodesByName.set(node.name, node); - } - } - - // Invert connections to find incoming connections by destination - // Cast to n8n-workflow IConnections since our local type has string for connection type - const connectionsByDest = mapConnectionsByDestination( - json.connections as unknown as N8nIConnections, - ); - - // Check each node that might be a parent with AI inputs - for (const parentNode of json.nodes) { - if (!parentNode.name) continue; - - // Try to get the node type to check for builderHint.inputs - const parentNodeType = nodeTypesProvider.getByNameAndVersion( - parentNode.type, - typeof parentNode.typeVersion === 'string' - ? parseFloat(parentNode.typeVersion) - : (parentNode.typeVersion ?? 1), - ); - const builderHintInputs = parentNodeType?.description?.builderHint?.inputs; - if (!builderHintInputs) continue; - - // For each AI input type the parent accepts - for (const [connectionType, inputConfig] of Object.entries(builderHintInputs)) { - if (!connectionType.startsWith('ai_')) continue; - if (!inputConfig?.displayOptions?.show) continue; - - // Find subnodes connected via this type - const incomingConnections = connectionsByDest[parentNode.name]?.[connectionType]; - if (!incomingConnections) continue; - - for (const connList of incomingConnections) { - if (!connList) continue; - for (const conn of connList) { - const subnodeName = conn.node; - const subnode = nodesByName.get(subnodeName); - if (!subnode?.parameters) continue; - - // Check if subnode params match displayOptions conditions - const { matches, mismatches } = checkDisplayOptionsMatch( - subnode.parameters, - inputConfig.displayOptions, - ); - - if (!matches) { - // `displayOptions` on `builderHint.inputs[type]` can describe - // either subnode-relative params (e.g. ai_vectorStore wants - // vector-store mode='retrieve-as-tool') or parent-relative - // params (e.g. ai_memory wants chatTrigger mode='hostedChat'). - // If every mismatched param is absent from the subnode, those - // params don't belong to the subnode at all — blaming it is a - // false-positive misdirect. Defer to validateParentSupportsInputs. - const subnodeOwnsAnyParam = mismatches.some((m) => m.actual !== undefined); - if (!subnodeOwnsAnyParam) continue; - - const sdkFn = AI_CONNECTION_TO_SDK_FUNCTION[connectionType] || connectionType; - - // Build error message with actual parameter names from displayOptions - const mismatchDetails = mismatches - .map( - (m) => - `${m.param}='${String(m.actual)}' (expected: ${m.expected.map((v) => `'${String(v)}'`).join(' or ')})`, - ) - .join(', '); - - warnings.push( - new ValidationWarning( - 'SUBNODE_PARAMETER_MISMATCH', - `'${subnodeName}' is connected to '${parentNode.name}' using ${sdkFn} but has ${mismatchDetails}. Update parameters to match the SDK function used.`, - subnodeName, - mismatches[0]?.param, - ), - ); - } - } - } - } - } -} - -/** - * Build a human-readable summary of which displayOptions conditions are not met. - */ -function buildConditionSummary( - displayOptions: IDisplayOptions, - parentParams: Record, -): string { - if (!displayOptions.show) return ''; - - const parts: string[] = []; - for (const [paramName, expectedValues] of Object.entries(displayOptions.show)) { - if (!expectedValues) continue; - // Use lodash get so nested paths (e.g. 'options.loadPreviousSession') - // resolve correctly — direct property access would read the literal - // dotted key and report 'undefined' even when the nested value is set. - const actual = get(parentParams, paramName); - const expectedStr = (expectedValues as unknown[]).map((v) => `'${String(v)}'`).join(' or '); - parts.push(`${paramName} should be ${expectedStr} (currently '${String(actual)}')`); - } - - return parts.length > 0 ? `Required: ${parts.join(', ')}.` : ''; -} - -/** - * Build a description of which parameters TRIGGERED a requirement. - * Used by `MISSING_REQUIRED_INPUT` where the displayOptions conditions are - * already satisfied (that's why the requirement applies) — the agent needs - * to know which params caused it so it can choose between satisfying the - * requirement or backing out by changing those params. - */ -function buildTriggeringConditionSummary( - displayOptions: IDisplayOptions, - parentParams: Record, -): string { - if (!displayOptions.show) return ''; - - const parts: string[] = []; - for (const [paramName, _expectedValues] of Object.entries(displayOptions.show)) { - const actual = get(parentParams, paramName); - parts.push(`${paramName}='${String(actual)}'`); - } - - return parts.join(', '); -} - -/** - * Validate that parent nodes actually support their connected AI input types - * based on the parent's own parameters and builderHint.inputs displayOptions. - * - * For example, if a vector store has mode='retrieve' but a documentLoader is connected, - * this produces a warning because ai_document requires mode='insert'. - */ -function validateParentSupportsInputs( - json: WorkflowJSON, - nodeTypesProvider: INodeTypes, - warnings: ValidationWarning[], -): void { - const nodesByName = new Map(); - for (const node of json.nodes) { - if (node.name) { - nodesByName.set(node.name, node); - } - } - - const connectionsByDest = mapConnectionsByDestination( - json.connections as unknown as N8nIConnections, - ); - - for (const parentNode of json.nodes) { - if (!parentNode.name) continue; - - const version = - typeof parentNode.typeVersion === 'string' - ? parseFloat(parentNode.typeVersion) - : (parentNode.typeVersion ?? 1); - - const parentNodeType = nodeTypesProvider.getByNameAndVersion(parentNode.type, version); - const builderHintInputs = parentNodeType?.description?.builderHint?.inputs; - if (!builderHintInputs) continue; - - const parentContext: DisplayOptionsContext = { - parameters: (parentNode.parameters ?? {}) as Record, - nodeVersion: version, - rootParameters: (parentNode.parameters ?? {}) as Record, - }; - - for (const [connectionType, inputConfig] of Object.entries(builderHintInputs)) { - if (!connectionType.startsWith('ai_')) continue; - if (!inputConfig?.displayOptions) continue; - - const parentSupportsInput = matchesDisplayOptions( - parentContext, - inputConfig.displayOptions as DisplayOptions, - ); - - if (parentSupportsInput) continue; - - const incomingConnections = connectionsByDest[parentNode.name]?.[connectionType]; - if (!incomingConnections) continue; - - for (const connList of incomingConnections) { - if (!connList) continue; - for (const conn of connList) { - const subnodeName = conn.node; - const subnodeField = AI_CONNECTION_TO_SUBNODE_FIELD[connectionType] || connectionType; - const conditionDetails = buildConditionSummary( - inputConfig.displayOptions, - (parentNode.parameters ?? {}) as Record, - ); - - warnings.push( - new ValidationWarning( - 'UNSUPPORTED_SUBNODE_INPUT', - `'${parentNode.name}' has a ${subnodeField} subnode ('${subnodeName}') connected, but its current configuration does not accept one. ${conditionDetails} These parameters must be set on '${parentNode.name}' itself, NOT on the ${subnodeField} subnode. Alternatively, remove the ${subnodeField} connection if this capability isn't needed.`, - parentNode.name, - undefined, - undefined, - 'major', - ), - ); - } - } - } - } -} - -/** - * Validate that required AI inputs declared in a parent node's builderHint.inputs - * are actually connected. - * - * For each parent node with a builderHint.inputs entry that has `required: true`, - * check whether its displayOptions (if any) match the parent's current parameters; - * if so, require that a connection of that AI type terminates at the parent. - * Emits a fatal error when the connection is missing — without it, the workflow - * silently passes validation but breaks at runtime (see INS-136: chat trigger - * with `loadPreviousSession: 'memory'` but no memory subnode connected). - */ -function validateRequiredInputsConnected( - json: WorkflowJSON, - nodeTypesProvider: INodeTypes, - errors: ValidationError[], -): void { - const connectionsByDest = mapConnectionsByDestination( - json.connections as unknown as N8nIConnections, - ); - - for (const parentNode of json.nodes) { - if (!parentNode.name) continue; - - const version = - typeof parentNode.typeVersion === 'string' - ? parseFloat(parentNode.typeVersion) - : (parentNode.typeVersion ?? 1); - - const parentNodeType = nodeTypesProvider.getByNameAndVersion(parentNode.type, version); - const builderHintInputs = parentNodeType?.description?.builderHint?.inputs; - if (!builderHintInputs) continue; - - const parentContext: DisplayOptionsContext = { - parameters: (parentNode.parameters ?? {}) as Record, - nodeVersion: version, - rootParameters: (parentNode.parameters ?? {}) as Record, - }; - - for (const [connectionType, inputConfig] of Object.entries(builderHintInputs)) { - if (!connectionType.startsWith('ai_')) continue; - if (!inputConfig?.required) continue; - - if (inputConfig.displayOptions) { - const conditionsMet = matchesDisplayOptions( - parentContext, - inputConfig.displayOptions as DisplayOptions, - ); - if (!conditionsMet) continue; - } - - const incoming = connectionsByDest[parentNode.name]?.[connectionType]; - const hasConnection = - Array.isArray(incoming) && incoming.some((slot) => Array.isArray(slot) && slot.length > 0); - if (hasConnection) continue; - - const subnodeField = AI_CONNECTION_TO_SUBNODE_FIELD[connectionType] || connectionType; - const triggerDetails = inputConfig.displayOptions - ? ` (triggered by ${buildTriggeringConditionSummary( - inputConfig.displayOptions, - (parentNode.parameters ?? {}) as Record, - )})` - : ''; - const alternative = inputConfig.displayOptions - ? ` Either connect a ${subnodeField} subnode, or change those parameters to remove the requirement.` - : ''; - - errors.push( - new ValidationError( - 'MISSING_REQUIRED_INPUT', - `'${parentNode.name}' requires a ${subnodeField} subnode connected to its ${connectionType} input${triggerDetails}, but none is connected.${alternative}`, - parentNode.name, - undefined, - 'major', - ), - ); - } - } -} - -/** - * Render an outgoing connection type as the SDK syntax that produces it, so warning - * messages speak the LLM agent's vocabulary instead of raw `main` / `ai_*` types. - * - * `target` flips the phrasing between describing the wiring already used by the source - * (e.g. `wired with .to()`) and describing where the source SHOULD attach instead - * (e.g. `subnodes.tools`). - */ -function describeOutputWiring(connectionType: string, target = false): string { - if (connectionType === 'main') return target ? '.to(...)' : 'wired with .to()'; - const field = AI_CONNECTION_TO_SUBNODE_FIELD[connectionType]; - if (field) return target ? `subnodes.${field}` : `attached as subnodes.${field}`; - return target ? connectionType : `connected via ${connectionType}`; -} - -/** - * Return the connection type from `outputsHint` whose displayOptions match the source - * node's current parameters — i.e. the output the node actually exposes given how it's - * configured. Used to suggest the correct wiring fix in `INVALID_OUTPUT_FOR_MODE`. - */ -function findEnabledAlternativeOutput( - outputsHint: Record, - ctx: DisplayOptionsContext, - excludeType: string, -): string | undefined { - for (const [type, cfg] of Object.entries(outputsHint)) { - if (type === excludeType) continue; - if (!cfg) continue; - if (cfg.displayOptions && !matchesDisplayOptions(ctx, cfg.displayOptions as DisplayOptions)) { - continue; - } - return type; - } - return undefined; -} - -/** - * Validate that connections leaving a node use connection types the node's current - * parameters actually expose. Driven by `builderHint.outputs` declared on the source node. - * - * Example: a vector store in `mode: 'retrieve'` exposes only `ai_vectorStore`. If the workflow - * has a `main` connection out of that node, this emits `INVALID_OUTPUT_FOR_MODE` because - * `builderHint.outputs.main.displayOptions` requires `mode` ∈ ['insert', 'load', 'update']. - */ -function validateOutputUsage( - json: WorkflowJSON, - nodeTypesProvider: INodeTypes, - warnings: ValidationWarning[], -): void { - for (const sourceNode of json.nodes) { - if (!sourceNode.name) continue; - - const outgoing = json.connections[sourceNode.name]; - if (!outgoing) continue; - - const version = - typeof sourceNode.typeVersion === 'string' - ? parseFloat(sourceNode.typeVersion) - : (sourceNode.typeVersion ?? 1); - - const nodeType = nodeTypesProvider.getByNameAndVersion(sourceNode.type, version); - const outputsHint = nodeType?.description?.builderHint?.outputs; - if (!outputsHint) continue; - - const ctx: DisplayOptionsContext = { - parameters: (sourceNode.parameters ?? {}) as Record, - nodeVersion: version, - rootParameters: (sourceNode.parameters ?? {}) as Record, - }; - - for (const [connectionType, cfg] of Object.entries(outputsHint)) { - // No displayOptions => assume the node always emits this connection type. - if (!cfg?.displayOptions) continue; - - const edges = outgoing[connectionType]; - if (!edges) continue; - const hasEdges = edges.some((slot) => Array.isArray(slot) && slot.length > 0); - if (!hasEdges) continue; - - const enabled = matchesDisplayOptions(ctx, cfg.displayOptions as DisplayOptions); - if (enabled) continue; - - const conditionDetails = buildConditionSummary( - cfg.displayOptions, - (sourceNode.parameters ?? {}) as Record, - ); - const usedWiring = describeOutputWiring(connectionType); - const enabledAlt = findEnabledAlternativeOutput(outputsHint, ctx, connectionType); - const altSuggestion = enabledAlt - ? ` To use this node as-is, attach it as ${describeOutputWiring(enabledAlt, true)} of a parent (it exposes ${enabledAlt} in this configuration).` - : ''; - - warnings.push( - new ValidationWarning( - 'INVALID_OUTPUT_FOR_MODE', - `'${sourceNode.name}' is ${usedWiring} but its current parameters disable that output. ${conditionDetails}${altSuggestion}`, - sourceNode.name, - undefined, - undefined, - 'major', - ), - ); - } - } -} - -function getSwitchRulesCount(parameters: Record | undefined): number { - const rules = parameters?.rules; - if (!isRecord(rules)) return 0; - - const values = rules.values; - if (Array.isArray(values)) return values.length; - - const legacyRules = rules.rules; - if (Array.isArray(legacyRules)) return legacyRules.length; - - return 0; -} - -function getSwitchFallbackOutput(parameters: Record | undefined): unknown { - const options = parameters?.options; - if (!isRecord(options)) return undefined; - - return options.fallbackOutput; -} - -function hasOutputConnections( - outputs: Array | null>, - outputIndex: number, -): boolean { - const output = outputs[outputIndex]; - return Array.isArray(output) && output.length > 0; -} - -function hasAnyMainOutputConnection(nodeConnections: unknown): boolean { - if (!isRecord(nodeConnections)) return false; - const main = nodeConnections.main; - if (!Array.isArray(main)) return false; - - return main.some((slot) => Array.isArray(slot) && slot.length > 0); -} - -/** - * A Switch with no outgoing branches is almost always an incomplete router: - * every matched item is dropped and downstream side effects never run. - */ -function validateSwitchHasOutgoingConnections( - json: WorkflowJSON, - warnings: ValidationWarning[], -): void { - for (const sourceNode of json.nodes) { - if (!sourceNode.name || sourceNode.type !== 'n8n-nodes-base.switch') continue; - if (hasAnyMainOutputConnection(json.connections[sourceNode.name])) continue; - - warnings.push( - new ValidationWarning( - 'SWITCH_NO_OUTPUT_CONNECTIONS', - `Switch node '${sourceNode.name}' has no outgoing connections. Connect at least one output branch to downstream action nodes, or remove the Switch node.`, - sourceNode.name, - 'connections', - undefined, - 'major', - ), - ); - } -} - -/** - * Validate that Switch fallback branches are only connected when the node - * actually exposes an extra fallback output. - */ -function validateSwitchFallbackOutputConnections( - json: WorkflowJSON, - warnings: ValidationWarning[], -): void { - for (const sourceNode of json.nodes) { - if (!sourceNode.name || sourceNode.type !== 'n8n-nodes-base.switch') continue; - - const mode = sourceNode.parameters?.mode; - if (mode !== undefined && mode !== 'rules') continue; - - const outgoing = json.connections[sourceNode.name]; - const mainOutputs = outgoing?.main; - if (!Array.isArray(mainOutputs)) continue; - - const rulesCount = getSwitchRulesCount(sourceNode.parameters); - const fallbackOutput = getSwitchFallbackOutput(sourceNode.parameters); - if (fallbackOutput === 'extra') continue; - - for (let outputIndex = rulesCount; outputIndex < mainOutputs.length; outputIndex++) { - if (!hasOutputConnections(mainOutputs, outputIndex)) continue; - - const isErrorOutput = - sourceNode.onError === 'continueErrorOutput' && outputIndex === rulesCount; - if (isErrorOutput) continue; - - warnings.push( - new ValidationWarning( - 'SWITCH_FALLBACK_OUTPUT_DISABLED', - `Switch node '${sourceNode.name}' has a connection from output ${outputIndex}, but rules mode only creates fallback output ${rulesCount} when options.fallbackOutput is set to 'extra'. Set options.fallbackOutput to 'extra' before wiring a catch-all branch, or route unmatched items to an existing rule output with a numeric fallbackOutput value.`, - sourceNode.name, - 'options.fallbackOutput', - undefined, - 'major', - ), - ); - } - } -} - -/** - * Reject `placeholder()` markers found in parameter slots whose property - * description carries `builderHint.placeholderSupported === false`. - * - * This is the runtime side of the type-level signal that used to live in the - * generated `string | Expression` union (which previously omitted - * `PlaceholderValue`). Now that `placeholder()` returns a plain `string`, the - * type system can no longer block placement; this validator does at runtime. - * - * Uses `containsPlaceholderMarker` (not `isPlaceholderValue`) so that the - * marker is rejected anywhere in the value — including `expr(placeholder())`, - * which produces `=<__PLACEHOLDER_VALUE__…__>`, and placeholders embedded - * inside `={{ … }}` expressions. - * - * Walks top-level properties only — the known declarations - * (webhook `path`, langchain agent `text`) are top-level fields. Nested - * collection / fixedCollection support can be added later if a node opts out - * of placeholders for a nested field. - */ -function validatePlaceholderSlots( - json: WorkflowJSON, - nodeTypesProvider: INodeTypes, - errors: ValidationError[], -): void { - for (const node of json.nodes) { - if (!node.name || !node.parameters) continue; - - const version = - typeof node.typeVersion === 'string' ? parseFloat(node.typeVersion) : (node.typeVersion ?? 1); - - const nodeType = nodeTypesProvider.getByNameAndVersion(node.type, version); - const properties = nodeType?.description?.properties; - if (!properties) continue; - - const params = node.parameters as Record; - for (const prop of properties) { - if (prop.builderHint?.placeholderSupported !== false) continue; - const value = params[prop.name]; - if (!containsPlaceholderMarker(value)) continue; - - errors.push( - new ValidationError( - 'INVALID_PARAMETER', - `Node "${node.name}": placeholder() is not supported for parameter '${prop.name}'. Use a literal value or expr() instead.`, - node.name, - prop.name, - ), - ); - } - } -} - -/** - * Check if connections use valid input indices for their target nodes. - * Reports warnings for connections to input indices that don't exist. - */ -/** - * Validate that every main connection originates from an output port the - * source node actually has. The legal slots are the node type's natural main - * outputs, plus one trailing error pin when the node sets - * `onError: 'continueErrorOutput'`. Connections from any higher index render - * as impossible edges on the canvas (INS-425). - */ -function checkNodeOutputIndices( - json: WorkflowJSON, - nodeTypesProvider: INodeTypes, - warnings: ValidationWarning[], -): void { - const nodesByName = new Map(); - for (const node of json.nodes) { - if (node.name) { - nodesByName.set(node.name, node); - } - } - - for (const [sourceName, nodeConnections] of Object.entries(json.connections)) { - const mainConnections = nodeConnections.main; - if (!mainConnections || !Array.isArray(mainConnections)) continue; - - const sourceNode = nodesByName.get(sourceName); - if (!sourceNode) continue; - - const version = - typeof sourceNode.typeVersion === 'string' - ? parseFloat(sourceNode.typeVersion) - : (sourceNode.typeVersion ?? 1); - - const mainOutputCount = resolveMainOutputCount(nodeTypesProvider, sourceNode.type, version); - - // If we couldn't resolve (dynamic outputs or unknown node), skip validation - if (mainOutputCount === undefined) continue; - - const errorPinCount = sourceNode.onError === 'continueErrorOutput' ? 1 : 0; - const allowedOutputCount = mainOutputCount + errorPinCount; - - for (let outputIndex = 0; outputIndex < mainConnections.length; outputIndex++) { - const outputs = mainConnections[outputIndex]; - if (!outputs || outputs.length === 0) continue; - - if (outputIndex >= allowedOutputCount) { - warnings.push( - new ValidationWarning( - 'INVALID_OUTPUT_INDEX', - `Connection from '${sourceName}' uses output index ${outputIndex}, but node only has ${allowedOutputCount} output(s) (indices 0-${allowedOutputCount - 1}). To route the error output, set onError: 'continueErrorOutput' on the node and use .onError(target).`, - sourceName, - ), - ); - } - } - } -} - -function checkNodeInputIndices( - json: WorkflowJSON, - nodeTypesProvider: INodeTypes, - warnings: ValidationWarning[], -): void { - // Build a map of node name -> node for quick lookup - const nodesByName = new Map(); - for (const node of json.nodes) { - if (node.name) { - nodesByName.set(node.name, node); - } - } - - // Track which (nodeName, inputIndex) pairs we've already warned about - // to avoid duplicate warnings when multiple sources connect to the same invalid input - const warnedInputs = new Set(); - - // Scan all connections to check input indices - for (const [_sourceName, nodeConnections] of Object.entries(json.connections)) { - // Only check main connections (not AI connections) - const mainConnections = nodeConnections.main; - if (!mainConnections || !Array.isArray(mainConnections)) continue; - - for (const outputs of mainConnections) { - if (!outputs) continue; - for (const conn of outputs) { - const targetNodeName = conn.node; - const targetInputIndex = conn.index; - - const targetNode = nodesByName.get(targetNodeName); - if (!targetNode) continue; - - // Get version number - const version = - typeof targetNode.typeVersion === 'string' - ? parseFloat(targetNode.typeVersion) - : (targetNode.typeVersion ?? 1); - - // Resolve the number of main inputs for this node type - const mainInputCount = resolveMainInputCount(nodeTypesProvider, targetNode.type, version); - - // If we couldn't resolve (dynamic inputs or unknown node), skip validation - if (mainInputCount === undefined) continue; - - // Check if the input index is valid - if (targetInputIndex >= mainInputCount) { - const warnKey = `${targetNodeName}:${targetInputIndex}`; - if (!warnedInputs.has(warnKey)) { - warnedInputs.add(warnKey); - warnings.push( - new ValidationWarning( - 'INVALID_INPUT_INDEX', - `Connection to '${targetNodeName}' uses input index ${targetInputIndex}, but node only has ${mainInputCount} input(s) (indices 0-${mainInputCount - 1})`, - targetNodeName, - ), - ); - } - } - } - } - } -} +} from './node-parameter-schema/schema-validator'; + +export { + type IssueSeverity, + isInformationalIssue, + partitionValidationIssues, +} from './issue-severity'; + +export { + validateWorkflowBuilder, + buildUncheckedNotes, + type ValidateWorkflowBuilderOptions, + type ValidateWorkflowBuilderResult, + type CollectedValidationIssue, + type ValidationIssueSource, +} from './validate-workflow-builder'; + +export { + matchesDisplayOptions, + checkConditions, + type DisplayOptions, + type DisplayOptionsContext, +} from './display-options'; + +export { resolveMainInputCount } from './node-port-resolvers/resolve-main-input-count'; +export { resolveMainOutputCount } from './node-port-resolvers/resolve-main-output-count'; diff --git a/packages/@n8n/workflow-sdk/src/validation/informational-validation-codes.test.ts b/packages/@n8n/workflow-sdk/src/validation/informational-validation-codes.test.ts new file mode 100644 index 00000000000..180901edb11 --- /dev/null +++ b/packages/@n8n/workflow-sdk/src/validation/informational-validation-codes.test.ts @@ -0,0 +1,23 @@ +import { partitionValidationIssues } from './issue-severity'; + +describe('partitionValidationIssues', () => { + it('separates blocking from informational by severity on the issue', () => { + const issues = [ + { code: 'MISSING_TRIGGER', message: 'No trigger', severity: 'informational' as const }, + { code: 'INVALID_PARAMETER', message: 'Bad parameter', severity: 'warning' as const }, + { code: 'SDK_AS_CONST', message: 'Avoid as const', severity: 'informational' as const }, + ]; + expect(partitionValidationIssues(issues)).toEqual({ + informational: [issues[0], issues[2]], + blocking: [issues[1]], + }); + }); + + it('treats missing severity as blocking', () => { + const issues = [{ code: 'UNKNOWN_CONFIG_KEY', message: 'Unknown key' }]; + expect(partitionValidationIssues(issues)).toEqual({ + informational: [], + blocking: [issues[0]], + }); + }); +}); diff --git a/packages/@n8n/workflow-sdk/src/validation/informational-validation-codes.ts b/packages/@n8n/workflow-sdk/src/validation/informational-validation-codes.ts new file mode 100644 index 00000000000..a947fbb678c --- /dev/null +++ b/packages/@n8n/workflow-sdk/src/validation/informational-validation-codes.ts @@ -0,0 +1,9 @@ +/** + * @deprecated Import from `./issue-severity` (or the validation barrel) instead. + * Re-exported here so older deep imports keep working. + */ +export { + type IssueSeverity, + isInformationalIssue, + partitionValidationIssues, +} from './issue-severity'; diff --git a/packages/@n8n/workflow-sdk/src/validation/input-validation.test.ts b/packages/@n8n/workflow-sdk/src/validation/input-index-validation.test.ts similarity index 92% rename from packages/@n8n/workflow-sdk/src/validation/input-validation.test.ts rename to packages/@n8n/workflow-sdk/src/validation/input-index-validation.test.ts index cce912ea390..72f7b93efe2 100644 --- a/packages/@n8n/workflow-sdk/src/validation/input-validation.test.ts +++ b/packages/@n8n/workflow-sdk/src/validation/input-index-validation.test.ts @@ -125,6 +125,40 @@ describe('input index validation', () => { const invalidInputWarnings = result.warnings.filter((w) => w.code === 'INVALID_INPUT_INDEX'); expect(invalidInputWarnings.length).toBe(2); }); + + it('warns for negative input index', () => { + const result = validateWorkflow( + { + id: 'test-id', + name: 'Test', + nodes: [ + { + id: '1', + name: 'Trigger', + type: 'n8n-nodes-base.manualTrigger', + typeVersion: 1, + position: [0, 0], + }, + { + id: 'a', + name: 'A', + type: 'n8n-nodes-base.aggregate', + typeVersion: 1, + position: [100, 0], + parameters: {}, + }, + ], + connections: { + Trigger: { main: [[{ node: 'A', type: 'main', index: -1 }]] }, + }, + }, + { nodeTypesProvider: mockNodeTypesProvider }, + ); + + const warning = result.warnings.find((w) => w.code === 'INVALID_INPUT_INDEX'); + expect(warning).toBeDefined(); + expect(warning?.message).toContain('input index -1'); + }); }); describe('merge node input-count validation', () => { diff --git a/packages/@n8n/workflow-sdk/src/validation/issue-severity.ts b/packages/@n8n/workflow-sdk/src/validation/issue-severity.ts new file mode 100644 index 00000000000..6922e2dfd7d --- /dev/null +++ b/packages/@n8n/workflow-sdk/src/validation/issue-severity.ts @@ -0,0 +1,36 @@ +/** + * Severity for validation / lint findings. + * + * - `error` — fatal for `ValidationResult.valid` + * - `warning` — non-fatal for `valid`, but blocks CLI exit / build-workflow save + * - `informational` — never blocks CLI exit / build-workflow save + * + * Set severity where the issue is created (validator plugin, ValidationWarning, + * or source-lint rule). Do not maintain a parallel code allowlist. + */ +export type IssueSeverity = 'error' | 'warning' | 'informational'; + +export function isInformationalIssue(issue: unknown): boolean { + if (typeof issue !== 'object' || issue === null || !('severity' in issue)) { + return false; + } + return Reflect.get(issue, 'severity') === 'informational'; +} + +export function partitionValidationIssues(issues: readonly T[]): { + blocking: T[]; + informational: T[]; +} { + const blocking: T[] = []; + const informational: T[] = []; + + for (const issue of issues) { + if (isInformationalIssue(issue)) { + informational.push(issue); + } else { + blocking.push(issue); + } + } + + return { blocking, informational }; +} diff --git a/packages/@n8n/workflow-sdk/src/validation/resolve-schema.test.ts b/packages/@n8n/workflow-sdk/src/validation/node-parameter-schema/resolve-schema.test.ts similarity index 100% rename from packages/@n8n/workflow-sdk/src/validation/resolve-schema.test.ts rename to packages/@n8n/workflow-sdk/src/validation/node-parameter-schema/resolve-schema.test.ts diff --git a/packages/@n8n/workflow-sdk/src/validation/resolve-schema.ts b/packages/@n8n/workflow-sdk/src/validation/node-parameter-schema/resolve-schema.ts similarity index 99% rename from packages/@n8n/workflow-sdk/src/validation/resolve-schema.ts rename to packages/@n8n/workflow-sdk/src/validation/node-parameter-schema/resolve-schema.ts index ae76a091a5a..f711536d6f5 100644 --- a/packages/@n8n/workflow-sdk/src/validation/resolve-schema.ts +++ b/packages/@n8n/workflow-sdk/src/validation/node-parameter-schema/resolve-schema.ts @@ -4,7 +4,7 @@ import { matchesDisplayOptions as matchesDisplayOptionsCore, type DisplayOptions, type DisplayOptionsContext, -} from './display-options'; +} from '../display-options'; // Re-export types from display-options for backward compatibility export type { DisplayOptions, DisplayOptionsContext }; diff --git a/packages/@n8n/workflow-sdk/src/validation/schema-helpers.test.ts b/packages/@n8n/workflow-sdk/src/validation/node-parameter-schema/schema-helpers.test.ts similarity index 100% rename from packages/@n8n/workflow-sdk/src/validation/schema-helpers.test.ts rename to packages/@n8n/workflow-sdk/src/validation/node-parameter-schema/schema-helpers.test.ts diff --git a/packages/@n8n/workflow-sdk/src/validation/schema-helpers.ts b/packages/@n8n/workflow-sdk/src/validation/node-parameter-schema/schema-helpers.ts similarity index 98% rename from packages/@n8n/workflow-sdk/src/validation/schema-helpers.ts rename to packages/@n8n/workflow-sdk/src/validation/node-parameter-schema/schema-helpers.ts index 778f8b4751a..1330d8f5e0f 100644 --- a/packages/@n8n/workflow-sdk/src/validation/schema-helpers.ts +++ b/packages/@n8n/workflow-sdk/src/validation/node-parameter-schema/schema-helpers.ts @@ -27,7 +27,7 @@ export { literalUnion, optionsWithExpression, multiOptionsSchema, -} from '../generate-types/zod-helpers'; +} from '../../generate-types/zod-helpers'; // Re-export resolveSchema and types from resolve-schema export { resolveSchema, resolveOneOfSchemas } from './resolve-schema'; diff --git a/packages/@n8n/workflow-sdk/src/validation/schema-validation-integration.test.ts b/packages/@n8n/workflow-sdk/src/validation/node-parameter-schema/schema-validation-integration.test.ts similarity index 99% rename from packages/@n8n/workflow-sdk/src/validation/schema-validation-integration.test.ts rename to packages/@n8n/workflow-sdk/src/validation/node-parameter-schema/schema-validation-integration.test.ts index ad1f9dfa14a..847b04136fe 100644 --- a/packages/@n8n/workflow-sdk/src/validation/schema-validation-integration.test.ts +++ b/packages/@n8n/workflow-sdk/src/validation/node-parameter-schema/schema-validation-integration.test.ts @@ -10,8 +10,8 @@ import { validateNodeConfig, loadSchema } from './schema-validator'; import { setupTestSchemas, teardownTestSchemas } from './test-schema-setup'; -import { parseWorkflowCode } from '../codegen/parse-workflow-code'; -import { validateWorkflow } from '../validation'; +import { parseWorkflowCode } from '../../codegen/parse-workflow-code'; +import { validateWorkflow } from '../../validation'; function requireSchema(nodeType: string, version: number): void { if (!loadSchema(nodeType, version)) { diff --git a/packages/@n8n/workflow-sdk/src/validation/schema-validator.test.ts b/packages/@n8n/workflow-sdk/src/validation/node-parameter-schema/schema-validator.test.ts similarity index 100% rename from packages/@n8n/workflow-sdk/src/validation/schema-validator.test.ts rename to packages/@n8n/workflow-sdk/src/validation/node-parameter-schema/schema-validator.test.ts diff --git a/packages/@n8n/workflow-sdk/src/validation/schema-validator.ts b/packages/@n8n/workflow-sdk/src/validation/node-parameter-schema/schema-validator.ts similarity index 100% rename from packages/@n8n/workflow-sdk/src/validation/schema-validator.ts rename to packages/@n8n/workflow-sdk/src/validation/node-parameter-schema/schema-validator.ts diff --git a/packages/@n8n/workflow-sdk/src/validation/test-schema-setup.ts b/packages/@n8n/workflow-sdk/src/validation/node-parameter-schema/test-schema-setup.ts similarity index 93% rename from packages/@n8n/workflow-sdk/src/validation/test-schema-setup.ts rename to packages/@n8n/workflow-sdk/src/validation/node-parameter-schema/test-schema-setup.ts index 23166858466..dde8fdce10e 100644 --- a/packages/@n8n/workflow-sdk/src/validation/test-schema-setup.ts +++ b/packages/@n8n/workflow-sdk/src/validation/node-parameter-schema/test-schema-setup.ts @@ -12,7 +12,7 @@ import * as os from 'os'; import * as path from 'path'; import { setSchemaBaseDirs, getSchemaBaseDirs } from './schema-validator'; -import { generateNodeDefinitions } from '../generate-types/generate-node-defs-cli'; +import { generateNodeDefinitions } from '../../generate-types/generate-node-defs-cli'; // Use a worker-specific directory to prevent race conditions when multiple test // workers run schema-using tests in parallel (they would otherwise concurrently @@ -31,7 +31,7 @@ let originalBaseDirs: string[] | undefined; * cached schemas in the temp directory are stale. */ function computeGeneratorHash(): string { - const generatorPath = path.resolve(__dirname, '../generate-types/generate-zod-schemas.ts'); + const generatorPath = path.resolve(__dirname, '../../generate-types/generate-zod-schemas.ts'); try { const content = fs.readFileSync(generatorPath, 'utf-8'); return crypto.createHash('md5').update(content).digest('hex'); @@ -73,7 +73,7 @@ export async function setupTestSchemas(): Promise { fs.rmSync(SCHEMA_TEST_DIR, { recursive: true, force: true }); } - const repoRoot = path.resolve(__dirname, '../../../../..'); + const repoRoot = path.resolve(__dirname, '../../../../../..'); const nodesBaseJson = path.join(repoRoot, 'packages/nodes-base/dist/types/nodes.json'); if (fs.existsSync(nodesBaseJson)) { diff --git a/packages/@n8n/workflow-sdk/src/validation/input-resolver.test.ts b/packages/@n8n/workflow-sdk/src/validation/node-port-resolvers/resolve-main-input-count.test.ts similarity index 97% rename from packages/@n8n/workflow-sdk/src/validation/input-resolver.test.ts rename to packages/@n8n/workflow-sdk/src/validation/node-port-resolvers/resolve-main-input-count.test.ts index d373bd45b55..77ee059017e 100644 --- a/packages/@n8n/workflow-sdk/src/validation/input-resolver.test.ts +++ b/packages/@n8n/workflow-sdk/src/validation/node-port-resolvers/resolve-main-input-count.test.ts @@ -1,6 +1,6 @@ import type { INodeTypes } from 'n8n-workflow'; -import { resolveMainInputCount } from './input-resolver'; +import { resolveMainInputCount } from './resolve-main-input-count'; describe('resolveMainInputCount', () => { const createMockProvider = (inputs: unknown): INodeTypes => diff --git a/packages/@n8n/workflow-sdk/src/validation/input-resolver.ts b/packages/@n8n/workflow-sdk/src/validation/node-port-resolvers/resolve-main-input-count.ts similarity index 100% rename from packages/@n8n/workflow-sdk/src/validation/input-resolver.ts rename to packages/@n8n/workflow-sdk/src/validation/node-port-resolvers/resolve-main-input-count.ts diff --git a/packages/@n8n/workflow-sdk/src/validation/output-resolver.ts b/packages/@n8n/workflow-sdk/src/validation/node-port-resolvers/resolve-main-output-count.ts similarity index 100% rename from packages/@n8n/workflow-sdk/src/validation/output-resolver.ts rename to packages/@n8n/workflow-sdk/src/validation/node-port-resolvers/resolve-main-output-count.ts diff --git a/packages/@n8n/workflow-sdk/src/validation/output-validation.test.ts b/packages/@n8n/workflow-sdk/src/validation/output-index-validation.test.ts similarity index 100% rename from packages/@n8n/workflow-sdk/src/validation/output-validation.test.ts rename to packages/@n8n/workflow-sdk/src/validation/output-index-validation.test.ts diff --git a/packages/@n8n/workflow-sdk/src/validation/validate-workflow-builder.test.ts b/packages/@n8n/workflow-sdk/src/validation/validate-workflow-builder.test.ts new file mode 100644 index 00000000000..55636cda6bd --- /dev/null +++ b/packages/@n8n/workflow-sdk/src/validation/validate-workflow-builder.test.ts @@ -0,0 +1,80 @@ +import { node, trigger, workflow } from '../index'; +import { getSchemaBaseDirs, setSchemaBaseDirs } from './node-parameter-schema/schema-validator'; +import { validateWorkflowBuilder } from './validate-workflow-builder'; + +describe('validateWorkflowBuilder', () => { + it('does not reuse a prior call schema dirs when nodeDefinitionDirs is omitted', () => { + const previous = getSchemaBaseDirs(); + setSchemaBaseDirs(['/tmp/stale-node-definitions']); + const t = trigger({ + type: 'n8n-nodes-base.manualTrigger', + version: 1, + config: { name: 'Start' }, + }); + const wf = workflow('id', 'name').add(t); + + try { + const result = validateWorkflowBuilder(wf); + // Call scoped to [] for validation, then restores the prior registry. + expect(result.nodeDefinitionDirs).toEqual([]); + expect(result.unchecked.some((note) => note.includes('no node definitions'))).toBe(true); + expect(getSchemaBaseDirs()).toEqual(['/tmp/stale-node-definitions']); + } finally { + setSchemaBaseDirs(previous); + } + }); + + it('runs graph + schema and partitions informational severity', () => { + const orphan = node({ + type: 'n8n-nodes-base.set', + version: 3, + config: { name: 'Orphan' }, + }); + const wf = workflow('id', 'name').add(orphan); + + const result = validateWorkflowBuilder(wf); + + expect(result.valid).toBe(true); + expect(result.ok).toBe(true); + expect(result.informational.some((issue) => issue.code === 'MISSING_TRIGGER')).toBe(true); + expect(result.informational.some((issue) => issue.code === 'DISCONNECTED_NODE')).toBe(true); + expect(result.blocking).toHaveLength(0); + }); + + it('includes source lint when lint: true and source is provided', () => { + const t = trigger({ + type: 'n8n-nodes-base.manualTrigger', + version: 1, + config: { name: 'Start' }, + }); + const wf = workflow('id', 'name').add(t); + const source = ` +import { sticky, trigger, workflow } from '@n8n/workflow-sdk'; +const t = trigger({ type: 'n8n-nodes-base.manualTrigger', version: 1, config: { name: 'Start' } }); +const note = sticky('hi'); +export default workflow('id', 'name').add(t).add(note); +`; + + const result = validateWorkflowBuilder(wf, { lint: true, source }); + + expect(result.lint.some((issue) => issue.code === 'SDK_UNSOLICITED_STICKY')).toBe(true); + expect(result.informational.some((issue) => issue.code === 'SDK_UNSOLICITED_STICKY')).toBe( + true, + ); + expect(result.ok).toBe(true); + }); + + it('skips lint when lint is not requested', () => { + const t = trigger({ + type: 'n8n-nodes-base.manualTrigger', + version: 1, + config: { name: 'Start' }, + }); + const wf = workflow('id', 'name').add(t); + const source = "const note = sticky('hi'); export default workflow('id','n').add(note);"; + + const result = validateWorkflowBuilder(wf, { source }); + + expect(result.lint).toHaveLength(0); + }); +}); diff --git a/packages/@n8n/workflow-sdk/src/validation/validate-workflow-builder.ts b/packages/@n8n/workflow-sdk/src/validation/validate-workflow-builder.ts new file mode 100644 index 00000000000..a09355f0cbe --- /dev/null +++ b/packages/@n8n/workflow-sdk/src/validation/validate-workflow-builder.ts @@ -0,0 +1,244 @@ +import { lintWorkflowSource, type SourceLintIssue } from '../lint'; +import { type IssueSeverity, partitionValidationIssues } from './issue-severity'; +import { getSchemaBaseDirs, setSchemaBaseDirs } from './node-parameter-schema/schema-validator'; +import { + validateWorkflow, + type ValidationError, + type ValidationOptions, + type ValidationResult, + type ValidationWarning, +} from './validate-workflow'; +import type { WorkflowJSON } from '../types/base'; + +export type ValidationIssueSource = 'graph' | 'schema' | 'sdk' | 'jsCode' | 'pythonCode'; + +export interface CollectedValidationIssue { + code: string; + message: string; + /** Severity set at the issue creation site. */ + severity: IssueSeverity; + nodeName?: string; + parameterPath?: string; + /** 1-based line in the workflow source file, when resolvable. */ + line?: number; + /** 1-based column in the workflow source file, when resolvable. */ + column?: number; + source: ValidationIssueSource; +} + +export interface ValidateWorkflowBuilderOptions extends ValidationOptions { + /** + * When true, also run source lint (SDK builder + embedded Code-node rules). + * Requires {@link source}. + */ + lint?: boolean; + /** TypeScript source of the workflow file (needed for lint + line mapping). */ + source?: string; + /** + * Directories for Zod parameter schemas (`setSchemaBaseDirs`). + * These do **not** build an `INodeTypes` provider — pass {@link nodeTypesProvider} + * separately when AI-input / port-bound checks are needed. + */ + nodeDefinitionDirs?: string[]; +} + +export interface ValidateWorkflowBuilderResult { + /** True when graph+schema report no fatal errors (`ValidationResult.valid`). */ + valid: boolean; + /** True when no blocking (non-informational) issues remain after partition. */ + ok: boolean; + issues: CollectedValidationIssue[]; + blocking: CollectedValidationIssue[]; + informational: CollectedValidationIssue[]; + graph: ValidationResult; + schema: ValidationResult; + lint: SourceLintIssue[]; + unchecked: string[]; + nodeDefinitionDirs: string[]; +} + +interface WorkflowBuilderLike { + validate: (options?: ValidationOptions) => ValidationResult; + toJSON: (options?: { tidyUp?: boolean }) => WorkflowJSON; +} + +const UNCHECKED_ALWAYS = [ + 'wrong-kind resource locator values', + // IF/Switch/SIB/Merge bounds are covered by connection-index-validator; + // other node types still need a full nodeTypesProvider. + 'input/output index bounds for non-control-flow nodes', + 'AI input type / required-input support', + 'n8n credits aiGateway constraints (needs Instance AI metadata)', +] as const; + +const UNCHECKED_WITHOUT_SCHEMAS = + 'node parameter names and values (no node definitions found — pass --node-types or set N8N_NODE_DEFINITION_DIRS)'; + +const UNCHECKED_WITHOUT_PROVIDER = + 'full nodeTypesProvider checks (node-definition dirs only supply Zod parameter schemas, not INodeTypes)'; + +/** Notes about checks the unified validator does not cover (CLI trailer / JSON). */ +export function buildUncheckedNotes(options: { + schemasLoaded: boolean; + hasNodeTypesProvider: boolean; +}): string[] { + const unchecked: string[] = [...UNCHECKED_ALWAYS]; + if (!options.schemasLoaded) { + unchecked.push(UNCHECKED_WITHOUT_SCHEMAS); + } + if (!options.hasNodeTypesProvider) { + unchecked.push(UNCHECKED_WITHOUT_PROVIDER); + } + return unchecked; +} + +function escapeRegExp(value: string): string { + return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); +} + +/** + * Best-effort: locate the `config.name` assignment for a node in the source. + * Validators don't carry AST locations, so we map nodeName → source line/column. + */ +function findNodeLocation( + sourceLines: string[], + nodeName: string, +): { line: number; column: number } | undefined { + const pattern = new RegExp(`\\bname:\\s*['"]${escapeRegExp(nodeName)}['"]`); + for (let i = 0; i < sourceLines.length; i++) { + const match = pattern.exec(sourceLines[i] ?? ''); + if (match) { + return { line: i + 1, column: match.index + 1 }; + } + } + return undefined; +} + +function toCollected( + issues: ReadonlyArray, + source: 'graph' | 'schema', + sourceLines: string[] | undefined, +): CollectedValidationIssue[] { + return issues.map((issue) => { + const parameterPath = + 'parameterPath' in issue && typeof issue.parameterPath === 'string' + ? issue.parameterPath + : 'parameterName' in issue && typeof issue.parameterName === 'string' + ? issue.parameterName + : undefined; + const location = + issue.nodeName && sourceLines ? findNodeLocation(sourceLines, issue.nodeName) : undefined; + return { + code: issue.code, + message: issue.message, + severity: issue.severity, + nodeName: issue.nodeName, + parameterPath, + line: location?.line, + column: location?.column, + source, + }; + }); +} + +function sourceLintToCollected(issue: SourceLintIssue): CollectedValidationIssue { + return { + code: issue.code, + message: issue.message, + severity: issue.severity, + line: issue.line, + column: issue.column, + source: issue.lintTarget, + nodeName: issue.nodeName, + parameterPath: issue.parameterPath, + }; +} + +/** + * Dedupe overlapping graph+schema findings (e.g. DISCONNECTED_NODE) without + * requiring identical messages — plugins and validateWorkflow phrase them differently. + * + * Keep `parameterPath` so distinct INVALID_PARAMETER (etc.) findings on the same + * node are not collapsed into one. + */ +function dedupeIssues(issues: CollectedValidationIssue[]): CollectedValidationIssue[] { + const seen = new Set(); + const deduped: CollectedValidationIssue[] = []; + for (const issue of issues) { + const key = `${issue.code}|${issue.nodeName ?? ''}|${issue.parameterPath ?? ''}|${issue.line ?? ''}|${issue.column ?? ''}`; + if (seen.has(key)) continue; + seen.add(key); + deduped.push(issue); + } + return deduped; +} + +/** + * Run graph validators (`wf.validate`), JSON/schema validation, and optionally + * source lint — the single path used by the CLI and fixture tests. + * + * Pass `{ lint: true, source }` to include SDK / Code-node source lint. + * Pass `nodeTypesProvider` when the host already has live node types; definition + * dirs alone cannot synthesize one (they only back Zod parameter schemas). + */ +export function validateWorkflowBuilder( + workflow: WorkflowBuilderLike, + options: ValidateWorkflowBuilderOptions = {}, +): ValidateWorkflowBuilderResult { + const nodeDefinitionDirs = options.nodeDefinitionDirs ?? []; + // Scope schema dirs to this call only — omit/empty must not reuse a prior + // call's dirs, and we must not leak empty/cleared dirs into later callers + // (tests and long-lived hosts share the process-level schema registry). + const previousSchemaDirs = getSchemaBaseDirs(); + setSchemaBaseDirs(nodeDefinitionDirs); + + try { + const unchecked = buildUncheckedNotes({ + schemasLoaded: nodeDefinitionDirs.length > 0, + hasNodeTypesProvider: options.nodeTypesProvider !== undefined, + }); + + const validationOptions: ValidationOptions = { + strictMode: options.strictMode, + allowDisconnectedNodes: options.allowDisconnectedNodes, + allowNoTrigger: options.allowNoTrigger, + validateSchema: options.validateSchema, + nodeTypesProvider: options.nodeTypesProvider, + }; + + const graph = workflow.validate(validationOptions); + const schema = validateWorkflow(workflow.toJSON({ tidyUp: true }), validationOptions); + + const source = options.source ?? ''; + const sourceLines = source.length > 0 ? source.split(/\r?\n/) : undefined; + const lint = + options.lint === true && source.length > 0 + ? lintWorkflowSource(source) + : ([] as SourceLintIssue[]); + + const allIssues = dedupeIssues([ + ...toCollected(graph.errors, 'graph', sourceLines), + ...toCollected(graph.warnings, 'graph', sourceLines), + ...toCollected(schema.errors, 'schema', sourceLines), + ...toCollected(schema.warnings, 'schema', sourceLines), + ...lint.map(sourceLintToCollected), + ]); + + const { blocking, informational } = partitionValidationIssues(allIssues); + + return { + valid: graph.valid && schema.valid, + ok: blocking.length === 0, + issues: allIssues, + blocking, + informational, + graph, + schema, + lint, + unchecked, + nodeDefinitionDirs, + }; + } finally { + setSchemaBaseDirs(previousSchemaDirs); + } +} diff --git a/packages/@n8n/workflow-sdk/src/validation/validation.test.ts b/packages/@n8n/workflow-sdk/src/validation/validate-workflow.test.ts similarity index 96% rename from packages/@n8n/workflow-sdk/src/validation/validation.test.ts rename to packages/@n8n/workflow-sdk/src/validation/validate-workflow.test.ts index 4ccbbe0e0ac..a81f59cf4aa 100644 --- a/packages/@n8n/workflow-sdk/src/validation/validation.test.ts +++ b/packages/@n8n/workflow-sdk/src/validation/validate-workflow.test.ts @@ -1,5 +1,5 @@ import { validateWorkflow, ValidationError } from '.'; -import { setupTestSchemas, teardownTestSchemas } from './test-schema-setup'; +import { setupTestSchemas, teardownTestSchemas } from './node-parameter-schema/test-schema-setup'; import type { NodeInstance, WorkflowJSON } from '../types/base'; import { workflow } from '../workflow-builder'; import { node, trigger, sticky } from '../workflow-builder/node-builders/node-builder'; @@ -128,6 +128,118 @@ describe('Validation', () => { // Subnodes should NOT be flagged as disconnected (they connect TO their parent via AI connections) expect(disconnectedWarnings).toHaveLength(0); }); + + it.each(['n8n-nodes-base.cron', 'n8n-nodes-base.start', 'n8n-nodes-base.emailReadImap'])( + 'should treat %s as a trigger (canonical trigger detection)', + (type) => { + const result = validateWorkflow({ + id: 'test-id', + name: 'Test', + nodes: [{ id: '1', name: 'Entry', type, typeVersion: 1, position: [0, 0] }], + connections: {}, + }); + + expect(result.warnings.filter((w) => w.code === 'MISSING_TRIGGER')).toHaveLength(0); + expect(result.warnings.filter((w) => w.code === 'DISCONNECTED_NODE')).toHaveLength(0); + }, + ); + + it('should warn when two sources connect to the same single-value AI input', () => { + const result = validateWorkflow({ + id: 'test-id', + name: 'Test', + nodes: [ + { + id: '1', + name: 'Trigger', + type: 'n8n-nodes-base.manualTrigger', + typeVersion: 1, + position: [0, 0], + }, + { + id: '2', + name: 'Agent', + type: '@n8n/n8n-nodes-langchain.agent', + typeVersion: 1, + position: [200, 0], + parameters: {}, + }, + { + id: '3', + name: 'Model A', + type: '@n8n/n8n-nodes-langchain.lmChatOpenAi', + typeVersion: 1, + position: [0, 200], + parameters: {}, + }, + { + id: '4', + name: 'Model B', + type: '@n8n/n8n-nodes-langchain.lmChatOpenAi', + typeVersion: 1, + position: [200, 200], + parameters: {}, + }, + ], + connections: { + Trigger: { main: [[{ node: 'Agent', type: 'main', index: 0 }]] }, + 'Model A': { + ai_languageModel: [[{ node: 'Agent', type: 'ai_languageModel', index: 0 }]], + }, + 'Model B': { + ai_languageModel: [[{ node: 'Agent', type: 'ai_languageModel', index: 0 }]], + }, + }, + }); + + const duplicateWarnings = result.warnings.filter( + (w) => w.code === 'DUPLICATE_SUBNODE_CONNECTION', + ); + expect(duplicateWarnings).toHaveLength(1); + expect(duplicateWarnings[0].message).toContain('ai_languageModel'); + expect(duplicateWarnings[0].message).toContain('Model A'); + expect(duplicateWarnings[0].message).toContain('Model B'); + }); + + it('should not warn for a single connection to a single-value AI input', () => { + const result = validateWorkflow({ + id: 'test-id', + name: 'Test', + nodes: [ + { + id: '1', + name: 'Trigger', + type: 'n8n-nodes-base.manualTrigger', + typeVersion: 1, + position: [0, 0], + }, + { + id: '2', + name: 'Agent', + type: '@n8n/n8n-nodes-langchain.agent', + typeVersion: 1, + position: [200, 0], + parameters: {}, + }, + { + id: '3', + name: 'Model', + type: '@n8n/n8n-nodes-langchain.lmChatOpenAi', + typeVersion: 1, + position: [0, 200], + parameters: {}, + }, + ], + connections: { + Trigger: { main: [[{ node: 'Agent', type: 'main', index: 0 }]] }, + Model: { ai_languageModel: [[{ node: 'Agent', type: 'ai_languageModel', index: 0 }]] }, + }, + }); + + expect(result.warnings.filter((w) => w.code === 'DUPLICATE_SUBNODE_CONNECTION')).toHaveLength( + 0, + ); + }); }); describe('ValidationError', () => { diff --git a/packages/@n8n/workflow-sdk/src/validation/validate-workflow.ts b/packages/@n8n/workflow-sdk/src/validation/validate-workflow.ts new file mode 100644 index 00000000000..42857217059 --- /dev/null +++ b/packages/@n8n/workflow-sdk/src/validation/validate-workflow.ts @@ -0,0 +1,1367 @@ +import { isRecord } from '@n8n/utils/is-record'; +import get from 'lodash/get'; +import type { INodeTypes, IConnections as N8nIConnections, IDisplayOptions } from 'n8n-workflow'; +import { mapConnectionsByDestination } from 'n8n-workflow'; + +import { matchesDisplayOptions } from './display-options'; +import type { DisplayOptions, DisplayOptionsContext } from './display-options'; +import { validateNodeConfig } from './node-parameter-schema/schema-validator'; +import { resolveMainInputCount } from './node-port-resolvers/resolve-main-input-count'; +import { resolveMainOutputCount } from './node-port-resolvers/resolve-main-output-count'; +import { isStickyNoteType, isHttpRequestType } from '../constants/node-types'; +import type { WorkflowBuilder, WorkflowJSON } from '../types/base'; +import { isTriggerNodeType } from '../utils/trigger-detection'; +import { containsPlaceholderMarker } from '../workflow-builder/string-utils'; + +/** + * Validation error codes + */ +export type ValidationErrorCode = + | 'NO_NODES' + | 'MISSING_TRIGGER' + | 'DISCONNECTED_NODE' + | 'MISSING_PARAMETER' + | 'INVALID_CONNECTION' + | 'CIRCULAR_REFERENCE' + | 'INVALID_EXPRESSION' + | 'AGENT_STATIC_PROMPT' + | 'AGENT_NO_SYSTEM_MESSAGE' + | 'HARDCODED_CREDENTIALS' + | 'SET_CREDENTIAL_FIELD' + | 'MERGE_SINGLE_INPUT' + | 'TOOL_NO_PARAMETERS' + | 'FROM_AI_IN_NON_TOOL' + | 'MISSING_EXPRESSION_PREFIX' + | 'INVALID_PARAMETER' + | 'INVALID_INPUT_INDEX' + | 'INVALID_OUTPUT_INDEX' + | 'SUBNODE_NOT_CONNECTED' + | 'DUPLICATE_SUBNODE_CONNECTION' + | 'SUBNODE_PARAMETER_MISMATCH' + | 'UNSUPPORTED_SUBNODE_INPUT' + | 'MISSING_REQUIRED_INPUT' + | 'INVALID_OUTPUT_FOR_MODE' + | 'SWITCH_NO_OUTPUT_CONNECTIONS' + | 'SWITCH_FALLBACK_OUTPUT_DISABLED' + | 'MAX_NODES_EXCEEDED' + | 'INVALID_EXPRESSION_PATH' + | 'PARTIAL_EXPRESSION_PATH' + | 'INVALID_DATE_METHOD' + | 'UNKNOWN_CONFIG_KEY'; + +/** + * Validation error class + */ +export class ValidationError { + readonly code: ValidationErrorCode; + readonly message: string; + readonly nodeName?: string; + readonly parameterName?: string; + /** Violation level for evaluation scoring (defaults to 'minor' if not set) */ + readonly violationLevel?: 'critical' | 'major' | 'minor'; + readonly severity = 'error' as const; + + constructor( + code: ValidationErrorCode, + message: string, + nodeName?: string, + parameterName?: string, + violationLevel?: 'critical' | 'major' | 'minor', + ) { + this.code = code; + this.message = message; + this.nodeName = nodeName; + this.parameterName = parameterName; + this.violationLevel = violationLevel; + } +} + +/** + * Validation warning class (non-fatal for `ValidationResult.valid`). + * + * Save/CLI gating uses {@link severity}: `informational` never blocks; + * `warning` blocks unless the caller chooses otherwise. + */ +export class ValidationWarning { + readonly code: ValidationErrorCode; + readonly message: string; + readonly nodeName?: string; + readonly parameterPath?: string; + readonly originalName?: string; + /** Violation level for evaluation scoring (defaults to 'minor' if not set) */ + readonly violationLevel?: 'critical' | 'major' | 'minor'; + readonly severity: 'warning' | 'informational'; + + constructor( + code: ValidationErrorCode, + message: string, + nodeName?: string, + parameterPath?: string, + originalName?: string, + violationLevel?: 'critical' | 'major' | 'minor', + severity: 'warning' | 'informational' = 'warning', + ) { + this.code = code; + this.message = message; + this.nodeName = nodeName; + this.parameterPath = parameterPath; + this.originalName = originalName; + this.violationLevel = violationLevel; + this.severity = severity; + } + + /** Soft graph findings that must not block save / CLI exit. */ + static informational( + code: ValidationErrorCode, + message: string, + nodeName?: string, + parameterPath?: string, + originalName?: string, + violationLevel?: 'critical' | 'major' | 'minor', + ): ValidationWarning { + return new ValidationWarning( + code, + message, + nodeName, + parameterPath, + originalName, + violationLevel, + 'informational', + ); + } +} + +/** + * Validation result + */ +export interface ValidationResult { + /** Whether the workflow is valid */ + valid: boolean; + /** Fatal errors that prevent the workflow from running */ + errors: ValidationError[]; + /** Warnings about potential issues */ + warnings: ValidationWarning[]; +} + +/** + * Validation options + */ +export interface ValidationOptions { + /** Enable strict mode with more warnings */ + strictMode?: boolean; + /** Skip disconnected node warnings */ + allowDisconnectedNodes?: boolean; + /** Skip trigger requirement */ + allowNoTrigger?: boolean; + /** Enable/disable Zod schema validation (default: true) */ + validateSchema?: boolean; + /** Optional node types provider for dynamic input index validation */ + nodeTypesProvider?: INodeTypes; +} + +/** + * AI connection types used by subnodes to connect to their parent nodes + */ +const AI_CONNECTION_TYPES = [ + 'ai_languageModel', + 'ai_memory', + 'ai_tool', + 'ai_outputParser', + 'ai_embedding', + 'ai_vectorStore', + 'ai_retriever', + 'ai_document', + 'ai_textSplitter', + 'ai_reranker', +]; + +/** + * Mapping from AI connection type to subnodes field name + */ +const AI_CONNECTION_TO_SUBNODE_FIELD: Record = { + ai_languageModel: 'model', + ai_memory: 'memory', + ai_tool: 'tools', + ai_outputParser: 'outputParser', + ai_embedding: 'embedding', + ai_vectorStore: 'vectorStore', + ai_retriever: 'retriever', + ai_document: 'documentLoader', + ai_textSplitter: 'textSplitter', + ai_reranker: 'reranker', +}; + +/** + * AI connection types that should always be arrays in subnodes + */ +const AI_ARRAY_TYPES = new Set(['ai_tool']); + +interface NodeJSON { + id?: string; + name?: string; + type: string; + typeVersion?: number | string; + position?: [number, number]; + parameters?: Record; + onError?: string; +} + +/** + * Reconstruct subnodes object from AI connections in the workflow. + * When SDK code defines subnodes, they get serialized as separate nodes with AI connections. + * This function reverses that transformation for validation purposes. + */ +function reconstructSubnodesFromConnections( + targetNodeName: string, + json: WorkflowJSON, +): Record | undefined { + const subnodes: Record = {}; + const nodesByName = new Map(); + + // Build a map of node name -> node for quick lookup + for (const node of json.nodes) { + if (node.name) { + nodesByName.set(node.name, node); + } + } + + // Scan all nodes' connections to find AI connections TO this target node + for (const [sourceNodeName, nodeConnections] of Object.entries(json.connections)) { + for (const connType of AI_CONNECTION_TYPES) { + const aiConns = nodeConnections[connType as keyof typeof nodeConnections]; + if (!aiConns || !Array.isArray(aiConns)) continue; + + for (const outputs of aiConns) { + if (!outputs) continue; + for (const conn of outputs) { + if (conn.node === targetNodeName) { + // Found an AI connection to our target node + const subnodeField = AI_CONNECTION_TO_SUBNODE_FIELD[connType]; + if (!subnodeField) continue; + + const sourceNode = nodesByName.get(sourceNodeName); + if (!sourceNode) continue; + + // Build a minimal subnode config for validation + const subnodeConfig = { + type: sourceNode.type, + version: sourceNode.typeVersion, + parameters: sourceNode.parameters ?? {}, + }; + + // For array types (like tools), collect into array + if (AI_ARRAY_TYPES.has(connType)) { + const existing = subnodes[subnodeField]; + if (Array.isArray(existing)) { + existing.push(subnodeConfig); + } else { + subnodes[subnodeField] = [subnodeConfig]; + } + } else { + // Single-value types keep the last connection; duplicates are + // surfaced by checkDuplicateSingleValueAiConnections instead of + // being silently dropped here. + subnodes[subnodeField] = subnodeConfig; + } + } + } + } + } + } + + // Return undefined if no subnodes were found + return Object.keys(subnodes).length > 0 ? subnodes : undefined; +} + +/** + * Single-value AI inputs (model, memory, embedding, …) accept exactly one connection + * at runtime. Subnode reconstruction keeps only the last source, so surface duplicates + * here instead of letting an earlier malformed subnode escape validation. + */ +function checkDuplicateSingleValueAiConnections( + json: WorkflowJSON, + warnings: ValidationWarning[], +): void { + const firstSourceByInput = new Map(); + const warnedInputs = new Set(); + + for (const [sourceNodeName, nodeConnections] of Object.entries(json.connections)) { + for (const connType of AI_CONNECTION_TYPES) { + if (AI_ARRAY_TYPES.has(connType)) continue; + const aiConns = nodeConnections[connType as keyof typeof nodeConnections]; + if (!aiConns || !Array.isArray(aiConns)) continue; + + for (const outputs of aiConns) { + if (!outputs) continue; + for (const conn of outputs) { + const key = `${conn.node}:${connType}`; + const firstSource = firstSourceByInput.get(key); + if (firstSource === undefined) { + firstSourceByInput.set(key, sourceNodeName); + continue; + } + if (firstSource === sourceNodeName || warnedInputs.has(key)) continue; + warnedInputs.add(key); + warnings.push( + new ValidationWarning( + 'DUPLICATE_SUBNODE_CONNECTION', + `'${conn.node}' has multiple '${connType}' connections ('${firstSource}' and '${sourceNodeName}'), but this input accepts only one. Remove the extra connection.`, + conn.node, + ), + ); + } + } + } + } +} + +/** + * Check if a node has AI connections to a parent node (making it a connected subnode) + */ +function hasAiConnectionToParent(nodeName: string, json: WorkflowJSON): boolean { + const nodeConnections = json.connections[nodeName]; + if (!nodeConnections) return false; + + for (const connType of AI_CONNECTION_TYPES) { + const aiConns = nodeConnections[connType as keyof typeof nodeConnections]; + if (aiConns && Array.isArray(aiConns)) { + for (const outputs of aiConns) { + if (outputs && outputs.length > 0) { + return true; // Has AI connection to parent + } + } + } + } + return false; +} + +/** + * Check if a node is used as a tool (connected via ai_tool connection type) + */ +function isToolSubnode(nodeName: string, json: WorkflowJSON): boolean { + const nodeConnections = json.connections[nodeName]; + if (!nodeConnections) return false; + + const toolConns = nodeConnections.ai_tool as unknown as Array>; + if (toolConns && Array.isArray(toolConns)) { + for (const outputs of toolConns) { + if (outputs && outputs.length > 0) { + return true; // Connected as a tool + } + } + } + return false; +} + +/** + * Find disconnected nodes (nodes that don't receive input from any other node) + */ +function findDisconnectedNodes(json: WorkflowJSON): string[] { + const hasIncoming = new Set(); + + // Find all nodes that have incoming connections + for (const [_sourceName, nodeConnections] of Object.entries(json.connections)) { + if (nodeConnections.main) { + for (const outputs of nodeConnections.main) { + if (outputs) { + for (const connection of outputs) { + hasIncoming.add(connection.node); + } + } + } + } + } + + // Find nodes without incoming connections (excluding triggers, sticky notes, and connected subnodes) + const disconnected: string[] = []; + for (const node of json.nodes) { + // Skip nodes without names (e.g., some sticky notes) + if (!node.name) continue; + + // Skip if node has incoming connection + if (hasIncoming.has(node.name)) continue; + + // Skip trigger nodes - they don't need incoming connections + if (isTriggerNodeType(node.type)) continue; + + // Skip sticky notes - they don't participate in data flow + if (isStickyNoteType(node.type)) continue; + + // Skip subnodes - they connect TO their parent via AI connections + if (hasAiConnectionToParent(node.name, json)) continue; + + disconnected.push(node.name); + } + + return disconnected; +} + +/** + * Validate a workflow + * + * Checks for: + * - Presence of trigger node (warning if missing) + * - Disconnected nodes (warning) + * - Required parameters (in strict mode) + * + * @param workflow - The workflow to validate (WorkflowBuilder or WorkflowJSON) + * @param options - Validation options + * @returns Validation result with errors and warnings + * + * @example + * ```typescript + * const wf = workflow('id', 'Test').add(trigger(...)).to(node(...)); + * const result = validateWorkflow(wf); + * + * if (!result.valid) { + * console.error('Errors:', result.errors); + * } + * if (result.warnings.length > 0) { + * console.warn('Warnings:', result.warnings); + * } + * ``` + */ +export function validateWorkflow( + workflowOrJson: WorkflowBuilder | WorkflowJSON, + options: ValidationOptions = {}, +): ValidationResult { + // Get JSON representation + const json: WorkflowJSON = 'toJSON' in workflowOrJson ? workflowOrJson.toJSON() : workflowOrJson; + + const errors: ValidationError[] = []; + const warnings: ValidationWarning[] = []; + + // Check for trigger node + if (!options.allowNoTrigger) { + const hasTrigger = json.nodes.some((node) => isTriggerNodeType(node.type)); + if (!hasTrigger) { + warnings.push( + ValidationWarning.informational( + 'MISSING_TRIGGER', + 'Workflow has no trigger node. It will need to be started manually.', + ), + ); + } + } + + // Check for disconnected nodes + if (!options.allowDisconnectedNodes) { + const disconnected = findDisconnectedNodes(json); + for (const nodeName of disconnected) { + warnings.push( + ValidationWarning.informational( + 'DISCONNECTED_NODE', + `Node '${nodeName}' is not connected to any input. It will not receive data.`, + nodeName, + ), + ); + } + } + + // Strict mode validations + if (options.strictMode) { + // Check for potentially missing required parameters + for (const node of json.nodes) { + // HTTP Request should have a URL + if (isHttpRequestType(node.type)) { + if (!node.parameters?.url && !node.parameters?.requestUrl) { + warnings.push( + new ValidationWarning( + 'MISSING_PARAMETER', + `HTTP Request node '${node.name}' may be missing URL parameter`, + node.name, + ), + ); + } + } + } + } + + // Schema validation (enabled by default) + if (options.validateSchema !== false) { + for (const node of json.nodes) { + // Get version number (handle both number and string versions) + const version = + typeof node.typeVersion === 'string' + ? parseFloat(node.typeVersion) + : (node.typeVersion ?? 1); + + // Build config object for validation + const config: { parameters?: unknown; subnodes?: unknown } = {}; + if (node.parameters !== undefined) { + config.parameters = node.parameters; + } + // Include subnodes if present (for AI nodes) + const nodeWithSubnodes = node as typeof node & { subnodes?: unknown }; + if (nodeWithSubnodes.subnodes !== undefined) { + config.subnodes = nodeWithSubnodes.subnodes; + } else if (node.name) { + // Try to reconstruct subnodes from AI connections in the workflow + // This handles the case where subnodes were serialized as separate nodes + const reconstructed = reconstructSubnodesFromConnections(node.name, json); + if (reconstructed) { + config.subnodes = reconstructed; + } + } + + // Determine if this node is being used as a tool (for @tool displayOptions) + // A node is a tool if it's connected via ai_tool connection type + const isToolNode = node.name ? isToolSubnode(node.name, json) : false; + + const schemaResult = validateNodeConfig(node.type, version, config, { isToolNode }); + + if (!schemaResult.valid) { + for (const error of schemaResult.errors) { + let message = error.message; + + // Enhance subnode errors with valid options when nodeTypesProvider is available + if ( + error.path === 'subnodes' && + message.includes('Unknown field') && + options.nodeTypesProvider + ) { + const nodeType = options.nodeTypesProvider.getByNameAndVersion(node.type, version); + const validInputs = nodeType?.description?.builderHint?.inputs; + if (validInputs) { + const validSubnodes = Object.keys(validInputs) + .map((k) => AI_CONNECTION_TO_SUBNODE_FIELD[k]) + .filter(Boolean); + if (validSubnodes.length > 0) { + // Transform message from "Unknown field(s) at "subnodes": "x", "y"." + // to "Invalid subnode(s) "x", "y". This node only accepts: a, b." + message = message.replace( + /Unknown field\(s\) at "subnodes": (.+)\./, + `Invalid subnode(s) $1. This node only accepts: ${validSubnodes.join(', ')}.`, + ); + } + } + } + + // Report as WARNING (non-blocking) to maintain backwards compatibility + warnings.push( + new ValidationWarning( + 'INVALID_PARAMETER', + `Node "${node.name}": ${message}`, + node.name, + ), + ); + } + } + } + } + + // Input index validation (only if provider is given) + if (options.nodeTypesProvider) { + checkNodeInputIndices(json, options.nodeTypesProvider, warnings); + // Validate that connections originate from output ports that actually exist + checkNodeOutputIndices(json, options.nodeTypesProvider, warnings); + // Validate subnode parameters match parent's displayOptions requirements + validateSubnodeParameters(json, options.nodeTypesProvider, warnings); + // Validate parent nodes actually support their connected AI input types + validateParentSupportsInputs(json, options.nodeTypesProvider, warnings); + // Validate required AI inputs on parent nodes are actually connected + validateRequiredInputsConnected(json, options.nodeTypesProvider, errors); + // Validate that emitted connection types are actually exposed by the source node's mode + validateOutputUsage(json, options.nodeTypesProvider, warnings); + // Reject placeholder() in slots that opt out via builderHint.placeholderSupported === false + validatePlaceholderSlots(json, options.nodeTypesProvider, errors); + } + + // Switch fallback output validation does not need node metadata. It is derived from + // the Switch node's dynamic output contract in rules mode. + validateSwitchHasOutgoingConnections(json, warnings); + validateSwitchFallbackOutputConnections(json, warnings); + + // Merge node input-count consistency + checkMergeNodeInputCount(json, warnings); + + // Duplicate connections to single-value AI inputs + checkDuplicateSingleValueAiConnections(json, warnings); + + return { + valid: errors.length === 0, + errors, + warnings, + }; +} + +/** + * Validate that the Merge node's `numberInputs` parameter is consistent with + * the input indices actually used by incoming connections. + * + * The Merge node has expression-based inputs (count derived from + * `numberInputs`, default 2), so checkNodeInputIndices can't resolve the count + * statically. Without this check, a workflow with three branches wired into a + * Merge node that still has `numberInputs=2` passes validation and silently + * drops the third branch at runtime. + */ +function checkMergeNodeInputCount(json: WorkflowJSON, warnings: ValidationWarning[]): void { + const connectionsByDest = mapConnectionsByDestination( + json.connections as unknown as N8nIConnections, + ); + + for (const node of json.nodes) { + if (!node.name) continue; + if (node.type !== 'n8n-nodes-base.merge') continue; + + const numberInputsParam = node.parameters?.numberInputs; + const declaredInputs = typeof numberInputsParam === 'number' ? numberInputsParam : 2; + + const incomingMain = connectionsByDest[node.name]?.main; + if (!incomingMain) continue; + + let maxConnectedIndex = -1; + for (let i = 0; i < incomingMain.length; i++) { + const slot = incomingMain[i]; + if (Array.isArray(slot) && slot.length > 0) { + maxConnectedIndex = i; + } + } + + if (maxConnectedIndex >= declaredInputs) { + warnings.push( + new ValidationWarning( + 'INVALID_INPUT_INDEX', + `Merge node '${node.name}' has a connection to input index ${maxConnectedIndex} but 'numberInputs' is ${declaredInputs}. Set 'numberInputs' to ${maxConnectedIndex + 1} so every branch is accepted.`, + node.name, + 'numberInputs', + undefined, + 'major', + ), + ); + } + } +} + +/** + * Mapping from AI connection type to SDK function name (for error messages) + */ +const AI_CONNECTION_TO_SDK_FUNCTION: Record = { + ai_languageModel: 'languageModel()', + ai_memory: 'memory()', + ai_tool: 'tool()', + ai_outputParser: 'outputParser()', + ai_embedding: 'embeddings()', + ai_vectorStore: 'vectorStore()', + ai_retriever: 'retriever()', + ai_document: 'documentLoader()', + ai_textSplitter: 'textSplitter()', + ai_reranker: 'reranker()', +}; + +/** + * Check if a subnode's parameters satisfy displayOptions conditions + */ +function checkDisplayOptionsMatch( + subnodeParams: Record, + displayOptions: IDisplayOptions, +): { + matches: boolean; + mismatches: Array<{ param: string; expected: unknown[]; actual: unknown }>; +} { + const mismatches: Array<{ param: string; expected: unknown[]; actual: unknown }> = []; + + if (!displayOptions.show) return { matches: true, mismatches }; + + for (const [paramName, expectedValues] of Object.entries(displayOptions.show)) { + if (!expectedValues) continue; // Skip undefined values + const actualValue = subnodeParams[paramName]; + if (!expectedValues.includes(actualValue as never)) { + mismatches.push({ + param: paramName, + expected: expectedValues as unknown[], + actual: actualValue, + }); + } + } + + return { matches: mismatches.length === 0, mismatches }; +} + +/** + * Validate that subnodes connected to parent nodes have parameters + * matching the displayOptions conditions in builderHint.inputs. + * + * For example, if an Agent's ai_tool input has displayOptions.show = { mode: ['retrieve-as-tool'] }, + * then any node connected via ai_tool must have mode='retrieve-as-tool'. + */ +function validateSubnodeParameters( + json: WorkflowJSON, + nodeTypesProvider: INodeTypes, + warnings: ValidationWarning[], +): void { + // Build a map of node name -> node for quick lookup + const nodesByName = new Map(); + for (const node of json.nodes) { + if (node.name) { + nodesByName.set(node.name, node); + } + } + + // Invert connections to find incoming connections by destination + // Cast to n8n-workflow IConnections since our local type has string for connection type + const connectionsByDest = mapConnectionsByDestination( + json.connections as unknown as N8nIConnections, + ); + + // Check each node that might be a parent with AI inputs + for (const parentNode of json.nodes) { + if (!parentNode.name) continue; + + // Try to get the node type to check for builderHint.inputs + const parentNodeType = nodeTypesProvider.getByNameAndVersion( + parentNode.type, + typeof parentNode.typeVersion === 'string' + ? parseFloat(parentNode.typeVersion) + : (parentNode.typeVersion ?? 1), + ); + const builderHintInputs = parentNodeType?.description?.builderHint?.inputs; + if (!builderHintInputs) continue; + + // For each AI input type the parent accepts + for (const [connectionType, inputConfig] of Object.entries(builderHintInputs)) { + if (!connectionType.startsWith('ai_')) continue; + if (!inputConfig?.displayOptions?.show) continue; + + // Find subnodes connected via this type + const incomingConnections = connectionsByDest[parentNode.name]?.[connectionType]; + if (!incomingConnections) continue; + + for (const connList of incomingConnections) { + if (!connList) continue; + for (const conn of connList) { + const subnodeName = conn.node; + const subnode = nodesByName.get(subnodeName); + if (!subnode?.parameters) continue; + + // Check if subnode params match displayOptions conditions + const { matches, mismatches } = checkDisplayOptionsMatch( + subnode.parameters, + inputConfig.displayOptions, + ); + + if (!matches) { + // `displayOptions` on `builderHint.inputs[type]` can describe + // either subnode-relative params (e.g. ai_vectorStore wants + // vector-store mode='retrieve-as-tool') or parent-relative + // params (e.g. ai_memory wants chatTrigger mode='hostedChat'). + // If every mismatched param is absent from the subnode, those + // params don't belong to the subnode at all — blaming it is a + // false-positive misdirect. Defer to validateParentSupportsInputs. + const subnodeOwnsAnyParam = mismatches.some((m) => m.actual !== undefined); + if (!subnodeOwnsAnyParam) continue; + + const sdkFn = AI_CONNECTION_TO_SDK_FUNCTION[connectionType] || connectionType; + + // Build error message with actual parameter names from displayOptions + const mismatchDetails = mismatches + .map( + (m) => + `${m.param}='${String(m.actual)}' (expected: ${m.expected.map((v) => `'${String(v)}'`).join(' or ')})`, + ) + .join(', '); + + warnings.push( + new ValidationWarning( + 'SUBNODE_PARAMETER_MISMATCH', + `'${subnodeName}' is connected to '${parentNode.name}' using ${sdkFn} but has ${mismatchDetails}. Update parameters to match the SDK function used.`, + subnodeName, + mismatches[0]?.param, + ), + ); + } + } + } + } + } +} + +/** + * Build a human-readable summary of which displayOptions conditions are not met. + */ +function buildConditionSummary( + displayOptions: IDisplayOptions, + parentParams: Record, +): string { + if (!displayOptions.show) return ''; + + const parts: string[] = []; + for (const [paramName, expectedValues] of Object.entries(displayOptions.show)) { + if (!expectedValues) continue; + // Use lodash get so nested paths (e.g. 'options.loadPreviousSession') + // resolve correctly — direct property access would read the literal + // dotted key and report 'undefined' even when the nested value is set. + const actual = get(parentParams, paramName); + const expectedStr = (expectedValues as unknown[]).map((v) => `'${String(v)}'`).join(' or '); + parts.push(`${paramName} should be ${expectedStr} (currently '${String(actual)}')`); + } + + return parts.length > 0 ? `Required: ${parts.join(', ')}.` : ''; +} + +/** + * Build a description of which parameters TRIGGERED a requirement. + * Used by `MISSING_REQUIRED_INPUT` where the displayOptions conditions are + * already satisfied (that's why the requirement applies) — the agent needs + * to know which params caused it so it can choose between satisfying the + * requirement or backing out by changing those params. + */ +function buildTriggeringConditionSummary( + displayOptions: IDisplayOptions, + parentParams: Record, +): string { + if (!displayOptions.show) return ''; + + const parts: string[] = []; + for (const [paramName, _expectedValues] of Object.entries(displayOptions.show)) { + const actual = get(parentParams, paramName); + parts.push(`${paramName}='${String(actual)}'`); + } + + return parts.join(', '); +} + +/** + * Validate that parent nodes actually support their connected AI input types + * based on the parent's own parameters and builderHint.inputs displayOptions. + * + * For example, if a vector store has mode='retrieve' but a documentLoader is connected, + * this produces a warning because ai_document requires mode='insert'. + */ +function validateParentSupportsInputs( + json: WorkflowJSON, + nodeTypesProvider: INodeTypes, + warnings: ValidationWarning[], +): void { + const nodesByName = new Map(); + for (const node of json.nodes) { + if (node.name) { + nodesByName.set(node.name, node); + } + } + + const connectionsByDest = mapConnectionsByDestination( + json.connections as unknown as N8nIConnections, + ); + + for (const parentNode of json.nodes) { + if (!parentNode.name) continue; + + const version = + typeof parentNode.typeVersion === 'string' + ? parseFloat(parentNode.typeVersion) + : (parentNode.typeVersion ?? 1); + + const parentNodeType = nodeTypesProvider.getByNameAndVersion(parentNode.type, version); + const builderHintInputs = parentNodeType?.description?.builderHint?.inputs; + if (!builderHintInputs) continue; + + const parentContext: DisplayOptionsContext = { + parameters: (parentNode.parameters ?? {}) as Record, + nodeVersion: version, + rootParameters: (parentNode.parameters ?? {}) as Record, + }; + + for (const [connectionType, inputConfig] of Object.entries(builderHintInputs)) { + if (!connectionType.startsWith('ai_')) continue; + if (!inputConfig?.displayOptions) continue; + + const parentSupportsInput = matchesDisplayOptions( + parentContext, + inputConfig.displayOptions as DisplayOptions, + ); + + if (parentSupportsInput) continue; + + const incomingConnections = connectionsByDest[parentNode.name]?.[connectionType]; + if (!incomingConnections) continue; + + for (const connList of incomingConnections) { + if (!connList) continue; + for (const conn of connList) { + const subnodeName = conn.node; + const subnodeField = AI_CONNECTION_TO_SUBNODE_FIELD[connectionType] || connectionType; + const conditionDetails = buildConditionSummary( + inputConfig.displayOptions, + (parentNode.parameters ?? {}) as Record, + ); + + warnings.push( + new ValidationWarning( + 'UNSUPPORTED_SUBNODE_INPUT', + `'${parentNode.name}' has a ${subnodeField} subnode ('${subnodeName}') connected, but its current configuration does not accept one. ${conditionDetails} These parameters must be set on '${parentNode.name}' itself, NOT on the ${subnodeField} subnode. Alternatively, remove the ${subnodeField} connection if this capability isn't needed.`, + parentNode.name, + undefined, + undefined, + 'major', + ), + ); + } + } + } + } +} + +/** + * Validate that required AI inputs declared in a parent node's builderHint.inputs + * are actually connected. + * + * For each parent node with a builderHint.inputs entry that has `required: true`, + * check whether its displayOptions (if any) match the parent's current parameters; + * if so, require that a connection of that AI type terminates at the parent. + * Emits a fatal error when the connection is missing — without it, the workflow + * silently passes validation but breaks at runtime (see INS-136: chat trigger + * with `loadPreviousSession: 'memory'` but no memory subnode connected). + */ +function validateRequiredInputsConnected( + json: WorkflowJSON, + nodeTypesProvider: INodeTypes, + errors: ValidationError[], +): void { + const connectionsByDest = mapConnectionsByDestination( + json.connections as unknown as N8nIConnections, + ); + + for (const parentNode of json.nodes) { + if (!parentNode.name) continue; + + const version = + typeof parentNode.typeVersion === 'string' + ? parseFloat(parentNode.typeVersion) + : (parentNode.typeVersion ?? 1); + + const parentNodeType = nodeTypesProvider.getByNameAndVersion(parentNode.type, version); + const builderHintInputs = parentNodeType?.description?.builderHint?.inputs; + if (!builderHintInputs) continue; + + const parentContext: DisplayOptionsContext = { + parameters: (parentNode.parameters ?? {}) as Record, + nodeVersion: version, + rootParameters: (parentNode.parameters ?? {}) as Record, + }; + + for (const [connectionType, inputConfig] of Object.entries(builderHintInputs)) { + if (!connectionType.startsWith('ai_')) continue; + if (!inputConfig?.required) continue; + + if (inputConfig.displayOptions) { + const conditionsMet = matchesDisplayOptions( + parentContext, + inputConfig.displayOptions as DisplayOptions, + ); + if (!conditionsMet) continue; + } + + const incoming = connectionsByDest[parentNode.name]?.[connectionType]; + const hasConnection = + Array.isArray(incoming) && incoming.some((slot) => Array.isArray(slot) && slot.length > 0); + if (hasConnection) continue; + + const subnodeField = AI_CONNECTION_TO_SUBNODE_FIELD[connectionType] || connectionType; + const triggerDetails = inputConfig.displayOptions + ? ` (triggered by ${buildTriggeringConditionSummary( + inputConfig.displayOptions, + (parentNode.parameters ?? {}) as Record, + )})` + : ''; + const alternative = inputConfig.displayOptions + ? ` Either connect a ${subnodeField} subnode, or change those parameters to remove the requirement.` + : ''; + + errors.push( + new ValidationError( + 'MISSING_REQUIRED_INPUT', + `'${parentNode.name}' requires a ${subnodeField} subnode connected to its ${connectionType} input${triggerDetails}, but none is connected.${alternative}`, + parentNode.name, + undefined, + 'major', + ), + ); + } + } +} + +/** + * Render an outgoing connection type as the SDK syntax that produces it, so warning + * messages speak the LLM agent's vocabulary instead of raw `main` / `ai_*` types. + * + * `target` flips the phrasing between describing the wiring already used by the source + * (e.g. `wired with .to()`) and describing where the source SHOULD attach instead + * (e.g. `subnodes.tools`). + */ +function describeOutputWiring(connectionType: string, target = false): string { + if (connectionType === 'main') return target ? '.to(...)' : 'wired with .to()'; + const field = AI_CONNECTION_TO_SUBNODE_FIELD[connectionType]; + if (field) return target ? `subnodes.${field}` : `attached as subnodes.${field}`; + return target ? connectionType : `connected via ${connectionType}`; +} + +/** + * Return the connection type from `outputsHint` whose displayOptions match the source + * node's current parameters — i.e. the output the node actually exposes given how it's + * configured. Used to suggest the correct wiring fix in `INVALID_OUTPUT_FOR_MODE`. + */ +function findEnabledAlternativeOutput( + outputsHint: Record, + ctx: DisplayOptionsContext, + excludeType: string, +): string | undefined { + for (const [type, cfg] of Object.entries(outputsHint)) { + if (type === excludeType) continue; + if (!cfg) continue; + if (cfg.displayOptions && !matchesDisplayOptions(ctx, cfg.displayOptions as DisplayOptions)) { + continue; + } + return type; + } + return undefined; +} + +/** + * Validate that connections leaving a node use connection types the node's current + * parameters actually expose. Driven by `builderHint.outputs` declared on the source node. + * + * Example: a vector store in `mode: 'retrieve'` exposes only `ai_vectorStore`. If the workflow + * has a `main` connection out of that node, this emits `INVALID_OUTPUT_FOR_MODE` because + * `builderHint.outputs.main.displayOptions` requires `mode` ∈ ['insert', 'load', 'update']. + */ +function validateOutputUsage( + json: WorkflowJSON, + nodeTypesProvider: INodeTypes, + warnings: ValidationWarning[], +): void { + for (const sourceNode of json.nodes) { + if (!sourceNode.name) continue; + + const outgoing = json.connections[sourceNode.name]; + if (!outgoing) continue; + + const version = + typeof sourceNode.typeVersion === 'string' + ? parseFloat(sourceNode.typeVersion) + : (sourceNode.typeVersion ?? 1); + + const nodeType = nodeTypesProvider.getByNameAndVersion(sourceNode.type, version); + const outputsHint = nodeType?.description?.builderHint?.outputs; + if (!outputsHint) continue; + + const ctx: DisplayOptionsContext = { + parameters: (sourceNode.parameters ?? {}) as Record, + nodeVersion: version, + rootParameters: (sourceNode.parameters ?? {}) as Record, + }; + + for (const [connectionType, cfg] of Object.entries(outputsHint)) { + // No displayOptions => assume the node always emits this connection type. + if (!cfg?.displayOptions) continue; + + const edges = outgoing[connectionType]; + if (!edges) continue; + const hasEdges = edges.some((slot) => Array.isArray(slot) && slot.length > 0); + if (!hasEdges) continue; + + const enabled = matchesDisplayOptions(ctx, cfg.displayOptions as DisplayOptions); + if (enabled) continue; + + const conditionDetails = buildConditionSummary( + cfg.displayOptions, + (sourceNode.parameters ?? {}) as Record, + ); + const usedWiring = describeOutputWiring(connectionType); + const enabledAlt = findEnabledAlternativeOutput(outputsHint, ctx, connectionType); + const altSuggestion = enabledAlt + ? ` To use this node as-is, attach it as ${describeOutputWiring(enabledAlt, true)} of a parent (it exposes ${enabledAlt} in this configuration).` + : ''; + + warnings.push( + new ValidationWarning( + 'INVALID_OUTPUT_FOR_MODE', + `'${sourceNode.name}' is ${usedWiring} but its current parameters disable that output. ${conditionDetails}${altSuggestion}`, + sourceNode.name, + undefined, + undefined, + 'major', + ), + ); + } + } +} + +function getSwitchRulesCount(parameters: Record | undefined): number { + const rules = parameters?.rules; + if (!isRecord(rules)) return 0; + + const values = rules.values; + if (Array.isArray(values)) return values.length; + + const legacyRules = rules.rules; + if (Array.isArray(legacyRules)) return legacyRules.length; + + return 0; +} + +function getSwitchFallbackOutput(parameters: Record | undefined): unknown { + const options = parameters?.options; + if (!isRecord(options)) return undefined; + + return options.fallbackOutput; +} + +function hasOutputConnections( + outputs: Array | null>, + outputIndex: number, +): boolean { + const output = outputs[outputIndex]; + return Array.isArray(output) && output.length > 0; +} + +function hasAnyMainOutputConnection(nodeConnections: unknown): boolean { + if (!isRecord(nodeConnections)) return false; + const main = nodeConnections.main; + if (!Array.isArray(main)) return false; + + return main.some((slot) => Array.isArray(slot) && slot.length > 0); +} + +/** + * A Switch with no outgoing branches is almost always an incomplete router: + * every matched item is dropped and downstream side effects never run. + */ +function validateSwitchHasOutgoingConnections( + json: WorkflowJSON, + warnings: ValidationWarning[], +): void { + for (const sourceNode of json.nodes) { + if (!sourceNode.name || sourceNode.type !== 'n8n-nodes-base.switch') continue; + if (hasAnyMainOutputConnection(json.connections[sourceNode.name])) continue; + + warnings.push( + new ValidationWarning( + 'SWITCH_NO_OUTPUT_CONNECTIONS', + `Switch node '${sourceNode.name}' has no outgoing connections. Connect at least one output branch to downstream action nodes, or remove the Switch node.`, + sourceNode.name, + 'connections', + undefined, + 'major', + ), + ); + } +} + +/** + * Validate that Switch fallback branches are only connected when the node + * actually exposes an extra fallback output. + */ +function validateSwitchFallbackOutputConnections( + json: WorkflowJSON, + warnings: ValidationWarning[], +): void { + for (const sourceNode of json.nodes) { + if (!sourceNode.name || sourceNode.type !== 'n8n-nodes-base.switch') continue; + + const mode = sourceNode.parameters?.mode; + if (mode !== undefined && mode !== 'rules') continue; + + const outgoing = json.connections[sourceNode.name]; + const mainOutputs = outgoing?.main; + if (!Array.isArray(mainOutputs)) continue; + + const rulesCount = getSwitchRulesCount(sourceNode.parameters); + const fallbackOutput = getSwitchFallbackOutput(sourceNode.parameters); + if (fallbackOutput === 'extra') continue; + + for (let outputIndex = rulesCount; outputIndex < mainOutputs.length; outputIndex++) { + if (!hasOutputConnections(mainOutputs, outputIndex)) continue; + + const isErrorOutput = + sourceNode.onError === 'continueErrorOutput' && outputIndex === rulesCount; + if (isErrorOutput) continue; + + warnings.push( + new ValidationWarning( + 'SWITCH_FALLBACK_OUTPUT_DISABLED', + `Switch node '${sourceNode.name}' has a connection from output ${outputIndex}, but rules mode only creates fallback output ${rulesCount} when options.fallbackOutput is set to 'extra'. Set options.fallbackOutput to 'extra' before wiring a catch-all branch, or route unmatched items to an existing rule output with a numeric fallbackOutput value.`, + sourceNode.name, + 'options.fallbackOutput', + undefined, + 'major', + ), + ); + } + } +} + +/** + * Reject `placeholder()` markers found in parameter slots whose property + * description carries `builderHint.placeholderSupported === false`. + * + * This is the runtime side of the type-level signal that used to live in the + * generated `string | Expression` union (which previously omitted + * `PlaceholderValue`). Now that `placeholder()` returns a plain `string`, the + * type system can no longer block placement; this validator does at runtime. + * + * Uses `containsPlaceholderMarker` (not `isPlaceholderValue`) so that the + * marker is rejected anywhere in the value — including `expr(placeholder())`, + * which produces `=<__PLACEHOLDER_VALUE__…__>`, and placeholders embedded + * inside `={{ … }}` expressions. + * + * Walks top-level properties only — the known declarations + * (webhook `path`, langchain agent `text`) are top-level fields. Nested + * collection / fixedCollection support can be added later if a node opts out + * of placeholders for a nested field. + */ +function validatePlaceholderSlots( + json: WorkflowJSON, + nodeTypesProvider: INodeTypes, + errors: ValidationError[], +): void { + for (const node of json.nodes) { + if (!node.name || !node.parameters) continue; + + const version = + typeof node.typeVersion === 'string' ? parseFloat(node.typeVersion) : (node.typeVersion ?? 1); + + const nodeType = nodeTypesProvider.getByNameAndVersion(node.type, version); + const properties = nodeType?.description?.properties; + if (!properties) continue; + + const params = node.parameters as Record; + for (const prop of properties) { + if (prop.builderHint?.placeholderSupported !== false) continue; + const value = params[prop.name]; + if (!containsPlaceholderMarker(value)) continue; + + errors.push( + new ValidationError( + 'INVALID_PARAMETER', + `Node "${node.name}": placeholder() is not supported for parameter '${prop.name}'. Use a literal value or expr() instead.`, + node.name, + prop.name, + ), + ); + } + } +} + +/** + * Check if connections use valid input indices for their target nodes. + * Reports warnings for connections to input indices that don't exist. + */ +/** + * Validate that every main connection originates from an output port the + * source node actually has. The legal slots are the node type's natural main + * outputs, plus one trailing error pin when the node sets + * `onError: 'continueErrorOutput'`. Connections from any higher index render + * as impossible edges on the canvas (INS-425). + */ +function checkNodeOutputIndices( + json: WorkflowJSON, + nodeTypesProvider: INodeTypes, + warnings: ValidationWarning[], +): void { + const nodesByName = new Map(); + for (const node of json.nodes) { + if (node.name) { + nodesByName.set(node.name, node); + } + } + + for (const [sourceName, nodeConnections] of Object.entries(json.connections)) { + const mainConnections = nodeConnections.main; + if (!mainConnections || !Array.isArray(mainConnections)) continue; + + const sourceNode = nodesByName.get(sourceName); + if (!sourceNode) continue; + + const version = + typeof sourceNode.typeVersion === 'string' + ? parseFloat(sourceNode.typeVersion) + : (sourceNode.typeVersion ?? 1); + + const mainOutputCount = resolveMainOutputCount(nodeTypesProvider, sourceNode.type, version); + + // If we couldn't resolve (dynamic outputs or unknown node), skip validation + if (mainOutputCount === undefined) continue; + + const errorPinCount = sourceNode.onError === 'continueErrorOutput' ? 1 : 0; + const allowedOutputCount = mainOutputCount + errorPinCount; + + for (let outputIndex = 0; outputIndex < mainConnections.length; outputIndex++) { + const outputs = mainConnections[outputIndex]; + if (!outputs || outputs.length === 0) continue; + + if (outputIndex >= allowedOutputCount) { + warnings.push( + new ValidationWarning( + 'INVALID_OUTPUT_INDEX', + `Connection from '${sourceName}' uses output index ${outputIndex}, but node only has ${allowedOutputCount} output(s) (indices 0-${allowedOutputCount - 1}). To route the error output, set onError: 'continueErrorOutput' on the node and use .onError(target).`, + sourceName, + ), + ); + } + } + } +} + +function checkNodeInputIndices( + json: WorkflowJSON, + nodeTypesProvider: INodeTypes, + warnings: ValidationWarning[], +): void { + // Build a map of node name -> node for quick lookup + const nodesByName = new Map(); + for (const node of json.nodes) { + if (node.name) { + nodesByName.set(node.name, node); + } + } + + // Track which (nodeName, inputIndex) pairs we've already warned about + // to avoid duplicate warnings when multiple sources connect to the same invalid input + const warnedInputs = new Set(); + + // Scan all connections to check input indices + for (const [_sourceName, nodeConnections] of Object.entries(json.connections)) { + // Only check main connections (not AI connections) + const mainConnections = nodeConnections.main; + if (!mainConnections || !Array.isArray(mainConnections)) continue; + + for (const outputs of mainConnections) { + if (!outputs) continue; + for (const conn of outputs) { + const targetNodeName = conn.node; + const targetInputIndex = conn.index; + + const targetNode = nodesByName.get(targetNodeName); + if (!targetNode) continue; + + // Get version number + const version = + typeof targetNode.typeVersion === 'string' + ? parseFloat(targetNode.typeVersion) + : (targetNode.typeVersion ?? 1); + + // Resolve the number of main inputs for this node type + const mainInputCount = resolveMainInputCount(nodeTypesProvider, targetNode.type, version); + + // If we couldn't resolve (dynamic inputs or unknown node), skip validation + if (mainInputCount === undefined) continue; + + // Check if the input index is valid + if (targetInputIndex < 0 || targetInputIndex >= mainInputCount) { + const warnKey = `${targetNodeName}:${targetInputIndex}`; + if (!warnedInputs.has(warnKey)) { + warnedInputs.add(warnKey); + warnings.push( + new ValidationWarning( + 'INVALID_INPUT_INDEX', + `Connection to '${targetNodeName}' uses input index ${targetInputIndex}, but node only has ${mainInputCount} input(s) (indices 0-${mainInputCount - 1})`, + targetNodeName, + ), + ); + } + } + } + } + } +} diff --git a/packages/@n8n/workflow-sdk/src/workflow-builder-plugins.test.ts b/packages/@n8n/workflow-sdk/src/workflow-builder-plugins.test.ts index 63d616152e6..ec50b195eb1 100644 --- a/packages/@n8n/workflow-sdk/src/workflow-builder-plugins.test.ts +++ b/packages/@n8n/workflow-sdk/src/workflow-builder-plugins.test.ts @@ -1083,7 +1083,7 @@ describe('WorkflowBuilder plugin integration', () => { }); describe('Phase 11.2: missingTriggerValidator plugin', () => { - it('validateWorkflow returns warning when no trigger node exists', () => { + it('validateWorkflow returns informational when no trigger node exists', () => { // Nodes map with only non-trigger nodes const nodesMap = new Map(); nodesMap.set('Set', { @@ -1102,7 +1102,7 @@ describe('WorkflowBuilder plugin integration', () => { expect(issues.length).toBe(1); expect(issues[0].code).toBe('MISSING_TRIGGER'); - expect(issues[0].severity).toBe('warning'); + expect(issues[0].severity).toBe('informational'); }); it('validateWorkflow returns empty array when trigger node exists', () => { diff --git a/packages/@n8n/workflow-sdk/src/workflow-builder.ts b/packages/@n8n/workflow-sdk/src/workflow-builder.ts index bdb33ddcfc3..4417686e322 100644 --- a/packages/@n8n/workflow-sdk/src/workflow-builder.ts +++ b/packages/@n8n/workflow-sdk/src/workflow-builder.ts @@ -747,6 +747,7 @@ class WorkflowBuilderImpl implements WorkflowBuilder { issue.parameterPath, issue.originalName, issue.violationLevel, + issue.severity === 'informational' ? 'informational' : 'warning', ), ); } diff --git a/packages/@n8n/workflow-sdk/src/workflow-builder/plugins/types.ts b/packages/@n8n/workflow-sdk/src/workflow-builder/plugins/types.ts index e0ce27a4507..81d417e6802 100644 --- a/packages/@n8n/workflow-sdk/src/workflow-builder/plugins/types.ts +++ b/packages/@n8n/workflow-sdk/src/workflow-builder/plugins/types.ts @@ -62,8 +62,13 @@ export interface ValidationIssue { readonly code: string; /** Human-readable message describing the issue */ readonly message: string; - /** Severity level: 'error' for fatal issues, 'warning' for non-fatal */ - readonly severity: 'error' | 'warning'; + /** + * Severity at the creation site: + * - `error` — fatal for `ValidationResult.valid` + * - `warning` — non-fatal for `valid`, blocks save / CLI exit + * - `informational` — never blocks save / CLI exit + */ + readonly severity: 'error' | 'warning' | 'informational'; /** Violation level for evaluation scoring (defaults to 'minor' if not set) */ readonly violationLevel?: 'critical' | 'major' | 'minor'; /** Name of the node where the issue was found (optional) */ diff --git a/packages/@n8n/workflow-sdk/src/workflow-builder/plugins/validators/disconnected-node-validator.ts b/packages/@n8n/workflow-sdk/src/workflow-builder/plugins/validators/disconnected-node-validator.ts index d8f2c282b50..09a8fab0a23 100644 --- a/packages/@n8n/workflow-sdk/src/workflow-builder/plugins/validators/disconnected-node-validator.ts +++ b/packages/@n8n/workflow-sdk/src/workflow-builder/plugins/validators/disconnected-node-validator.ts @@ -215,7 +215,7 @@ export const disconnectedNodeValidator: ValidatorPlugin = { issues.push({ code: 'DISCONNECTED_NODE', message: `${nodeRef} is not connected to any input. It will not receive data.`, - severity: 'warning', + severity: 'informational', violationLevel: 'major', nodeName: displayName, originalName: origForWarning, diff --git a/packages/@n8n/workflow-sdk/src/workflow-builder/plugins/validators/missing-trigger-validator.ts b/packages/@n8n/workflow-sdk/src/workflow-builder/plugins/validators/missing-trigger-validator.ts index 1e739c869ec..e469e63ce61 100644 --- a/packages/@n8n/workflow-sdk/src/workflow-builder/plugins/validators/missing-trigger-validator.ts +++ b/packages/@n8n/workflow-sdk/src/workflow-builder/plugins/validators/missing-trigger-validator.ts @@ -40,7 +40,7 @@ export const missingTriggerValidator: ValidatorPlugin = { { code: 'MISSING_TRIGGER', message: 'Workflow has no trigger node. It will need to be started manually.', - severity: 'warning', + severity: 'informational', }, ]; } diff --git a/packages/@n8n/workflow-sdk/test-fixtures/committed-workflows/11.json b/packages/@n8n/workflow-sdk/test-fixtures/committed-workflows/11.json new file mode 100644 index 00000000000..e74a7e93b84 --- /dev/null +++ b/packages/@n8n/workflow-sdk/test-fixtures/committed-workflows/11.json @@ -0,0 +1,29 @@ +{ + "id": "lint-code-network", + "name": "Lint: Code node network call", + "nodes": [ + { + "parameters": {}, + "id": "trigger-1", + "name": "Manual Trigger", + "type": "n8n-nodes-base.manualTrigger", + "typeVersion": 1, + "position": [0, 0] + }, + { + "parameters": { + "jsCode": "const res = await fetch('https://example.com');\nreturn [{ json: { ok: true } }];" + }, + "id": "code-1", + "name": "Fetch Data", + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [220, 0] + } + ], + "connections": { + "Manual Trigger": { + "main": [[{ "node": "Fetch Data", "type": "main", "index": 0 }]] + } + } +} diff --git a/packages/@n8n/workflow-sdk/test-fixtures/committed-workflows/12.json b/packages/@n8n/workflow-sdk/test-fixtures/committed-workflows/12.json new file mode 100644 index 00000000000..76344deb770 --- /dev/null +++ b/packages/@n8n/workflow-sdk/test-fixtures/committed-workflows/12.json @@ -0,0 +1,49 @@ +{ + "id": "lint-unsolicited-sticky", + "name": "Lint: unsolicited sticky note", + "nodes": [ + { + "parameters": {}, + "id": "trigger-1", + "name": "Manual Trigger", + "type": "n8n-nodes-base.manualTrigger", + "typeVersion": 1, + "position": [0, 0] + }, + { + "parameters": { + "content": "Please do not add this unless asked" + }, + "id": "sticky-1", + "name": "Sticky Note", + "type": "n8n-nodes-base.stickyNote", + "typeVersion": 1, + "position": [220, -120] + }, + { + "parameters": { + "assignments": { + "assignments": [ + { + "id": "id-1", + "name": "value", + "value": "ok", + "type": "string" + } + ] + }, + "options": {} + }, + "id": "set-1", + "name": "Edit Fields", + "type": "n8n-nodes-base.set", + "typeVersion": 3.4, + "position": [220, 0] + } + ], + "connections": { + "Manual Trigger": { + "main": [[{ "node": "Edit Fields", "type": "main", "index": 0 }]] + } + } +} diff --git a/packages/@n8n/workflow-sdk/test-fixtures/committed-workflows/13.json b/packages/@n8n/workflow-sdk/test-fixtures/committed-workflows/13.json new file mode 100644 index 00000000000..266c66509a5 --- /dev/null +++ b/packages/@n8n/workflow-sdk/test-fixtures/committed-workflows/13.json @@ -0,0 +1,30 @@ +{ + "id": "lint-python-network", + "name": "Lint: Python Code node network import", + "nodes": [ + { + "parameters": {}, + "id": "trigger-1", + "name": "Manual Trigger", + "type": "n8n-nodes-base.manualTrigger", + "typeVersion": 1, + "position": [0, 0] + }, + { + "parameters": { + "language": "pythonNative", + "pythonCode": "import requests\nreturn [{ \"json\": { \"ok\": True } }]" + }, + "id": "code-1", + "name": "Python Fetch", + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [220, 0] + } + ], + "connections": { + "Manual Trigger": { + "main": [[{ "node": "Python Fetch", "type": "main", "index": 0 }]] + } + } +} diff --git a/packages/@n8n/workflow-sdk/test-fixtures/committed-workflows/manifest.json b/packages/@n8n/workflow-sdk/test-fixtures/committed-workflows/manifest.json index 11072ccdab2..7efc26fe78e 100644 --- a/packages/@n8n/workflow-sdk/test-fixtures/committed-workflows/manifest.json +++ b/packages/@n8n/workflow-sdk/test-fixtures/committed-workflows/manifest.json @@ -109,6 +109,24 @@ "id": 10, "name": "Node groups - linear chain with a grouped section", "success": true + }, + { + "id": 11, + "name": "Lint: Code node network call", + "success": true, + "expectedLintIssues": [{ "code": "CODE_NODE_NETWORK_CALL" }] + }, + { + "id": 12, + "name": "Lint: unsolicited sticky note", + "success": true, + "expectedLintIssues": [{ "code": "SDK_UNSOLICITED_STICKY" }] + }, + { + "id": 13, + "name": "Lint: Python Code node network import", + "success": true, + "expectedLintIssues": [{ "code": "CODE_NODE_NETWORK_CALL" }] } ] } diff --git a/packages/cli/package.json b/packages/cli/package.json index c35fb182321..c55b36c92c7 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -105,7 +105,6 @@ "axios": "catalog:", "concurrently": "^8.2.0", "esbuild": "catalog:", - "file-type": "catalog:", "form-data": "catalog:", "ioredis-mock": "^8.8.1", "minifaker": "1.34.1", @@ -173,7 +172,7 @@ "@n8n/workflow-sdk": "workspace:*", "@n8n_io/ai-assistant-sdk": "catalog:", "@n8n_io/license-sdk": "3.0.0", - "@opentelemetry/api": "^1.9.0", + "@opentelemetry/api": "catalog:", "@opentelemetry/core": "2.7.1", "@opentelemetry/exporter-trace-otlp-proto": "^0.217.0", "@opentelemetry/instrumentation": "^0.217.0", @@ -213,6 +212,7 @@ "fast-json-patch": "catalog:", "fastest-levenshtein": "catalog:", "fflate": "catalog:", + "file-type": "catalog:", "flat": "5.0.2", "flatted": "catalog:", "formidable": "3.5.4", diff --git a/packages/cli/src/__tests__/active-executions.test.ts b/packages/cli/src/__tests__/active-executions.test.ts index 1cd38adc7a0..a6c5ff62916 100644 --- a/packages/cli/src/__tests__/active-executions.test.ts +++ b/packages/cli/src/__tests__/active-executions.test.ts @@ -11,10 +11,10 @@ import type { IWorkflowExecutionDataProcess, StructuredChunk, } from 'n8n-workflow'; +import { sleep } from '@n8n/utils/sleep'; import { createEmptyRunExecutionData, ManualExecutionCancelledError, - sleep, SystemShutdownExecutionCancelledError, } from 'n8n-workflow'; import PCancelable from 'p-cancelable'; @@ -29,8 +29,7 @@ import type { ExecutionPersistence } from '@/executions/execution-persistence'; import type { License } from '@/license'; import type { Telemetry } from '@/telemetry'; -vi.mock('n8n-workflow', async () => ({ - ...(await vi.importActual('n8n-workflow')), +vi.mock('@n8n/utils/sleep', () => ({ sleep: vi.fn(), })); @@ -116,7 +115,10 @@ describe('ActiveExecutions', () => { }); test('Should update execution if add is called with execution ID', async () => { - const executionId = await activeExecutions.add(executionData, FAKE_SECOND_EXECUTION_ID); + const executionId = await activeExecutions.add(executionData, { + executionId: FAKE_SECOND_EXECUTION_ID, + expectedStatus: 'waiting', + }); expect(executionId).toBe(FAKE_SECOND_EXECUTION_ID); expect(activeExecutions.getActiveExecutions()).toHaveLength(1); @@ -124,6 +126,46 @@ describe('ActiveExecutions', () => { expect(executionPersistence.updateExistingExecution).toHaveBeenCalledTimes(1); }); + // CAT-3862: the claim used to be hardcoded to `waiting`, so an execution + // enqueued before a restart (status `new`) could never be claimed. + test.each(['waiting', 'new'] as const)( + 'Should only claim an existing execution while it is still in status %s', + async (expectedStatus) => { + await activeExecutions.add(executionData, { + executionId: FAKE_SECOND_EXECUTION_ID, + expectedStatus, + }); + + expect(executionPersistence.updateExistingExecution).toHaveBeenCalledWith( + FAKE_SECOND_EXECUTION_ID, + expect.objectContaining({ id: FAKE_SECOND_EXECUTION_ID, status: 'running' }), + { requireStatus: expectedStatus }, + ); + }, + ); + + test('Should set startedAt without overwriting an existing value when claiming an enqueued execution', async () => { + await activeExecutions.add(executionData, { + executionId: FAKE_SECOND_EXECUTION_ID, + expectedStatus: 'new', + }); + + expect(executionRepository.setRunning).toHaveBeenCalledWith(FAKE_SECOND_EXECUTION_ID); + const update = executionPersistence.updateExistingExecution.mock.calls[0][1]; + expect(update).not.toHaveProperty('startedAt'); + }); + + test('Should preserve startedAt when resuming a waiting execution', async () => { + await activeExecutions.add(executionData, { + executionId: FAKE_SECOND_EXECUTION_ID, + expectedStatus: 'waiting', + }); + + expect(executionRepository.setRunning).not.toHaveBeenCalled(); + const update = executionPersistence.updateExistingExecution.mock.calls[0][1]; + expect(update).not.toHaveProperty('startedAt'); + }); + test('Should forward deduplicationKey to executionPersistence.create', async () => { const executionDataWithKey: IWorkflowExecutionDataProcess = { ...executionData, @@ -143,9 +185,12 @@ describe('ActiveExecutions', () => { // Mock updateExistingExecution to return false (status check failed) executionPersistence.updateExistingExecution.mockResolvedValue(false); - await expect(activeExecutions.add(executionData, FAKE_SECOND_EXECUTION_ID)).rejects.toThrow( - 'Execution is already being resumed by another process', - ); + await expect( + activeExecutions.add(executionData, { + executionId: FAKE_SECOND_EXECUTION_ID, + expectedStatus: 'waiting', + }), + ).rejects.toThrow('Execution is already being resumed by another process'); // Verify execution was NOT added to active executions expect(activeExecutions.getActiveExecutions()).toHaveLength(0); @@ -156,9 +201,12 @@ describe('ActiveExecutions', () => { // Mock updateExistingExecution to return false (another process is resuming) executionPersistence.updateExistingExecution.mockResolvedValue(false); - await expect(activeExecutions.add(executionData, FAKE_SECOND_EXECUTION_ID)).rejects.toThrow( - 'Execution is already being resumed by another process', - ); + await expect( + activeExecutions.add(executionData, { + executionId: FAKE_SECOND_EXECUTION_ID, + expectedStatus: 'waiting', + }), + ).rejects.toThrow('Execution is already being resumed by another process'); // Verify capacity was reserved and then released expect(concurrencyControl.throttle).toHaveBeenCalledWith({ @@ -281,7 +329,10 @@ describe('ActiveExecutions', () => { }); test('Should successfully attach execution to valid executionId', async () => { - await activeExecutions.add(executionData, FAKE_EXECUTION_ID); + await activeExecutions.add(executionData, { + executionId: FAKE_EXECUTION_ID, + expectedStatus: 'waiting', + }); expect(() => activeExecutions.attachWorkflowExecution(FAKE_EXECUTION_ID, workflowExecution), @@ -290,7 +341,10 @@ describe('ActiveExecutions', () => { }); test('Should attach and resolve response promise to existing execution', async () => { - await activeExecutions.add(executionData, FAKE_EXECUTION_ID); + await activeExecutions.add(executionData, { + executionId: FAKE_EXECUTION_ID, + expectedStatus: 'waiting', + }); activeExecutions.attachResponsePromise(FAKE_EXECUTION_ID, responsePromise); const fakeResponse = { data: { resultData: { runData: {} } } }; activeExecutions.resolveResponsePromise(FAKE_EXECUTION_ID, fakeResponse); @@ -307,7 +361,7 @@ describe('ActiveExecutions', () => { expect(waitingExecution.responsePromise).toBeDefined(); // Resume the execution - await activeExecutions.add(executionData, executionId); + await activeExecutions.add(executionData, { executionId, expectedStatus: 'waiting' }); const resumedExecution = activeExecutions.getExecutionOrFail(executionId); expect(resumedExecution.startedAt).toBe(waitingExecution.startedAt); diff --git a/packages/cli/src/__tests__/active-workflow-manager.test.ts b/packages/cli/src/__tests__/active-workflow-manager.test.ts index 7605b272134..4ed71f8285d 100644 --- a/packages/cli/src/__tests__/active-workflow-manager.test.ts +++ b/packages/cli/src/__tests__/active-workflow-manager.test.ts @@ -26,7 +26,8 @@ import type { WorkflowActivateMode, WorkflowExecuteMode, } from 'n8n-workflow'; -import { sleep, Workflow, WorkflowActivationError } from 'n8n-workflow'; +import { sleep } from '@n8n/utils/sleep'; +import { Workflow, WorkflowActivationError } from 'n8n-workflow'; import { mock } from 'vitest-mock-extended'; import type { ActivationErrorsService } from '@/activation-errors.service'; diff --git a/packages/cli/src/__tests__/credentials-overwrites.test.ts b/packages/cli/src/__tests__/credentials-overwrites.test.ts index ae8c5b8b117..c44b7949ed4 100644 --- a/packages/cli/src/__tests__/credentials-overwrites.test.ts +++ b/packages/cli/src/__tests__/credentials-overwrites.test.ts @@ -630,16 +630,15 @@ describe('CredentialsOverwrites', () => { settingsRepository.findByKey.mockResolvedValue(settingData); cipher.decryptV2.mockResolvedValue(JSON.stringify(overwriteData)); - // Mock the reloadFrontendService to avoid circular dependency issues - const mockReloadFrontendService = vi.fn(); - (pubsubCredentialsOverwrites as any).reloadFrontendService = mockReloadFrontendService; + const mockReloadHandler = vi.fn(); + pubsubCredentialsOverwrites.registerReloadHandler(mockReloadHandler); await pubsubCredentialsOverwrites.reloadOverwriteCredentials(); expect(settingsRepository.findByKey).toHaveBeenCalledWith('credentialsOverwrite'); expect(cipher.decryptV2).toHaveBeenCalledWith('encrypted-data'); expect(pubsubCredentialsOverwrites.getAll()).toEqual(overwriteData); - expect(mockReloadFrontendService).toHaveBeenCalled(); + expect(mockReloadHandler).toHaveBeenCalled(); }); it('should handle database loading errors gracefully', async () => { @@ -698,9 +697,7 @@ describe('CredentialsOverwrites', () => { }); cipher.decryptV2.mockResolvedValue(JSON.stringify(overwriteData)); - // Mock the reloadFrontendService - const mockReloadFrontendService = vi.fn(); - (pubsubCredentialsOverwrites as any).reloadFrontendService = mockReloadFrontendService; + pubsubCredentialsOverwrites.registerReloadHandler(vi.fn()); // Start first reload const firstReload = pubsubCredentialsOverwrites.reloadOverwriteCredentials(); @@ -757,9 +754,7 @@ describe('CredentialsOverwrites', () => { settingsRepository.findByKey.mockResolvedValue(settingData); cipher.decryptV2.mockResolvedValue(JSON.stringify(overwriteData)); - // Mock the reloadFrontendService - const mockReloadFrontendService = vi.fn(); - (pubsubCredentialsOverwrites as any).reloadFrontendService = mockReloadFrontendService; + pubsubCredentialsOverwrites.registerReloadHandler(vi.fn()); await expect( pubsubCredentialsOverwrites.reloadOverwriteCredentials(), @@ -875,33 +870,29 @@ describe('CredentialsOverwrites', () => { vi.restoreAllMocks(); }); - describe('reloadFrontendService via setData', () => { + describe('reload handler via setData', () => { + let reloadHandler: Mock; + beforeEach(() => { - // Mock the reloadFrontendService method directly to test through setData - vi.spyOn(frontendCredentialsOverwrites as any, 'reloadFrontendService').mockResolvedValue( - undefined, - ); + reloadHandler = vi.fn().mockResolvedValue(undefined); + frontendCredentialsOverwrites.registerReloadHandler(reloadHandler); }); - it('should call reloadFrontendService when reloadFrontend is true', async () => { + it('should call the reload handler when reloadFrontend is true', async () => { const testData = { test: { username: 'frontendUser' } }; - const reloadSpy = frontendCredentialsOverwrites['reloadFrontendService'] as Mock; await frontendCredentialsOverwrites.setData(testData, false, true); - // Verify reloadFrontendService was called - expect(reloadSpy).toHaveBeenCalledTimes(1); + expect(reloadHandler).toHaveBeenCalledTimes(1); expect(frontendCredentialsOverwrites.getAll()).toEqual(testData); }); - it('should skip reloadFrontendService when reloadFrontend is false', async () => { + it('should skip the reload handler when reloadFrontend is false', async () => { const testData = { test: { username: 'noReloadUser' } }; - const reloadSpy = frontendCredentialsOverwrites['reloadFrontendService'] as Mock; await frontendCredentialsOverwrites.setData(testData, false, false); - // Verify reloadFrontendService was NOT called - expect(reloadSpy).not.toHaveBeenCalled(); + expect(reloadHandler).not.toHaveBeenCalled(); expect(frontendCredentialsOverwrites.getAll()).toEqual(testData); }); diff --git a/packages/cli/src/__tests__/wait-tracker.test.ts b/packages/cli/src/__tests__/wait-tracker.test.ts index 1b7eceb8c7d..262e09debd2 100644 --- a/packages/cli/src/__tests__/wait-tracker.test.ts +++ b/packages/cli/src/__tests__/wait-tracker.test.ts @@ -162,7 +162,7 @@ describe('WaitTracker', () => { }, false, false, - execution.id, + { executionId: execution.id, expectedStatus: 'waiting' }, ); }); @@ -185,7 +185,7 @@ describe('WaitTracker', () => { }), false, false, - execution.id, + { executionId: execution.id, expectedStatus: 'waiting' }, ); }); @@ -264,7 +264,7 @@ describe('WaitTracker', () => { }, false, false, - execution.id, + { executionId: execution.id, expectedStatus: 'waiting' }, ); // ACT 1 @@ -285,7 +285,7 @@ describe('WaitTracker', () => { }, false, false, - parentExecution.id, + { executionId: parentExecution.id, expectedStatus: 'waiting' }, ); }); @@ -334,7 +334,7 @@ describe('WaitTracker', () => { }), false, false, - parentExecution.id, + { executionId: parentExecution.id, expectedStatus: 'waiting' }, ); }); @@ -411,13 +411,10 @@ describe('WaitTracker', () => { // ASSERT 1 expect(workflowRunner.run).toHaveBeenCalledTimes(1); - expect(workflowRunner.run).toHaveBeenNthCalledWith( - 1, - expect.any(Object), - false, - false, - execution.id, - ); + expect(workflowRunner.run).toHaveBeenNthCalledWith(1, expect.any(Object), false, false, { + executionId: execution.id, + expectedStatus: 'waiting', + }); // ACT 2 subExecutionPromise.resolve(subworkflowResults); @@ -454,13 +451,10 @@ describe('WaitTracker', () => { // Verify parent was resumed expect(workflowRunner.run).toHaveBeenCalledTimes(2); - expect(workflowRunner.run).toHaveBeenNthCalledWith( - 2, - expect.any(Object), - false, - false, - parentExecution.id, - ); + expect(workflowRunner.run).toHaveBeenNthCalledWith(2, expect.any(Object), false, false, { + executionId: parentExecution.id, + expectedStatus: 'waiting', + }); }); it('should not resume parent execution if it has already finished', async () => { @@ -531,7 +525,7 @@ describe('WaitTracker', () => { }), false, false, - childExecution.id, + { executionId: childExecution.id, expectedStatus: 'waiting' }, ); }); @@ -558,7 +552,7 @@ describe('WaitTracker', () => { }), false, false, - execution.id, + { executionId: execution.id, expectedStatus: 'waiting' }, ); // ACT 2 - Child execution goes into waiting state @@ -664,13 +658,10 @@ describe('WaitTracker', () => { // run() is called once for the sub-workflow, then once per parent resume attempt. // The second call is the first parent attempt; all attempts fail, so run() is called 4 times total // (sub-workflow + the 3 exhausted retries) and the final failure is logged rather than lost - expect(workflowRunner.run).toHaveBeenNthCalledWith( - 2, - expect.any(Object), - false, - false, - parentExecution.id, - ); + expect(workflowRunner.run).toHaveBeenNthCalledWith(2, expect.any(Object), false, false, { + executionId: parentExecution.id, + expectedStatus: 'waiting', + }); expect(workflowRunner.run).toHaveBeenCalledTimes(4); expect(logger.error).toHaveBeenCalled(); }); @@ -695,12 +686,10 @@ describe('WaitTracker', () => { // Parent ultimately resumed (subworkflow + failed parent resume attempt + successful retry = 3 runs), // and because it recovered, nothing is logged as an error expect(workflowRunner.run).toHaveBeenCalledTimes(3); - expect(workflowRunner.run).toHaveBeenLastCalledWith( - expect.any(Object), - false, - false, - parentExecution.id, - ); + expect(workflowRunner.run).toHaveBeenLastCalledWith(expect.any(Object), false, false, { + executionId: parentExecution.id, + expectedStatus: 'waiting', + }); expect(logger.error).not.toHaveBeenCalled(); }); diff --git a/packages/cli/src/__tests__/workflow-helpers.test.ts b/packages/cli/src/__tests__/workflow-helpers.test.ts index 38366665354..f7fabaafdb4 100644 --- a/packages/cli/src/__tests__/workflow-helpers.test.ts +++ b/packages/cli/src/__tests__/workflow-helpers.test.ts @@ -17,6 +17,7 @@ import { VariablesService } from '@/environments.ee/variables/variables.service. import { ExecutionPersistence } from '@/executions/execution-persistence'; import { OwnershipService } from '@/services/ownership.service'; import { + dropInvalidNodeGroups, getLastExecutedNodeData, getLastExecutedNodeRuns, getVariables, @@ -472,10 +473,18 @@ describe('validateWorkflowStructure', () => { }); }); -describe('validateWorkflowNodeGroups', () => { - const makeNode = (id: string) => - ({ id, name: `Node ${id}`, type: 'test', position: [0, 0], parameters: {} }) as never; +// Shared by both node-group suites: the connection fixtures below key off the +// `Node ` naming, so the two must stay defined together. +const makeNode = (id: string) => + ({ id, name: `Node ${id}`, type: 'test', position: [0, 0], parameters: {} }) as never; +const regularNodeType = { group: ['transform'] } as never; +// Two nodes connected n1 → n2 form a groupable chain. +const connectedNodes = [makeNode('n1'), makeNode('n2')]; +const chainConnections = { + 'Node n1': { main: [[{ node: 'Node n2', type: 'main', index: 0 }]] }, +} as never; +describe('validateWorkflowNodeGroups', () => { it('should pass when nodeGroups is undefined', () => { expect(() => validateWorkflowNodeGroups({ nodes: [makeNode('n1')], nodeGroups: undefined }, null), @@ -583,12 +592,8 @@ describe('validateWorkflowNodeGroups', () => { describe('full validation', () => { const triggerType = { group: ['trigger'] } as never; - const regularType = { group: ['transform'] } as never; - // Two nodes connected n1 → n2 form a groupable chain. - const connectedNodes = [makeNode('n1'), makeNode('n2')]; - const connections = { - 'Node n1': { main: [[{ node: 'Node n2', type: 'main', index: 0 }]] }, - } as never; + const regularType = regularNodeType; + const connections = chainConnections; it('passes for a valid connected group', () => { expect(() => @@ -613,7 +618,7 @@ describe('validateWorkflowNodeGroups', () => { }, () => triggerType, ), - ).toThrow('Node group "Has trigger" (g1) cannot contain trigger nodes'); + ).toThrow('Node group "Has trigger" cannot contain trigger nodes'); }); it('does not run full checks when getNodeType is null (basic-only)', () => { @@ -682,13 +687,97 @@ describe('validateWorkflowNodeGroups', () => { getNodeType, ), ).toThrow( - 'Node group "Disconnected" (g1) must form a single connected subgraph with a single entry and exit.', + 'Node group "Disconnected" must form a single connected subgraph with a single entry and exit.', ); }); }); }); }); +describe('dropInvalidNodeGroups', () => { + it('leaves a valid workflow untouched and reports nothing', () => { + const workflow = { + nodes: [makeNode('n1'), makeNode('n2')], + nodeGroups: [{ id: 'g1', name: 'Group', nodeIds: ['n1', 'n2'] }], + }; + + expect(dropInvalidNodeGroups(workflow, null)).toEqual([]); + expect(workflow.nodeGroups).toEqual([{ id: 'g1', name: 'Group', nodeIds: ['n1', 'n2'] }]); + }); + + it('drops every violating group and keeps the valid ones', () => { + const workflow = { + nodes: [makeNode('n1')], + nodeGroups: [ + { id: 'g1', name: 'Valid', nodeIds: ['n1'] }, + { id: 'g2', name: 'Unknown member', nodeIds: ['n999'] }, + ], + }; + + const violations = dropInvalidNodeGroups(workflow, null); + + expect(violations).toHaveLength(1); + expect(violations[0]).toMatchObject({ groupId: 'g2', code: 'unknown-node-id' }); + expect(workflow.nodeGroups).toEqual([{ id: 'g1', name: 'Valid', nodeIds: ['n1'] }]); + }); + + describe('with a shouldDrop predicate', () => { + // Two groups sharing n1: the second is flagged for the overlap, and the + // first for holding a node that now belongs elsewhere. A caller that can + // only blame one of them must be able to drop just that one. + const buildOverlapping = () => ({ + nodes: connectedNodes, + connections: chainConnections, + nodeGroups: [ + { id: 'g1', name: 'First', nodeIds: ['n1', 'n2'] }, + { id: 'g2', name: 'Second', nodeIds: ['n1'] }, + ], + }); + + const regularType = regularNodeType; + + it('drops only the matching groups and reports only those', () => { + const workflow = buildOverlapping(); + + const violations = dropInvalidNodeGroups( + workflow, + () => regularType, + (violation) => violation.groupId === 'g2', + ); + + expect(violations).toHaveLength(1); + expect(violations[0].groupId).toBe('g2'); + expect(workflow.nodeGroups).toEqual([{ id: 'g1', name: 'First', nodeIds: ['n1', 'n2'] }]); + }); + + it('clears the collateral violation once the culprit is gone', () => { + const workflow = buildOverlapping(); + dropInvalidNodeGroups( + workflow, + () => regularType, + (violation) => violation.groupId === 'g2', + ); + + // Second pass: "First" only ever failed because "Second" overlapped it. + expect(dropInvalidNodeGroups(workflow, () => regularType)).toEqual([]); + expect(workflow.nodeGroups).toHaveLength(1); + }); + + it('keeps the workflow untouched when nothing matches', () => { + const workflow = buildOverlapping(); + + expect( + dropInvalidNodeGroups( + workflow, + () => regularType, + () => false, + ), + ).toEqual([]); + expect(workflow.nodeGroups).toHaveLength(2); + }); + }); +}); + describe('sanitizeNodeGroupDescriptions', () => { it('returns no warnings and leaves descriptions within the cap untouched', () => { const workflow = { diff --git a/packages/cli/src/__tests__/workflow-runner.test.ts b/packages/cli/src/__tests__/workflow-runner.test.ts index 1fa45298fac..6edc39615a8 100644 --- a/packages/cli/src/__tests__/workflow-runner.test.ts +++ b/packages/cli/src/__tests__/workflow-runner.test.ts @@ -151,7 +151,7 @@ describe('processError', () => { ); await Container.get(ActiveExecutions).add( { executionMode: 'webhook', workflowData: workflow }, - execution.id, + { executionId: execution.id, expectedStatus: 'waiting' }, ); globalConfig.executions.mode = 'regular'; await runner.processError( @@ -239,6 +239,37 @@ describe('run', () => { expect(recreateNodeExecutionStackSpy).not.toHaveBeenCalled(); }); + it.each([ + { existingExecution: undefined }, + { existingExecution: { executionId: '42', expectedStatus: 'waiting' as const } }, + { existingExecution: { executionId: '42', expectedStatus: 'new' as const } }, + ])('forwards $existingExecution to activeExecutions.add', async ({ existingExecution }) => { + // ARRANGE + const activeExecutions = Container.get(ActiveExecutions); + const addSpy = vi.spyOn(activeExecutions, 'add').mockResolvedValue('1'); + vi.spyOn(activeExecutions, 'attachWorkflowExecution').mockReturnValueOnce(); + const permissionChecker = Container.get(CredentialsPermissionChecker); + vi.spyOn(permissionChecker, 'check').mockResolvedValueOnce(); + vi.spyOn(WorkflowExecute.prototype, 'run').mockReturnValueOnce( + new PCancelable(() => mock()), + ); + + const data = mock({ + triggerToStartFrom: undefined, + workflowData: { nodes: [], staticData: {} }, + executionData: undefined, + startNodes: undefined, + destinationNode: undefined, + runData: undefined, + }); + + // ACT + await runner.run(data, undefined, false, existingExecution); + + // ASSERT + expect(addSpy).toHaveBeenCalledWith(data, existingExecution); + }); + it('run partial execution with additional data', async () => { // ARRANGE const activeExecutions = Container.get(ActiveExecutions); diff --git a/packages/cli/src/active-executions.ts b/packages/cli/src/active-executions.ts index e260c163486..4307865842b 100644 --- a/packages/cli/src/active-executions.ts +++ b/packages/cli/src/active-executions.ts @@ -12,18 +12,19 @@ import type { StructuredChunk, WebhookResponseMode, } from 'n8n-workflow'; -import { - ExecutionCancelledError, - sleep, - SystemShutdownExecutionCancelledError, -} from 'n8n-workflow'; +import { ExecutionCancelledError, SystemShutdownExecutionCancelledError } from 'n8n-workflow'; +import { sleep } from '@n8n/utils/sleep'; import { strict as assert } from 'node:assert'; import type PCancelable from 'p-cancelable'; import { ExecutionAlreadyResumingError } from '@/errors/execution-already-resuming.error'; import { ExecutionNotFoundError } from '@/errors/execution-not-found-error'; import { ExecutionPersistence } from '@/executions/execution-persistence'; -import type { IExecutingWorkflowData, IExecutionsCurrentSummary } from '@/interfaces'; +import type { + IExecutingWorkflowData, + IExecutionsCurrentSummary, + ResumableExecution, +} from '@/interfaces'; import { isWorkflowIdValid } from '@/utils'; import { ConcurrencyCapacityReservation } from './concurrency/concurrency-capacity-reservation'; @@ -60,9 +61,9 @@ export class ActiveExecutions { */ async add( executionData: IWorkflowExecutionDataProcess, - maybeExecutionId?: string, + existingExecution?: ResumableExecution, ): Promise { - let executionStatus: ExecutionStatus = maybeExecutionId ? 'running' : 'new'; + let executionStatus: ExecutionStatus = existingExecution ? 'running' : 'new'; const mode = executionData.executionMode; const capacityReservation = new ConcurrencyCapacityReservation(this.concurrencyControl); @@ -78,8 +79,10 @@ export class ActiveExecutions { // nothing was reserved. const shouldReserveCapacity = mode !== 'evaluation'; + let executionId: string; + try { - if (maybeExecutionId === undefined) { + if (existingExecution === undefined) { const fullExecutionData: CreateExecutionPayload = { data: executionData.executionData!, mode, @@ -97,41 +100,46 @@ export class ActiveExecutions { fullExecutionData.workflowId = workflowId; } - maybeExecutionId = await this.executionPersistence.create(fullExecutionData); - assert(maybeExecutionId); + executionId = await this.executionPersistence.create(fullExecutionData); + assert(executionId); if (shouldReserveCapacity) { - await capacityReservation.reserve({ mode, executionId: maybeExecutionId }); + await capacityReservation.reserve({ mode, executionId }); } if (this.executionsConfig.mode === 'regular') { - await this.executionRepository.setRunning(maybeExecutionId); + await this.executionRepository.setRunning(executionId); } executionStatus = 'running'; } else { // Is an existing execution we want to finish so update in DB + executionId = existingExecution.executionId; if (shouldReserveCapacity) { - await capacityReservation.reserve({ mode, executionId: maybeExecutionId }); + await capacityReservation.reserve({ mode, executionId }); } const execution: Pick = { - id: maybeExecutionId, + id: executionId, data: executionData.executionData!, waitTill: null, status: executionStatus, - // this is resuming, so keep `startedAt` as it was }; const updateSucceeded = await this.executionPersistence.updateExistingExecution( - maybeExecutionId, + executionId, execution, - { requireStatus: 'waiting' }, // Only update if status is 'waiting' + // Only claim the execution if it is still in the status the caller expected + { requireStatus: existingExecution.expectedStatus }, ); if (!updateSucceeded) { // Another process is already resuming this execution - throw new ExecutionAlreadyResumingError(maybeExecutionId); + throw new ExecutionAlreadyResumingError(executionId); + } + + if (existingExecution.expectedStatus === 'new') { + await this.executionRepository.setRunning(executionId); } } } catch (error) { @@ -139,8 +147,6 @@ export class ActiveExecutions { throw error; } - // At this point executionId is guaranteed to be defined - capture it for use in closures - const executionId = maybeExecutionId; const resumingExecution = this.activeExecutions[executionId]; const postExecutePromise = createDeferredPromise(); diff --git a/packages/cli/src/auth/__tests__/auth.service.test.ts b/packages/cli/src/auth/__tests__/auth.service.test.ts index 31125d2fcb8..18dc60b2d56 100644 --- a/packages/cli/src/auth/__tests__/auth.service.test.ts +++ b/packages/cli/src/auth/__tests__/auth.service.test.ts @@ -39,6 +39,7 @@ describe('AuthService', () => { const globalConfig = mock({ auth: { cookie: { secure: true, samesite: 'lax' } }, userManagement: { jwtSecret: 'random-secret' }, + endpoints: { rest: 'rest' }, }); const jwtService = new JwtService(mock(), globalConfig); const urlService = mock(); @@ -685,6 +686,34 @@ describe('AuthService', () => { expect(res.cookie).not.toHaveBeenCalled(); }); + it('should skip browserId check for GET requests matching a RegExp skip entry', async () => { + userRepository.findOne.mockResolvedValue(user); + // Project-scoped routes resolve :projectId into req.baseUrl, so the + // skip entry must be a pattern rather than an exact string. + const req = mock({ + browserId: 'another-browser', + method: 'GET', + baseUrl: '/rest/projects/9xbqXk3hZVlVlPsN/agents/v2', + route: { path: '/:agentId/chat/attachments/:attachmentId' }, + }); + + const result = await authService.resolveJwt(validToken, req, res); + expect(result).toEqual([user, { usedMfa: false }]); + expect(res.cookie).not.toHaveBeenCalled(); + }); + + it('should not skip browserId check for other project-scoped agent routes', async () => { + userRepository.findOne.mockResolvedValue(user); + const req = mock({ + browserId: 'another-browser', + method: 'GET', + baseUrl: '/rest/projects/9xbqXk3hZVlVlPsN/agents/v2', + route: { path: '/:agentId/chat/:threadId/messages' }, + }); + + await expect(authService.resolveJwt(validToken, req, res)).rejects.toThrow('Unauthorized'); + }); + it('should not skip browserId check for POST requests on skip endpoints', async () => { userRepository.findOne.mockResolvedValue(user); const req = mock({ diff --git a/packages/cli/src/auth/auth.service.ts b/packages/cli/src/auth/auth.service.ts index 6f0d019948d..03f8c6d6b08 100644 --- a/packages/cli/src/auth/auth.service.ts +++ b/packages/cli/src/auth/auth.service.ts @@ -39,6 +39,10 @@ interface PasswordResetToken { hash: string; } +function escapeRegExp(value: string): string { + return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); +} + interface CreateAuthMiddlewareOptions { /** * If true, MFA is not enforced @@ -58,8 +62,13 @@ interface CreateAuthMiddlewareOptions { @Service() export class AuthService { - // The browser-id check needs to be skipped on these endpoints - private skipBrowserIdCheckEndpoints: string[]; + /** + * Endpoints exempt from the browser-id check on GET requests. Strings are + * matched exactly against `baseUrl + route path`; RegExps cover routes + * whose controller prefix carries resolved params (e.g. a `:projectId` + * that express substitutes into `req.baseUrl`). + */ + private skipBrowserIdCheckEndpoints: Array; constructor( private readonly globalConfig: GlobalConfig, @@ -100,6 +109,13 @@ export class AuthService { // Skip browser ID check for Instance AI SSE endpoint — EventSource can't send custom headers `/${restEndpoint}/instance-ai/events/:threadId`, + + // Agent chat attachments render via tags, which can't send the + // browser-id header. The controller prefix carries a resolved + // :projectId in req.baseUrl, so this one needs a pattern. + new RegExp( + `^/${escapeRegExp(restEndpoint)}/projects/[^/]+/agents/v2/:agentId/chat/attachments/:attachmentId$`, + ), ]; } @@ -316,13 +332,19 @@ export class AuthService { throw new AuthError('Unauthorized'); } + private endpointSkipsBrowserIdCheck(endpoint: string): boolean { + return this.skipBrowserIdCheckEndpoints.some((entry) => + typeof entry === 'string' ? entry === endpoint : entry.test(endpoint), + ); + } + private validateBrowserId( jwtPayload: IssuedJWT, browserId: string | undefined, endpoint: string, method: string, ) { - if (method === 'GET' && this.skipBrowserIdCheckEndpoints.includes(endpoint)) { + if (method === 'GET' && this.endpointSkipsBrowserIdCheck(endpoint)) { this.logger.debug(`Skipped browserId check on ${endpoint}`); } else if ( jwtPayload.browserId && diff --git a/packages/cli/src/chat/__tests__/chat-execution-manager.test.ts b/packages/cli/src/chat/__tests__/chat-execution-manager.test.ts index 75e80585081..6ef26ba01dc 100644 --- a/packages/cli/src/chat/__tests__/chat-execution-manager.test.ts +++ b/packages/cli/src/chat/__tests__/chat-execution-manager.test.ts @@ -65,7 +65,10 @@ describe('ChatExecutionManager', () => { await chatExecutionManager.runWorkflow(execution, message); - expect(workflowRunner.run).toHaveBeenCalledWith(runData, true, true, '1'); + expect(workflowRunner.run).toHaveBeenCalledWith(runData, true, true, { + executionId: '1', + expectedStatus: 'waiting', + }); }); }); @@ -275,6 +278,59 @@ describe('ChatExecutionManager', () => { }); }); + describe('when node is a HITL tool', () => { + // HITL tools carry a generated `HitlTool` type (e.g. chatHitlTool), + // not CHAT_TOOL_NODE_TYPE, but must get the same resume fix-up so the + // approval is logged on the ai_tool channel and the gated tool executes. + const HITL_TOOL_NODE_TYPE = '@n8n/n8n-nodes-langchain.chatHitlTool'; + const message: ChatMessage = { sessionId: '123', action: 'sendMessage', chatInput: '' }; + + function makeHitlToolExecution() { + return { + id: '1', + workflowData: { id: 'workflowId' }, + data: { + resultData: { + pinData: {}, + lastNodeExecuted: 'HitlToolNode', + runData: { + HitlToolNode: [ + { startTime: 123, executionTime: 456, executionIndex: 0, source: [] }, + ], + }, + }, + executionData: { + nodeExecutionStack: [ + { + node: { name: 'HitlToolNode', type: HITL_TOOL_NODE_TYPE, disabled: false }, + data: { main: [[]] }, + }, + ], + }, + pushRef: 'pushRef', + waitTill: new Date(), + }, + mode: 'manual', + } as any; + } + + beforeEach(() => { + vi.spyOn(chatExecutionManager as any, 'runNode').mockResolvedValue(null); + ownershipService.getWorkflowProjectCached.mockResolvedValue({ id: 'projectId' } as any); + }); + + it('should disable node, clear waitTill and rewire output to AiTool', async () => { + const execution = makeHitlToolExecution(); + + await (chatExecutionManager as any).getRunData(execution, message); + + const resumingNode = execution.data.executionData.nodeExecutionStack[0].node; + expect(resumingNode.disabled).toBe(true); + expect(execution.data.waitTill).toBeUndefined(); + expect(resumingNode.rewireOutputLogTo).toBe(NodeConnectionTypes.AiTool); + }); + }); + describe('when node is a regular chat node', () => { const message: ChatMessage = { sessionId: '123', action: 'sendMessage', chatInput: 'input' }; diff --git a/packages/cli/src/chat/chat-execution-manager.ts b/packages/cli/src/chat/chat-execution-manager.ts index 7beef922b80..d80fd4d8776 100644 --- a/packages/cli/src/chat/chat-execution-manager.ts +++ b/packages/cli/src/chat/chat-execution-manager.ts @@ -13,6 +13,7 @@ import { UnexpectedError, CHAT_TOOL_NODE_TYPE, NodeConnectionTypes, + isHitlToolType, } from 'n8n-workflow'; import { NotFoundError } from '../errors/response-errors/not-found.error'; @@ -36,12 +37,10 @@ export class ChatExecutionManager { ) {} async runWorkflow(execution: IExecutionResponse, message: ChatMessage) { - await this.workflowRunner.run( - await this.getRunData(execution, message), - true, - true, - execution.id, - ); + await this.workflowRunner.run(await this.getRunData(execution, message), true, true, { + executionId: execution.id, + expectedStatus: 'waiting', + }); } async cancelExecution(executionId: string) { @@ -158,11 +157,16 @@ export class ChatExecutionManager { [{ json: message }], ]; - if (runExecutionData.executionData!.nodeExecutionStack[0].node.type === CHAT_TOOL_NODE_TYPE) { + // The chat-based HITL tool resumes here too, but carries the generated + // `chatHitlTool` type rather than `chatTool`. Without this its output stays on + // the `main` channel instead of `ai_tool`, so the agent never sees the approval + // and the gated tool is never executed. The webhook resume path keys off the + // same `HitlTool` suffix. + const resumingNode = runExecutionData.executionData!.nodeExecutionStack[0].node; + if (resumingNode.type === CHAT_TOOL_NODE_TYPE || isHitlToolType(resumingNode.type)) { runExecutionData.waitTill = undefined; - runExecutionData.executionData!.nodeExecutionStack[0].node.disabled = true; - runExecutionData.executionData!.nodeExecutionStack[0].node.rewireOutputLogTo = - NodeConnectionTypes.AiTool; + resumingNode.disabled = true; + resumingNode.rewireOutputLogTo = NodeConnectionTypes.AiTool; const lastNodeExecuted = runExecutionData.resultData.lastNodeExecuted as string; const runDataArray = runExecutionData.resultData.runData[lastNodeExecuted]; diff --git a/packages/cli/src/commands/base-command.ts b/packages/cli/src/commands/base-command.ts index 8c7655e567f..45a89664d82 100644 --- a/packages/cli/src/commands/base-command.ts +++ b/packages/cli/src/commands/base-command.ts @@ -25,7 +25,8 @@ import { ExecutionContextHookRegistry, StorageConfig, } from 'n8n-core'; -import { Expression, sleep, UnexpectedError } from 'n8n-workflow'; +import { sleep } from '@n8n/utils/sleep'; +import { Expression, UnexpectedError } from 'n8n-workflow'; import type { AbstractServer } from '@/abstract-server'; import * as CrashJournal from '@/crash-journal'; diff --git a/packages/cli/src/commands/import/workflow.ts b/packages/cli/src/commands/import/workflow.ts index 1c539a3c2f4..8f10e20013d 100644 --- a/packages/cli/src/commands/import/workflow.ts +++ b/packages/cli/src/commands/import/workflow.ts @@ -16,9 +16,9 @@ import { jsonParse, UserError } from 'n8n-workflow'; import { z } from 'zod'; import { UM_FIX_INSTRUCTION } from '@/constants'; +import { EventService } from '@/events/event.service'; import type { IWorkflowToImport, IWorkflowWithVersionMetadata } from '@/interfaces'; import { ImportService } from '@/services/import.service'; -import { EventService } from '@/events/event.service'; import { BaseCommand } from '../base-command'; diff --git a/packages/cli/src/commands/start.ts b/packages/cli/src/commands/start.ts index f2573a2255f..137fa69dac7 100644 --- a/packages/cli/src/commands/start.ts +++ b/packages/cli/src/commands/start.ts @@ -14,7 +14,8 @@ import glob from 'fast-glob'; import { createReadStream, createWriteStream, existsSync } from 'fs'; import { mkdir } from 'fs/promises'; import { BinaryDataConfig } from 'n8n-core'; -import { jsonParse, sleep, type IWorkflowExecutionDataProcess } from 'n8n-workflow'; +import { sleep } from '@n8n/utils/sleep'; +import { jsonParse } from 'n8n-workflow'; import path from 'path'; import replaceStream from 'replacestream'; import { pipeline } from 'stream/promises'; @@ -29,7 +30,6 @@ import { DeprecationService } from '@/deprecation/deprecation.service'; import { FeatureNotLicensedError } from '@/errors/feature-not-licensed.error'; import { MessageEventBus } from '@/eventbus/message-event-bus/message-event-bus'; import { EventService } from '@/events/event.service'; -import { ExecutionService } from '@/executions/execution.service'; import { LoadNodesAndCredentials } from '@/load-nodes-and-credentials'; import { N8NCheckpointStorage } from '@/modules/agents/integrations/n8n-checkpoint-storage'; import { MultiMainSetup } from '@/scaling/multi-main-setup.ee'; @@ -40,13 +40,11 @@ import { DurableScheduler } from '@/scheduling/durable-scheduler'; import { PollJobProvider } from '@/scheduling/poll-trigger-node/poll-job-provider'; import { Server } from '@/server'; import { JwtService } from '@/services/jwt.service'; -import { OwnershipService } from '@/services/ownership.service'; import { ExecutionsPruningService } from '@/services/pruning/executions-pruning.service'; import { WorkflowHistoryCompactionService } from '@/services/pruning/workflow-history-compaction.service'; import { WorkflowStatisticsRollupService } from '@/services/workflow-statistics-rollup.service'; import { UrlService } from '@/services/url.service'; import { WaitTracker } from '@/wait-tracker'; -import { WorkflowRunner } from '@/workflow-runner'; import { BaseCommand } from './base-command'; @@ -411,7 +409,10 @@ export class Start extends BaseCommand> { Container.get(DurableScheduler).start(); if (this.globalConfig.executions.mode === 'regular') { - await this.runEnqueuedExecutions(); + const { EnqueuedExecutionRecoveryService } = await import( + '@/executions/enqueued-execution-recovery.service.js' + ); + await Container.get(EnqueuedExecutionRecoveryService).recoverEnqueuedExecutions(); } // Start to get active workflows and run their triggers @@ -499,41 +500,4 @@ export class Start extends BaseCommand> { if (error.stack) this.logger.error(error.stack); await this.exitWithCrash('Exiting due to an error.', error); } - - /** - * During startup, we may find executions that had been enqueued at the time of shutdown. - * - * If so, start running any such executions concurrently up to the concurrency limit, and - * enqueue any remaining ones until we have spare concurrency capacity again. - */ - private async runEnqueuedExecutions() { - const executions = await Container.get(ExecutionService).findAllEnqueuedExecutions(); - - if (executions.length === 0) return; - - this.logger.debug('[Startup] Found enqueued executions to run', { - executionIds: executions.map((e) => e.id), - }); - - const ownershipService = Container.get(OwnershipService); - const workflowRunner = Container.get(WorkflowRunner); - - for (const execution of executions) { - const project = await ownershipService.getWorkflowProjectCached(execution.workflowId); - - const data: IWorkflowExecutionDataProcess = { - executionMode: execution.mode, - executionData: execution.data, - workflowData: execution.workflowData, - projectId: project.id, - }; - - Container.get(EventService).emit('execution-started-during-bootup', { - executionId: execution.id, - }); - - // do not block - each execution either runs concurrently or is queued - void workflowRunner.run(data, undefined, false, execution.id); - } - } } diff --git a/packages/cli/src/concurrency/__tests__/concurrency-queue.test.ts b/packages/cli/src/concurrency/__tests__/concurrency-queue.test.ts index c8fb3c0c616..6a5e43cdc6f 100644 --- a/packages/cli/src/concurrency/__tests__/concurrency-queue.test.ts +++ b/packages/cli/src/concurrency/__tests__/concurrency-queue.test.ts @@ -1,4 +1,4 @@ -import { sleep } from 'n8n-workflow'; +import { sleep } from '@n8n/utils/sleep'; import { ConcurrencyQueue } from '../concurrency-queue'; diff --git a/packages/cli/src/controllers/api-keys.controller.ts b/packages/cli/src/controllers/api-keys.controller.ts index 7e6ede7a3a1..d3611a4cfc9 100644 --- a/packages/cli/src/controllers/api-keys.controller.ts +++ b/packages/cli/src/controllers/api-keys.controller.ts @@ -61,8 +61,9 @@ export class ApiKeysController { }; } - // `apiKey:manage` callers see every key by default; `ownership=mine` narrows to own. - @GlobalScope('apiKey:list') + // Every authenticated user may list their own keys. The service only + // includes other users' keys for `apiKey:manage` callers; `ownership=mine` + // narrows back to own. @Get('/', { middlewares: [isApiEnabledMiddleware] }) async getApiKeys(req: AuthenticatedRequest, _res: Response, @Query query: ListApiKeysQueryDto) { return await this.publicApiKeyService.getRedactedApiKeys(req.user, { @@ -75,8 +76,8 @@ export class ApiKeysController { }); } - // Members can delete their own keys; `apiKey:manage` holders can revoke anyone's. - @GlobalScope('apiKey:delete') + // No role scope required: own keys are always revocable. The service + // restricts deleting other users' keys to `apiKey:manage` holders. @Delete('/:id', { middlewares: [isApiEnabledMiddleware] }) async deleteApiKey(req: AuthenticatedRequest, _res: Response, @Param('id') apiKeyId: string) { const { isOwn } = await this.publicApiKeyService.deleteApiKey(req.user, apiKeyId); @@ -124,7 +125,8 @@ export class ApiKeysController { }; } - @GlobalScope('apiKey:list') + // No role scope required: returns the scopes the caller's role can assign + // to a key — empty-ish for roles without apiKey grants. @Get('/scopes', { middlewares: [isApiEnabledMiddleware] }) async getApiKeyScopes(req: AuthenticatedRequest, _res: Response) { const scopes = getApiKeyScopesForRole(req.user); diff --git a/packages/cli/src/controllers/e2e.controller.ts b/packages/cli/src/controllers/e2e.controller.ts index 7b574d7ee41..cf68a585740 100644 --- a/packages/cli/src/controllers/e2e.controller.ts +++ b/packages/cli/src/controllers/e2e.controller.ts @@ -265,7 +265,20 @@ export class E2EController { @Post('/scheduled-jobs/fire-now', { skipAuth: true }) async fireScheduledJobsNow(req: Request<{}, {}, { workflowId: string; nodeId: string }>) { const { workflowId, nodeId } = req.body; - await this.scheduledJobRepository.forceDueNowByWorkflowNode(workflowId, nodeId); + await this.scheduledJobRepository.backdateNextRunAt(workflowId, nodeId, 0); + return { success: true }; + } + + /** + * Backdates a job's `nextRunAt`, so the next materializer pass sees the same + * overdue schedule it would after a real outage of that length. + */ + @Post('/scheduled-jobs/backdate', { skipAuth: true }) + async backdateScheduledJob( + req: Request<{}, {}, { workflowId: string; nodeId: string; secondsAgo: number }>, + ) { + const { workflowId, nodeId, secondsAgo } = req.body; + await this.scheduledJobRepository.backdateNextRunAt(workflowId, nodeId, secondsAgo); return { success: true }; } diff --git a/packages/cli/src/crash-journal.ts b/packages/cli/src/crash-journal.ts index 23c32a60988..0632aafda92 100644 --- a/packages/cli/src/crash-journal.ts +++ b/packages/cli/src/crash-journal.ts @@ -2,8 +2,8 @@ import { inProduction, Logger } from '@n8n/backend-common'; import { Container } from '@n8n/di'; import { existsSync } from 'fs'; import { mkdir, utimes, open, rm } from 'fs/promises'; +import { sleep } from '@n8n/utils/sleep'; import { InstanceSettings } from 'n8n-core'; -import { sleep } from 'n8n-workflow'; import { join, dirname } from 'path'; export const touchFile = async (filePath: string): Promise => { diff --git a/packages/cli/src/credentials-overwrites.ts b/packages/cli/src/credentials-overwrites.ts index 030733e9002..0c4dcae83c0 100644 --- a/packages/cli/src/credentials-overwrites.ts +++ b/packages/cli/src/credentials-overwrites.ts @@ -28,6 +28,13 @@ export class CredentialsOverwrites { private readonly cipher: Cipher, ) {} + /** Registered by FrontendService to regenerate credential types after overwrites change, without this module depending on it. */ + private reloadHandler?: () => Promise; + + registerReloadHandler(handler: () => Promise) { + this.reloadHandler = handler; + } + async init() { const data = this.globalConfig.credentials.overwrite.data; if (data) { @@ -126,17 +133,10 @@ export class CredentialsOverwrites { } if (reloadFrontend) { - await this.reloadFrontendService(); + await this.reloadHandler?.(); } } - private async reloadFrontendService() { - // FrontendService has CredentialOverwrites injected via the constructor - // to break the circular dependency we need to use the container to get the instance - const { FrontendService } = await import('./services/frontend.service.js'); - await Container.get(FrontendService)?.generateTypes(); - } - applyOverwrite(type: string, data: ICredentialDataDecryptedObject) { const overwrites = this.get(type); diff --git a/packages/cli/src/credentials/__tests__/credentials.controller.test.ts b/packages/cli/src/credentials/__tests__/credentials.controller.test.ts index 2a2c67cbd11..a61400d0ef8 100644 --- a/packages/cli/src/credentials/__tests__/credentials.controller.test.ts +++ b/packages/cli/src/credentials/__tests__/credentials.controller.test.ts @@ -114,6 +114,8 @@ describe('CredentialsController', () => { ensureCanManageEndUserCredentialSpy = vi .spyOn(credentialsService, 'ensureCanManageEndUserCredential') .mockResolvedValue(undefined); + // stubbed by default: backed by connectionStatusProxy, which unit tests don't wire up + vi.spyOn(credentialsService, 'populateConnectedByMe').mockResolvedValue(undefined); findCredentialOwningProjectSpy = sharedCredentialsRepository.findCredentialOwningProject; emitSpy = eventService.emit; // Set up credentialsRepository.create to return the input data @@ -850,6 +852,7 @@ describe('CredentialsController', () => { expect(getChangedSharedFieldsSpy).toHaveBeenCalled(); expect(updateSpy).toHaveBeenCalledWith(credentialId, expect.any(Object), expect.any(Object), { deleteUserEntries: true, + user: ownerReq.user, }); expect(emitSpy).toHaveBeenCalledWith('private-credential-connections-cleared', { user: ownerReq.user, @@ -879,6 +882,7 @@ describe('CredentialsController', () => { expect(updateSpy).toHaveBeenCalledWith(credentialId, expect.any(Object), expect.any(Object), { deleteUserEntries: false, + user: ownerReq.user, }); const emittedEventNames = emitSpy.mock.calls.map((call) => call[0]); expect(emittedEventNames).not.toContain('private-credential-connections-cleared'); @@ -906,6 +910,7 @@ describe('CredentialsController', () => { expect(getChangedSharedFieldsSpy).not.toHaveBeenCalled(); expect(updateSpy).toHaveBeenCalledWith(credentialId, expect.any(Object), expect.any(Object), { deleteUserEntries: true, + user: ownerReq.user, }); }); diff --git a/packages/cli/src/credentials/__tests__/credentials.service.test.ts b/packages/cli/src/credentials/__tests__/credentials.service.test.ts index 0f32205417b..9a218971117 100644 --- a/packages/cli/src/credentials/__tests__/credentials.service.test.ts +++ b/packages/cli/src/credentials/__tests__/credentials.service.test.ts @@ -1122,6 +1122,42 @@ describe('CredentialsService', () => { encrypted, ); }); + + it('enriches the returned credential with connectedByMe when a user is passed', async () => { + const credential = mock({ + id: 'private-credential', + isResolvable: true, + }); + const transactionManager = mock(); + transactionManager.findOneBy.mockResolvedValue(credential); + setRepositoryTransaction(transactionManager); + const encrypted = mock({ id: credential.id }); + connectionStatusProxy.findConnectedCredentialIds.mockResolvedValue(new Set([credential.id])); + + const result = await service.update(credential.id, encrypted, undefined, { + user: ownerUser, + }); + + expect(connectionStatusProxy.findConnectedCredentialIds).toHaveBeenCalledWith(ownerUser.id, [ + credential.id, + ]); + expect(result).toMatchObject({ connectedByMe: true }); + }); + + it('does not enrich the returned credential when no user is passed', async () => { + const credential = mock({ + id: 'private-credential', + isResolvable: true, + }); + const transactionManager = mock(); + transactionManager.findOneBy.mockResolvedValue(credential); + setRepositoryTransaction(transactionManager); + const encrypted = mock({ id: credential.id }); + + await service.update(credential.id, encrypted); + + expect(connectionStatusProxy.findConnectedCredentialIds).not.toHaveBeenCalled(); + }); }); describe('updateInstanceCredential', () => { @@ -3465,6 +3501,34 @@ describe('CredentialsService', () => { }); }); + describe('unredact (field-level sentinel restore)', () => { + const saved = { + privateKey: '-----BEGIN RSA PRIVATE KEY-----\nabc\n-----END RSA PRIVATE KEY-----', + }; + + it('should restore the saved value for an exact blanking sentinel', () => { + const result = service.unredact({ privateKey: CREDENTIAL_BLANKING_VALUE }, saved); + expect(result.privateKey).toBe(saved.privateKey); + }); + + it('should restore the saved value when the sentinel was switched to expression mode', () => { + // Toggling a redacted field to expression mode prepends `=`; the saved + // key must still be restored instead of persisting the sentinel. + const result = service.unredact({ privateKey: `=${CREDENTIAL_BLANKING_VALUE}` }, saved); + expect(result.privateKey).toBe(saved.privateKey); + }); + + it('should restore the saved value for an expression-prefixed empty sentinel', () => { + const result = service.unredact({ privateKey: `=${CREDENTIAL_EMPTY_VALUE}` }, saved); + expect(result.privateKey).toBe(saved.privateKey); + }); + + it('should keep a genuine new value that is not a sentinel', () => { + const result = service.unredact({ privateKey: '={{ $secrets.key }}' }, saved); + expect(result.privateKey).toBe('={{ $secrets.key }}'); + }); + }); + describe('checkCredentialData', () => { const testProjectId = 'test-project-id'; diff --git a/packages/cli/src/credentials/credentials.controller.ts b/packages/cli/src/credentials/credentials.controller.ts index 4b642c15e2c..cedb850f197 100644 --- a/packages/cli/src/credentials/credentials.controller.ts +++ b/packages/cli/src/credentials/credentials.controller.ts @@ -315,6 +315,7 @@ export class CredentialsController { { deleteUserEntries: isTogglingToStatic || sharedFieldsChanged, instanceCredential: credential.usageScope === 'instance' ? credential : undefined, + user: req.user, }, ); diff --git a/packages/cli/src/credentials/credentials.service.ts b/packages/cli/src/credentials/credentials.service.ts index a0573badf5f..89685f7884e 100644 --- a/packages/cli/src/credentials/credentials.service.ts +++ b/packages/cli/src/credentials/credentials.service.ts @@ -102,6 +102,8 @@ type UpdateOptions = { deleteUserEntries?: boolean; /** Existing instance credential whose hook-mutated payload must be revalidated. */ instanceCredential?: Pick; + /** When provided, the returned entity is enriched with `connectedByMe` for this user. */ + user?: User; }; type CreateCredentialOptions = CreateCredentialDto & { @@ -908,23 +910,37 @@ export class CredentialsService { return await transactionManager.findOneBy(CredentialsEntity, { id: credentialId }); }; + let result: CredentialsEntity | null; if (!options?.instanceCredential) { - return await this.credentialsRepository.manager.transaction(persist); + result = await this.credentialsRepository.manager.transaction(persist); + } else { + const instanceCredential = options.instanceCredential; + const hookedData = await this.getValidatedInstanceCredentialHookData( + newCredentialData, + instanceCredential.id, + instanceCredential.type, + ); + result = await this.dbLockService.withLock( + DbLock.INSTANCE_AI_SETTINGS, + async (transactionManager, ctx) => { + await this.validateInstanceCredentialUpdate( + instanceCredential, + hookedData, + undefined, + ctx, + ); + return await persist(transactionManager); + }, + ); } - const instanceCredential = options.instanceCredential; - const hookedData = await this.getValidatedInstanceCredentialHookData( - newCredentialData, - instanceCredential.id, - instanceCredential.type, - ); - return await this.dbLockService.withLock( - DbLock.INSTANCE_AI_SETTINGS, - async (transactionManager, ctx) => { - await this.validateInstanceCredentialUpdate(instanceCredential, hookedData, undefined, ctx); - return await persist(transactionManager); - }, - ); + // Reflect connections cleared above by deleteUserEntries, not a stale pre-update value. + const enriched: (CredentialsEntity & { connectedByMe?: boolean }) | null = result; + if (enriched && options?.user) { + await this.populateConnectedByMe([enriched], options.user); + } + + return enriched; } /** @@ -1311,7 +1327,11 @@ export class CredentialsService { private unredactRestoreValues(unmerged: any, replacement: any) { // eslint-disable-next-line @typescript-eslint/no-unsafe-argument for (const [key, value] of Object.entries(unmerged)) { - if (value === CREDENTIAL_BLANKING_VALUE || value === CREDENTIAL_EMPTY_VALUE) { + // Strip a leading `=`: switching a redacted field to expression mode + // prepends it to the sentinel, which would otherwise defeat the match + // and persist the sentinel as the real value. + const sentinel = typeof value === 'string' && value.startsWith('=') ? value.slice(1) : value; + if (sentinel === CREDENTIAL_BLANKING_VALUE || sentinel === CREDENTIAL_EMPTY_VALUE) { // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access unmerged[key] = replacement[key]; } else if ( diff --git a/packages/cli/src/errors/__tests__/http-error-serializers.test.ts b/packages/cli/src/errors/__tests__/http-error-serializers.test.ts index 057354b827b..8bfef0f842c 100644 --- a/packages/cli/src/errors/__tests__/http-error-serializers.test.ts +++ b/packages/cli/src/errors/__tests__/http-error-serializers.test.ts @@ -7,6 +7,7 @@ import { } from '@/errors/http-error-serializers'; import { LicenseEulaRequiredError } from '@/errors/response-errors/license-eula-required.error'; import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { WorkflowPublishBlockedError } from '@/errors/response-errors/workflow-publish-blocked.error'; import { toImportBlockedError } from '@/modules/n8n-packages/engine/import-blocked.error'; describe('http-error-serializers', () => { @@ -49,6 +50,36 @@ describe('http-error-serializers', () => { }); }); + it('returns review details publicly while keeping editor-only validation metadata internal', () => { + const descriptor = classifyHttpError( + new WorkflowPublishBlockedError({ + reason: 'changes_requested', + workflowReviewRequestId: 'review-1', + }), + ); + + expect(serializePublicApiError(descriptor)).toEqual({ + status: 409, + body: { + message: expect.stringContaining('requested changes'), + reason: 'changes_requested', + workflowReviewRequestId: 'review-1', + }, + }); + expect(serializeInternalRestError(descriptor)).toEqual({ + status: 409, + body: { + code: 409, + message: expect.stringContaining('requested changes'), + meta: { + reason: 'changes_requested', + workflowReviewRequestId: 'review-1', + validationError: true, + }, + }, + }); + }); + it('serializePublicApiError: 422 with issues when only credentials are unresolved', () => { const issues = [ { diff --git a/packages/cli/src/errors/http-error-serializers.ts b/packages/cli/src/errors/http-error-serializers.ts index e6e8dba7921..66e0ed6a266 100644 --- a/packages/cli/src/errors/http-error-serializers.ts +++ b/packages/cli/src/errors/http-error-serializers.ts @@ -1,3 +1,5 @@ +import { isWorkflowPublishBlockedDetails } from '@n8n/api-types'; + import { HttpErrorKind, type HttpErrorDescriptor } from '@/errors/http-error-classifier'; const GENERIC_PUBLIC_MESSAGE = 'Internal server error'; @@ -24,9 +26,17 @@ export function serializePublicApiError(descriptor: HttpErrorDescriptor): { if (descriptor.meta?.issues !== undefined) { body.issues = descriptor.meta.issues; } + const workflowPublishBlockedDetails = { + reason: descriptor.meta?.reason, + workflowReviewRequestId: descriptor.meta?.workflowReviewRequestId, + }; return { status: descriptor.status, - body, + body: { + ...body, + ...(isWorkflowPublishBlockedDetails(workflowPublishBlockedDetails) && + workflowPublishBlockedDetails), + }, }; } case HttpErrorKind.userError: diff --git a/packages/cli/src/errors/response-errors/workflow-publish-blocked.error.ts b/packages/cli/src/errors/response-errors/workflow-publish-blocked.error.ts new file mode 100644 index 00000000000..3d6d5500b25 --- /dev/null +++ b/packages/cli/src/errors/response-errors/workflow-publish-blocked.error.ts @@ -0,0 +1,19 @@ +import type { WorkflowPublishBlockedDetails, WorkflowPublishBlockedReason } from '@n8n/api-types'; + +import { ConflictError } from './conflict.error'; + +const messages: Record = { + review_pending: + "Workflow can't be published while its review is open. Submit this version to the review, or wait for the review to close.", + changes_requested: + "Workflow can't be published because its review has requested changes. Submit this version to the review, or wait for the review to close.", +}; + +export class WorkflowPublishBlockedError extends ConflictError { + constructor(readonly details: WorkflowPublishBlockedDetails) { + super(messages[details.reason], undefined, { + ...details, + validationError: true, + }); + } +} diff --git a/packages/cli/src/errors/webhook-response-too-large.error.ts b/packages/cli/src/errors/webhook-response-too-large.error.ts new file mode 100644 index 00000000000..59e9dd97057 --- /dev/null +++ b/packages/cli/src/errors/webhook-response-too-large.error.ts @@ -0,0 +1,13 @@ +import { UserError } from 'n8n-workflow'; + +export class WebhookResponseTooLargeError extends UserError { + constructor(maxSizeInMiB: number) { + super( + `The response is too large to be sent back from the worker (limit is ${maxSizeInMiB} MiB)`, + { + description: + 'In scaling mode a response is relayed to the main instance through the queue, which limits how large it can be. Respond with binary data to have the payload streamed from storage instead, or raise N8N_WEBHOOK_RESPONSE_RELAY_SIZE_MAX.', + }, + ); + } +} diff --git a/packages/cli/src/events/__tests__/log-streaming-event-relay.test.ts b/packages/cli/src/events/__tests__/log-streaming-event-relay.test.ts index 7330a52cc47..68acee477e9 100644 --- a/packages/cli/src/events/__tests__/log-streaming-event-relay.test.ts +++ b/packages/cli/src/events/__tests__/log-streaming-event-relay.test.ts @@ -78,6 +78,8 @@ describe('LogStreamingEventRelay', () => { dataTableSchemaConflictPolicy: 'keep-existing', variableMissingMode: 'create-stub', variableParentPolicy: 'project', + tagMissingMode: 'create', + tagConflictPolicy: 'skip', }, packageSourceId: 'source-instance-1', packageVersion: '1', @@ -109,6 +111,13 @@ describe('LogStreamingEventRelay', () => { created: 0, requirements: 1, }, + tags: { + matched: 1, + created: 0, + renamed: 0, + skipped: 0, + requirements: 1, + }, }, }; @@ -137,6 +146,8 @@ describe('LogStreamingEventRelay', () => { dataTableSchemaConflictPolicy: 'keep-existing', variableMissingMode: 'create-stub', variableParentPolicy: 'project', + tagMissingMode: 'create', + tagConflictPolicy: 'skip', }, packageSourceId: 'source-instance-1', packageVersion: '1', @@ -168,6 +179,7 @@ describe('LogStreamingEventRelay', () => { credentials: 1, dataTables: 1, variables: 1, + tags: 1, }, }; diff --git a/packages/cli/src/events/__tests__/telemetry-event-relay.test.ts b/packages/cli/src/events/__tests__/telemetry-event-relay.test.ts index 20342221c6c..485462ed74a 100644 --- a/packages/cli/src/events/__tests__/telemetry-event-relay.test.ts +++ b/packages/cli/src/events/__tests__/telemetry-event-relay.test.ts @@ -2238,6 +2238,8 @@ describe('TelemetryEventRelay', () => { dataTableSchemaConflictPolicy: 'keep-existing', variableMissingMode: 'create-stub', variableParentPolicy: 'global', + tagMissingMode: 'create', + tagConflictPolicy: 'rename', }, packageSourceId: 'source-instance-1', packageVersion: '1', @@ -2268,6 +2270,13 @@ describe('TelemetryEventRelay', () => { created: 3, requirements: 2, }, + tags: { + matched: 6, + created: 7, + renamed: 8, + skipped: 9, + requirements: 10, + }, }, }; @@ -2286,6 +2295,8 @@ describe('TelemetryEventRelay', () => { data_table_schema_conflict_policy: 'keep-existing', variable_missing_mode: 'create-stub', variable_parent_policy: 'global', + tag_missing_mode: 'create', + tag_conflict_policy: 'rename', workflows_created: 2, workflows_updated: 1, workflows_skipped: 1, @@ -2299,6 +2310,11 @@ describe('TelemetryEventRelay', () => { variables_missing: 1, variables_created: 3, variables_required: 2, + tags_matched: 6, + tags_created: 7, + tags_renamed: 8, + tags_skipped: 9, + tags_required: 10, }); }); @@ -2313,6 +2329,7 @@ describe('TelemetryEventRelay', () => { credentials: 2, dataTables: 1, variables: 4, + tags: 2, }, }; @@ -2325,6 +2342,7 @@ describe('TelemetryEventRelay', () => { credential_count: 2, data_table_count: 1, variable_count: 4, + tag_count: 2, }); }); diff --git a/packages/cli/src/events/maps/workflow-publication-metrics.event-map.ts b/packages/cli/src/events/maps/workflow-publication-metrics.event-map.ts index da1e25bdf6d..340270b2b90 100644 --- a/packages/cli/src/events/maps/workflow-publication-metrics.event-map.ts +++ b/packages/cli/src/events/maps/workflow-publication-metrics.event-map.ts @@ -57,4 +57,10 @@ export type WorkflowPublicationMetricsEventMap = { versionSkewCount: number; durationMs: number; }; + + /** Emitted only when a sweep found something, so idle follower passes stay off the bus. */ + 'workflow-publication-ghost-trigger-sweep': { + /** Workflows whose ghost triggers were torn down on a non-leader instance. */ + removedCount: number; + }; }; diff --git a/packages/cli/src/events/relays/telemetry.event-relay.ts b/packages/cli/src/events/relays/telemetry.event-relay.ts index 7fc40504e20..dffedbafbfd 100644 --- a/packages/cli/src/events/relays/telemetry.event-relay.ts +++ b/packages/cli/src/events/relays/telemetry.event-relay.ts @@ -1063,6 +1063,8 @@ export class TelemetryEventRelay extends EventRelay { data_table_schema_conflict_policy: options.dataTableSchemaConflictPolicy, variable_missing_mode: options.variableMissingMode, variable_parent_policy: options.variableParentPolicy, + tag_missing_mode: options.tagMissingMode, + tag_conflict_policy: options.tagConflictPolicy, workflows_created: counts.workflows.created, workflows_updated: counts.workflows.updated, workflows_skipped: counts.workflows.skipped, @@ -1076,6 +1078,11 @@ export class TelemetryEventRelay extends EventRelay { variables_missing: counts.variables.missing, variables_created: counts.variables.created, variables_required: counts.variables.requirements, + tags_matched: counts.tags.matched, + tags_created: counts.tags.created, + tags_renamed: counts.tags.renamed, + tags_skipped: counts.tags.skipped, + tags_required: counts.tags.requirements, }); } @@ -1087,6 +1094,7 @@ export class TelemetryEventRelay extends EventRelay { credential_count: counts.credentials, data_table_count: counts.dataTables, variable_count: counts.variables, + tag_count: counts.tags, }); } diff --git a/packages/cli/src/execution-lifecycle/__tests__/execution-lifecycle-hooks.test.ts b/packages/cli/src/execution-lifecycle/__tests__/execution-lifecycle-hooks.test.ts index 5f12d06a8d2..6bed054d952 100644 --- a/packages/cli/src/execution-lifecycle/__tests__/execution-lifecycle-hooks.test.ts +++ b/packages/cli/src/execution-lifecycle/__tests__/execution-lifecycle-hooks.test.ts @@ -481,9 +481,29 @@ describe('Execution Lifecycle Hooks', () => { }; describe('getLifecycleHooksForRegularMain', () => { - const createHooks = (executionMode: WorkflowExecuteMode = 'manual') => + const createHooks = ( + executionMode: WorkflowExecuteMode = 'manual', + suppressErrorWorkflow?: boolean, + ) => getLifecycleHooksForRegularMain( - { executionMode, workflowData, pushRef, retryOf, userId }, + { executionMode, workflowData, pushRef, retryOf, userId, suppressErrorWorkflow }, + executionId, + ); + + /** Mirrors wait resume / crash recovery: run data rebuilt from the persisted execution. */ + const createHooksFromPersistedExecution = ( + executionMode: WorkflowExecuteMode, + suppressErrorWorkflow?: boolean, + ) => + getLifecycleHooksForRegularMain( + { + executionMode, + workflowData, + executionData: createRunExecutionData({ + resultData: { runData: {} }, + manualData: { suppressErrorWorkflow }, + }), + }, executionId, ); @@ -1118,6 +1138,50 @@ describe('Execution Lifecycle Hooks', () => { project, ); }); + + it('should not execute error workflow when the run opts out of it', async () => { + lifecycleHooks = createHooks('trigger', true); + + workflowData.settings = { errorWorkflow: 'error-workflow-id' }; + ownershipService.getWorkflowProjectCached + .calledWith(workflowId) + .mockResolvedValue(mock()); + + await lifecycleHooks.runHook('workflowExecuteAfter', [failedRun, {}]); + + expect(workflowExecutionService.executeErrorWorkflow).not.toHaveBeenCalled(); + }); + + it('should honour the opt-out when run data is rebuilt from a persisted execution', async () => { + lifecycleHooks = createHooksFromPersistedExecution('trigger', true); + + workflowData.settings = { errorWorkflow: 'error-workflow-id' }; + ownershipService.getWorkflowProjectCached + .calledWith(workflowId) + .mockResolvedValue(mock()); + + await lifecycleHooks.runHook('workflowExecuteAfter', [failedRun, {}]); + + expect(workflowExecutionService.executeErrorWorkflow).not.toHaveBeenCalled(); + }); + + it('should still execute error workflow when a persisted execution did not opt out', async () => { + lifecycleHooks = createHooksFromPersistedExecution('trigger'); + + const errorWorkflow = 'error-workflow-id'; + workflowData.settings = { errorWorkflow }; + ownershipService.getWorkflowProjectCached + .calledWith(workflowId) + .mockResolvedValue(mock()); + + await lifecycleHooks.runHook('workflowExecuteAfter', [failedRun, {}]); + + expect(workflowExecutionService.executeErrorWorkflow).toHaveBeenCalledWith( + errorWorkflow, + expect.objectContaining({ workflow: { id: workflowId, name: workflowData.name } }), + expect.anything(), + ); + }); }); it('should restore binary data IDs after workflow execution for webhooks', async () => { @@ -1379,9 +1443,29 @@ describe('Execution Lifecycle Hooks', () => { }); describe('getLifecycleHooksForScalingWorker', () => { - const createHooks = (executionMode: WorkflowExecuteMode = 'manual') => + const createHooks = ( + executionMode: WorkflowExecuteMode = 'manual', + suppressErrorWorkflow?: boolean, + ) => getLifecycleHooksForScalingWorker( - { executionMode, workflowData, pushRef, retryOf }, + { executionMode, workflowData, pushRef, retryOf, suppressErrorWorkflow }, + executionId, + ); + + /** Mirrors the main-process cancel / stalled-job paths in queue mode. */ + const createHooksFromPersistedExecution = ( + executionMode: WorkflowExecuteMode, + suppressErrorWorkflow?: boolean, + ) => + getLifecycleHooksForScalingWorker( + { + executionMode, + workflowData, + executionData: createRunExecutionData({ + resultData: { runData: {} }, + manualData: { suppressErrorWorkflow }, + }), + }, executionId, ); @@ -1474,6 +1558,47 @@ describe('Execution Lifecycle Hooks', () => { project, ); }); + + it('should not execute error workflow when the run opts out of it', async () => { + lifecycleHooks = createHooks('trigger', true); + workflowData.settings = { errorWorkflow: 'error-workflow-id' }; + ownershipService.getWorkflowProjectCached + .calledWith(workflowId) + .mockResolvedValue(mock()); + + await lifecycleHooks.runHook('workflowExecuteAfter', [failedRun, {}]); + + expect(workflowExecutionService.executeErrorWorkflow).not.toHaveBeenCalled(); + }); + + it('should honour the opt-out when run data is rebuilt from a persisted execution', async () => { + lifecycleHooks = createHooksFromPersistedExecution('trigger', true); + workflowData.settings = { errorWorkflow: 'error-workflow-id' }; + ownershipService.getWorkflowProjectCached + .calledWith(workflowId) + .mockResolvedValue(mock()); + + await lifecycleHooks.runHook('workflowExecuteAfter', [failedRun, {}]); + + expect(workflowExecutionService.executeErrorWorkflow).not.toHaveBeenCalled(); + }); + + it('should still execute error workflow when a persisted execution did not opt out', async () => { + lifecycleHooks = createHooksFromPersistedExecution('trigger'); + const errorWorkflow = 'error-workflow-id'; + workflowData.settings = { errorWorkflow }; + ownershipService.getWorkflowProjectCached + .calledWith(workflowId) + .mockResolvedValue(mock()); + + await lifecycleHooks.runHook('workflowExecuteAfter', [failedRun, {}]); + + expect(workflowExecutionService.executeErrorWorkflow).toHaveBeenCalledWith( + errorWorkflow, + expect.objectContaining({ workflow: { id: workflowId, name: workflowData.name } }), + expect.anything(), + ); + }); }); describe('metadata handling', () => { @@ -1509,12 +1634,7 @@ describe('Execution Lifecycle Hooks', () => { return executionPersistence.updateExistingExecution.mock.calls[0][1]; }; - beforeEach(() => { - process.env.N8N_SKIP_UNSAVED_EXECUTION_DATA_WRITES = 'true'; - }); - afterEach(() => { - delete process.env.N8N_SKIP_UNSAVED_EXECUTION_DATA_WRITES; workflowData.settings = {}; }); @@ -1530,18 +1650,6 @@ describe('Execution Lifecycle Hooks', () => { expect(payload.status).toBe('success'); }); - it('should write run data when the flag is not enabled', async () => { - delete process.env.N8N_SKIP_UNSAVED_EXECUTION_DATA_WRITES; - workflowData.settings = { saveDataSuccessExecution: 'none' }; - const lifecycleHooks = createHooks('trigger'); - - await lifecycleHooks.runHook('workflowExecuteAfter', [successfulRun, {}]); - - const payload = getUpdatePayload(); - expect(payload.data).toBeDefined(); - expect(payload.workflowData).toBeDefined(); - }); - it('should write run data when successful executions are saved', async () => { workflowData.settings = { saveDataSuccessExecution: 'all' }; const lifecycleHooks = createHooks('trigger'); @@ -1597,6 +1705,17 @@ describe('Execution Lifecycle Hooks', () => { expect(payload.workflowData).toBeDefined(); }); + it('should write run data for manual executions so the editor can read it back', async () => { + workflowData.settings = { saveDataSuccessExecution: 'none' }; + const lifecycleHooks = createHooks('manual'); + + await lifecycleHooks.runHook('workflowExecuteAfter', [successfulRun, {}]); + + const payload = getUpdatePayload(); + expect(payload.data).toBeDefined(); + expect(payload.workflowData).toBeDefined(); + }); + it('should write run data when a workflow.postExecute external hook is registered', async () => { workflowData.settings = { saveDataSuccessExecution: 'none' }; externalHooks.hasHook.mockReturnValueOnce(true); diff --git a/packages/cli/src/execution-lifecycle/execution-lifecycle-hooks.ts b/packages/cli/src/execution-lifecycle/execution-lifecycle-hooks.ts index c975d2bfe5a..9bd8538ed3a 100644 --- a/packages/cli/src/execution-lifecycle/execution-lifecycle-hooks.ts +++ b/packages/cli/src/execution-lifecycle/execution-lifecycle-hooks.ts @@ -140,6 +140,7 @@ type HooksSetupParameters = { retryOf?: string; parentExecution?: RelatedExecution; source?: IWorkflowExecutionDataProcess['source']; + suppressErrorWorkflow?: IWorkflowExecutionDataProcess['suppressErrorWorkflow']; }; function hookFunctionsWorkflowEvents( @@ -542,7 +543,14 @@ async function duplicateBinaryDataToParent( */ function hookFunctionsSave( hooks: ExecutionLifecycleHooks, - { pushRef, retryOf, saveSettings, parentExecution, source }: HooksSetupParameters, + { + pushRef, + retryOf, + saveSettings, + parentExecution, + source, + suppressErrorWorkflow, + }: HooksSetupParameters, ) { const logger = Container.get(Logger); const errorReporter = Container.get(ErrorReporter); @@ -567,6 +575,7 @@ function hookFunctionsSave( } const isManualMode = this.mode === 'manual'; + const dispatchesErrorWorkflow = !isManualMode && !suppressErrorWorkflow; try { if (!isManualMode && isWorkflowIdValid(this.workflowData.id) && newStaticData) { @@ -603,7 +612,15 @@ function hookFunctionsSave( (fullRunData.status !== 'success' && !saveSettings.error); if (shouldNotSave && !fullRunData.waitTill && !isManualMode) { - executeErrorWorkflow(this.workflowData, fullRunData, this.mode, this.executionId, retryOf); + if (dispatchesErrorWorkflow) { + executeErrorWorkflow( + this.workflowData, + fullRunData, + this.mode, + this.executionId, + retryOf, + ); + } await executionPersistence.deleteInFlightExecution({ workflowId: this.workflowData.id, @@ -649,7 +666,7 @@ function hookFunctionsSave( fullRunData.data?.resultData?.metadata, ); - if (!isManualMode) { + if (dispatchesErrorWorkflow) { executeErrorWorkflow(this.workflowData, fullRunData, this.mode, this.executionId, retryOf); } } finally { @@ -662,16 +679,18 @@ function hookFunctionsSave( }); } +/** + * Modes whose run data may be skipped when main is certain to discard it. + */ const DISCARDABLE_DATA_MODES: WorkflowExecuteMode[] = ['trigger', 'cli', 'error', 'internal']; /** * Returns hook functions to save workflow execution and call error workflow - * for running with queues. Manual executions should never run on queues as - * they are always executed in the main process. + * for running with queues. */ function hookFunctionsSaveWorker( hooks: ExecutionLifecycleHooks, - { pushRef, retryOf, saveSettings, source }: HooksSetupParameters, + { pushRef, retryOf, saveSettings, source, suppressErrorWorkflow }: HooksSetupParameters, ) { const logger = Container.get(Logger); const errorReporter = Container.get(ErrorReporter); @@ -700,7 +719,12 @@ function hookFunctionsSaveWorker( } } - if (!isManualMode && fullRunData.status !== 'success' && fullRunData.status !== 'waiting') { + if ( + !isManualMode && + !suppressErrorWorkflow && + fullRunData.status !== 'success' && + fullRunData.status !== 'waiting' + ) { executeErrorWorkflow(this.workflowData, fullRunData, this.mode, this.executionId, retryOf); } @@ -724,7 +748,6 @@ function hookFunctionsSaveWorker( runDataAttemptedDynamicCredentials(resultRunData); const mainWillDiscardData = - process.env.N8N_SKIP_UNSAVED_EXECUTION_DATA_WRITES === 'true' && fullRunData.status === 'success' && !saveSettings.success && !fullRunData.waitTill && @@ -778,6 +801,16 @@ export function getLifecycleHooksForSubExecutions( return hooks; } +/** + * Paths that resume or finalize an existing execution — wait resume, crash + * recovery, enqueued recovery, chat — rebuild the run data from the persisted + * execution and carry no transient top-level fields, so fall back to the copy + * kept in `manualData`. + */ +function resolveSuppressErrorWorkflow(data: IWorkflowExecutionDataProcess): boolean | undefined { + return data.suppressErrorWorkflow ?? data.executionData?.manualData?.suppressErrorWorkflow; +} + /** * Returns ExecutionLifecycleHooks instance for worker in scaling mode. */ @@ -786,6 +819,7 @@ export function getLifecycleHooksForScalingWorker( executionId: string, ): ExecutionLifecycleHooks { const { pushRef, retryOf, executionMode, workflowData, source } = data; + const suppressErrorWorkflow = resolveSuppressErrorWorkflow(data); const hooks = new ExecutionLifecycleHooks( executionMode, executionId, @@ -793,7 +827,13 @@ export function getLifecycleHooksForScalingWorker( retryOf ?? undefined, ); const saveSettings = toSaveSettings(workflowData.settings); - const optionalParameters = { pushRef, retryOf: retryOf ?? undefined, saveSettings, source }; + const optionalParameters = { + pushRef, + retryOf: retryOf ?? undefined, + saveSettings, + source, + suppressErrorWorkflow, + }; hookFunctionsNodeEvents(hooks); hookFunctionsFinalizeExecutionStatus(hooks); hookFunctionsSaveWorker(hooks, optionalParameters); @@ -916,6 +956,7 @@ export function getLifecycleHooksForRegularMain( source, telemetryMetadata, } = data; + const suppressErrorWorkflow = resolveSuppressErrorWorkflow(data); const hooks = new ExecutionLifecycleHooks( executionMode, executionId, @@ -923,7 +964,13 @@ export function getLifecycleHooksForRegularMain( retryOf ?? undefined, ); const saveSettings = toSaveSettings(workflowData.settings); - const optionalParameters = { pushRef, retryOf: retryOf ?? undefined, saveSettings, source }; + const optionalParameters = { + pushRef, + retryOf: retryOf ?? undefined, + saveSettings, + source, + suppressErrorWorkflow, + }; hookFunctionsWorkflowEvents(hooks, userId, projectId, projectName, source, telemetryMetadata); hookFunctionsNodeEvents(hooks); hookFunctionsFinalizeExecutionStatus(hooks); diff --git a/packages/cli/src/executions/__tests__/enqueued-execution-recovery.service.test.ts b/packages/cli/src/executions/__tests__/enqueued-execution-recovery.service.test.ts new file mode 100644 index 00000000000..865f61db662 --- /dev/null +++ b/packages/cli/src/executions/__tests__/enqueued-execution-recovery.service.test.ts @@ -0,0 +1,128 @@ +import { mockLogger } from '@n8n/backend-test-utils'; +import type { ExecutionsConfig } from '@n8n/config'; +import type { ExecutionRepository, IExecutionResponse, Project } from '@n8n/db'; +import type { ErrorReporter } from 'n8n-core'; +import { mock } from 'vitest-mock-extended'; + +import { ExecutionAlreadyResumingError } from '@/errors/execution-already-resuming.error'; +import type { EventService } from '@/events/event.service'; +import { EnqueuedExecutionRecoveryService } from '@/executions/enqueued-execution-recovery.service'; +import type { ExecutionService } from '@/executions/execution.service'; +import type { OwnershipService } from '@/services/ownership.service'; +import type { WorkflowRunner } from '@/workflow-runner'; + +const project = mock({ id: 'project-1' }); + +const enqueuedExecution = (id: string) => + mock({ id, mode: 'webhook', workflowId: `workflow-for-${id}` }); + +describe('EnqueuedExecutionRecoveryService', () => { + const logger = mockLogger(); + vi.mocked(logger.scoped).mockReturnValue(logger); + const errorReporter = mock(); + const executionService = mock(); + const executionRepository = mock(); + const ownershipService = mock(); + const workflowRunner = mock(); + const eventService = mock(); + + const createService = (mode: 'regular' | 'queue' = 'regular') => + new EnqueuedExecutionRecoveryService( + logger, + errorReporter, + mock({ mode }), + executionService, + executionRepository, + ownershipService, + workflowRunner, + eventService, + ); + + beforeEach(() => { + vi.clearAllMocks(); + ownershipService.getWorkflowProjectCached.mockResolvedValue(project); + workflowRunner.run.mockResolvedValue('1'); + }); + + test('refuses to touch anything in queue mode', async () => { + executionService.findAllEnqueuedExecutions.mockResolvedValue([enqueuedExecution('1')]); + + await expect(createService('queue').recoverEnqueuedExecutions()).rejects.toThrow( + 'Enqueued execution recovery must not run in queue mode', + ); + + expect(executionService.findAllEnqueuedExecutions).not.toHaveBeenCalled(); + expect(workflowRunner.run).not.toHaveBeenCalled(); + }); + + test('runs each enqueued execution, claiming it while it is still `new`', async () => { + executionService.findAllEnqueuedExecutions.mockResolvedValue([ + enqueuedExecution('1'), + enqueuedExecution('2'), + ]); + + await createService().recoverEnqueuedExecutions(); + + expect(workflowRunner.run).toHaveBeenCalledWith( + expect.objectContaining({ executionMode: 'webhook', projectId: 'project-1' }), + undefined, + false, + { executionId: '1', expectedStatus: 'new' }, + ); + expect(workflowRunner.run.mock.calls.map((call) => call[3])).toEqual([ + { executionId: '1', expectedStatus: 'new' }, + { executionId: '2', expectedStatus: 'new' }, + ]); + }); + + // CAT-3862: previously the rejection landed in a void'd promise and the row stayed `new`. + test('marks an execution as crashed when its run rejects', async () => { + executionService.findAllEnqueuedExecutions.mockResolvedValue([ + enqueuedExecution('1'), + enqueuedExecution('2'), + ]); + workflowRunner.run.mockRejectedValueOnce(new Error('cannot start')); + + await createService().recoverEnqueuedExecutions(); + await new Promise(setImmediate); // `run` is not awaited, let the rejection settle + + expect(executionRepository.markAsCrashed).toHaveBeenCalledExactlyOnceWith('1'); + expect(errorReporter.error).toHaveBeenCalledTimes(1); + expect(workflowRunner.run).toHaveBeenCalledTimes(2); + }); + + test('does not crash an execution claimed by another runner', async () => { + executionService.findAllEnqueuedExecutions.mockResolvedValue([enqueuedExecution('1')]); + workflowRunner.run.mockRejectedValueOnce(new ExecutionAlreadyResumingError('1')); + + await createService().recoverEnqueuedExecutions(); + await new Promise(setImmediate); // `run` is not awaited, let the rejection settle + + expect(executionRepository.markAsCrashed).not.toHaveBeenCalled(); + expect(errorReporter.error).not.toHaveBeenCalled(); + }); + + // A throw used to abort the whole loop, leaving every remaining execution at `new`. + test('keeps going when preparing an execution throws', async () => { + executionService.findAllEnqueuedExecutions.mockResolvedValue([ + enqueuedExecution('1'), + enqueuedExecution('2'), + ]); + ownershipService.getWorkflowProjectCached.mockRejectedValueOnce( + new Error('workflow not found'), + ); + + await createService().recoverEnqueuedExecutions(); + + expect(executionRepository.markAsCrashed).toHaveBeenCalledExactlyOnceWith('1'); + expect(workflowRunner.run).toHaveBeenCalledExactlyOnceWith( + expect.anything(), + undefined, + false, + { + executionId: '2', + expectedStatus: 'new', + }, + ); + }); +}); diff --git a/packages/cli/src/executions/__tests__/execution-persistence.integration.test.ts b/packages/cli/src/executions/__tests__/execution-persistence.integration.test.ts index 2a291a83e41..7649cf48486 100644 --- a/packages/cli/src/executions/__tests__/execution-persistence.integration.test.ts +++ b/packages/cli/src/executions/__tests__/execution-persistence.integration.test.ts @@ -141,6 +141,42 @@ describe('ExecutionPersistence', () => { const executionEntity = await executionRepo.findOneBy({ id: executionId }); expect(executionEntity?.status).toEqual('new'); }); + + // CAT-3862: `ActiveExecutions.add` claims an existing execution through this + // path. It used to require `waiting` unconditionally, so an execution enqueued + // before a restart (status `new`) could never be claimed and stayed `new` forever. + describe.each([ + { requireStatus: 'new' as const, expected: true }, + { requireStatus: 'waiting' as const, expected: false }, + ])( + 'claiming a `new` execution with requireStatus: $requireStatus', + ({ requireStatus, expected }) => { + it(`returns ${expected}`, async () => { + const executionPersistence = Container.get(ExecutionPersistence); + const executionRepo = Container.get(ExecutionRepository); + const workflow = await createWorkflow({ settings: { executionOrder: 'v1' } }); + + const executionId = await executionPersistence.create({ + workflowId: workflow.id, + data: createEmptyRunExecutionData(), + workflowData: workflow, + mode: 'webhook', + status: 'new', + finished: false, + }); + + const claimed = await executionPersistence.updateExistingExecution( + executionId, + { data: createEmptyRunExecutionData(), waitTill: null, status: 'running' }, + { requireStatus }, + ); + + expect(claimed).toBe(expected); + const row = await executionRepo.findOneBy({ id: executionId }); + expect(row?.status).toEqual(expected ? 'running' : 'new'); + }); + }, + ); }); describe('findSingleExecution', () => { diff --git a/packages/cli/src/executions/__tests__/execution-persistence.test.ts b/packages/cli/src/executions/__tests__/execution-persistence.test.ts index 95ca8dc316c..58326693cd7 100644 --- a/packages/cli/src/executions/__tests__/execution-persistence.test.ts +++ b/packages/cli/src/executions/__tests__/execution-persistence.test.ts @@ -587,6 +587,27 @@ describe('ExecutionPersistence', () => { expect(result).toBe(true); expect(executionRepository.update).not.toHaveBeenCalled(); }); + + it('should not overwrite startedAt', async () => { + const executionPersistence = createPersistenceService('db'); + executionRepository.update.mockResolvedValue({ + affected: 1, + generatedMaps: [], + raw: {}, + }); + const startedAt = new Date(); + + const result = await executionPersistence.updateExistingExecution(executionId, { + status: 'running', + startedAt, + }); + + expect(result).toBe(true); + expect(executionRepository.update).toHaveBeenCalledWith( + { id: executionId }, + { status: 'running' }, + ); + }); }); describe('data updates on db-mode executions', () => { @@ -1105,13 +1126,15 @@ describe('ExecutionPersistence', () => { const mockTx = createMockTransaction(); executionRepository.manager.transaction = createMockTx(mockTx); + const startedAt = new Date(); + await executionPersistence.updateExistingExecution(executionId, { id: executionId, data: runData, workflowId: 'other-wf', workflowVersionId: 'v-new', createdAt: new Date(), - startedAt: new Date(), + startedAt, customData: { foo: 'bar' }, status: 'success', }); diff --git a/packages/cli/src/executions/__tests__/execution-recovery.service.integration.test.ts b/packages/cli/src/executions/__tests__/execution-recovery.service.integration.test.ts index 57ade9af05e..1817d5ea8c5 100644 --- a/packages/cli/src/executions/__tests__/execution-recovery.service.integration.test.ts +++ b/packages/cli/src/executions/__tests__/execution-recovery.service.integration.test.ts @@ -6,7 +6,13 @@ import { getWorkflowById, } from '@n8n/backend-test-utils'; import { GlobalConfig } from '@n8n/config'; -import { ExecutionRepository, WorkflowRepository, ProjectRelationRepository } from '@n8n/db'; +import { + ExecutionRepository, + WorkflowRepository, + ProjectRelationRepository, + WorkflowPublicationOutboxRepository, + WorkflowPublishHistoryRepository, +} from '@n8n/db'; import type { Project, User } from '@n8n/db'; import { Container } from '@n8n/di'; import { stringify } from 'flatted'; @@ -16,6 +22,7 @@ import assert from 'node:assert'; import { v4 as uuid } from 'uuid'; import { mock } from 'vitest-mock-extended'; +import { ActiveWorkflowManager } from '@/active-workflow-manager'; import { ARTIFICIAL_TASK_DATA } from '@/constants'; import { NodeCrashedError } from '@/errors/node-crashed.error'; import { WorkflowCrashedError } from '@/errors/workflow-crashed.error'; @@ -23,8 +30,10 @@ import type { EventMessageTypes as EventMessage } from '@/eventbus/event-message import { EventMessageNode } from '@/eventbus/event-message-classes/event-message-node'; import { ExecutionPersistence } from '@/executions/execution-persistence'; import { ExecutionRecoveryService } from '@/executions/execution-recovery.service'; +import { ExternalHooks } from '@/external-hooks'; import { Push } from '@/push'; import { OwnershipService } from '@/services/ownership.service'; +import { WorkflowPublicationNotifier } from '@/workflows/publication/workflow-publication-notifier'; import { createExecution } from '@test-integration/db/executions'; import { IN_PROGRESS_EXECUTION_DATA, OOM_WORKFLOW } from './constants'; @@ -35,6 +44,9 @@ describe('ExecutionRecoveryService', () => { const instanceSettings = Container.get(InstanceSettings); const ownershipService = mockInstance(OwnershipService); const projectRelationRepository = mockInstance(ProjectRelationRepository); + const externalHooks = mockInstance(ExternalHooks); + const activeWorkflowManager = mockInstance(ActiveWorkflowManager); + mockInstance(WorkflowPublicationNotifier); let executionRecoveryService: ExecutionRecoveryService; let executionRepository: ExecutionRepository; @@ -70,12 +82,14 @@ describe('ExecutionRecoveryService', () => { afterEach(async () => { vi.restoreAllMocks(); globalConfig.executions.recovery.workflowDeactivationEnabled = false; + globalConfig.workflows.useWorkflowPublicationService = false; await testDb.truncate([ 'ExecutionEntity', 'ExecutionData', 'WorkflowEntity', 'WorkflowHistory', 'WorkflowPublishHistory', + 'WorkflowPublicationOutbox', ]); }); @@ -83,6 +97,23 @@ describe('ExecutionRecoveryService', () => { await testDb.terminate(); }); + async function createCrashedActiveWorkflow() { + const workflow = await createActiveWorkflow({ ...OOM_WORKFLOW }); + expect(workflow.activeVersionId).not.toBeNull(); + await createExecution({ status: 'crashed' }, workflow); + await createExecution({ status: 'crashed' }, workflow); + await createExecution({ status: 'crashed' }, workflow); + return workflow; + } + + function mockOwnershipForDeactivation() { + ownershipService.getWorkflowProjectCached.mockResolvedValue( + mock({ id: uuid(), type: 'personal' }), + ); + ownershipService.getInstanceOwner.mockResolvedValue(mock({ id: uuid() })); + projectRelationRepository.find.mockResolvedValue([]); + } + describe('recoverFromLogs', () => { describe('if follower', () => { test('should do nothing', async () => { @@ -479,19 +510,8 @@ describe('ExecutionRecoveryService', () => { */ globalConfig.executions.recovery.workflowDeactivationEnabled = true; - const workflow = await createActiveWorkflow({ - ...OOM_WORKFLOW, - }); - expect(workflow.activeVersionId).not.toBeNull(); - await createExecution({ status: 'crashed' }, workflow); - await createExecution({ status: 'crashed' }, workflow); - await createExecution({ status: 'crashed' }, workflow); - - ownershipService.getWorkflowProjectCached.mockResolvedValue( - mock({ id: uuid(), type: 'personal' }), - ); - ownershipService.getInstanceOwner.mockResolvedValue(mock({ id: uuid() })); - projectRelationRepository.find.mockResolvedValue([]); + const workflow = await createCrashedActiveWorkflow(); + mockOwnershipForDeactivation(); /** * Act @@ -505,6 +525,137 @@ describe('ExecutionRecoveryService', () => { if (!updatedWorkflow) expect.fail('Expected `updatedWorkflow` to be defined'); expect(updatedWorkflow.activeVersionId).toBeNull(); }); + + test('should unpublish via outbox and record publish history on auto-deactivation', async () => { + /** + * Arrange + */ + globalConfig.executions.recovery.workflowDeactivationEnabled = true; + globalConfig.workflows.useWorkflowPublicationService = true; + + const workflow = await createCrashedActiveWorkflow(); + mockOwnershipForDeactivation(); + + /** + * Act + */ + await executionRecoveryService.autoDeactivateWorkflowsIfNeeded(new Set([workflow.id])); + + /** + * Assert + */ + // Trigger/webhook teardown happens via the publication outbox: without an + // unpublish record the applier never deregisters webhooks, so the + // workflow keeps executing after "deactivation". + const outboxRecord = await Container.get( + WorkflowPublicationOutboxRepository, + ).findInFlightByWorkflowId(workflow.id); + expect(outboxRecord).not.toBeNull(); + + // The publish timeline reads from publish history: without a + // 'deactivated' record the UI keeps showing the version as published. + const deactivationRecords = await Container.get(WorkflowPublishHistoryRepository).findBy({ + workflowId: workflow.id, + event: 'deactivated', + }); + expect(deactivationRecords).toHaveLength(1); + // System-initiated: no user attribution + expect(deactivationRecords[0].userId).toBeNull(); + + expect(externalHooks.run).toHaveBeenCalledWith('workflow.deactivate', [ + expect.objectContaining({ id: workflow.id }), + expect.anything(), // workflow hook context service + ]); + }); + + test('should tear down triggers and record publish history on auto-deactivation (legacy mode)', async () => { + /** + * Arrange + */ + globalConfig.executions.recovery.workflowDeactivationEnabled = true; + + const workflow = await createCrashedActiveWorkflow(); + mockOwnershipForDeactivation(); + + /** + * Act + */ + await executionRecoveryService.autoDeactivateWorkflowsIfNeeded(new Set([workflow.id])); + + /** + * Assert + */ + // Legacy mode tears down webhooks/triggers via the active workflow manager + expect(activeWorkflowManager.remove).toHaveBeenCalledWith(workflow.id); + + const updatedWorkflow = await getWorkflowById(workflow.id); + expect(updatedWorkflow?.activeVersionId).toBeNull(); + + const deactivationRecords = await Container.get(WorkflowPublishHistoryRepository).findBy({ + workflowId: workflow.id, + event: 'deactivated', + }); + expect(deactivationRecords).toHaveLength(1); + expect(deactivationRecords[0].userId).toBeNull(); + + const outboxRecord = await Container.get( + WorkflowPublicationOutboxRepository, + ).findInFlightByWorkflowId(workflow.id); + expect(outboxRecord).toBeNull(); + }); + + test('should mark executions crashed and process remaining workflows when a deactivation fails', async () => { + /** + * Arrange + */ + globalConfig.executions.recovery.workflowDeactivationEnabled = true; + + const failing = await createActiveWorkflow({ ...OOM_WORKFLOW }); + const succeeding = await createActiveWorkflow({ ...OOM_WORKFLOW }); + for (const workflow of [failing, succeeding]) { + await createExecution({ status: 'crashed' }, workflow); + await createExecution({ status: 'crashed' }, workflow); + await createExecution({ status: 'crashed' }, workflow); + } + // older than the crashed ones so it stays outside the last-N window + const running = await createExecution( + { status: 'running', startedAt: new Date(Date.now() - 60_000) }, + failing, + ); + + ownershipService.getWorkflowProjectCached.mockResolvedValue( + mock({ id: uuid(), type: 'personal' }), + ); + ownershipService.getInstanceOwner.mockResolvedValue(mock({ id: uuid() })); + projectRelationRepository.find.mockResolvedValue([]); + + const { WorkflowService } = await import('@/workflows/workflow.service.js'); + vi.spyOn(WorkflowService.prototype, 'deactivateWorkflowAsSystem').mockRejectedValueOnce( + new Error('deactivation failed'), + ); + + /** + * Act + */ + await executionRecoveryService.autoDeactivateWorkflowsIfNeeded( + new Set([failing.id, succeeding.id]), + ); + + /** + * Assert + */ + // the failing workflow's executions are still marked as crashed + const executions = await executionRepository.findMultipleExecutions({ + select: ['id', 'status'], + where: { id: running.id }, + }); + expect(executions).toHaveLength(1); + expect(executions[0].status).toBe('crashed'); + + // the remaining workflow is still deactivated + const updatedSucceeding = await getWorkflowById(succeeding.id); + expect(updatedSucceeding?.activeVersionId).toBeNull(); + }); }); }); }); diff --git a/packages/cli/src/executions/enqueued-execution-recovery.service.ts b/packages/cli/src/executions/enqueued-execution-recovery.service.ts new file mode 100644 index 00000000000..09a095ce073 --- /dev/null +++ b/packages/cli/src/executions/enqueued-execution-recovery.service.ts @@ -0,0 +1,97 @@ +import { Logger } from '@n8n/backend-common'; +import { ExecutionsConfig } from '@n8n/config'; +import { ExecutionRepository } from '@n8n/db'; +import { Service } from '@n8n/di'; +import { ErrorReporter } from 'n8n-core'; +import type { IWorkflowExecutionDataProcess } from 'n8n-workflow'; +import { strict as assert } from 'node:assert'; + +import { ExecutionAlreadyResumingError } from '@/errors/execution-already-resuming.error'; +import { EventService } from '@/events/event.service'; +import { ExecutionService } from '@/executions/execution.service'; +import { OwnershipService } from '@/services/ownership.service'; +import { WorkflowRunner } from '@/workflow-runner'; + +/** + * Recovers executions left in the `new` (enqueued) status by a shutdown, so they do + * not sit at "Queued" forever. + */ +@Service() +export class EnqueuedExecutionRecoveryService { + constructor( + private readonly logger: Logger, + private readonly errorReporter: ErrorReporter, + private readonly executionsConfig: ExecutionsConfig, + private readonly executionService: ExecutionService, + private readonly executionRepository: ExecutionRepository, + private readonly ownershipService: OwnershipService, + private readonly workflowRunner: WorkflowRunner, + private readonly eventService: EventService, + ) { + this.logger = this.logger.scoped('enqueued-execution-recovery'); + } + + /** + * During startup, we may find executions that had been enqueued at the time of + * shutdown. If so, start running any such executions concurrently up to the + * concurrency limit, and enqueue any remaining ones until we have spare + * concurrency capacity again. + * + * Regular mode only: in queue mode enqueued executions are picked up by workers, + * and dangling ones are handled by `ScalingService.recoverFromQueue`. + */ + async recoverEnqueuedExecutions() { + assert( + this.executionsConfig.mode === 'regular', + 'Enqueued execution recovery must not run in queue mode', + ); + + const executions = await this.executionService.findAllEnqueuedExecutions(); + + if (executions.length === 0) return; + + this.logger.debug('Found enqueued executions to run', { + executionIds: executions.map((e) => e.id), + }); + + for (const execution of executions) { + // capture the id so the rejection handler below does not retain the whole + // execution - and with it its run data - while the execution sits queued + const executionId = execution.id; + + try { + const project = await this.ownershipService.getWorkflowProjectCached(execution.workflowId); + + const data: IWorkflowExecutionDataProcess = { + executionMode: execution.mode, + executionData: execution.data, + workflowData: execution.workflowData, + projectId: project.id, + }; + + this.eventService.emit('execution-started-during-bootup', { executionId }); + + // do not block - each execution either runs concurrently or is queued + void this.workflowRunner + .run(data, undefined, false, { executionId, expectedStatus: 'new' }) + .catch(async (error) => await this.crashUnstartableExecution(executionId, error)); + } catch (error) { + await this.crashUnstartableExecution(executionId, error); + } + } + } + + /** An enqueued execution we cannot start would otherwise stay at `new` indefinitely. */ + private async crashUnstartableExecution(executionId: string, error: unknown) { + if (error instanceof ExecutionAlreadyResumingError) { + this.logger.debug('Execution was already claimed, skipping recovery', { executionId }); + return; + } + + // `shouldBeLogged: false` - the log line below carries the same error with more context + this.errorReporter.error(error, { executionId, shouldBeLogged: false }); + this.logger.error('Failed to run enqueued execution', { executionId, error }); + + await this.executionRepository.markAsCrashed(executionId); + } +} diff --git a/packages/cli/src/executions/execution-persistence.ts b/packages/cli/src/executions/execution-persistence.ts index 265c3ab6ef3..b9d583ba168 100644 --- a/packages/cli/src/executions/execution-persistence.ts +++ b/packages/cli/src/executions/execution-persistence.ts @@ -162,7 +162,8 @@ export class ExecutionPersistence { * it up, so a redelivery racing that window deletes a genuinely enqueued row and * re-dispatches the occurrence (the worker's job then finds no execution and fails * noisily, but the occurrence still runs). Telling "inserted, never enqueued" from - * "enqueued, not yet picked up" apart needs schema the misfire-policy work owns. + * "enqueued, not yet picked up" apart needs an enqueued marker the execution row + * does not carry. * * @returns the deleted tombstone's storage location, so `create` can clear its * out-of-band data after committing, or `null` when there was nothing to reclaim. diff --git a/packages/cli/src/executions/execution-recovery.service.ts b/packages/cli/src/executions/execution-recovery.service.ts index 0a71f6bc79a..01b17943abc 100644 --- a/packages/cli/src/executions/execution-recovery.service.ts +++ b/packages/cli/src/executions/execution-recovery.service.ts @@ -8,11 +8,13 @@ import { User, } from '@n8n/db'; import { ExecutionRepository, WorkflowRepository } from '@n8n/db'; -import { Service } from '@n8n/di'; +import { Container, Service } from '@n8n/di'; import { PROJECT_ADMIN_ROLE_SLUG, PROJECT_OWNER_ROLE_SLUG } from '@n8n/permissions'; +import { ensureError } from '@n8n/utils/errors/ensure-error'; import type { DateTime } from 'luxon'; +import { sleep } from '@n8n/utils/sleep'; import { InstanceSettings } from 'n8n-core'; -import { createEmptyRunExecutionData, sleep } from 'n8n-workflow'; +import { createEmptyRunExecutionData } from 'n8n-workflow'; import { ExecutionStatus, type IRun, type ITaskData } from 'n8n-workflow'; import { ARTIFICIAL_TASK_DATA } from '@/constants'; @@ -69,21 +71,34 @@ export class ExecutionRecoveryService { } if (workflow.activeVersionId !== null) { - await this.workflowRepository.updateActiveState(workflowId, false); - this.logger.warn( - `Autodeactivated workflow ${workflowId} due to too many crashed executions.`, - ); + try { + // Lazy resolution breaks the DI cycle: WorkflowService → + // ActiveWorkflowManager → MessageEventBus → this service + const { WorkflowService } = await import('@/workflows/workflow.service.js'); + await Container.get(WorkflowService).deactivateWorkflowAsSystem(workflowId); + this.logger.warn( + `Autodeactivated workflow ${workflowId} due to too many crashed executions.`, + ); - const recipient = await this.getAutodeactivationRecipient(workflow); - await this.userManagementMailer.notifyWorkflowAutodeactivated({ - recipient, - workflow, - }); + const recipient = await this.getAutodeactivationRecipient(workflow); + await this.userManagementMailer.notifyWorkflowAutodeactivated({ + recipient, + workflow, + }); - this.push.once('editorUiConnected', async () => { - await sleep(1000); - this.push.broadcast({ type: 'workflowAutoDeactivated', data: { workflowId } }); - }); + this.push.once('editorUiConnected', async () => { + await sleep(1000); + this.push.broadcast({ type: 'workflowAutoDeactivated', data: { workflowId } }); + }); + } catch (error) { + // A throw here would abort startup recovery for the remaining + // workflows and leave the event bus's recovery-in-progress marker + // behind, making every future startup skip recovery entirely. + this.logger.error(`Failed to auto-deactivate workflow ${workflowId}`, { + workflowId, + error: ensureError(error), + }); + } } await this.executionRepository.update( diff --git a/packages/cli/src/instance-settings-loader/__tests__/sso/oidc.instance-settings-loader.test.ts b/packages/cli/src/instance-settings-loader/__tests__/sso/oidc.instance-settings-loader.test.ts index 7c00b258768..dc10dfaa810 100644 --- a/packages/cli/src/instance-settings-loader/__tests__/sso/oidc.instance-settings-loader.test.ts +++ b/packages/cli/src/instance-settings-loader/__tests__/sso/oidc.instance-settings-loader.test.ts @@ -18,6 +18,8 @@ describe('OidcInstanceSettingsLoader', () => { oidcLoginEnabled: false, oidcPrompt: 'select_account', oidcAcrValues: '', + oidcAdditionalScopes: '', + oidcRpInitiatedLogoutEnabled: false, }; const validConfig: Partial = { @@ -82,6 +84,20 @@ describe('OidcInstanceSettingsLoader', () => { const parsed = getUpsertedValue(); expect(parsed.authenticationContextClassReference).toEqual(['mfa', 'phrh']); }); + + it('should persist additionalScopes and rpInitiatedLogoutEnabled', async () => { + const loader = createLoader({ + ...validConfig, + oidcAdditionalScopes: 'offline_access', + oidcRpInitiatedLogoutEnabled: true, + }); + + await loader.apply(); + + const parsed = getUpsertedValue(); + expect(parsed.additionalScopes).toBe('offline_access'); + expect(parsed.rpInitiatedLogoutEnabled).toBe(true); + }); }); describe('when OIDC login is disabled', () => { diff --git a/packages/cli/src/instance-settings-loader/loaders/sso/oidc.instance-settings-loader.ts b/packages/cli/src/instance-settings-loader/loaders/sso/oidc.instance-settings-loader.ts index 0e7fc83dc92..bf8d1e2fec4 100644 --- a/packages/cli/src/instance-settings-loader/loaders/sso/oidc.instance-settings-loader.ts +++ b/packages/cli/src/instance-settings-loader/loaders/sso/oidc.instance-settings-loader.ts @@ -29,6 +29,8 @@ const oidcEnvSchema = z }), }), oidcAcrValues: z.string(), + oidcAdditionalScopes: z.string(), + oidcRpInitiatedLogoutEnabled: z.boolean(), }) .transform((input) => ({ clientId: input.oidcClientId, @@ -42,6 +44,8 @@ const oidcEnvSchema = z .map((v) => v.trim()) .filter(Boolean) : [], + additionalScopes: input.oidcAdditionalScopes, + rpInitiatedLogoutEnabled: input.oidcRpInitiatedLogoutEnabled, })); @Service() diff --git a/packages/cli/src/interfaces.ts b/packages/cli/src/interfaces.ts index 03773bcb8fc..1630c21cf0c 100644 --- a/packages/cli/src/interfaces.ts +++ b/packages/cli/src/interfaces.ts @@ -132,6 +132,17 @@ export interface IExecutionsCurrentSummary { status: ExecutionStatus; } +/** + * An already-persisted execution a process wants to take over, plus the status it must + * still be in for the claim to succeed. The status travels with the id so the takeover + * is a compare-and-swap: if the row moved on, another process already claimed it. + */ +export interface ResumableExecution { + executionId: string; + /** `waiting` for a row being resumed, `new` for one enqueued before a restart. */ + expectedStatus: Extract; +} + export interface IExecutingWorkflowData { executionData: IWorkflowExecutionDataProcess; startedAt: Date; diff --git a/packages/cli/src/metrics/prometheus/__tests__/scheduler-metrics.service.test.ts b/packages/cli/src/metrics/prometheus/__tests__/scheduler-metrics.service.test.ts index 1382167284b..b14487ba4fc 100644 --- a/packages/cli/src/metrics/prometheus/__tests__/scheduler-metrics.service.test.ts +++ b/packages/cli/src/metrics/prometheus/__tests__/scheduler-metrics.service.test.ts @@ -1,5 +1,6 @@ import { mockInstance } from '@n8n/backend-test-utils'; import { PrometheusMetricsConfig } from '@n8n/config'; +import { ScheduledJobMisfirePolicy } from '@n8n/constants'; import type { ScheduledTaskMetricSnapshot, ScheduledTaskRepository } from '@n8n/db'; import type { InstanceSettings } from 'n8n-core'; import promClient from 'prom-client'; @@ -156,6 +157,8 @@ describe('PrometheusSchedulerMetricsService', () => { for (const name of [ 'n8n_scheduler_occurrences_materialized_total', 'n8n_scheduler_jobs_deferred_total', + 'n8n_scheduler_occurrences_retired_total', + 'n8n_scheduler_occurrences_missed_total', 'n8n_scheduler_tasks_reclaimed_total', 'n8n_scheduler_tasks_dead_lettered_total', 'n8n_scheduler_tasks_pruned_total', @@ -268,7 +271,7 @@ describe('PrometheusSchedulerMetricsService', () => { }); it('increments reclaimed and dead-lettered into their own counters', () => { - service.recordReaped(3, 1); + service.recordReaped(3, 1, 0); const reclaimed = counterIncFor('n8n_scheduler_tasks_reclaimed_total'); const deadLettered = counterIncFor('n8n_scheduler_tasks_dead_lettered_total'); @@ -278,6 +281,25 @@ describe('PrometheusSchedulerMetricsService', () => { expect(deadLettered).toHaveBeenCalledTimes(1); }); + it('labels discarded occurrences by task type and policy', () => { + service.recordMisfired([ + { taskType: 'workflow', policy: ScheduledJobMisfirePolicy.Coalesce, discarded: 4 }, + { taskType: 'poll', policy: ScheduledJobMisfirePolicy.Skip, discarded: 2 }, + ]); + + const misfired = counterIncFor('n8n_scheduler_occurrences_misfired_total'); + expect(misfired).toHaveBeenCalledWith({ task_type: 'workflow', policy: 'coalesce' }, 4); + expect(misfired).toHaveBeenCalledWith({ task_type: 'poll', policy: 'skip' }, 2); + }); + + it('counts retired and reaper-missed occurrences on their own counters', () => { + service.recordRetired(2); + service.recordReaped(0, 0, 6); + + expect(counterIncFor('n8n_scheduler_occurrences_retired_total')).toHaveBeenCalledWith(2); + expect(counterIncFor('n8n_scheduler_occurrences_missed_total')).toHaveBeenCalledWith(6); + }); + it('increments the dead-lettered counter by one on the executor terminal-failure path', () => { service.recordDeadLettered(); @@ -303,7 +325,11 @@ describe('PrometheusSchedulerMetricsService', () => { service.recordRetry('workflow'); service.observeDispatchLagSeconds('workflow', 1); service.recordMaterialized(1, 1); - service.recordReaped(1, 1); + service.recordMisfired([ + { taskType: 'workflow', policy: ScheduledJobMisfirePolicy.Coalesce, discarded: 1 }, + ]); + service.recordRetired(1); + service.recordReaped(1, 1, 1); service.recordDeadLettered(); service.recordPruned(1); diff --git a/packages/cli/src/metrics/prometheus/__tests__/workflow-publication-metrics.service.test.ts b/packages/cli/src/metrics/prometheus/__tests__/workflow-publication-metrics.service.test.ts index 754bdd6d9cf..df07b2f6407 100644 --- a/packages/cli/src/metrics/prometheus/__tests__/workflow-publication-metrics.service.test.ts +++ b/packages/cli/src/metrics/prometheus/__tests__/workflow-publication-metrics.service.test.ts @@ -189,6 +189,12 @@ describe('PrometheusWorkflowPublicationMetricsService', () => { expect(mockCounterInc).toHaveBeenCalledWith(2); expect(mockHistogramObserve).toHaveBeenCalledWith({ result: 'success' }, 0.5); }); + + it('records ghost-trigger sweep removals', () => { + eventHandler('workflow-publication-ghost-trigger-sweep')!({ removedCount: 3 } as never); + + expect(mockCounterInc).toHaveBeenCalledWith(3); + }); }); describe('gauge collection', () => { diff --git a/packages/cli/src/metrics/prometheus/scheduler-metrics.service.ts b/packages/cli/src/metrics/prometheus/scheduler-metrics.service.ts index 9c3226ed436..5d7dadb1d75 100644 --- a/packages/cli/src/metrics/prometheus/scheduler-metrics.service.ts +++ b/packages/cli/src/metrics/prometheus/scheduler-metrics.service.ts @@ -2,7 +2,7 @@ import { PrometheusMetricsConfig } from '@n8n/config'; import { Time } from '@n8n/constants'; import { ScheduledTaskRepository, type ScheduledTaskMetricSnapshot } from '@n8n/db'; import { Service } from '@n8n/di'; -import type { SchedulerMetrics } from '@n8n/scheduler'; +import type { MisfireCount, SchedulerMetrics } from '@n8n/scheduler'; import { InstanceSettings } from 'n8n-core'; import promClient from 'prom-client'; @@ -33,6 +33,9 @@ export class PrometheusSchedulerMetricsService private taskRetries!: promClient.Counter<'task_type'>; private occurrencesMaterialized!: promClient.Counter; private jobsDeferred!: promClient.Counter; + private occurrencesMisfired!: promClient.Counter<'task_type' | 'policy'>; + private occurrencesRetired!: promClient.Counter; + private occurrencesMissed!: promClient.Counter; private tasksReclaimed!: promClient.Counter; private tasksDeadLettered!: promClient.Counter; private tasksPruned!: promClient.Counter; @@ -86,6 +89,22 @@ export class PrometheusSchedulerMetricsService help: 'Total number of jobs deferred for retry during materialization.', }); + this.occurrencesMisfired = new promClient.Counter({ + name: `${prefix}scheduler_occurrences_misfired_total`, + help: "Total number of due occurrences a schedule's misfire policy discarded rather than record, by task type and policy.", + labelNames: ['task_type', 'policy'], + }); + + this.occurrencesRetired = new promClient.Counter({ + name: `${prefix}scheduler_occurrences_retired_total`, + help: 'Total number of already-recorded occurrences retired because a catch-up run superseded them.', + }); + + this.occurrencesMissed = new promClient.Counter({ + name: `${prefix}scheduler_occurrences_missed_total`, + help: 'Total number of pending occurrences the reaper marked missed after they went past their deadline unclaimed.', + }); + this.tasksReclaimed = new promClient.Counter({ name: `${prefix}scheduler_tasks_reclaimed_total`, help: 'Total number of expired scheduler tasks reclaimed by the reaper.', @@ -112,6 +131,8 @@ export class PrometheusSchedulerMetricsService // series are created lazily as task types are discovered at runtime. this.occurrencesMaterialized.inc(0); this.jobsDeferred.inc(0); + this.occurrencesRetired.inc(0); + this.occurrencesMissed.inc(0); this.tasksReclaimed.inc(0); this.tasksDeadLettered.inc(0); this.tasksPruned.inc(0); @@ -208,10 +229,28 @@ export class PrometheusSchedulerMetricsService } } - recordReaped(reclaimed: number, deadLettered: number) { + recordMisfired(discarded: MisfireCount[]) { + if (this.initialized) { + for (const group of discarded) { + this.occurrencesMisfired.inc( + { task_type: group.taskType, policy: group.policy }, + group.discarded, + ); + } + } + } + + recordRetired(retired: number) { + if (this.initialized) { + this.occurrencesRetired.inc(retired); + } + } + + recordReaped(reclaimed: number, deadLettered: number, missed: number) { if (this.initialized) { this.tasksReclaimed.inc(reclaimed); this.tasksDeadLettered.inc(deadLettered); + this.occurrencesMissed.inc(missed); } } diff --git a/packages/cli/src/metrics/prometheus/workflow-publication-metrics.service.ts b/packages/cli/src/metrics/prometheus/workflow-publication-metrics.service.ts index 5e0bdd385ff..4e92e1687da 100644 --- a/packages/cli/src/metrics/prometheus/workflow-publication-metrics.service.ts +++ b/packages/cli/src/metrics/prometheus/workflow-publication-metrics.service.ts @@ -57,6 +57,7 @@ export class PrometheusWorkflowPublicationMetricsService implements PrometheusMe this.initTriggerMetrics(); this.initCleanupMetrics(); this.initReconciliationMetrics(); + this.initGhostSweepMetrics(); } private initOutboxGauges() { @@ -226,4 +227,17 @@ export class PrometheusWorkflowPublicationMetricsService implements PrometheusMe }, ); } + + private initGhostSweepMetrics() { + const prefix = this.config.prefix; + + const removed = new promClient.Counter({ + name: `${prefix}workflow_publication_reconciliation_ghost_trigger_workflows_total`, + help: 'Total number of workflows whose ghost triggers were torn down because they were registered on a non-leader instance.', + }); + + this.eventService.on('workflow-publication-ghost-trigger-sweep', ({ removedCount }) => { + removed.inc(removedCount); + }); + } } diff --git a/packages/cli/src/modules/agent-evals/__tests__/agent-eval-case-generation.service.test.ts b/packages/cli/src/modules/agent-evals/__tests__/agent-eval-case-generation.service.test.ts index a5c4ca9cc3c..9ac088349cd 100644 --- a/packages/cli/src/modules/agent-evals/__tests__/agent-eval-case-generation.service.test.ts +++ b/packages/cli/src/modules/agent-evals/__tests__/agent-eval-case-generation.service.test.ts @@ -1,5 +1,4 @@ import type { AgentJsonConfig } from '@n8n/api-types'; -import { AGENT_EVALS_FLAG } from '@n8n/api-types'; import type { Logger } from '@n8n/backend-common'; import type { AgentEvalDataset, AgentEvalDatasetRepository, User } from '@n8n/db'; import type { Mocked } from 'vitest'; @@ -9,13 +8,13 @@ import type { CredentialsService } from '@/credentials/credentials.service'; import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; import { NotFoundError } from '@/errors/response-errors/not-found.error'; import type { SourceControlPreferencesService } from '@/modules/source-control.ee/source-control-preferences.service.ee'; -import type { PostHogClient } from '@/posthog'; import type { AgentConfigService } from '../../agents/agent-config.service'; import type { DataTable } from '../../data-table/data-table.entity'; import type { DataTableService } from '../../data-table/data-table.service'; import { DataTableNameConflictError } from '../../data-table/errors/data-table-name-conflict.error'; import { AgentEvalCaseGenerationService } from '../agent-eval-case-generation.service'; +import type { AgentEvalsFlagGate } from '../agent-evals-flag-gate'; // Stub the @n8n/agents SDK: fluent builder is a no-op; `generate` is a // controllable mock so tests drive the model's (in)valid structured output. @@ -75,7 +74,7 @@ describe('AgentEvalCaseGenerationService', () => { let credentialsService: Mocked; let dataTableService: Mocked; let datasetRepository: Mocked; - let postHogClient: Mocked; + let flagGate: Mocked; let sourceControlPreferences: Mocked; beforeEach(() => { @@ -85,7 +84,7 @@ describe('AgentEvalCaseGenerationService', () => { credentialsService = mock(); dataTableService = mock(); datasetRepository = mock(); - postHogClient = mock(); + flagGate = mock(); sourceControlPreferences = mock(); sourceControlPreferences.getPreferences.mockReturnValue({ branchReadOnly: false, @@ -95,7 +94,7 @@ describe('AgentEvalCaseGenerationService', () => { resolveModelMock.mockReset(); resolveModelMock.mockResolvedValue({ id: 'anthropic/claude-sonnet-4-5' }); - postHogClient.getFeatureFlags.mockResolvedValue({ [AGENT_EVALS_FLAG]: true }); + flagGate.assertEnabled.mockResolvedValue(undefined); agentConfigService.getConfig.mockResolvedValue(makeConfig()); dataTableService.createDataTable.mockResolvedValue({ id: 'dt-1' } as DataTable); dataTableService.insertRows.mockResolvedValue(undefined as never); @@ -107,13 +106,13 @@ describe('AgentEvalCaseGenerationService', () => { credentialsService, dataTableService, datasetRepository, - postHogClient, + flagGate, sourceControlPreferences, ); }); it('rejects when the agent-evals flag is disabled (as not-found, leaking no flag state)', async () => { - postHogClient.getFeatureFlags.mockResolvedValue({}); + flagGate.assertEnabled.mockRejectedValue(new NotFoundError('Not found')); await expect(service.generateDraftCases(user, 'project-1', 'agent-1')).rejects.toThrow( NotFoundError, diff --git a/packages/cli/src/modules/agent-evals/__tests__/agent-eval-rating.repository.integration.test.ts b/packages/cli/src/modules/agent-evals/__tests__/agent-eval-rating.repository.integration.test.ts new file mode 100644 index 00000000000..8bcee6e9cb2 --- /dev/null +++ b/packages/cli/src/modules/agent-evals/__tests__/agent-eval-rating.repository.integration.test.ts @@ -0,0 +1,151 @@ +import { createTeamProject, testDb, testModules } from '@n8n/backend-test-utils'; +import { + AgentEvalDatasetRepository, + AgentEvalRatingRepository, + AgentEvalResultRepository, + AgentEvalRunRepository, + GLOBAL_OWNER_ROLE, + type AgentEvalResult, + type User, +} from '@n8n/db'; +import { Container } from '@n8n/di'; +import { DataSource } from '@n8n/typeorm'; + +import { Agent } from '@/modules/agents/entities/agent.entity'; +import { createUserShell } from '@test-integration/db/users'; + +// `findLatestByRunId` joins two tables, so it runs against the real driver here: +// a mocked entity manager would green-light SQL the database rejects. + +let owner: User; +let ratingRepository: AgentEvalRatingRepository; +let resultRepository: AgentEvalResultRepository; +let runRepository: AgentEvalRunRepository; +let datasetRepository: AgentEvalDatasetRepository; + +/** Insert a minimal real agent row so `agent_eval_dataset.agentId`'s FK holds. */ +async function createAgent(projectId: string): Promise { + return await Container.get(DataSource) + .getRepository(Agent) + .save(Object.assign(new Agent(), { name: 'test agent', projectId })); +} + +/** A run with `count` seeded result rows, wired through a real dataset + agent. */ +async function createRunWithResults(count: number): Promise { + const project = await createTeamProject(); + const agent = await createAgent(project.id); + + const dataset = await datasetRepository.createDataset({ + name: 'ds', + agentId: agent.id, + datasetSource: 'data_table', + datasetRef: { dataTableId: 'dt-1' }, + columnMapping: { input: 'question' }, + createdById: owner.id, + }); + const run = await runRepository.createRun({ datasetId: dataset.id, createdById: owner.id }); + + return await resultRepository.seedResults( + Array.from({ length: count }, (_, index) => ({ runId: run.id, runIndex: index })), + ); +} + +// Timestamps are millisecond-precision, so a pause guarantees a newer `createdAt`. +const tick = async () => await new Promise((resolve) => setTimeout(resolve, 10)); + +beforeAll(async () => { + await testModules.loadModules(['agents']); + await testDb.init(); + owner = await createUserShell(GLOBAL_OWNER_ROLE); + + ratingRepository = Container.get(AgentEvalRatingRepository); + resultRepository = Container.get(AgentEvalResultRepository); + runRepository = Container.get(AgentEvalRunRepository); + datasetRepository = Container.get(AgentEvalDatasetRepository); +}); + +beforeEach(async () => { + // `Agent`/`Project` aren't truncated: per-test, and absent from testDb's union. + await testDb.truncate(['AgentEvalRating', 'AgentEvalResult', 'AgentEvalRun', 'AgentEvalDataset']); +}); + +afterAll(async () => { + await testDb.terminate(); +}); + +describe('AgentEvalRatingRepository.findLatestByRunId (integration)', () => { + it('returns only the newest rating for a re-voted result', async () => { + const [result] = await createRunWithResults(1); + + await ratingRepository.createRating({ + resultId: result.id, + vote: 'down', + correction: { finalText: 'the answer the agent should have given' }, + ratedById: owner.id, + }); + await tick(); + const newest = await ratingRepository.createRating({ + resultId: result.id, + vote: 'up', + comment: 'second look, this is fine', + ratedById: owner.id, + }); + + const latest = await ratingRepository.findLatestByRunId(result.runId); + + expect(latest).toHaveLength(1); + expect(latest[0]).toMatchObject({ id: newest.id, vote: 'up' }); + // The superseded correction is still on record for calibration. + await expect(ratingRepository.findByResultId(result.id)).resolves.toHaveLength(2); + }); + + it('returns one rating per rated result and skips unrated ones', async () => { + const [first, second, unrated] = await createRunWithResults(3); + + await ratingRepository.createRating({ resultId: first.id, vote: 'up' }); + await ratingRepository.createRating({ resultId: second.id, vote: 'down' }); + + const latest = await ratingRepository.findLatestByRunId(first.runId); + + expect(latest).toHaveLength(2); + expect(latest.map((rating) => rating.resultId).sort()).toEqual([first.id, second.id].sort()); + expect(latest.map((rating) => rating.resultId)).not.toContain(unrated.id); + }); + + it('persists the corrected answer as JSON, readable back unchanged', async () => { + const [result] = await createRunWithResults(1); + const correction = { finalText: 'Refunds are processed within 14 days.' }; + + await ratingRepository.createRating({ + resultId: result.id, + vote: 'down', + correction, + ratedById: owner.id, + }); + + const [latest] = await ratingRepository.findLatestByRunId(result.runId); + + expect(latest.correction).toEqual(correction); + expect(latest.ratedById).toBe(owner.id); + expect(latest.createdAt).toBeInstanceOf(Date); + }); + + it('scopes to the run, ignoring ratings from another run', async () => { + const [mine] = await createRunWithResults(1); + const [theirs] = await createRunWithResults(1); + + await ratingRepository.createRating({ resultId: mine.id, vote: 'up' }); + await ratingRepository.createRating({ resultId: theirs.id, vote: 'down' }); + + const latest = await ratingRepository.findLatestByRunId(mine.runId); + + expect(latest).toHaveLength(1); + expect(latest[0].resultId).toBe(mine.id); + }); + + it('returns nothing for a run with no ratings', async () => { + const [result] = await createRunWithResults(1); + + await expect(ratingRepository.findLatestByRunId(result.runId)).resolves.toEqual([]); + }); +}); diff --git a/packages/cli/src/modules/agent-evals/__tests__/agent-eval-rating.service.test.ts b/packages/cli/src/modules/agent-evals/__tests__/agent-eval-rating.service.test.ts new file mode 100644 index 00000000000..78d60d3c9df --- /dev/null +++ b/packages/cli/src/modules/agent-evals/__tests__/agent-eval-rating.service.test.ts @@ -0,0 +1,293 @@ +import { AGENT_EVALS_FLAG, type CreateAgentEvalRatingPayload } from '@n8n/api-types'; +import type { Logger } from '@n8n/backend-common'; +import type { + AgentEvalDataset, + AgentEvalDatasetRepository, + AgentEvalRating, + AgentEvalRatingRepository, + AgentEvalResult, + AgentEvalResultRepository, + AgentEvalRun, + AgentEvalRunRepository, + User, +} from '@n8n/db'; +import type { Mocked } from 'vitest'; +import { mock } from 'vitest-mock-extended'; + +import { BadRequestError } from '@/errors/response-errors/bad-request.error'; +import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; +import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import type { AgentRepository } from '@/modules/agents/repositories/agent.repository'; +import { userHasScopes } from '@/permissions.ee/check-access'; +import type { PostHogClient } from '@/posthog'; + +import { AgentEvalRatingService } from '../agent-eval-rating.service'; + +// Stub the statically imported specifiers to keep the agents module graph out. +vi.mock('@/modules/agents/repositories/agent.repository', () => ({ + AgentRepository: class AgentRepository {}, +})); +vi.mock('@/permissions.ee/check-access', () => ({ + userHasScopes: vi.fn().mockResolvedValue(true), +})); + +const user = mock({ id: 'user-1' }); +const PROJECT_ID = 'project-1'; + +describe('AgentEvalRatingService', () => { + let service: AgentEvalRatingService; + let logger: Mocked; + let ratingRepository: Mocked; + let resultRepository: Mocked; + let runRepository: Mocked; + let datasetRepository: Mocked; + let agentRepository: Mocked; + let postHogClient: Mocked; + + const result = mock({ id: 'res-1', runId: 'run-1', status: 'success' }); + const run = mock({ id: 'run-1', datasetId: 'ds-1' }); + const dataset = mock({ id: 'ds-1', agentId: 'agent-1' }); + + beforeEach(() => { + vi.mocked(userHasScopes).mockResolvedValue(true); + + logger = mock(); + ratingRepository = mock(); + resultRepository = mock(); + runRepository = mock(); + datasetRepository = mock(); + agentRepository = mock(); + postHogClient = mock(); + + postHogClient.getFeatureFlags.mockResolvedValue({ [AGENT_EVALS_FLAG]: true }); + resultRepository.findById.mockResolvedValue(result); + runRepository.findById.mockResolvedValue(run); + datasetRepository.findById.mockResolvedValue(dataset); + agentRepository.existsByIdAndProjectId.mockResolvedValue(true); + ratingRepository.createRating.mockImplementation(async (attrs) => + mock({ id: 'rating-1', ...attrs }), + ); + + service = new AgentEvalRatingService( + logger, + ratingRepository, + resultRepository, + runRepository, + datasetRepository, + agentRepository, + postHogClient, + ); + }); + + describe('access control', () => { + it('rejects when the agent-evals flag is disabled (as not-found, leaking no flag state)', async () => { + postHogClient.getFeatureFlags.mockResolvedValue({ [AGENT_EVALS_FLAG]: false }); + + await expect( + service.rateResult(user, PROJECT_ID, 'res-1', { vote: 'up' }), + ).rejects.toThrowError(NotFoundError); + expect(ratingRepository.createRating).not.toHaveBeenCalled(); + }); + + it('rejects a caller lacking the rating scope, without touching the eval tables', async () => { + vi.mocked(userHasScopes).mockResolvedValue(false); + + await expect( + service.rateResult(user, PROJECT_ID, 'res-1', { vote: 'up' }), + ).rejects.toThrowError(ForbiddenError); + // Authorization runs before any lookup, so result ids can't be probed. + expect(resultRepository.findById).not.toHaveBeenCalled(); + }); + + // A project viewer holds execute but not update. + it('requires agent:execute to rate and agent:read to read', async () => { + await service.rateResult(user, PROJECT_ID, 'res-1', { vote: 'up' }); + expect(vi.mocked(userHasScopes)).toHaveBeenCalledWith(user, ['agent:execute'], false, { + projectId: PROJECT_ID, + }); + + vi.mocked(userHasScopes).mockClear(); + + await service.listRatingsForResult(user, PROJECT_ID, 'res-1'); + expect(vi.mocked(userHasScopes)).toHaveBeenCalledWith(user, ['agent:read'], false, { + projectId: PROJECT_ID, + }); + }); + + it('rejects an unknown result', async () => { + resultRepository.findById.mockResolvedValue(null); + + await expect( + service.rateResult(user, PROJECT_ID, 'res-1', { vote: 'up' }), + ).rejects.toThrowError(NotFoundError); + }); + + it('rejects a result whose agent lives in another project (as not-found)', async () => { + agentRepository.existsByIdAndProjectId.mockResolvedValue(false); + + await expect( + service.rateResult(user, PROJECT_ID, 'res-1', { vote: 'up' }), + ).rejects.toThrowError(NotFoundError); + expect(ratingRepository.createRating).not.toHaveBeenCalled(); + }); + }); + + describe('rateResult', () => { + it('persists an upvote with no correction, attributed to the rater', async () => { + await service.rateResult(user, PROJECT_ID, 'res-1', { vote: 'up' }); + + expect(ratingRepository.createRating).toHaveBeenCalledWith({ + resultId: 'res-1', + vote: 'up', + comment: null, + correction: null, + ratedById: 'user-1', + }); + }); + + it('persists a downvote with a comment and the edited answer', async () => { + await service.rateResult(user, PROJECT_ID, 'res-1', { + vote: 'down', + comment: 'missed the refund policy', + correction: { finalText: 'Refunds are processed within 14 days.' }, + }); + + expect(ratingRepository.createRating).toHaveBeenCalledWith({ + resultId: 'res-1', + vote: 'down', + comment: 'missed the refund policy', + correction: { finalText: 'Refunds are processed within 14 days.' }, + ratedById: 'user-1', + }); + }); + + it.each(['new', 'running'] as const)('refuses to rate a %s case', async (status) => { + resultRepository.findById.mockResolvedValue( + mock({ id: 'res-1', runId: 'run-1', status }), + ); + + await expect( + service.rateResult(user, PROJECT_ID, 'res-1', { vote: 'up' }), + ).rejects.toThrowError(BadRequestError); + expect(ratingRepository.createRating).not.toHaveBeenCalled(); + }); + + it.each(['error', 'cancelled'] as const)( + 'allows rating a %s case — "it failed" is a judgment', + async (status) => { + resultRepository.findById.mockResolvedValue( + mock({ id: 'res-1', runId: 'run-1', status }), + ); + + await service.rateResult(user, PROJECT_ID, 'res-1', { vote: 'down' }); + + expect(ratingRepository.createRating).toHaveBeenCalledWith( + expect.objectContaining({ vote: 'down' }), + ); + }, + ); + + it('appends on re-vote rather than overwriting the earlier rating', async () => { + await service.rateResult(user, PROJECT_ID, 'res-1', { vote: 'down' }); + await service.rateResult(user, PROJECT_ID, 'res-1', { vote: 'up' }); + + expect(ratingRepository.createRating).toHaveBeenCalledTimes(2); + expect(ratingRepository.createRating).toHaveBeenLastCalledWith( + expect.objectContaining({ resultId: 'res-1', vote: 'up' }), + ); + }); + + it('rejects an over-long comment', async () => { + await expect( + service.rateResult(user, PROJECT_ID, 'res-1', { + vote: 'down', + comment: 'x'.repeat(2_001), + }), + ).rejects.toThrowError(BadRequestError); + expect(ratingRepository.createRating).not.toHaveBeenCalled(); + }); + + it('rejects an over-long corrected answer', async () => { + await expect( + service.rateResult(user, PROJECT_ID, 'res-1', { + vote: 'down', + correction: { finalText: 'x'.repeat(20_001) }, + }), + ).rejects.toThrowError(BadRequestError); + expect(ratingRepository.createRating).not.toHaveBeenCalled(); + }); + + it('rejects an oversized correction spread across other keys', async () => { + const correction = { + finalText: 'short', + ...Object.fromEntries( + Array.from({ length: 20 }, (_, i) => [`field${i}`, 'x'.repeat(2_000)]), + ), + }; + + await expect( + service.rateResult(user, PROJECT_ID, 'res-1', { vote: 'down', correction }), + ).rejects.toThrowError(BadRequestError); + }); + + // The service is the enforcement point until the REST layer's DTO validation + // lands, and a correction nothing can read defeats the point of capturing it. + it.each([ + { label: 'an absent finalText', correction: {} }, + { label: 'the wrong key', correction: { output: 'the expected answer' } }, + { label: 'a null finalText', correction: { finalText: null } }, + { label: 'a blank finalText', correction: { finalText: ' ' } }, + { label: 'a non-string finalText', correction: { finalText: { nested: 'object' } } }, + ])('rejects a correction with $label', async ({ correction }) => { + // Cast: these shapes are what an unvalidated caller sends, and the tightened + // payload type now refuses them at compile time. + const payload = { vote: 'down', correction } as CreateAgentEvalRatingPayload; + + await expect(service.rateResult(user, PROJECT_ID, 'res-1', payload)).rejects.toThrowError( + BadRequestError, + ); + expect(ratingRepository.createRating).not.toHaveBeenCalled(); + }); + + it('keeps extra correction keys alongside the edited answer', async () => { + const correction = { finalText: 'the expected answer', fields: { tone: 'formal' } }; + + await service.rateResult(user, PROJECT_ID, 'res-1', { vote: 'down', correction }); + + expect(ratingRepository.createRating).toHaveBeenCalledWith( + expect.objectContaining({ correction }), + ); + }); + }); + + describe('listRatingsForResult', () => { + it('returns the result history', async () => { + const ratings = [mock({ id: 'rating-2' })]; + ratingRepository.findByResultId.mockResolvedValue(ratings); + + await expect(service.listRatingsForResult(user, PROJECT_ID, 'res-1')).resolves.toBe(ratings); + expect(ratingRepository.findByResultId).toHaveBeenCalledWith('res-1'); + }); + }); + + describe('listLatestRatingsForRun', () => { + it('returns the newest rating per result for the run', async () => { + const ratings = [mock({ id: 'rating-3' })]; + ratingRepository.findLatestByRunId.mockResolvedValue(ratings); + + await expect(service.listLatestRatingsForRun(user, PROJECT_ID, 'run-1')).resolves.toBe( + ratings, + ); + expect(ratingRepository.findLatestByRunId).toHaveBeenCalledWith('run-1'); + }); + + it('rejects a run from another project (as not-found)', async () => { + agentRepository.existsByIdAndProjectId.mockResolvedValue(false); + + await expect(service.listLatestRatingsForRun(user, PROJECT_ID, 'run-1')).rejects.toThrowError( + NotFoundError, + ); + expect(ratingRepository.findLatestByRunId).not.toHaveBeenCalled(); + }); + }); +}); diff --git a/packages/cli/src/modules/agent-evals/__tests__/agent-eval-runner.service.integration.test.ts b/packages/cli/src/modules/agent-evals/__tests__/agent-eval-runner.service.integration.test.ts index 1c99a872706..973ea835d22 100644 --- a/packages/cli/src/modules/agent-evals/__tests__/agent-eval-runner.service.integration.test.ts +++ b/packages/cli/src/modules/agent-evals/__tests__/agent-eval-runner.service.integration.test.ts @@ -1,3 +1,4 @@ +import { ModuleRegistry } from '@n8n/backend-common'; import { createTeamProject, testDb, testModules } from '@n8n/backend-test-utils'; import { GlobalConfig } from '@n8n/config'; import { @@ -21,6 +22,7 @@ import type { EvalAgentExecutionService } from '@/modules/instance-ai/eval/agent import { createUserShell } from '@test-integration/db/users'; import { AgentEvalRunnerService } from '../agent-eval-runner.service'; +import { AgentEvalsFlagGate } from '../agent-evals-flag-gate'; // The agent under test runs through the real reconstruction + live-model path, // which needs credentials/network and is covered by the instance-ai eval suite; @@ -46,6 +48,9 @@ const buildRunner = () => mock(), Container.get(GlobalConfig), instanceSettings, + // `loadModules` above marks agents/data-table active, so the module guard + // in `startRun` sees the same state production would. + Container.get(ModuleRegistry), Container.get(AgentEvalDatasetRepository), Container.get(AgentEvalRunRepository), Container.get(AgentEvalResultRepository), @@ -54,6 +59,11 @@ const buildRunner = () => evalAgentExecutionService, concurrencyControl, Container.get(License), + // Real gate, not a mock: it resolves the flag through the real + // `PostHogClient`, which (uninitialized here, so no network) falls back to + // the env overrides — exercising the operator force-enable path the + // `agentEvalsEnabled` assignment in `beforeEach` relies on. + Container.get(AgentEvalsFlagGate), ); /** Insert a minimal real agent row so `agent_eval_dataset.agentId`'s FK holds. */ @@ -140,7 +150,7 @@ describe('AgentEvalRunnerService (integration)', () => { const { runId, finished } = await runner.startRun(dataset.id, project.id, owner); await finished; - const summary = await runner.getRunSummary(runId); + const summary = await runner.getRunSummary(runId, agent.id); expect(summary.status).toBe('completed'); expect(summary.counts).toMatchObject({ total: 2, success: 2, error: 0, cancelled: 0 }); @@ -203,7 +213,7 @@ describe('AgentEvalRunnerService (integration)', () => { const { runId, finished } = await runner.startRun(dataset.id, project.id, owner); await finished; - const summary = await runner.getRunSummary(runId); + const summary = await runner.getRunSummary(runId, agent.id); expect(summary.status).toBe('completed'); expect(summary.counts).toMatchObject({ total: 1, success: 0, error: 1 }); diff --git a/packages/cli/src/modules/agent-evals/__tests__/agent-eval-runner.service.test.ts b/packages/cli/src/modules/agent-evals/__tests__/agent-eval-runner.service.test.ts index e81b798011b..c19a11668e1 100644 --- a/packages/cli/src/modules/agent-evals/__tests__/agent-eval-runner.service.test.ts +++ b/packages/cli/src/modules/agent-evals/__tests__/agent-eval-runner.service.test.ts @@ -1,3 +1,4 @@ +import type { ModuleRegistry } from '@n8n/backend-common'; import type { GlobalConfig } from '@n8n/config'; import type { AgentEvalDataset, @@ -11,6 +12,7 @@ import type { InstanceSettings } from 'n8n-core'; import { mock, type MockProxy } from 'vitest-mock-extended'; import type { ConcurrencyControlService } from '@/concurrency/concurrency-control.service'; +import { NotFoundError } from '@/errors/response-errors/not-found.error'; import { resolveEvaluationConcurrencyLimit } from '@/evaluation.ee/evaluation-concurrency.helper'; import type { License } from '@/license'; import type { Agent } from '@/modules/agents/entities/agent.entity'; @@ -20,6 +22,7 @@ import type { EvalAgentExecutionService } from '@/modules/instance-ai/eval/agent import { userHasScopes } from '@/permissions.ee/check-access'; import { AgentEvalRunnerService } from '../agent-eval-runner.service'; +import type { AgentEvalsFlagGate } from '../agent-evals-flag-gate'; // Stub the cross-module specifiers the service statically imports so the unit // test doesn't pull in the real agents / data-table / instance-ai module graph. @@ -92,6 +95,7 @@ describe('AgentEvalRunnerService', () => { let globalConfig: GlobalConfig; let instanceSettings: InstanceSettings; + let moduleRegistry: MockProxy; let datasetRepository: MockProxy; let runRepository: MockProxy; let resultRepository: MockProxy; @@ -100,6 +104,7 @@ describe('AgentEvalRunnerService', () => { let evalAgentExecutionService: MockProxy; let concurrencyControl: MockProxy; let license: MockProxy; + let flagGate: MockProxy; let service: AgentEvalRunnerService; const dataset = mock({ @@ -115,10 +120,13 @@ describe('AgentEvalRunnerService', () => { vi.mocked(resolveEvaluationConcurrencyLimit).mockReturnValue(1); // serial by default globalConfig = { - evaluation: { agentEvalsEnabled: true }, + // `agentEvalsEnabled` is gone from here: the flag is the gate's business now. + evaluation: { agentEvalsRunTimeoutMinutes: 60 }, executions: { mode: 'regular' }, } as unknown as GlobalConfig; instanceSettings = { hostId: 'main-1' } as unknown as InstanceSettings; + moduleRegistry = mock(); + moduleRegistry.isActive.mockReturnValue(true); datasetRepository = mock(); runRepository = mock(); resultRepository = mock(); @@ -127,6 +135,7 @@ describe('AgentEvalRunnerService', () => { evalAgentExecutionService = mock(); concurrencyControl = mock(); license = mock(); + flagGate = mock(); datasetRepository.findById.mockResolvedValue(dataset); agentRepository.findByIdAndProjectId.mockResolvedValue( @@ -147,6 +156,7 @@ describe('AgentEvalRunnerService', () => { mock(), globalConfig, instanceSettings, + moduleRegistry, datasetRepository, runRepository, resultRepository, @@ -155,6 +165,7 @@ describe('AgentEvalRunnerService', () => { evalAgentExecutionService, concurrencyControl, license, + flagGate, ); }); @@ -171,11 +182,14 @@ describe('AgentEvalRunnerService', () => { }; describe('gating', () => { - it('refuses when the flag is off', async () => { - globalConfig.evaluation.agentEvalsEnabled = false; - await expect(service.startRun('ds-1', 'proj-1', user)).rejects.toThrow( - 'Agent evals are not enabled', - ); + // The flag is resolved per requesting user (PostHog owns cohort rollout), + // not per instance — so the gate is asked about `user`, not a config value. + it('refuses when the flag is off for the requesting user', async () => { + flagGate.assertEnabled.mockRejectedValue(new NotFoundError('Not found')); + + await expect(service.startRun('ds-1', 'proj-1', user)).rejects.toThrow(NotFoundError); + + expect(flagGate.assertEnabled).toHaveBeenCalledWith(user); expect(runRepository.createRun).not.toHaveBeenCalled(); }); @@ -185,6 +199,17 @@ describe('AgentEvalRunnerService', () => { expect(runRepository.createRun).not.toHaveBeenCalled(); }); + it('refuses when a module the run depends on is inactive', async () => { + // Entities are registered per module: without this guard the run reaches + // TypeORM with no `data_table` entity and dies there instead. + moduleRegistry.isActive.mockImplementation((name) => name !== 'data-table'); + + await expect(service.startRun('ds-1', 'proj-1', user)).rejects.toThrow( + 'require these modules to be active: data-table', + ); + expect(runRepository.createRun).not.toHaveBeenCalled(); + }); + it('rejects when the user cannot run agents in the project', async () => { vi.mocked(userHasScopes).mockResolvedValueOnce(false); // agent:execute check await expect(service.startRun('ds-1', 'proj-1', user)).rejects.toThrow( @@ -427,6 +452,221 @@ describe('AgentEvalRunnerService', () => { }); }); + describe('settling the run', () => { + it('tallies the run even when a cancellation write throws mid-pool', async () => { + seedFor( + [ + { id: 'row-1', question: 'Q1' }, + { id: 'row-2', question: 'Q2' }, + ], + { cancelled: 1, new: 1 }, + ); + runRepository.isCancellationRequested.mockResolvedValue(true); + // This write sits outside `runCase`'s safety net: it used to reject the + // whole pool, leaving the run errored with no counts recorded. + resultRepository.markAsCancelled + .mockRejectedValueOnce(new Error('db down')) + .mockResolvedValue(undefined as never); + + const { finished } = await service.startRun('ds-1', 'proj-1', user); + await finished; + + // The surviving case is still processed rather than abandoned mid-flight. + expect(resultRepository.markAsCancelled).toHaveBeenCalledTimes(2); + expect(runRepository.markAsCancelled).toHaveBeenCalledWith( + 'run-1', + expect.objectContaining({ total: 2, cancelled: 1, pending: 1 }), + ); + expect(runRepository.markAsError).not.toHaveBeenCalled(); + }); + + it('reports a dispatch failure with the counts rather than losing the tally', async () => { + seedFor( + [ + { id: 'row-1', question: 'Q1' }, + { id: 'row-2', question: 'Q2' }, + ], + { success: 1, new: 1 }, + ); + // The first case's cancellation read blows up; the run itself is not + // cancelled, so the second case runs to completion. + runRepository.isCancellationRequested + .mockRejectedValueOnce(new Error('db down')) + .mockResolvedValue(false); + evalAgentExecutionService.executeWithLlmMock.mockResolvedValue(successExec() as never); + + const { finished } = await service.startRun('ds-1', 'proj-1', user); + await finished; + + expect(evalAgentExecutionService.executeWithLlmMock).toHaveBeenCalledTimes(1); + expect(runRepository.markAsError).toHaveBeenCalledWith( + 'run-1', + 'case_dispatch_failed', + expect.objectContaining({ errors: ['db down'] }), + expect.objectContaining({ total: 2, success: 1 }), + ); + expect(runRepository.markAsCompleted).not.toHaveBeenCalled(); + }); + + it('keeps the tally when the final cancellation read throws', async () => { + seedFor([{ id: 'row-1', question: 'Q1' }], { success: 1 }); + evalAgentExecutionService.executeWithLlmMock.mockResolvedValue(successExec() as never); + // Both per-case checks pass, then the post-pool re-read fails. Letting it + // escape would reach `failRun` and drop the counts entirely. + runRepository.isCancellationRequested + .mockResolvedValueOnce(false) + .mockResolvedValueOnce(false) + .mockRejectedValue(new Error('db down')); + + const { finished } = await service.startRun('ds-1', 'proj-1', user); + await finished; + + expect(runRepository.markAsError).toHaveBeenCalledWith( + 'run-1', + 'case_dispatch_failed', + expect.objectContaining({ errors: ['db down'] }), + expect.objectContaining({ total: 1, success: 1 }), + ); + // Not the tally-less `run_failed` path. + expect(runRepository.markAsError).not.toHaveBeenCalledWith( + 'run-1', + 'run_failed', + expect.anything(), + ); + }); + }); + + describe('run deadline', () => { + afterEach(() => { + vi.useRealTimers(); + }); + + it('stops starting cases at the deadline and errors the run as timed out', async () => { + vi.useFakeTimers(); + globalConfig.evaluation.agentEvalsRunTimeoutMinutes = 30; + seedFor( + [ + { id: 'row-1', question: 'Q1' }, + { id: 'row-2', question: 'Q2' }, + ], + { cancelled: 2 }, + ); + // Never granted a slot: on a 1-concurrency plan this is the run that would + // otherwise sit here until the process restarts. + concurrencyControl.throttle.mockImplementation(async () => await new Promise(() => {})); + + const { finished } = await service.startRun('ds-1', 'proj-1', user); + await vi.advanceTimersByTimeAsync(30 * 60_000); + await finished; + + expect(evalAgentExecutionService.executeWithLlmMock).not.toHaveBeenCalled(); + // The queued case is evicted so it stops holding a place in the queue. + expect(concurrencyControl.remove).toHaveBeenCalledWith( + expect.objectContaining({ mode: 'evaluation' }), + ); + expect(resultRepository.markAsCancelled).toHaveBeenCalledTimes(2); + expect(runRepository.markAsError).toHaveBeenCalledWith( + 'run-1', + 'timeout', + expect.objectContaining({ + message: expect.stringContaining('2 case(s) were not started'), + }), + // counts land under `metrics`, same as every other terminal status + expect.objectContaining({ total: 2, cancelled: 2 }), + ); + // A deadline is a failure, not the user's Stop. + expect(runRepository.markAsCancelled).not.toHaveBeenCalled(); + expect(runRepository.markAsCompleted).not.toHaveBeenCalled(); + }); + + it('reports a user Stop as cancelled even when the deadline also fired', async () => { + vi.useFakeTimers(); + globalConfig.evaluation.agentEvalsRunTimeoutMinutes = 30; + seedFor([{ id: 'row-1', question: 'Q1' }], { cancelled: 1 }); + concurrencyControl.throttle.mockImplementation(async () => await new Promise(() => {})); + // Stop was requested; the case is parked, so only the post-pool re-read + // observes it. + runRepository.isCancellationRequested.mockResolvedValueOnce(false).mockResolvedValue(true); + + const { finished } = await service.startRun('ds-1', 'proj-1', user); + await vi.advanceTimersByTimeAsync(30 * 60_000); + await finished; + + expect(runRepository.markAsCancelled).toHaveBeenCalledWith('run-1', expect.anything()); + expect(runRepository.markAsError).not.toHaveBeenCalled(); + }); + + it('completes a run whose last case finished just past the deadline', async () => { + vi.useFakeTimers(); + globalConfig.evaluation.agentEvalsRunTimeoutMinutes = 30; + seedFor([{ id: 'row-1', question: 'Q1' }], { success: 1 }); + // The only case is still executing when the deadline fires, so the abort + // has nothing left to skip. Expiry alone must not fail a run that finished. + let caseStarted: (() => void) | undefined; + const started = new Promise((resolve) => { + caseStarted = resolve; + }); + let finishCase: (() => void) | undefined; + evalAgentExecutionService.executeWithLlmMock.mockImplementation(async () => { + caseStarted?.(); + await new Promise((resolve) => { + finishCase = resolve; + }); + return successExec() as never; + }); + + const { finished } = await service.startRun('ds-1', 'proj-1', user); + await started; + await vi.advanceTimersByTimeAsync(30 * 60_000); + finishCase?.(); + await finished; + + expect(runRepository.markAsCompleted).toHaveBeenCalledWith( + 'run-1', + expect.objectContaining({ total: 1, success: 1 }), + ); + expect(runRepository.markAsError).not.toHaveBeenCalled(); + }); + + it('does not fire instantly when the configured deadline overflows the timer', async () => { + vi.useFakeTimers(); + // Past the 32-bit ms ceiling, where an unclamped delay collapses to 1ms. + globalConfig.evaluation.agentEvalsRunTimeoutMinutes = 40_000; + seedFor([{ id: 'row-1', question: 'Q1' }], { success: 1 }); + evalAgentExecutionService.executeWithLlmMock.mockResolvedValue(successExec() as never); + // Park the case on the clock, so an over-eager deadline gets to abort it + // before the pool can finish on microtasks alone. + let releaseSlot: (() => void) | undefined; + concurrencyControl.throttle.mockImplementation(async () => { + await new Promise((resolve) => { + releaseSlot = resolve; + }); + }); + + const { finished } = await service.startRun('ds-1', 'proj-1', user); + await vi.advanceTimersByTimeAsync(60_000); + releaseSlot?.(); + await finished; + + expect(evalAgentExecutionService.executeWithLlmMock).toHaveBeenCalledTimes(1); + expect(runRepository.markAsCompleted).toHaveBeenCalled(); + expect(runRepository.markAsError).not.toHaveBeenCalled(); + }); + + it('runs without a deadline when the timeout is disabled', async () => { + vi.useFakeTimers(); + globalConfig.evaluation.agentEvalsRunTimeoutMinutes = 0; + seedFor([{ id: 'row-1', question: 'Q1' }], { success: 1 }); + evalAgentExecutionService.executeWithLlmMock.mockResolvedValue(successExec() as never); + + const { finished } = await service.startRun('ds-1', 'proj-1', user); + await finished; + + expect(vi.getTimerCount()).toBe(0); + expect(runRepository.markAsCompleted).toHaveBeenCalled(); + }); + }); + describe('concurrency', () => { it('runs each case through the shared evaluation queue and releases the slot', async () => { seedFor( @@ -523,17 +763,30 @@ describe('AgentEvalRunnerService', () => { describe('getRunSummary', () => { it('404s when the run is missing', async () => { - runRepository.findById.mockResolvedValue(null); - await expect(service.getRunSummary('run-x')).rejects.toThrow('not found'); + runRepository.findByIdAndAgentId.mockResolvedValue(null); + await expect(service.getRunSummary('run-x', 'agent-1')).rejects.toThrow('not found'); + }); + + it('404s a run belonging to a different agent, without counting its cases', async () => { + // The agent-scoped read is the whole permission check on this path: a + // caller authorized for one agent must not be able to poll another + // agent's run by id. + runRepository.findByIdAndAgentId.mockResolvedValue(null); + + await expect(service.getRunSummary('run-1', 'other-agent')).rejects.toThrow('not found'); + expect(runRepository.findByIdAndAgentId).toHaveBeenCalledWith('run-1', 'other-agent'); + expect(resultRepository.countByStatus).not.toHaveBeenCalled(); }); it('reports status + per-status counts from the count query', async () => { - runRepository.findById.mockResolvedValue(mock({ id: 'run-1', status: 'completed' })); + runRepository.findByIdAndAgentId.mockResolvedValue( + mock({ id: 'run-1', status: 'completed' }), + ); resultRepository.countByStatus.mockResolvedValue( counts({ success: 3, error: 1, running: 1 }), ); - const summary = await service.getRunSummary('run-1'); + const summary = await service.getRunSummary('run-1', 'agent-1'); expect(summary).toEqual({ runId: 'run-1', diff --git a/packages/cli/src/modules/agent-evals/__tests__/agent-eval-scoped-reads.integration.test.ts b/packages/cli/src/modules/agent-evals/__tests__/agent-eval-scoped-reads.integration.test.ts new file mode 100644 index 00000000000..f1c6191cd8b --- /dev/null +++ b/packages/cli/src/modules/agent-evals/__tests__/agent-eval-scoped-reads.integration.test.ts @@ -0,0 +1,161 @@ +import { createTeamProject, testDb, testModules } from '@n8n/backend-test-utils'; +import { + AgentEvalDatasetRepository, + AgentEvalResultRepository, + AgentEvalRunRepository, +} from '@n8n/db'; +import { Container } from '@n8n/di'; +import { DataSource } from '@n8n/typeorm'; + +import { Agent } from '@/modules/agents/entities/agent.entity'; + +/** + * Real DB on purpose: mocking the entity manager only proves which `where` object + * was built, not that the filter applied — and a test using the *matching* agent + * would pass even with the filter dropped. So every case here pairs the allowed + * read with the foreign-agent read that must come back empty. + */ +describe('agent-eval scoped repository reads (integration)', () => { + let datasetRepository: AgentEvalDatasetRepository; + let runRepository: AgentEvalRunRepository; + let resultRepository: AgentEvalResultRepository; + + /** Real agent row so `agent_eval_dataset.agentId`'s FK holds. */ + async function createAgent(): Promise { + const project = await createTeamProject(); + return await Container.get(DataSource) + .getRepository(Agent) + .save(Object.assign(new Agent(), { name: 'test agent', projectId: project.id })); + } + + beforeAll(async () => { + await testModules.loadModules(['data-table', 'agents']); + await testDb.init(); + datasetRepository = Container.get(AgentEvalDatasetRepository); + runRepository = Container.get(AgentEvalRunRepository); + resultRepository = Container.get(AgentEvalResultRepository); + }); + + beforeEach(async () => { + await testDb.truncate(['AgentEvalResult', 'AgentEvalRun', 'AgentEvalDataset']); + }); + + afterAll(async () => { + await testDb.terminate(); + }); + + /** Two agents, each owning a dataset; a run + case under the first one's. */ + async function seedTwoAgents() { + const [own, foreign] = await Promise.all([createAgent(), createAgent()]); + + const dataset = await datasetRepository.createDataset({ + name: 'own dataset', + agentId: own.id, + datasetSource: 'data_table', + datasetRef: { dataTableId: 'dt-own' }, + columnMapping: { input: 'question' }, + }); + const foreignDataset = await datasetRepository.createDataset({ + name: 'foreign dataset', + agentId: foreign.id, + datasetSource: 'data_table', + datasetRef: { dataTableId: 'dt-foreign' }, + }); + + const run = await runRepository.createRun({ datasetId: dataset.id }); + const [result] = await resultRepository.seedResults([ + { runId: run.id, sourceRowId: 'row-1', runIndex: 0, input: { input: 'q' } }, + ]); + + return { own, foreign, dataset, foreignDataset, run, result }; + } + + describe('dataset reads', () => { + it('resolves a dataset for its own agent and hides it from another', async () => { + const { own, foreign, dataset } = await seedTwoAgents(); + + await expect(datasetRepository.findByIdAndAgentId(dataset.id, own.id)).resolves.toMatchObject( + { id: dataset.id }, + ); + await expect( + datasetRepository.findByIdAndAgentId(dataset.id, foreign.id), + ).resolves.toBeNull(); + }); + + it('lists only the addressed agent’s datasets', async () => { + const { own, dataset } = await seedTwoAgents(); + + const listed = await datasetRepository.findByAgentId(own.id); + + expect(listed.map((d) => d.id)).toEqual([dataset.id]); + }); + }); + + // A run has no agentId of its own, so these reads filter via the dataset relation. + describe('run reads', () => { + it('resolves a run for its dataset’s agent and hides it from another', async () => { + const { own, foreign, run } = await seedTwoAgents(); + + await expect(runRepository.findByIdAndAgentId(run.id, own.id)).resolves.toMatchObject({ + id: run.id, + }); + await expect(runRepository.findByIdAndAgentId(run.id, foreign.id)).resolves.toBeNull(); + }); + + it('lists a dataset’s runs only for that dataset’s agent', async () => { + const { own, foreign, dataset, run } = await seedTwoAgents(); + + await expect( + runRepository.findByDatasetIdAndAgentId(dataset.id, own.id), + ).resolves.toMatchObject([{ id: run.id }]); + // Right dataset id, wrong agent: the pairing has to be checked, or a foreign + // caller reads another agent's run history. + await expect( + runRepository.findByDatasetIdAndAgentId(dataset.id, foreign.id), + ).resolves.toEqual([]); + }); + }); + + describe('dataset mutations', () => { + it('patches a dataset for its own agent', async () => { + const { own, dataset } = await seedTwoAgents(); + + const updated = await datasetRepository.updateDataset(dataset.id, own.id, { + name: 'renamed', + description: null, + }); + + expect(updated).toMatchObject({ id: dataset.id, name: 'renamed', description: null }); + }); + + it('refuses to patch another agent’s dataset and leaves it untouched', async () => { + const { foreign, dataset } = await seedTwoAgents(); + + await expect( + datasetRepository.updateDataset(dataset.id, foreign.id, { name: 'hijacked' }), + ).resolves.toBeNull(); + + const reread = await datasetRepository.findById(dataset.id); + expect(reread?.name).toBe('own dataset'); + }); + + it('refuses to delete another agent’s dataset and leaves the row in place', async () => { + const { foreign, dataset } = await seedTwoAgents(); + + await expect(datasetRepository.deleteDataset(dataset.id, foreign.id)).resolves.toBe(false); + await expect(datasetRepository.findById(dataset.id)).resolves.not.toBeNull(); + }); + + // The service never touches runs or results, so the cascade is what removes them. + it('deletes a dataset for its own agent, cascading to its runs and results', async () => { + const { own, dataset, run, result } = await seedTwoAgents(); + + await expect(datasetRepository.deleteDataset(dataset.id, own.id)).resolves.toBe(true); + + await expect(datasetRepository.findById(dataset.id)).resolves.toBeNull(); + await expect(runRepository.findById(run.id)).resolves.toBeNull(); + await expect(resultRepository.findByRunId(run.id)).resolves.toEqual([]); + expect(result).toBeDefined(); + }); + }); +}); diff --git a/packages/cli/src/modules/agent-evals/__tests__/agent-eval.service.test.ts b/packages/cli/src/modules/agent-evals/__tests__/agent-eval.service.test.ts new file mode 100644 index 00000000000..eba86ddb061 --- /dev/null +++ b/packages/cli/src/modules/agent-evals/__tests__/agent-eval.service.test.ts @@ -0,0 +1,425 @@ +import type { ModuleRegistry } from '@n8n/backend-common'; +import type { + AgentEvalDataset, + AgentEvalDatasetRepository, + AgentEvalResult, + AgentEvalResultRepository, + AgentEvalRun, + AgentEvalRunRepository, + User, +} from '@n8n/db'; +import { mock, type MockProxy } from 'vitest-mock-extended'; + +import { BadRequestError } from '@/errors/response-errors/bad-request.error'; +import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import type { Agent } from '@/modules/agents/entities/agent.entity'; +import type { AgentRepository } from '@/modules/agents/repositories/agent.repository'; + +import type { AgentEvalCaseGenerationService } from '../agent-eval-case-generation.service'; +import type { AgentEvalRunnerService } from '../agent-eval-runner.service'; +import { AgentEvalService } from '../agent-eval.service'; + +// Stub the cross-module specifiers the service statically imports so this unit +// test doesn't pull in the real agents / instance-ai module graph. +vi.mock('@/modules/agents/repositories/agent.repository', () => ({ + AgentRepository: class AgentRepository {}, +})); +vi.mock('../agent-eval-runner.service', () => ({ + AgentEvalRunnerService: class AgentEvalRunnerService {}, +})); +vi.mock('../agent-eval-case-generation.service', () => ({ + AgentEvalCaseGenerationService: class AgentEvalCaseGenerationService {}, +})); + +const AGENT_ID = 'agent-1'; +const PROJECT_ID = 'proj-1'; + +describe('AgentEvalService', () => { + const user = mock({ id: 'user-1' }); + + let moduleRegistry: MockProxy; + let agentRepository: MockProxy; + let datasetRepository: MockProxy; + let runRepository: MockProxy; + let resultRepository: MockProxy; + let runner: MockProxy; + let caseGenerationService: MockProxy; + let service: AgentEvalService; + + const makeDataset = (over: Partial = {}) => + mock({ + id: 'ds-1', + name: 'cases', + description: null, + agentId: AGENT_ID, + datasetSource: 'data_table', + datasetRef: { dataTableId: 'dt-1' }, + columnMapping: { input: 'question' }, + createdById: 'user-1', + createdAt: new Date('2026-01-01T00:00:00.000Z'), + updatedAt: new Date('2026-01-02T00:00:00.000Z'), + ...over, + }); + + const makeRun = (over: Partial = {}) => + mock({ + id: 'run-1', + datasetId: 'ds-1', + agentVersionId: null, + status: 'running', + runAt: new Date('2026-01-03T00:00:00.000Z'), + completedAt: null, + metrics: null, + errorCode: null, + errorDetails: null, + createdById: 'user-1', + createdAt: new Date('2026-01-03T00:00:00.000Z'), + updatedAt: new Date('2026-01-03T00:00:00.000Z'), + runningInstanceId: 'main-1', + cancelRequested: false, + ...over, + }); + + beforeEach(() => { + moduleRegistry = mock(); + moduleRegistry.isActive.mockReturnValue(true); + agentRepository = mock(); + datasetRepository = mock(); + runRepository = mock(); + resultRepository = mock(); + runner = mock(); + caseGenerationService = mock(); + + agentRepository.findByIdAndProjectId.mockResolvedValue(mock({ id: AGENT_ID })); + datasetRepository.findByIdAndAgentId.mockResolvedValue(makeDataset()); + runRepository.findByIdAndAgentId.mockResolvedValue(makeRun()); + + service = new AgentEvalService( + moduleRegistry, + agentRepository, + datasetRepository, + runRepository, + resultRepository, + runner, + caseGenerationService, + ); + }); + + // `@ProjectScope` proves the caller may act on the project in the URL. It says + // nothing about whether the addressed agent is in that project, so every entry + // point has to resolve the agent through the pair. + describe('agent scoping', () => { + const callsRequiringAnAgent: Array<[string, () => Promise]> = [ + ['listDatasets', async () => await service.listDatasets(AGENT_ID, PROJECT_ID)], + ['getDataset', async () => await service.getDataset(AGENT_ID, PROJECT_ID, 'ds-1')], + [ + 'createDataset', + async () => + await service.createDataset(user, AGENT_ID, PROJECT_ID, { + name: 'cases', + agentId: AGENT_ID, + datasetSource: 'data_table', + datasetRef: { dataTableId: 'dt-1' }, + }), + ], + [ + 'updateDataset', + async () => await service.updateDataset(AGENT_ID, PROJECT_ID, 'ds-1', { name: 'x' }), + ], + ['deleteDataset', async () => await service.deleteDataset(AGENT_ID, PROJECT_ID, 'ds-1')], + [ + 'generateDraftCases', + async () => await service.generateDraftCases(user, AGENT_ID, PROJECT_ID, {}), + ], + ['startRun', async () => await service.startRun(user, AGENT_ID, PROJECT_ID, 'ds-1', {})], + ['listRuns', async () => await service.listRuns(AGENT_ID, PROJECT_ID, 'ds-1')], + ['getRunDetail', async () => await service.getRunDetail(AGENT_ID, PROJECT_ID, 'run-1')], + ['getRunSummary', async () => await service.getRunSummary(AGENT_ID, PROJECT_ID, 'run-1')], + ['cancelRun', async () => await service.cancelRun(AGENT_ID, PROJECT_ID, 'run-1')], + ]; + + it.each(callsRequiringAnAgent)( + '%s 404s when the agent is not in the project', + async (_name, call) => { + agentRepository.findByIdAndProjectId.mockResolvedValue(null); + + await expect(call()).rejects.toThrow(NotFoundError); + }, + ); + + it.each(callsRequiringAnAgent)('%s resolves the agent against the project', async (_, call) => { + await call().catch(() => {}); + + expect(agentRepository.findByIdAndProjectId).toHaveBeenCalledWith(AGENT_ID, PROJECT_ID); + }); + + // The agent lookup below reads a module entity, so the dependency check has + // to land before it — otherwise TypeORM raises missing metadata first. + it.each(callsRequiringAnAgent)( + '%s reports the inactive module instead of querying the agent', + async (_, call) => { + moduleRegistry.isActive.mockImplementation((name) => name !== 'agents'); + + await expect(call()).rejects.toThrow('Agent evals require these modules to be active'); + expect(agentRepository.findByIdAndProjectId).not.toHaveBeenCalled(); + }, + ); + }); + + // A dataset/run id from another agent must not resolve just because the caller + // legitimately holds the scope on this one. + describe('cross-agent id isolation', () => { + it('404s a dataset belonging to another agent', async () => { + datasetRepository.findByIdAndAgentId.mockResolvedValue(null); + + await expect(service.getDataset(AGENT_ID, PROJECT_ID, 'ds-other')).rejects.toThrow( + NotFoundError, + ); + expect(datasetRepository.findByIdAndAgentId).toHaveBeenCalledWith('ds-other', AGENT_ID); + }); + + it('404s a run belonging to another agent, and reads no results for it', async () => { + runRepository.findByIdAndAgentId.mockResolvedValue(null); + + await expect(service.getRunDetail(AGENT_ID, PROJECT_ID, 'run-other')).rejects.toThrow( + NotFoundError, + ); + expect(resultRepository.findByRunId).not.toHaveBeenCalled(); + }); + + it('refuses to start a run on another agent’s dataset, without touching the runner', async () => { + datasetRepository.findByIdAndAgentId.mockResolvedValue(null); + + await expect(service.startRun(user, AGENT_ID, PROJECT_ID, 'ds-other', {})).rejects.toThrow( + NotFoundError, + ); + expect(runner.startRun).not.toHaveBeenCalled(); + }); + + it('passes the agent through to the summary read so it is scoped there too', async () => { + runner.getRunSummary.mockResolvedValue({ + runId: 'run-1', + status: 'running', + counts: { total: 1, success: 0, error: 0, cancelled: 0, pending: 1 }, + }); + + await service.getRunSummary(AGENT_ID, PROJECT_ID, 'run-1'); + + expect(runner.getRunSummary).toHaveBeenCalledWith('run-1', AGENT_ID); + }); + }); + + describe('createDataset', () => { + it('rejects a body whose agentId contradicts the URL', async () => { + await expect( + service.createDataset(user, AGENT_ID, PROJECT_ID, { + name: 'cases', + agentId: 'agent-2', + datasetSource: 'data_table', + datasetRef: { dataTableId: 'dt-1' }, + }), + ).rejects.toThrow(BadRequestError); + + expect(datasetRepository.createDataset).not.toHaveBeenCalled(); + }); + + it('persists with the URL agent and the creating user, defaulting optionals', async () => { + datasetRepository.createDataset.mockResolvedValue(makeDataset()); + + await service.createDataset(user, AGENT_ID, PROJECT_ID, { + name: 'cases', + agentId: AGENT_ID, + datasetSource: 'data_table', + datasetRef: { dataTableId: 'dt-1' }, + }); + + expect(datasetRepository.createDataset).toHaveBeenCalledWith({ + name: 'cases', + description: null, + agentId: AGENT_ID, + datasetSource: 'data_table', + datasetRef: { dataTableId: 'dt-1' }, + columnMapping: null, + createdById: 'user-1', + }); + }); + }); + + describe('deleteDataset', () => { + it('404s when nothing was removed', async () => { + datasetRepository.deleteDataset.mockResolvedValue(false); + + await expect(service.deleteDataset(AGENT_ID, PROJECT_ID, 'ds-1')).rejects.toThrow( + NotFoundError, + ); + }); + + it('resolves when the dataset was removed', async () => { + datasetRepository.deleteDataset.mockResolvedValue(true); + + await expect(service.deleteDataset(AGENT_ID, PROJECT_ID, 'ds-1')).resolves.toBeUndefined(); + }); + }); + + describe('startRun', () => { + beforeEach(() => { + runner.startRun.mockResolvedValue({ runId: 'run-1', finished: Promise.resolve() }); + runRepository.findById.mockResolvedValue(makeRun()); + }); + + it('returns the seeded run without waiting for the cases to finish', async () => { + let settled = false; + runner.startRun.mockResolvedValue({ + runId: 'run-1', + // A promise that never settles stands in for a long-running batch: if + // the service awaited it, this test would time out. + finished: new Promise(() => {}).finally(() => { + settled = true; + }), + }); + + const run = await service.startRun(user, AGENT_ID, PROJECT_ID, 'ds-1', {}); + + expect(run.id).toBe('run-1'); + expect(settled).toBe(false); + }); + + it('refuses a pinned agent version while the runner can only run the live agent', async () => { + await expect( + service.startRun(user, AGENT_ID, PROJECT_ID, 'ds-1', { agentVersionId: 'v-1' }), + ).rejects.toThrow(BadRequestError); + + expect(runner.startRun).not.toHaveBeenCalled(); + }); + }); + + describe('cancelRun', () => { + it.each(['completed', 'error', 'cancelled'] as const)( + 'refuses to cancel a run that already finished as %s', + async (status) => { + runRepository.findByIdAndAgentId.mockResolvedValue(makeRun({ status })); + + await expect(service.cancelRun(AGENT_ID, PROJECT_ID, 'run-1')).rejects.toThrow( + BadRequestError, + ); + expect(runRepository.requestCancellation).not.toHaveBeenCalled(); + }, + ); + + it.each(['new', 'running'] as const)('requests cancellation of a %s run', async (status) => { + runRepository.findByIdAndAgentId.mockResolvedValue(makeRun({ status })); + runRepository.findById.mockResolvedValue(makeRun({ status })); + + await service.cancelRun(AGENT_ID, PROJECT_ID, 'run-1'); + + expect(runRepository.requestCancellation).toHaveBeenCalledWith('run-1'); + }); + + it('falls back to the pre-cancel run when the re-read comes up empty', async () => { + runRepository.findById.mockResolvedValue(null); + + const run = await service.cancelRun(AGENT_ID, PROJECT_ID, 'run-1'); + + expect(run.id).toBe('run-1'); + }); + }); + + describe('response mapping', () => { + it('serializes dates as ISO strings and keeps run coordination columns off the wire', async () => { + resultRepository.findByRunId.mockResolvedValue([]); + + const detail = await service.getRunDetail(AGENT_ID, PROJECT_ID, 'run-1'); + + expect(detail.runAt).toBe('2026-01-03T00:00:00.000Z'); + expect(detail.createdAt).toBe('2026-01-03T00:00:00.000Z'); + expect(detail).not.toHaveProperty('runningInstanceId'); + expect(detail).not.toHaveProperty('cancelRequested'); + }); + + it('reassembles the dataset source pointer as a narrowable pair', async () => { + const record = await service.getDataset(AGENT_ID, PROJECT_ID, 'ds-1'); + + expect(record).toMatchObject({ + datasetSource: 'data_table', + datasetRef: { dataTableId: 'dt-1' }, + createdAt: '2026-01-01T00:00:00.000Z', + updatedAt: '2026-01-02T00:00:00.000Z', + }); + }); + + it('maps a google-sheets dataset onto its own source arm', async () => { + datasetRepository.findByIdAndAgentId.mockResolvedValue( + makeDataset({ + datasetSource: 'google_sheets', + datasetRef: { credentialId: 'c-1', spreadsheetId: 's-1', sheetName: 'Sheet1' }, + }), + ); + + const record = await service.getDataset(AGENT_ID, PROJECT_ID, 'ds-1'); + + expect(record).toMatchObject({ + datasetSource: 'google_sheets', + datasetRef: { credentialId: 'c-1', spreadsheetId: 's-1', sheetName: 'Sheet1' }, + }); + }); + + // The source column is authoritative, so a ref that doesn't match it means a + // corrupt row. Failing loudly beats reporting a wrong-but-well-typed + // `datasetSource` the client would then narrow on. + it('refuses to map a dataset whose source and ref disagree', async () => { + datasetRepository.findByIdAndAgentId.mockResolvedValue( + makeDataset({ + datasetSource: 'google_sheets', + datasetRef: { dataTableId: 'dt-1' }, + }), + ); + + await expect(service.getDataset(AGENT_ID, PROJECT_ID, 'ds-1')).rejects.toThrow( + /does not match that shape/, + ); + }); + + it('includes every per-case result in a run detail', async () => { + resultRepository.findByRunId.mockResolvedValue([ + mock({ + id: 'res-1', + runId: 'run-1', + status: 'success', + runAt: new Date('2026-01-03T00:00:01.000Z'), + completedAt: new Date('2026-01-03T00:00:02.000Z'), + createdAt: new Date('2026-01-03T00:00:00.000Z'), + updatedAt: new Date('2026-01-03T00:00:02.000Z'), + }), + ]); + + const detail = await service.getRunDetail(AGENT_ID, PROJECT_ID, 'run-1'); + + expect(detail.results).toHaveLength(1); + expect(detail.results[0]).toMatchObject({ + id: 'res-1', + status: 'success', + runAt: '2026-01-03T00:00:01.000Z', + completedAt: '2026-01-03T00:00:02.000Z', + }); + }); + }); + + describe('generateDraftCases', () => { + it('delegates with the project resolved from the URL', async () => { + caseGenerationService.generateDraftCases.mockResolvedValue({ + datasetId: 'ds-1', + dataTableId: 'dt-1', + cases: [], + }); + + await service.generateDraftCases(user, AGENT_ID, PROJECT_ID, { count: 3 }); + + expect(caseGenerationService.generateDraftCases).toHaveBeenCalledWith( + user, + PROJECT_ID, + AGENT_ID, + { count: 3 }, + ); + }); + }); +}); diff --git a/packages/cli/src/modules/agent-evals/__tests__/agent-evals.controller.test.ts b/packages/cli/src/modules/agent-evals/__tests__/agent-evals.controller.test.ts new file mode 100644 index 00000000000..278bbb182a7 --- /dev/null +++ b/packages/cli/src/modules/agent-evals/__tests__/agent-evals.controller.test.ts @@ -0,0 +1,216 @@ +import type { AuthenticatedRequest, User } from '@n8n/db'; +import { ControllerRegistryMetadata } from '@n8n/decorators'; +import { Container } from '@n8n/di'; +import { mock, type MockProxy } from 'vitest-mock-extended'; + +import { BadRequestError } from '@/errors/response-errors/bad-request.error'; +import { NotFoundError } from '@/errors/response-errors/not-found.error'; + +import type { AgentEvalService } from '../agent-eval.service'; +import type { AgentEvalsFlagGate } from '../agent-evals-flag-gate'; +import { AgentEvalsController } from '../agent-evals.controller'; + +vi.mock('../agent-eval.service', () => ({ AgentEvalService: class AgentEvalService {} })); +vi.mock('../agent-evals-flag-gate', () => ({ AgentEvalsFlagGate: class AgentEvalsFlagGate {} })); + +const PROJECT_ID = 'proj-1'; +const AGENT_ID = 'agent-1'; + +describe('AgentEvalsController', () => { + const user = mock({ id: 'user-1' }); + + let service: MockProxy; + let flagGate: MockProxy; + let controller: AgentEvalsController; + + function makeReq

>( + params: P, + body: unknown = {}, + ): AuthenticatedRequest

{ + return { user, params, body } as unknown as AuthenticatedRequest

; + } + + const agentReq = () => makeReq({ projectId: PROJECT_ID, agentId: AGENT_ID }); + const datasetReq = () => makeReq({ projectId: PROJECT_ID, agentId: AGENT_ID, datasetId: 'ds-1' }); + const runReq = () => makeReq({ projectId: PROJECT_ID, agentId: AGENT_ID, runId: 'run-1' }); + + beforeEach(() => { + service = mock(); + flagGate = mock(); + flagGate.assertEnabled.mockResolvedValue(undefined); + controller = new AgentEvalsController(service, flagGate); + }); + + /** + * Regression guard: a route added without an access scope fails here. Every + * agent-eval route is authenticated and project-scoped — there is no public + * surface, so the assertion is unconditional. + */ + describe('route access scopes', () => { + const metadata = Container.get(ControllerRegistryMetadata).getControllerMetadata( + AgentEvalsController as never, + ); + const routeCases = Array.from(metadata.routes.entries()).map(([handlerName, route]) => ({ + handlerName, + route, + })); + + it('registers every handler', () => { + expect(routeCases).not.toHaveLength(0); + }); + + it.each(routeCases)('$handlerName is gated by a project-scoped agent:* check', ({ route }) => { + expect(route.accessScope).toBeDefined(); + expect(route.accessScope?.globalOnly).toBe(false); + expect(route.accessScope?.scope.startsWith('agent:')).toBe(true); + }); + + it.each([ + // Reads stay on agent:read; anything that writes eval config — including + // generation, which spends the builder's model credits — needs + // agent:update; starting/cancelling a run is agent:execute. + ['listDatasets', 'agent:read'], + ['getDataset', 'agent:read'], + ['listRuns', 'agent:read'], + ['getRun', 'agent:read'], + ['getRunSummary', 'agent:read'], + ['createDataset', 'agent:update'], + ['updateDataset', 'agent:update'], + ['deleteDataset', 'agent:update'], + ['generateDraftCases', 'agent:update'], + ['startRun', 'agent:execute'], + ['cancelRun', 'agent:execute'], + ])('%s uses %s', (handlerName, scope) => { + expect(metadata.routes.get(handlerName)?.accessScope?.scope).toBe(scope); + }); + }); + + // Flag-off must look like an unknown endpoint on every route, and must not + // reach the service. + describe('flag gating', () => { + const calls: Array<[string, () => Promise]> = [ + ['listDatasets', async () => await controller.listDatasets(agentReq())], + ['getDataset', async () => await controller.getDataset(datasetReq())], + [ + 'createDataset', + async () => + await controller.createDataset( + makeReq( + { projectId: PROJECT_ID, agentId: AGENT_ID }, + { + name: 'cases', + agentId: AGENT_ID, + datasetSource: 'data_table', + datasetRef: { dataTableId: 'dt-1' }, + }, + ), + ), + ], + [ + 'updateDataset', + async () => await controller.updateDataset(datasetReq(), undefined, { name: 'x' }), + ], + ['deleteDataset', async () => await controller.deleteDataset(datasetReq())], + [ + 'generateDraftCases', + async () => await controller.generateDraftCases(agentReq(), undefined, {}), + ], + ['startRun', async () => await controller.startRun(datasetReq(), undefined, {})], + ['listRuns', async () => await controller.listRuns(datasetReq())], + ['getRun', async () => await controller.getRun(runReq())], + ['getRunSummary', async () => await controller.getRunSummary(runReq())], + ['cancelRun', async () => await controller.cancelRun(runReq())], + ]; + + it.each(calls)('%s 404s when the flag is off for the user', async (_name, call) => { + flagGate.assertEnabled.mockRejectedValue(new NotFoundError('Not found')); + + await expect(call()).rejects.toThrow(NotFoundError); + }); + + it.each(calls)('%s asks the gate about the requesting user', async (_name, call) => { + await call().catch(() => {}); + + expect(flagGate.assertEnabled).toHaveBeenCalledWith(user); + }); + }); + + describe('delegation', () => { + it('lists datasets for the path agent', async () => { + await controller.listDatasets(agentReq()); + + expect(service.listDatasets).toHaveBeenCalledWith(AGENT_ID, PROJECT_ID); + }); + + it('passes a valid create body through', async () => { + const body = { + name: 'cases', + agentId: AGENT_ID, + datasetSource: 'data_table', + datasetRef: { dataTableId: 'dt-1' }, + }; + + await controller.createDataset(makeReq({ projectId: PROJECT_ID, agentId: AGENT_ID }, body)); + + expect(service.createDataset).toHaveBeenCalledWith( + user, + AGENT_ID, + PROJECT_ID, + expect.objectContaining(body), + ); + }); + + // This body carries the `DatasetRef` union, so it can't be bound via + // `@Body` and is parsed in the handler — which means the handler owns + // turning invalid input into a 400 rather than a 500. + it('rejects an invalid create body as a bad request', async () => { + await expect( + controller.createDataset( + makeReq({ projectId: PROJECT_ID, agentId: AGENT_ID }, { name: '' }), + ), + ).rejects.toThrow(BadRequestError); + + expect(service.createDataset).not.toHaveBeenCalled(); + }); + + it('rejects a create body with an unknown dataset source', async () => { + await expect( + controller.createDataset( + makeReq( + { projectId: PROJECT_ID, agentId: AGENT_ID }, + { + name: 'cases', + agentId: AGENT_ID, + datasetSource: 'sqlite', + datasetRef: { dataTableId: 'dt-1' }, + }, + ), + ), + ).rejects.toThrow(BadRequestError); + }); + + it('starts a run for the path dataset', async () => { + await controller.startRun(datasetReq(), undefined, {}); + + expect(service.startRun).toHaveBeenCalledWith(user, AGENT_ID, PROJECT_ID, 'ds-1', {}); + }); + + it('reports a delete as a success envelope', async () => { + await expect(controller.deleteDataset(datasetReq())).resolves.toEqual({ success: true }); + + expect(service.deleteDataset).toHaveBeenCalledWith(AGENT_ID, PROJECT_ID, 'ds-1'); + }); + + it('reads a run summary scoped to the path agent', async () => { + await controller.getRunSummary(runReq()); + + expect(service.getRunSummary).toHaveBeenCalledWith(AGENT_ID, PROJECT_ID, 'run-1'); + }); + + it('cancels a run scoped to the path agent', async () => { + await controller.cancelRun(runReq()); + + expect(service.cancelRun).toHaveBeenCalledWith(AGENT_ID, PROJECT_ID, 'run-1'); + }); + }); +}); diff --git a/packages/cli/src/modules/agent-evals/agent-eval-case-generation.service.ts b/packages/cli/src/modules/agent-evals/agent-eval-case-generation.service.ts index 8f0741cd0f3..79795f1db5a 100644 --- a/packages/cli/src/modules/agent-evals/agent-eval-case-generation.service.ts +++ b/packages/cli/src/modules/agent-evals/agent-eval-case-generation.service.ts @@ -1,7 +1,6 @@ import type { Agent } from '@n8n/agents'; import { getProviderPrefix } from '@n8n/ai-utilities/agent-config'; import { - AGENT_EVALS_FLAG, MANAGED_CREDENTIAL_TOKEN, type AgentEvalDraftCase, type AgentJsonConfig, @@ -16,10 +15,9 @@ import { OperationalError, UserError } from 'n8n-workflow'; import { CredentialsService } from '@/credentials/credentials.service'; import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; import { SourceControlPreferencesService } from '@/modules/source-control.ee/source-control-preferences.service.ee'; -import { PostHogClient } from '@/posthog'; +import { AgentEvalsFlagGate } from './agent-evals-flag-gate'; import { AgentConfigService } from '../agents/agent-config.service'; import { buildAgentSummary, @@ -83,7 +81,7 @@ export class AgentEvalCaseGenerationService { private readonly credentialsService: CredentialsService, private readonly dataTableService: DataTableService, private readonly datasetRepository: AgentEvalDatasetRepository, - private readonly postHogClient: PostHogClient, + private readonly flagGate: AgentEvalsFlagGate, private readonly sourceControlPreferencesService: SourceControlPreferencesService, ) {} @@ -99,7 +97,7 @@ export class AgentEvalCaseGenerationService { agentId: string, options: GenerateDraftCasesOptions = {}, ): Promise { - await this.assertFeatureEnabled(user); + await this.flagGate.assertEnabled(user); this.assertInstanceWriteAccess(); const config = await this.agentConfigService.getConfig(agentId, projectId); @@ -144,18 +142,6 @@ export class AgentEvalCaseGenerationService { // ---- internals ---- - /** - * Gate on the agent-evals rollout flag (honors the env override). Throws - * NotFoundError rather than Forbidden so a flag-off instance looks like an - * unknown feature and leaks no flag state (matching the eval-collections gate). - */ - private async assertFeatureEnabled(user: User): Promise { - const flags = await this.postHogClient.getFeatureFlags(user); - if (flags?.[AGENT_EVALS_FLAG] !== true) { - throw new NotFoundError('Not found'); - } - } - /** * Block writes on a source-control read-only (protected) instance. This * service writes a Data Table directly (bypassing the data-table controller), diff --git a/packages/cli/src/modules/agent-evals/agent-eval-rating.service.ts b/packages/cli/src/modules/agent-evals/agent-eval-rating.service.ts new file mode 100644 index 00000000000..e7e8d35cb09 --- /dev/null +++ b/packages/cli/src/modules/agent-evals/agent-eval-rating.service.ts @@ -0,0 +1,182 @@ +import { AGENT_EVALS_FLAG, type CreateAgentEvalRatingPayload } from '@n8n/api-types'; +import { Logger } from '@n8n/backend-common'; +import type { AgentEvalRating, AgentEvalResult, User } from '@n8n/db'; +import { + AgentEvalDatasetRepository, + AgentEvalRatingRepository, + AgentEvalResultRepository, + AgentEvalRunRepository, +} from '@n8n/db'; +import { Service } from '@n8n/di'; +import type { Scope } from '@n8n/permissions'; + +import { BadRequestError } from '@/errors/response-errors/bad-request.error'; +import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; +import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { AgentRepository } from '@/modules/agents/repositories/agent.repository'; +import { userHasScopes } from '@/permissions.ee/check-access'; +import { PostHogClient } from '@/posthog'; + +// The body arrives straight from a request, so bound it before it hits the column. +const MAX_COMMENT_CHARS = 2_000; +const MAX_CORRECTION_TEXT_CHARS = 20_000; +const MAX_CORRECTION_CHARS = 32_000; + +/** + * A human's 👍/👎 on an eval result, with an optional comment and correction. + * Corrections stay on the rating row (never the dataset); ratings are append-only. + */ +@Service() +export class AgentEvalRatingService { + constructor( + private readonly logger: Logger, + private readonly ratingRepository: AgentEvalRatingRepository, + private readonly resultRepository: AgentEvalResultRepository, + private readonly runRepository: AgentEvalRunRepository, + private readonly datasetRepository: AgentEvalDatasetRepository, + private readonly agentRepository: AgentRepository, + private readonly postHogClient: PostHogClient, + ) {} + + /** Authorization is enforced here too, so no caller can skip it. */ + async rateResult( + user: User, + projectId: string, + resultId: string, + payload: CreateAgentEvalRatingPayload, + ): Promise { + await this.assertFeatureEnabled(user); + // Execute, not update: a project viewer holds execute and can run the eval, and + // the reviewer of a result is often not its builder. + await this.assertProjectScopes(user, projectId, ['agent:execute']); + assertPayloadWithinBounds(payload); + + const result = await this.resolveResultInProject(projectId, resultId); + // A pending case has no output, so the vote would judge nothing. Errored and + // cancelled stay rateable — "it failed" is a judgment. + if (result.status === 'new' || result.status === 'running') { + throw new BadRequestError('This case has not finished running yet.'); + } + + const rating = await this.ratingRepository.createRating({ + resultId: result.id, + vote: payload.vote, + comment: payload.comment ?? null, + correction: payload.correction ?? null, + ratedById: user.id, + }); + + this.logger.debug('Recorded agent eval rating', { + resultId: result.id, + vote: rating.vote, + hasCorrection: rating.correction !== null, + }); + + return rating; + } + + /** The per-case history, newest first. */ + async listRatingsForResult( + user: User, + projectId: string, + resultId: string, + ): Promise { + await this.assertFeatureEnabled(user); + await this.assertProjectScopes(user, projectId, ['agent:read']); + + const result = await this.resolveResultInProject(projectId, resultId); + + return await this.ratingRepository.findByResultId(result.id); + } + + /** What a reopened run renders, and the corrections calibration reads. */ + async listLatestRatingsForRun( + user: User, + projectId: string, + runId: string, + ): Promise { + await this.assertFeatureEnabled(user); + await this.assertProjectScopes(user, projectId, ['agent:read']); + + await this.resolveRunInProject(projectId, runId); + + return await this.ratingRepository.findLatestByRunId(runId); + } + + // ---- internals ---- + + /** + * Not-found rather than forbidden, so a flag-off instance leaks no flag state + * (matching the case-generation gate). + */ + private async assertFeatureEnabled(user: User): Promise { + const flags = await this.postHogClient.getFeatureFlags(user); + if (flags?.[AGENT_EVALS_FLAG] !== true) { + throw new NotFoundError('Not found'); + } + } + + /** Runs before any lookup, so unauthorized callers can't probe for ids. */ + private async assertProjectScopes(user: User, projectId: string, scopes: Scope[]): Promise { + if (!(await userHasScopes(user, scopes, false, { projectId }))) { + throw new ForbiddenError('You do not have permission to review agent evals in this project.'); + } + } + + private async resolveResultInProject( + projectId: string, + resultId: string, + ): Promise { + const result = await this.resultRepository.findById(resultId); + if (!result) throw new NotFoundError(`Agent eval result ${resultId} not found.`); + + await this.resolveRunInProject(projectId, result.runId); + + return result; + } + + /** + * Walks run → dataset → agent to confirm project ownership. A run elsewhere reads + * as missing, not forbidden, so its existence doesn't leak. + */ + private async resolveRunInProject(projectId: string, runId: string): Promise { + const notFound = () => new NotFoundError(`Agent eval run ${runId} not found.`); + + const run = await this.runRepository.findById(runId); + if (!run) throw notFound(); + + const dataset = await this.datasetRepository.findById(run.datasetId); + if (!dataset) throw notFound(); + + // Existence only: `findByIdAndProjectId` would load `activeVersion` per rating. + const inProject = await this.agentRepository.existsByIdAndProjectId(dataset.agentId, projectId); + if (!inProject) throw notFound(); + } +} + +/** Rejects rather than truncating: a silently clipped gold answer is worse. */ +function assertPayloadWithinBounds(payload: CreateAgentEvalRatingPayload): void { + if (payload.comment !== undefined && payload.comment.length > MAX_COMMENT_CHARS) { + throw new BadRequestError(`A rating comment cannot exceed ${MAX_COMMENT_CHARS} characters.`); + } + + const { correction } = payload; + if (correction === undefined) return; + + // Required, not optional: a correction without a readable `finalText` records an + // edit that calibration can never compare against the agent's answer. + const finalText = correction.finalText; + if (typeof finalText !== 'string' || finalText.trim().length === 0) { + throw new BadRequestError("A correction must carry the edited answer in 'finalText'."); + } + if (finalText.length > MAX_CORRECTION_TEXT_CHARS) { + throw new BadRequestError( + `A corrected answer cannot exceed ${MAX_CORRECTION_TEXT_CHARS} characters.`, + ); + } + if (JSON.stringify(correction).length > MAX_CORRECTION_CHARS) { + throw new BadRequestError( + `A correction cannot exceed ${MAX_CORRECTION_CHARS} characters in total.`, + ); + } +} diff --git a/packages/cli/src/modules/agent-evals/agent-eval-record-mappers.ts b/packages/cli/src/modules/agent-evals/agent-eval-record-mappers.ts new file mode 100644 index 00000000000..48db48a79a3 --- /dev/null +++ b/packages/cli/src/modules/agent-evals/agent-eval-record-mappers.ts @@ -0,0 +1,97 @@ +import type { + AgentEvalDatasetRecord, + AgentEvalResultRecord, + AgentEvalRunRecord, + DataTableDatasetRef, + DatasetRef, + GoogleSheetsDatasetRef, +} from '@n8n/api-types'; +import type { AgentEvalDataset, AgentEvalResult, AgentEvalRun } from '@n8n/db'; +import { UnexpectedError } from 'n8n-workflow'; + +/** + * Entity → wire-record mappers. Mapped field-by-field so dates serialize as ISO + * strings and a new entity column can't leak onto the API by accident — notably + * the run's `runningInstanceId` (an instance host id) and `cancelRequested`. + */ + +const toIso = (date: Date | null): string | null => date?.toISOString() ?? null; + +// Used only to re-narrow a ref, never to decide which source a dataset is. +const isDataTableRef = (ref: DatasetRef['datasetRef']): ref is DataTableDatasetRef => + 'dataTableId' in ref; + +const isGoogleSheetsRef = (ref: DatasetRef['datasetRef']): ref is GoogleSheetsDatasetRef => + 'spreadsheetId' in ref; + +/** + * Rebuild the source/ref union, which the entity stores as two uncorrelated + * columns. Switches on `datasetSource` — inferring it from the ref's shape would + * silently relabel a future third source as one of today's two. A disagreeing + * pair is unreachable via `createDataset`, so it means a corrupt row. + */ +function toDatasetRefPair(dataset: AgentEvalDataset): DatasetRef { + const { datasetSource, datasetRef } = dataset; + + if (datasetSource === 'data_table' && isDataTableRef(datasetRef)) { + return { datasetSource, datasetRef }; + } + if (datasetSource === 'google_sheets' && isGoogleSheetsRef(datasetRef)) { + return { datasetSource, datasetRef }; + } + + throw new UnexpectedError( + `Agent eval dataset ${dataset.id} has a '${datasetSource}' source whose ref does not match that shape.`, + ); +} + +export function toDatasetRecord(dataset: AgentEvalDataset): AgentEvalDatasetRecord { + return { + id: dataset.id, + name: dataset.name, + description: dataset.description, + agentId: dataset.agentId, + columnMapping: dataset.columnMapping, + createdById: dataset.createdById, + createdAt: dataset.createdAt.toISOString(), + updatedAt: dataset.updatedAt.toISOString(), + ...toDatasetRefPair(dataset), + }; +} + +export function toRunRecord(run: AgentEvalRun): AgentEvalRunRecord { + return { + id: run.id, + datasetId: run.datasetId, + agentVersionId: run.agentVersionId, + status: run.status, + runAt: toIso(run.runAt), + completedAt: toIso(run.completedAt), + metrics: run.metrics, + errorCode: run.errorCode, + errorDetails: run.errorDetails, + createdById: run.createdById, + createdAt: run.createdAt.toISOString(), + updatedAt: run.updatedAt.toISOString(), + }; +} + +export function toResultRecord(result: AgentEvalResult): AgentEvalResultRecord { + return { + id: result.id, + runId: result.runId, + sourceRowId: result.sourceRowId, + runIndex: result.runIndex, + status: result.status, + input: result.input, + output: result.output, + toolCalls: result.toolCalls, + metrics: result.metrics, + runAt: toIso(result.runAt), + completedAt: toIso(result.completedAt), + errorCode: result.errorCode, + errorDetails: result.errorDetails, + createdAt: result.createdAt.toISOString(), + updatedAt: result.updatedAt.toISOString(), + }; +} diff --git a/packages/cli/src/modules/agent-evals/agent-eval-runner.service.ts b/packages/cli/src/modules/agent-evals/agent-eval-runner.service.ts index 9f4c4fcb311..4c4b745a8b9 100644 --- a/packages/cli/src/modules/agent-evals/agent-eval-runner.service.ts +++ b/packages/cli/src/modules/agent-evals/agent-eval-runner.service.ts @@ -1,6 +1,7 @@ -import { Logger } from '@n8n/backend-common'; +import type { AgentEvalRunSummary } from '@n8n/api-types'; +import { Logger, ModuleRegistry } from '@n8n/backend-common'; import { GlobalConfig } from '@n8n/config'; -import type { AgentEvalDataset, AgentEvalResult, AgentEvalRunStatus, User } from '@n8n/db'; +import type { AgentEvalDataset, AgentEvalResult, User } from '@n8n/db'; import { AgentEvalDatasetRepository, AgentEvalResultRepository, @@ -29,6 +30,9 @@ import { DataTableService } from '@/modules/data-table/data-table.service'; import { EvalAgentExecutionService } from '@/modules/instance-ai/eval/agent-execution.service'; import { userHasScopes } from '@/permissions.ee/check-access'; +import { AgentEvalsFlagGate } from './agent-evals-flag-gate'; +import { assertRequiredModulesActive } from './agent-evals-required-modules'; + const ROW_PAGE_SIZE = 100; // Per-run in-flight cap layered on the shared evaluation queue: keeps one run // from flooding the queue (and bounds fan-out when the queue is unlimited). @@ -36,6 +40,9 @@ const MAX_PER_RUN_CONCURRENCY = 10; // Each case is a real agent execution, so a run is capped rather than letting a // large table launch thousands of model calls. Raise deliberately if needed. const MAX_CASES = 500; +// `setTimeout` holds its delay in a 32-bit int and silently coerces anything +// larger to 1ms, so a deadline past ~24.8 days must be clamped, not passed on. +const MAX_TIMER_DELAY_MS = 2_147_483_647; /** A dataset row resolved into a runnable case via the dataset's column mapping. */ interface ResolvedCase { @@ -52,12 +59,6 @@ interface CaseUsage { outputTokens: number; } -export interface AgentEvalRunSummary { - runId: string; - status: AgentEvalRunStatus; - counts: { total: number; success: number; error: number; cancelled: number; pending: number }; -} - /** * Executes an agent-eval dataset against a real agent and persists per-case * results, then aggregates run-level status. @@ -69,6 +70,8 @@ export interface AgentEvalRunSummary { * queue (the same one the workflow eval uses), so a single run can't flood the * queue and concurrent runs can't collectively exceed the plan limit; * cross-main *cancellation* is honored via the run's `cancelRequested` flag. + * A run-level deadline bounds the wall clock, since a 1-slot plan otherwise + * lets a large dataset run until the process restarts. * Behind the `101_agent_evals` flag. */ @Service() @@ -77,6 +80,7 @@ export class AgentEvalRunnerService { private readonly logger: Logger, private readonly globalConfig: GlobalConfig, private readonly instanceSettings: InstanceSettings, + private readonly moduleRegistry: ModuleRegistry, private readonly datasetRepository: AgentEvalDatasetRepository, private readonly runRepository: AgentEvalRunRepository, private readonly resultRepository: AgentEvalResultRepository, @@ -85,6 +89,7 @@ export class AgentEvalRunnerService { private readonly evalAgentExecutionService: EvalAgentExecutionService, private readonly concurrencyControl: ConcurrencyControlService, private readonly license: License, + private readonly flagGate: AgentEvalsFlagGate, ) {} /** @@ -98,19 +103,17 @@ export class AgentEvalRunnerService { user: User, options: { timeoutMs?: number } = {}, ): Promise<{ runId: string; finished: Promise }> { - // Behind 101_agent_evals. `agentEvalsEnabled` is a force-enable-only operator - // override, so this treats it as the sole gate for now. When the REST layer - // lands it must instead resolve the flag per-user via PostHog (which is the - // source of truth for cohort rollout) — a rolled-out user shouldn't need the - // env var. Until then this stays hard-gated on the override. - if (!this.globalConfig.evaluation.agentEvalsEnabled) { - throw new BadRequestError('Agent evals are not enabled on this instance.'); - } + // Per user, since PostHog owns cohort rollout. The REST layer gates too — this + // is the backstop for any other caller. + await this.flagGate.assertEnabled(user); if (this.globalConfig.executions.mode === 'queue') { throw new BadRequestError('Agent eval runs are not supported in queue mode.'); } + // Backstop for direct callers; the REST path asserts before its own lookups. + assertRequiredModulesActive(this.moduleRegistry); + // Authorize up front. `executeWithLlmMock` also checks `agent:execute`, but // it returns an error result rather than throwing — without this a caller // lacking permission would get a created run with every case marked failed @@ -187,9 +190,10 @@ export class AgentEvalRunnerService { return { runId: run.id, finished }; } - /** Run + per-case status counts, for polling a run's progress. */ - async getRunSummary(runId: string): Promise { - const run = await this.runRepository.findById(runId); + // Scoped to the agent under test, so a bare run id can't read another agent's + // progress even if the caller skipped its own check. + async getRunSummary(runId: string, agentId: string): Promise { + const run = await this.runRepository.findByIdAndAgentId(runId, agentId); if (!run) throw new NotFoundError(`Agent eval run ${runId} not found.`); // Count in the DB — this is polled by the UI, so never load the full // per-case rows (input/output/toolCalls JSON) just to tally statuses. @@ -233,48 +237,59 @@ export class AgentEvalRunnerService { ? Math.min(resolvedLimit, MAX_PER_RUN_CONCURRENCY) : MAX_PER_RUN_CONCURRENCY, ); - // Cooperative cancellation: whichever case first observes the flag aborts, - // which evicts every case still waiting for a queue slot. + // Whichever case first observes a cancel aborts, evicting every case still + // queued. The deadline below trips the same signal. const abort = new AbortController(); const totalUsage: CaseUsage = { inputTokens: 0, outputTokens: 0 }; - const cancelCase = async (resultRow: AgentEvalResult) => { + // Set only when a case actually observed the cancellation flag, so the + // settle step can tell a user cancel apart from a deadline — both abort. + let cancelObserved = false; + const shouldStopCase = async (): Promise => { + if (abort.signal.aborted) return true; + // One DB read per case at most: once the first observer aborts, the + // check above short-circuits for the rest. + if (!(await this.runRepository.isCancellationRequested(runId))) return false; + cancelObserved = true; abort.abort(); + return true; + }; + + // Cases the run never started: lets the settle step tell a deadline that + // cost work from one that merely elapsed as the last case finished. + let stoppedCases = 0; + const stopCase = async (resultRow: AgentEvalResult) => { + stoppedCases++; await this.resultRepository.markAsCancelled(resultRow.id); }; - await Promise.all( + const deadline = this.startRunDeadline(abort); + + // The stop reads/writes around `runCase` sit outside its safety net, so + // collect their throws instead of letting one reject the whole pool. + const settlements = await Promise.allSettled( cases.map( async (resolvedCase, index) => await limit(async () => { const resultRow = seeded[index]; - // Don't enqueue a case once the run is cancelled (the flag short- - // circuits after the first observer, so this is one DB read per - // case at most). - if ( - abort.signal.aborted || - (await this.runRepository.isCancellationRequested(runId)) - ) { - await cancelCase(resultRow); + // Don't enqueue a case once the run is stopping. + if (await shouldStopCase()) { + await stopCase(resultRow); return; } - // Wait for a queue slot, bailing (and evicting the entry) if the run - // is cancelled while queued. The helper owns release/remove for the - // bail path; the finally owns release for the acquired path. + // The helper owns release/remove when the run stops while queued; + // the finally owns release once a slot is held. const executionId = `${runId}-case-${index}`; if (!(await this.acquireEvaluationSlot(executionId, abort.signal))) { - await this.resultRepository.markAsCancelled(resultRow.id); + await stopCase(resultRow); return; } try { - // A cancel may have landed while we waited for the slot. - if ( - abort.signal.aborted || - (await this.runRepository.isCancellationRequested(runId)) - ) { - await cancelCase(resultRow); + // A cancel or the deadline may have landed while we waited. + if (await shouldStopCase()) { + await stopCase(resultRow); return; } const usage = await this.runCase(resultRow, resolvedCase, ctx); @@ -288,19 +303,70 @@ export class AgentEvalRunnerService { }), ), ); + // `allSettled` never rejects, so the timer is always cleared here. + deadline.clear(); + const dispatchFailures: string[] = []; + for (const settlement of settlements) { + if (settlement.status !== 'rejected') continue; + const reason: unknown = settlement.reason; + dispatchFailures.push(reason instanceof Error ? reason.message : String(reason)); + } // Re-read once more: a cancel that arrived after every case had already // settled is never observed above, so honor it rather than reporting the - // run completed with the flag still set. - const wasCancelled = - abort.signal.aborted || (await this.runRepository.isCancellationRequested(runId)); + // run completed with the flag still set. Collected like the pool's throws, + // since letting this one escape would lose the tally it guards. + let wasCancelled = cancelObserved; + if (!wasCancelled) { + try { + wasCancelled = await this.runRepository.isCancellationRequested(runId); + } catch (error) { + dispatchFailures.push(error instanceof Error ? error.message : String(error)); + } + } + + if (dispatchFailures.length > 0) { + this.logger.error( + `[AgentEvalRunner] ${dispatchFailures.length} failure(s) outside case execution in run ${runId}`, + { errors: dispatchFailures }, + ); + } const counts = await this.resultRepository.countByStatus(runId); const metrics: IDataObject = { ...toSummaryCounts(counts), usage: { ...totalUsage } }; + // Every branch records the tally under `metrics`, so pollers never find a + // settled run whose counts are missing or filed elsewhere. if (wasCancelled) { + // A user stop outranks the deadline: it's intent, not a failure. await this.runRepository.markAsCancelled(runId, metrics); + } else if (deadline.hasExpired() && stoppedCases > 0) { + await this.runRepository.markAsError( + runId, + 'timeout', + { + message: `Run exceeded its ${deadline.deadlineMinutes}-minute deadline; ${stoppedCases} case(s) were not started.`, + }, + metrics, + ); + } else if (dispatchFailures.length > 0) { + await this.runRepository.markAsError( + runId, + 'case_dispatch_failed', + { + message: `${dispatchFailures.length} failure(s) occurred outside case execution.`, + errors: dispatchFailures, + }, + metrics, + ); } else { + if (deadline.hasExpired()) { + // Overran, but nothing was left to skip. Logged for anyone tuning the + // limit, since the run itself still succeeded. + this.logger.debug( + `[AgentEvalRunner] Run ${runId} overran its ${deadline.deadlineMinutes}-minute deadline, but every case had finished`, + ); + } await this.runRepository.markAsCompleted(runId, metrics); } } catch (error) { @@ -309,6 +375,34 @@ export class AgentEvalRunnerService { } } + /** + * Arm the run-level deadline; expiring trips the run's abort. Non-positive + * disables. Bounds when the last case may *start*, not when the run ends. + */ + private startRunDeadline(abort: AbortController): { + hasExpired: () => boolean; + clear: () => void; + deadlineMinutes: number; + } { + const deadlineMinutes = this.globalConfig.evaluation.agentEvalsRunTimeoutMinutes; + if (deadlineMinutes <= 0) { + return { hasExpired: () => false, clear: () => undefined, deadlineMinutes }; + } + + let expired = false; + const timer = setTimeout( + () => { + expired = true; + abort.abort(); + }, + Math.min(deadlineMinutes * 60_000, MAX_TIMER_DELAY_MS), + ); + // Never hold the process open for a run that outlived its deadline. + timer.unref(); + + return { hasExpired: () => expired, clear: () => clearTimeout(timer), deadlineMinutes }; + } + /** * Acquire a slot in the shared evaluation queue, abort-aware. Resolves `true` * with the slot held (the caller must `release`); resolves `false` if the run diff --git a/packages/cli/src/modules/agent-evals/agent-eval.service.ts b/packages/cli/src/modules/agent-evals/agent-eval.service.ts new file mode 100644 index 00000000000..9ab87de88ab --- /dev/null +++ b/packages/cli/src/modules/agent-evals/agent-eval.service.ts @@ -0,0 +1,232 @@ +import type { + AgentEvalDatasetRecord, + AgentEvalRunDetail, + AgentEvalRunRecord, + AgentEvalRunSummary, + CreateAgentEvalDatasetDto, + CreateAgentEvalRunPayload, + GenerateDraftCasesOptions, + GenerateDraftCasesResult, + UpdateAgentEvalDatasetPayload, +} from '@n8n/api-types'; +import { ModuleRegistry } from '@n8n/backend-common'; +import type { AgentEvalDataset, AgentEvalRun, User } from '@n8n/db'; +import { + AgentEvalDatasetRepository, + AgentEvalResultRepository, + AgentEvalRunRepository, +} from '@n8n/db'; +import { Service } from '@n8n/di'; + +import { BadRequestError } from '@/errors/response-errors/bad-request.error'; +import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { AgentRepository } from '@/modules/agents/repositories/agent.repository'; + +import { AgentEvalCaseGenerationService } from './agent-eval-case-generation.service'; +import { toDatasetRecord, toResultRecord, toRunRecord } from './agent-eval-record-mappers'; +import { AgentEvalRunnerService } from './agent-eval-runner.service'; +import { assertRequiredModulesActive } from './agent-evals-required-modules'; + +/** Statuses a run can still be asked to stop from. */ +const CANCELLABLE_STATUSES = new Set(['new', 'running']); + +/** + * Dataset CRUD, run reads and cancellation behind the agent-eval REST routes. + * + * **Every method is agent-scoped.** `@ProjectScope` proves the caller may act on + * `:projectId` — not that the agent lives there, nor that a dataset/run id + * belongs to it. So each entry point resolves `(agentId, projectId)` and then + * reads through agent-filtered queries. Foreign ids 404 like missing ones. + */ +@Service() +export class AgentEvalService { + constructor( + private readonly moduleRegistry: ModuleRegistry, + private readonly agentRepository: AgentRepository, + private readonly datasetRepository: AgentEvalDatasetRepository, + private readonly runRepository: AgentEvalRunRepository, + private readonly resultRepository: AgentEvalResultRepository, + private readonly runner: AgentEvalRunnerService, + private readonly caseGenerationService: AgentEvalCaseGenerationService, + ) {} + + // ---- datasets ---- + + async listDatasets(agentId: string, projectId: string): Promise { + await this.assertAgentInProject(agentId, projectId); + const datasets = await this.datasetRepository.findByAgentId(agentId); + return datasets.map(toDatasetRecord); + } + + async getDataset( + agentId: string, + projectId: string, + datasetId: string, + ): Promise { + await this.assertAgentInProject(agentId, projectId); + return toDatasetRecord(await this.resolveDataset(agentId, datasetId)); + } + + // `agentId` is in both the body and the path; disagreement means the client is + // confused about which agent it's configuring, so neither side wins. + async createDataset( + user: User, + agentId: string, + projectId: string, + payload: CreateAgentEvalDatasetDto, + ): Promise { + await this.assertAgentInProject(agentId, projectId); + + if (payload.agentId !== agentId) { + throw new BadRequestError( + `The dataset's agentId ('${payload.agentId}') does not match the agent in the URL.`, + ); + } + + const dataset = await this.datasetRepository.createDataset({ + name: payload.name, + description: payload.description ?? null, + agentId, + datasetSource: payload.datasetSource, + datasetRef: payload.datasetRef, + columnMapping: payload.columnMapping ?? null, + createdById: user.id, + }); + + return toDatasetRecord(dataset); + } + + async updateDataset( + agentId: string, + projectId: string, + datasetId: string, + payload: UpdateAgentEvalDatasetPayload, + ): Promise { + await this.assertAgentInProject(agentId, projectId); + const updated = await this.datasetRepository.updateDataset(datasetId, agentId, payload); + if (!updated) throw new NotFoundError(`Agent eval dataset ${datasetId} not found.`); + return toDatasetRecord(updated); + } + + // FK cascade takes the runs and results. The backing Data Table is left alone — + // it's independent, and may be shared or hand-authored. + async deleteDataset(agentId: string, projectId: string, datasetId: string): Promise { + await this.assertAgentInProject(agentId, projectId); + const deleted = await this.datasetRepository.deleteDataset(datasetId, agentId); + if (!deleted) throw new NotFoundError(`Agent eval dataset ${datasetId} not found.`); + } + + // ---- case generation ---- + + async generateDraftCases( + user: User, + agentId: string, + projectId: string, + options: GenerateDraftCasesOptions, + ): Promise { + await this.assertAgentInProject(agentId, projectId); + return await this.caseGenerationService.generateDraftCases(user, projectId, agentId, options); + } + + // ---- runs ---- + + // Returns once seeded; cases run in the background, so callers poll the summary. + // The runner's `finished` promise is dropped on purpose — it never rejects. + async startRun( + user: User, + agentId: string, + projectId: string, + datasetId: string, + payload: CreateAgentEvalRunPayload, + ): Promise { + await this.assertAgentInProject(agentId, projectId); + await this.resolveDataset(agentId, datasetId); + + // Accepting a pin and running the live agent would misreport what was measured. + if (payload.agentVersionId !== undefined) { + throw new BadRequestError('Pinning an agent version for an eval run is not supported yet.'); + } + + const { runId } = await this.runner.startRun(datasetId, projectId, user); + + const run = await this.runRepository.findById(runId); + if (!run) throw new NotFoundError(`Agent eval run ${runId} not found.`); + return toRunRecord(run); + } + + async listRuns( + agentId: string, + projectId: string, + datasetId: string, + ): Promise { + await this.assertAgentInProject(agentId, projectId); + await this.resolveDataset(agentId, datasetId); + const runs = await this.runRepository.findByDatasetIdAndAgentId(datasetId, agentId); + return runs.map(toRunRecord); + } + + /** A run with every per-case result — the "open a run" view. */ + async getRunDetail( + agentId: string, + projectId: string, + runId: string, + ): Promise { + await this.assertAgentInProject(agentId, projectId); + const run = await this.resolveRun(agentId, runId); + const results = await this.resultRepository.findByRunId(runId); + return { ...toRunRecord(run), results: results.map(toResultRecord) }; + } + + // Per-case status counts for progress polling, ownership-resolved first so this + // read path is gated like the writes. + async getRunSummary( + agentId: string, + projectId: string, + runId: string, + ): Promise { + await this.assertAgentInProject(agentId, projectId); + return await this.runner.getRunSummary(runId, agentId); + } + + // Sets the flag rather than stopping anything: running cases abort at their next + // checkpoint, so the returned run is still `running` and settles shortly after. + async cancelRun(agentId: string, projectId: string, runId: string): Promise { + await this.assertAgentInProject(agentId, projectId); + const run = await this.resolveRun(agentId, runId); + + if (!CANCELLABLE_STATUSES.has(run.status)) { + throw new BadRequestError(`Agent eval run ${runId} has already finished ('${run.status}').`); + } + + await this.runRepository.requestCancellation(runId); + + const updated = await this.runRepository.findById(runId); + return toRunRecord(updated ?? run); + } + + // ---- internals ---- + + // `@ProjectScope` only checks the project in the URL, so this is what stops a + // caller with access to one project from addressing an agent in another. + // + // Every public method starts here, which makes it the one place to assert the + // modules this one depends on — before the agent lookup that would otherwise + // fail as a TypeORM missing-metadata error. + private async assertAgentInProject(agentId: string, projectId: string): Promise { + assertRequiredModulesActive(this.moduleRegistry); + const agent = await this.agentRepository.findByIdAndProjectId(agentId, projectId); + if (!agent) throw new NotFoundError(`Agent ${agentId} not found.`); + } + + private async resolveDataset(agentId: string, datasetId: string): Promise { + const dataset = await this.datasetRepository.findByIdAndAgentId(datasetId, agentId); + if (!dataset) throw new NotFoundError(`Agent eval dataset ${datasetId} not found.`); + return dataset; + } + + private async resolveRun(agentId: string, runId: string): Promise { + const run = await this.runRepository.findByIdAndAgentId(runId, agentId); + if (!run) throw new NotFoundError(`Agent eval run ${runId} not found.`); + return run; + } +} diff --git a/packages/cli/src/modules/agent-evals/agent-evals-flag-gate.ts b/packages/cli/src/modules/agent-evals/agent-evals-flag-gate.ts new file mode 100644 index 00000000000..9fb8377b3bb --- /dev/null +++ b/packages/cli/src/modules/agent-evals/agent-evals-flag-gate.ts @@ -0,0 +1,30 @@ +import { AGENT_EVALS_FLAG } from '@n8n/api-types'; +import type { User } from '@n8n/db'; +import { Service } from '@n8n/di'; + +import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { PostHogClient } from '@/posthog'; + +/** + * Per-user rollout gate for every agent-eval surface, shared by the controller + * and the services behind it so the two can't disagree on what "enabled" means. + * + * PostHog owns cohort rollout; `N8N_AGENT_EVALS_ENABLED` still force-enables + * because {@link PostHogClient} layers that override on top of the resolved + * flags. It also caches per user and falls back to those overrides when PostHog + * is unreachable, so this needs no caching or error handling of its own. + */ +@Service() +export class AgentEvalsFlagGate { + constructor(private readonly postHogClient: PostHogClient) {} + + async isEnabled(user: User): Promise { + const flags = await this.postHogClient.getFeatureFlags(user); + return flags?.[AGENT_EVALS_FLAG] === true; + } + + // 404 rather than 403: a flag-off surface should look unknown, not forbidden. + async assertEnabled(user: User): Promise { + if (!(await this.isEnabled(user))) throw new NotFoundError('Not found'); + } +} diff --git a/packages/cli/src/modules/agent-evals/agent-evals-required-modules.ts b/packages/cli/src/modules/agent-evals/agent-evals-required-modules.ts new file mode 100644 index 00000000000..c793b26f38d --- /dev/null +++ b/packages/cli/src/modules/agent-evals/agent-evals-required-modules.ts @@ -0,0 +1,19 @@ +import type { ModuleRegistry } from '@n8n/backend-common'; + +import { BadRequestError } from '@/errors/response-errors/bad-request.error'; + +const REQUIRED_MODULES = ['agents', 'data-table'] as const; + +/** + * Agent evals read agents and Data Tables, whose entities only exist while those + * modules are active. Callers must assert before their first repository touch, + * or TypeORM raises a missing-metadata error instead of saying what is off. + */ +export function assertRequiredModulesActive(moduleRegistry: ModuleRegistry): void { + const inactive = REQUIRED_MODULES.filter((name) => !moduleRegistry.isActive(name)); + if (inactive.length > 0) { + throw new BadRequestError( + `Agent evals require these modules to be active: ${inactive.join(', ')}.`, + ); + } +} diff --git a/packages/cli/src/modules/agent-evals/agent-evals.controller.ts b/packages/cli/src/modules/agent-evals/agent-evals.controller.ts new file mode 100644 index 00000000000..dab14b1d5ec --- /dev/null +++ b/packages/cli/src/modules/agent-evals/agent-evals.controller.ts @@ -0,0 +1,156 @@ +import { + CreateAgentEvalRunDto, + GenerateDraftCasesOptionsDto, + UpdateAgentEvalDatasetDto, + createAgentEvalDatasetSchema, + type AgentEvalDatasetRecord, + type AgentEvalRunDetail, + type AgentEvalRunRecord, + type AgentEvalRunSummary, + type GenerateDraftCasesResult, +} from '@n8n/api-types'; +import type { AuthenticatedRequest } from '@n8n/db'; +import { Body, Delete, Get, Patch, Post, ProjectScope, RestController } from '@n8n/decorators'; + +import { BadRequestError } from '@/errors/response-errors/bad-request.error'; + +import { AgentEvalService } from './agent-eval.service'; +import { AgentEvalsFlagGate } from './agent-evals-flag-gate'; + +type AgentParam = { projectId: string; agentId: string }; +type DatasetParam = AgentParam & { datasetId: string }; +type RunParam = AgentParam & { runId: string }; + +/** + * REST surface for agent evals: generation, datasets, runs and per-case results. + * Nested under the agent so `@ProjectScope` rejects before the handler runs. + * + * `agent:read` for reads, `agent:execute` for running a run, `agent:update` for + * eval-config writes — including generation, which spends the builder's model + * credits and so must stay closed to viewers (they hold `agent:execute`). + * Ratings ship with the service that persists them. + */ +@RestController('/projects/:projectId/agents/v2') +export class AgentEvalsController { + constructor( + private readonly service: AgentEvalService, + private readonly flagGate: AgentEvalsFlagGate, + ) {} + + // ---- datasets ---- + + @Get('/:agentId/evals/datasets') + @ProjectScope('agent:read') + async listDatasets(req: AuthenticatedRequest): Promise { + await this.flagGate.assertEnabled(req.user); + const { agentId, projectId } = req.params; + return await this.service.listDatasets(agentId, projectId); + } + + @Post('/:agentId/evals/datasets') + @ProjectScope('agent:update') + async createDataset(req: AuthenticatedRequest): Promise { + await this.flagGate.assertEnabled(req.user); + const { agentId, projectId } = req.params; + // Hand-parsed, not `@Body`-bound: the `DatasetRef` union can't be expressed as + // a `Z.class`, so this DTO ships as a schema and the handler owns the 400. + const parsed = createAgentEvalDatasetSchema.safeParse(req.body); + if (!parsed.success) { + throw new BadRequestError(parsed.error.issues.map((i) => i.message).join(', ')); + } + return await this.service.createDataset(req.user, agentId, projectId, parsed.data); + } + + @Get('/:agentId/evals/datasets/:datasetId') + @ProjectScope('agent:read') + async getDataset(req: AuthenticatedRequest): Promise { + await this.flagGate.assertEnabled(req.user); + const { agentId, projectId, datasetId } = req.params; + return await this.service.getDataset(agentId, projectId, datasetId); + } + + @Patch('/:agentId/evals/datasets/:datasetId') + @ProjectScope('agent:update') + async updateDataset( + req: AuthenticatedRequest, + _res: unknown, + @Body payload: UpdateAgentEvalDatasetDto, + ): Promise { + await this.flagGate.assertEnabled(req.user); + const { agentId, projectId, datasetId } = req.params; + return await this.service.updateDataset(agentId, projectId, datasetId, payload); + } + + @Delete('/:agentId/evals/datasets/:datasetId') + @ProjectScope('agent:update') + async deleteDataset(req: AuthenticatedRequest): Promise<{ success: true }> { + await this.flagGate.assertEnabled(req.user); + const { agentId, projectId, datasetId } = req.params; + await this.service.deleteDataset(agentId, projectId, datasetId); + return { success: true }; + } + + // ---- case generation ---- + + // Drafts cases from the agent's config into a new dataset, calling the agent's + // model with its own credential — hence never on a read-only scope. + @Post('/:agentId/evals/generate') + @ProjectScope('agent:update') + async generateDraftCases( + req: AuthenticatedRequest, + _res: unknown, + @Body payload: GenerateDraftCasesOptionsDto, + ): Promise { + await this.flagGate.assertEnabled(req.user); + const { agentId, projectId } = req.params; + return await this.service.generateDraftCases(req.user, agentId, projectId, payload); + } + + // ---- runs ---- + + /** Returns as soon as the run is seeded; poll the summary route for progress. */ + @Post('/:agentId/evals/datasets/:datasetId/runs') + @ProjectScope('agent:execute') + async startRun( + req: AuthenticatedRequest, + _res: unknown, + @Body payload: CreateAgentEvalRunDto, + ): Promise { + await this.flagGate.assertEnabled(req.user); + const { agentId, projectId, datasetId } = req.params; + return await this.service.startRun(req.user, agentId, projectId, datasetId, payload); + } + + @Get('/:agentId/evals/datasets/:datasetId/runs') + @ProjectScope('agent:read') + async listRuns(req: AuthenticatedRequest): Promise { + await this.flagGate.assertEnabled(req.user); + const { agentId, projectId, datasetId } = req.params; + return await this.service.listRuns(agentId, projectId, datasetId); + } + + @Get('/:agentId/evals/runs/:runId') + @ProjectScope('agent:read') + async getRun(req: AuthenticatedRequest): Promise { + await this.flagGate.assertEnabled(req.user); + const { agentId, projectId, runId } = req.params; + return await this.service.getRunDetail(agentId, projectId, runId); + } + + /** Counts only — cheap enough for the UI to poll while a run is in flight. */ + @Get('/:agentId/evals/runs/:runId/summary') + @ProjectScope('agent:read') + async getRunSummary(req: AuthenticatedRequest): Promise { + await this.flagGate.assertEnabled(req.user); + const { agentId, projectId, runId } = req.params; + return await this.service.getRunSummary(agentId, projectId, runId); + } + + @Post('/:agentId/evals/runs/:runId/cancel') + @ProjectScope('agent:execute') + async cancelRun(req: AuthenticatedRequest): Promise { + await this.flagGate.assertEnabled(req.user); + const { agentId, projectId, runId } = req.params; + return await this.service.cancelRun(agentId, projectId, runId); + } +} diff --git a/packages/cli/src/modules/agent-evals/agent-evals.module.ts b/packages/cli/src/modules/agent-evals/agent-evals.module.ts index e3bdbdbc456..28e97cee9a5 100644 --- a/packages/cli/src/modules/agent-evals/agent-evals.module.ts +++ b/packages/cli/src/modules/agent-evals/agent-evals.module.ts @@ -3,14 +3,17 @@ import { BackendModule } from '@n8n/decorators'; import { Container } from '@n8n/di'; /** - * Agent-scoped evaluations. Runs eval datasets against a real agent and - * persists per-case results. Main-process only — the reused tool-mock substrate - * cannot cross a queue boundary. Opt-in (not a default module) and dark until - * the `101_agent_evals` flag is on; the persistence layer lives in `@n8n/db`. + * Agent-scoped evaluations: runs eval datasets against a real agent, persists + * per-case results, and serves the REST surface the editor consumes. + * + * Main-process only — the reused tool-mock substrate can't cross a queue + * boundary. Opt-in module, and dark until `101_agent_evals` is on for the user. */ @BackendModule({ name: 'agent-evals', instanceTypes: ['main'] }) export class AgentEvalsModule implements ModuleInterface { async init() { + await import('./agent-evals.controller.js'); + const { AgentEvalRunnerService } = await import('./agent-eval-runner.service.js'); const runner = Container.get(AgentEvalRunnerService); // The runner can't resume runs interrupted by a restart — sweep any left diff --git a/packages/cli/src/modules/agents/__tests__/agent-chat-attachment.service.test.ts b/packages/cli/src/modules/agents/__tests__/agent-chat-attachment.service.test.ts new file mode 100644 index 00000000000..132ead852c2 --- /dev/null +++ b/packages/cli/src/modules/agents/__tests__/agent-chat-attachment.service.test.ts @@ -0,0 +1,227 @@ +import type { Logger } from '@n8n/backend-common'; +import type { BinaryDataService } from 'n8n-core'; +import { mock } from 'vitest-mock-extended'; + +import { AgentChatAttachmentService } from '../agent-chat-attachment.service'; +import type { AgentChatAttachment } from '../entities/agent-chat-attachment.entity'; +import type { AgentChatAttachmentRepository } from '../repositories/agent-chat-attachment.repository'; + +describe('AgentChatAttachmentService', () => { + let binaryDataService = mock(); + let repository = mock(); + let service: AgentChatAttachmentService; + + beforeEach(() => { + vi.clearAllMocks(); + binaryDataService = mock(); + repository = mock(); + service = new AgentChatAttachmentService(mock(), binaryDataService, repository); + }); + + describe('storeInbound', () => { + it('stores bytes via BinaryDataService and persists a scoped row', async () => { + binaryDataService.store.mockResolvedValue({ + id: 'filesystem-v2:agents/agent-1/attachments/att-1/x', + data: 'filesystem-v2', + mimeType: 'image/png', + }); + repository.create.mockImplementation((input) => input as AgentChatAttachment); + repository.save.mockImplementation(async (input) => input as AgentChatAttachment); + + const stored = await service.storeInbound({ + agentId: 'agent-1', + projectId: 'project-1', + threadId: 'thread-1', + resourceId: 'user-1', + source: 'chat', + fileName: 'photo.png', + mimeType: 'image/png', + data: Buffer.from([1, 2, 3]), + }); + + expect(binaryDataService.store).toHaveBeenCalledTimes(1); + expect(binaryDataService.store).toHaveBeenCalledWith( + expect.objectContaining({ + type: 'custom', + pathSegments: ['agents', 'agent-1', 'attachments', expect.any(String)], + }), + expect.anything(), + expect.anything(), + ); + + expect(repository.save).toHaveBeenCalledTimes(1); + expect(stored.binaryDataId).toBe('filesystem-v2:agents/agent-1/attachments/att-1/x'); + expect(stored.fileSizeBytes).toBe(3); + expect(stored.source).toBe('chat'); + }); + + it('throws when the binary storage mode does not persist ids (in-memory)', async () => { + binaryDataService.store.mockResolvedValue({ data: 'abc', mimeType: 'image/png' }); + + await expect( + service.storeInbound({ + agentId: 'agent-1', + projectId: 'project-1', + threadId: 'thread-1', + source: 'chat', + fileName: 'photo.png', + mimeType: 'image/png', + data: Buffer.from([1]), + }), + ).rejects.toThrow('persisted binary data storage mode'); + }); + + it('cleans up stored bytes when the row insert fails', async () => { + binaryDataService.deleteManyByBinaryDataId.mockResolvedValue(undefined as never); + binaryDataService.store.mockResolvedValue({ + id: 'filesystem-v2:x', + data: 'filesystem-v2', + mimeType: 'image/png', + }); + repository.create.mockImplementation((input) => input as AgentChatAttachment); + repository.save.mockRejectedValue(new Error('db down')); + + await expect( + service.storeInbound({ + agentId: 'agent-1', + projectId: 'project-1', + threadId: 'thread-1', + source: 'chat', + fileName: 'photo.png', + mimeType: 'image/png', + data: Buffer.from([1]), + }), + ).rejects.toThrow('db down'); + expect(binaryDataService.deleteManyByBinaryDataId).toHaveBeenCalledWith(['filesystem-v2:x']); + }); + }); + + describe('getFileStore', () => { + it('loads bytes for attachments in scope', async () => { + repository.findByIdForAgent.mockResolvedValue({ + id: 'att-1', + binaryDataId: 'filesystem-v2:x', + mimeType: 'image/png', + } as AgentChatAttachment); + binaryDataService.getAsBuffer.mockResolvedValue(Buffer.from([1, 2])); + + const store = service.getFileStore( + { agentId: 'agent-1', projectId: 'project-1' }, + 'anthropic', + ); + const bytes = await store.load({ id: 'att-1' }); + + expect(repository.findByIdForAgent).toHaveBeenCalledWith('att-1', { + agentId: 'agent-1', + projectId: 'project-1', + }); + expect(bytes).toEqual(Buffer.from([1, 2])); + }); + + it('scopes the lookup to the thread when the run provides one', async () => { + repository.findByIdInThread.mockResolvedValue({ + id: 'att-1', + binaryDataId: 'filesystem-v2:x', + mimeType: 'image/png', + } as AgentChatAttachment); + binaryDataService.getAsBuffer.mockResolvedValue(Buffer.from([1, 2])); + + const store = service.getFileStore( + { agentId: 'agent-1', projectId: 'project-1' }, + 'anthropic', + ); + const bytes = await store.load({ id: 'att-1' }, { threadId: 'thread-1' }); + + expect(repository.findByIdInThread).toHaveBeenCalledWith('att-1', { + projectId: 'project-1', + threadId: 'thread-1', + }); + expect(repository.findByIdForAgent).not.toHaveBeenCalled(); + expect(bytes).toEqual(Buffer.from([1, 2])); + }); + + it('returns null for attachments outside the run thread', async () => { + repository.findByIdInThread.mockResolvedValue(null); + + const store = service.getFileStore( + { agentId: 'agent-1', projectId: 'project-1' }, + 'anthropic', + ); + + expect(await store.load({ id: 'att-1' }, { threadId: 'other-thread' })).toBeNull(); + expect(binaryDataService.getAsBuffer).not.toHaveBeenCalled(); + }); + + it('returns null for attachments outside the agent/project scope', async () => { + repository.findByIdForAgent.mockResolvedValue(null); + + const store = service.getFileStore( + { agentId: 'agent-2', projectId: 'project-1' }, + 'anthropic', + ); + expect(await store.load({ id: 'att-1' })).toBeNull(); + expect(binaryDataService.getAsBuffer).not.toHaveBeenCalled(); + }); + + it('returns null instead of throwing when byte loading fails', async () => { + repository.findByIdForAgent.mockResolvedValue({ + id: 'att-1', + binaryDataId: 'filesystem-v2:x', + mimeType: 'image/png', + } as AgentChatAttachment); + binaryDataService.getAsBuffer.mockRejectedValue(new Error('storage down')); + + const store = service.getFileStore( + { agentId: 'agent-1', projectId: 'project-1' }, + 'anthropic', + ); + expect(await store.load({ id: 'att-1' })).toBeNull(); + }); + + it('gates media types by the provider capability map', () => { + const anthropic = service.getFileStore( + { agentId: 'agent-1', projectId: 'project-1' }, + 'anthropic', + ); + expect(anthropic.isMediaTypeSupported?.('image/png')).toBe(true); + expect(anthropic.isMediaTypeSupported?.('application/pdf')).toBe(true); + expect(anthropic.isMediaTypeSupported?.('audio/ogg')).toBe(false); + + const google = service.getFileStore({ agentId: 'agent-1', projectId: 'project-1' }, 'google'); + expect(google.isMediaTypeSupported?.('audio/ogg')).toBe(true); + + const groq = service.getFileStore({ agentId: 'agent-1', projectId: 'project-1' }, 'groq'); + expect(groq.isMediaTypeSupported?.('image/png')).toBe(false); + + const unknown = service.getFileStore({ agentId: 'agent-1', projectId: 'project-1' }, 'nope'); + expect(unknown.isMediaTypeSupported?.('image/png')).toBe(false); + }); + }); + + describe('deleteByThread', () => { + it('deletes rows and bytes for the thread within the given scope', async () => { + binaryDataService.deleteManyByBinaryDataId.mockResolvedValue(undefined as never); + repository.findByThread.mockResolvedValue([ + { id: 'att-1', binaryDataId: 'filesystem-v2:a' }, + { id: 'att-2', binaryDataId: 'filesystem-v2:b' }, + ] as AgentChatAttachment[]); + + await service.deleteByThread('thread-1', { projectId: 'project-1' }); + + expect(repository.findByThread).toHaveBeenCalledWith('thread-1', { + projectId: 'project-1', + }); + expect(repository.delete).toHaveBeenCalledWith(['att-1', 'att-2']); + expect(binaryDataService.deleteManyByBinaryDataId).toHaveBeenCalledWith([ + 'filesystem-v2:a', + 'filesystem-v2:b', + ]); + }); + + it('is a no-op for threads without attachments', async () => { + repository.findByThread.mockResolvedValue([]); + await service.deleteByThread('thread-1', { agentId: 'agent-1' }); + expect(repository.delete).not.toHaveBeenCalled(); + }); + }); +}); diff --git a/packages/cli/src/modules/agents/__tests__/agent-chat.controller.test.ts b/packages/cli/src/modules/agents/__tests__/agent-chat.controller.test.ts index f85cf504efc..b55034f6c01 100644 --- a/packages/cli/src/modules/agents/__tests__/agent-chat.controller.test.ts +++ b/packages/cli/src/modules/agents/__tests__/agent-chat.controller.test.ts @@ -2,9 +2,12 @@ import { EventEmitter } from 'node:events'; import type { Mocked } from 'vitest'; import { mock } from 'vitest-mock-extended'; +import { FileNotFoundError } from 'n8n-core'; + import type { CredentialsService } from '@/credentials/credentials.service'; import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import type { AgentChatAttachmentService } from '../agent-chat-attachment.service'; import { AgentChatController } from '../agent-chat.controller'; import type { AgentExecutionOrchestratorService } from '../agent-execution-orchestrator.service'; import type { AgentExecutionService } from '../agent-execution.service'; @@ -25,6 +28,7 @@ function makeController() { const agentsBuilderService = mock(); const agentValidationService = mock(); const agentExecutionService = mock(); + const agentChatAttachmentService = mock(); agentValidationService.validateAgentIsRunnable.mockResolvedValue({ missing: [] }); const controller = new AgentChatController( @@ -35,6 +39,7 @@ function makeController() { mock(), agentExecutionService, agentsService as unknown as AgentsService, + agentChatAttachmentService, ); return { @@ -42,6 +47,7 @@ function makeController() { agentExecutionOrchestratorService, agentValidationService, agentExecutionService, + agentChatAttachmentService, agentsService: { findById: agentsService.findById, getConversationHistory: agentExecutionOrchestratorService.getConversationHistory, @@ -85,6 +91,7 @@ describe('AgentChatController route access scopes', () => { it.each([ ['chat', 'agent:execute'], ['chatResume', 'agent:execute'], + ['cancelChatRun', 'agent:execute'], ['getChatMessages', 'agent:read'], ['getTestChatMessages', 'agent:read'], ['clearTestChatMessages', 'agent:update'], @@ -150,6 +157,71 @@ describe('AgentChatController chat message history', () => { }); describe('AgentChatController SSE done payload', () => { + it('does not start a chat after the response closes during session lookup', async () => { + const { + controller, + agentExecutionOrchestratorService, + agentExecutionService, + agentValidationService, + } = makeController(); + let resolveLookup = (_value: null) => {}; + agentExecutionService.findThreadById.mockReturnValue( + new Promise((resolve) => { + resolveLookup = resolve; + }), + ); + agentExecutionOrchestratorService.executeForChat.mockImplementation(async function* () { + yield* []; + }); + + const res = makeSseResponse([]); + const request = controller.chat( + { params: { projectId: 'project-1' }, user: { id: 'user-1' } } as never, + res, + 'agent-1', + { message: 'hi', sessionId: 'thread-1' } as never, + ); + await vi.waitFor(() => expect(agentExecutionService.findThreadById).toHaveBeenCalled()); + + (res as unknown as EventEmitter).emit('close'); + resolveLookup(null); + await request; + + expect(agentValidationService.validateAgentIsRunnable).not.toHaveBeenCalled(); + expect(agentExecutionOrchestratorService.executeForChat).not.toHaveBeenCalled(); + }); + + it('does not start a chat after the response closes during validation', async () => { + const { controller, agentExecutionOrchestratorService, agentValidationService } = + makeController(); + let resolveValidation = (_value: { missing: string[] }) => {}; + agentValidationService.validateAgentIsRunnable.mockReturnValue( + new Promise((resolve) => { + resolveValidation = resolve; + }), + ); + agentExecutionOrchestratorService.executeForChat.mockImplementation(async function* () { + yield* []; + }); + + const res = makeSseResponse([]); + const request = controller.chat( + { params: { projectId: 'project-1' }, user: { id: 'user-1' } } as never, + res, + 'agent-1', + { message: 'hi' } as never, + ); + await vi.waitFor(() => + expect(agentValidationService.validateAgentIsRunnable).toHaveBeenCalled(), + ); + + (res as unknown as EventEmitter).emit('close'); + resolveValidation({ missing: [] }); + await request; + + expect(agentExecutionOrchestratorService.executeForChat).not.toHaveBeenCalled(); + }); + it('includes executionId on done when recorded', async () => { const { controller, agentExecutionOrchestratorService, agentExecutionService } = makeController(); @@ -206,4 +278,244 @@ describe('AgentChatController SSE done payload', () => { executionId: 'exec-resume-1', }); }); + + it.each([ + { + name: 'new chat', + start: async (controller: AgentChatController, res: FlushableResponse) => + await controller.chat( + { params: { projectId: 'project-1' }, user: { id: 'user-1' } } as never, + res, + 'agent-1', + { message: 'hi' } as never, + ), + method: 'executeForChat' as const, + }, + { + name: 'resumed chat', + start: async (controller: AgentChatController, res: FlushableResponse) => + await controller.chatResume( + { params: { projectId: 'project-1' }, user: { id: 'user-1' } } as never, + res, + 'agent-1', + { runId: 'run-1', toolCallId: 'tc-1', resumeData: { approved: true } } as never, + ), + method: 'resumeForChat' as const, + }, + ])('aborts the $name when its SSE response closes early', async ({ start, method }) => { + const { controller, agentExecutionOrchestratorService, agentExecutionService } = + makeController(); + agentExecutionService.findThreadById.mockResolvedValue(null); + + let receivedSignal: AbortSignal | undefined; + let releaseRun = () => {}; + let markStarted = () => {}; + const runStarted = new Promise((resolve) => { + markStarted = resolve; + }); + const runBlocked = new Promise((resolve) => { + releaseRun = resolve; + }); + agentExecutionOrchestratorService[method].mockImplementation(async function* (config) { + receivedSignal = (config as { abortSignal?: AbortSignal }).abortSignal; + markStarted(); + await runBlocked; + yield* []; + }); + + const res = makeSseResponse([]); + const request = start(controller, res); + await runStarted; + (res as unknown as EventEmitter).emit('close'); + releaseRun(); + await request; + + expect(receivedSignal?.aborted).toBe(true); + }); +}); + +describe('AgentChatController HITL cancellation', () => { + it('cancels a suspended run for the current preview user', async () => { + const { controller, agentExecutionOrchestratorService, agentsService } = makeController(); + agentsService.findById.mockResolvedValue({ id: 'agent-1' } as never); + agentExecutionOrchestratorService.cancelChatRun.mockResolvedValue(true); + + await expect( + controller.cancelChatRun( + { + params: { projectId: 'project-1' }, + user: { id: 'user-1' }, + } as never, + {} as never, + 'agent-1', + 'run-1', + ), + ).resolves.toEqual({ cancelled: true }); + + expect(agentExecutionOrchestratorService.cancelChatRun).toHaveBeenCalledWith({ + agentId: 'agent-1', + runId: 'run-1', + resourceId: 'draft-chat:user-1', + }); + }); +}); + +describe('AgentChatController attachment cleanup on failed turns', () => { + const textAttachment = (fileName: string) => ({ + fileName, + mimeType: 'text/plain', + data: Buffer.from('hello').toString('base64'), + }); + + function makeCleanupSseResponse() { + const writes: string[] = []; + const res = makeSseResponse(writes); + const events = () => + writes + .filter((line) => line.startsWith('data: ')) + .map((line) => JSON.parse(line.slice(6).trim()) as { type: string; message?: string }); + return { res, events }; + } + + it('deletes stored attachments when the run fails before an execution is recorded', async () => { + const { + controller, + agentExecutionOrchestratorService, + agentExecutionService, + agentChatAttachmentService, + } = makeController(); + agentExecutionService.findThreadById.mockResolvedValue(null); + agentChatAttachmentService.storeInbound.mockResolvedValue({ + id: 'att-1', + fileName: 'notes.txt', + mimeType: 'text/plain', + fileSizeBytes: 5, + } as never); + agentChatAttachmentService.deleteByIds.mockResolvedValue(undefined); + // eslint-disable-next-line @typescript-eslint/require-await + agentExecutionOrchestratorService.executeForChat.mockImplementation(async function* () { + yield* []; + throw new Error('model unavailable'); + }); + const { res, events } = makeCleanupSseResponse(); + + await controller.chat( + { params: { projectId: 'project-1' }, user: { id: 'user-1' } } as never, + res, + 'agent-1', + { message: 'hi', attachments: [textAttachment('notes.txt')] } as never, + ); + + expect(events()).toContainEqual({ type: 'error', message: 'model unavailable' }); + expect(agentChatAttachmentService.deleteByIds).toHaveBeenCalledWith(['att-1']); + }); + + it('keeps stored attachments when the run fails after an execution was recorded', async () => { + const { + controller, + agentExecutionOrchestratorService, + agentExecutionService, + agentChatAttachmentService, + } = makeController(); + agentExecutionService.findThreadById.mockResolvedValue(null); + agentChatAttachmentService.storeInbound.mockResolvedValue({ + id: 'att-1', + fileName: 'notes.txt', + mimeType: 'text/plain', + fileSizeBytes: 5, + } as never); + // eslint-disable-next-line @typescript-eslint/require-await + agentExecutionOrchestratorService.executeForChat.mockImplementation(async function* (config) { + config.onExecutionRecorded?.('exec-1'); + yield* []; + throw new Error('flaky post-persist failure'); + }); + const { res } = makeCleanupSseResponse(); + + await controller.chat( + { params: { projectId: 'project-1' }, user: { id: 'user-1' } } as never, + res, + 'agent-1', + { message: 'hi', attachments: [textAttachment('notes.txt')] } as never, + ); + + expect(agentChatAttachmentService.deleteByIds).not.toHaveBeenCalled(); + }); + + it('deletes earlier attachments when a later one in the same message fails to store', async () => { + const { + controller, + agentExecutionOrchestratorService, + agentExecutionService, + agentChatAttachmentService, + } = makeController(); + agentExecutionService.findThreadById.mockResolvedValue(null); + agentChatAttachmentService.storeInbound + .mockResolvedValueOnce({ + id: 'att-1', + fileName: 'a.txt', + mimeType: 'text/plain', + fileSizeBytes: 5, + } as never) + .mockRejectedValueOnce(new Error('storage down')); + const { res, events } = makeCleanupSseResponse(); + + await controller.chat( + { params: { projectId: 'project-1' }, user: { id: 'user-1' } } as never, + res, + 'agent-1', + { message: 'hi', attachments: [textAttachment('a.txt'), textAttachment('b.txt')] } as never, + ); + + expect(events()).toContainEqual({ type: 'error', message: 'storage down' }); + expect(agentChatAttachmentService.deleteByIds).toHaveBeenCalledWith(['att-1']); + expect(agentExecutionOrchestratorService.executeForChat).not.toHaveBeenCalled(); + }); + + it('rejects an empty attachment with a dedicated error message', async () => { + const { controller, agentExecutionService, agentChatAttachmentService } = makeController(); + agentExecutionService.findThreadById.mockResolvedValue(null); + const { res, events } = makeCleanupSseResponse(); + + await controller.chat( + { params: { projectId: 'project-1' }, user: { id: 'user-1' } } as never, + res, + 'agent-1', + { + message: 'hi', + attachments: [{ fileName: 'empty.txt', mimeType: 'text/plain', data: '' }], + } as never, + ); + + expect(events()).toContainEqual({ + type: 'error', + message: 'Attachment "empty.txt" is empty', + }); + expect(agentChatAttachmentService.storeInbound).not.toHaveBeenCalled(); + }); +}); + +describe('AgentChatController attachment download', () => { + it('returns 404 when the attachment bytes are gone from storage', async () => { + const { controller, agentsService, agentChatAttachmentService } = makeController(); + agentsService.findById.mockResolvedValue({ id: 'agent-1' } as never); + agentChatAttachmentService.getForAgent.mockResolvedValue({ + id: 'att-1', + mimeType: 'image/png', + fileName: 'photo.png', + fileSizeBytes: 33, + } as never); + agentChatAttachmentService.getStream.mockRejectedValue( + new FileNotFoundError('filesystem-v2:agents/agent-1/attachments/att-1'), + ); + + const req = { + params: { projectId: 'p1', agentId: 'agent-1', attachmentId: 'att-1' }, + } as never; + const res = { setHeader: vi.fn() } as never; + + await expect(controller.getChatAttachment(req, res)).rejects.toThrow(NotFoundError); + // Headers must not be written for a failed stream open. + expect((res as { setHeader: ReturnType }).setHeader).not.toHaveBeenCalled(); + }); }); diff --git a/packages/cli/src/modules/agents/__tests__/agent-checkpoint.repository.test.ts b/packages/cli/src/modules/agents/__tests__/agent-checkpoint.repository.test.ts index ed3bf76f38e..9f1d98cbc5b 100644 --- a/packages/cli/src/modules/agents/__tests__/agent-checkpoint.repository.test.ts +++ b/packages/cli/src/modules/agents/__tests__/agent-checkpoint.repository.test.ts @@ -1,4 +1,5 @@ /* eslint-disable @typescript-eslint/unbound-method -- mock-based tests intentionally reference unbound methods */ +import { Equal, IsNull, Or } from '@n8n/typeorm'; import { mockEntityManager } from '@test/mocking'; import { AgentCheckpoint } from '../entities/agent-checkpoint.entity'; @@ -37,4 +38,24 @@ describe('AgentCheckpointRepository', () => { ).resolves.toBe(false); }); }); + + describe('cancelSuspended', () => { + it('matches checkpoints scoped to the current agent or unscoped', async () => { + vi.spyOn(repository, 'update').mockResolvedValue({ affected: 1 } as never); + + await expect( + repository.cancelSuspended('run-1', 'agent-1', '{"status":"suspended"}'), + ).resolves.toBe(true); + + expect(repository.update).toHaveBeenCalledWith( + { + runId: 'run-1', + agentId: Or(Equal('agent-1'), IsNull()), + expired: false, + state: '{"status":"suspended"}', + }, + { expired: true, state: null }, + ); + }); + }); }); diff --git a/packages/cli/src/modules/agents/__tests__/agent-config.service.test.ts b/packages/cli/src/modules/agents/__tests__/agent-config.service.test.ts index 1e88a5c7b1e..02e4b8709c6 100644 --- a/packages/cli/src/modules/agents/__tests__/agent-config.service.test.ts +++ b/packages/cli/src/modules/agents/__tests__/agent-config.service.test.ts @@ -1,5 +1,5 @@ import type { Mocked } from 'vitest'; -import type { AgentJsonConfig } from '@n8n/api-types'; +import { DEFAULT_AGENT_PERSONALISATION, type AgentJsonConfig } from '@n8n/api-types'; import { mockLogger } from '@n8n/backend-test-utils'; import type { WorkflowRepository } from '@n8n/db'; import { mock } from 'vitest-mock-extended'; @@ -252,6 +252,59 @@ describe('AgentConfigService', () => { expect(runtimeCacheService.clearRuntimes).toHaveBeenCalledWith(agentId); }); + it('drops stored optional fields omitted from the payload when clearOmittedOptionalFields is set', async () => { + const { service, agentRepository, credentialsService } = makeService(); + const agent = makeAgent({ + schema: { + ...baseConfig, + credential: 'stored-cred', + memory: { enabled: true, storage: 'n8n' }, + tools: [{ type: 'custom', id: 'tool-1' }], + } as unknown as AgentJsonConfig, + }); + agentRepository.findByIdAndProjectId.mockResolvedValue(agent); + mockAccessibleCredentials(credentialsService, ['stored-cred']); + + const result = await service.updateConfig( + agentId, + projectId, + { ...baseConfig, memory: { enabled: false, storage: 'n8n' } }, + undefined, + { clearOmittedOptionalFields: true }, + ); + + const saved = agentRepository.save.mock.calls.at(-1)?.[0] as Agent; + // Provided fields keep their submitted value; omitted ones are removed + // instead of retaining the stored value. + expect(saved.schema?.memory).toEqual({ enabled: false, storage: 'n8n' }); + expect(saved.schema).not.toHaveProperty('credential'); + expect(saved.schema).not.toHaveProperty('tools'); + expect(result.config).not.toHaveProperty('credential'); + }); + + it('resolves accessible credentials via the user when one is provided', async () => { + const { service, agentRepository, credentialsService } = makeService(); + agentRepository.findByIdAndProjectId.mockResolvedValue(makeAgent()); + credentialsService.getCredentialsAUserCanUseInAWorkflow.mockResolvedValue([ + { id: 'user-cred', type: 'openAiApi', name: 'user-cred' }, + ] as never); + const user = { id: 'user-1' } as never; + + await service.updateConfig( + agentId, + projectId, + { ...baseConfig, credential: 'user-cred' }, + user, + ); + + expect(credentialsService.getCredentialsAUserCanUseInAWorkflow).toHaveBeenCalledWith(user, { + projectId, + }); + expect(credentialsService.findAllCredentialIdsForProject).not.toHaveBeenCalled(); + const saved = agentRepository.save.mock.calls.at(-1)?.[0] as Agent; + expect((saved.schema as AgentJsonConfig).credential).toBe('user-cred'); + }); + it('rewrites an id-valued workflow tool ref to the workflow name on save', async () => { const { service, agentRepository, workflowRepository } = makeService(); const agent = makeAgent(); @@ -475,6 +528,43 @@ describe('AgentConfigService', () => { }); }); + it('resets an omitted gradient to the schema default when clearOmittedOptionalFields is set', async () => { + const { service, agentRepository } = makeService(); + const agent = makeAgent({ + schema: { + ...baseConfig, + personalisation: { + icon: 'bot', + gradient: { + from: '#111111', + to: '#222222', + angle: 42, + fromStop: 12, + toStop: 88, + }, + }, + }, + }); + agentRepository.findByIdAndProjectId.mockResolvedValue(agent); + + await service.updateConfig( + agentId, + projectId, + { + ...baseConfig, + personalisation: { icon: 'mail' }, + }, + undefined, + { clearOmittedOptionalFields: true }, + ); + + const saved = agentRepository.save.mock.calls[0][0] as Agent; + expect(saved.schema?.personalisation).toEqual({ + icon: 'mail', + gradient: DEFAULT_AGENT_PERSONALISATION.gradient, + }); + }); + it('stores only existing published subagents and rejects invalid subagent refs', async () => { const { service, agentRepository } = makeService(); const agent = makeAgent(); diff --git a/packages/cli/src/modules/agents/__tests__/agent-execution-orchestrator.service.test.ts b/packages/cli/src/modules/agents/__tests__/agent-execution-orchestrator.service.test.ts index 38458d42371..bee0609617e 100644 --- a/packages/cli/src/modules/agents/__tests__/agent-execution-orchestrator.service.test.ts +++ b/packages/cli/src/modules/agents/__tests__/agent-execution-orchestrator.service.test.ts @@ -1,4 +1,4 @@ -import type { Agent as RuntimeAgent, StreamChunk } from '@n8n/agents'; +import type { Agent as RuntimeAgent, SerializableAgentState, StreamChunk } from '@n8n/agents'; import { N8N_CHAT_INTEGRATION_TYPE, type AgentJsonConfig } from '@n8n/api-types'; import { mockLogger } from '@n8n/backend-test-utils'; import type { User } from '@n8n/db'; @@ -133,6 +133,7 @@ describe('AgentExecutionOrchestratorService', () => { it('streams chat responses and records suspended executions', async () => { const { service, executionService } = makeService(); + const abortController = new AbortController(); const runtime = makeRuntime([ { type: 'text-start', id: 'text-1' }, { type: 'text-delta', id: 'text-1', delta: 'Choose one' }, @@ -153,6 +154,7 @@ describe('AgentExecutionOrchestratorService', () => { message: 'hello', memory: { threadId: 'thread-1', resourceId: 'resource-1' }, projectId, + abortSignal: abortController.signal, }), ); @@ -162,6 +164,7 @@ describe('AgentExecutionOrchestratorService', () => { expect.objectContaining({ persistence: { threadId: 'thread-1', resourceId: 'resource-1' }, executionCounter: expect.any(Object), + abortSignal: abortController.signal, }), ); expect(executionService.recordMessage).toHaveBeenCalledWith( @@ -485,6 +488,44 @@ describe('AgentExecutionOrchestratorService', () => { ); }); + it('persists an aborted chat stream as cancelled without discarding partial output', async () => { + const { service, executionService } = makeService(); + const abortController = new AbortController(); + const runtime = makeRuntime([ + { type: 'text-start', id: 'text-1' }, + { type: 'text-delta', id: 'text-1', delta: 'partial answer' }, + { type: 'error', error: new Error('This operation was aborted') }, + { type: 'finish', finishReason: 'error' }, + ]); + const stream = service.streamChatResponse({ + agentInstance: runtime.agent, + toolRegistry: runtime.toolRegistry, + agentId, + message: 'hello', + memory: { threadId: 'thread-1', resourceId: 'resource-1' }, + projectId, + abortSignal: abortController.signal, + onExecutionRecorded: vi.fn(), + }); + + await stream.next(); + await stream.next(); + abortController.abort(); + await collect(stream); + + expect(executionService.recordMessage).toHaveBeenCalledWith( + expect.objectContaining({ + userMessage: 'hello', + record: expect.objectContaining({ + assistantResponse: 'partial answer', + finishReason: 'cancelled', + error: null, + timeline: [expect.objectContaining({ type: 'text', content: 'partial answer' })], + }), + }), + ); + }); + it('maps persisted execution history to chat DTOs', async () => { const { service, executionService } = makeService(); executionService.getThreadDetail.mockResolvedValue({ @@ -540,6 +581,7 @@ describe('AgentExecutionOrchestratorService', () => { } as never); runtimeCacheService.getRuntime.mockResolvedValue(runtime); + const abortController = new AbortController(); await collect( service.resumeForChat({ agentId, @@ -548,13 +590,18 @@ describe('AgentExecutionOrchestratorService', () => { toolCallId: 'tc-1', resumeData: { value: 'yes' }, integrationType: 'slack', + abortSignal: abortController.signal, }), ); expect(runtime.agent.resume).toHaveBeenCalledWith( 'stream', { value: 'yes' }, - expect.objectContaining({ runId: 'run-1', toolCallId: 'tc-1' }), + expect.objectContaining({ + runId: 'run-1', + toolCallId: 'tc-1', + abortSignal: abortController.signal, + }), ); expect(externalHooks.run).not.toHaveBeenCalled(); expect(JSON.stringify(runtime.agent.resume.mock.calls[0])).not.toContain('platform-user-1'); @@ -571,6 +618,80 @@ describe('AgentExecutionOrchestratorService', () => { ); }); + it('persists an aborted resumed stream as cancelled without discarding partial output', async () => { + const { service, checkpointStorage, runtimeCacheService, executionService } = makeService(); + const abortController = new AbortController(); + const runtime = makeRuntime([ + { type: 'text-start', id: 'text-1' }, + { type: 'text-delta', id: 'text-1', delta: 'partial resumed answer' }, + { type: 'error', error: new Error('This operation was aborted') }, + { type: 'finish', finishReason: 'error' }, + ]); + checkpointStorage.getStatus.mockResolvedValue({ + status: 'active', + checkpoint: { persistence: { threadId: 'thread-1', resourceId: 'resource-1' } }, + } as never); + runtimeCacheService.getRuntime.mockResolvedValue(runtime); + const stream = service.resumeForChat({ + agentId, + projectId, + runId: 'run-1', + toolCallId: 'tc-1', + resumeData: { value: 'yes' }, + abortSignal: abortController.signal, + onExecutionRecorded: vi.fn(), + }); + + await stream.next(); + await stream.next(); + abortController.abort(); + await collect(stream); + + expect(executionService.recordMessage).toHaveBeenCalledWith( + expect.objectContaining({ + userMessage: null, + hitlStatus: 'resumed', + record: expect.objectContaining({ + assistantResponse: 'partial resumed answer', + finishReason: 'cancelled', + error: null, + timeline: [expect.objectContaining({ type: 'text', content: 'partial resumed answer' })], + }), + }), + ); + }); + + it('atomically cancels only suspended checkpoints owned by the preview user', async () => { + const { service, checkpointStorage } = makeService(); + const checkpoint: SerializableAgentState = { + status: 'suspended', + persistence: { threadId: 'thread-1', resourceId: 'draft-chat:user-1' }, + messageList: { messages: [], historyIds: [], inputIds: [], responseIds: [] }, + pendingToolCalls: {}, + }; + checkpointStorage.getStatus.mockResolvedValue({ status: 'active', checkpoint }); + checkpointStorage.cancelSuspended.mockResolvedValue(true); + + await expect( + service.cancelChatRun({ + agentId, + runId: 'run-1', + resourceId: 'draft-chat:user-1', + }), + ).resolves.toBe(true); + expect(checkpointStorage.cancelSuspended).toHaveBeenCalledWith('run-1', checkpoint, agentId); + + checkpointStorage.cancelSuspended.mockClear(); + await expect( + service.cancelChatRun({ + agentId, + runId: 'run-1', + resourceId: 'draft-chat:another-user', + }), + ).resolves.toBe(false); + expect(checkpointStorage.cancelSuspended).not.toHaveBeenCalled(); + }); + it('passes tracing telemetry returned by AgentRunTracingService into stream() and resume()', async () => { const { service, checkpointStorage, runtimeCacheService, agentRunTracingService } = makeService(); diff --git a/packages/cli/src/modules/agents/__tests__/agent-execution.service.test.ts b/packages/cli/src/modules/agents/__tests__/agent-execution.service.test.ts index e0500757d73..d065dedf696 100644 --- a/packages/cli/src/modules/agents/__tests__/agent-execution.service.test.ts +++ b/packages/cli/src/modules/agents/__tests__/agent-execution.service.test.ts @@ -5,6 +5,7 @@ import type { ErrorReporter, StorageConfig } from 'n8n-core'; import type { Telemetry } from '@/telemetry'; +import type { AgentChatAttachmentService } from '../agent-chat-attachment.service'; import { AgentExecutionService } from '../agent-execution.service'; import type { AgentExecutionThread } from '../entities/agent-execution-thread.entity'; import type { AgentExecution } from '../entities/agent-execution.entity'; @@ -62,6 +63,7 @@ describe('AgentExecutionService', () => { let agentExecutionLogStore: Mocked; let storageConfig: Mocked; let errorReporter: Mocked; + let agentChatAttachmentService: Mocked; beforeEach(() => { vi.clearAllMocks(); @@ -75,6 +77,7 @@ describe('AgentExecutionService', () => { agentExecutionLogStore = mock(); storageConfig = mock({ modeTag: 'db' }); errorReporter = mock(); + agentChatAttachmentService = mock(); service = new AgentExecutionService( mockLogger(), @@ -82,6 +85,7 @@ describe('AgentExecutionService', () => { agentExecutionThreadRepository, n8nMemory, telemetry, + agentChatAttachmentService, agentExecutionLogStore, storageConfig, errorReporter, @@ -97,6 +101,7 @@ describe('AgentExecutionService', () => { agentExecutionThreadRepository, n8nMemory, telemetry, + mock(), agentExecutionLogStore, storageConfig, errorReporter, @@ -151,6 +156,7 @@ describe('AgentExecutionService', () => { agentExecutionThreadRepository, n8nMemory, telemetry, + mock(), agentExecutionLogStore, storageConfig, errorReporter, @@ -634,6 +640,9 @@ describe('AgentExecutionService', () => { }); expect(n8nMemory.getImplementation).toHaveBeenCalledWith('agent-1'); expect(memoryBackend.deleteThread).toHaveBeenCalledWith('thread-1'); + expect(agentChatAttachmentService.deleteByThread).toHaveBeenCalledWith('thread-1', { + projectId: 'project-1', + }); expect(agentExecutionThreadRepository.delete).toHaveBeenCalledWith({ id: 'thread-1' }); }); diff --git a/packages/cli/src/modules/agents/__tests__/agent-mcp-access.service.test.ts b/packages/cli/src/modules/agents/__tests__/agent-mcp-access.service.test.ts new file mode 100644 index 00000000000..2194e8fa848 --- /dev/null +++ b/packages/cli/src/modules/agents/__tests__/agent-mcp-access.service.test.ts @@ -0,0 +1,218 @@ +import { mockInstance } from '@n8n/backend-test-utils'; +import { User } from '@n8n/db'; + +import { ProjectScopeService } from '@/permissions.ee/project-scope.service'; + +import { AgentMcpAccessService } from '../agent-mcp-access.service'; +import { AgentRepository } from '../repositories/agent.repository'; + +const user = Object.assign(new User(), { id: 'user-1' }); + +const candidate = (id: string, projectId: string, availableInMCP: boolean) => ({ + id, + projectId, + availableInMCP, +}); + +describe('AgentMcpAccessService', () => { + const agentRepository = mockInstance(AgentRepository); + const projectScopeService = mockInstance(ProjectScopeService); + const service = new AgentMcpAccessService(agentRepository, projectScopeService); + + beforeEach(() => { + vi.clearAllMocks(); + projectScopeService.getProjectIds.mockResolvedValue(['project-1']); + }); + + describe('getAgents', () => { + it('lists non-exposed agents from projects where the user holds agent:update', async () => { + agentRepository.findByProjectIdsPaginated.mockResolvedValue({ count: 0, data: [] }); + + await service.getAgents(user, { skip: 0, take: 10 } as never); + + expect(projectScopeService.getProjectIds).toHaveBeenCalledWith(user, ['agent:update']); + expect(agentRepository.findByProjectIdsPaginated).toHaveBeenCalledWith( + ['project-1'], + expect.objectContaining({ filter: { availableInMCP: false } }), + { withProject: true }, + ); + }); + + it('lists agents without a project restriction for global agent:update', async () => { + projectScopeService.getProjectIds.mockResolvedValue(null); + agentRepository.findByProjectIdsPaginated.mockResolvedValue({ count: 0, data: [] }); + + await service.getAgents(user, { skip: 0, take: 10 } as never); + + expect(agentRepository.findByProjectIdsPaginated).toHaveBeenCalledWith( + null, + expect.objectContaining({ filter: { availableInMCP: false } }), + { withProject: true }, + ); + }); + + it('lists exposed agents only from projects where the user holds agent:update', async () => { + projectScopeService.getProjectIds.mockResolvedValue(['project-1']); + agentRepository.findByProjectIdsPaginated.mockResolvedValue({ count: 0, data: [] }); + + await service.getAgents(user, { + skip: 0, + take: 10, + filter: { availableInMCP: true }, + } as never); + + expect(agentRepository.findByProjectIdsPaginated).toHaveBeenCalledWith( + ['project-1'], + expect.objectContaining({ filter: { availableInMCP: true } }), + { withProject: true }, + ); + }); + + it('lists exposed agents without a project restriction for global agent:update', async () => { + projectScopeService.getProjectIds.mockResolvedValue(null); + agentRepository.findByProjectIdsPaginated.mockResolvedValue({ count: 0, data: [] }); + + await service.getAgents(user, { + skip: 0, + take: 10, + filter: { availableInMCP: true }, + } as never); + + expect(agentRepository.findByProjectIdsPaginated).toHaveBeenCalledWith( + null, + expect.objectContaining({ filter: { availableInMCP: true } }), + { withProject: true }, + ); + }); + }); + + describe('bulkSetAvailableInMCP', () => { + it('rejects when no target is provided', async () => { + await expect( + service.bulkSetAvailableInMCP(user, { availableInMCP: true } as never), + ).rejects.toThrow('exactly one'); + }); + + it('rejects when multiple targets are provided', async () => { + await expect( + service.bulkSetAvailableInMCP(user, { + availableInMCP: true, + agentIds: ['a1'], + allAgents: true, + } as never), + ).rejects.toThrow('exactly one'); + }); + + it('updates only accessible agents not already in the requested state', async () => { + agentRepository.findMcpAvailabilityCandidates.mockResolvedValue([ + candidate('a1', 'project-1', false), + candidate('a2', 'project-1', true), + candidate('a3', 'project-2', false), + ]); + + const result = await service.bulkSetAvailableInMCP(user, { + availableInMCP: true, + agentIds: ['a1', 'a2', 'a3'], + } as never); + + expect(agentRepository.setAvailableInMCP).toHaveBeenCalledWith(['a1'], true); + expect(result).toEqual({ + updatedCount: 1, + updatedIds: ['a1'], + unchangedIds: ['a2'], + }); + }); + + it('resolves candidates from every user project for allAgents and omits id lists', async () => { + projectScopeService.getProjectIds.mockResolvedValue(['p1', 'p2']); + agentRepository.findMcpAvailabilityCandidates.mockResolvedValue([ + candidate('a1', 'p1', true), + ]); + + const result = await service.bulkSetAvailableInMCP(user, { + availableInMCP: false, + allAgents: true, + } as never); + + expect(agentRepository.findMcpAvailabilityCandidates).toHaveBeenCalledWith({ + projectIds: ['p1', 'p2'], + }); + expect(agentRepository.setAvailableInMCP).toHaveBeenCalledWith(['a1'], false); + expect(result.updatedCount).toBe(1); + expect(result.updatedIds).toBeUndefined(); + expect(result.unchangedIds).toBeUndefined(); + }); + + it('resolves all candidates for allAgents with global agent:update', async () => { + projectScopeService.getProjectIds.mockResolvedValue(null); + agentRepository.findMcpAvailabilityCandidates.mockResolvedValue([ + candidate('a1', 'p1', false), + candidate('a2', 'p2', false), + ]); + + const result = await service.bulkSetAvailableInMCP(user, { + availableInMCP: true, + allAgents: true, + } as never); + + expect(agentRepository.findMcpAvailabilityCandidates).toHaveBeenCalledWith({ all: true }); + expect(agentRepository.setAvailableInMCP).toHaveBeenCalledWith(['a1', 'a2'], true); + expect(result.updatedCount).toBe(2); + }); + + it('chunks large updates to stay within database parameter limits', async () => { + projectScopeService.getProjectIds.mockResolvedValue(null); + const candidates = Array.from({ length: 600 }, (_, index) => + candidate(`a${index}`, `p${index}`, false), + ); + agentRepository.findMcpAvailabilityCandidates.mockResolvedValue(candidates); + + const result = await service.bulkSetAvailableInMCP(user, { + availableInMCP: true, + allAgents: true, + } as never); + + expect(agentRepository.setAvailableInMCP).toHaveBeenCalledTimes(2); + expect(agentRepository.setAvailableInMCP).toHaveBeenNthCalledWith( + 1, + candidates.slice(0, 500).map(({ id }) => id), + true, + ); + expect(agentRepository.setAvailableInMCP).toHaveBeenNthCalledWith( + 2, + candidates.slice(500).map(({ id }) => id), + true, + ); + expect(result.updatedCount).toBe(600); + }); + + it('does not load agents from a project the user cannot update', async () => { + projectScopeService.getProjectIds.mockResolvedValue([]); + + const result = await service.bulkSetAvailableInMCP(user, { + availableInMCP: true, + projectId: 'p1', + } as never); + + expect(agentRepository.findMcpAvailabilityCandidates).not.toHaveBeenCalled(); + expect(agentRepository.setAvailableInMCP).not.toHaveBeenCalled(); + expect(result).toEqual({ updatedCount: 0 }); + }); + + it('resolves candidates from the given project only', async () => { + projectScopeService.getProjectIds.mockResolvedValue(['p1']); + agentRepository.findMcpAvailabilityCandidates.mockResolvedValue([ + candidate('a1', 'p1', false), + ]); + + await service.bulkSetAvailableInMCP(user, { + availableInMCP: true, + projectId: 'p1', + } as never); + + expect(agentRepository.findMcpAvailabilityCandidates).toHaveBeenCalledWith({ + projectIds: ['p1'], + }); + }); + }); +}); diff --git a/packages/cli/src/modules/agents/__tests__/agent-message-mapper.test.ts b/packages/cli/src/modules/agents/__tests__/agent-message-mapper.test.ts new file mode 100644 index 00000000000..b1b5c32931e --- /dev/null +++ b/packages/cli/src/modules/agents/__tests__/agent-message-mapper.test.ts @@ -0,0 +1,45 @@ +import type { AgentDbMessage } from '@n8n/agents'; + +import { messageToDto } from '../agent-message-mapper'; + +describe('agent-message-mapper — file parts', () => { + it('maps fileRef file parts to reference metadata, never bytes', () => { + const message: AgentDbMessage = { + id: 'm-1', + createdAt: new Date(), + role: 'user', + content: [ + { type: 'text', text: 'look' }, + { + type: 'file', + mediaType: 'image/png', + data: new Uint8Array([1, 2, 3]), + fileRef: { id: 'att-1', fileName: 'photo.png', sizeBytes: 3 }, + }, + ], + }; + + const dto = messageToDto(message); + + expect(dto?.content[1]).toEqual({ + type: 'file', + fileId: 'att-1', + fileName: 'photo.png', + mimeType: 'image/png', + sizeBytes: 3, + }); + }); + + it('exposes no file fields for file parts without a fileRef', () => { + const message: AgentDbMessage = { + id: 'm-1', + createdAt: new Date(), + role: 'assistant', + content: [{ type: 'file', mediaType: 'image/png', data: new Uint8Array([1]) }], + }; + + const dto = messageToDto(message); + + expect(dto?.content[0]).toEqual({ type: 'file' }); + }); +}); diff --git a/packages/cli/src/modules/agents/__tests__/agent-model-catalog.service.test.ts b/packages/cli/src/modules/agents/__tests__/agent-model-catalog.service.test.ts index fc3de616b06..0f3bb5b6a32 100644 --- a/packages/cli/src/modules/agents/__tests__/agent-model-catalog.service.test.ts +++ b/packages/cli/src/modules/agents/__tests__/agent-model-catalog.service.test.ts @@ -1,5 +1,6 @@ -import type { User } from '@n8n/db'; +import { AI_GATEWAY_MANAGED_TAG } from '@n8n/api-types'; import { mockLogger } from '@n8n/backend-test-utils'; +import type { User } from '@n8n/db'; import { mock } from 'vitest-mock-extended'; import { AgentModelCatalogService } from '../agent-model-catalog.service'; @@ -84,6 +85,66 @@ describe('AgentModelCatalogService', () => { ); }); + it('verifies against the gateway allowlist for the n8n Connect managed tag', async () => { + const { service, lookupService } = makeService(); + // Gateway serves only Sonnet — the retired/unsupported Opus must be pruned. + lookupService.list.mockResolvedValue([ + { name: 'Claude Sonnet 4.6', value: 'claude-sonnet-4-6' }, + ]); + + const result = await service.getProviderModels( + user, + 'project-1', + 'anthropic', + AI_GATEWAY_MANAGED_TAG, + ); + + expect(result.verified).toBe(true); + expect(result.models.map((m) => m.id)).toEqual(['claude-sonnet-4-6']); + expect(lookupService.list).toHaveBeenCalledWith( + user, + 'project-1', + AI_GATEWAY_MANAGED_TAG, + 'anthropicApi', + 'anthropic', + ); + }); + + it('uses the gateway exact (snapshot) id for the managed tag, not the catalog alias', async () => { + const { service, lookupService } = makeService(); + fetchProviderCatalog.mockResolvedValue({ + anthropic: { + id: 'anthropic', + name: 'Anthropic', + models: { + 'claude-haiku-4-5': { + id: 'claude-haiku-4-5', + name: 'Claude Haiku 4.5', + reasoning: true, + toolCall: true, + }, + }, + }, + }); + // The gateway returns the dated snapshot — its allowlist matches this, not the alias. + lookupService.list.mockResolvedValue([ + { name: 'Claude Haiku 4.5', value: 'claude-haiku-4-5-20251001' }, + ]); + + const result = await service.getProviderModels( + user, + 'project-1', + 'anthropic', + AI_GATEWAY_MANAGED_TAG, + ); + + expect(result.verified).toBe(true); + // Exact gateway id (callable), catalog display name. + expect(result.models).toEqual([ + expect.objectContaining({ id: 'claude-haiku-4-5-20251001', name: 'Claude Haiku 4.5' }), + ]); + }); + it('verifies a catalog alias when the provider lists only its dated snapshot', async () => { const { service, lookupService } = makeService(); fetchProviderCatalog.mockResolvedValue({ diff --git a/packages/cli/src/modules/agents/__tests__/agent-publish.service.test.ts b/packages/cli/src/modules/agents/__tests__/agent-publish.service.test.ts index 4c5b5e97b4f..c9114d1e379 100644 --- a/packages/cli/src/modules/agents/__tests__/agent-publish.service.test.ts +++ b/packages/cli/src/modules/agents/__tests__/agent-publish.service.test.ts @@ -516,6 +516,34 @@ describe('AgentPublishService', () => { ); }); + it('returns a version snapshot with its task snapshots', async () => { + const { service, agentRepository, agentHistoryRepository, taskSnapshotRepository } = + makeService(); + const version = makeHistory({ versionId: 'old-version' }); + agentRepository.findByIdAndProjectId.mockResolvedValue(makeAgent()); + agentHistoryRepository.findByVersionAndAgentId.mockResolvedValue(version); + taskSnapshotRepository.findByVersionId.mockResolvedValue([makeTaskSnapshot()]); + + const result = await service.getVersion(agentId, projectId, 'old-version'); + + expect(agentHistoryRepository.findByVersionAndAgentId).toHaveBeenCalledWith( + 'old-version', + agentId, + ); + expect(result.version).toBe(version); + expect(result.tasks).toEqual([makeTaskSnapshot()]); + }); + + it('throws when the requested version does not exist for the agent', async () => { + const { service, agentRepository, agentHistoryRepository } = makeService(); + agentRepository.findByIdAndProjectId.mockResolvedValue(makeAgent()); + agentHistoryRepository.findByVersionAndAgentId.mockResolvedValue(null); + + await expect(service.getVersion(agentId, projectId, 'nope')).rejects.toThrow( + 'Version "nope" not found for agent "agent-1"', + ); + }); + it('ignores other draft integrations when connecting a channel (ignoreDraftIntegrations)', async () => { const { service, agentRepository, agentValidationService } = makeService(); const integrations = [ diff --git a/packages/cli/src/modules/agents/__tests__/agent-run-tracing.service.test.ts b/packages/cli/src/modules/agents/__tests__/agent-run-tracing.service.test.ts index 7e6c2bb946d..0901a3d1033 100644 --- a/packages/cli/src/modules/agents/__tests__/agent-run-tracing.service.test.ts +++ b/packages/cli/src/modules/agents/__tests__/agent-run-tracing.service.test.ts @@ -5,7 +5,7 @@ import { mock } from 'vitest-mock-extended'; import { AgentRunTracingService, modelIdFromSnapshot } from '../agent-run-tracing.service'; vi.mock('@opentelemetry/api', () => ({ - trace: { getTracer: vi.fn(() => ({ startActiveSpan: vi.fn() })) }, + trace: { getTracer: vi.fn(() => ({ startSpan: vi.fn(), startActiveSpan: vi.fn() })) }, })); describe('AgentRunTracingService', () => { diff --git a/packages/cli/src/modules/agents/__tests__/agent-runtime-reconstruction-integration-tools.test.ts b/packages/cli/src/modules/agents/__tests__/agent-runtime-reconstruction-integration-tools.test.ts index 6b9476dc256..e2c316e39fa 100644 --- a/packages/cli/src/modules/agents/__tests__/agent-runtime-reconstruction-integration-tools.test.ts +++ b/packages/cli/src/modules/agents/__tests__/agent-runtime-reconstruction-integration-tools.test.ts @@ -27,11 +27,13 @@ import type { EphemeralNodeExecutor } from '@/node-execution'; import type { NodeTypes } from '@/node-types'; import type { OauthService } from '@/oauth/oauth.service'; import type { Publisher } from '@/scaling/pubsub/publisher.service'; +import type { AiGatewayService } from '@/services/ai-gateway.service'; import type { AiService } from '@/services/ai.service'; import type { UrlService } from '@/services/url.service'; import type { Telemetry } from '@/telemetry'; import type { WorkflowFinderService } from '@/workflows/workflow-finder.service'; +import type { AgentChatAttachmentService } from '../agent-chat-attachment.service'; import { AgentConfigService } from '../agent-config.service'; import { AgentCustomToolsService } from '../agent-custom-tools.service'; import { AgentExecutionOrchestratorService } from '../agent-execution-orchestrator.service'; @@ -132,6 +134,7 @@ function makeRuntimeReconstructionService( mock(), mock(), mock(), + mock(), ); } @@ -278,6 +281,7 @@ describe('AgentRuntimeReconstructionService integration tools', () => { mock(), mock(), chatIntegrationRegistry, + mock(), ); agentPublishService = new AgentPublishService( logger, @@ -292,11 +296,12 @@ describe('AgentRuntimeReconstructionService integration tools', () => { credentialsService, telemetry, ); - agentTestChatService = new AgentTestChatService(n8nMemory); + agentTestChatService = new AgentTestChatService(n8nMemory, mock()); agentsService = new AgentsService( logger, agentRepository, projectRelationRepository, + mock(), agentKnowledgeService, runtimeCacheService, agentTestChatService, @@ -351,6 +356,7 @@ describe('AgentRuntimeReconstructionService integration tools', () => { on: vi.fn(), hasCheckpointStorage: vi.fn().mockReturnValue(true), checkpoint: vi.fn(), + fileStore: vi.fn(), }; const reconstructionService = makeRuntimeReconstructionService(); diff --git a/packages/cli/src/modules/agents/__tests__/agent-runtime-reconstruction-tool-filtering.test.ts b/packages/cli/src/modules/agents/__tests__/agent-runtime-reconstruction-tool-filtering.test.ts index af6a44e5bd9..9b7e129792e 100644 --- a/packages/cli/src/modules/agents/__tests__/agent-runtime-reconstruction-tool-filtering.test.ts +++ b/packages/cli/src/modules/agents/__tests__/agent-runtime-reconstruction-tool-filtering.test.ts @@ -22,6 +22,7 @@ import type { AiService } from '@/services/ai.service'; import type { UrlService } from '@/services/url.service'; import type { WorkflowFinderService } from '@/workflows/workflow-finder.service'; +import type { AgentChatAttachmentService } from '../agent-chat-attachment.service'; import { AgentRuntimeReconstructionService } from '../agent-runtime-reconstruction.service'; import type { AgentKnowledgeSandboxService } from '../agent-knowledge-sandbox.service'; import type { Agent } from '../entities/agent.entity'; @@ -138,6 +139,7 @@ function makeService(overrides: { mock(), credentialsFinderService, workflowFinderService, + mock(), ); return { service, credentialsFinderService, workflowFinderService, workflowRepository }; diff --git a/packages/cli/src/modules/agents/__tests__/agent-sse-stream.test.ts b/packages/cli/src/modules/agents/__tests__/agent-sse-stream.test.ts index ef187b98aef..ef54e0695f3 100644 --- a/packages/cli/src/modules/agents/__tests__/agent-sse-stream.test.ts +++ b/packages/cli/src/modules/agents/__tests__/agent-sse-stream.test.ts @@ -131,9 +131,58 @@ describe('agent-sse-stream — stringifyError (via pumpChunks error chunk)', () // null passes the typeof === 'object' branch → JSON.stringify(null) = 'null' expect(events).toEqual([{ type: 'error', message: 'null' }]); }); + + it('surfaces the n8n Connect gateway message from an ai-sdk error responseBody', async () => { + const responseBody = JSON.stringify({ + error: { + message: + "n8n Connect doesn't currently support this operation. Switch to using your own credential to continue.", + type: 'ai_gateway_request_error', + }, + }); + const error = Object.assign(new Error('Bad Request'), { responseBody }); + + const events = await collectEvents([{ type: 'error', error }]); + + expect(events).toEqual([ + { + type: 'error', + message: + "n8n Connect doesn't currently support this operation. Switch to using your own credential to continue.", + }, + ]); + }); + + it('surfaces the gateway message when the ai-sdk error is wrapped in error.cause', async () => { + const responseBody = JSON.stringify({ + error: { message: 'Switch to using your own credential to continue.', type: 'x' }, + }); + const inner = Object.assign(new Error('Bad Request'), { responseBody }); + const wrapped = new Error('Bad Request', { cause: inner }); + + const events = await collectEvents([{ type: 'error', error: wrapped }]); + + expect(events).toEqual([ + { type: 'error', message: 'Switch to using your own credential to continue.' }, + ]); + }); }); describe('agent-sse-stream — stream completion', () => { + it('forwards the reasoning lifecycle with block ids and deltas', async () => { + const events = await collectEvents([ + { type: 'reasoning-start', id: 'reasoning-1' }, + { type: 'reasoning-delta', id: 'reasoning-1', delta: 'Check the inputs.' }, + { type: 'reasoning-end', id: 'reasoning-1' }, + ]); + + expect(events).toEqual([ + { type: 'reasoning-start', id: 'reasoning-1' }, + { type: 'reasoning-delta', id: 'reasoning-1', delta: 'Check the inputs.' }, + { type: 'reasoning-end', id: 'reasoning-1' }, + ]); + }); + it('completes after the runtime stream closes even when a finish chunk is present', async () => { const events = await collectEvents([ { type: 'text-delta', id: 't-1', delta: 'hello' }, @@ -265,3 +314,39 @@ describe('agent-sse-stream — tool execution lifecycle chunks', () => { ]); }); }); + +describe('agent-sse-stream — subagent-chunk', () => { + it('forwards allowlisted subagent-chunk events with parentToolCallId', async () => { + const events = await collectEvents([ + { + type: 'subagent-chunk', + taskName: 'research', + taskPath: '/root/research_0', + parentToolCallId: 'tc-parent', + chunk: { type: 'text-delta', id: 't-1', delta: 'hello' }, + }, + ]); + + expect(events).toEqual([ + { + type: 'subagent-chunk', + parentToolCallId: 'tc-parent', + taskPath: '/root/research_0', + chunk: { type: 'text-delta', id: 't-1', delta: 'hello' }, + }, + ]); + }); + + it('drops subagent-chunk events without parentToolCallId', async () => { + const events = await collectEvents([ + { + type: 'subagent-chunk', + taskName: 'research', + taskPath: '/root/research_0', + chunk: { type: 'text-delta', id: 't-1', delta: 'hello' }, + }, + ]); + + expect(events).toEqual([]); + }); +}); diff --git a/packages/cli/src/modules/agents/__tests__/agent-test-chat.service.test.ts b/packages/cli/src/modules/agents/__tests__/agent-test-chat.service.test.ts index 30b8a66c2f8..d1863590db9 100644 --- a/packages/cli/src/modules/agents/__tests__/agent-test-chat.service.test.ts +++ b/packages/cli/src/modules/agents/__tests__/agent-test-chat.service.test.ts @@ -1,5 +1,6 @@ import { mock } from 'vitest-mock-extended'; +import type { AgentChatAttachmentService } from '../agent-chat-attachment.service'; import { AgentTestChatService, chatThreadId } from '../agent-test-chat.service'; import type { N8nMemory } from '../integrations/n8n-memory'; @@ -10,12 +11,14 @@ type MemoryImplementation = ReturnType; function makeService() { const n8nMemory = mock(); const memory = mock(); + const attachmentService = mock(); n8nMemory.getImplementation.mockReturnValue(memory); return { - service: new AgentTestChatService(n8nMemory), + service: new AgentTestChatService(n8nMemory, attachmentService), n8nMemory, memory, + attachmentService, }; } diff --git a/packages/cli/src/modules/agents/__tests__/agent-validation.service.test.ts b/packages/cli/src/modules/agents/__tests__/agent-validation.service.test.ts index 4decee02d9c..61a1bc0b751 100644 --- a/packages/cli/src/modules/agents/__tests__/agent-validation.service.test.ts +++ b/packages/cli/src/modules/agents/__tests__/agent-validation.service.test.ts @@ -1,9 +1,10 @@ import type { CredentialProvider } from '@n8n/agents'; -import type { AgentJsonConfig } from '@n8n/api-types'; +import { AI_GATEWAY_MANAGED_TAG, type AgentJsonConfig } from '@n8n/api-types'; import type { WorkflowRepository } from '@n8n/db'; import { mock } from 'vitest-mock-extended'; import type { NodeTypes } from '@/node-types'; +import type { AiGatewayService } from '@/services/ai-gateway.service'; import type { AgentSkillsService } from '../agent-skills.service'; import { AgentValidationService } from '../agent-validation.service'; @@ -61,6 +62,8 @@ function makeService() { agentRepository.findByIdsAndProjectId.mockResolvedValue([]); const chatIntegrationRegistry = mock(); chatIntegrationRegistry.get.mockReturnValue(undefined); + const aiGatewayService = mock(); + aiGatewayService.getCredentialTypeForProvider.mockResolvedValue(undefined); return { service: new AgentValidationService( @@ -70,6 +73,7 @@ function makeService() { nodeTypes, workflowRepository, chatIntegrationRegistry, + aiGatewayService, ), agentRepository, agentSkillsService, @@ -78,6 +82,7 @@ function makeService() { nodeTypes, workflowRepository, chatIntegrationRegistry, + aiGatewayService, }; } @@ -164,6 +169,57 @@ describe('AgentValidationService — structured issues', () => { ); }); + it('accepts the n8n Connect managed tag on the main model when the gateway serves the provider, else flags it', async () => { + const { service, agentRepository, aiGatewayService } = makeService(); + agentRepository.findByIdAndProjectId.mockResolvedValue( + makeAgent({ ...runnableConfig, credential: AI_GATEWAY_MANAGED_TAG }), + ); + + // Gateway serves the model's provider → the managed credential is valid. + aiGatewayService.getCredentialTypeForProviderCached.mockReturnValue('openAiApi'); + const served = await service.validateAgentConfiguration( + agentId, + projectId, + makeCredentialProvider([]), + ); + expect(served.issues).not.toEqual( + expect.arrayContaining([expect.objectContaining({ path: 'credential' })]), + ); + + // Cached config present but does not serve the provider → incompatible_credential. + aiGatewayService.getCredentialTypeForProviderCached.mockReturnValue(null); + const unserved = await service.validateAgentConfiguration( + agentId, + projectId, + makeCredentialProvider([]), + ); + expect(unserved.issues).toEqual( + expect.arrayContaining([ + expect.objectContaining({ code: 'incompatible_credential', path: 'credential' }), + ]), + ); + }); + + it('does not flag the managed tag when gateway support cannot be determined (no cached config)', async () => { + const { service, agentRepository, aiGatewayService } = makeService(); + agentRepository.findByIdAndProjectId.mockResolvedValue( + makeAgent({ ...runnableConfig, credential: AI_GATEWAY_MANAGED_TAG }), + ); + // No cached config (e.g. cold start or a throttled/failed fetch) → support is + // unknown. The static validator must fail open rather than block a working agent. + aiGatewayService.getCredentialTypeForProviderCached.mockReturnValue(undefined); + + const result = await service.validateAgentConfiguration( + agentId, + projectId, + makeCredentialProvider([]), + ); + + expect(result.issues).not.toEqual( + expect.arrayContaining([expect.objectContaining({ path: 'credential' })]), + ); + }); + it('flags a node tool missing a required credential slot but accepts one with the slot configured and accessible', async () => { const { service, agentRepository, nodeTypes } = makeService(); nodeTypes.getByNameAndVersion.mockReturnValue({ diff --git a/packages/cli/src/modules/agents/__tests__/agent.repository.test.ts b/packages/cli/src/modules/agents/__tests__/agent.repository.test.ts index df9c16068d5..a0ef21892a5 100644 --- a/packages/cli/src/modules/agents/__tests__/agent.repository.test.ts +++ b/packages/cli/src/modules/agents/__tests__/agent.repository.test.ts @@ -1,5 +1,6 @@ /* eslint-disable @typescript-eslint/unbound-method -- mock-based tests intentionally reference unbound methods */ import type { AgentIntegrationConfig } from '@n8n/api-types'; +import { In } from '@n8n/typeorm'; import { mock } from 'vitest-mock-extended'; import { mockEntityManager } from '@test/mocking'; @@ -41,6 +42,45 @@ describe('AgentRepository', () => { }); }); + describe('findById', () => { + it('calls findOne with the id alone and the activeVersion relation', async () => { + const agent = mock({ id: 'agent-1' }); + vi.spyOn(repository, 'findOne').mockResolvedValue(agent); + + const result = await repository.findById('agent-1'); + + expect(repository.findOne).toHaveBeenCalledWith({ + where: { id: 'agent-1' }, + relations: { activeVersion: true }, + }); + expect(result).toBe(agent); + }); + }); + + describe('findByIdInProjects', () => { + it('returns null without querying when projectIds is empty', async () => { + const findOne = vi.spyOn(repository, 'findOne'); + + const result = await repository.findByIdInProjects('agent-1', []); + + expect(result).toBeNull(); + expect(findOne).not.toHaveBeenCalled(); + }); + + it('constrains the lookup to the given project ids', async () => { + const agent = mock({ id: 'agent-1' }); + vi.spyOn(repository, 'findOne').mockResolvedValue(agent); + + const result = await repository.findByIdInProjects('agent-1', ['project-1', 'project-2']); + + expect(repository.findOne).toHaveBeenCalledWith({ + where: { id: 'agent-1', projectId: In(['project-1', 'project-2']) }, + relations: { activeVersion: true }, + }); + expect(result).toBe(agent); + }); + }); + describe('findByProjectId', () => { it('calls find ordered by updatedAt descending with the activeVersion relation', async () => { const agents = [mock(), mock()]; @@ -65,6 +105,77 @@ describe('AgentRepository', () => { }); }); + describe('findSummariesByProjectIds', () => { + const makeQb = () => ({ + select: vi.fn().mockReturnThis(), + where: vi.fn().mockReturnThis(), + andWhere: vi.fn().mockReturnThis(), + orderBy: vi.fn().mockReturnThis(), + take: vi.fn().mockReturnThis(), + getMany: vi.fn().mockResolvedValue([]), + }); + + it('returns [] without querying when projectIds is empty', async () => { + const createQueryBuilder = vi.spyOn(repository, 'createQueryBuilder'); + + const result = await repository.findSummariesByProjectIds([], { limit: 10 }); + + expect(result).toEqual([]); + expect(createQueryBuilder).not.toHaveBeenCalled(); + }); + + it('selects only summary columns without the activeVersion join', async () => { + const mockQb = makeQb(); + vi.spyOn(repository, 'createQueryBuilder').mockReturnValue(mockQb as never); + + await repository.findSummariesByProjectIds(['project-1']); + + expect(mockQb.select).toHaveBeenCalledWith([ + 'agent.id', + 'agent.name', + 'agent.projectId', + 'agent.activeVersionId', + 'agent.availableInMCP', + 'agent.updatedAt', + ]); + expect(mockQb.where).toHaveBeenCalledWith('agent.projectId IN (:...projectIds)', { + projectIds: ['project-1'], + }); + expect(mockQb.andWhere).not.toHaveBeenCalled(); + expect(mockQb.take).not.toHaveBeenCalled(); + }); + + it('omits the project filter when project access is global', async () => { + const mockQb = makeQb(); + vi.spyOn(repository, 'createQueryBuilder').mockReturnValue(mockQb as never); + + await repository.findSummariesByProjectIds(null); + + expect(mockQb.where).not.toHaveBeenCalled(); + }); + + it('pushes all filters and the limit into the query', async () => { + const mockQb = makeQb(); + vi.spyOn(repository, 'createQueryBuilder').mockReturnValue(mockQb as never); + + await repository.findSummariesByProjectIds(['p1', 'p2'], { + query: 'sales', + publishedOnly: true, + excludeAgentId: 'agent-3', + limit: 10, + }); + + expect(mockQb.andWhere).toHaveBeenCalledWith('LOWER(agent.name) LIKE LOWER(:query)', { + query: '%sales%', + }); + expect(mockQb.andWhere).toHaveBeenCalledWith('agent.activeVersionId IS NOT NULL'); + expect(mockQb.andWhere).toHaveBeenCalledWith('agent.id != :excludeAgentId', { + excludeAgentId: 'agent-3', + }); + expect(mockQb.take).toHaveBeenCalledWith(10); + }); + }); + describe('findByProjectIdsPaginated', () => { it('returns { count: 0, data: [] } immediately when projectIds is empty', async () => { const result = await repository.findByProjectIdsPaginated([], { @@ -103,6 +214,27 @@ describe('AgentRepository', () => { expect(result).toEqual({ count: 1, data: agents }); }); + it('omits the project filter when project access is global', async () => { + const mockQb = { + leftJoinAndSelect: vi.fn().mockReturnThis(), + where: vi.fn().mockReturnThis(), + andWhere: vi.fn().mockReturnThis(), + addSelect: vi.fn().mockReturnThis(), + orderBy: vi.fn().mockReturnThis(), + skip: vi.fn().mockReturnThis(), + take: vi.fn().mockReturnThis(), + getManyAndCount: vi.fn().mockResolvedValue([[], 0]), + }; + vi.spyOn(repository, 'createQueryBuilder').mockReturnValue(mockQb as never); + + await repository.findByProjectIdsPaginated(null, { + skip: 0, + take: 25, + } as never); + + expect(mockQb.where).not.toHaveBeenCalled(); + }); + it('applies the name search filter', async () => { const mockQb = { leftJoinAndSelect: vi.fn().mockReturnThis(), @@ -128,6 +260,18 @@ describe('AgentRepository', () => { }); }); + describe('findMcpAvailabilityCandidates', () => { + it('omits the where clause when all agents are requested', async () => { + const find = vi.spyOn(repository, 'find').mockResolvedValue([]); + + await repository.findMcpAvailabilityCandidates({ all: true }); + + expect(find).toHaveBeenCalledWith({ + select: ['id', 'projectId', 'availableInMCP'], + }); + }); + }); + describe('findByIntegrationCredential', () => { const makeAgent = (id: string, integrations: AgentIntegrationConfig[]) => ({ id, integrations }) as Agent; diff --git a/packages/cli/src/modules/agents/__tests__/agents-builder-tools.service.test.ts b/packages/cli/src/modules/agents/__tests__/agents-builder-tools.service.test.ts index 790c3d3245e..bef51d5f83d 100644 --- a/packages/cli/src/modules/agents/__tests__/agents-builder-tools.service.test.ts +++ b/packages/cli/src/modules/agents/__tests__/agents-builder-tools.service.test.ts @@ -14,12 +14,14 @@ import type { } from '@n8n/backend-network'; import type { SsrfProtectionConfig } from '@n8n/config'; import type { User } from '@n8n/db'; -import { mock } from 'vitest-mock-extended'; import { NodeConnectionTypes } from 'n8n-workflow'; +import { mock } from 'vitest-mock-extended'; import type { CredentialTypes } from '@/credential-types'; import type { McpRegistryService } from '@/modules/mcp-registry/registry/mcp-registry.service'; import type { NodeTypes } from '@/node-types'; +import type { AiGatewayService } from '@/services/ai-gateway.service'; +import type { AiService } from '@/services/ai.service'; import type { DynamicNodeParametersService } from '@/services/dynamic-node-parameters.service'; import type { FreeAiCreditsService } from '@/services/free-ai-credits.service'; import type { Telemetry } from '@/telemetry'; @@ -34,15 +36,12 @@ import type { AgentValidationService } from '../agent-validation.service'; import type { AgentsToolsService } from '../agents-tools.service'; import type { AgentsService } from '../agents.service'; import type { AttachableWorkflowsService } from '../attachable-workflows.service'; -import { - AgentsBuilderToolsService, - getAgentConfigHash, -} from '../builder/agents-builder-tools.service'; +import { AgentsBuilderToolsService } from '../builder/agents-builder-tools.service'; import type { BuilderModelLiveLookupService } from '../builder/builder-model-live-lookup.service'; import { BUILDER_TOOLS } from '../builder/builder-tool-names'; import type { Agent } from '../entities/agent.entity'; import type { AgentSecureRuntime } from '../runtime/agent-secure-runtime'; -import type { AiService } from '@/services/ai.service'; +import { getAgentConfigHash } from '../utils/agent-config-hash'; import * as checkAccess from '@/permissions.ee/check-access'; const ctx = { @@ -116,6 +115,7 @@ function makeService() { agentTaskService, agentPublishService, aiService, + mock(), outboundHttp, dynamicNodeParametersService, nodeTypes, @@ -1430,6 +1430,32 @@ describe('AgentsBuilderToolsService', () => { name: 'seo_analyzer', }); }); + + it('soft-fails on a build error but rethrows when the run was aborted', async () => { + const { service, secureRuntime } = makeService(); + secureRuntime.describeToolSecurely.mockRejectedValue(new Error('compile failed')); + const controller = new AbortController(); + const abortableCtx = { ...ctx, abortSignal: controller.signal }; + + await expect( + getBuildCustomTool(service).handler!({ code: 'broken' }, abortableCtx), + ).resolves.toEqual({ ok: false, errors: [{ message: 'compile failed' }] }); + + // The isolate compiles model-authored code, so an abort-shaped error from + // the generated tool must not be read as a cancellation of the run. + secureRuntime.describeToolSecurely.mockRejectedValue(new Error('Aborted')); + + await expect( + getBuildCustomTool(service).handler!({ code: 'broken' }, abortableCtx), + ).resolves.toEqual({ ok: false, errors: [{ message: 'Aborted' }] }); + + secureRuntime.describeToolSecurely.mockRejectedValue(new Error('compile failed')); + controller.abort(); + + await expect( + getBuildCustomTool(service).handler!({ code: 'broken' }, abortableCtx), + ).rejects.toThrow('compile failed'); + }); }); describe('create_skills tool', () => { diff --git a/packages/cli/src/modules/agents/__tests__/agents-service-reconstruct-gating.test.ts b/packages/cli/src/modules/agents/__tests__/agents-service-reconstruct-gating.test.ts index 569324dbe1c..efb530ffdc8 100644 --- a/packages/cli/src/modules/agents/__tests__/agents-service-reconstruct-gating.test.ts +++ b/packages/cli/src/modules/agents/__tests__/agents-service-reconstruct-gating.test.ts @@ -33,6 +33,7 @@ import type { AiService } from '@/services/ai.service'; import type { UrlService } from '@/services/url.service'; import type { WorkflowFinderService } from '@/workflows/workflow-finder.service'; +import type { AgentChatAttachmentService } from '../agent-chat-attachment.service'; import { AgentRuntimeReconstructionService } from '../agent-runtime-reconstruction.service'; import type { AgentKnowledgeSandboxService } from '../agent-knowledge-sandbox.service'; import type { Agent } from '../entities/agent.entity'; @@ -123,6 +124,7 @@ function makeReconstructionService( mock(), mock(), mock(), + mock(), ); } diff --git a/packages/cli/src/modules/agents/__tests__/agents-tools.service.test.ts b/packages/cli/src/modules/agents/__tests__/agents-tools.service.test.ts index 8360ef94128..1e95cebc71a 100644 --- a/packages/cli/src/modules/agents/__tests__/agents-tools.service.test.ts +++ b/packages/cli/src/modules/agents/__tests__/agents-tools.service.test.ts @@ -1,9 +1,6 @@ import { mock } from 'vitest-mock-extended'; import type { CredentialProvider } from '@n8n/agents'; -import { - AGENT_BUILDER_AVAILABLE_AI_UTILITY_TOOL_NODE_TYPES, - AGENT_BUILDER_HIDDEN_AVAILABLE_TOOL_NODE_TYPES, -} from '@n8n/api-types'; +import { AGENT_BUILDER_HIDDEN_AVAILABLE_TOOL_NODE_TYPES } from '@n8n/api-types'; import type { NodeCatalogService } from '@/node-catalog'; @@ -160,9 +157,7 @@ describe('AgentsToolsService', () => { expect(isAgentToolNodeType('n8n-nodes-base.slackHitlTool')).toBe(false); }); - it('admits whitelisted AI provider nodes (full vendor APIs)', () => { - expect(isAgentToolNodeType('@n8n/n8n-nodes-langchain.openAi')).toBe(true); - expect(isAgentToolNodeType('@n8n/n8n-nodes-langchain.anthropic')).toBe(true); + it('rejects non-provider langchain nodes that are not tool types', () => { // Non-provider langchain nodes stay excluded. expect(isAgentToolNodeType('@n8n/n8n-nodes-langchain.lmChatOpenAi')).toBe(false); expect(isAgentToolNodeType('@n8n/n8n-nodes-langchain.agent')).toBe(false); @@ -174,16 +169,6 @@ describe('AgentsToolsService', () => { } }); - it('rejects the sub-workflow tool node', () => { - expect(isAgentToolNodeType('@n8n/n8n-nodes-langchain.toolWorkflow')).toBe(false); - }); - - it('allows shared AI utility tool node IDs', () => { - for (const nodeType of AGENT_BUILDER_AVAILABLE_AI_UTILITY_TOOL_NODE_TYPES) { - expect(isAgentToolNodeType(nodeType)).toBe(true); - } - }); - it('does not allow MCP tool nodes', () => { expect(isAgentToolNodeType('@n8n/n8n-nodes-langchain.mcpClientTool')).toBe(false); expect(isAgentToolNodeType('@n8n/mcp-registry.notion')).toBe(false); diff --git a/packages/cli/src/modules/agents/__tests__/agents.service.test.ts b/packages/cli/src/modules/agents/__tests__/agents.service.test.ts index 3a08aaadac1..0f8cd7599d5 100644 --- a/packages/cli/src/modules/agents/__tests__/agents.service.test.ts +++ b/packages/cli/src/modules/agents/__tests__/agents.service.test.ts @@ -1,12 +1,13 @@ /* eslint-disable @typescript-eslint/require-await, @typescript-eslint/unbound-method -- async mock stubs, unbound-method references and short `cb` names are acceptable test idioms */ import { mockLogger } from '@n8n/backend-test-utils'; -import type { ProjectRelationRepository } from '@n8n/db'; +import type { ProjectRelationRepository, User } from '@n8n/db'; import { Container } from '@n8n/di'; import { mock } from 'vitest-mock-extended'; import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import type { AgentChatAttachmentService } from '../agent-chat-attachment.service'; import type { AgentKnowledgeService } from '../agent-knowledge.service'; import type { AgentExecutionService } from '../agent-execution.service'; import type { AgentRuntimeCacheService } from '../agent-runtime-cache.service'; @@ -43,6 +44,7 @@ function makeAgent(overrides: Partial = {}): Agent { function makeService() { const agentRepository = mock(); + const projectRelationRepository = mock(); const agentKnowledgeService = mock(); const runtimeCacheService = mock(); const testChatService = mock(); @@ -64,7 +66,8 @@ function makeService() { const service = new AgentsService( mockLogger(), agentRepository, - mock(), + projectRelationRepository, + mock(), agentKnowledgeService, runtimeCacheService, testChatService, @@ -77,6 +80,7 @@ function makeService() { return { service, agentRepository, + projectRelationRepository, agentKnowledgeService, runtimeCacheService, testChatService, @@ -118,6 +122,7 @@ describe('AgentsService', () => { skills: [], }, versionId: expect.any(String), + availableInMCP: false, }); }); @@ -209,6 +214,49 @@ describe('AgentsService', () => { expect(agentRepository.remove).not.toHaveBeenCalled(); }); + describe('findByIdForUser', () => { + const makeUser = (scopeSlugs: string[]): User => + ({ id: 'user-9', role: { scopes: scopeSlugs.map((slug) => ({ slug })) } }) as unknown as User; + + it('looks the agent up directly for users with the global agent:read scope', async () => { + const { service, agentRepository, projectRelationRepository } = makeService(); + const agent = makeAgent(); + agentRepository.findById.mockResolvedValue(agent); + + await expect(service.findByIdForUser(agentId, makeUser(['agent:read']))).resolves.toBe(agent); + + expect(agentRepository.findById).toHaveBeenCalledWith(agentId); + expect(projectRelationRepository.findAllByUser).not.toHaveBeenCalled(); + }); + + it('restricts the lookup to the projects the user belongs to', async () => { + const { service, agentRepository, projectRelationRepository } = makeService(); + const agent = makeAgent(); + projectRelationRepository.findAllByUser.mockResolvedValue([ + { projectId: 'project-1' }, + { projectId: 'project-2' }, + ] as never); + agentRepository.findByIdInProjects.mockResolvedValue(agent); + + await expect(service.findByIdForUser(agentId, makeUser([]))).resolves.toBe(agent); + + expect(projectRelationRepository.findAllByUser).toHaveBeenCalledWith('user-9'); + expect(agentRepository.findByIdInProjects).toHaveBeenCalledWith(agentId, [ + 'project-1', + 'project-2', + ]); + expect(agentRepository.findById).not.toHaveBeenCalled(); + }); + + it('returns null when the agent is outside the user’s projects', async () => { + const { service, agentRepository, projectRelationRepository } = makeService(); + projectRelationRepository.findAllByUser.mockResolvedValue([]); + agentRepository.findByIdInProjects.mockResolvedValue(null); + + await expect(service.findByIdForUser(agentId, makeUser([]))).resolves.toBeNull(); + }); + }); + describe('getCapabilitySummary', () => { it('projects model, channels, tools, skills and tasks into per-item labels', async () => { const { service, agentRepository, agentTaskRepository } = makeService(); diff --git a/packages/cli/src/modules/agents/__tests__/execution-recorder.test.ts b/packages/cli/src/modules/agents/__tests__/execution-recorder.test.ts index fe91b6a39f1..f2486e952ae 100644 --- a/packages/cli/src/modules/agents/__tests__/execution-recorder.test.ts +++ b/packages/cli/src/modules/agents/__tests__/execution-recorder.test.ts @@ -89,6 +89,64 @@ describe('ExecutionRecorder', () => { }); describe('timeline ordering', () => { + afterEach(() => { + vi.useRealTimers(); + }); + + it('records reasoning with timing without adding it to the assistant response', () => { + vi.useFakeTimers(); + vi.setSystemTime(1_000); + const recorder = new ExecutionRecorder(); + + recorder.record({ type: 'reasoning-start', id: 'r1' }); + recorder.record({ type: 'reasoning-delta', id: 'r1', delta: 'Check the inputs. ' }); + vi.setSystemTime(1_500); + recorder.record({ type: 'reasoning-delta', id: 'r1', delta: 'Then answer.' }); + vi.setSystemTime(2_000); + recorder.record({ type: 'reasoning-end', id: 'r1' }); + recorder.record({ type: 'text-delta', id: 't1', delta: 'Done' }); + recorder.record({ type: 'finish', finishReason: 'stop' } as StreamChunk); + + const record = recorder.getMessageRecord(); + + expect(record.timeline).toEqual([ + { + type: 'reasoning', + content: 'Check the inputs. Then answer.', + timestamp: 1_000, + endTime: 2_000, + }, + { type: 'text', content: 'Done', timestamp: 2_000, endTime: 2_000 }, + ]); + expect(record.assistantResponse).toBe('Done'); + }); + + it('keeps partial reasoning when the stream errors', () => { + vi.useFakeTimers(); + vi.setSystemTime(1_000); + const recorder = new ExecutionRecorder(); + + recorder.record({ type: 'reasoning-start', id: 'r1' }); + recorder.record({ type: 'reasoning-delta', id: 'r1', delta: 'Partial analysis' }); + vi.setSystemTime(2_000); + recorder.record({ type: 'error', error: new Error('Stream failed') }); + + expect(recorder.getMessageRecord()).toEqual( + expect.objectContaining({ + assistantResponse: '', + error: 'Stream failed', + timeline: [ + { + type: 'reasoning', + content: 'Partial analysis', + timestamp: 1_000, + endTime: 2_000, + }, + ], + }), + ); + }); + it('captures text → tool call → text in order', () => { const recorder = new ExecutionRecorder(); @@ -773,3 +831,148 @@ describe('ExecutionRecorder — node-tool {{$json.x}} resolution', () => { }); }); }); + +describe('ExecutionRecorder — subagent-chunk', () => { + it('attaches forwarded child chunks to the open delegate tool-call entry', () => { + const recorder = new ExecutionRecorder(); + recorder.record(makeToolCallChunk('delegate_subagent', { goal: 'x' }, 'tc-parent')); + recorder.record({ + type: 'subagent-chunk', + taskName: 'research', + taskPath: '/root/research_0', + parentToolCallId: 'tc-parent', + chunk: { type: 'text-delta', id: 't-1', delta: 'hello' }, + } as StreamChunk); + recorder.record({ + type: 'subagent-chunk', + taskName: 'research', + taskPath: '/root/research_0', + parentToolCallId: 'tc-parent', + chunk: { + type: 'tool-input-start', + toolCallId: 'child-tc-1', + toolName: 'web_search', + }, + } as StreamChunk); + recorder.record({ + type: 'tool-execution-end', + toolCallId: 'tc-parent', + toolName: 'delegate_subagent', + isError: false, + endTime: 2_000, + }); + recorder.record(makeToolResultChunk('delegate_subagent', { status: 'completed' }, 'tc-parent')); + + const entry = recorder + .getMessageRecord() + .timeline.find((e) => e.type === 'tool-call' && e.toolCallId === 'tc-parent'); + expect(entry).toMatchObject({ + childTrace: { + text: 'hello', + steps: [{ toolCallId: 'child-tc-1', toolName: 'web_search', running: false }], + }, + }); + }); + + it('caps persisted child text at 4000 characters, trimming a delta that straddles it', () => { + const recorder = new ExecutionRecorder(); + recorder.record(makeToolCallChunk('delegate_subagent', {}, 'tc-parent')); + // Neither delta lands on the boundary, so the second must be trimmed + // rather than persisted whole. + recorder.record({ + type: 'subagent-chunk', + taskName: 'research', + taskPath: '/root/research_0', + parentToolCallId: 'tc-parent', + chunk: { type: 'text-delta', id: 't-1', delta: 'a'.repeat(3_000) }, + } as StreamChunk); + recorder.record({ + type: 'subagent-chunk', + taskName: 'research', + taskPath: '/root/research_0', + parentToolCallId: 'tc-parent', + chunk: { type: 'text-delta', id: 't-1', delta: 'b'.repeat(3_000) }, + } as StreamChunk); + recorder.record({ + type: 'subagent-chunk', + taskName: 'research', + taskPath: '/root/research_0', + parentToolCallId: 'tc-parent', + chunk: { type: 'text-delta', id: 't-1', delta: 'over budget' }, + } as StreamChunk); + recorder.record({ + type: 'subagent-chunk', + taskName: 'research', + taskPath: '/root/research_0', + parentToolCallId: 'tc-parent', + chunk: { + type: 'tool-input-start', + toolCallId: 'child-tc-1', + toolName: 'web_search', + }, + } as StreamChunk); + recorder.record({ + type: 'subagent-chunk', + taskName: 'research', + taskPath: '/root/research_0', + parentToolCallId: 'tc-parent', + chunk: { + type: 'tool-execution-end', + toolCallId: 'child-tc-1', + toolName: 'web_search', + isError: false, + endTime: 1, + }, + } as StreamChunk); + + const entry = recorder + .getMessageRecord() + .timeline.find((e) => e.type === 'tool-call' && e.toolCallId === 'tc-parent'); + expect(entry?.type).toBe('tool-call'); + if (entry?.type !== 'tool-call') return; + expect(entry.childTrace?.text).toHaveLength(4_000); + expect(entry.childTrace?.steps).toEqual([ + { toolCallId: 'child-tc-1', toolName: 'web_search', running: false }, + ]); + }); + + it('settles child steps when the delegation closes via tool-result alone', () => { + const recorder = new ExecutionRecorder(); + recorder.record(makeToolCallChunk('delegate_subagent', {}, 'tc-parent')); + recorder.record({ + type: 'subagent-chunk', + taskName: 'research', + taskPath: '/root/research_0', + parentToolCallId: 'tc-parent', + chunk: { + type: 'tool-input-start', + toolCallId: 'child-tc-1', + toolName: 'web_search', + }, + } as StreamChunk); + // No tool-execution-end for the parent — only the batched tool-result. + recorder.record(makeToolResultChunk('delegate_subagent', { status: 'completed' }, 'tc-parent')); + + const entry = recorder + .getMessageRecord() + .timeline.find((e) => e.type === 'tool-call' && e.toolCallId === 'tc-parent'); + expect(entry?.type).toBe('tool-call'); + if (entry?.type !== 'tool-call') return; + expect(entry.childTrace?.steps).toEqual([ + { toolCallId: 'child-tc-1', toolName: 'web_search', running: false }, + ]); + }); + + it('ignores a subagent-chunk with no matching open tool-call entry', () => { + const recorder = new ExecutionRecorder(); + recorder.record({ + type: 'subagent-chunk', + taskName: 'research', + taskPath: '/root/research_0', + parentToolCallId: 'missing', + chunk: { type: 'text-delta', id: 't-1', delta: 'orphan' }, + } as StreamChunk); + + expect(recorder.getMessageRecord().timeline).toEqual([]); + }); +}); diff --git a/packages/cli/src/modules/agents/__tests__/execution-to-message-mapper.test.ts b/packages/cli/src/modules/agents/__tests__/execution-to-message-mapper.test.ts index 744aac084e1..9c94cf8fa93 100644 --- a/packages/cli/src/modules/agents/__tests__/execution-to-message-mapper.test.ts +++ b/packages/cli/src/modules/agents/__tests__/execution-to-message-mapper.test.ts @@ -14,6 +14,72 @@ function execution(overrides: Partial = {}): AgentExecution { } describe('execution-to-message-mapper', () => { + it('maps reasoning timeline events with timing into assistant message content', () => { + const result = executionToMessagesDto( + execution({ + timeline: [ + { + type: 'reasoning', + content: 'Check the inputs.', + timestamp: 100, + endTime: 150, + }, + { type: 'text', content: 'Done.', timestamp: 151, endTime: 160 }, + ], + }), + ); + + expect(result[1]?.content).toEqual([ + { + type: 'reasoning', + text: 'Check the inputs.', + startTime: 100, + endTime: 150, + }, + { type: 'text', text: 'Done.' }, + ]); + }); + + it('carries childTrace onto the persisted tool-call content part', () => { + const childTrace = { + text: 'child said this', + reasoningSegments: [{ id: 'r-1', content: 'thinking' }], + steps: [{ toolCallId: 'child-tc-1', toolName: 'web_search', running: false }], + }; + const result = executionToMessagesDto( + execution({ + timeline: [ + { + type: 'tool-call', + kind: 'tool', + name: 'delegate_subagent', + toolCallId: 'tc-parent', + input: { goal: 'x' }, + output: { status: 'completed', answer: 'done' }, + startTime: 100, + endTime: 200, + success: true, + childTrace, + }, + ], + }), + ); + + expect(result[1]?.content).toEqual([ + { + type: 'tool-call', + toolName: 'delegate_subagent', + toolCallId: 'tc-parent', + input: { goal: 'x' }, + startTime: 100, + endTime: 200, + state: 'resolved', + output: { status: 'completed', answer: 'done' }, + childTrace, + }, + ]); + }); + it('maps execution timeline text and tool calls into assistant message content', () => { const result = executionToMessagesDto( execution({ @@ -112,6 +178,78 @@ describe('execution-to-message-mapper', () => { ]); }); + it('includes attachment file parts on the user message', () => { + const result = executionToMessagesDto( + execution({ + attachments: [{ id: 'att-1', fileName: 'photo.png', mimeType: 'image/png', sizeBytes: 33 }], + }), + ); + + expect(result[0]).toEqual({ + id: 'execution-1:user', + role: 'user', + content: [ + { type: 'text', text: 'Hello' }, + { + type: 'file', + fileId: 'att-1', + fileName: 'photo.png', + mimeType: 'image/png', + sizeBytes: 33, + }, + ], + executionId: 'execution-1', + }); + }); + + it('emits a user message for attachment-only turns without text', () => { + const result = executionToMessagesDto( + execution({ + userMessage: null, + attachments: [ + { id: 'att-1', fileName: 'voice.ogg', mimeType: 'audio/ogg', sizeBytes: 100 }, + ], + }), + ); + + expect(result[0].role).toBe('user'); + expect(result[0].content).toEqual([ + { + type: 'file', + fileId: 'att-1', + fileName: 'voice.ogg', + mimeType: 'audio/ogg', + sizeBytes: 100, + }, + ]); + }); + + it('includes the execution outcome on assistant messages', () => { + const result = executionToMessagesDto( + execution({ + status: 'error', + timeline: [ + { + type: 'tool-call', + kind: 'tool', + name: 'slow_tool', + toolCallId: 'call-1', + input: {}, + output: undefined, + startTime: 100, + endTime: 0, + success: false, + }, + ], + }), + ); + + expect(result[1]).toMatchObject({ + role: 'assistant', + executionStatus: 'error', + }); + }); + it('flattens multiple executions into a single message list', () => { const result = executionsToMessagesDto([ execution({ diff --git a/packages/cli/src/modules/agents/__tests__/from-json-config.test.ts b/packages/cli/src/modules/agents/__tests__/from-json-config.test.ts index 27b690ef4b6..9290792832c 100644 --- a/packages/cli/src/modules/agents/__tests__/from-json-config.test.ts +++ b/packages/cli/src/modules/agents/__tests__/from-json-config.test.ts @@ -564,9 +564,9 @@ describe('buildFromJson()', () => { expect(agent.snapshot.tools.some((t) => t.name === 'Test Workflow')).toBe(true); }); - it('sets thinking config', async () => { + it('sets generic reasoning effort', async () => { const config = makeConfig({ - config: { thinking: { provider: 'anthropic', budgetTokens: 5000 } }, + config: { reasoning: 'high' }, }); const agent = await buildFromJson( @@ -580,8 +580,7 @@ describe('buildFromJson()', () => { ); const snap: AgentSnapshot = agent.snapshot; - expect(snap.thinking).not.toBeNull(); - expect(snap.thinking).toMatchObject({ budgetTokens: 5000 }); + expect(snap.reasoning).toBe('high'); }); it('sets prompt caching config with an Anthropic ttl', async () => { diff --git a/packages/cli/src/modules/agents/__tests__/instance-ai-builder-delegate.adapter.test.ts b/packages/cli/src/modules/agents/__tests__/instance-ai-builder-delegate.adapter.test.ts index 79d78aa0a3e..bd27c4c5faa 100644 --- a/packages/cli/src/modules/agents/__tests__/instance-ai-builder-delegate.adapter.test.ts +++ b/packages/cli/src/modules/agents/__tests__/instance-ai-builder-delegate.adapter.test.ts @@ -55,6 +55,8 @@ async function* asAsyncGenerator(values: T[]): AsyncGenerator { for (const value of values) yield value; } +const abortSignal = new AbortController().signal; + describe('InstanceAiBuilderDelegateAdapterService', () => { afterEach(() => { vi.restoreAllMocks(); @@ -77,6 +79,7 @@ describe('InstanceAiBuilderDelegateAdapterService', () => { hostThreadId: 'thread-1', runId: 'run-1', modelConfig: 'anthropic/claude-sonnet-host-resolved', + abortSignal, }); const seen: unknown[] = []; @@ -97,6 +100,7 @@ describe('InstanceAiBuilderDelegateAdapterService', () => { hostThreadId: 'thread-1', runId: 'run-1', modelConfig: 'anthropic/claude-sonnet-host-resolved', + abortSignal, telemetry: sentinel, }); @@ -111,6 +115,7 @@ describe('InstanceAiBuilderDelegateAdapterService', () => { hostThreadId: 'thread-1', runId: 'run-1', modelConfig: 'anthropic/claude-sonnet-host-resolved', + abortSignal, instructionsAddendum: INSTANCE_AI_BUILDER_ADDENDUM, telemetry: sentinel, }, @@ -127,6 +132,7 @@ describe('InstanceAiBuilderDelegateAdapterService', () => { hostThreadId: 'thread-1', runId: 'run-1', modelConfig: 'anthropic/claude-sonnet-host-resolved', + abortSignal, }); const [, , , , , sessionArg] = agentsBuilderService.buildAgent.mock.calls[0]; @@ -143,6 +149,7 @@ describe('InstanceAiBuilderDelegateAdapterService', () => { hostThreadId: 'thread-1', runId: 'run-1', modelConfig: 'anthropic/claude-sonnet-host-resolved', + abortSignal, }), ).rejects.toThrow(ForbiddenError); expect(agentsBuilderService.buildAgent).not.toHaveBeenCalled(); @@ -168,6 +175,7 @@ describe('InstanceAiBuilderDelegateAdapterService', () => { hostThreadId: 'thread-1', runId: 'run-1', modelConfig: 'anthropic/claude-sonnet-host-resolved', + abortSignal, }, ); @@ -189,6 +197,7 @@ describe('InstanceAiBuilderDelegateAdapterService', () => { hostThreadId: 'thread-1', runId: 'run-1', modelConfig: 'anthropic/claude-sonnet-host-resolved', + abortSignal, instructionsAddendum: INSTANCE_AI_BUILDER_ADDENDUM, }, ); @@ -207,6 +216,7 @@ describe('InstanceAiBuilderDelegateAdapterService', () => { hostThreadId: 'thread-1', runId: 'run-1', modelConfig: 'anthropic/claude-sonnet-host-resolved', + abortSignal, }, ), ).rejects.toThrow(ForbiddenError); @@ -256,6 +266,7 @@ describe('InstanceAiBuilderDelegateAdapterService', () => { hostThreadId: 'thread-1', runId: 'run-1', modelConfig: 'anthropic/claude-sonnet-host-resolved', + abortSignal, }); expect(agentsBuilderService.findOpenCheckpointForThread).toHaveBeenCalledWith( @@ -278,6 +289,7 @@ describe('InstanceAiBuilderDelegateAdapterService', () => { hostThreadId: 'thread-1', runId: 'run-1', modelConfig: 'anthropic/claude-sonnet-host-resolved', + abortSignal, }); expect(result).toEqual([]); @@ -297,6 +309,7 @@ describe('InstanceAiBuilderDelegateAdapterService', () => { hostThreadId: 'thread-1', runId: 'run-1', modelConfig: 'anthropic/claude-sonnet-host-resolved', + abortSignal, }); expect(result).toEqual([]); @@ -312,6 +325,7 @@ describe('InstanceAiBuilderDelegateAdapterService', () => { hostThreadId: 'thread-1', runId: 'run-1', modelConfig: 'anthropic/claude-sonnet-host-resolved', + abortSignal, }), ).rejects.toThrow(ForbiddenError); expect(agentsBuilderService.findOpenCheckpointForThread).not.toHaveBeenCalled(); diff --git a/packages/cli/src/modules/agents/adapters/__tests__/agents-credential-provider.test.ts b/packages/cli/src/modules/agents/adapters/__tests__/agents-credential-provider.test.ts new file mode 100644 index 00000000000..d23fdf43a09 --- /dev/null +++ b/packages/cli/src/modules/agents/adapters/__tests__/agents-credential-provider.test.ts @@ -0,0 +1,51 @@ +import type { User } from '@n8n/db'; +import { Container } from '@n8n/di'; +import { mock } from 'vitest-mock-extended'; + +import type { CredentialsService } from '@/credentials/credentials.service'; +import { AiGatewayService } from '@/services/ai-gateway.service'; + +import { AgentsCredentialProvider } from '../agents-credential-provider'; + +describe('AgentsCredentialProvider', () => { + describe('resolveAiGatewayModelCredential', () => { + const projectId = 'proj-1'; + const user = { id: 'user-1' } as User; + + afterEach(() => { + vi.restoreAllMocks(); + }); + + it('mints the gateway synthetic credential for a supported provider', async () => { + const aiGatewayService = mock(); + aiGatewayService.getCredentialTypeForProvider.mockResolvedValue('openAiApi'); + aiGatewayService.getSyntheticCredential.mockResolvedValue({ + apiKey: 'jwt', + url: 'https://gw/openai/v1', + }); + Container.set(AiGatewayService, aiGatewayService); + + const provider = new AgentsCredentialProvider(mock(), projectId, user); + const result = await provider.resolveAiGatewayModelCredential('openai'); + + expect(aiGatewayService.getCredentialTypeForProvider).toHaveBeenCalledWith('openai'); + expect(aiGatewayService.getSyntheticCredential).toHaveBeenCalledWith({ + credentialType: 'openAiApi', + userId: 'user-1', + projectId, + }); + expect(result).toEqual({ apiKey: 'jwt', url: 'https://gw/openai/v1' }); + }); + + it('throws when the gateway does not serve the provider', async () => { + const aiGatewayService = mock(); + aiGatewayService.getCredentialTypeForProvider.mockResolvedValue(undefined); + Container.set(AiGatewayService, aiGatewayService); + + const provider = new AgentsCredentialProvider(mock(), projectId); + + await expect(provider.resolveAiGatewayModelCredential('xai')).rejects.toThrow(); + expect(aiGatewayService.getSyntheticCredential).not.toHaveBeenCalled(); + }); + }); +}); diff --git a/packages/cli/src/modules/agents/adapters/agents-credential-provider.ts b/packages/cli/src/modules/agents/adapters/agents-credential-provider.ts index 5afd211c765..55688350aef 100644 --- a/packages/cli/src/modules/agents/adapters/agents-credential-provider.ts +++ b/packages/cli/src/modules/agents/adapters/agents-credential-provider.ts @@ -1,7 +1,12 @@ import type { CredentialProvider, ResolvedCredential, CredentialListItem } from '@n8n/agents'; import type { CredentialsEntity, User } from '@n8n/db'; +import { Container } from '@n8n/di'; +import { UserError } from 'n8n-workflow'; import type { CredentialsService } from '@/credentials/credentials.service'; +import { AiGatewayService } from '@/services/ai-gateway.service'; + +import type { AiGatewayModelCredentialResolver } from '../json-config/model-config'; function toResolvedCredential(data: unknown): ResolvedCredential { const resolved = data !== null && typeof data === 'object' && !Array.isArray(data) ? data : {}; @@ -18,13 +23,35 @@ function toResolvedCredential(data: unknown): ResolvedCredential { * follows the same credential set as the workflow editor for that project. * Published runtime execution can omit the user and stays project-scoped. */ -export class AgentsCredentialProvider implements CredentialProvider { +export class AgentsCredentialProvider + implements CredentialProvider, AiGatewayModelCredentialResolver +{ constructor( private readonly credentialsService: CredentialsService, private readonly projectId: string, private readonly user?: User, ) {} + /** + * Mint the n8n Connect (AI Gateway) synthetic credential for a model slot + * marked with `AI_GATEWAY_MANAGED_TAG`, keyed by the model's provider prefix + * (e.g. `openai`). The provider → credential-type mapping and support check + * live in `AiGatewayService`, resolved lazily so no gateway wiring leaks into + * this provider's construction sites. + */ + async resolveAiGatewayModelCredential(provider: string): Promise { + const aiGatewayService = Container.get(AiGatewayService); + const credentialType = await aiGatewayService.getCredentialTypeForProvider(provider); + if (!credentialType) { + throw new UserError(`n8n credits does not support the "${provider}" model provider.`); + } + return await aiGatewayService.getSyntheticCredential({ + credentialType, + userId: this.user?.id, + projectId: this.projectId, + }); + } + /** * Resolve a credential by ID, then decrypt and return the raw data. * diff --git a/packages/cli/src/modules/agents/agent-chat-attachment.service.ts b/packages/cli/src/modules/agents/agent-chat-attachment.service.ts new file mode 100644 index 00000000000..ae0e3118ab8 --- /dev/null +++ b/packages/cli/src/modules/agents/agent-chat-attachment.service.ts @@ -0,0 +1,214 @@ +import { isAttachmentMediaTypeSupported } from '@n8n/api-types'; +import { Logger } from '@n8n/backend-common'; +import type { SourceType } from '@n8n/db'; +import { Service } from '@n8n/di'; +import { generateNanoId } from '@n8n/utils/generate-nano-id'; +import type { BuiltFileStore, ContentFileRef } from '@n8n/agents'; +import { BinaryDataService, FileLocation, FileNotFoundError } from 'n8n-core'; +import { OperationalError, type IBinaryData } from 'n8n-workflow'; +import type { Readable } from 'node:stream'; + +import { AgentChatAttachment } from './entities/agent-chat-attachment.entity'; +import { AgentChatAttachmentRepository } from './repositories/agent-chat-attachment.repository'; + +// Typed against `SourceType` so a drift from the `binary_data` schema enum +// (see `packages/@n8n/db/src/entities/binary-data-file.ts`) is a compile error. +const ATTACHMENT_SOURCE_TYPE: SourceType = 'agent_chat_attachment'; + +export interface StoreInboundAttachmentParams { + /** Null for inline agents, which have no `agents` row. */ + agentId: string | null; + projectId: string; + threadId: string; + resourceId?: string; + /** Surface the file arrived from, e.g. 'chat', 'slack', 'telegram'. */ + source: string; + fileName: string; + mimeType: string; + data: Buffer; +} + +/** Reference passed from ingestion (controller/bridge) to the orchestrator. */ +export interface StoredAttachmentRef { + id: string; + fileName: string; + mimeType: string; + sizeBytes: number; +} + +/** + * One directory per attachment, so deleting a single file never touches + * others. Inline agents share the `agents/inline/` directory (a real agent id + * is a 16-char nano ID, so `inline` cannot collide). + */ +function buildAttachmentFileLocation(agentId: string | null, attachmentId: string) { + return FileLocation.ofCustom({ + pathSegments: ['agents', agentId ?? 'inline', 'attachments', attachmentId], + sourceType: ATTACHMENT_SOURCE_TYPE, + sourceId: attachmentId, + }); +} + +/** + * Stores and resolves files users attach to agent conversations. Bytes live in + * `BinaryDataService`; rows in `agent_chat_attachments` carry the metadata and + * the authorization scope (project + thread). Message content parts reference + * attachments by row id (`ContentFileRef.id`), resolved back to bytes through + * the `BuiltFileStore` adapter injected into the agent runtime. + */ +@Service() +export class AgentChatAttachmentService { + constructor( + private readonly logger: Logger, + private readonly binaryDataService: BinaryDataService, + private readonly repository: AgentChatAttachmentRepository, + ) {} + + async storeInbound(params: StoreInboundAttachmentParams): Promise { + const attachmentId = generateNanoId(); + + const binaryData: IBinaryData = { + data: '', + mimeType: params.mimeType, + fileName: params.fileName, + }; + const stored = await this.binaryDataService.store( + buildAttachmentFileLocation(params.agentId, attachmentId), + params.data, + binaryData, + ); + if (!stored.id) { + throw new OperationalError( + 'Agent chat attachments require a persisted binary data storage mode', + ); + } + + try { + const attachment = this.repository.create({ + id: attachmentId, + agentId: params.agentId, + projectId: params.projectId, + threadId: params.threadId, + resourceId: params.resourceId ?? null, + binaryDataId: stored.id, + fileName: params.fileName, + mimeType: params.mimeType, + fileSizeBytes: params.data.byteLength, + source: params.source, + }); + return await this.repository.save(attachment); + } catch (error) { + await this.binaryDataService + .deleteManyByBinaryDataId([stored.id]) + .catch((cleanupError: unknown) => + this.logger.warn('Failed to delete agent chat attachment bytes', { + binaryDataId: stored.id, + error: cleanupError, + }), + ); + throw error; + } + } + + /** Metadata lookup scoped to the owning agent + project; null when out of scope. */ + async getForAgent( + attachmentId: string, + scope: { agentId: string; projectId: string }, + ): Promise { + return await this.repository.findByIdForAgent(attachmentId, scope); + } + + async getStream(attachment: AgentChatAttachment): Promise { + return await this.binaryDataService.getAsStream(attachment.binaryDataId); + } + + /** + * Compensating cleanup for a turn that failed before anything referenced its + * attachments — removes only the given rows, never the rest of the thread. + */ + async deleteByIds(attachmentIds: string[]): Promise { + if (attachmentIds.length === 0) return; + await this.deleteAttachments(await this.repository.findByIds(attachmentIds), { + attachmentIds: attachmentIds.join(','), + }); + } + + /** `scope` carries the caller's authorization (project or agent) and drives index use. */ + async deleteByThread( + threadId: string, + scope: { projectId: string } | { agentId: string }, + ): Promise { + await this.deleteAttachments(await this.repository.findByThread(threadId, scope), { + threadId, + }); + } + + /** Called before the agent row is removed, so bytes don't outlive the cascading rows. */ + async deleteByAgent(agentId: string): Promise { + await this.deleteAttachments(await this.repository.findBy({ agentId }), { agentId }); + } + + private async deleteAttachments( + attachments: AgentChatAttachment[], + logContext: Record, + ): Promise { + if (attachments.length === 0) return; + + await this.repository.delete(attachments.map((attachment) => attachment.id)); + await this.binaryDataService + .deleteManyByBinaryDataId(attachments.map((attachment) => attachment.binaryDataId)) + .catch((error: unknown) => + this.logger.warn('Failed to delete agent chat attachment bytes', { + ...logContext, + error, + }), + ); + } + + /** + * `BuiltFileStore` for an agent runtime. Lookups are scoped to the run's + * thread (+ project) when the runtime provides one, so a fileId from + * another conversation resolves to nothing; runs without a thread fall + * back to the agent + project scope. `provider` drives capability gating + * of hydration. + */ + getFileStore(scope: { agentId: string; projectId: string }, provider: string): BuiltFileStore { + return { + load: async ( + ref: ContentFileRef, + runScope?: { threadId?: string }, + ): Promise => { + const attachment = runScope?.threadId + ? await this.repository.findByIdInThread(ref.id, { + projectId: scope.projectId, + threadId: runScope.threadId, + }) + : await this.repository.findByIdForAgent(ref.id, scope); + if (!attachment) return null; + try { + return await this.binaryDataService.getAsBuffer({ + id: attachment.binaryDataId, + data: '', + mimeType: attachment.mimeType, + }); + } catch (error) { + // Missing bytes are an expected lifecycle state (pruned or expired + // storage) and recur on every turn of the thread, so they don't + // warrant a warning; anything else is a real storage failure. + if (error instanceof FileNotFoundError) { + this.logger.debug('Agent chat attachment bytes no longer in storage', { + attachmentId: ref.id, + }); + } else { + this.logger.warn('Failed to load agent chat attachment bytes', { + attachmentId: ref.id, + error, + }); + } + return null; + } + }, + isMediaTypeSupported: (mediaType) => isAttachmentMediaTypeSupported(provider, mediaType), + }; + } +} diff --git a/packages/cli/src/modules/agents/agent-chat.controller.ts b/packages/cli/src/modules/agents/agent-chat.controller.ts index efab0d247c1..3641073d3f9 100644 --- a/packages/cli/src/modules/agents/agent-chat.controller.ts +++ b/packages/cli/src/modules/agents/agent-chat.controller.ts @@ -1,17 +1,30 @@ import { + type AgentChatAttachmentPayload, AgentChatMessageDto, type AgentChatMessagesResponse, AgentChatResumeDto, + MAX_AGENT_CHAT_ATTACHMENT_SIZE_BYTES, + MAX_AGENT_CHAT_ATTACHMENT_SIZE_MB, N8N_CHAT_INTEGRATION_TYPE, + ViewableMimeTypes, } from '@n8n/api-types'; import type { AuthenticatedRequest } from '@n8n/db'; import { Body, Delete, Get, Param, Post, ProjectScope, RestController } from '@n8n/decorators'; +import { sanitizeFilename } from '@n8n/utils/files/sanitize-filename'; import { randomUUID } from 'crypto'; +import type { Response } from 'express'; +import { FileNotFoundError, getHtmlSandboxCSP } from 'n8n-core'; +import { pipeline } from 'node:stream/promises'; import { CredentialsService } from '@/credentials/credentials.service'; +import { BadRequestError } from '@/errors/response-errors/bad-request.error'; import { NotFoundError } from '@/errors/response-errors/not-found.error'; import { AgentsCredentialProvider } from './adapters/agents-credential-provider'; +import { + AgentChatAttachmentService, + type StoredAttachmentRef, +} from './agent-chat-attachment.service'; import { AgentExecutionOrchestratorService } from './agent-execution-orchestrator.service'; import { AgentExecutionService, threadBelongsTo } from './agent-execution.service'; import { messagesToDto } from './agent-message-mapper'; @@ -21,6 +34,7 @@ import { AgentValidationService } from './agent-validation.service'; import { AgentsService } from './agents.service'; import { AgentsBuilderService } from './builder/agents-builder.service'; import { draftChatMemoryResourceId } from './utils/agent-memory-scope'; +import { resolveInboundMimeType } from './utils/inbound-attachments'; import { withOpenSuspensions } from './utils/messages-envelope'; @RestController('/projects/:projectId/agents/v2') @@ -33,8 +47,59 @@ export class AgentChatController { private readonly credentialsService: CredentialsService, private readonly agentExecutionService: AgentExecutionService, private readonly agentsService: AgentsService, + private readonly agentChatAttachmentService: AgentChatAttachmentService, ) {} + /** Decode, sniff, and persist inbound chat attachments; returns refs for the user turn. */ + private async storeChatAttachments(params: { + attachments: AgentChatAttachmentPayload[] | undefined; + agentId: string; + projectId: string; + threadId: string; + resourceId: string; + }): Promise { + const { attachments, agentId, projectId, threadId, resourceId } = params; + if (!attachments?.length) return undefined; + + const stored: StoredAttachmentRef[] = []; + try { + for (const attachment of attachments) { + const data = Buffer.from(attachment.data, 'base64'); + if (data.byteLength === 0) { + throw new BadRequestError(`Attachment "${attachment.fileName}" is empty`); + } + if (data.byteLength > MAX_AGENT_CHAT_ATTACHMENT_SIZE_BYTES) { + throw new BadRequestError( + `Attachment "${attachment.fileName}" exceeds the ${MAX_AGENT_CHAT_ATTACHMENT_SIZE_MB} MB limit`, + ); + } + + const mimeType = await resolveInboundMimeType(attachment.mimeType, data); + const row = await this.agentChatAttachmentService.storeInbound({ + agentId, + projectId, + threadId, + resourceId, + source: 'chat', + fileName: attachment.fileName, + mimeType, + data, + }); + stored.push({ + id: row.id, + fileName: row.fileName, + mimeType: row.mimeType, + sizeBytes: row.fileSizeBytes, + }); + } + } catch (error) { + // Nothing references the already-stored attachments of a rejected message. + await this.agentChatAttachmentService.deleteByIds(stored.map((ref) => ref.id)); + throw error; + } + return stored; + } + @Post('/:agentId/chat', { usesTemplates: true }) @ProjectScope('agent:execute') async chat( @@ -44,7 +109,8 @@ export class AgentChatController { @Body payload: AgentChatMessageDto, ) { const { projectId } = req.params; - const { message, sessionId } = payload; + // The text-or-attachment invariant is enforced by the DTO schema. + const { message, sessionId, attachments } = payload; const credentialProvider = new AgentsCredentialProvider( this.credentialsService, @@ -53,45 +119,56 @@ export class AgentChatController { ); const { send } = initSseStream(res); + const abortController = new AbortController(); + const abortOnClose = () => abortController.abort(); + res.once('close', abortOnClose); + let executionId: string | undefined; + let storedAttachments: StoredAttachmentRef[] | undefined; + try { + // If the client supplied a sessionId and a thread already exists under that id, + // the thread must belong to this (project, agent). Otherwise a caller could + // append messages to another user's thread. A non-existent id is fine - + // executeForChat will create the thread on first persisted message. + if (sessionId) { + const existing = await this.agentExecutionService.findThreadById(sessionId); + if (abortController.signal.aborted) return; + if (existing && !threadBelongsTo(existing, projectId, agentId)) { + send({ type: 'error', message: 'Session not found' }); + return; + } + } - // If the client supplied a sessionId and a thread already exists under that id, - // the thread must belong to this (project, agent). Otherwise a caller could - // append messages to another user's thread. A non-existent id is fine - - // executeForChat will create the thread on first persisted message. - if (sessionId) { - const existing = await this.agentExecutionService.findThreadById(sessionId); - if (existing && !threadBelongsTo(existing, projectId, agentId)) { - send({ type: 'error', message: 'Session not found' }); - res.end(); + const threadId = sessionId ?? randomUUID(); + const { missing } = await this.agentValidationService.validateAgentIsRunnable( + agentId, + projectId, + credentialProvider, + ); + if (abortController.signal.aborted) return; + if (missing.length > 0) { + send({ + type: 'error', + message: 'This agent is not ready to run yet.', + errorCode: 'agent_misconfigured', + missing, + }); return; } - } - const threadId = sessionId ?? randomUUID(); - - const { missing } = await this.agentValidationService.validateAgentIsRunnable( - agentId, - projectId, - credentialProvider, - ); - if (missing.length > 0) { - send({ - type: 'error', - message: 'This agent is not ready to run yet.', - errorCode: 'agent_misconfigured', - missing, + storedAttachments = await this.storeChatAttachments({ + attachments, + agentId, + projectId, + threadId, + resourceId: draftChatMemoryResourceId(req.user.id), }); - res.end(); - return; - } - try { - let executionId: string | undefined; const suspended = await pumpChunks( this.agentExecutionOrchestratorService.executeForChat({ agentId, projectId, message, + attachments: storedAttachments, user: req.user, memory: { threadId, @@ -100,6 +177,7 @@ export class AgentChatController { onExecutionRecorded: (id) => { executionId = id; }, + abortSignal: abortController.signal, }), send, ); @@ -107,11 +185,22 @@ export class AgentChatController { send({ type: 'done', sessionId: threadId, ...(executionId ? { executionId } : {}) }); } } catch (error) { - const errorMessage = error instanceof Error ? error.message : 'Chat failed'; - send({ type: 'error', message: errorMessage }); + // No execution recorded means nothing references this turn's attachments — + // remove them so failed turns can't accumulate orphans. Best-effort, and + // deliberately also on aborted turns. + if (!executionId && storedAttachments?.length) { + await this.agentChatAttachmentService + .deleteByIds(storedAttachments.map((ref) => ref.id)) + .catch(() => {}); + } + if (!abortController.signal.aborted) { + const errorMessage = error instanceof Error ? error.message : 'Chat failed'; + send({ type: 'error', message: errorMessage }); + } + } finally { + res.off('close', abortOnClose); + res.end(); } - - res.end(); } @Post('/:agentId/chat/resume', { usesTemplates: true }) @@ -126,6 +215,9 @@ export class AgentChatController { const { runId, toolCallId, resumeData } = payload; const { send } = initSseStream(res); + const abortController = new AbortController(); + const abortOnClose = () => abortController.abort(); + res.once('close', abortOnClose); try { let executionId: string | undefined; const suspended = await pumpChunks( @@ -141,6 +233,7 @@ export class AgentChatController { onExecutionRecorded: (id) => { executionId = id; }, + abortSignal: abortController.signal, }), send, ); @@ -148,11 +241,34 @@ export class AgentChatController { send({ type: 'done', ...(executionId ? { executionId } : {}) }); } } catch (error) { - const errorMessage = error instanceof Error ? error.message : 'Resume failed'; - send({ type: 'error', message: errorMessage }); + if (!abortController.signal.aborted) { + const errorMessage = error instanceof Error ? error.message : 'Resume failed'; + send({ type: 'error', message: errorMessage }); + } + } finally { + res.off('close', abortOnClose); + res.end(); } + } - res.end(); + @Delete('/:agentId/chat/runs/:runId') + @ProjectScope('agent:execute') + async cancelChatRun( + req: AuthenticatedRequest<{ projectId: string }>, + _res: Response, + @Param('agentId') agentId: string, + @Param('runId') runId: string, + ) { + const { projectId } = req.params; + const agent = await this.agentsService.findById(agentId, projectId); + if (!agent) throw new NotFoundError(`Agent "${agentId}" not found`); + + const cancelled = await this.agentExecutionOrchestratorService.cancelChatRun({ + agentId, + runId, + resourceId: draftChatMemoryResourceId(req.user.id), + }); + return { cancelled }; } @Get('/:agentId/chat/:threadId/messages') @@ -202,6 +318,66 @@ export class AgentChatController { return withOpenSuspensions(messagesToDto(messages), checkpoint); } + @Get('/:agentId/chat/attachments/:attachmentId') + @ProjectScope('agent:read') + async getChatAttachment( + req: AuthenticatedRequest<{ projectId: string; agentId: string; attachmentId: string }>, + res: Response, + ) { + const { projectId, agentId, attachmentId } = req.params; + const agent = await this.agentsService.findById(agentId, projectId); + if (!agent) throw new NotFoundError(`Agent "${agentId}" not found`); + + const attachment = await this.agentChatAttachmentService.getForAgent(attachmentId, { + agentId, + projectId, + }); + if (!attachment) throw new NotFoundError(`Attachment "${attachmentId}" not found`); + + // Open the stream before writing headers: bytes can be gone while the row + // remains (out-of-band storage cleanup), and that must surface as a clean + // 404 rather than a half-written response. + let stream: Awaited>; + try { + stream = await this.agentChatAttachmentService.getStream(attachment); + } catch (error) { + if (error instanceof FileNotFoundError) { + throw new NotFoundError(`Attachment "${attachmentId}" is no longer available`); + } + throw error; + } + + res.setHeader('Content-Type', attachment.mimeType); + res.setHeader('Content-Length', attachment.fileSizeBytes); + res.setHeader('X-Content-Type-Options', 'nosniff'); + // Sandbox anything rendered inline: attachments are user-supplied content + // served same-origin, so active content in them must never script against + // the n8n session (same posture as the binary-data controller). + res.setHeader('Content-Security-Policy', getHtmlSandboxCSP()); + // Non-viewable types must not render inline in the browser. + if (!ViewableMimeTypes.includes(attachment.mimeType.toLowerCase())) { + res.setHeader( + 'Content-Disposition', + `attachment; filename="${sanitizeFilename(attachment.fileName)}"`, + ); + } + + // pipeline destroys the source when the client disconnects mid-transfer, + // so aborted downloads don't leak file descriptors or object-store sockets. + try { + await pipeline(stream, res); + } catch (error) { + if ( + error instanceof Error && + 'code' in error && + error.code === 'ERR_STREAM_PREMATURE_CLOSE' + ) { + return; + } + throw error; + } + } + @Delete('/:agentId/chat/messages') @ProjectScope('agent:update') async clearTestChatMessages(req: AuthenticatedRequest<{ projectId: string; agentId: string }>) { diff --git a/packages/cli/src/modules/agents/agent-config.service.ts b/packages/cli/src/modules/agents/agent-config.service.ts index 82528cd22b1..c0d9a953324 100644 --- a/packages/cli/src/modules/agents/agent-config.service.ts +++ b/packages/cli/src/modules/agents/agent-config.service.ts @@ -8,7 +8,7 @@ import { type AgentJsonToolConfig, } from '@n8n/api-types'; import { Logger } from '@n8n/backend-common'; -import { WorkflowRepository } from '@n8n/db'; +import { WorkflowRepository, type User } from '@n8n/db'; import { Service } from '@n8n/di'; import { UserError } from 'n8n-workflow'; @@ -100,16 +100,28 @@ export class AgentConfigService { /** * Persist a new AgentJsonConfig (full replace). + * + * By default an optional field absent from `config` retains its previous + * value. With `clearOmittedOptionalFields`, absence removes the field + * instead — true replace semantics for callers whose clients submit the + * complete config (e.g. MCP config.replace / config.patch, where an RFC + * 6902 `remove` op must actually remove the field). */ async updateConfig( agentId: string, projectId: string, config: unknown, + user?: User, + options?: { clearOmittedOptionalFields?: boolean }, ): Promise<{ config: AgentJsonConfig; updatedAt: string; versionId: string | null }> { const entity = await this.agentRepository.findByIdAndProjectId(agentId, projectId); if (!entity) throw new NotFoundError('Agent not found'); - const credentialProvider = createAgentCredentialProvider(this.credentialsService, projectId); + const credentialProvider = createAgentCredentialProvider( + this.credentialsService, + projectId, + user, + ); const accessibleCredentialIds = new Set( (await credentialProvider.list()).map((credential) => credential.id), ); @@ -164,12 +176,16 @@ export class AgentConfigService { decomposeJsonConfig(validatedConfig); const nextIntegrations = integrationsProvided ? decomposedIntegrations : previousIntegrations; + // Under clearOmittedOptionalFields an omitted gradient is a deliberate + // removal, so the schema default wins instead of the previous gradient. const nextPersonalisation = personalisationProvided - ? mergePersonalisationWithPreviousGradient( - decomposedSchema.personalisation, - previousSchema, - config, - ) + ? options?.clearOmittedOptionalFields + ? decomposedSchema.personalisation + : mergePersonalisationWithPreviousGradient( + decomposedSchema.personalisation, + previousSchema, + config, + ) : undefined; const nextSchema: AgentJsonConfig = { @@ -190,6 +206,10 @@ export class AgentConfigService { ...(vectorStoresProvided ? { vectorStores: decomposedSchema.vectorStores } : {}), }; + if (options?.clearOmittedOptionalFields) { + clearOmittedOptionalFields(nextSchema, validatedConfig); + } + entity.schema = nextSchema; entity.name = validatedConfig.name; entity.integrations = nextIntegrations; @@ -319,6 +339,26 @@ function hasNodeToolInputSchema(raw: unknown): boolean { return raw.tools.some((tool) => isRecord(tool) && tool.type === 'node' && 'inputSchema' in tool); } +/** Drop optional fields the submitted config omitted instead of retaining the previous value. */ +function clearOmittedOptionalFields(schema: AgentJsonConfig, submitted: AgentJsonConfig): void { + const optionalFields = [ + 'credential', + 'personalisation', + 'memory', + 'subAgents', + 'tools', + 'skills', + 'tasks', + 'providerTools', + 'config', + 'mcpServers', + 'vectorStores', + ] as const; + for (const field of optionalFields) { + if (submitted[field] === undefined) delete schema[field]; + } +} + function omitLegacyAgentDescription(config: AgentJsonConfig | null): Partial { if (!config) return {}; diff --git a/packages/cli/src/modules/agents/agent-execution-orchestrator.service.ts b/packages/cli/src/modules/agents/agent-execution-orchestrator.service.ts index 34a7fefa3b1..696ac00a2e4 100644 --- a/packages/cli/src/modules/agents/agent-execution-orchestrator.service.ts +++ b/packages/cli/src/modules/agents/agent-execution-orchestrator.service.ts @@ -9,14 +9,17 @@ import { UserError } from 'n8n-workflow'; import { ExternalHooks } from '@/external-hooks'; import type { AgentRunTelemetryType, IAgentConfigurationTelemetryProperties } from '@/interfaces'; import { Telemetry } from '@/telemetry'; + import { AgentExecutionService, type RecordMessageParams } from './agent-execution.service'; import { AgentRunTracingService, modelIdFromSnapshot } from './agent-run-tracing.service'; import { AgentRuntimeCacheService } from './agent-runtime-cache.service'; -import { ExecutionRecorder } from './execution-recorder'; +import { ExecutionRecorder, type MessageRecord } from './execution-recorder'; import { IntegrationMessageContextService } from './integrations/integration-message-context.service'; import { N8NCheckpointStorage } from './integrations/n8n-checkpoint-storage'; import type { ToolRegistry } from './tool-registry'; +import type { StoredAttachmentRef } from './agent-chat-attachment.service'; import { createAgentExecutionCounter } from './utils/agent-execution-counter'; +import { buildInboundUserMessage } from './utils/inbound-attachments'; import { streamAgentChunks } from './utils/agent-stream'; import { executionsToMessagesDto } from './utils/execution-to-message-mapper'; @@ -38,8 +41,11 @@ export interface ExecuteForChatConfig { user: User; /** Memory scope — resourceId is the chat platform user (e.g. Slack / Telegram user ID). */ memory: AgentMemoryScope; + /** Stored attachments to include as file parts on the user turn. */ + attachments?: StoredAttachmentRef[]; /** Fired after the turn is persisted; used to attach `executionId` to SSE `done`. */ onExecutionRecorded?: (executionId: string) => void; + abortSignal?: AbortSignal; } export interface ExecuteForChatPublishedConfig { @@ -48,6 +54,7 @@ export interface ExecuteForChatPublishedConfig { message: string; /** Memory scope — resourceId is the chat platform user (e.g. Slack / Telegram user ID). */ memory: AgentMemoryScope; + attachments?: StoredAttachmentRef[]; integrationType?: string; // No `user` field here: a published chat integration (Slack, Telegram, …) // run is triggered by an inbound platform event, not an interactive n8n @@ -81,6 +88,7 @@ export interface ResumeForChatConfig { integrationType?: string; /** Fired after the resumed turn is persisted; used to attach `executionId` to SSE `done`. */ onExecutionRecorded?: (executionId: string) => void; + abortSignal?: AbortSignal; } export interface ExecuteForTaskPublishedConfig { @@ -123,6 +131,7 @@ export interface StreamChatResponseConfig { agentId: string; userId?: string; message: string; + attachments?: StoredAttachmentRef[]; memory: AgentMemoryScope; projectId: string; source?: string; @@ -134,6 +143,7 @@ export interface StreamChatResponseConfig { }; /** Fired after the turn is persisted; used to attach `executionId` to SSE `done`. */ onExecutionRecorded?: (executionId: string) => void; + abortSignal?: AbortSignal; } function getMaxIterationsChunks(): StreamChunk[] { @@ -149,6 +159,14 @@ function getMaxIterationsChunks(): StreamChunk[] { ]; } +function normalizeAbortedMessageRecord( + record: MessageRecord, + abortSignal?: AbortSignal, +): MessageRecord { + if (!abortSignal?.aborted) return record; + return { ...record, finishReason: 'cancelled', error: null }; +} + /** * Executes agents for the interactive surfaces — in-app test chat, published * chat integrations (Slack, Telegram, …), and scheduled/manual tasks — as @@ -187,6 +205,29 @@ export class AgentExecutionOrchestratorService { return executionsToMessagesDto(detail.executions); } + async cancelChatRun(params: { + agentId: string; + runId: string; + resourceId: string; + }): Promise { + const checkpointStatus = await this.n8nCheckpointStorage.getStatus(params.runId); + if (checkpointStatus.status !== 'active') return false; + + const { checkpoint } = checkpointStatus; + if ( + checkpoint.status !== 'suspended' || + checkpoint.persistence?.resourceId !== params.resourceId + ) { + return false; + } + + return await this.n8nCheckpointStorage.cancelSuspended( + params.runId, + checkpoint, + params.agentId, + ); + } + /** * Resume a suspended tool call and yield the resulting stream chunks. * Used by chat integration handlers to continue an agent run after @@ -203,6 +244,7 @@ export class AgentExecutionOrchestratorService { user, usePublishedVersion = true, onExecutionRecorded, + abortSignal, } = config; const checkpointStatus = await this.n8nCheckpointStorage.getStatus(runId); @@ -266,6 +308,7 @@ export class AgentExecutionOrchestratorService { userId: user?.id, }), ...(tracing ? { telemetry: tracing } : {}), + ...(abortSignal ? { abortSignal } : {}), }); for await (const value of streamAgentChunks(resultStream.stream)) { @@ -280,7 +323,7 @@ export class AgentExecutionOrchestratorService { // Always record resumed executions — even if they suspend again (chained HITL) // or fail while streaming. Don't repeat the original user message — the // pre-suspension execution already has it. - const messageRecord = recorder.getMessageRecord(); + const messageRecord = normalizeAbortedMessageRecord(recorder.getMessageRecord(), abortSignal); await this.persistRecordedExecution({ onExecutionRecorded, failureMessage: 'Failed to record resumed agent execution', @@ -305,7 +348,16 @@ export class AgentExecutionOrchestratorService { * Execute an agent for the in-app test chat and yield stream chunks. */ async *executeForChat(config: ExecuteForChatConfig): AsyncGenerator { - const { agentId, projectId, message, user, memory, onExecutionRecorded } = config; + const { + agentId, + projectId, + message, + user, + memory, + attachments, + onExecutionRecorded, + abortSignal, + } = config; // `user` is always set (see ExecuteForChatConfig) — this builds/reuses a // runtime scoped to this specific user's tool access. @@ -330,6 +382,7 @@ export class AgentExecutionOrchestratorService { agentId, userId: user.id, message, + attachments, memory, projectId: runtime.projectId, telemetry: { @@ -337,6 +390,7 @@ export class AgentExecutionOrchestratorService { configuration: runtime.telemetryConfiguration, }, onExecutionRecorded, + abortSignal, }); } @@ -348,7 +402,7 @@ export class AgentExecutionOrchestratorService { async *executeForChatPublished( config: ExecuteForChatPublishedConfig, ): AsyncGenerator { - const { agentId, projectId, message, memory, integrationType } = config; + const { agentId, projectId, message, memory, integrationType, attachments } = config; await this.externalHooks.run('agent.preExecute', [agentId]); // No `user` (see ExecuteForChatPublishedConfig): this is the shared, @@ -367,6 +421,7 @@ export class AgentExecutionOrchestratorService { toolRegistry: runtime.toolRegistry, agentId, message, + attachments, memory, projectId: runtime.projectId, source: integrationType, @@ -456,6 +511,7 @@ export class AgentExecutionOrchestratorService { agentId, userId, message, + attachments, memory, projectId, source, @@ -463,6 +519,7 @@ export class AgentExecutionOrchestratorService { taskVersionId, telemetry, onExecutionRecorded, + abortSignal, } = config; const { threadId, resourceId } = memory; @@ -478,10 +535,12 @@ export class AgentExecutionOrchestratorService { modelId: modelIdFromSnapshot(agentInstance.snapshot.model), }); - const resultStream = await agentInstance.stream(message, { + const input = attachments?.length ? buildInboundUserMessage(message, attachments) : message; + const resultStream = await agentInstance.stream(input, { persistence: { threadId, resourceId }, executionCounter: createAgentExecutionCounter(this.telemetry, { agentId, userId }), ...(tracing ? { telemetry: tracing } : {}), + ...(abortSignal ? { abortSignal } : {}), }); for await (const value of streamAgentChunks(resultStream.stream)) { @@ -508,7 +567,7 @@ export class AgentExecutionOrchestratorService { } finally { // Always record — even if suspended or failed, the pre-suspension/error // response text and tool calls are valuable. - const messageRecord = recorder.getMessageRecord(); + const messageRecord = normalizeAbortedMessageRecord(recorder.getMessageRecord(), abortSignal); await this.persistRecordedExecution({ onExecutionRecorded, failureMessage: 'Failed to record agent execution', @@ -518,6 +577,7 @@ export class AgentExecutionOrchestratorService { agentName: agentInstance.name, projectId, userMessage: message, + attachments, record: messageRecord, hitlStatus: recorder.suspended ? 'suspended' : undefined, source, diff --git a/packages/cli/src/modules/agents/agent-execution.service.ts b/packages/cli/src/modules/agents/agent-execution.service.ts index ffe92874ca8..7cbdc55b20b 100644 --- a/packages/cli/src/modules/agents/agent-execution.service.ts +++ b/packages/cli/src/modules/agents/agent-execution.service.ts @@ -8,6 +8,10 @@ import { UnexpectedError } from 'n8n-workflow'; import type { AgentRunTelemetryType, IAgentConfigurationTelemetryProperties } from '@/interfaces'; import { Telemetry } from '@/telemetry'; +import { + AgentChatAttachmentService, + type StoredAttachmentRef, +} from './agent-chat-attachment.service'; import { AgentExecutionThread } from './entities/agent-execution-thread.entity'; import { AgentExecution } from './entities/agent-execution.entity'; import type { MessageRecord } from './execution-recorder'; @@ -23,6 +27,8 @@ export interface RecordMessageParams { agentName: string; projectId: string; userMessage: string | null; + /** Attachments included on the user turn; persisted on the run for the sessions view. */ + attachments?: StoredAttachmentRef[]; record: MessageRecord; /** Set to 'suspended' or 'resumed' for HITL tool call flows. */ hitlStatus?: 'suspended' | 'resumed'; @@ -63,6 +69,7 @@ export class AgentExecutionService { private readonly agentExecutionThreadRepository: AgentExecutionThreadRepository, private readonly n8nMemory: N8nMemory, private readonly telemetry: Telemetry, + private readonly agentChatAttachmentService: AgentChatAttachmentService, private readonly agentExecutionLogStore: AgentExecutionLogStore, private readonly storageConfig: StorageConfig, private readonly errorReporter: ErrorReporter, @@ -140,6 +147,7 @@ export class AgentExecutionService { error: record.error, hitlStatus: hitlStatus ?? null, source: source ?? null, + attachments: params.attachments?.length ? params.attachments : null, }), ); @@ -278,6 +286,7 @@ export class AgentExecutionService { ); await this.n8nMemory.getImplementation(agentId).deleteThread(threadId); + await this.agentChatAttachmentService.deleteByThread(threadId, { projectId }); await Promise.all([ this.agentExecutionThreadRepository.delete({ id: threadId }), this.agentExecutionLogStore.delete( diff --git a/packages/cli/src/modules/agents/agent-mcp-access.controller.ts b/packages/cli/src/modules/agents/agent-mcp-access.controller.ts new file mode 100644 index 00000000000..2be16535b46 --- /dev/null +++ b/packages/cli/src/modules/agents/agent-mcp-access.controller.ts @@ -0,0 +1,34 @@ +import { ListAgentsQueryDto, UpdateAgentsMcpAvailabilityDto } from '@n8n/api-types'; +import type { AuthenticatedRequest } from '@n8n/db'; +import { Body, Get, Patch, Query, RestController } from '@n8n/decorators'; +import type { Response } from 'express'; + +import { AgentMcpAccessService } from './agent-mcp-access.service'; + +/** + * Per-agent MCP availability endpoints, mirroring the per-workflow ones on + * `McpSettingsController` (`/mcp/workflows`). They live in the agents module + * so they only exist when the module is active. + * + * No `@ProjectScope` decorators: these routes carry no project in their URL, + * so `AgentMcpAccessService` enforces the `agent:update` scope per project + * instead — same reasoning as the workflow toggle endpoint. + */ +@RestController('/mcp/agents') +export class AgentMcpAccessController { + constructor(private readonly agentMcpAccessService: AgentMcpAccessService) {} + + @Get('/') + async getMcpAgents(req: AuthenticatedRequest, res: Response, @Query query: ListAgentsQueryDto) { + res.json(await this.agentMcpAccessService.getAgents(req.user, query)); + } + + @Patch('/toggle-access') + async toggleAgentsMCPAccess( + req: AuthenticatedRequest, + _res: Response, + @Body dto: UpdateAgentsMcpAvailabilityDto, + ) { + return await this.agentMcpAccessService.bulkSetAvailableInMCP(req.user, dto); + } +} diff --git a/packages/cli/src/modules/agents/agent-mcp-access.service.ts b/packages/cli/src/modules/agents/agent-mcp-access.service.ts new file mode 100644 index 00000000000..fdff07bad95 --- /dev/null +++ b/packages/cli/src/modules/agents/agent-mcp-access.service.ts @@ -0,0 +1,123 @@ +import type { ListAgentsQueryDto, UpdateAgentsMcpAvailabilityDto } from '@n8n/api-types'; +import type { User } from '@n8n/db'; +import { Service } from '@n8n/di'; + +import { BadRequestError } from '@/errors/response-errors/bad-request.error'; +import { ProjectScopeService } from '@/permissions.ee/project-scope.service'; + +import type { Agent } from './entities/agent.entity'; +import { AgentRepository } from './repositories/agent.repository'; + +const BULK_CHUNK_SIZE = 500; + +type BulkSetAvailableInMCPResult = { + updatedCount: number; + /** Only present when the request targeted explicit `agentIds`. */ + updatedIds?: string[]; + unchangedIds?: string[]; +}; + +/** + * Grants and revokes per-agent MCP availability (the `availableInMCP` flag), + * mirroring the per-workflow flow in `McpSettingsService`. Project access is + * resolved here because the REST routes have no project in their URL for a + * `@ProjectScope` gate. + */ +@Service() +export class AgentMcpAccessService { + constructor( + private readonly agentRepository: AgentRepository, + private readonly projectScopeService: ProjectScopeService, + ) {} + + /** + * Paginated list of agents in projects where the user holds `agent:update`. + * Defaults to agents that are not yet available to MCP. + */ + async getAgents( + user: User, + options: ListAgentsQueryDto, + ): Promise<{ count: number; data: Agent[] }> { + const projectIds = await this.projectScopeService.getProjectIds(user, ['agent:update']); + return await this.agentRepository.findByProjectIdsPaginated( + projectIds, + { + ...options, + filter: { + ...options.filter, + availableInMCP: options.filter?.availableInMCP ?? false, + }, + }, + { withProject: true }, + ); + } + + async bulkSetAvailableInMCP( + user: User, + dto: UpdateAgentsMcpAvailabilityDto, + ): Promise { + const targets = [dto.agentIds, dto.projectId, dto.allAgents].filter( + (target) => target !== undefined, + ); + if (targets.length !== 1) { + throw new BadRequestError('Provide exactly one of "agentIds", "projectId", or "allAgents".'); + } + + const projectIds = await this.projectScopeService.getProjectIds(user, ['agent:update']); + const allowedProjectIds = projectIds === null ? null : new Set(projectIds); + if (dto.projectId && allowedProjectIds !== null && !allowedProjectIds.has(dto.projectId)) { + return { updatedCount: 0 }; + } + + const candidates = await this.resolveCandidates(dto, projectIds); + const accessible = + allowedProjectIds === null + ? candidates + : candidates.filter((agent) => allowedProjectIds.has(agent.projectId)); + + const toUpdate = accessible.filter((agent) => agent.availableInMCP !== dto.availableInMCP); + + const agentIds = toUpdate.map((agent) => agent.id); + for (let start = 0; start < agentIds.length; start += BULK_CHUNK_SIZE) { + await this.agentRepository.setAvailableInMCP( + agentIds.slice(start, start + BULK_CHUNK_SIZE), + dto.availableInMCP, + ); + } + + return { + updatedCount: toUpdate.length, + // Per-id breakdown only for explicit `agentIds` requests; the caller + // uses it to confirm each requested agent landed in a known state. + ...(dto.agentIds + ? { + updatedIds: agentIds, + unchangedIds: accessible + .filter((agent) => agent.availableInMCP === dto.availableInMCP) + .map((agent) => agent.id), + } + : {}), + }; + } + + private async resolveCandidates( + dto: UpdateAgentsMcpAvailabilityDto, + projectIds: string[] | null, + ): Promise>> { + if (dto.agentIds) { + return await this.agentRepository.findMcpAvailabilityCandidates({ + ids: [...new Set(dto.agentIds)], + }); + } + + if (dto.projectId) { + return await this.agentRepository.findMcpAvailabilityCandidates({ + projectIds: [dto.projectId], + }); + } + + return await this.agentRepository.findMcpAvailabilityCandidates( + projectIds === null ? { all: true } : { projectIds }, + ); + } +} diff --git a/packages/cli/src/modules/agents/agent-message-mapper.ts b/packages/cli/src/modules/agents/agent-message-mapper.ts index 5b418bd67df..d5285e9d356 100644 --- a/packages/cli/src/modules/agents/agent-message-mapper.ts +++ b/packages/cli/src/modules/agents/agent-message-mapper.ts @@ -12,6 +12,14 @@ export function contentPartToDto(part: MessageContent): AgentPersistedMessageCon if ('state' in part && typeof part.state === 'string') dto.state = part.state; if ('output' in part) dto.output = part.output; if ('error' in part && typeof part.error === 'string') dto.error = part.error; + // File parts expose reference metadata only — never bytes. Parts without a + // fileRef (e.g. model-generated files) have nothing fetchable to expose. + if (part.type === 'file' && part.fileRef) { + dto.fileId = part.fileRef.id; + if (part.fileRef.fileName !== undefined) dto.fileName = part.fileRef.fileName; + if (part.mediaType !== undefined) dto.mimeType = part.mediaType; + if (part.fileRef.sizeBytes !== undefined) dto.sizeBytes = part.fileRef.sizeBytes; + } return dto; } diff --git a/packages/cli/src/modules/agents/agent-model-catalog.service.ts b/packages/cli/src/modules/agents/agent-model-catalog.service.ts index b34a6eb7b82..5e305cad1c2 100644 --- a/packages/cli/src/modules/agents/agent-model-catalog.service.ts +++ b/packages/cli/src/modules/agents/agent-model-catalog.service.ts @@ -1,4 +1,8 @@ -import type { AgentCatalogModel, AgentProviderModelsResponse } from '@n8n/api-types'; +import { + AI_GATEWAY_MANAGED_TAG, + type AgentCatalogModel, + type AgentProviderModelsResponse, +} from '@n8n/api-types'; import { Logger } from '@n8n/backend-common'; import type { User } from '@n8n/db'; import { Service } from '@n8n/di'; @@ -88,9 +92,38 @@ export class AgentModelCatalogService { provider, error: error instanceof Error ? error.message : String(error), }); + // Managed slot: the gateway allowlist is the contract, so don't fall back + // to the static catalog (it may list models the gateway won't serve). A + // verified empty list shows nothing rather than un-served models. + if (credentialId === AI_GATEWAY_MANAGED_TAG) { + return { provider, verified: true, models: [] }; + } return { provider, verified: false, models: Object.values(catalogModels) }; } + // n8n Connect managed slot: the gateway's `/models` is the authoritative, + // allowlist-filtered set, and its exact ids are what the gateway's model + // allowlist matches (e.g. a dated snapshot, not the catalog's versionless + // alias). Use those ids verbatim, enriched with catalog display/metadata. + if (credentialId === AI_GATEWAY_MANAGED_TAG) { + return { + provider, + verified: true, + models: liveModels.map((live) => { + const id = normalizeLiveModelValue(provider, live.value); + const catalogMatch = catalogModels[id] ?? catalogModels[id.replace(SNAPSHOT_SUFFIX, '')]; + return catalogMatch + ? { ...catalogMatch, id } + : { + id, + name: normalizeLiveModelValue(provider, live.name) || id, + reasoning: false, + toolCall: true, + }; + }), + }; + } + const liveModelIds = new Set( liveModels.flatMap((live) => liveModelIdVariants(normalizeLiveModelValue(provider, live.value)), diff --git a/packages/cli/src/modules/agents/agent-publish.service.ts b/packages/cli/src/modules/agents/agent-publish.service.ts index f9b4747154c..2f59238ce0e 100644 --- a/packages/cli/src/modules/agents/agent-publish.service.ts +++ b/packages/cli/src/modules/agents/agent-publish.service.ts @@ -25,6 +25,7 @@ import { AgentRuntimeCacheService } from './agent-runtime-cache.service'; import { AgentValidationService } from './agent-validation.service'; import type { AgentHistory } from './entities/agent-history.entity'; import { AgentTask } from './entities/agent-task.entity'; +import type { AgentTaskSnapshot } from './entities/agent-task-snapshot.entity'; import type { Agent } from './entities/agent.entity'; import { ChatIntegrationService } from './integrations/chat-integration.service'; import { AgentHistoryRepository } from './repositories/agent-history.repository'; @@ -376,6 +377,29 @@ export class AgentPublishService { return await this.agentHistoryRepository.existsForAgent(agentId); } + /** + * Load one published version snapshot (schema, tools, skills) plus its + * frozen task rows, for read-only inspection. + */ + async getVersion( + agentId: string, + projectId: string, + versionId: string, + ): Promise<{ agent: Agent; version: AgentHistory; tasks: AgentTaskSnapshot[] }> { + const agent = await this.agentRepository.findByIdAndProjectId(agentId, projectId); + if (!agent) { + throw new NotFoundError(`Agent "${agentId}" not found`); + } + + const version = await this.agentHistoryRepository.findByVersionAndAgentId(versionId, agentId); + if (!version) { + throw new NotFoundError(`Version "${versionId}" not found for agent "${agentId}"`); + } + + const tasks = await this.agentTaskSnapshotRepository.findByVersionId(versionId); + return { agent, version, tasks }; + } + async listPublishHistory( agentId: string, projectId: string, diff --git a/packages/cli/src/modules/agents/agent-runtime-reconstruction.service.ts b/packages/cli/src/modules/agents/agent-runtime-reconstruction.service.ts index a3d156a30e5..7ab4fc069a1 100644 --- a/packages/cli/src/modules/agents/agent-runtime-reconstruction.service.ts +++ b/packages/cli/src/modules/agents/agent-runtime-reconstruction.service.ts @@ -47,6 +47,7 @@ import { WorkflowRunner } from '@/workflow-runner'; import { WorkflowFinderService } from '@/workflows/workflow-finder.service'; import { isAgentKnowledgeBaseEnabled } from './agent-knowledge-gate'; +import { AgentChatAttachmentService } from './agent-chat-attachment.service'; import { AgentKnowledgeSandboxService } from './agent-knowledge-sandbox.service'; import type { AgentRuntimeInstrumentation } from './agent-runtime-instrumentation'; import { Agent } from './entities/agent.entity'; @@ -161,6 +162,7 @@ export class AgentRuntimeReconstructionService { private readonly ssrfProtectionService: SsrfProtectionService, private readonly credentialsFinderService: CredentialsFinderService, private readonly workflowFinderService: WorkflowFinderService, + private readonly agentChatAttachmentService: AgentChatAttachmentService, ) {} async reconstructFromAgentEntity( @@ -645,6 +647,16 @@ export class AgentRuntimeReconstructionService { if (runtimeProfile !== 'inline' && !agent.hasCheckpointStorage()) { agent.checkpoint(this.n8nCheckpointStorage.getStorage(agentId)); } + + // Attachment lookups are agent-scoped, so a synthetic inline id would + // never match a row — inline agents get their file input via workflow + // items instead. + if (runtimeProfile !== 'inline') { + const provider = config.model.split('/')[0]; + agent.fileStore( + this.agentChatAttachmentService.getFileStore({ agentId, projectId }, provider), + ); + } } private async attachSubAgentDelegationTool(params: { diff --git a/packages/cli/src/modules/agents/agent-sse-stream.ts b/packages/cli/src/modules/agents/agent-sse-stream.ts index 77e432df389..18af2cb8c63 100644 --- a/packages/cli/src/modules/agents/agent-sse-stream.ts +++ b/packages/cli/src/modules/agents/agent-sse-stream.ts @@ -225,6 +225,16 @@ function emitChunkEvents(chunk: StreamChunk, ctx: ChunkHandlerCtx): { suspended: if (sseMessage) ctx.send({ type: 'message', message: sseMessage }); return { suspended: false }; } + case 'subagent-chunk': { + if (chunk.parentToolCallId === undefined) return { suspended: false }; + ctx.send({ + type: 'subagent-chunk', + parentToolCallId: chunk.parentToolCallId, + taskPath: chunk.taskPath, + chunk: chunk.chunk, + }); + return { suspended: false }; + } case 'error': { const errMsg = stringifyError(chunk.error); ctx.send({ type: 'error', message: errMsg }); @@ -245,8 +255,34 @@ function emitChunkEvents(chunk: StreamChunk, ctx: ChunkHandlerCtx): { suspended: } } +/** Find an ai-sdk `responseBody` on the error or its `cause` chain (the API error can arrive wrapped). */ +function readResponseBody(error: unknown): string | undefined { + if (typeof error !== 'object' || error === null) return undefined; + if ('responseBody' in error && typeof error.responseBody === 'string') return error.responseBody; + if ('cause' in error && error.cause !== error) return readResponseBody(error.cause); + return undefined; +} + +/** + * The actionable message an ai-sdk error carries in its JSON `responseBody` — + * e.g. the n8n Connect gateway's "switch to your own credential" guidance. Prefer + * this over the bare status text ("Bad Request") so the chat shows what to do. + */ +function apiCallErrorMessage(error: unknown): string | undefined { + const body = readResponseBody(error); + if (!body) return undefined; + try { + const parsed = JSON.parse(body) as { message?: string; error?: { message?: string } }; + return parsed?.error?.message ?? parsed?.message ?? undefined; + } catch { + return undefined; + } +} + function stringifyError(error: unknown): string { try { + const gatewayMessage = apiCallErrorMessage(error); + if (gatewayMessage) return gatewayMessage; if (error instanceof Error) { return error.message; } diff --git a/packages/cli/src/modules/agents/agent-test-chat.service.ts b/packages/cli/src/modules/agents/agent-test-chat.service.ts index 7a773c19af7..810d5c19efe 100644 --- a/packages/cli/src/modules/agents/agent-test-chat.service.ts +++ b/packages/cli/src/modules/agents/agent-test-chat.service.ts @@ -1,5 +1,6 @@ import { Service } from '@n8n/di'; +import { AgentChatAttachmentService } from './agent-chat-attachment.service'; import { AGENT_THREAD_PREFIX } from './builder/builder-tool-names'; import { N8nMemory } from './integrations/n8n-memory'; import { draftChatMemoryResourceId } from './utils/agent-memory-scope'; @@ -12,7 +13,10 @@ export function chatThreadId(agentId: string, userId?: string): string { @Service() export class AgentTestChatService { - constructor(private readonly n8nMemory: N8nMemory) {} + constructor( + private readonly n8nMemory: N8nMemory, + private readonly agentChatAttachmentService: AgentChatAttachmentService, + ) {} /** * Return persisted test-chat messages for an agent scoped to the current @@ -30,7 +34,9 @@ export class AgentTestChatService { * Clear the current user's test-chat messages for an agent. */ async clearTestChatMessages(agentId: string, userId: string) { - await this.n8nMemory.getImplementation(agentId).deleteThread(chatThreadId(agentId, userId)); + const threadId = chatThreadId(agentId, userId); + await this.n8nMemory.getImplementation(agentId).deleteThread(threadId); + await this.agentChatAttachmentService.deleteByThread(threadId, { agentId }); } /** Delete all test-chat messages + the thread row — used when the agent itself is deleted. */ diff --git a/packages/cli/src/modules/agents/agent-validation.service.ts b/packages/cli/src/modules/agents/agent-validation.service.ts index f73c2ce7dac..e34ed2dbd02 100644 --- a/packages/cli/src/modules/agents/agent-validation.service.ts +++ b/packages/cli/src/modules/agents/agent-validation.service.ts @@ -3,6 +3,7 @@ import { getProviderPrefix } from '@n8n/ai-utilities/agent-config'; import { getRequiredNodeCredentialSlots } from '@n8n/ai-utilities/node-catalog'; import { AgentModelSchema, + AI_GATEWAY_MANAGED_TAG, agentTaskSchema, findVectorStoreToolNameCollisions, isDraftAgentConfig, @@ -22,6 +23,7 @@ import { isMcpOAuth2Authentication, NodeHelpers, type INodeParameters } from 'n8 import { getMissingSkillIds } from '@/modules/agents/utils/agent-missing-skill-ids'; import { NodeTypes } from '@/node-types'; +import { AiGatewayService } from '@/services/ai-gateway.service'; import { LLM_PROVIDER_DEFAULTS } from './llm-provider-defaults'; import type { AgentHistory } from './entities/agent-history.entity'; @@ -78,8 +80,23 @@ export class AgentValidationService { private readonly nodeTypes: NodeTypes, private readonly workflowRepository: WorkflowRepository, private readonly chatIntegrationRegistry: ChatIntegrationRegistry, + private readonly aiGatewayService: AiGatewayService, ) {} + /** + * Whether n8n Connect (AI Gateway) can serve the model's provider, using only + * the cached gateway config — this static validator must not perform a network + * fetch. Returns `undefined` when support can't be determined (no cached + * config), so the caller can tell "gateway says no" from "could not ask". + */ + private isAiGatewayModelSupported(model: string): boolean | undefined { + const provider = getProviderPrefix(model); + if (!provider) return false; + const credentialType = this.aiGatewayService.getCredentialTypeForProviderCached(provider); + if (credentialType === undefined) return undefined; + return credentialType !== null; + } + /** * Backward-compatible wrapper over {@link validateAgentConfiguration}. * Returns the flat list of issue paths — used by the chat SSE @@ -337,6 +354,21 @@ export class AgentValidationService { } const credentialId = config.credential.trim(); + + // n8n Connect managed credential: no stored credential to resolve — it is + // valid as long as the gateway can serve the selected model's provider. + if (credentialId === AI_GATEWAY_MANAGED_TAG) { + const model = config.model?.trim(); + // Only flag a definitive "gateway does not serve this provider". When + // support can't be determined (no cached gateway config), don't fail + // closed — a transient/cold config must not make a working managed agent + // look broken and block Publish / chat runs. + if (!model || this.isAiGatewayModelSupported(model) === false) { + issues.push(agentIssue('incompatible_credential', 'credential')); + } + return; + } + const credential = await this.findCredentialSafe(findCredential, credentialId); if (!credential) { issues.push(agentIssue('invalid_credential', 'credential')); diff --git a/packages/cli/src/modules/agents/agents-tools.service.ts b/packages/cli/src/modules/agents/agents-tools.service.ts index 15c5e1e8f7e..411c6db5310 100644 --- a/packages/cli/src/modules/agents/agents-tools.service.ts +++ b/packages/cli/src/modules/agents/agents-tools.service.ts @@ -1,17 +1,13 @@ import type { BuiltTool, CredentialProvider } from '@n8n/agents'; import { Tool } from '@n8n/agents/tool'; import type { CodeBuilderSearchResult } from '@n8n/ai-utilities/node-catalog'; -import { - AGENT_BUILDER_AVAILABLE_AI_UTILITY_TOOL_NODE_TYPES, - AGENT_BUILDER_HIDDEN_AVAILABLE_TOOL_NODE_TYPES, -} from '@n8n/api-types'; +import { AGENT_BUILDER_HIDDEN_AVAILABLE_TOOL_NODE_TYPES } from '@n8n/api-types'; import { Service } from '@n8n/di'; import { isToolType, isTriggerNodeType } from 'n8n-workflow'; import { z } from 'zod'; import { NodeCatalogService } from '@/node-catalog'; import { - isAgentProviderNode, isUnsupportedEphemeralNodeOperation, unsupportedEphemeralNodeOperationMessage, } from '@/node-execution'; @@ -35,20 +31,15 @@ type NodeRequest = export const isExecutableNodeType = (nodeId: string): boolean => !isTriggerNodeType(nodeId); const hiddenAgentToolNodeTypes = new Set(AGENT_BUILDER_HIDDEN_AVAILABLE_TOOL_NODE_TYPES); -const aiUtilityAgentToolNodeTypes = new Set( - AGENT_BUILDER_AVAILABLE_AI_UTILITY_TOOL_NODE_TYPES, -); /** * Node IDs the agent builder should surface when configuring node-backed * tools. For regular nodes marked `usableAsTool`, the loader creates a * mirrored `*Tool` node type; native tool nodes already follow this shape. * HITL tools are excluded because the builder wires regular executable tools, - * not approval-gated workflow steps. Provider nodes (OpenAI etc.) are - * admitted via the explicit whitelist — they ship the full vendor API - * (image, audio, …) but lack the `usableAsTool` flag. - * Frontend-hidden tool variants are excluded here too, so `search_nodes` - * cannot offer tools the modal intentionally hides. + * not approval-gated workflow steps. `AGENT_BUILDER_HIDDEN_AVAILABLE_TOOL_NODE_TYPES` + * is shared with the frontend tools picker, so discovery and the picker cannot + * offer different tool sets. * * Exported as a stable reference so the catalog service can cache its * filtered search tool per filter identity. @@ -61,10 +52,7 @@ export const isAgentToolNodeType = (nodeId: string): boolean => { return false; } - const isAllowedAiUtilityTool = aiUtilityAgentToolNodeTypes.has(nodeId); - const isAllowedTool = isToolType(nodeId, { includeHitl: false }) && !isMcpToolNodeType(nodeId); - const isAllowedProviderNode = isAgentProviderNode(nodeId); - return isAllowedAiUtilityTool || isAllowedTool || isAllowedProviderNode; + return isToolType(nodeId, { includeHitl: false }) && !isMcpToolNodeType(nodeId); }; const MCP_CLIENT_TOOL_NODE_TYPE = '@n8n/n8n-nodes-langchain.mcpClientTool'; diff --git a/packages/cli/src/modules/agents/agents.module.ts b/packages/cli/src/modules/agents/agents.module.ts index fb833a012aa..2f728468d2d 100644 --- a/packages/cli/src/modules/agents/agents.module.ts +++ b/packages/cli/src/modules/agents/agents.module.ts @@ -21,6 +21,7 @@ export class AgentsModule implements ModuleInterface { await import('./agent-tasks.controller.js'); await import('./agent-sandbox.controller.js'); await import('./agents-list.controller.js'); + await import('./agent-mcp-access.controller.js'); await import('./builder/agents-builder-settings.controller.js'); const { AgentsService } = await import('./agents.service.js'); @@ -128,6 +129,7 @@ export class AgentsModule implements ModuleInterface { async entities() { const { Agent } = await import('./entities/agent.entity.js'); const { AgentFile } = await import('./entities/agent-file.entity.js'); + const { AgentChatAttachment } = await import('./entities/agent-chat-attachment.entity.js'); const { AgentChatSubscription } = await import('./entities/agent-chat-subscription.entity.js'); const { AgentCheckpoint } = await import('./entities/agent-checkpoint.entity.js'); const { AgentResourceEntity } = await import('./entities/agent-resource.entity.js'); @@ -160,6 +162,7 @@ export class AgentsModule implements ModuleInterface { return [ Agent, AgentFile, + AgentChatAttachment, AgentChatSubscription, AgentCheckpoint, AgentResourceEntity, diff --git a/packages/cli/src/modules/agents/agents.service.ts b/packages/cli/src/modules/agents/agents.service.ts index a225807bdbc..e8a6ce0397f 100644 --- a/packages/cli/src/modules/agents/agents.service.ts +++ b/packages/cli/src/modules/agents/agents.service.ts @@ -6,12 +6,14 @@ import { type ListAgentsQueryDto, } from '@n8n/api-types'; import { Logger } from '@n8n/backend-common'; -import { In, ProjectRelationRepository } from '@n8n/db'; +import { In, ProjectRelationRepository, type User } from '@n8n/db'; import { Container, Service } from '@n8n/di'; +import { hasGlobalScope } from '@n8n/permissions'; import { v4 as uuid } from 'uuid'; import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { AgentChatAttachmentService } from './agent-chat-attachment.service'; import { AgentKnowledgeService } from './agent-knowledge.service'; import { AgentExecutionService } from './agent-execution.service'; import { AgentRuntimeCacheService } from './agent-runtime-cache.service'; @@ -19,7 +21,11 @@ import { AgentTestChatService } from './agent-test-chat.service'; import { Agent } from './entities/agent.entity'; import { ChatIntegrationService } from './integrations/chat-integration.service'; import { AgentTaskRepository } from './repositories/agent-task.repository'; -import { AgentRepository } from './repositories/agent.repository'; +import { + AgentRepository, + type AgentSummary, + type AgentSummaryFilters, +} from './repositories/agent.repository'; import { SubAgentCleanupService } from './sub-agents/sub-agent-cleanup.service'; import { EventService } from '@/events/event.service'; @@ -29,6 +35,7 @@ export class AgentsService { private readonly logger: Logger, private readonly agentRepository: AgentRepository, private readonly projectRelationRepository: ProjectRelationRepository, + private readonly agentChatAttachmentService: AgentChatAttachmentService, private readonly agentKnowledgeService: AgentKnowledgeService, private readonly runtimeCacheService: AgentRuntimeCacheService, private readonly testChatService: AgentTestChatService, @@ -38,7 +45,11 @@ export class AgentsService { private readonly agentExecutionService: AgentExecutionService, ) {} - async create(projectId: string, name: string): Promise { + async create( + projectId: string, + name: string, + { availableInMCP = false }: { availableInMCP?: boolean } = {}, + ): Promise { const defaultConfig: AgentJsonConfig = { name, model: '', @@ -52,6 +63,7 @@ export class AgentsService { projectId, schema: defaultConfig, versionId: uuid(), + availableInMCP, }); const saved = await this.agentRepository.save(agent); @@ -161,6 +173,35 @@ export class AgentsService { }); } + /** + * Lean agent listing (no JSON config columns, no activeVersion join) with + * filters and limit applied in the database. + */ + async findSummariesInProjects( + projectIds: string[] | null, + options: AgentSummaryFilters = {}, + ): Promise { + return await this.agentRepository.findSummariesByProjectIds(projectIds, options); + } + + /** + * Resolves an agent by ID within the projects the user can access. Agent IDs + * are globally unique, so this lets callers address an agent without knowing + * its project up front. Mirrors `@ProjectScope`'s access model: global agent + * scopes (instance owners/admins) grant access without an explicit project + * relation. + */ + async findByIdForUser(agentId: string, user: User): Promise { + if (hasGlobalScope(user, 'agent:read')) { + return await this.agentRepository.findById(agentId); + } + + const projectRelations = await this.projectRelationRepository.findAllByUser(user.id); + const projectIds = projectRelations.map((pr) => pr.projectId); + + return await this.agentRepository.findByIdInProjects(agentId, projectIds); + } + async findByUserPaginated( userId: string, options: ListAgentsQueryDto, @@ -207,6 +248,15 @@ export class AgentsService { await this.agentKnowledgeService.destroySandbox(projectId, agentId); + try { + await this.agentChatAttachmentService.deleteByAgent(agentId); + } catch (error) { + this.logger.warn('Failed to delete chat attachments on agent delete', { + agentId, + error: error instanceof Error ? error.message : error, + }); + } + const chatIntegrationService = Container.get(ChatIntegrationService); for (const integration of agent.integrations ?? []) { await chatIntegrationService.disconnectChannel(agentId, integration); diff --git a/packages/cli/src/modules/agents/builder/__tests__/agents-builder.service.session.test.ts b/packages/cli/src/modules/agents/builder/__tests__/agents-builder.service.session.test.ts index dd0e0c97443..ca1bfd20670 100644 --- a/packages/cli/src/modules/agents/builder/__tests__/agents-builder.service.session.test.ts +++ b/packages/cli/src/modules/agents/builder/__tests__/agents-builder.service.session.test.ts @@ -21,13 +21,14 @@ import { AgentsBuilderService } from '../agents-builder.service'; const agentsSdkMocks = vi.hoisted(() => { const streamCalls: Array<{ message: string; - options: { persistence: { threadId: string; resourceId: string } }; + options: { persistence: { threadId: string; resourceId: string }; abortSignal?: AbortSignal }; }> = []; + const resumeCalls: Array<{ options: Record }> = []; const instructionsCalls: string[] = []; const registeredToolNames: string[] = []; const modelCalls: unknown[] = []; const promptCachingCalls: unknown[] = []; - const thinkingCalls: Array<{ provider: string; config: unknown }> = []; + const reasoningCalls: string[] = []; const skillsCalls: unknown[] = []; const telemetryCalls: unknown[] = []; const memoryTaskObserverCalls: unknown[] = []; @@ -54,8 +55,8 @@ const agentsSdkMocks = vi.hoisted(() => { promptCachingCalls.push(config); return this; } - thinking(provider: string, config?: unknown) { - thinkingCalls.push({ provider, config }); + reasoning(effort: string) { + reasoningCalls.push(effort); return this; } instructions(text: string) { @@ -94,7 +95,8 @@ const agentsSdkMocks = vi.hoisted(() => { streamCalls.push({ message, options }); return { stream: emptyStream() }; } - async resume() { + async resume(_mode: string, _resumeData: unknown, options: Record) { + resumeCalls.push({ options }); return { stream: emptyStream() }; } } @@ -122,11 +124,12 @@ const agentsSdkMocks = vi.hoisted(() => { return { streamCalls, + resumeCalls, instructionsCalls, registeredToolNames, modelCalls, promptCachingCalls, - thinkingCalls, + reasoningCalls, skillsCalls, telemetryCalls, memoryTaskObserverCalls, @@ -223,16 +226,18 @@ const baseSession = { hostThreadId: 'instance-thread-1', runId: 'run-1', modelConfig: 'anthropic/claude-sonnet-host-resolved', + abortSignal: new AbortController().signal, }; describe('AgentsBuilderService session isolation', () => { beforeEach(() => { agentsSdkMocks.streamCalls.length = 0; + agentsSdkMocks.resumeCalls.length = 0; agentsSdkMocks.instructionsCalls.length = 0; agentsSdkMocks.registeredToolNames.length = 0; agentsSdkMocks.modelCalls.length = 0; agentsSdkMocks.promptCachingCalls.length = 0; - agentsSdkMocks.thinkingCalls.length = 0; + agentsSdkMocks.reasoningCalls.length = 0; agentsSdkMocks.skillsCalls.length = 0; agentsSdkMocks.telemetryCalls.length = 0; agentsSdkMocks.memoryTaskObserverCalls.length = 0; @@ -252,6 +257,33 @@ describe('AgentsBuilderService session isolation', () => { ); }); + it('forwards session.abortSignal to the SDK stream and resume calls', async () => { + const { service, user, credentialProvider, n8nCheckpointStorage } = setup(); + n8nCheckpointStorage.getStatus.mockResolvedValue({ status: 'active', checkpoint: {} as never }); + const abortSignal = new AbortController().signal; + + await drain( + service.buildAgent('agent-1', 'project-1', 'hi', credentialProvider, user, { + ...baseSession, + abortSignal, + }), + ); + await drain( + service.resumeBuild( + 'agent-1', + 'project-1', + 'builder-run-1', + 'tool-call-1', + {}, + credentialProvider, + user, + { ...baseSession, abortSignal }, + ), + ); + expect(agentsSdkMocks.streamCalls[0]?.options.abortSignal).toBe(abortSignal); + expect(agentsSdkMocks.resumeCalls[0]?.options.abortSignal).toBe(abortSignal); + }); + it('appends the session instructionsAddendum to the built prompt when provided', async () => { const { service, user, credentialProvider } = setup(); @@ -332,44 +364,21 @@ describe('AgentsBuilderService session isolation', () => { expect(agentsSdkMocks.promptCachingCalls).toEqual([{ anthropic: { ttl: '5m' } }]); }); - it('enables adaptive thinking for an Anthropic builder model', async () => { - const { service, user, credentialProvider } = setup(); - - await drain( - service.buildAgent('agent-1', 'project-1', 'hi', credentialProvider, user, baseSession), - ); - - expect(agentsSdkMocks.thinkingCalls).toEqual([ - { provider: 'anthropic', config: { mode: 'adaptive', effort: 'medium' } }, - ]); - }); - - it('enables high-effort reasoning for an OpenAI builder model', async () => { + it.each([ + ['Anthropic', 'anthropic/claude-sonnet-host-resolved'], + ['OpenAI', 'openai/gpt-5.5'], + ['Google', 'google/gemini-2.5-pro'], + ])('enables generic reasoning for a %s builder model', async (_provider, modelConfig) => { const { service, user, credentialProvider } = setup(); await drain( service.buildAgent('agent-1', 'project-1', 'hi', credentialProvider, user, { ...baseSession, - modelConfig: 'openai/gpt-5.5', + modelConfig, }), ); - expect(agentsSdkMocks.thinkingCalls).toEqual([ - { provider: 'openai', config: { reasoningEffort: 'high' } }, - ]); - }); - - it('does not configure thinking for a provider without reasoning support', async () => { - const { service, user, credentialProvider } = setup(); - - await drain( - service.buildAgent('agent-1', 'project-1', 'hi', credentialProvider, user, { - ...baseSession, - modelConfig: 'google/gemini-2.5-pro', - }), - ); - - expect(agentsSdkMocks.thinkingCalls).toEqual([]); + expect(agentsSdkMocks.reasoningCalls).toEqual(['medium']); }); it('attaches session.telemetry when provided, and omits it otherwise', async () => { diff --git a/packages/cli/src/modules/agents/builder/__tests__/builder-model-live-lookup.service.test.ts b/packages/cli/src/modules/agents/builder/__tests__/builder-model-live-lookup.service.test.ts index 89a33910818..f4cc16bdb20 100644 --- a/packages/cli/src/modules/agents/builder/__tests__/builder-model-live-lookup.service.test.ts +++ b/packages/cli/src/modules/agents/builder/__tests__/builder-model-live-lookup.service.test.ts @@ -1,9 +1,11 @@ +import { AI_GATEWAY_MANAGED_TAG } from '@n8n/api-types'; import type { CustomFetch, HttpTransport, OutboundHttp } from '@n8n/backend-network'; import type { CredentialsEntity, User } from '@n8n/db'; import { mock } from 'vitest-mock-extended'; import type { CredentialsFinderService } from '@/credentials/credentials-finder.service'; import type { CredentialsService } from '@/credentials/credentials.service'; +import type { AiGatewayService } from '@/services/ai-gateway.service'; import { BuilderModelLiveLookupService } from '../builder-model-live-lookup.service'; @@ -22,13 +24,15 @@ function makeService() { transport.asCustomFetch.mockReturnValue(vi.fn() as unknown as CustomFetch); const outboundHttp = mock(); outboundHttp.transport.mockReturnValue(transport); + const aiGatewayService = mock(); const service = new BuilderModelLiveLookupService( credentialsService, credentialsFinderService, outboundHttp, + aiGatewayService, ); - return { service, credentialsService, credentialsFinderService }; + return { service, credentialsService, credentialsFinderService, aiGatewayService }; } function usable(id: string, type: string) { @@ -105,4 +109,52 @@ describe('BuilderModelLiveLookupService', () => { ).rejects.toThrow('not found or not accessible'); expect(listModelsForProvider).not.toHaveBeenCalled(); }); + + describe('list with the n8n Connect managed tag', () => { + it('resolves the synthetic gateway credential and lists its allowlisted models', async () => { + const { service, aiGatewayService, credentialsService } = makeService(); + aiGatewayService.getCredentialTypeForProvider.mockResolvedValue('openAiApi'); + aiGatewayService.getSyntheticCredential.mockResolvedValue({ + apiKey: 'gateway-jwt', + url: 'https://gw.example/v1/gateway/openai/v1', + }); + listModelsForProvider.mockResolvedValue([{ id: 'gpt-5-mini', name: 'GPT-5 mini' }]); + + const result = await service.list( + user, + projectId, + AI_GATEWAY_MANAGED_TAG, + 'openAiApi', + 'openai', + ); + + expect(result).toEqual([{ name: 'GPT-5 mini', value: 'gpt-5-mini' }]); + expect(aiGatewayService.getSyntheticCredential).toHaveBeenCalledWith({ + credentialType: 'openAiApi', + userId: 'user-1', + projectId, + }); + // No stored-credential lookup for the managed tag. + expect(credentialsService.getCredentialsAUserCanUseInAWorkflow).not.toHaveBeenCalled(); + // Discovery hits the gateway baseURL → the gateway returns only allowlisted models. + expect(listModelsForProvider).toHaveBeenCalledWith( + 'openai', + expect.objectContaining({ + apiKey: 'gateway-jwt', + baseURL: 'https://gw.example/v1/gateway/openai/v1', + }), + ); + }); + + it('throws when the gateway does not serve the provider', async () => { + const { service, aiGatewayService } = makeService(); + aiGatewayService.getCredentialTypeForProvider.mockResolvedValue(undefined); + + await expect( + service.list(user, projectId, AI_GATEWAY_MANAGED_TAG, 'xAiApi', 'xai'), + ).rejects.toThrow('does not support'); + expect(aiGatewayService.getSyntheticCredential).not.toHaveBeenCalled(); + expect(listModelsForProvider).not.toHaveBeenCalled(); + }); + }); }); diff --git a/packages/cli/src/modules/agents/builder/__tests__/format-preview-context.test.ts b/packages/cli/src/modules/agents/builder/__tests__/format-preview-context.test.ts index cc581e8506d..9aa905fb7e3 100644 --- a/packages/cli/src/modules/agents/builder/__tests__/format-preview-context.test.ts +++ b/packages/cli/src/modules/agents/builder/__tests__/format-preview-context.test.ts @@ -53,6 +53,7 @@ describe('formatPreviewSessionContext', () => { id: 'exec-1', userMessage: 'First question', timeline: [ + { type: 'reasoning', content: 'I should search the orders.', timestamp: 0, endTime: 1 }, toolCallEvent(), { type: 'text', content: 'Here are your orders.', timestamp: 1, endTime: 2 }, ], @@ -69,6 +70,7 @@ describe('formatPreviewSessionContext', () => { expect(block).toContain('scope: whole session, turns: 2'); expect(block).toContain('User: First question'); expect(block).toContain('User: Second question'); + expect(block).toContain('Reasoning: I should search the orders.'); expect(block).toContain('Tool call: search_orders | kind=tool | succeeded | 500ms'); expect(block).toContain('Input: {"query":"open orders"}'); expect(block).toContain('Output: {"count":2}'); diff --git a/packages/cli/src/modules/agents/builder/agents-builder-tools.service.ts b/packages/cli/src/modules/agents/builder/agents-builder-tools.service.ts index e0a0b41473b..03a6a46af92 100644 --- a/packages/cli/src/modules/agents/builder/agents-builder-tools.service.ts +++ b/packages/cli/src/modules/agents/builder/agents-builder-tools.service.ts @@ -1,4 +1,5 @@ import type { BuiltTool, CredentialProvider } from '@n8n/agents'; +import { isAbortError } from '@n8n/agents'; import { Tool } from '@n8n/agents/tool'; import { applyNativeWebSearchDefaultOn, @@ -26,9 +27,7 @@ import { OutboundHttp, SsrfProtectionService } from '@n8n/backend-network'; import { SsrfProtectionConfig } from '@n8n/config'; import type { User } from '@n8n/db'; import { Service } from '@n8n/di'; -import { isRecord } from '@n8n/utils/is-record'; import type { Operation } from 'fast-json-patch'; -import { createHash } from 'node:crypto'; import { z } from 'zod'; import { CredentialTypes } from '@/credential-types'; @@ -36,6 +35,7 @@ import { McpRegistryService } from '@/modules/mcp-registry/registry/mcp-registry import { NodeTypes } from '@/node-types'; import { OauthService } from '@/oauth/oauth.service'; import { userHasScopes } from '@/permissions.ee/check-access'; +import { AiGatewayService } from '@/services/ai-gateway.service'; import { AiService } from '@/services/ai.service'; import { DynamicNodeParametersService } from '@/services/dynamic-node-parameters.service'; import { FreeAiCreditsService } from '@/services/free-ai-credits.service'; @@ -72,6 +72,7 @@ import { TASK_OBJECTIVE_GUIDANCE } from './task-objective-template'; import { buildVerifyMcpServerTool } from './verify-mcp-server.tool'; import { composeJsonConfig } from '../json-config/agent-config-composition'; import { AgentSecureRuntime } from '../runtime/agent-secure-runtime'; +import { getAgentConfigHash } from '../utils/agent-config-hash'; const STALE_CONFIG_ERROR: ConfigValidationError = { path: '(root)', @@ -120,27 +121,6 @@ interface BuilderTelemetryContext { runId?: string; } -function canonicalizeJson(value: unknown): unknown { - if (Array.isArray(value)) { - return value.map((item) => canonicalizeJson(item)); - } - - if (!isRecord(value)) return value; - - const sorted: Record = {}; - for (const key of Object.keys(value).sort()) { - sorted[key] = canonicalizeJson(value[key]); - } - return sorted; -} - -export function getAgentConfigHash(config: AgentJsonConfig | null): string | null { - if (!config) return null; - return createHash('sha256') - .update(JSON.stringify(canonicalizeJson(config))) - .digest('hex'); -} - function snapshotFromConfig(config: AgentJsonConfig | null): AgentConfigSnapshot { return { config, @@ -227,6 +207,7 @@ export class AgentsBuilderToolsService { private readonly agentTaskService: AgentTaskService, private readonly agentPublishService: AgentPublishService, private readonly aiService: AiService, + private readonly aiGatewayService: AiGatewayService, private readonly outboundHttp: OutboundHttp, private readonly dynamicNodeParametersService: DynamicNodeParametersService, private readonly nodeTypes: NodeTypes, @@ -648,6 +629,8 @@ export class AgentsBuilderToolsService { .build(); const modelLookup: ModelLookup = { + // `list` resolves the n8n Connect managed tag to the synthetic gateway + // credential internally, so no managed branch is needed here. list: async (credentialId, credentialType, provider) => await this.builderModelLiveLookupService.list( user, @@ -669,6 +652,15 @@ export class AgentsBuilderToolsService { buildResolveLlmTool({ credentialProvider, modelLookup, + isProviderServedByGateway: async (provider) => { + try { + return ( + (await this.aiGatewayService.getCredentialTypeForProvider(provider)) !== undefined + ); + } catch { + return false; + } + }, freeCredits: { isEligible: () => this.freeAiCreditsService.isEligible(user), claim: async () => { @@ -796,7 +788,7 @@ export class AgentsBuilderToolsService { .describe('Complete TypeScript source using export default new Tool(...)'), }), ) - .handler(async ({ code }: { code: string }) => { + .handler(async ({ code }: { code: string }, ctx) => { try { const descriptor = await this.secureRuntime.describeToolSecurely(code); const built = await this.agentCustomToolsService.buildCustomTool( @@ -807,6 +799,12 @@ export class AgentsBuilderToolsService { ); return { ok: true, id: built.id, name: descriptor.name }; } catch (e) { + // Unlike its sibling handlers, this one runs long isolate work, so an + // abort can land mid-call and must not be reported as a build error. + // When a signal is present it is the authority: the isolate compiles + // model-authored code, so a generated tool throwing `Aborted` must not + // be mistaken for a cancellation and kill the whole builder run. + if (ctx.abortSignal ? ctx.abortSignal.aborted : isAbortError(e)) throw e; return { ok: false, errors: [{ message: e instanceof Error ? e.message : String(e) }], diff --git a/packages/cli/src/modules/agents/builder/agents-builder.service.ts b/packages/cli/src/modules/agents/builder/agents-builder.service.ts index 1cd006140bc..64924637306 100644 --- a/packages/cli/src/modules/agents/builder/agents-builder.service.ts +++ b/packages/cli/src/modules/agents/builder/agents-builder.service.ts @@ -14,7 +14,7 @@ import { createObservationLogObserveFn, createObservationLogReflectFn } from '@n import { Logger } from '@n8n/backend-common'; import type { User } from '@n8n/db'; import { Service } from '@n8n/di'; -import { applyAgentThinking, tokenUsageToBuilderUsageItems } from '@n8n/instance-ai'; +import { tokenUsageToBuilderUsageItems } from '@n8n/instance-ai'; import { IsNull } from '@n8n/typeorm'; import { jsonParse } from 'n8n-workflow'; @@ -73,6 +73,8 @@ export interface InstanceAiBuilderSessionOptions { * after the host trace's root finalizes. */ memoryTaskObserver?: (event: ScopedMemoryTaskEvent) => void; + /** Host run's abort signal, so a user stop ends the builder's own loop rather than only the host's consumption of it. */ + abortSignal: AbortSignal; } @Service() @@ -113,6 +115,9 @@ export class AgentsBuilderService { const resourceId = user.id; const resultStream = await builder.stream(message, { persistence: { threadId: session.threadId, resourceId }, + abortSignal: session.abortSignal, + // Keep billing a stopped builder turn for the tokens it already spent. + recoverUsageOnAbort: true, }); yield* this.streamFromAgent(resultStream); @@ -168,6 +173,9 @@ export class AgentsBuilderService { const resultStream = await builder.resume('stream', resumeData, { runId, toolCallId, + abortSignal: session.abortSignal, + // Keep billing a stopped builder turn for the tokens it already spent. + recoverUsageOnAbort: true, }); yield* this.streamFromAgent(resultStream); @@ -288,7 +296,7 @@ export class AgentsBuilderService { }), ); - applyAgentThinking(builder, modelConfig); + builder.reasoning('medium'); return builder; } diff --git a/packages/cli/src/modules/agents/builder/builder-model-live-lookup.service.ts b/packages/cli/src/modules/agents/builder/builder-model-live-lookup.service.ts index 1721e12c1a8..3b311825cbc 100644 --- a/packages/cli/src/modules/agents/builder/builder-model-live-lookup.service.ts +++ b/packages/cli/src/modules/agents/builder/builder-model-live-lookup.service.ts @@ -1,9 +1,11 @@ +import { AI_GATEWAY_MANAGED_TAG } from '@n8n/api-types'; import { OutboundHttp } from '@n8n/backend-network'; import type { User } from '@n8n/db'; import { Service } from '@n8n/di'; import { CredentialsFinderService } from '@/credentials/credentials-finder.service'; import { CredentialsService } from '@/credentials/credentials.service'; +import { AiGatewayService } from '@/services/ai-gateway.service'; import { createAiProxyFetch } from '@/utils/ai-proxy-fetch'; import { mapCredentialForProvider } from '../json-config/credential-field-mapping'; @@ -23,13 +25,15 @@ export class BuilderModelLiveLookupService { private readonly credentialsService: CredentialsService, private readonly credentialsFinderService: CredentialsFinderService, private readonly outboundHttp: OutboundHttp, + private readonly aiGatewayService: AiGatewayService, ) {} /** * Returns `{ name, value }` pairs (value = the provider's model id, exactly - * as the provider API expects it). Throws if the credential is not usable by - * the user in the project, its type doesn't match, or the provider has no - * model discovery support. + * as the provider API expects it). For the n8n Connect managed tag, resolves + * the synthetic gateway credential (so discovery hits the gateway's + * allowlisted `/models`); otherwise the credential must be usable by the user + * in the project, its type must match, and the provider must support discovery. */ async list( user: User, @@ -38,6 +42,12 @@ export class BuilderModelLiveLookupService { credentialType: string, provider: string, ): Promise> { + // n8n Connect managed slot: there is no stored credential to decrypt — + // resolve the synthetic gateway credential instead, then discover as usual. + if (credentialId === AI_GATEWAY_MANAGED_TAG) { + return await this.listAiGatewayManagedModels(projectId, provider, user); + } + const usableCredentials = await this.credentialsService.getCredentialsAUserCanUseInAWorkflow( user, { projectId }, @@ -53,6 +63,39 @@ export class BuilderModelLiveLookupService { } const rawData = await this.credentialsService.decrypt(credential, true); + return await this.discoverModels(provider, rawData); + } + + /** + * Lists the chat models n8n Connect (AI Gateway) allows for a provider, via + * the synthetic gateway credential — discovery hits the gateway's `/models`, + * already filtered to the allowlist. Throws if the gateway does not serve it. + */ + private async listAiGatewayManagedModels( + projectId: string, + provider: string, + user?: User, + ): Promise> { + const credentialType = await this.aiGatewayService.getCredentialTypeForProvider(provider); + if (!credentialType) { + throw new Error(`n8n credits does not support the "${provider}" model provider`); + } + const raw = await this.aiGatewayService.getSyntheticCredential({ + credentialType, + userId: user?.id, + projectId, + }); + return await this.discoverModels(provider, raw); + } + + /** + * Runs provider model discovery against a raw credential record (real or the + * gateway synthetic credential), returning `{ name, value }` pairs. + */ + private async discoverModels( + provider: string, + rawData: object, + ): Promise> { const { listModelsForProvider } = await import('@n8n/ai-utilities/model-discovery'); const mapped = mapCredentialForProvider(provider, { apiKey: '', ...rawData }); const apiKey = typeof mapped.apiKey === 'string' ? mapped.apiKey : ''; diff --git a/packages/cli/src/modules/agents/builder/format-preview-context.ts b/packages/cli/src/modules/agents/builder/format-preview-context.ts index 598051b168e..1bb81b6b384 100644 --- a/packages/cli/src/modules/agents/builder/format-preview-context.ts +++ b/packages/cli/src/modules/agents/builder/format-preview-context.ts @@ -37,6 +37,9 @@ function formatTimelineEvent(event: TimelineEvent): string { if (event.type === 'text') { return `Assistant: ${truncate(event.content, MAX_TEXT_CHARS)}`; } + if (event.type === 'reasoning') { + return `Reasoning: ${truncate(event.content, MAX_TEXT_CHARS)}`; + } if (event.type === 'suspension') { return `[suspended waiting on ${event.toolName}]`; } diff --git a/packages/cli/src/modules/agents/builder/interactive/__tests__/resolve-llm.tool.test.ts b/packages/cli/src/modules/agents/builder/interactive/__tests__/resolve-llm.tool.test.ts index 772880be050..350ad11bdee 100644 --- a/packages/cli/src/modules/agents/builder/interactive/__tests__/resolve-llm.tool.test.ts +++ b/packages/cli/src/modules/agents/builder/interactive/__tests__/resolve-llm.tool.test.ts @@ -1,4 +1,5 @@ import type { CredentialListItem, CredentialProvider } from '@n8n/agents'; +import { AI_GATEWAY_MANAGED_TAG } from '@n8n/api-types'; import type { Mock } from 'vitest'; import { LLM_PROVIDER_DEFAULTS, LLM_PROVIDER_PRIORITY } from '../../../llm-provider-defaults'; @@ -563,4 +564,272 @@ describe('resolve_llm tool', () => { }); }); }); + + describe('n8n Connect managed credentials', () => { + it('defaults to n8n Connect with the provider default when the gateway allowlists it', async () => { + const modelLookup = makeModelLookup(async () => [ + { name: 'Claude Sonnet 4.6', value: 'claude-sonnet-4-6' }, + { name: 'Claude Haiku 4.5', value: 'claude-haiku-4-5' }, + ]); + const tool = buildResolveLlmTool({ + credentialProvider: makeProvider([]), + modelLookup, + isProviderServedByGateway: async (provider) => provider === 'anthropic', + freeCredits: makeFreeCredits(), + }); + const result = await tool.handler!({ provider: 'anthropic' }, {}); + + // Default model resolved against the gateway allowlist, not blindly from the static default. + expect(result).toEqual({ + ok: true, + provider: 'anthropic', + model: 'claude-sonnet-4-6', + credentialId: AI_GATEWAY_MANAGED_TAG, + credentialName: 'n8n credits', + }); + expect(modelLookup.list).toHaveBeenCalledWith( + AI_GATEWAY_MANAGED_TAG, + 'anthropicApi', + 'anthropic', + ); + }); + + it('falls back to the first allowlisted model when the gateway does not serve the static default', async () => { + // Gateway serves the provider but not the provider's static default model. + const modelLookup = makeModelLookup(async () => [ + { name: 'Claude Haiku 4.5', value: 'claude-haiku-4-5' }, + ]); + const tool = buildResolveLlmTool({ + credentialProvider: makeProvider([]), + modelLookup, + isProviderServedByGateway: async (provider) => provider === 'anthropic', + freeCredits: makeFreeCredits(), + }); + const result = await tool.handler!({ provider: 'anthropic' }, {}); + + expect(result).toMatchObject({ + ok: true, + provider: 'anthropic', + model: 'claude-haiku-4-5', + credentialId: AI_GATEWAY_MANAGED_TAG, + }); + }); + + it('does not auto-resolve to an un-allowlisted default: reports unknown_model when the gateway lists nothing', async () => { + const tool = buildResolveLlmTool({ + credentialProvider: makeProvider([]), + modelLookup: makeModelLookup(async () => []), + isProviderServedByGateway: async (provider) => provider === 'anthropic', + freeCredits: makeFreeCredits(), + }); + const result = await tool.handler!({ provider: 'anthropic' }, {}); + + expect(result).toMatchObject({ + ok: false, + reason: 'unknown_model', + provider: 'anthropic', + }); + }); + + it('returns missing_credential when no own credential and the gateway does not serve the provider', async () => { + const tool = buildResolveLlmTool({ + credentialProvider: makeProvider([]), + modelLookup: makeModelLookup(), + isProviderServedByGateway: async () => false, + freeCredits: makeFreeCredits(), + }); + const result = await tool.handler!({ provider: 'anthropic' }, {}); + + expect(result).toMatchObject({ + ok: false, + reason: 'missing_credential', + provider: 'anthropic', + }); + }); + + it('does not offer n8n Connect for a provider the user already has credentials for', async () => { + const tool = buildResolveLlmTool({ + credentialProvider: makeProvider([ + { id: 'cred-1', name: 'Anthropic A', type: 'anthropicApi' }, + { id: 'cred-2', name: 'Anthropic B', type: 'anthropicApi' }, + ]), + modelLookup: makeModelLookup(), + isProviderServedByGateway: async () => true, + freeCredits: makeFreeCredits(), + }); + const result = await tool.handler!({ provider: 'anthropic' }, {}); + + // The user has Anthropic keys, so n8n Connect is not offered for Anthropic — + // only their own credentials are ambiguous. + expect(result).toMatchObject({ + ok: false, + reason: 'ambiguous_credential', + credentials: [ + { id: 'cred-1', name: 'Anthropic A' }, + { id: 'cred-2', name: 'Anthropic B' }, + ], + }); + }); + + it('additively appends n8n Connect options for gateway providers the user has no key for', async () => { + const served = new Set(['openai', 'anthropic', 'google']); + const tool = buildResolveLlmTool({ + credentialProvider: makeProvider([ + // Two keys for the top-priority provider (anthropic) keep the result + // ambiguous, so the full credential list is returned instead of an auto-pick. + { id: 'cred-1', name: 'My Anthropic', type: 'anthropicApi' }, + { id: 'cred-1b', name: 'My Anthropic 2', type: 'anthropicApi' }, + { id: 'cred-2', name: 'My xAI', type: 'xAiApi' }, + ]), + modelLookup: makeModelLookup(), + isProviderServedByGateway: async (provider) => served.has(provider), + freeCredits: makeFreeCredits(), + }); + const result = (await tool.handler!({}, {})) as { + credentials?: Array<{ id: string; name: string; type: string; provider: string }>; + }; + + const creds = result.credentials ?? []; + // Own credential preserved. + expect(creds).toContainEqual({ + id: 'cred-1', + name: 'My Anthropic', + type: 'anthropicApi', + provider: 'anthropic', + }); + // n8n Connect options appended for supported providers without an own key (openai, google). + expect(creds).toContainEqual({ + id: AI_GATEWAY_MANAGED_TAG, + name: 'n8n credits', + type: 'openAiApi', + provider: 'openai', + }); + expect(creds).toContainEqual({ + id: AI_GATEWAY_MANAGED_TAG, + name: 'n8n credits', + type: 'googlePalmApi', + provider: 'google', + }); + // No n8n Connect entry for Anthropic — the user already has a key for it. + expect( + creds.filter((c) => c.provider === 'anthropic' && c.id === AI_GATEWAY_MANAGED_TAG), + ).toHaveLength(0); + }); + + it('validates a requested managed model against the gateway allowlist', async () => { + const modelLookup = makeModelLookup(async () => [ + { name: 'GPT-5 mini', value: 'gpt-5-mini' }, + ]); + const tool = buildResolveLlmTool({ + credentialProvider: makeProvider([]), + modelLookup, + isProviderServedByGateway: async (provider) => provider === 'openai', + freeCredits: makeFreeCredits(), + }); + const result = await tool.handler!({ provider: 'openai', model: 'gpt-5-mini' }, {}); + + expect(result).toMatchObject({ + ok: true, + model: 'gpt-5-mini', + credentialId: AI_GATEWAY_MANAGED_TAG, + }); + expect(modelLookup.list).toHaveBeenCalledWith(AI_GATEWAY_MANAGED_TAG, 'openAiApi', 'openai'); + }); + + it('uses the provider to disambiguate repeated managed credential ids', async () => { + const modelLookup = makeModelLookup(async (_credentialId, _credentialType, provider) => + provider === 'google' + ? [{ name: 'Gemini 2.5 Pro', value: 'gemini-2.5-pro' }] + : [{ name: 'GPT-5 mini', value: 'gpt-5-mini' }], + ); + const tool = buildResolveLlmTool({ + credentialProvider: makeProvider([]), + modelLookup, + isProviderServedByGateway: async (provider) => + provider === 'openai' || provider === 'google', + freeCredits: makeFreeCredits(), + }); + const result = await tool.handler!( + { provider: 'google', credentialId: AI_GATEWAY_MANAGED_TAG }, + {}, + ); + + expect(result).toEqual({ + ok: true, + provider: 'google', + model: 'gemini-2.5-pro', + credentialId: AI_GATEWAY_MANAGED_TAG, + credentialName: 'n8n credits', + }); + expect(modelLookup.list).toHaveBeenCalledWith( + AI_GATEWAY_MANAGED_TAG, + 'googlePalmApi', + 'google', + ); + }); + + it('stays ambiguous for a passed-back managed tag when no provider disambiguates it', async () => { + const tool = buildResolveLlmTool({ + credentialProvider: makeProvider([]), + modelLookup: makeModelLookup(), + isProviderServedByGateway: async (provider) => + provider === 'openai' || provider === 'google', + freeCredits: makeFreeCredits(), + }); + // Several managed entries share the tag; without a provider the tool must not + // silently pick the first one. + const result = await tool.handler!({ credentialId: AI_GATEWAY_MANAGED_TAG }, {}); + + expect(result).toMatchObject({ ok: false, reason: 'ambiguous_credential' }); + const { credentials } = result as { + credentials: Array<{ id: string; type: string; provider: string }>; + }; + expect(credentials).toContainEqual({ + id: AI_GATEWAY_MANAGED_TAG, + name: 'n8n credits', + type: 'openAiApi', + provider: 'openai', + }); + expect(credentials).toContainEqual({ + id: AI_GATEWAY_MANAGED_TAG, + name: 'n8n credits', + type: 'googlePalmApi', + provider: 'google', + }); + }); + + it('returns unsupported_provider when the passed-back managed tag is paired with an unknown provider', async () => { + const tool = buildResolveLlmTool({ + credentialProvider: makeProvider([]), + modelLookup: makeModelLookup(), + isProviderServedByGateway: async (provider) => + provider === 'openai' || provider === 'google', + freeCredits: makeFreeCredits(), + }); + const result = await tool.handler!( + { provider: 'not-a-provider', credentialId: AI_GATEWAY_MANAGED_TAG }, + {}, + ); + + expect(result).toMatchObject({ ok: false, reason: 'unsupported_provider' }); + }); + + it('stays ambiguous when the provider is valid but matches no repeated managed entry', async () => { + const tool = buildResolveLlmTool({ + credentialProvider: makeProvider([]), + modelLookup: makeModelLookup(), + // Anthropic is a known provider but not served here, so no managed entry + // matches it — the tag stays ambiguous rather than resolving wrongly. + isProviderServedByGateway: async (provider) => + provider === 'openai' || provider === 'google', + freeCredits: makeFreeCredits(), + }); + const result = await tool.handler!( + { provider: 'anthropic', credentialId: AI_GATEWAY_MANAGED_TAG }, + {}, + ); + + expect(result).toMatchObject({ ok: false, reason: 'ambiguous_credential' }); + }); + }); }); diff --git a/packages/cli/src/modules/agents/builder/interactive/resolve-llm.tool.ts b/packages/cli/src/modules/agents/builder/interactive/resolve-llm.tool.ts index 8f4c431d973..cd0243bd520 100644 --- a/packages/cli/src/modules/agents/builder/interactive/resolve-llm.tool.ts +++ b/packages/cli/src/modules/agents/builder/interactive/resolve-llm.tool.ts @@ -1,6 +1,7 @@ import type { BuiltTool, CredentialListItem, CredentialProvider } from '@n8n/agents'; import { Tool } from '@n8n/agents/tool'; import { isModelDiscoveryProvider } from '@n8n/ai-utilities/model-discovery'; +import { AI_GATEWAY_MANAGED_TAG } from '@n8n/api-types'; import { z } from 'zod'; import { BUILDER_TOOLS } from '../builder-tool-names'; @@ -10,6 +11,9 @@ import { type LlmProviderDefault, } from '../../llm-provider-defaults'; +/** User-facing name written for an n8n credits (AI Gateway managed) model credential. */ +const N8N_CONNECT_CREDENTIAL_NAME = 'n8n credits'; + export interface ModelLookup { list( credentialId: string, @@ -27,6 +31,12 @@ export interface FreeCreditsProvisioner { export interface ResolveLlmToolDeps { credentialProvider: CredentialProvider; modelLookup: ModelLookup; + /** + * Whether n8n Connect (AI Gateway) serves the given model provider (e.g. + * `openai`). When provided, the tool offers n8n Connect as an additional + * credential for served providers the user has no own credential for. + */ + isProviderServedByGateway?(provider: string): Promise; freeCredits: FreeCreditsProvisioner; } @@ -119,6 +129,46 @@ async function resolveModelAgainstLookup( }; } +async function resolveDefaultModelForCredential( + credential: CredentialListItem, + defaults: LlmProviderDefault, + modelLookup: ModelLookup, +) { + // Managed credential: no fallback key, and gateway discovery is authoritative — the + // static default may not be on the allowlist — so pick the default (or first served) + // model from the gateway's list. Own credentials keep the static default. + if (credential.id !== AI_GATEWAY_MANAGED_TAG || !isModelDiscoveryProvider(defaults.provider)) { + return toLlmResolution(credential, defaults); + } + + let availableModels: Array<{ name: string; value: string }>; + try { + availableModels = await modelLookup.list(credential.id, credential.type, defaults.provider); + } catch (error) { + return { + ok: false as const, + reason: 'model_lookup_failed' as const, + provider: defaults.provider, + requestedModel: defaults.defaultModel, + error: error instanceof Error ? error.message : String(error), + }; + } + + if (availableModels.length === 0) { + return { + ok: false as const, + reason: 'unknown_model' as const, + provider: defaults.provider, + requestedModel: defaults.defaultModel, + availableModels, + }; + } + + const preferred = + availableModels.find((m) => m.value === defaults.defaultModel) ?? availableModels[0]; + return toLlmResolution(credential, defaults, preferred.value); +} + export function buildResolveLlmTool(deps: ResolveLlmToolDeps): BuiltTool { return new Tool(BUILDER_TOOLS.RESOLVE_LLM) .description( @@ -135,7 +185,10 @@ export function buildResolveLlmTool(deps: ResolveLlmToolDeps): BuiltTool { 'change on an existing agent, ask immediately and keep the current model and credential until the new one resolves. ' + 'When no matching credential exists and the user is eligible for free OpenAI credits, the tool ' + 'claims them automatically and resolves to openai/gpt-5-mini — the result carries ' + - 'claimedFreeOpenAiCredits: true; tell the user free OpenAI credits were set up. When multiple ' + + 'claimedFreeOpenAiCredits: true; tell the user free OpenAI credits were set up. When the ' + + 'provider has no own credential but n8n credits (the managed option) serves it, the tool ' + + 'resolves to the managed credential — the result credentialName is "n8n credits"; persist it ' + + 'like any credential and tell the user the model runs on n8n credits. When multiple ' + 'providers each have one credential, the tool auto-picks the recommended provider — the result ' + 'carries autoPicked: true and otherProviders; state the pick as changeable, do not ask to confirm it. ' + 'When the user picks between multiple credentials of one provider, pass the picked credentialId ' + @@ -172,11 +225,31 @@ export function buildResolveLlmTool(deps: ResolveLlmToolDeps): BuiltTool { credentialId?: string; }) => { const all = await deps.credentialProvider.list(); - const llmCredentials = all.filter((credential) => LLM_PROVIDER_DEFAULTS[credential.type]); + const ownCredentials = all.filter((credential) => LLM_PROVIDER_DEFAULTS[credential.type]); + + // Offer n8n Connect as an additional credential for each gateway-served + // provider the user has no own credential for. It then flows through the same + // resolution below as any credential: single → auto-use, several → ask, none → + // missing (a legitimate setup prompt, e.g. a provider n8n Connect does not serve). + const managedCredentials: CredentialListItem[] = []; + for (const [credentialType, defaults] of Object.entries(LLM_PROVIDER_DEFAULTS)) { + const hasOwnCredential = ownCredentials.some((c) => c.type === credentialType); + if ( + !hasOwnCredential && + ((await deps.isProviderServedByGateway?.(defaults.provider)) ?? false) + ) { + managedCredentials.push({ + id: AI_GATEWAY_MANAGED_TAG, + name: N8N_CONNECT_CREDENTIAL_NAME, + type: credentialType, + }); + } + } + const llmCredentials = [...ownCredentials, ...managedCredentials]; if (credentialId) { - const credential = llmCredentials.find((c) => c.id === credentialId); - if (!credential) { + const matchingCredentials = llmCredentials.filter((c) => c.id === credentialId); + if (matchingCredentials.length === 0) { return { ok: false as const, reason: 'unknown_credential' as const, @@ -188,11 +261,49 @@ export function buildResolveLlmTool(deps: ResolveLlmToolDeps): BuiltTool { })), }; } + + let credential = matchingCredentials.length === 1 ? matchingCredentials[0] : undefined; + if (!credential && provider) { + const providerEntry = findProviderDefault(provider); + if (!providerEntry) { + return { + ok: false as const, + reason: 'unsupported_provider' as const, + provider, + supportedProviders: Object.values(LLM_PROVIDER_DEFAULTS).map( + (defaults) => defaults.provider, + ), + }; + } + + const [credentialType] = providerEntry; + const providerMatches = matchingCredentials.filter((c) => c.type === credentialType); + if (providerMatches.length === 1) { + credential = providerMatches[0]; + } + } + + if (!credential) { + return { + ok: false as const, + reason: 'ambiguous_credential' as const, + credentials: matchingCredentials.map((c) => { + const defaults = LLM_PROVIDER_DEFAULTS[c.type]; + return { + id: c.id, + name: c.name, + type: c.type, + provider: defaults.provider, + }; + }), + }; + } + const defaults = LLM_PROVIDER_DEFAULTS[credential.type]; if (model?.trim()) { return await resolveModelAgainstLookup(credential, defaults, model, deps.modelLookup); } - return toLlmResolution(credential, defaults); + return await resolveDefaultModelForCredential(credential, defaults, deps.modelLookup); } if (provider) { @@ -218,7 +329,7 @@ export function buildResolveLlmTool(deps: ResolveLlmToolDeps): BuiltTool { if (model?.trim()) { return await resolveModelAgainstLookup(credential, defaults, model, deps.modelLookup); } - return toLlmResolution(credential, defaults); + return await resolveDefaultModelForCredential(credential, defaults, deps.modelLookup); } if ( @@ -251,7 +362,7 @@ export function buildResolveLlmTool(deps: ResolveLlmToolDeps): BuiltTool { if (model?.trim()) { return await resolveModelAgainstLookup(credential, defaults, model, deps.modelLookup); } - return toLlmResolution(credential, defaults); + return await resolveDefaultModelForCredential(credential, defaults, deps.modelLookup); } if (llmCredentials.length === 0 && !model?.trim()) { @@ -269,8 +380,14 @@ export function buildResolveLlmTool(deps: ResolveLlmToolDeps): BuiltTool { const topProvider = LLM_PROVIDER_PRIORITY.find((candidate) => byProvider.has(candidate)); const topCredentials = topProvider ? byProvider.get(topProvider) : undefined; if (topProvider && topCredentials?.length === 1) { + const resolved = await resolveDefaultModelForCredential( + topCredentials[0], + LLM_PROVIDER_DEFAULTS[topCredentials[0].type], + deps.modelLookup, + ); + if (!resolved.ok) return resolved; return { - ...toLlmResolution(topCredentials[0], LLM_PROVIDER_DEFAULTS[topCredentials[0].type]), + ...resolved, autoPicked: true as const, otherProviders: [...byProvider.keys()].filter((other) => other !== topProvider), }; diff --git a/packages/cli/src/modules/agents/builder/prompts/config-mutation.prompt.ts b/packages/cli/src/modules/agents/builder/prompts/config-mutation.prompt.ts index 4d0655b8bf5..ec79eb33ca9 100644 --- a/packages/cli/src/modules/agents/builder/prompts/config-mutation.prompt.ts +++ b/packages/cli/src/modules/agents/builder/prompts/config-mutation.prompt.ts @@ -80,7 +80,7 @@ Use \`patch_config\` with: #### Configure Native Provider Features -- Thinking lives under \`config.thinking\`. +- Reasoning effort lives under \`config.reasoning\` (\`low\`, \`medium\`, or \`high\`). - Prompt caching follows Agent Config Rules above; the write path enforces it automatically. - Web search lives under \`config.webSearch\`. diff --git a/packages/cli/src/modules/agents/builder/prompts/llm-selection.prompt.ts b/packages/cli/src/modules/agents/builder/prompts/llm-selection.prompt.ts index da034d3bd69..a1ff4b4e1a5 100644 --- a/packages/cli/src/modules/agents/builder/prompts/llm-selection.prompt.ts +++ b/packages/cli/src/modules/agents/builder/prompts/llm-selection.prompt.ts @@ -13,10 +13,10 @@ Use this to resolve the target agent's main \`model\` and \`credential\`. ### Workflow 1. For fresh agents, call \`resolve_llm\` once, silently, before the first config write — with provider/model when the user named them, otherwise with no arguments. -2. If \`resolve_llm\` succeeds, persist \`model = "{provider}/{model}"\` and \`credential = credentialId\`. If the result has \`claimedFreeOpenAiCredits: true\`, tell the user you set them up with free OpenAI credits. If it has \`autoPicked: true\`, tell the user which provider and model you picked and that they can ask to change it — do not ask for confirmation and do not raise a trailing model question. +2. If \`resolve_llm\` succeeds, persist \`model = "{provider}/{model}"\` and \`credential = credentialId\`. If the result has \`claimedFreeOpenAiCredits: true\`, tell the user you set them up with free OpenAI credits. If the result's \`credentialName\` is "n8n credits" (its \`credentialId\` is the managed tag), persist it like any credential and tell the user the model runs on n8n credits — never surface the internal "n8n Connect" or "AI Gateway" names. If it has \`autoPicked: true\`, tell the user which provider and model you picked and that they can ask to change it — do not ask for confirmation and do not raise a trailing model question. 3. If the user asks to pick, change, confirm, or configure a model or main credential, ask via \`ask_questions\`; do not ask in prose. 4. During an initial build, if \`resolve_llm\` reports missing or ambiguous credentials/provider, do not ask: mark the model task \`blocked\`, keep building with \`model: ""\` and no \`credential\`, and include the model choice as a question in the trailing \`finish_setup\` call — for ambiguity between multiple credentials of one provider, use the credential names from the resolve_llm result as the question's options — when it resolves, call \`resolve_llm\` with the answer (pass \`credentialId\` when the user picked a specific credential) and patch \`/model\` and \`/credential\` — after \`read_config\`, since the user may have already set the model in the panel. For a model change on an existing agent, ask immediately instead, and never write \`model: ""\` over an existing model — keep the current model and credential until the new one is resolved. -5. If \`resolve_llm\` reports \`unknown_model\`, retry with a plausible returned model value or ask via \`ask_questions\`. +5. If \`resolve_llm\` reports \`unknown_model\`, retry with a plausible returned model value or ask via \`ask_questions\`. If it reports \`model_lookup_failed\` (the provider's model list could not be fetched, e.g. a transient error — possible even on the default no-model path), retry \`resolve_llm\`; do not guess a model. 6. If the model is still unresolved when the user asks to run or publish the agent, leave the draft with \`model: ""\`, do not guess a model, and tell the user the agent needs a model and credential first — in the panel or here in chat. If they dismiss a model-change question on an existing agent, keep the current model and credential unchanged. ### Rules diff --git a/packages/cli/src/modules/agents/entities/agent-chat-attachment.entity.ts b/packages/cli/src/modules/agents/entities/agent-chat-attachment.entity.ts new file mode 100644 index 00000000000..74df41137e7 --- /dev/null +++ b/packages/cli/src/modules/agents/entities/agent-chat-attachment.entity.ts @@ -0,0 +1,62 @@ +import { Project, WithTimestampsAndStringId } from '@n8n/db'; +import { Column, Entity, Index, JoinColumn, ManyToOne, type Relation } from '@n8n/typeorm'; + +import { Agent } from './agent.entity'; + +/** + * A file a user attached to an agent conversation (preview chat or a channel + * message). Bytes live in `BinaryDataService`; message content parts reference + * rows here via `ContentFileRef.id`. + * + * Scoped by `projectId` + `threadId` rather than the agent alone: inline + * agents (config embedded in a workflow node) have no `agents` row, so + * `agentId` is nullable — mirroring how `agents_threads`/`agents_messages` + * carry no agent FK. + */ +@Entity({ name: 'agent_chat_attachments' }) +@Index(['projectId', 'threadId']) +@Index(['agentId', 'threadId']) +export class AgentChatAttachment extends WithTimestampsAndStringId { + @ManyToOne(() => Agent, { onDelete: 'CASCADE', nullable: true }) + @JoinColumn({ name: 'agentId' }) + agent: Relation | null; + + @Column({ type: 'varchar', length: 36, nullable: true }) + agentId: string | null; + + @ManyToOne(() => Project, { onDelete: 'CASCADE' }) + @JoinColumn({ name: 'projectId' }) + project: Relation; + + @Column({ type: 'varchar', length: 36 }) + projectId: string; + + /** Conversation thread id; widths match agent_execution.threadId (prefixed ids exceed a bare uuid). */ + @Column({ type: 'varchar', length: 128 }) + threadId: string; + + /** Per-user scope within the thread (platform user), when known. */ + @Column({ type: 'varchar', length: 255, nullable: true }) + resourceId: string | null; + + /** + * BinaryDataService id (e.g. `filesystem-v2:agents//attachments//binary_data/`; + * inline agents share `agents/inline/`). + * Not a DB FK — see `BinaryDataService` in `n8n-core` for how the bytes are resolved from this id. + */ + @Column({ type: 'text' }) + binaryDataId: string; + + @Column({ type: 'varchar', length: 255 }) + fileName: string; + + @Column({ type: 'varchar', length: 255 }) + mimeType: string; + + @Column({ type: 'int' }) + fileSizeBytes: number; + + /** Surface the file arrived from, e.g. 'chat', 'slack', 'telegram'. */ + @Column({ type: 'varchar', length: 32 }) + source: string; +} diff --git a/packages/cli/src/modules/agents/entities/agent-execution.entity.ts b/packages/cli/src/modules/agents/entities/agent-execution.entity.ts index 16898ca105f..042d98b52c5 100644 --- a/packages/cli/src/modules/agents/entities/agent-execution.entity.ts +++ b/packages/cli/src/modules/agents/entities/agent-execution.entity.ts @@ -53,6 +53,15 @@ export class AgentExecution extends WithTimestampsAndStringId { @Column({ type: 'text', nullable: true }) userMessage: string | null; + /** Metadata of files attached to the user turn ({id, fileName, mimeType, sizeBytes}[]); bytes live in BinaryDataService. */ + @JsonColumn({ nullable: true }) + attachments: Array<{ + id: string; + fileName: string; + mimeType: string; + sizeBytes: number; + }> | null; + @Column({ type: 'varchar', length: 255, nullable: true }) model: string | null; diff --git a/packages/cli/src/modules/agents/entities/agent.entity.ts b/packages/cli/src/modules/agents/entities/agent.entity.ts index 15ed6303283..d8c929c02b4 100644 --- a/packages/cli/src/modules/agents/entities/agent.entity.ts +++ b/packages/cli/src/modules/agents/entities/agent.entity.ts @@ -35,6 +35,10 @@ export class Agent extends WithTimestampsAndStringId { @JsonColumn({ default: '{}' }) skills: Record; + /** Whether MCP clients granted agent scopes may operate on this agent. */ + @Column({ default: false }) + availableInMCP: boolean; + /** UUID identifying the current draft; bumped on the first config save after each publish. */ @Column({ type: 'varchar', length: 36, nullable: true }) versionId: string | null; diff --git a/packages/cli/src/modules/agents/execution-recorder.ts b/packages/cli/src/modules/agents/execution-recorder.ts index 7bb57b311c4..43f3b49dd12 100644 --- a/packages/cli/src/modules/agents/execution-recorder.ts +++ b/packages/cli/src/modules/agents/execution-recorder.ts @@ -1,10 +1,20 @@ import type { StreamChunk } from '@n8n/agents'; +import { + applyForwardedChildChunk, + emptyChildTrace, + settleChildTrace, + type PersistedChildTrace, +} from '@n8n/api-types'; import { isRecord } from '@n8n/utils/is-record'; import { scrubSecretsInText } from '@n8n/utils/scrub-secrets'; import { extractFromAICalls, isFromAIOnlyExpression } from 'n8n-workflow'; import type { ToolRegistry } from './tool-registry'; +/** Cap on child trace characters persisted per delegation. Tighter than the + * live forwarding budget because this is written into every parent execution row. */ +const CHILD_TRACE_PERSIST_CHAR_BUDGET = 4_000; + /** * Walk a nodeParameters tree and substitute templated values with what the * LLM passed: both `$fromAI('key', ...)` placeholders and `={{ $json.path }}` @@ -200,6 +210,7 @@ export interface RecordedUsage { export type TimelineEvent = | { type: 'text'; content: string; timestamp: number; endTime?: number } + | { type: 'reasoning'; content: string; timestamp: number; endTime?: number } | { type: 'tool-call'; kind: 'tool' | 'workflow' | 'node'; @@ -224,6 +235,7 @@ export type TimelineEvent = * detail viewer can show what the node was set up to do. */ nodeParameters?: Record; + childTrace?: PersistedChildTrace; } | { type: 'suspension'; toolName: string; toolCallId: string; timestamp: number }; @@ -255,6 +267,9 @@ export class ExecutionRecorder { /** Text buffer for the current segment (flushed to timeline on boundaries). */ private textBuffer: string[] = []; + /** Reasoning buffer for the current segment (kept separate from user-facing text). */ + private reasoningBuffer: string[] = []; + private model: string | null = null; private finishReason = 'unknown'; @@ -268,20 +283,39 @@ export class ExecutionRecorder { /** Wall-clock when the first text-delta of the current segment arrived. */ private textStartTime: number | null = null; + /** Wall-clock when the current reasoning segment started. */ + private reasoningStartTime: number | null = null; + private _suspended = false; private error: string | null = null; private readonly startTime = Date.now(); + private childTraceChars = new Map(); + /** Feed a stream chunk into the recorder. */ record(chunk: StreamChunk): void { switch (chunk.type) { case 'text-delta': + this.flushReasoningBuffer(); if (this.textStartTime === null) this.textStartTime = Date.now(); this.textParts.push(chunk.delta); this.textBuffer.push(chunk.delta); break; + case 'reasoning-start': + this.flushTextBuffer(); + this.flushReasoningBuffer(); + this.reasoningStartTime = Date.now(); + break; + case 'reasoning-delta': + this.flushTextBuffer(); + if (this.reasoningStartTime === null) this.reasoningStartTime = Date.now(); + this.reasoningBuffer.push(chunk.delta); + break; + case 'reasoning-end': + this.flushReasoningBuffer(); + break; case 'tool-call': this.recordToolCall(chunk.toolCallId, chunk.toolName, chunk.input); break; @@ -291,6 +325,25 @@ export class ExecutionRecorder { case 'tool-execution-end': this.recordToolExecutionEnd(chunk.toolCallId, chunk.isError, chunk.endTime); break; + case 'subagent-chunk': { + if (chunk.parentToolCallId === undefined) break; + const entry = this.findOpenTimelineToolCall(chunk.parentToolCallId); + if (!entry) break; + let inner = chunk.chunk; + if (inner.type === 'text-delta' || inner.type === 'reasoning-delta') { + // Trim rather than skip whole: a delta straddling the cap would + // otherwise be persisted in full and overshoot it. + const used = this.childTraceChars.get(chunk.parentToolCallId) ?? 0; + const remaining = CHILD_TRACE_PERSIST_CHAR_BUDGET - used; + if (remaining <= 0) break; + const delta = inner.delta.slice(0, remaining); + this.childTraceChars.set(chunk.parentToolCallId, used + delta.length); + inner = { ...inner, delta }; + } + entry.childTrace ??= emptyChildTrace(); + applyForwardedChildChunk(entry.childTrace, inner); + break; + } case 'tool-result': this.recordToolResult( chunk.toolCallId, @@ -300,6 +353,7 @@ export class ExecutionRecorder { ); break; case 'finish': + this.flushReasoningBuffer(); this.flushTextBuffer(); this.finishReason = chunk.finishReason; if (chunk.usage) { @@ -313,6 +367,7 @@ export class ExecutionRecorder { this.totalCost = chunk.usage?.cost ?? null; break; case 'tool-call-suspended': + this.flushReasoningBuffer(); this.flushTextBuffer(); this._suspended = true; this.timeline.push({ @@ -323,6 +378,8 @@ export class ExecutionRecorder { }); break; case 'error': { + this.flushReasoningBuffer(); + this.flushTextBuffer(); this.error = normaliseStreamError(chunk.error); break; } @@ -336,6 +393,7 @@ export class ExecutionRecorder { /** Build the final message record after the stream has ended. */ getMessageRecord(): MessageRecord { + this.flushReasoningBuffer(); this.flushTextBuffer(); return { assistantResponse: this.textParts.join(''), @@ -369,11 +427,32 @@ export class ExecutionRecorder { this.textStartTime = null; } + /** Flush accumulated reasoning without including it in `assistantResponse`. */ + private flushReasoningBuffer(): void { + if (this.reasoningBuffer.length === 0) { + this.reasoningStartTime = null; + return; + } + const content = this.reasoningBuffer.join(''); + if (content.trim()) { + const now = Date.now(); + this.timeline.push({ + type: 'reasoning', + content, + timestamp: this.reasoningStartTime ?? now, + endTime: now, + }); + } + this.reasoningBuffer = []; + this.reasoningStartTime = null; + } + /** * Record a discrete `tool-call` chunk from the stream. The matching * `tool-result` chunk closes the timeline entry. */ private recordToolCall(toolCallId: string, name: string, input: unknown): void { + this.flushReasoningBuffer(); this.flushTextBuffer(); const recordedInput = sanitizeExecutionLogValue(input); @@ -434,6 +513,7 @@ export class ExecutionRecorder { if (entry) { entry.endTime = endTime; entry.success = !isError; + if (entry.childTrace) settleChildTrace(entry.childTrace); } } @@ -484,6 +564,7 @@ export class ExecutionRecorder { if (pendingTimeline.endTime === 0) { pendingTimeline.endTime = Date.now(); } + if (pendingTimeline.childTrace) settleChildTrace(pendingTimeline.childTrace); if (pendingTimeline.kind === 'workflow' && isRecord(recordedOutput)) { const execId = recordedOutput.executionId; @@ -494,6 +575,7 @@ export class ExecutionRecorder { return; } + this.flushReasoningBuffer(); this.flushTextBuffer(); const entry = this.registry.get(name); const now = Date.now(); diff --git a/packages/cli/src/modules/agents/instance-ai-builder-delegate.adapter.ts b/packages/cli/src/modules/agents/instance-ai-builder-delegate.adapter.ts index 3b1ba1a74d2..67952d0e59b 100644 --- a/packages/cli/src/modules/agents/instance-ai-builder-delegate.adapter.ts +++ b/packages/cli/src/modules/agents/instance-ai-builder-delegate.adapter.ts @@ -90,6 +90,7 @@ export class InstanceAiBuilderDelegateAdapterService { modelConfig: session.modelConfig, ...(session.telemetry ? { telemetry: session.telemetry } : {}), ...(session.memoryTaskObserver ? { memoryTaskObserver: session.memoryTaskObserver } : {}), + abortSignal: session.abortSignal, }; } diff --git a/packages/cli/src/modules/agents/integrations/__tests__/agent-chat-bridge.test.ts b/packages/cli/src/modules/agents/integrations/__tests__/agent-chat-bridge.test.ts index fa7d8780cd5..86a3f2bb286 100644 --- a/packages/cli/src/modules/agents/integrations/__tests__/agent-chat-bridge.test.ts +++ b/packages/cli/src/modules/agents/integrations/__tests__/agent-chat-bridge.test.ts @@ -1,5 +1,6 @@ import type { Mock } from 'vitest'; import type { StreamChunk } from '@n8n/agents'; +import { MAX_AGENT_CHAT_ATTACHMENT_FILENAME_LENGTH } from '@n8n/api-types'; import { Container } from '@n8n/di'; import { mock } from 'vitest-mock-extended'; import { type Logger } from 'n8n-workflow'; @@ -630,6 +631,209 @@ describe('AgentChatBridge — consumeStream', () => { }); }); + describe('when the inbound message carries attachments', () => { + function makeAttachmentService() { + let counter = 0; + return { + storeInbound: vi.fn( + async (params: { fileName: string; mimeType: string; data: Buffer }) => ({ + id: `att-${++counter}`, + fileName: params.fileName, + mimeType: params.mimeType, + fileSizeBytes: params.data.byteLength, + }), + ), + deleteByIds: vi.fn(async () => {}), + }; + } + + function makeBridge( + agentExecutor: ReturnType, + attachmentService: ReturnType, + ) { + const { bot, handlers } = makeBot(); + new AgentChatBridge( + bot as unknown as ChatBotLike, + 'agent-1', + agentExecutor as never, + componentMapper, + logger, + 'project-1', + streamingIntegration, + undefined, + attachmentService as never, + ); + return handlers; + } + + // PNG magic bytes so the mime sniff confirms the declared type. + const pngBytes = Buffer.from([ + 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, + 0x52, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x08, 0x06, 0x00, 0x00, 0x00, 0x1f, + 0x15, 0xc4, 0x89, + ]); + + it('downloads, stores, and passes attachment refs to the executor', async () => { + const agentExecutor = makeAgentExecutor([finishChunk]); + const attachmentService = makeAttachmentService(); + const handlers = makeBridge(agentExecutor, attachmentService); + const thread = makeThread(); + + await handlers.mention!(thread, { + text: 'look at this', + author: { userId: 'u1', userName: 'user1' }, + attachments: [ + { + type: 'image', + name: 'photo.png', + mimeType: 'image/png', + fetchData: vi.fn().mockResolvedValue(pngBytes), + }, + ], + }); + + expect(attachmentService.storeInbound).toHaveBeenCalledWith( + expect.objectContaining({ + agentId: 'agent-1', + projectId: 'project-1', + threadId: 'agent-1:thread-1', + source: 'test-streaming', + fileName: 'photo.png', + mimeType: 'image/png', + }), + ); + expect(agentExecutor.executeForChatPublished).toHaveBeenCalledWith( + expect.objectContaining({ + message: 'look at this', + attachments: [ + { id: 'att-1', fileName: 'photo.png', mimeType: 'image/png', sizeBytes: 33 }, + ], + }), + ); + }); + + it('deletes stored attachments when execution setup fails before the stream is consumed', async () => { + const agentExecutor = makeAgentExecutor([finishChunk]); + const attachmentService = makeAttachmentService(); + const handlers = makeBridge(agentExecutor, attachmentService); + const thread = makeThread(); + const integrationImpl = registry.get('test-streaming')!; + // The test integration doesn't implement this optional hook; inject one. + integrationImpl.createBridgeExecutionContext = vi + .fn() + .mockRejectedValue(new Error('platform down')); + + try { + await handlers.mention!(thread, { + text: 'look at this', + author: { userId: 'u1', userName: 'user1' }, + attachments: [ + { + type: 'image', + name: 'photo.png', + mimeType: 'image/png', + fetchData: vi.fn().mockResolvedValue(pngBytes), + }, + ], + }); + + expect(attachmentService.deleteByIds).toHaveBeenCalledWith(['att-1']); + expect(agentExecutor.executeForChatPublished).not.toHaveBeenCalled(); + // The mention handler swallows the re-thrown error and reports it to + // the platform thread — the awaited call above resolving (rather than + // rejecting) is by design. + expect(thread.post).toHaveBeenCalledWith( + '⚠️ Something went wrong while processing your request. Please try again.', + ); + } finally { + delete integrationImpl.createBridgeExecutionContext; + } + }); + + it('truncates a platform file name to the fileName column width', async () => { + const agentExecutor = makeAgentExecutor([finishChunk]); + const attachmentService = makeAttachmentService(); + const handlers = makeBridge(agentExecutor, attachmentService); + const thread = makeThread(); + + await handlers.mention!(thread, { + text: 'look at this', + author: { userId: 'u1', userName: 'user1' }, + attachments: [ + { + type: 'image', + name: `${'a'.repeat(MAX_AGENT_CHAT_ATTACHMENT_FILENAME_LENGTH + 10)}.png`, + mimeType: 'image/png', + fetchData: vi.fn().mockResolvedValue(pngBytes), + }, + ], + }); + + expect(attachmentService.storeInbound).toHaveBeenCalledWith( + expect.objectContaining({ + fileName: 'a'.repeat(MAX_AGENT_CHAT_ATTACHMENT_FILENAME_LENGTH), + }), + ); + }); + + it('executes attachment-only messages that have no text', async () => { + const agentExecutor = makeAgentExecutor([finishChunk]); + const attachmentService = makeAttachmentService(); + const handlers = makeBridge(agentExecutor, attachmentService); + + await handlers.mention!(makeThread(), { + text: '', + author: { userId: 'u1', userName: 'user1' }, + attachments: [ + { + type: 'image', + name: 'photo.png', + mimeType: 'image/png', + fetchData: vi.fn().mockResolvedValue(pngBytes), + }, + ], + }); + + expect(agentExecutor.executeForChatPublished).toHaveBeenCalledWith( + expect.objectContaining({ + attachments: [expect.objectContaining({ id: 'att-1' })], + }), + ); + }); + + it('degrades failed downloads and oversize files to text notes without aborting', async () => { + const agentExecutor = makeAgentExecutor([finishChunk]); + const attachmentService = makeAttachmentService(); + const handlers = makeBridge(agentExecutor, attachmentService); + + await handlers.mention!(makeThread(), { + text: 'hi', + author: { userId: 'u1', userName: 'user1' }, + attachments: [ + { + type: 'file', + name: 'broken.pdf', + mimeType: 'application/pdf', + fetchData: vi.fn().mockRejectedValue(new Error('download failed')), + }, + { + type: 'file', + name: 'huge.pdf', + mimeType: 'application/pdf', + size: 50 * 1024 * 1024, + fetchData: vi.fn(), + }, + ], + }); + + expect(attachmentService.storeInbound).not.toHaveBeenCalled(); + const config = agentExecutor.captured[0] as { message: string; attachments?: unknown }; + expect(config.attachments).toBeUndefined(); + expect(config.message).toContain('[Attachment "broken.pdf" could not be processed]'); + expect(config.message).toContain('[Attachment "huge.pdf" was skipped: larger than 10 MB]'); + }); + }); + describe('when integration keeps streaming enabled', () => { it('posts an AsyncIterable whose drained content equals the concatenated deltas', async () => { const { bot, handlers } = makeBot(); @@ -657,6 +861,21 @@ describe('AgentChatBridge — consumeStream', () => { expect(received).toBe('Hello world'); }); + it('omits reasoning chunks from streamed external-channel responses', async () => { + const thread = await runMention(streamingIntegration, [ + { type: 'reasoning-start', id: 'reasoning-1' }, + { type: 'reasoning-delta', id: 'reasoning-1', delta: 'The user is asking...' }, + { type: 'reasoning-end', id: 'reasoning-1' }, + { type: 'text-start', id: 'text-1' }, + { type: 'text-delta', id: 'text-1', delta: 'Final answer' }, + { type: 'text-end', id: 'text-1' }, + finishChunk, + ]); + + expect(thread.post).toHaveBeenCalledOnce(); + expect(await drainIterable(thread.post.mock.calls[0][0])).toBe('Final answer'); + }); + it('posts an error message when the streaming post fails', async () => { const { bot, handlers } = makeBot(); const thread = makeThread(); @@ -1078,10 +1297,14 @@ describe('AgentChatBridge — consumeStream', () => { it('sets a thinking status and buffers the response when resuming a Slack action', async () => { const { bot, handlers } = makeBot(); const thread = makeThread(); + const deleteMessage = vi.fn().mockResolvedValue(undefined); const agentExecutor = { executeForChatPublished: vi.fn(() => toStream([{ type: 'finish', finishReason: 'stop' }])), resumeForChat: vi.fn(() => toStream([ + { type: 'reasoning-start', id: 'reasoning-1' }, + { type: 'reasoning-delta', id: 'reasoning-1', delta: 'The tool was approved.' }, + { type: 'reasoning-end', id: 'reasoning-1' }, { type: 'text-delta', id: 't1', delta: 'Approved ' }, { type: 'text-delta', id: 't1', delta: 'response' }, { type: 'finish', finishReason: 'stop' }, @@ -1106,9 +1329,10 @@ describe('AgentChatBridge — consumeStream', () => { thread, threadId: 'thread-1', user: { userId: 'u2', userName: 'user2' }, - adapter: { deleteMessage: vi.fn().mockResolvedValue(undefined) }, + adapter: { deleteMessage }, }); + expect(deleteMessage).toHaveBeenCalledWith('thread-1', 'card-message-1'); expect(thread.startTyping).toHaveBeenCalledWith('Thinking...'); expect(thread.startTyping.mock.invocationCallOrder[0]).toBeLessThan( thread.post.mock.invocationCallOrder[0], diff --git a/packages/cli/src/modules/agents/integrations/__tests__/helpers/telegram/replay-test-context.ts b/packages/cli/src/modules/agents/integrations/__tests__/helpers/telegram/replay-test-context.ts index 7696de69c41..517634ef756 100644 --- a/packages/cli/src/modules/agents/integrations/__tests__/helpers/telegram/replay-test-context.ts +++ b/packages/cli/src/modules/agents/integrations/__tests__/helpers/telegram/replay-test-context.ts @@ -117,12 +117,18 @@ function telegramMethodFromUrl(url: string): string { * `getMe` returns the bot fixture (so the adapter learns its identity), and * `sendMessage` returns a minimal message; every call is recorded for assertions. */ -function installTelegramApiStub(bot: TelegramUserFixture) { +function installTelegramApiStub(bot: TelegramUserFixture, failedMethods: string[] = []) { let nextMessageId = 1000; return installFetchStub({ match: /api\.telegram\.org/, onRequest: ({ url, body }) => { const method = telegramMethodFromUrl(url); + if (failedMethods.includes(method)) { + return { + apiCall: { method, body }, + responseBody: { ok: false, error_code: 500, description: 'Test failure' }, + }; + } let result: unknown = true; if (method === 'getMe') { result = bot; @@ -158,6 +164,7 @@ export function callbackPayloadWithData( payload: TelegramUpdateFixture, data: string, messageId: number, + messageText?: string, ): TelegramUpdateFixture { return { ...payload, @@ -166,7 +173,11 @@ export function callbackPayloadWithData( ...payload.callback_query, data, message: payload.callback_query.message - ? { ...payload.callback_query.message, message_id: messageId } + ? { + ...payload.callback_query.message, + message_id: messageId, + ...(messageText !== undefined ? { text: messageText } : {}), + } : undefined, } : undefined, @@ -178,9 +189,10 @@ export async function createTelegramReplayContext( options: { stream?: StreamChunk[]; integration?: AgentIntegrationConfig; + failedApiMethods?: string[]; } = {}, ): Promise { - const stub = installTelegramApiStub(fixtures.bot); + const stub = installTelegramApiStub(fixtures.bot, options.failedApiMethods); // Dynamic imports — the chat packages are ESM-only. Unlike production (which // must route through esm-loader to dodge the CJS transform), vitest loads ESM @@ -251,6 +263,7 @@ export async function createTelegramReplayContext( export function getTelegramInlineCallbackData( call: TelegramApiCall | undefined, + buttonIndex = 0, ): string | undefined { // The real adapter sends `reply_markup` as a JSON object in the request body // (Telegram accepts it inline in JSON mode); tolerate a stringified form too. @@ -266,5 +279,5 @@ export function getTelegramInlineCallbackData( if (!markup || typeof markup !== 'object') return undefined; const inlineKeyboard = (markup as { inline_keyboard?: Array> }) .inline_keyboard; - return inlineKeyboard?.[0]?.[0]?.callback_data; + return inlineKeyboard?.flat()[buttonIndex]?.callback_data; } diff --git a/packages/cli/src/modules/agents/integrations/__tests__/integration-action-executor.test.ts b/packages/cli/src/modules/agents/integrations/__tests__/integration-action-executor.test.ts index de0e6323564..f5f3a9f1b78 100644 --- a/packages/cli/src/modules/agents/integrations/__tests__/integration-action-executor.test.ts +++ b/packages/cli/src/modules/agents/integrations/__tests__/integration-action-executor.test.ts @@ -366,6 +366,192 @@ describe('ChatIntegrationActionExecutor', () => { }); }); + it('edits an explicit message in the current Telegram conversation', async () => { + const editMessage = vi.fn().mockResolvedValue({ + id: '123456:1000', + threadId: 'telegram:123456', + raw: {}, + }); + const chat = mock(); + chat.getAdapter.mockReturnValue({ editMessage }); + const chatIntegrationService = mock(); + chatIntegrationService.getChatInstance.mockReturnValue(chat); + const registry = buildRegistry(); + registry.register(new ShortCallbackTelegramIntegration()); + const executor = new ChatIntegrationActionExecutor(chatIntegrationService, registry); + const descriptor = getIntegrationToolConnectionDescriptors([telegram], 'agent-1', () => ({ + actions: ['respond', 'send_dm', 'edit_message'], + }))[0]; + const currentMessageContext = { + integrationConnectionId: 'telegram:cred-telegram', + platform: 'telegram', + target: { type: 'thread' as const, threadId: 'telegram:123456' }, + messageId: '123456:11', + interactingUserId: '123456', + updatedAt: '2026-05-18T10:00:00.000Z', + }; + + const result = await executor.execute({ + descriptor, + action: 'edit_message', + input: { messageId: '123456:1000', message: { text: 'Updated status' } }, + awaitResponse: false, + currentMessageContext, + }); + + expect(chat.getAdapter).toHaveBeenCalledWith('telegram'); + expect(editMessage).toHaveBeenCalledWith('telegram:123456', '123456:1000', 'Updated status'); + expect(result).toEqual({ + ok: true, + messageContext: { + ...currentMessageContext, + messageId: '123456:1000', + updatedAt: expect.any(String), + }, + }); + }); + + it('returns a structured error when edit_message has no current thread', async () => { + const editMessage = vi.fn(); + const chat = mock(); + chat.getAdapter.mockReturnValue({ editMessage }); + const chatIntegrationService = mock(); + chatIntegrationService.getChatInstance.mockReturnValue(chat); + const registry = buildRegistry(); + registry.register(new ShortCallbackTelegramIntegration()); + const executor = new ChatIntegrationActionExecutor(chatIntegrationService, registry); + const descriptor = getIntegrationToolConnectionDescriptors([telegram], 'agent-1', () => ({ + actions: ['respond', 'send_dm', 'edit_message'], + }))[0]; + + const result = await executor.execute({ + descriptor, + action: 'edit_message', + input: { messageId: '123456:1000', message: { text: 'Updated status' } }, + awaitResponse: false, + currentMessageContext: { + integrationConnectionId: 'telegram:cred-telegram', + platform: 'telegram', + target: { type: 'channel', channelId: 'telegram:123456' }, + updatedAt: '2026-05-18T10:00:00.000Z', + }, + }); + + expect(chat.getAdapter).not.toHaveBeenCalled(); + expect(editMessage).not.toHaveBeenCalled(); + expect(result).toEqual({ + ok: false, + error: { + code: 'NO_MESSAGE_CONTEXT', + message: 'There is no current conversation to edit. Send a message first, then try again.', + }, + }); + }); + + it('returns a structured error when the active adapter cannot edit messages', async () => { + const chat = mock(); + chat.getAdapter.mockReturnValue({}); + const chatIntegrationService = mock(); + chatIntegrationService.getChatInstance.mockReturnValue(chat); + const registry = buildRegistry(); + registry.register(new ShortCallbackTelegramIntegration()); + const executor = new ChatIntegrationActionExecutor(chatIntegrationService, registry); + const descriptor = getIntegrationToolConnectionDescriptors([telegram], 'agent-1', () => ({ + actions: ['respond', 'send_dm', 'edit_message'], + }))[0]; + + const result = await executor.execute({ + descriptor, + action: 'edit_message', + input: { messageId: '123456:1000', message: { text: 'Updated status' } }, + awaitResponse: false, + currentMessageContext: { + integrationConnectionId: 'telegram:cred-telegram', + platform: 'telegram', + target: { type: 'thread', threadId: 'telegram:123456' }, + updatedAt: '2026-05-18T10:00:00.000Z', + }, + }); + + expect(result).toEqual({ + ok: false, + error: { + code: 'UNSUPPORTED_ACTION', + message: "The telegram integration can't edit messages. Use a supported action instead.", + }, + }); + }); + + it('maps and shortens callbacks when editing a Telegram rich card', async () => { + const editMessage = vi.fn().mockResolvedValue({ + id: '123456:1000', + threadId: 'telegram:123456', + raw: {}, + }); + const chat = mock(); + chat.getAdapter.mockReturnValue({ editMessage }); + const chatIntegrationService = mock(); + chatIntegrationService.getChatInstance.mockReturnValue(chat); + const shortenCallback = vi.fn(async (_actionId: string, _value: string) => ({ + id: 'short1234', + value: '', + })); + Object.assign(chatIntegrationService, { + getShortenCallback: vi.fn().mockReturnValue(shortenCallback), + }); + const registry = buildRegistry(); + registry.register(new ShortCallbackTelegramIntegration()); + Container.set(ChatIntegrationRegistry, registry); + const executor = new ChatIntegrationActionExecutor(chatIntegrationService, registry); + const descriptor = getIntegrationToolConnectionDescriptors([telegram], 'agent-1', () => ({ + actions: ['respond', 'send_dm', 'edit_message'], + }))[0]; + + const result = await executor.execute({ + descriptor, + action: 'edit_message', + input: { + messageId: '123456:1000', + message: { + text: 'Choose the next step', + card: { + components: [ + { type: 'section', text: 'Review the updated status.' }, + { type: 'button', label: 'Approve', value: 'approve', style: 'primary' }, + ], + }, + }, + }, + awaitResponse: true, + runId: 'run-1', + toolCallId: 'tool-1', + currentMessageContext: { + integrationConnectionId: 'telegram:cred-telegram', + platform: 'telegram', + target: { type: 'thread', threadId: 'telegram:123456' }, + messageId: '123456:11', + updatedAt: '2026-05-18T10:00:00.000Z', + }, + }); + + expect(result).toEqual(expect.objectContaining({ ok: true })); + expect(shortenCallback).toHaveBeenCalledWith( + 'resume:run-1:tool-1:0', + JSON.stringify({ type: 'button', value: 'approve' }), + ); + expect(editMessage).toHaveBeenCalledWith( + 'telegram:123456', + '123456:1000', + expect.objectContaining({ card: expect.any(Object) }), + ); + expect(mockButton).toHaveBeenLastCalledWith({ + id: 'short1234', + label: 'Approve', + style: 'primary', + value: '', + }); + }); + it('adds Slack reactions to the current message context', async () => { const slackAdapter = { addReaction: vi.fn().mockResolvedValue(undefined), diff --git a/packages/cli/src/modules/agents/integrations/__tests__/integration-tools.test.ts b/packages/cli/src/modules/agents/integrations/__tests__/integration-tools.test.ts index a6880f26ed9..a2cff72f36c 100644 --- a/packages/cli/src/modules/agents/integrations/__tests__/integration-tools.test.ts +++ b/packages/cli/src/modules/agents/integrations/__tests__/integration-tools.test.ts @@ -27,6 +27,11 @@ const linear: AgentIntegrationConfig = { credentialId: 'cred-c', }; +const telegram: AgentIntegrationConfig = { + type: 'telegram', + credentialId: 'cred-telegram', +}; + function makeInterruptibleCtx( overrides: Partial = {}, ): InterruptibleToolContext { @@ -450,6 +455,72 @@ describe('integration tools', () => { expect(actionExecutor.execute).not.toHaveBeenCalled(); }); + it('validates the opt-in edit_message action shape', () => { + const telegramTool = createIntegrationActionTool({ + descriptor: getIntegrationToolConnectionDescriptors([telegram], 'agent-1', () => ({ + actions: ['respond', 'send_dm', 'edit_message'], + }))[0], + messageContextStore: mock(), + actionExecutor: mock(), + }).build(); + const telegramSchema = telegramTool.inputSchema as z.ZodType; + + expect( + telegramSchema.safeParse({ + action: 'edit_message', + input: { + messageId: '123456:1000', + message: { text: 'Updated status' }, + }, + }).success, + ).toBe(true); + expect( + telegramSchema.safeParse({ + action: 'edit_message', + input: { message: { text: 'Updated status' } }, + }).success, + ).toBe(false); + expect( + telegramSchema.safeParse({ + action: 'edit_message', + input: { messageId: '', message: { text: 'Updated status' } }, + }).success, + ).toBe(false); + expect( + telegramSchema.safeParse({ + action: 'edit_message', + input: { messageId: '123456:1000' }, + }).success, + ).toBe(false); + expect( + telegramSchema.safeParse({ + action: 'edit_message', + input: { + threadId: 'telegram:999999', + messageId: '123456:1000', + message: { text: 'Updated status' }, + }, + }).success, + ).toBe(false); + expect(telegramTool.description).toContain( + 'Uses the latest message context to choose the conversation', + ); + + const slackTool = createIntegrationActionTool({ + descriptor: getIntegrationToolConnectionDescriptors([slackA], 'agent-1')[0], + messageContextStore: mock(), + actionExecutor: mock(), + }).build(); + const slackSchema = slackTool.inputSchema as z.ZodType; + + expect( + slackSchema.safeParse({ + action: 'edit_message', + input: { messageId: '123456:1000', message: { text: 'Updated status' } }, + }).success, + ).toBe(false); + }); + it('action tool schema requires platform IDs for explicit user and channel targets', () => { const tool = createIntegrationActionTool({ descriptor: getIntegrationToolConnectionDescriptors([slackA])[0], diff --git a/packages/cli/src/modules/agents/integrations/__tests__/n8n-checkpoint-storage.test.ts b/packages/cli/src/modules/agents/integrations/__tests__/n8n-checkpoint-storage.test.ts index b1f54acc57b..0bd868e5f18 100644 --- a/packages/cli/src/modules/agents/integrations/__tests__/n8n-checkpoint-storage.test.ts +++ b/packages/cli/src/modules/agents/integrations/__tests__/n8n-checkpoint-storage.test.ts @@ -1,7 +1,7 @@ import type { SerializableAgentState } from '@n8n/agents'; +import type { ModuleRegistry } from '@n8n/backend-common'; import { mockLogger } from '@n8n/backend-test-utils'; import type { AgentsConfig } from '@n8n/config'; -import type { ModuleRegistry } from '@n8n/backend-common'; import type { InstanceSettings } from 'n8n-core'; import { mock } from 'vitest-mock-extended'; @@ -64,6 +64,19 @@ describe('N8NCheckpointStorage', () => { await expect(service.claimForResume('run-1', suspendedState)).resolves.toBe(false); }); + it('atomically expires a suspended checkpoint for an agent', async () => { + const { service, repository } = makeService(); + repository.cancelSuspended.mockResolvedValue(true); + + await expect(service.cancelSuspended('run-1', suspendedState, 'agent-1')).resolves.toBe(true); + + expect(repository.cancelSuspended).toHaveBeenCalledWith( + 'run-1', + 'agent-1', + JSON.stringify(suspendedState), + ); + }); + it('expires a checkpoint by runId when no agentId is given', async () => { const { service, repository } = makeService(); diff --git a/packages/cli/src/modules/agents/integrations/__tests__/n8n-memory.test.ts b/packages/cli/src/modules/agents/integrations/__tests__/n8n-memory.test.ts index 4d4e4c7bb7f..8e71e96f231 100644 --- a/packages/cli/src/modules/agents/integrations/__tests__/n8n-memory.test.ts +++ b/packages/cli/src/modules/agents/integrations/__tests__/n8n-memory.test.ts @@ -204,6 +204,39 @@ describe('N8nMemory', () => { } as unknown as AgentMessageEntity; } + describe('saveMessages — hydrated file parts', () => { + it('strips data from fileRef file parts before upserting, keeping the reference', async () => { + const bytes = new Uint8Array([1, 2, 3]); + await memory.saveMessages({ + threadId: 'thread-1', + resourceId: 'user-1', + messages: [ + { + id: 'm-1', + createdAt: new Date('2026-05-12T10:00:00Z'), + role: 'user', + content: [ + { type: 'text', text: 'look at this' }, + { + type: 'file', + mediaType: 'image/png', + data: bytes, + fileRef: { id: 'att-1', fileName: 'photo.png', sizeBytes: 3 }, + }, + ], + }, + ], + }); + + expect(messageRepository.upsert).toHaveBeenCalledTimes(1); + const [entities] = messageRepository.upsert.mock.calls[0]; + const content = (entities as Array<{ content: { content: unknown[] } }>)[0].content; + const filePart = content.content[1] as Record; + expect(filePart.data).toBeUndefined(); + expect(filePart.fileRef).toEqual({ id: 'att-1', fileName: 'photo.png', sizeBytes: 3 }); + }); + }); + describe('getMessages — resourceId filter', () => { beforeEach(() => { messageRepository.find.mockResolvedValue([]); diff --git a/packages/cli/src/modules/agents/integrations/agent-chat-bridge.ts b/packages/cli/src/modules/agents/integrations/agent-chat-bridge.ts index efb6f0799e4..dc39d731451 100644 --- a/packages/cli/src/modules/agents/integrations/agent-chat-bridge.ts +++ b/packages/cli/src/modules/agents/integrations/agent-chat-bridge.ts @@ -1,10 +1,21 @@ import type { AgentMessage, StreamChunk } from '@n8n/agents'; +import { + MAX_AGENT_CHAT_ATTACHMENT_FILENAME_LENGTH, + MAX_AGENT_CHAT_ATTACHMENT_SIZE_BYTES, + MAX_AGENT_CHAT_ATTACHMENT_SIZE_MB, + MAX_AGENT_CHAT_ATTACHMENTS_PER_MESSAGE, +} from '@n8n/api-types'; import { Container } from '@n8n/di'; -import type { Author, Chat, Message, Thread } from 'chat'; +import type { Attachment, Author, Chat, Message, Thread } from 'chat'; import type { Logger } from 'n8n-workflow'; +import { + AgentChatAttachmentService, + type StoredAttachmentRef, +} from '../agent-chat-attachment.service'; import type { AgentExecutionOrchestratorService } from '../agent-execution-orchestrator.service'; import { integrationMemoryResourceId } from '../utils/agent-memory-scope'; +import { resolveInboundMimeType } from '../utils/inbound-attachments'; import type { AgentChatIntegration, BridgeExecutionContext, @@ -17,8 +28,8 @@ import { AgentChatStreamConsumer, type SuspensionHandlingResult, } from './agent-chat-stream-consumer'; -import { buildSuspendCardPayload } from './agent-chat-suspension-cards'; -import { CallbackStore } from './callback-store'; +import { buildSuspendCardPayload, isApprovalSuspendPayload } from './agent-chat-suspension-cards'; +import { CallbackStore, type CallbackMetadata } from './callback-store'; import type { ComponentMapper, ShortenCallback } from './component-mapper'; import { IntegrationMessageContextService } from './integration-message-context.service'; import type { AgentIntegrationConfig } from '@n8n/api-types'; @@ -30,6 +41,7 @@ interface AgentExecutor { agentId: string; projectId: string; message: string; + attachments?: StoredAttachmentRef[]; memory: { threadId: InternalThread; resourceId: string }; integrationType?: string; }): AsyncGenerator; @@ -86,6 +98,7 @@ export class AgentChatBridge { private readonly n8nProjectId: string, private readonly integration: AgentIntegrationConfig, messageContextStore?: IntegrationMessageContextService, + private readonly attachmentService?: AgentChatAttachmentService, ) { this.integrationImpl = Container.get(ChatIntegrationRegistry).get(integration.type); this.messageContextBridge = new AgentChatMessageContextBridge( @@ -112,6 +125,10 @@ export class AgentChatBridge { agentService, logger, callbackStore: this.callbackStore, + deleteActionMessageBeforeResume: + this.integrationImpl?.deleteActionMessageBeforeResume ?? true, + formatApprovalDecisionMessage: (params) => + this.integrationImpl?.formatApprovalDecisionMessage?.(params), resolvePlatformThreadId: this.resolvePlatformThreadId.bind(this), toAgentThreadId: this.toAgentThreadId.bind(this), getPlatformAgentContext: this.getPlatformAgentContext.bind(this), @@ -147,11 +164,18 @@ export class AgentChatBridge { integration: AgentIntegrationConfig, ): AgentChatBridge { const agentExecutor: AgentExecutor = { - async *executeForChatPublished({ memory, agentId: aid, message, integrationType }) { + async *executeForChatPublished({ + memory, + agentId: aid, + message, + attachments, + integrationType, + }) { yield* agentService.executeForChatPublished({ agentId: aid, projectId: n8nProjectId, message, + attachments, memory: { threadId: memory.threadId.id, resourceId: memory.resourceId, @@ -175,6 +199,7 @@ export class AgentChatBridge { n8nProjectId, integration, Container.get(IntegrationMessageContextService), + Container.get(AgentChatAttachmentService), ); } @@ -237,11 +262,11 @@ export class AgentChatBridge { * Returns a callback shortener function for platforms with short callback * data limits (Telegram). Returns undefined for other platforms. */ - getShortenCallback(): ShortenCallback | undefined { + getShortenCallback(metadata?: CallbackMetadata): ShortenCallback | undefined { if (!this.callbackStore) return undefined; const store = this.callbackStore; return async (actionId: string, value: string) => { - const key = await store.store(actionId, value); + const key = await store.store(actionId, value, metadata); return { id: key, value: '' }; }; } @@ -258,26 +283,36 @@ export class AgentChatBridge { const { isNewMention } = options; const platformAgentContext = this.getPlatformAgentContext(); const text = this.prepareInboundText(message.text, platformAgentContext).trim(); - if (!text) return; + // `?? []` guards rehydrated/serialized messages that predate the field. + const inboundAttachments = message.attachments ?? []; + if (!text && inboundAttachments.length === 0) return; const platformThreadId = this.resolvePlatformThreadId(thread); const threadId = this.toAgentThreadId(platformThreadId); + const resourceId = integrationMemoryResourceId(this.integration.type, message.author.userId); + const { attachments, attachmentNotes } = await this.storeInboundAttachments( + inboundAttachments, + threadId.id, + resourceId, + ); const statusRetry = new AbortController(); - // Platform status hooks, the lazy `message.subject` fetch, and any - // thread-history fetch are all remote round-trips on independent - // resources — run them concurrently. - const [bridgeExecutionContext, subject] = await Promise.all([ - this.resolveBridgeExecutionContext( - thread, - message, - platformAgentContext, - statusRetry, - isNewMention, - ), - this.messageContextBridge.resolveSubject(message), - ]); - const statusHandle = onceStatusHandle(bridgeExecutionContext.statusHandle); + let statusHandle: ReturnType | undefined; + let consumeStarted = false; try { + // Platform status hooks, the lazy `message.subject` fetch, and any + // thread-history fetch are all remote round-trips on independent + // resources — run them concurrently. + const [bridgeExecutionContext, subject] = await Promise.all([ + this.resolveBridgeExecutionContext( + thread, + message, + platformAgentContext, + statusRetry, + isNewMention, + ), + this.messageContextBridge.resolveSubject(message), + ]); + statusHandle = onceStatusHandle(bridgeExecutionContext.statusHandle); await this.messageContextBridge.updateLatest(threadId.id, message.author.userId, thread, { messageId: message.id, interactingUserId: message.author.userId, @@ -288,24 +323,35 @@ export class AgentChatBridge { // the platform user identity so episodic recall works across threads for // the same user while staying isolated between users. // Always run the published snapshot — integrations are production traffic. + const textWithNotes = [text, ...attachmentNotes].filter(Boolean).join('\n'); const agentInput = bridgeExecutionContext.historyContext - ? `${bridgeExecutionContext.historyContext}\n\n${text}` - : text; + ? `${bridgeExecutionContext.historyContext}\n\n${textWithNotes}` + : textWithNotes; const stream = this.agentService.executeForChatPublished({ agentId: this.agentId, projectId: this.n8nProjectId, message: agentInput, + attachments: attachments.length > 0 ? attachments : undefined, memory: { threadId, - resourceId: integrationMemoryResourceId(this.integration.type, message.author.userId), + resourceId, }, integrationType: this.integration.type, }); + consumeStarted = true; await this.streamConsumer.consume(stream, thread, { forceBuffered: bridgeExecutionContext.forceBuffered, statusHandle, }); + } catch (error) { + // The execution generator is lazy: a throw before consumption started means + // nothing ran and nothing references this turn's attachments — remove them + // (best-effort). Once consumption starts, the turn may be persisted. + if (!consumeStarted && attachments.length > 0) { + await this.attachmentService?.deleteByIds(attachments.map((ref) => ref.id)).catch(() => {}); + } + throw error; } finally { statusRetry.abort(); // The stream consumer clears the status right before the first response; @@ -317,6 +363,97 @@ export class AgentChatBridge { } } + /** + * Download and persist inbound platform attachments (Slack/Telegram adapters + * deliver them with authenticated `fetchData`). Oversize or failed downloads + * degrade to a text note on the user turn — an attachment problem never + * aborts the run. Returns stored refs plus the notes to append. + */ + private async storeInboundAttachments( + inboundAttachments: Attachment[], + threadId: string, + resourceId: string, + ): Promise<{ attachments: StoredAttachmentRef[]; attachmentNotes: string[] }> { + const attachments: StoredAttachmentRef[] = []; + const attachmentNotes: string[] = []; + if (!this.attachmentService || inboundAttachments.length === 0) { + return { attachments, attachmentNotes }; + } + + const skipped = inboundAttachments.slice(MAX_AGENT_CHAT_ATTACHMENTS_PER_MESSAGE); + for (const attachment of skipped) { + attachmentNotes.push( + `[Attachment "${attachment.name ?? 'file'}" was not processed: too many attachments in one message]`, + ); + } + + for (const attachment of inboundAttachments.slice(0, MAX_AGENT_CHAT_ATTACHMENTS_PER_MESSAGE)) { + // Platform attachments bypass DTO validation, so cap the name to the + // fileName column width here. + const name = (attachment.name ?? 'attachment').slice( + 0, + MAX_AGENT_CHAT_ATTACHMENT_FILENAME_LENGTH, + ); + try { + if ( + attachment.size !== undefined && + attachment.size > MAX_AGENT_CHAT_ATTACHMENT_SIZE_BYTES + ) { + attachmentNotes.push( + `[Attachment "${name}" was skipped: larger than ${MAX_AGENT_CHAT_ATTACHMENT_SIZE_MB} MB]`, + ); + continue; + } + + const data = await this.fetchAttachmentData(attachment); + if (!data || data.byteLength === 0) { + attachmentNotes.push(`[Attachment "${name}" could not be downloaded]`); + continue; + } + if (data.byteLength > MAX_AGENT_CHAT_ATTACHMENT_SIZE_BYTES) { + attachmentNotes.push( + `[Attachment "${name}" was skipped: larger than ${MAX_AGENT_CHAT_ATTACHMENT_SIZE_MB} MB]`, + ); + continue; + } + + const mimeType = await resolveInboundMimeType(attachment.mimeType, data); + const stored = await this.attachmentService.storeInbound({ + agentId: this.agentId, + projectId: this.n8nProjectId, + threadId, + resourceId, + source: this.integration.type, + fileName: name, + mimeType, + data, + }); + attachments.push({ + id: stored.id, + fileName: stored.fileName, + mimeType: stored.mimeType, + sizeBytes: stored.fileSizeBytes, + }); + } catch (error) { + this.logger.warn('[AgentChatBridge] Failed to ingest attachment', { + agentId: this.agentId, + threadId, + error: error instanceof Error ? error.message : String(error), + }); + attachmentNotes.push(`[Attachment "${name}" could not be processed]`); + } + } + + return { attachments, attachmentNotes }; + } + + private async fetchAttachmentData(attachment: Attachment): Promise { + if (attachment.fetchData) return await attachment.fetchData(); + if (Buffer.isBuffer(attachment.data)) return attachment.data; + if (attachment.data) return Buffer.from(await attachment.data.arrayBuffer()); + return null; + } + private async resolveBridgeExecutionContext( thread: Thread, message: Message, @@ -354,6 +491,9 @@ export class AgentChatBridge { const cardPayload = buildSuspendCardPayload(suspendPayload); if (!cardPayload) return 'skipped'; + const callbackMetadata: CallbackMetadata | undefined = isApprovalSuspendPayload(suspendPayload) + ? { kind: 'approval', groupId: JSON.stringify([runId, toolCallId]) } + : undefined; try { const card = await this.componentMapper.toCard( @@ -361,7 +501,7 @@ export class AgentChatBridge { runId, toolCallId, chunk.resumeSchema, - this.getShortenCallback(), + this.getShortenCallback(callbackMetadata), this.integration.type, ); await thread.post({ card }); diff --git a/packages/cli/src/modules/agents/integrations/agent-chat-hitl-resume-handler.ts b/packages/cli/src/modules/agents/integrations/agent-chat-hitl-resume-handler.ts index 0f6a518ebb8..0ef33d24f5f 100644 --- a/packages/cli/src/modules/agents/integrations/agent-chat-hitl-resume-handler.ts +++ b/packages/cli/src/modules/agents/integrations/agent-chat-hitl-resume-handler.ts @@ -3,7 +3,11 @@ import type { AgentIntegrationConfig } from '@n8n/api-types'; import type { ActionEvent, Thread } from 'chat'; import type { Logger } from 'n8n-workflow'; -import type { BridgeResumeExecutionContext, PlatformAgentContext } from './agent-chat-integration'; +import type { + ApprovalDecisionMessageFormatter, + BridgeResumeExecutionContext, + PlatformAgentContext, +} from './agent-chat-integration'; import { onceStatusHandle } from './agent-chat-integration'; import type { AgentChatMessageContextBridge } from './agent-chat-message-context'; import type { AgentChatStreamConsumer } from './agent-chat-stream-consumer'; @@ -28,6 +32,8 @@ interface AgentChatHitlResumeHandlerOptions { agentService: ResumeExecutor; logger: Logger; callbackStore?: CallbackStore; + deleteActionMessageBeforeResume: boolean; + formatApprovalDecisionMessage?: ApprovalDecisionMessageFormatter; resolvePlatformThreadId: (thread: Thread) => string; toAgentThreadId: (platformThreadId: string) => InternalThread; getPlatformAgentContext: () => PlatformAgentContext; @@ -76,7 +82,7 @@ export class AgentChatHitlResumeHandler { ...this.options.getPlatformAgentContext(), }); - await this.cleanUpBeforeResume(event); + await this.cleanUpBeforeResume(event, parsed.resumeData, callbackData.kind); await this.executeResume(thread, parsed.runId, parsed.toolCallId, parsed.resumeData); } @@ -124,7 +130,7 @@ export class AgentChatHitlResumeHandler { actionId: string, value: string | undefined, thread: Thread, - ): Promise<{ actionId: string; value: string | undefined } | null> { + ): Promise<{ actionId: string; value: string | undefined; kind?: 'approval' } | null> { if (!this.options.callbackStore) return { actionId, value }; const resolved = await this.options.callbackStore.resolve(actionId); @@ -135,23 +141,58 @@ export class AgentChatHitlResumeHandler { ); return null; } - return { actionId: resolved.actionId, value: resolved.value }; + return { actionId: resolved.actionId, value: resolved.value, kind: resolved.kind }; } - /** - * Delete the card message and apply platform-specific workarounds before - * resuming the agent. - */ - private async cleanUpBeforeResume(event: ActionEvent): Promise { + /** Clean up the action message according to integration policy before resuming. */ + private async cleanUpBeforeResume( + event: ActionEvent, + resumeData: unknown, + callbackKind?: 'approval', + ): Promise { + if (this.options.deleteActionMessageBeforeResume) { + try { + await event.adapter.deleteMessage(event.threadId, event.messageId); + } catch (deleteError) { + this.options.logger.warn('[AgentChatBridge] Failed to delete card message', { + error: deleteError instanceof Error ? deleteError.message : String(deleteError), + }); + } + return; + } + + if (callbackKind !== 'approval') return; + const approved = this.getApprovalDecision(resumeData); + if (approved === undefined) return; + try { - await event.adapter.deleteMessage(event.threadId, event.messageId); - } catch (deleteError) { - this.options.logger.warn('[AgentChatBridge] Failed to delete card message', { - error: deleteError instanceof Error ? deleteError.message : String(deleteError), + const message = this.options.formatApprovalDecisionMessage?.({ + approved, + raw: event.raw, + user: event.user, + }); + if (message) { + await event.adapter.editMessage(event.threadId, event.messageId, message); + } + } catch (editError) { + this.options.logger.warn('[AgentChatBridge] Failed to settle approval card', { + error: editError instanceof Error ? editError.message : String(editError), }); } } + private getApprovalDecision(resumeData: unknown): boolean | undefined { + if ( + typeof resumeData !== 'object' || + resumeData === null || + !('approved' in resumeData) || + typeof resumeData.approved !== 'boolean' + ) { + return undefined; + } + return resumeData.approved; + } + /** * Guard against double resumption, then resume the agent and stream the * response back into the thread. diff --git a/packages/cli/src/modules/agents/integrations/agent-chat-integration.ts b/packages/cli/src/modules/agents/integrations/agent-chat-integration.ts index 0a35806fdee..eb4551df8e6 100644 --- a/packages/cli/src/modules/agents/integrations/agent-chat-integration.ts +++ b/packages/cli/src/modules/agents/integrations/agent-chat-integration.ts @@ -102,6 +102,16 @@ export interface BridgeMessageContextParams { isNewMention: boolean; } +export interface ApprovalDecisionMessageParams { + approved: boolean; + raw: unknown; + user: Author; +} + +export type ApprovalDecisionMessageFormatter = ( + params: ApprovalDecisionMessageParams, +) => string | undefined; + /** * A chat platform (Slack, Telegram, …) that an agent can be connected to. * @@ -180,6 +190,9 @@ export abstract class AgentChatIntegration { */ readonly needsShortCallbackData: boolean = false; + /** Whether action messages are deleted before the agent resumes. */ + readonly deleteActionMessageBeforeResume: boolean = true; + /** * True if the bridge should buffer streaming output and post it as a single * message instead of streaming text deltas via post-and-edit. @@ -290,6 +303,9 @@ export abstract class AgentChatIntegration { /** Optional text normalization before the message is handed to the agent. */ prepareInboundText?(text: string, context: PlatformAgentContext): string; + /** Replacement text for approval cards preserved after a user responds. */ + formatApprovalDecisionMessage?(params: ApprovalDecisionMessageParams): string | undefined; + /** * Optional per-message execution policy for platform-specific bridge behavior, * such as status indicators or forcing buffered output for a specific thread. diff --git a/packages/cli/src/modules/agents/integrations/agent-chat-stream-consumer.ts b/packages/cli/src/modules/agents/integrations/agent-chat-stream-consumer.ts index e96e7f9eb17..df07ecf2882 100644 --- a/packages/cli/src/modules/agents/integrations/agent-chat-stream-consumer.ts +++ b/packages/cli/src/modules/agents/integrations/agent-chat-stream-consumer.ts @@ -163,13 +163,6 @@ export class AgentChatStreamConsumer { if (delta.trim()) responseState.hasVisibleResponse = true; break; } - case 'reasoning-delta': { - const { delta } = chunk; - await responseLifecycle.startStreamingResponse(); - textStream.yield?.(`_${delta}_`); - if (delta.trim()) responseState.hasVisibleResponse = true; - break; - } case 'tool-call-suspended': { await responseLifecycle.startDiscreteResponse(); const result = await this.options.handleSuspension(chunk, thread); @@ -199,8 +192,8 @@ export class AgentChatStreamConsumer { } break; default: - // Ignore other chunk types (finish, tool-input-*, - // start-step, finish-step, etc.) + // Ignore non-user-visible chunks (reasoning, finish, + // tool-input-*, start-step, finish-step, etc.) break; } } @@ -293,9 +286,6 @@ export class AgentChatStreamConsumer { case 'text-delta': buffer += chunk.delta; break; - case 'reasoning-delta': - buffer += `_${chunk.delta}_`; - break; case 'tool-call-suspended': { await flushBuffer(); await responseLifecycle.startDiscreteResponse(); diff --git a/packages/cli/src/modules/agents/integrations/agent-chat-suspension-cards.ts b/packages/cli/src/modules/agents/integrations/agent-chat-suspension-cards.ts index 6b013bad751..c734cd17350 100644 --- a/packages/cli/src/modules/agents/integrations/agent-chat-suspension-cards.ts +++ b/packages/cli/src/modules/agents/integrations/agent-chat-suspension-cards.ts @@ -23,7 +23,7 @@ export function isIntegrationActionSuspendPayload(value: unknown): boolean { ); } -function isApprovalSuspendPayload(value: unknown): value is ApprovalSuspendPayload { +export function isApprovalSuspendPayload(value: unknown): value is ApprovalSuspendPayload { return ( isRecord(value) && value.type === 'approval' && diff --git a/packages/cli/src/modules/agents/integrations/callback-store.ts b/packages/cli/src/modules/agents/integrations/callback-store.ts index ef9aae24e95..beae22a032d 100644 --- a/packages/cli/src/modules/agents/integrations/callback-store.ts +++ b/packages/cli/src/modules/agents/integrations/callback-store.ts @@ -5,8 +5,12 @@ import { TtlMap } from '@/utils/ttl-map'; export interface CallbackPayload { actionId: string; value: string; + kind?: 'approval'; + groupId?: string; } +export type CallbackMetadata = Pick; + /** * Maps short callback keys to full action payloads. * @@ -23,12 +27,12 @@ export class CallbackStore { } /** Store a callback payload and return a short key (8 hex chars). */ - async store(actionId: string, value: string): Promise { + async store(actionId: string, value: string, metadata: CallbackMetadata = {}): Promise { let key: string; do { key = randomBytes(4).toString('hex'); } while (this.entries.has(key)); - this.entries.set(key, { actionId, value }); + this.entries.set(key, { actionId, value, ...metadata }); return key; } @@ -36,7 +40,13 @@ export class CallbackStore { async resolve(key: string): Promise { const entry = this.entries.get(key); if (!entry) return undefined; - this.entries.delete(key); + if (entry.groupId) { + for (const [candidateKey, candidate] of this.entries) { + if (candidate.groupId === entry.groupId) this.entries.delete(candidateKey); + } + } else { + this.entries.delete(key); + } return entry; } diff --git a/packages/cli/src/modules/agents/integrations/integration-action-executor.ts b/packages/cli/src/modules/agents/integrations/integration-action-executor.ts index efaa5365510..dba5cf646c2 100644 --- a/packages/cli/src/modules/agents/integrations/integration-action-executor.ts +++ b/packages/cli/src/modules/agents/integrations/integration-action-executor.ts @@ -1,6 +1,7 @@ import { richMessageSchema } from '@n8n/api-types'; import { Service } from '@n8n/di'; -import type { SentMessage } from 'chat'; +import { isRecord } from '@n8n/utils/is-record'; +import type { Adapter, SentMessage } from 'chat'; import { z } from 'zod'; import { ChatIntegrationRegistry } from './agent-chat-integration'; @@ -38,12 +39,18 @@ const sendChannelMessageInputSchema = z.object({ channelId: z.string().min(1), message: messageSchema, }); +const editMessageInputSchema = z + .object({ + messageId: z.string().min(1), + message: messageSchema, + }) + .strict(); type MessagePayload = z.infer; /** * Dispatches action invocations between cross-platform actions (`respond`, - * `send_dm`, `send_channel_message`) and platform-specific actions + * `send_dm`, `send_channel_message`, `edit_message`) and platform-specific actions * (`add_reaction`, `create_issue`, `create_comment`, …) owned by each * {@link AgentChatIntegration} subclass. */ @@ -111,6 +118,9 @@ export class ChatIntegrationActionExecutor implements IntegrationActionExecutor if (params.action === 'send_dm') { return await this.sendDirectMessage(chat, params); } + if (params.action === 'edit_message') { + return await this.editMessageInCurrentThread(chat, params); + } // Platform-specific actions delegate to the integration implementation. if (integrationDef?.executeAction) { @@ -183,6 +193,44 @@ export class ChatIntegrationActionExecutor implements IntegrationActionExecutor }; } + private async editMessageInCurrentThread( + chat: ChatInstance, + params: ExecuteParams, + ): Promise { + const input = editMessageInputSchema.parse(params.input); + const currentMessageContext = params.currentMessageContext; + const threadId = currentMessageContext?.target.threadId; + if (!currentMessageContext || !threadId) { + return integrationError( + INTEGRATION_ERROR_CODES.NO_MESSAGE_CONTEXT, + 'There is no current conversation to edit. Send a message first, then try again.', + ); + } + + const adapter = chat.getAdapter(params.descriptor.integration.type); + if (!supportsMessageEditing(adapter)) { + return integrationError( + INTEGRATION_ERROR_CODES.UNSUPPORTED_ACTION, + `The ${params.descriptor.integration.type} integration can't edit messages. Use a supported action instead.`, + ); + } + + const edited = await adapter.editMessage( + threadId, + input.messageId, + await this.toPostable(params.descriptor, input.message, params), + ); + + return { + ok: true, + messageContext: { + ...currentMessageContext, + messageId: edited.id, + updatedAt: new Date().toISOString(), + }, + }; + } + private async sendChannelMessage( chat: ChatInstance, params: ExecuteParams, @@ -247,6 +295,10 @@ export class ChatIntegrationActionExecutor implements IntegrationActionExecutor } } +function supportsMessageEditing(adapter: unknown): adapter is Pick { + return isRecord(adapter) && typeof adapter.editMessage === 'function'; +} + interface ExecuteParams { descriptor: IntegrationToolConnectionDescriptor; action: IntegrationAction; diff --git a/packages/cli/src/modules/agents/integrations/integration-tool-definitions.ts b/packages/cli/src/modules/agents/integrations/integration-tool-definitions.ts index 6c42bfe0d55..534309e7504 100644 --- a/packages/cli/src/modules/agents/integrations/integration-tool-definitions.ts +++ b/packages/cli/src/modules/agents/integrations/integration-tool-definitions.ts @@ -174,6 +174,19 @@ const sendChannelMessageActionInputSchema = z.object({ .strict(), }); +const editMessageActionInputSchema = z.object({ + action: z.literal('edit_message'), + input: z + .object({ + messageId: z + .string() + .min(1) + .describe('Message ID returned by a previous action or get_current_message_context.'), + message: messageSchema, + }) + .strict(), +}); + export const GENERIC_CONTEXT_QUERY_TOOL_DEFINITIONS = [ { name: 'get_current_message_context', @@ -246,6 +259,15 @@ export const GENERIC_ACTION_TOOL_DEFINITIONS = [ }, ] satisfies IntegrationActionDefinition[]; +const EDIT_MESSAGE_ACTION_TOOL_DEFINITIONS = [ + { + name: 'edit_message', + inputSchema: editMessageActionInputSchema, + description: + "edit_message: input.messageId and input.message are required. Uses the latest message context to choose the conversation, so input.threadId isn't accepted.", + }, +] satisfies IntegrationActionDefinition[]; + export const DEFAULT_INTEGRATION_CONTEXT_TOOL_DEFINITIONS = GENERIC_CONTEXT_QUERY_TOOL_DEFINITIONS; export const DEFAULT_INTEGRATION_ACTION_TOOL_DEFINITIONS = GENERIC_ACTION_TOOL_DEFINITIONS; @@ -257,6 +279,7 @@ const ALL_CONTEXT_QUERY_TOOL_DEFINITIONS = [ const ALL_ACTION_TOOL_DEFINITIONS = [ ...GENERIC_ACTION_TOOL_DEFINITIONS, + ...EDIT_MESSAGE_ACTION_TOOL_DEFINITIONS, ...SLACK_ACTION_TOOL_DEFINITIONS, ...LINEAR_ACTION_TOOL_DEFINITIONS, ] satisfies IntegrationActionDefinition[]; diff --git a/packages/cli/src/modules/agents/integrations/integration-tool-types.ts b/packages/cli/src/modules/agents/integrations/integration-tool-types.ts index 417e95ea526..df0b52453dc 100644 --- a/packages/cli/src/modules/agents/integrations/integration-tool-types.ts +++ b/packages/cli/src/modules/agents/integrations/integration-tool-types.ts @@ -80,6 +80,7 @@ export type IntegrationAction = | 'respond' | 'send_dm' | 'send_channel_message' + | 'edit_message' | 'add_reaction' | 'create_issue' | 'update_issue' diff --git a/packages/cli/src/modules/agents/integrations/n8n-checkpoint-storage.ts b/packages/cli/src/modules/agents/integrations/n8n-checkpoint-storage.ts index 2ba299918db..357df8f9a67 100644 --- a/packages/cli/src/modules/agents/integrations/n8n-checkpoint-storage.ts +++ b/packages/cli/src/modules/agents/integrations/n8n-checkpoint-storage.ts @@ -1,4 +1,8 @@ -import type { CheckpointStore, SerializableAgentState } from '@n8n/agents'; +import { + stripHydratedFileData, + type CheckpointStore, + type SerializableAgentState, +} from '@n8n/agents'; import { Logger, ModuleRegistry } from '@n8n/backend-common'; import { AgentsConfig } from '@n8n/config'; import { Time } from '@n8n/constants'; @@ -10,6 +14,18 @@ import { strict } from 'node:assert'; import { AgentCheckpointRepository } from '../repositories/agent-checkpoint.repository'; +/** File parts are checkpointed reference-only (a `Uint8Array` would not survive JSON round-tripping). */ +function stripStateFileData(state: SerializableAgentState): SerializableAgentState { + if (!state.messageList) return state; + return { + ...state, + messageList: { + ...state.messageList, + messages: state.messageList.messages.map(stripHydratedFileData), + }, + }; +} + type CheckpointStatus = | { status: 'expired'; @@ -57,9 +73,10 @@ export class N8NCheckpointStorage { async save( key: string, - state: SerializableAgentState, + checkpointState: SerializableAgentState, agentId: string | null = null, ): Promise { + const state = stripStateFileData(checkpointState); const existing = await this.agentCheckpointRepository.findOneBy({ runId: key }); if (existing) { @@ -94,7 +111,8 @@ export class N8NCheckpointStorage { return state; } - async claimForResume(key: string, state: SerializableAgentState): Promise { + async claimForResume(key: string, checkpointState: SerializableAgentState): Promise { + const state = stripStateFileData(checkpointState); return await this.agentCheckpointRepository.claimForResume( key, JSON.stringify(state), @@ -102,6 +120,19 @@ export class N8NCheckpointStorage { ); } + async cancelSuspended( + key: string, + state: SerializableAgentState, + agentId: string, + ): Promise { + if (state.status !== 'suspended') return false; + return await this.agentCheckpointRepository.cancelSuspended( + key, + agentId, + JSON.stringify(state), + ); + } + async getStatus(key: string): Promise { const checkpoint = await this.agentCheckpointRepository.findOneBy({ runId: key }); if (!checkpoint) return { status: 'not-found' }; diff --git a/packages/cli/src/modules/agents/integrations/n8n-memory.ts b/packages/cli/src/modules/agents/integrations/n8n-memory.ts index bdf3482a5c5..c63f0be441a 100644 --- a/packages/cli/src/modules/agents/integrations/n8n-memory.ts +++ b/packages/cli/src/modules/agents/integrations/n8n-memory.ts @@ -39,6 +39,7 @@ import { type ObservationLogTaskLockHandle, type RetrievedEpisodicMemoryEntry, type Thread, + stripHydratedFileData, } from '@n8n/agents'; import { Service } from '@n8n/di'; import type { EntityManager, FindOperator, FindOptionsWhere } from '@n8n/typeorm'; @@ -283,7 +284,8 @@ export class N8nMemoryImpl // the column is preserved on conflict; updatedAt is set manually because // the @BeforeUpdate hook does not fire during upsert. const now = new Date(); - const entities = args.messages.map((dbMsg) => { + const entities = args.messages.map((message) => { + const dbMsg = stripHydratedFileData(message); const role = 'role' in dbMsg ? (dbMsg.role as string) : 'custom'; const type = 'type' in dbMsg ? (dbMsg.type as string) : null; return { diff --git a/packages/cli/src/modules/agents/integrations/platforms/__tests__/telegram/integration.test.ts b/packages/cli/src/modules/agents/integrations/platforms/__tests__/telegram/integration.test.ts index 38af435b226..682333a1891 100644 --- a/packages/cli/src/modules/agents/integrations/platforms/__tests__/telegram/integration.test.ts +++ b/packages/cli/src/modules/agents/integrations/platforms/__tests__/telegram/integration.test.ts @@ -89,6 +89,21 @@ const makeIntegration = ( }; }; +describe('TelegramIntegration capabilities', () => { + it('advertises message editing with contextual targeting guidance', () => { + const { integration } = makeIntegration(); + + expect(integration.actions).toEqual(['respond', 'send_dm', 'edit_message']); + expect(integration.builderGuidance.capabilities).toContain( + 'Edit existing messages in the current Telegram conversation.', + ); + expect(integration.actionToolGuidance).toEqual([ + 'For edit_message, pass the messageId returned by a previous Telegram action or get_current_message_context. The current Telegram conversation is selected automatically.', + 'After a Telegram callback, edit the source message promptly so stale buttons are removed.', + ]); + }); +}); + describe('TelegramIntegration.onBeforeConnect', () => { let agentRepository: Mocked; let integration: TelegramIntegration; diff --git a/packages/cli/src/modules/agents/integrations/platforms/__tests__/telegram/recorded-integration.test.ts b/packages/cli/src/modules/agents/integrations/platforms/__tests__/telegram/recorded-integration.test.ts index d3a786e0d48..049aafbbf2f 100644 --- a/packages/cli/src/modules/agents/integrations/platforms/__tests__/telegram/recorded-integration.test.ts +++ b/packages/cli/src/modules/agents/integrations/platforms/__tests__/telegram/recorded-integration.test.ts @@ -1,8 +1,12 @@ +import type { StreamChunk } from '@n8n/agents'; import { readFileSync } from 'fs'; import { jsonParse } from 'n8n-workflow'; import { join } from 'path'; -import type { TelegramReplayFixtures } from '../../../__tests__/helpers/telegram/replay-test-context'; +import type { + TelegramReplayContext, + TelegramReplayFixtures, +} from '../../../__tests__/helpers/telegram/replay-test-context'; import { callbackPayloadWithData, createTelegramReplayContext, @@ -31,6 +35,49 @@ const recordedSession = jsonParse( ), ); +const approvalCardText = [ + 'Approval required', + 'The agent wants to run this tool: Send Telegram message', + 'Tool: Send Telegram message', + 'Input: {', + ' "text": "Ship it?"', + '}', +].join('\n'); + +function approvalSuspensionStream(): StreamChunk[] { + return [ + { + type: 'tool-call-suspended', + runId: 'run-telegram-1', + toolCallId: 'tool-approval-1', + toolName: 'approval', + suspendPayload: { + type: 'approval', + toolName: 'send_telegram_message', + displayName: 'Send Telegram message', + args: { text: 'Ship it?' }, + }, + resumeSchema: { + type: 'object', + properties: { approved: { type: 'boolean' } }, + required: ['approved'], + }, + }, + { type: 'finish', finishReason: 'stop' }, + ]; +} + +async function clickTelegramCard( + ctx: TelegramReplayContext, + callbackData: string, + messageText = approvalCardText, +): Promise { + ctx.nextStream([{ type: 'finish', finishReason: 'stop' }]); + await ctx.sendTelegramWebhook( + callbackPayloadWithData(telegramFixtures.callbackBase, callbackData, 1000, messageText), + ); +} + function getRecordedWebhook() { const record = recordedSession.find( (entry): entry is Extract => @@ -88,12 +135,18 @@ describe('Telegram recorded integration replay', () => { vi.restoreAllMocks(); }); - it('replays the captured Telegram session webhook and outbound post', async () => { + it('replays the captured Telegram session without exposing reasoning', async () => { const fixtures = recordedTelegramFixtures(); const ctx = await createTelegramReplayContext(fixtures, { stream: [ + { type: 'reasoning-start', id: 'recorded-reasoning' }, + { type: 'reasoning-delta', id: 'recorded-reasoning', delta: 'The ' }, + { type: 'reasoning-delta', id: 'recorded-reasoning', delta: 'user is asking...' }, + { type: 'reasoning-end', id: 'recorded-reasoning' }, + { type: 'text-start', id: 'recorded-response' }, { type: 'text-delta', id: 'recorded-response', delta: 'Test response' }, + { type: 'text-end', id: 'recorded-response' }, { type: 'finish', finishReason: 'stop' }, ], }); @@ -115,7 +168,9 @@ describe('Telegram recorded integration replay', () => { channelId: 'telegram:123456789', }, }); - expect(ctx.lastApiCall('sendMessage')?.body).toMatchObject({ + const sentMessages = ctx.apiCalls.filter((call) => call.method === 'sendMessage'); + expect(sentMessages).toHaveLength(1); + expect(sentMessages[0]?.body).toMatchObject({ chat_id: '123456789', text: 'Test response', }); @@ -170,19 +225,116 @@ describe('Telegram recorded integration replay', () => { } }); - it('uses short callback data for Telegram rich cards and resumes the agent from callback queries', async () => { + it.each([ + { buttonIndex: 0, approved: true, outcome: '✅ Approved by Alice' }, + { buttonIndex: 1, approved: false, outcome: '🚫 Declined by Alice' }, + ])( + 'automatically settles Telegram approval cards in place ($outcome)', + async ({ buttonIndex, approved, outcome }) => { + const ctx = await createTelegramReplayContext(telegramFixtures, { + stream: approvalSuspensionStream(), + }); + try { + await ctx.sendTelegramWebhook(telegramFixtures.mention); + + const cardMessage = ctx.lastApiCall('sendMessage'); + const callbackData = getTelegramInlineCallbackData(cardMessage, buttonIndex); + expect(callbackData).toBeDefined(); + expect(Buffer.byteLength(callbackData ?? '', 'utf8')).toBeLessThanOrEqual(64); + const sendMessageCountBeforeCallback = ctx.apiCalls.filter( + (call) => call.method === 'sendMessage', + ).length; + + await clickTelegramCard(ctx, callbackData ?? ''); + + expect(ctx.agentExecutor.resumeForChat).toHaveBeenCalledWith( + expect.objectContaining({ + runId: 'run-telegram-1', + toolCallId: 'tool-approval-1', + resumeData: { approved }, + integrationType: 'telegram', + }), + ); + expect(ctx.latestContext()).toMatchObject({ + platform: 'telegram', + messageId: '123456:1000', + target: { threadId: 'telegram:123456' }, + }); + + expect(ctx.lastApiCall('deleteMessage')).toBeUndefined(); + expect(ctx.lastApiCall('answerCallbackQuery')?.body).toMatchObject({ + callback_query_id: 'callback-1', + }); + expect(ctx.lastApiCall('editMessageText')?.body).toMatchObject({ + chat_id: '123456', + message_id: 1000, + text: `${approvalCardText}\n\n${outcome}`, + reply_markup: { inline_keyboard: [] }, + }); + expect(ctx.apiCalls.filter((call) => call.method === 'sendMessage')).toHaveLength( + sendMessageCountBeforeCallback, + ); + } finally { + await ctx.shutdown(); + } + }, + ); + + it('invalidates sibling Telegram approval callbacks after a decision', async () => { + const ctx = await createTelegramReplayContext(telegramFixtures, { + stream: approvalSuspensionStream(), + }); + try { + await ctx.sendTelegramWebhook(telegramFixtures.mention); + + const cardMessage = ctx.lastApiCall('sendMessage'); + const approveCallback = getTelegramInlineCallbackData(cardMessage, 0); + const denyCallback = getTelegramInlineCallbackData(cardMessage, 1); + expect(approveCallback).toBeDefined(); + expect(denyCallback).toBeDefined(); + + await clickTelegramCard(ctx, approveCallback ?? ''); + await clickTelegramCard(ctx, denyCallback ?? ''); + + expect(ctx.agentExecutor.resumeForChat).toHaveBeenCalledTimes(1); + } finally { + await ctx.shutdown(); + } + }); + + it('resumes Telegram approvals when settling the card fails', async () => { + const ctx = await createTelegramReplayContext(telegramFixtures, { + failedApiMethods: ['editMessageText'], + stream: approvalSuspensionStream(), + }); + try { + await ctx.sendTelegramWebhook(telegramFixtures.mention); + + const cardMessage = ctx.lastApiCall('sendMessage'); + const callbackData = getTelegramInlineCallbackData(cardMessage); + await clickTelegramCard(ctx, callbackData ?? ''); + + expect(ctx.lastApiCall('editMessageText')).toBeDefined(); + expect(ctx.agentExecutor.resumeForChat).toHaveBeenCalledTimes(1); + expect(ctx.agentExecutor.resumeForChat).toHaveBeenCalledWith( + expect.objectContaining({ resumeData: { approved: true } }), + ); + } finally { + await ctx.shutdown(); + } + }); + + it('leaves non-approval Telegram cards unchanged after callbacks', async () => { const ctx = await createTelegramReplayContext(telegramFixtures, { stream: [ { type: 'tool-call-suspended', runId: 'run-telegram-1', - toolCallId: 'tool-approval-1', - toolName: 'approval', + toolCallId: 'tool-card-1', + toolName: 'custom_card', suspendPayload: { - type: 'approval', - toolName: 'send_telegram_message', - displayName: 'Send Telegram message', - args: { text: 'Ship it?' }, + title: 'Choose an action', + components: [{ type: 'button', label: 'Continue', value: 'continue' }], }, }, { type: 'finish', finishReason: 'stop' }, @@ -191,38 +343,11 @@ describe('Telegram recorded integration replay', () => { try { await ctx.sendTelegramWebhook(telegramFixtures.mention); - const cardMessage = ctx.lastApiCall('sendMessage'); - const callbackData = getTelegramInlineCallbackData(cardMessage); - expect(callbackData).toBeDefined(); - expect(Buffer.byteLength(callbackData ?? '', 'utf8')).toBeLessThanOrEqual(64); + const callbackData = getTelegramInlineCallbackData(ctx.lastApiCall('sendMessage')); + await clickTelegramCard(ctx, callbackData ?? '', 'Choose an action'); - ctx.nextStream([ - { type: 'text-delta', id: 'resume-text', delta: 'Approved' }, - { type: 'finish', finishReason: 'stop' }, - ]); - await ctx.sendTelegramWebhook( - callbackPayloadWithData(telegramFixtures.callbackBase, callbackData ?? '', 1000), - ); - - expect(ctx.agentExecutor.resumeForChat).toHaveBeenCalledWith( - expect.objectContaining({ - runId: 'run-telegram-1', - toolCallId: 'tool-approval-1', - resumeData: { value: 'true' }, - integrationType: 'telegram', - }), - ); - expect(ctx.lastApiCall('deleteMessage')?.body).toMatchObject({ - chat_id: '123456', - message_id: 1000, - }); - expect(ctx.lastApiCall('answerCallbackQuery')?.body).toMatchObject({ - callback_query_id: 'callback-1', - }); - expect(ctx.lastApiCall('sendMessage')?.body).toMatchObject({ - chat_id: '123456', - text: 'Approved', - }); + expect(ctx.agentExecutor.resumeForChat).toHaveBeenCalledTimes(1); + expect(ctx.lastApiCall('editMessageText')).toBeUndefined(); } finally { await ctx.shutdown(); } diff --git a/packages/cli/src/modules/agents/integrations/platforms/slack-bridge-behavior.ts b/packages/cli/src/modules/agents/integrations/platforms/slack-bridge-behavior.ts index 6e31fb118d8..cde317bb5d7 100644 --- a/packages/cli/src/modules/agents/integrations/platforms/slack-bridge-behavior.ts +++ b/packages/cli/src/modules/agents/integrations/platforms/slack-bridge-behavior.ts @@ -1,4 +1,5 @@ import { isRecord } from '@n8n/utils/is-record'; +import { sleep } from '@n8n/utils/sleep'; import type { Message, Thread } from 'chat'; import type { @@ -218,7 +219,11 @@ async function setSlackAssistantStatusWithRetry( } } - if (!(await sleep(SLACK_STATUS_RETRY_DELAY_MS, options.statusRetry?.signal))) return; + try { + await sleep(SLACK_STATUS_RETRY_DELAY_MS, options.statusRetry?.signal); + } catch { + return; + } // The status may have been cleared while we were sleeping. Bail out so the // retry doesn't re-set "Thinking..." over an already-cleared status. if (options.statusRetry?.signal.aborted) return; @@ -383,19 +388,3 @@ function getSlackErrorCode(error: unknown): string | undefined { if (!isRecord(data)) return undefined; return stringValue(data.error); } - -async function sleep(ms: number, signal?: AbortSignal): Promise { - if (signal?.aborted) return false; - return await new Promise((resolve) => { - const timeout = setTimeout(() => { - signal?.removeEventListener('abort', abort); - resolve(true); - }, ms); - const abort = () => { - clearTimeout(timeout); - signal?.removeEventListener('abort', abort); - resolve(false); - }; - signal?.addEventListener('abort', abort, { once: true }); - }); -} diff --git a/packages/cli/src/modules/agents/integrations/platforms/telegram-integration.ts b/packages/cli/src/modules/agents/integrations/platforms/telegram-integration.ts index 5a8d05ae721..f2be7decea1 100644 --- a/packages/cli/src/modules/agents/integrations/platforms/telegram-integration.ts +++ b/packages/cli/src/modules/agents/integrations/platforms/telegram-integration.ts @@ -4,6 +4,7 @@ import { Logger } from '@n8n/backend-common'; import { OutboundHttp, SsrfProtectionService } from '@n8n/backend-network'; import { SsrfProtectionConfig } from '@n8n/config'; import { Service } from '@n8n/di'; +import { isRecord } from '@n8n/utils/is-record'; import type { Thread, Author } from 'chat'; import { createHmac } from 'crypto'; import { InstanceSettings } from 'n8n-core'; @@ -16,6 +17,7 @@ import { AgentRepository } from '../../repositories/agent.repository'; import { AgentChatIntegration, type AgentChatIntegrationContext, + type ApprovalDecisionMessageParams, type BridgeExecutionContext, type BridgeMessageContextParams, type BridgeResumeExecutionContext, @@ -57,11 +59,13 @@ export class TelegramIntegration extends AgentChatIntegration { capabilities: [ 'Receive Telegram messages as agent triggers.', 'Respond in Telegram conversations and send direct Telegram messages.', + 'Edit existing messages in the current Telegram conversation.', 'Render Telegram-compatible cards with buttons.', ], useIntegrationWhen: [ 'The agent should be chatted with from Telegram or act as a Telegram bot.', 'The agent needs to reply to Telegram users in the same conversation context.', + 'The agent needs to update a Telegram message in the current conversation.', 'The agent should send Telegram messages as the connected Telegram bot.', ], useNodeToolWhen: [ @@ -77,12 +81,33 @@ export class TelegramIntegration extends AgentChatIntegration { 'fields', ]; - readonly actionToolDefinitions = resolveIntegrationActionDefinitions(['respond', 'send_dm']); + readonly actionToolDefinitions = resolveIntegrationActionDefinitions([ + 'respond', + 'send_dm', + 'edit_message', + ]); + + readonly actionToolGuidance = [ + 'For edit_message, pass the messageId returned by a previous Telegram action or get_current_message_context. The current Telegram conversation is selected automatically.', + 'After a Telegram callback, edit the source message promptly so stale buttons are removed.', + ]; readonly needsShortCallbackData = true; + readonly deleteActionMessageBeforeResume = false; + readonly disableStreaming = true; + formatApprovalDecisionMessage({ approved, raw, user }: ApprovalDecisionMessageParams): string { + const originalText = + isRecord(raw) && isRecord(raw.message) && typeof raw.message.text === 'string' + ? raw.message.text + : ''; + const responder = user.fullName || user.userName || user.userId; + const outcome = approved ? `✅ Approved by ${responder}` : `🚫 Declined by ${responder}`; + return originalText ? `${originalText}\n\n${outcome}` : outcome; + } + readonly formatThreadId = { fromSdk: (thread: Thread) => { const adapter = thread.adapter; diff --git a/packages/cli/src/modules/agents/integrations/slack-app-setup.service.ts b/packages/cli/src/modules/agents/integrations/slack-app-setup.service.ts index d8e626e8edd..98bb76ed356 100644 --- a/packages/cli/src/modules/agents/integrations/slack-app-setup.service.ts +++ b/packages/cli/src/modules/agents/integrations/slack-app-setup.service.ts @@ -60,6 +60,7 @@ const REQUIRED_BOT_SCOPES = [ 'mpim:history', 'mpim:read', 'mpim:write', + 'reactions:write', 'search:read.public', 'users:read', 'users:read.email', diff --git a/packages/cli/src/modules/agents/json-config/__tests__/mcp-client-factory.test.ts b/packages/cli/src/modules/agents/json-config/__tests__/mcp-client-factory.test.ts index 35cde0eec7c..203a15dc33f 100644 --- a/packages/cli/src/modules/agents/json-config/__tests__/mcp-client-factory.test.ts +++ b/packages/cli/src/modules/agents/json-config/__tests__/mcp-client-factory.test.ts @@ -6,17 +6,23 @@ import { UserError } from 'n8n-workflow'; import type { OauthService } from '@/oauth/oauth.service'; -import { buildMcpClientForServer, mapApprovalToSdk } from '../mcp-client-factory'; +import { + buildMcpClientForServer, + listMcpServerTools, + mapApprovalToSdk, +} from '../mcp-client-factory'; // --------------------------------------------------------------------------- // Module mocks // --------------------------------------------------------------------------- const mcpClientCtor = vi.fn(); +const listToolsMock = vi.fn(); +const closeMock = vi.fn(); vi.mock('@n8n/agents', () => ({ McpClient: vi.fn(function (configs: unknown) { mcpClientCtor(configs); - return { configs, close: vi.fn() }; + return { configs, close: closeMock, listTools: listToolsMock }; }), })); @@ -567,3 +573,54 @@ describe('buildMcpClientForServer — credential domain restrictions', () => { expect(credentialProvider.resolve).not.toHaveBeenCalled(); }); }); + +// --------------------------------------------------------------------------- +// listMcpServerTools +// --------------------------------------------------------------------------- + +describe('listMcpServerTools', () => { + const deps = () => ({ + credentialProvider: mock(), + oauthService: mock(), + projectId: 'proj-1', + proxyFetch, + }); + + beforeEach(() => { + mcpClientCtor.mockReset(); + listToolsMock.mockReset(); + closeMock.mockReset(); + closeMock.mockResolvedValue(undefined); + }); + + it('returns name/description pairs (empty description fallback) and closes the client', async () => { + listToolsMock.mockResolvedValue([ + { name: 'echo', description: 'Echoes input' }, + { name: 'bare', description: undefined }, + ]); + + const tools = await listMcpServerTools(makeServer(), deps()); + + expect(tools).toEqual([ + { name: 'echo', description: 'Echoes input' }, + { name: 'bare', description: '' }, + ]); + expect(closeMock).toHaveBeenCalledTimes(1); + }); + + it('still closes the client and propagates the error when listing fails', async () => { + listToolsMock.mockRejectedValue(new Error('connection refused')); + + await expect(listMcpServerTools(makeServer(), deps())).rejects.toThrow('connection refused'); + expect(closeMock).toHaveBeenCalledTimes(1); + }); + + it('swallows close errors', async () => { + listToolsMock.mockResolvedValue([{ name: 'echo', description: 'd' }]); + closeMock.mockRejectedValue(new Error('already closed')); + + await expect(listMcpServerTools(makeServer(), deps())).resolves.toEqual([ + { name: 'echo', description: 'd' }, + ]); + }); +}); diff --git a/packages/cli/src/modules/agents/json-config/__tests__/model-config.test.ts b/packages/cli/src/modules/agents/json-config/__tests__/model-config.test.ts new file mode 100644 index 00000000000..8801dfaec0a --- /dev/null +++ b/packages/cli/src/modules/agents/json-config/__tests__/model-config.test.ts @@ -0,0 +1,66 @@ +import type { CredentialProvider, ResolvedCredential } from '@n8n/agents'; +import { AI_GATEWAY_MANAGED_TAG } from '@n8n/api-types'; +import { mock } from 'vitest-mock-extended'; + +import { + resolveCredentialAwareModelConfig, + type AiGatewayModelCredentialResolver, +} from '../model-config'; + +describe('resolveCredentialAwareModelConfig', () => { + it('resolves a real credential via the credential provider (unchanged path)', async () => { + const credentialProvider = mock(); + credentialProvider.resolve.mockResolvedValue({ + apiKey: 'real-key', + url: 'https://api.openai.com', + } as ResolvedCredential); + + const result = await resolveCredentialAwareModelConfig( + 'openai/gpt-5', + 'cred-123', + credentialProvider, + ); + + expect(credentialProvider.resolve).toHaveBeenCalledWith('cred-123'); + expect(result).toEqual({ + id: 'openai/gpt-5', + apiKey: 'real-key', + baseURL: 'https://api.openai.com', + }); + }); + + it('resolves the managed tag through the provider gateway resolver, keyed by provider prefix', async () => { + const credentialProvider = mock(); + credentialProvider.resolveAiGatewayModelCredential.mockResolvedValue({ + apiKey: 'gateway-jwt', + url: 'https://gw.example/v1/gateway/openai/v1', + } as ResolvedCredential); + + const result = await resolveCredentialAwareModelConfig( + 'openai/gpt-5', + AI_GATEWAY_MANAGED_TAG, + credentialProvider, + ); + + expect(credentialProvider.resolveAiGatewayModelCredential).toHaveBeenCalledWith('openai'); + expect(credentialProvider.resolve).not.toHaveBeenCalled(); + expect(result).toEqual({ + id: 'openai/gpt-5', + apiKey: 'gateway-jwt', + baseURL: 'https://gw.example/v1/gateway/openai/v1', + }); + }); + + it('falls through to resolve() for the managed tag when the provider has no gateway capability', async () => { + const resolve = vi.fn().mockResolvedValue({ apiKey: 'x' }); + const credentialProvider = { resolve } as unknown as CredentialProvider; + + await resolveCredentialAwareModelConfig( + 'openai/gpt-5', + AI_GATEWAY_MANAGED_TAG, + credentialProvider, + ); + + expect(resolve).toHaveBeenCalledWith(AI_GATEWAY_MANAGED_TAG); + }); +}); diff --git a/packages/cli/src/modules/agents/json-config/__tests__/sanitize-unknown-agent-credentials.test.ts b/packages/cli/src/modules/agents/json-config/__tests__/sanitize-unknown-agent-credentials.test.ts index 06f5201a603..1e2db8aff3c 100644 --- a/packages/cli/src/modules/agents/json-config/__tests__/sanitize-unknown-agent-credentials.test.ts +++ b/packages/cli/src/modules/agents/json-config/__tests__/sanitize-unknown-agent-credentials.test.ts @@ -1,8 +1,63 @@ +import { AI_GATEWAY_MANAGED_TAG } from '@n8n/api-types'; + import { sanitizeUnknownAgentCredentials } from '../sanitize-unknown-agent-credentials'; describe('sanitizeUnknownAgentCredentials', () => { const accessibleCredentialIds = new Set(['known-cred', 'nested-cred']); + it('preserves the n8n Connect tag on the main model credential', () => { + const result = sanitizeUnknownAgentCredentials( + { credential: AI_GATEWAY_MANAGED_TAG, model: 'openai/gpt-5' }, + accessibleCredentialIds, + ); + + expect(result).toEqual({ credential: AI_GATEWAY_MANAGED_TAG, model: 'openai/gpt-5' }); + }); + + it('preserves the n8n Connect tag on difficulty model credentials', () => { + const result = sanitizeUnknownAgentCredentials( + { + subAgents: { + modelsByDifficulty: { + low: { model: 'openai/gpt-5-mini', credential: AI_GATEWAY_MANAGED_TAG }, + high: { model: 'anthropic/claude-sonnet-4-6', credential: AI_GATEWAY_MANAGED_TAG }, + }, + }, + }, + accessibleCredentialIds, + ); + + expect(result).toEqual({ + subAgents: { + modelsByDifficulty: { + low: { model: 'openai/gpt-5-mini', credential: AI_GATEWAY_MANAGED_TAG }, + high: { model: 'anthropic/claude-sonnet-4-6', credential: AI_GATEWAY_MANAGED_TAG }, + }, + }, + }); + }); + + it('clears the n8n Connect tag on out-of-scope credential paths (memory worker models)', () => { + const result = sanitizeUnknownAgentCredentials( + { + memory: { + observationalMemory: { + observerModel: { model: 'openai/gpt-4o-mini', credential: AI_GATEWAY_MANAGED_TAG }, + }, + }, + }, + accessibleCredentialIds, + ); + + expect(result).toEqual({ + memory: { + observationalMemory: { + observerModel: { model: 'openai/gpt-4o-mini', credential: '' }, + }, + }, + }); + }); + it('clears unknown top-level credential fields', () => { const result = sanitizeUnknownAgentCredentials( { diff --git a/packages/cli/src/modules/agents/json-config/from-json-config.ts b/packages/cli/src/modules/agents/json-config/from-json-config.ts index de2caa864cf..5847a6a6080 100644 --- a/packages/cli/src/modules/agents/json-config/from-json-config.ts +++ b/packages/cli/src/modules/agents/json-config/from-json-config.ts @@ -223,9 +223,8 @@ export async function buildFromJson( // Config options if (config.config) { - if (config.config.thinking) { - const { provider, ...rest } = config.config.thinking; - agent.thinking(provider, rest); + if (config.config.reasoning) { + agent.reasoning(config.config.reasoning); } if (config.config.promptCaching) { agent.promptCaching(config.config.promptCaching); diff --git a/packages/cli/src/modules/agents/json-config/mcp-client-factory.ts b/packages/cli/src/modules/agents/json-config/mcp-client-factory.ts index ec64e3ef611..7bec741df14 100644 --- a/packages/cli/src/modules/agents/json-config/mcp-client-factory.ts +++ b/packages/cli/src/modules/agents/json-config/mcp-client-factory.ts @@ -234,3 +234,21 @@ export async function buildMcpClientForServer( return new McpClient([sdkServerConfig]); } + +/** + * Connect to an MCP server, list its tools, and close the connection. + * Verification handshake for the instance MCP's verify-server tool. + */ +export async function listMcpServerTools( + server: AgentJsonMcpServerConfig, + deps: BuildMcpClientDeps, +): Promise> { + let client: McpClient | undefined; + try { + client = await buildMcpClientForServer(server, deps); + const tools = await client.listTools(); + return tools.map((tool) => ({ name: tool.name, description: tool.description ?? '' })); + } finally { + await client?.close().catch(() => {}); + } +} diff --git a/packages/cli/src/modules/agents/json-config/model-config.ts b/packages/cli/src/modules/agents/json-config/model-config.ts index 5291589f520..e3eca259caf 100644 --- a/packages/cli/src/modules/agents/json-config/model-config.ts +++ b/packages/cli/src/modules/agents/json-config/model-config.ts @@ -1,14 +1,41 @@ -import type { CredentialProvider, ModelConfig } from '@n8n/agents'; +import type { CredentialProvider, ModelConfig, ResolvedCredential } from '@n8n/agents'; import { getProviderPrefix } from '@n8n/ai-utilities/agent-config'; +import { AI_GATEWAY_MANAGED_TAG } from '@n8n/api-types'; import { mapCredentialForProvider } from './credential-field-mapping'; +/** + * A `CredentialProvider` that can also mint the n8n Connect (AI Gateway) + * synthetic credential for a model slot, keyed by the model's provider prefix + * (e.g. `openai`). `AgentsCredentialProvider` implements this; keeping the + * capability on the provider avoids threading a resolver through the build path. + */ +export interface AiGatewayModelCredentialResolver { + resolveAiGatewayModelCredential(provider: string): Promise; +} + +function canResolveAiGatewayModel( + credentialProvider: CredentialProvider, +): credentialProvider is CredentialProvider & AiGatewayModelCredentialResolver { + return ( + typeof (credentialProvider as Partial) + .resolveAiGatewayModelCredential === 'function' + ); +} + export async function resolveCredentialAwareModelConfig( model: string, credential: string, credentialProvider: CredentialProvider, ): Promise { + const provider = getProviderPrefix(model); + + if (credential === AI_GATEWAY_MANAGED_TAG && canResolveAiGatewayModel(credentialProvider)) { + const raw = await credentialProvider.resolveAiGatewayModelCredential(provider); + return { id: model, ...mapCredentialForProvider(provider, raw) } as ModelConfig; + } + const raw = await credentialProvider.resolve(credential); - const mapped = mapCredentialForProvider(getProviderPrefix(model), raw); + const mapped = mapCredentialForProvider(provider, raw); return { id: model, ...mapped } as ModelConfig; } diff --git a/packages/cli/src/modules/agents/json-config/sanitize-unknown-agent-credentials.ts b/packages/cli/src/modules/agents/json-config/sanitize-unknown-agent-credentials.ts index 947157d48c1..0ca29311951 100644 --- a/packages/cli/src/modules/agents/json-config/sanitize-unknown-agent-credentials.ts +++ b/packages/cli/src/modules/agents/json-config/sanitize-unknown-agent-credentials.ts @@ -1,15 +1,19 @@ -import { isDraftAgentConfig, MANAGED_CREDENTIAL_TOKEN } from '@n8n/api-types'; +import { + AI_GATEWAY_MANAGED_TAG, + isDraftAgentConfig, + MANAGED_CREDENTIAL_TOKEN, +} from '@n8n/api-types'; function clearUnknownCredentialId( credentialId: unknown, accessibleCredentialIds: ReadonlySet, - allowManagedCredentialToken = false, + allowedManagedTokens: readonly string[] = [], ): unknown { if (typeof credentialId !== 'string' || credentialId === '') { return credentialId; } - if (allowManagedCredentialToken && credentialId === MANAGED_CREDENTIAL_TOKEN) { + if (allowedManagedTokens.includes(credentialId)) { return credentialId; } @@ -20,6 +24,23 @@ function isManagedEpisodicMemoryCredentialPath(path: readonly string[]): boolean return path.join('.') === 'memory.episodicMemory.credential'; } +/** Model-credential paths where the n8n Connect managed tag is a valid value. */ +function isAiGatewayModelCredentialPath(path: readonly string[]): boolean { + const joined = path.join('.'); + return ( + joined === 'credential' || + /^subAgents\.modelsByDifficulty\.(low|medium|high)\.credential$/.test(joined) + ); +} + +/** Managed credential tokens allowed to survive sanitization at the given path. */ +function allowedManagedTokensForPath(path: readonly string[]): string[] { + const tokens: string[] = []; + if (isManagedEpisodicMemoryCredentialPath(path)) tokens.push(MANAGED_CREDENTIAL_TOKEN); + if (isAiGatewayModelCredentialPath(path)) tokens.push(AI_GATEWAY_MANAGED_TAG); + return tokens; +} + function sanitizeUnknownCredentialsInValue( value: unknown, accessibleCredentialIds: ReadonlySet, @@ -44,7 +65,7 @@ function sanitizeUnknownCredentialsInValue( sanitized[key] = clearUnknownCredentialId( entry, accessibleCredentialIds, - isManagedEpisodicMemoryCredentialPath(nextPath), + allowedManagedTokensForPath(nextPath), ); continue; } diff --git a/packages/cli/src/modules/agents/repositories/agent-chat-attachment.repository.ts b/packages/cli/src/modules/agents/repositories/agent-chat-attachment.repository.ts new file mode 100644 index 00000000000..fd895b9f288 --- /dev/null +++ b/packages/cli/src/modules/agents/repositories/agent-chat-attachment.repository.ts @@ -0,0 +1,43 @@ +import { Service } from '@n8n/di'; +import { DataSource, In, Repository } from '@n8n/typeorm'; + +import { AgentChatAttachment } from '../entities/agent-chat-attachment.entity'; + +@Service() +export class AgentChatAttachmentRepository extends Repository { + constructor(dataSource: DataSource) { + super(AgentChatAttachment, dataSource.manager); + } + + /** Scoped lookup for hydration: the reference must belong to the requesting conversation. */ + async findByIdInThread( + id: string, + scope: { projectId: string; threadId: string }, + ): Promise { + return await this.findOneBy({ id, projectId: scope.projectId, threadId: scope.threadId }); + } + + /** Scoped lookup for downloads: authorization checked at the agent/project level. */ + async findByIdForAgent( + id: string, + scope: { agentId: string; projectId: string }, + ): Promise { + return await this.findOneBy({ id, agentId: scope.agentId, projectId: scope.projectId }); + } + + /** + * Thread lookup scoped to a project or agent — the scope carries the + * caller's authorization and lets the matching composite index + * ((projectId, threadId) or (agentId, threadId)) serve the query. + */ + async findByThread( + threadId: string, + scope: { projectId: string } | { agentId: string }, + ): Promise { + return await this.findBy({ threadId, ...scope }); + } + + async findByIds(ids: string[]): Promise { + return await this.findBy({ id: In(ids) }); + } +} diff --git a/packages/cli/src/modules/agents/repositories/agent-checkpoint.repository.ts b/packages/cli/src/modules/agents/repositories/agent-checkpoint.repository.ts index d6a604ad24a..b0efcf67c15 100644 --- a/packages/cli/src/modules/agents/repositories/agent-checkpoint.repository.ts +++ b/packages/cli/src/modules/agents/repositories/agent-checkpoint.repository.ts @@ -1,5 +1,5 @@ import { Service } from '@n8n/di'; -import { DataSource, Repository } from '@n8n/typeorm'; +import { DataSource, Equal, IsNull, Or, Repository } from '@n8n/typeorm'; import { AgentCheckpoint } from '../entities/agent-checkpoint.entity'; @@ -22,6 +22,20 @@ export class AgentCheckpointRepository extends Repository { return (result.affected ?? 0) > 0; } + async cancelSuspended(runId: string, agentId: string, suspendedState: string): Promise { + const result = await this.update( + { + runId, + agentId: Or(Equal(agentId), IsNull()), + expired: false, + state: suspendedState, + }, + { expired: true, state: null }, + ); + + return (result.affected ?? 0) > 0; + } + async markExpired(olderThan: Date): Promise { const result = await this.createQueryBuilder() .update() diff --git a/packages/cli/src/modules/agents/repositories/agent.repository.ts b/packages/cli/src/modules/agents/repositories/agent.repository.ts index 420a4059b6c..dd4ab7c95fe 100644 --- a/packages/cli/src/modules/agents/repositories/agent.repository.ts +++ b/packages/cli/src/modules/agents/repositories/agent.repository.ts @@ -4,6 +4,18 @@ import { DataSource, In, Repository, type SelectQueryBuilder } from '@n8n/typeor import { Agent } from '../entities/agent.entity'; +export type AgentSummary = Pick< + Agent, + 'id' | 'name' | 'projectId' | 'activeVersionId' | 'availableInMCP' | 'updatedAt' +>; + +export type AgentSummaryFilters = { + query?: string; + publishedOnly?: boolean; + excludeAgentId?: string; + limit?: number; +}; + @Service() export class AgentRepository extends Repository { constructor(dataSource: DataSource) { @@ -18,16 +30,68 @@ export class AgentRepository extends Repository { }); } - async findByProjectIdsPaginated( - projectIds: string[], - options: ListAgentsQueryDto, - ): Promise<{ count: number; data: Agent[] }> { - if (projectIds.length === 0) return { count: 0, data: [] }; + /** + * Lean listing for search surfaces: selects only summary columns, skipping + * the JSON config columns and the activeVersion join, and pushes all + * filters and the limit into the query. + */ + async findSummariesByProjectIds( + projectIds: string[] | null, + options: AgentSummaryFilters = {}, + ): Promise { + if (projectIds?.length === 0) return []; const query = this.createQueryBuilder('agent') - .leftJoinAndSelect('agent.activeVersion', 'activeVersion') - .where('agent.projectId IN (:...projectIds)', { projectIds }); + .select([ + 'agent.id', + 'agent.name', + 'agent.projectId', + 'agent.activeVersionId', + 'agent.availableInMCP', + 'agent.updatedAt', + ]) + .orderBy('agent.updatedAt', 'DESC'); + if (projectIds !== null) { + query.where('agent.projectId IN (:...projectIds)', { projectIds }); + } + if (options.query) { + query.andWhere('LOWER(agent.name) LIKE LOWER(:query)', { query: `%${options.query}%` }); + } + if (options.publishedOnly) { + query.andWhere('agent.activeVersionId IS NOT NULL'); + } + if (options.excludeAgentId) { + query.andWhere('agent.id != :excludeAgentId', { excludeAgentId: options.excludeAgentId }); + } + if (options.limit !== undefined) { + query.take(options.limit); + } + + return await query.getMany(); + } + + async findByProjectIdsPaginated( + projectIds: string[] | null, + options: ListAgentsQueryDto, + { withProject = false }: { withProject?: boolean } = {}, + ): Promise<{ count: number; data: Agent[] }> { + if (projectIds?.length === 0) return { count: 0, data: [] }; + + const query = this.createQueryBuilder('agent').leftJoinAndSelect( + 'agent.activeVersion', + 'activeVersion', + ); + + // Only cross-project consumers (MCP settings) label each agent by its home + // project; the overview lists don't read it, so they skip the extra join. + if (withProject) { + query.leftJoinAndSelect('agent.project', 'project'); + } + + if (projectIds !== null) { + query.where('agent.projectId IN (:...projectIds)', { projectIds }); + } this.applyFilters(query, options.filter); this.applySorting(query, options.sortBy); query.skip(options.skip).take(options.take); @@ -43,6 +107,11 @@ export class AgentRepository extends Repository { if (filter?.query) { query.andWhere('LOWER(agent.name) LIKE LOWER(:query)', { query: `%${filter.query}%` }); } + if (filter?.availableInMCP !== undefined) { + query.andWhere('agent.availableInMCP = :availableInMCP', { + availableInMCP: filter.availableInMCP, + }); + } } private applySorting( @@ -78,6 +147,31 @@ export class AgentRepository extends Repository { }); } + /** + * Finds an agent by ID alone. Agent IDs are globally unique, so this is safe + * for callers whose access check does not hinge on a specific project (e.g. + * users with global agent scopes). + */ + async findById(id: string): Promise { + return await this.findOne({ + where: { id }, + relations: { activeVersion: true }, + }); + } + + async findByIdInProjects(id: string, projectIds: string[]): Promise { + if (projectIds.length === 0) return null; + return await this.findOne({ + where: { id, projectId: In(projectIds) }, + relations: { activeVersion: true }, + }); + } + + /** Ownership check only — skips `findByIdAndProjectId`'s `activeVersion` load. */ + async existsByIdAndProjectId(id: string, projectId: string): Promise { + return await this.exists({ where: { id, projectId } }); + } + async findByIdsAndProjectId( ids: string[], projectId: string, @@ -89,6 +183,30 @@ export class AgentRepository extends Repository { }); } + async findMcpAvailabilityCandidates( + where: { ids: string[] } | { projectIds: string[] } | { all: true }, + ): Promise>> { + if ('ids' in where && where.ids.length === 0) return []; + if ('projectIds' in where && where.projectIds.length === 0) return []; + + const criteria = + 'ids' in where + ? { id: In(where.ids) } + : 'projectIds' in where + ? { projectId: In(where.projectIds) } + : undefined; + + return await this.find({ + select: ['id', 'projectId', 'availableInMCP'], + where: criteria, + }); + } + + async setAvailableInMCP(agentIds: string[], availableInMCP: boolean): Promise { + if (agentIds.length === 0) return; + await this.update({ id: In(agentIds) }, { availableInMCP }); + } + async findPublished(): Promise { return await this.createQueryBuilder('agent') .innerJoinAndSelect('agent.activeVersion', 'activeVersion') diff --git a/packages/cli/src/modules/agents/sub-agents/__tests__/delegate-sub-agent-tool.test.ts b/packages/cli/src/modules/agents/sub-agents/__tests__/delegate-sub-agent-tool.test.ts index 456c4034a6a..3866942d9ba 100644 --- a/packages/cli/src/modules/agents/sub-agents/__tests__/delegate-sub-agent-tool.test.ts +++ b/packages/cli/src/modules/agents/sub-agents/__tests__/delegate-sub-agent-tool.test.ts @@ -294,7 +294,7 @@ describe('createN8nDelegateSubAgentTool', () => { taskPath: '/root/research_api_0', childCount: 0, }, - { runInlineSubAgent }, + { runInlineSubAgent, emitChunk: () => undefined }, ), ).resolves.toMatchObject({ status: 'completed', diff --git a/packages/cli/src/modules/agents/sub-agents/delegate-sub-agent-tool.ts b/packages/cli/src/modules/agents/sub-agents/delegate-sub-agent-tool.ts index e77055508a8..b0c812e15dc 100644 --- a/packages/cli/src/modules/agents/sub-agents/delegate-sub-agent-tool.ts +++ b/packages/cli/src/modules/agents/sub-agents/delegate-sub-agent-tool.ts @@ -84,6 +84,7 @@ export function createN8nDelegateSubAgentTool(options: CreateN8nDelegateSubAgent ? { abortSignal: request.parentAbortSignal } : {}), ...(request.parentTelemetry !== undefined ? { telemetry: request.parentTelemetry } : {}), + onChunk: helpers.emitChunk, }, ); diff --git a/packages/cli/src/modules/agents/sub-agents/sub-agent-foreground-runner.ts b/packages/cli/src/modules/agents/sub-agents/sub-agent-foreground-runner.ts index 5e4f3ff482e..fc14423b724 100644 --- a/packages/cli/src/modules/agents/sub-agents/sub-agent-foreground-runner.ts +++ b/packages/cli/src/modules/agents/sub-agents/sub-agent-foreground-runner.ts @@ -8,6 +8,8 @@ import { type BuiltTelemetry, type CredentialProvider, type GenerateResult, + type SerializableAgentState, + type StreamChunk, type SubAgentTaskPath, } from '@n8n/agents'; import type { ResolvedSubAgentSource, SubAgentSpawnRequest } from '@n8n/api-types'; @@ -51,6 +53,8 @@ export interface SubAgentForegroundRunContext { * (model fetch, MCP fetch, tool execution contexts). */ instrumentation?: AgentRuntimeInstrumentation; + /** Optional callback to forward child stream chunks to the parent chat. */ + onChunk?: (chunk: StreamChunk) => void; } export interface SubAgentForegroundResult { @@ -142,6 +146,7 @@ export class SubAgentForegroundRunner { for await (const value of streamAgentChunks(resultStream.stream)) { recorder.record(value); + context.onChunk?.(value); if (value.type === 'tool-call-suspended') { childSuspended = true; } @@ -171,9 +176,7 @@ export class SubAgentForegroundRunner { messages: [], finishReason: 'error', error: DELEGATED_CHILD_SUSPEND_UNSUPPORTED_MESSAGE, - getState: () => { - throw new Error('getState is not implemented for sub-agent foreground runner'); - }, + getState: () => resultStream.getState(), }, }; } @@ -182,6 +185,7 @@ export class SubAgentForegroundRunner { resultStream.runId, messageRecord, structuredOutput, + () => resultStream.getState(), ); return { @@ -271,6 +275,7 @@ function buildGenerateResultFromRecord( runId: string, record: MessageRecord, structuredOutput: unknown, + getState: () => SerializableAgentState, ): GenerateResult { const messages = createAssistantMessages(record.assistantResponse); const finishReason = toKnownFinishReason(record.finishReason); @@ -289,9 +294,7 @@ function buildGenerateResultFromRecord( : {}), ...(structuredOutput !== undefined ? { structuredOutput } : {}), ...(record.error !== null ? { error: record.error } : {}), - getState: () => { - throw new Error('getState is not implemented for sub-agent foreground runner'); - }, + getState, }; return result; } diff --git a/packages/cli/src/modules/agents/utils/__tests__/inbound-attachments.test.ts b/packages/cli/src/modules/agents/utils/__tests__/inbound-attachments.test.ts new file mode 100644 index 00000000000..6cf881efd88 --- /dev/null +++ b/packages/cli/src/modules/agents/utils/__tests__/inbound-attachments.test.ts @@ -0,0 +1,78 @@ +import type { Message } from '@n8n/agents'; +import { MAX_AGENT_CHAT_ATTACHMENT_MIMETYPE_LENGTH } from '@n8n/api-types'; + +import { buildInboundUserMessage, resolveInboundMimeType } from '../inbound-attachments'; + +// Minimal real PNG header (magic bytes + IHDR chunk) so file-type can sniff it. +const PNG_BYTES = Buffer.from([ + 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x08, 0x06, 0x00, 0x00, 0x00, 0x1f, 0x15, 0xc4, + 0x89, +]); + +const TEXT_BYTES = Buffer.from('just some text, no magic bytes'); + +describe('buildInboundUserMessage', () => { + it('emits one text part followed by reference-only file parts', () => { + const [message] = buildInboundUserMessage('look at these', [ + { id: 'att-1', fileName: 'photo.png', mimeType: 'image/png', sizeBytes: 33 }, + { id: 'att-2', fileName: 'voice.ogg', mimeType: 'audio/ogg', sizeBytes: 100 }, + ]) as Message[]; + + expect(message.role).toBe('user'); + expect(message.content).toEqual([ + { type: 'text', text: 'look at these' }, + { + type: 'file', + mediaType: 'image/png', + fileRef: { id: 'att-1', fileName: 'photo.png', sizeBytes: 33 }, + }, + { + type: 'file', + mediaType: 'audio/ogg', + fileRef: { id: 'att-2', fileName: 'voice.ogg', sizeBytes: 100 }, + }, + ]); + }); + + it('omits the text part for attachment-only sends', () => { + const [message] = buildInboundUserMessage('', [ + { id: 'att-1', fileName: 'photo.png', mimeType: 'image/png', sizeBytes: 33 }, + ]) as Message[]; + + expect(message.content).toHaveLength(1); + expect(message.content[0].type).toBe('file'); + }); +}); + +describe('resolveInboundMimeType', () => { + it('confirms a declared image type against magic bytes', async () => { + expect(await resolveInboundMimeType('image/png', PNG_BYTES)).toBe('image/png'); + }); + + it('demotes a model-eligible declaration that magic bytes contradict', async () => { + expect(await resolveInboundMimeType('image/png', TEXT_BYTES)).toBe('application/octet-stream'); + }); + + it('passes through declarations that never reach the model as bytes', async () => { + expect(await resolveInboundMimeType('text/csv', TEXT_BYTES)).toBe('text/csv'); + }); + + it('sniffs the type when no declaration is provided', async () => { + expect(await resolveInboundMimeType(undefined, PNG_BYTES)).toBe('image/png'); + expect(await resolveInboundMimeType(undefined, TEXT_BYTES)).toBe('application/octet-stream'); + }); + + it('classifies declarations that carry media-type parameters', async () => { + const pdfBytes = Buffer.from('%PDF-1.4\n1 0 obj\n<<>>\nendobj\ntrailer\n<<>>\n%%EOF'); + expect(await resolveInboundMimeType('application/pdf; charset=binary', pdfBytes)).toBe( + 'application/pdf', + ); + expect(await resolveInboundMimeType('image/png; foo=bar', PNG_BYTES)).toBe('image/png'); + }); + + it('normalizes a declaration longer than the mimeType column to octet-stream', async () => { + const overlong = `text/${'x'.repeat(MAX_AGENT_CHAT_ATTACHMENT_MIMETYPE_LENGTH)}`; + expect(await resolveInboundMimeType(overlong, TEXT_BYTES)).toBe('application/octet-stream'); + }); +}); diff --git a/packages/cli/src/modules/agents/utils/agent-config-hash.ts b/packages/cli/src/modules/agents/utils/agent-config-hash.ts new file mode 100644 index 00000000000..3b7da5ce01c --- /dev/null +++ b/packages/cli/src/modules/agents/utils/agent-config-hash.ts @@ -0,0 +1,19 @@ +import type { AgentJsonConfig } from '@n8n/api-types'; +import { isRecord } from '@n8n/utils/is-record'; +import { createHash } from 'node:crypto'; + +function canonicalizeJson(value: unknown): unknown { + if (Array.isArray(value)) return value.map((item) => canonicalizeJson(item)); + if (!isRecord(value)) return value; + + const sorted: Record = {}; + for (const key of Object.keys(value).sort()) sorted[key] = canonicalizeJson(value[key]); + return sorted; +} + +export function getAgentConfigHash(config: AgentJsonConfig | null): string | null { + if (!config) return null; + return createHash('sha256') + .update(JSON.stringify(canonicalizeJson(config))) + .digest('hex'); +} diff --git a/packages/cli/src/modules/agents/utils/execution-to-message-mapper.ts b/packages/cli/src/modules/agents/utils/execution-to-message-mapper.ts index fc2ee2ba818..a0a3b3d355a 100644 --- a/packages/cli/src/modules/agents/utils/execution-to-message-mapper.ts +++ b/packages/cli/src/modules/agents/utils/execution-to-message-mapper.ts @@ -4,7 +4,10 @@ import { isRecord } from '@n8n/utils/is-record'; import type { AgentExecution } from '../entities/agent-execution.entity'; import type { TimelineEvent } from '../execution-recorder'; -type ExecutionTranscript = Pick; +type ExecutionTranscript = Pick< + AgentExecution, + 'id' | 'userMessage' | 'timeline' | 'attachments' | 'status' +>; type ToolCallTimelineEvent = Extract; type ToolCallContentPart = AgentPersistedMessageContentPart & { @@ -17,24 +20,6 @@ function textPart(text: string): AgentPersistedMessageContentPart | null { return { type: 'text', text }; } -function textMessageDto( - id: string, - role: AgentPersistedMessageDto['role'], - text: string | null, - executionId: string, -): AgentPersistedMessageDto | null { - if (!text) return null; - const contentPart = textPart(text); - if (!contentPart) return null; - - return { - id, - role, - content: [contentPart], - executionId, - }; -} - function toolCallState(event: ToolCallTimelineEvent): 'resolved' | 'rejected' | undefined { if (typeof event.endTime !== 'number' || event.endTime <= 0) return undefined; return event.success ? 'resolved' : 'rejected'; @@ -97,6 +82,7 @@ function timelineToolCallToPart(event: ToolCallTimelineEvent): AgentPersistedMes input: event.input, ...(event.startTime > 0 ? { startTime: event.startTime } : {}), ...(event.endTime > 0 ? { endTime: event.endTime } : {}), + ...(event.childTrace ? { childTrace: event.childTrace } : {}), }; if (state === undefined) return base; @@ -126,6 +112,14 @@ function assistantContentFromExecution( if (!part) continue; content.push(part); + } else if (event.type === 'reasoning') { + if (!event.content.trim()) continue; + content.push({ + type: 'reasoning', + text: event.content, + startTime: event.timestamp, + ...(event.endTime !== undefined && { endTime: event.endTime }), + }); } else if (event.type === 'tool-call') { content.push(timelineToolCallToPart(event)); } @@ -140,13 +134,26 @@ export function executionToMessagesDto(execution: ExecutionTranscript): AgentPer // Message `id` stays `${execution.id}:role` for stable client keys. Turn // scope for handoff/history is the explicit `executionId` field — do not // make consumers parse it back out of `id`. - const userMessage = textMessageDto( - `${execution.id}:user`, - 'user', - execution.userMessage, - execution.id, - ); - if (userMessage) messages.push(userMessage); + const userContent: AgentPersistedMessageContentPart[] = []; + const userText = execution.userMessage === null ? null : textPart(execution.userMessage); + if (userText) userContent.push(userText); + for (const attachment of execution.attachments ?? []) { + userContent.push({ + type: 'file', + fileId: attachment.id, + fileName: attachment.fileName, + mimeType: attachment.mimeType, + sizeBytes: attachment.sizeBytes, + }); + } + if (userContent.length > 0) { + messages.push({ + id: `${execution.id}:user`, + role: 'user', + content: userContent, + executionId: execution.id, + }); + } const assistantContent = assistantContentFromExecution(execution); if (assistantContent.length > 0) { @@ -155,6 +162,7 @@ export function executionToMessagesDto(execution: ExecutionTranscript): AgentPer role: 'assistant', content: assistantContent, executionId: execution.id, + ...(execution.status ? { executionStatus: execution.status } : {}), }); } diff --git a/packages/cli/src/modules/agents/utils/inbound-attachments.ts b/packages/cli/src/modules/agents/utils/inbound-attachments.ts new file mode 100644 index 00000000000..24a3670b05c --- /dev/null +++ b/packages/cli/src/modules/agents/utils/inbound-attachments.ts @@ -0,0 +1,71 @@ +import type { AgentMessage, Message } from '@n8n/agents'; +import { MAX_AGENT_CHAT_ATTACHMENT_MIMETYPE_LENGTH } from '@n8n/api-types'; + +import type { StoredAttachmentRef } from '../agent-chat-attachment.service'; + +/** Media-type families that can be promoted to model file parts (see `isAttachmentMediaTypeSupported`). */ +function modelEligibleFamily(mimeType: string): 'image' | 'pdf' | 'audio' | null { + // Classify by the bare media type — declarations may carry parameters + // (e.g. `application/pdf; charset=binary`). + const baseType = mimeType.split(';')[0].trim().toLowerCase(); + if (baseType.startsWith('image/')) return 'image'; + if (baseType === 'application/pdf') return 'pdf'; + if (baseType.startsWith('audio/')) return 'audio'; + return null; +} + +/** + * Verify a declared media type against the file's magic bytes before it can + * be promoted to a model file part. Only model-eligible declarations are + * checked; a mismatch demotes the file to `application/octet-stream`, which + * always takes the text-metadata path. Other declarations pass through — they + * never reach the model as bytes. An absent declaration (some channel + * platforms omit it) resolves to whatever the magic bytes say. + */ +export async function resolveInboundMimeType( + declared: string | undefined, + data: Buffer, +): Promise { + // An overlong declaration is garbage and would overflow the mimeType + // column; sniffed values below are always short IANA types. + if (declared && declared.length > MAX_AGENT_CHAT_ATTACHMENT_MIMETYPE_LENGTH) { + return 'application/octet-stream'; + } + const declaredFamily = declared ? modelEligibleFamily(declared) : null; + if (declared && !declaredFamily) return declared; + + const { fileTypeFromBuffer } = await import('file-type'); + const sniffed = await fileTypeFromBuffer(data); + if (!declared) return sniffed?.mime ?? 'application/octet-stream'; + if (!sniffed || modelEligibleFamily(sniffed.mime) !== declaredFamily) { + return 'application/octet-stream'; + } + return sniffed.mime; +} + +/** + * Build the user turn for a message with attachments: one text part (when + * text is present) followed by a reference-only file part per attachment. + * Capability gating happens later, at hydration. + */ +export function buildInboundUserMessage( + text: string, + attachments: StoredAttachmentRef[], +): AgentMessage[] { + const content: Message['content'] = []; + if (text) { + content.push({ type: 'text', text }); + } + for (const attachment of attachments) { + content.push({ + type: 'file', + mediaType: attachment.mimeType, + fileRef: { + id: attachment.id, + fileName: attachment.fileName, + sizeBytes: attachment.sizeBytes, + }, + }); + } + return [{ role: 'user', content }]; +} diff --git a/packages/cli/src/modules/breaking-changes/rules/index.ts b/packages/cli/src/modules/breaking-changes/rules/index.ts index 9c51af3397c..fc118aad4e4 100644 --- a/packages/cli/src/modules/breaking-changes/rules/index.ts +++ b/packages/cli/src/modules/breaking-changes/rules/index.ts @@ -26,6 +26,7 @@ import './v3/chat-trigger-embedded-json.rule'; import './v3/compression-node-limits.rule'; import './v3/docker-only-deployment.rule'; import './v3/execute-workflow-each-mode.rule'; +import './v3/execute-workflow-source-modes.rule'; import './v3/get-paired-item.rule'; import './v3/gmail-trigger-version.rule'; import './v3/in-memory-binary-data.rule'; diff --git a/packages/cli/src/modules/breaking-changes/rules/v3/__tests__/execute-workflow-source-modes.rule.test.ts b/packages/cli/src/modules/breaking-changes/rules/v3/__tests__/execute-workflow-source-modes.rule.test.ts new file mode 100644 index 00000000000..536df2b73a2 --- /dev/null +++ b/packages/cli/src/modules/breaking-changes/rules/v3/__tests__/execute-workflow-source-modes.rule.test.ts @@ -0,0 +1,108 @@ +import { createNode, createWorkflow } from '../../../__tests__/test-helpers'; +import { ExecuteWorkflowSourceModesRule } from '../execute-workflow-source-modes.rule'; + +describe('ExecuteWorkflowSourceModesRule', () => { + let rule: ExecuteWorkflowSourceModesRule; + + beforeEach(() => { + rule = new ExecuteWorkflowSourceModesRule(); + }); + + describe('detectWorkflow()', () => { + it('should not be affected when there is no Execute Workflow node', async () => { + const { workflow, nodesGroupedByType } = createWorkflow('wf-1', 'Test Workflow', [ + createNode('HTTP', 'n8n-nodes-base.httpRequest'), + ]); + + const result = await rule.detectWorkflow(workflow, nodesGroupedByType); + + expect(result.isAffected).toBe(false); + expect(result.issues).toHaveLength(0); + }); + + it('should not be affected when Execute Workflow node uses the "database" source', async () => { + const { workflow, nodesGroupedByType } = createWorkflow('wf-1', 'Test Workflow', [ + createNode('SubWF', 'n8n-nodes-base.executeWorkflow', { source: 'database' }), + ]); + + const result = await rule.detectWorkflow(workflow, nodesGroupedByType); + + expect(result.isAffected).toBe(false); + expect(result.issues).toHaveLength(0); + }); + + it('should not be affected when Execute Workflow node uses the "parameter" source', async () => { + const { workflow, nodesGroupedByType } = createWorkflow('wf-1', 'Test Workflow', [ + createNode('SubWF', 'n8n-nodes-base.executeWorkflow', { source: 'parameter' }), + ]); + + const result = await rule.detectWorkflow(workflow, nodesGroupedByType); + + expect(result.isAffected).toBe(false); + expect(result.issues).toHaveLength(0); + }); + + it('should not be affected when source is unset (defaults to "database")', async () => { + const { workflow, nodesGroupedByType } = createWorkflow('wf-1', 'Test Workflow', [ + createNode('SubWF', 'n8n-nodes-base.executeWorkflow'), + ]); + + const result = await rule.detectWorkflow(workflow, nodesGroupedByType); + + expect(result.isAffected).toBe(false); + expect(result.issues).toHaveLength(0); + }); + + it('should detect Execute Workflow node using the "localFile" source', async () => { + const { workflow, nodesGroupedByType } = createWorkflow('wf-1', 'Test Workflow', [ + createNode('SubWF', 'n8n-nodes-base.executeWorkflow', { source: 'localFile' }), + ]); + + const result = await rule.detectWorkflow(workflow, nodesGroupedByType); + + expect(result.isAffected).toBe(true); + expect(result.issues).toHaveLength(1); + expect(result.issues[0].title).toContain('SubWF'); + expect(result.issues[0].title).toContain('Local File'); + expect(result.issues[0].level).toBe('error'); + expect(result.issues[0].nodeName).toBe('SubWF'); + }); + + it('should detect Execute Workflow node using the "url" source', async () => { + const { workflow, nodesGroupedByType } = createWorkflow('wf-1', 'Test Workflow', [ + createNode('SubWF', 'n8n-nodes-base.executeWorkflow', { source: 'url' }), + ]); + + const result = await rule.detectWorkflow(workflow, nodesGroupedByType); + + expect(result.isAffected).toBe(true); + expect(result.issues).toHaveLength(1); + expect(result.issues[0].title).toContain('URL'); + expect(result.issues[0].level).toBe('error'); + }); + + it('should flag only the removed-source nodes when multiple Execute Workflow nodes exist', async () => { + const { workflow, nodesGroupedByType } = createWorkflow('wf-1', 'Test Workflow', [ + createNode('LocalOne', 'n8n-nodes-base.executeWorkflow', { source: 'localFile' }), + createNode('UrlOne', 'n8n-nodes-base.executeWorkflow', { source: 'url' }), + createNode('DbOne', 'n8n-nodes-base.executeWorkflow', { source: 'database' }), + ]); + + const result = await rule.detectWorkflow(workflow, nodesGroupedByType); + + expect(result.isAffected).toBe(true); + expect(result.issues).toHaveLength(2); + expect(result.issues.map((issue) => issue.nodeName)).toEqual(['LocalOne', 'UrlOne']); + }); + }); + + describe('getRecommendations()', () => { + it('should recommend the database source and node-defined JSON', async () => { + const recommendations = await rule.getRecommendations([]); + + expect(recommendations).toHaveLength(2); + expect(recommendations[0].action).toContain('Database'); + expect(recommendations[1].action).toContain('workflow JSON'); + }); + }); +}); diff --git a/packages/cli/src/modules/breaking-changes/rules/v3/execute-workflow-source-modes.rule.ts b/packages/cli/src/modules/breaking-changes/rules/v3/execute-workflow-source-modes.rule.ts new file mode 100644 index 00000000000..99e60ef74f7 --- /dev/null +++ b/packages/cli/src/modules/breaking-changes/rules/v3/execute-workflow-source-modes.rule.ts @@ -0,0 +1,77 @@ +import type { BreakingChangeAffectedWorkflow, BreakingChangeRecommendation } from '@n8n/api-types'; +import type { WorkflowEntity } from '@n8n/db'; +import { BreakingChangeRule } from '@n8n/decorators'; +import type { INode } from 'n8n-workflow'; + +import type { + BreakingChangeRuleMetadata, + IBreakingChangeWorkflowRule, + WorkflowDetectionReport, +} from '../../types'; +import { BreakingChangeCategory } from '../../types'; + +const EXECUTE_WORKFLOW_NODE_TYPE = 'n8n-nodes-base.executeWorkflow'; +const REMOVED_SOURCES = ['localFile', 'url']; + +const SOURCE_LABELS: Record = { + localFile: 'Local File', + url: 'URL', +}; + +@BreakingChangeRule({ version: 'v3' }) +export class ExecuteWorkflowSourceModesRule implements IBreakingChangeWorkflowRule { + id = 'execute-workflow-source-modes-v3'; + + getMetadata(): BreakingChangeRuleMetadata { + return { + version: 'v3', + title: 'Execute Sub-workflow "Local File" and "URL" sources removed', + description: + 'The "Local File" and "URL" sources of the Execute Sub-workflow node are being removed. Sub-workflows must be loaded from the database or defined in the node parameters.', + category: BreakingChangeCategory.workflow, + severity: 'medium', + }; + } + + // eslint-disable-next-line @typescript-eslint/require-await + async getRecommendations( + _workflowResults: BreakingChangeAffectedWorkflow[], + ): Promise { + return [ + { + action: 'Switch to the "Database" source', + description: + 'Import the referenced workflow into this n8n instance and select it via the "Database" source on the flagged Execute Sub-workflow node.', + }, + { + action: 'Or define the workflow JSON in the node', + description: + 'Paste the referenced workflow\'s JSON into the "Parameter" source to keep the sub-workflow definition inside the node.', + }, + ]; + } + + // eslint-disable-next-line @typescript-eslint/require-await + async detectWorkflow( + _workflow: WorkflowEntity, + nodesGroupedByType: Map, + ): Promise { + const affectedNodes = (nodesGroupedByType.get(EXECUTE_WORKFLOW_NODE_TYPE) ?? []).filter( + (node) => REMOVED_SOURCES.includes(node.parameters.source as string), + ); + + if (affectedNodes.length === 0) return { isAffected: false, issues: [] }; + + return { + isAffected: true, + issues: affectedNodes.map((node) => ({ + title: `Node '${node.name}' uses the removed "${SOURCE_LABELS[node.parameters.source as string]}" source`, + description: + 'This source is being removed. Import the referenced workflow into this n8n instance and use the "Database" source, or paste its JSON into the "Parameter" source.', + level: 'error', + nodeId: node.id, + nodeName: node.name, + })), + }; + } +} diff --git a/packages/cli/src/modules/chat-hub/chat-hub-execution.service.ts b/packages/cli/src/modules/chat-hub/chat-hub-execution.service.ts index 24c8d1e19a3..0497a305b01 100644 --- a/packages/cli/src/modules/chat-hub/chat-hub-execution.service.ts +++ b/packages/cli/src/modules/chat-hub/chat-hub-execution.service.ts @@ -8,6 +8,7 @@ import { Logger } from '@n8n/backend-common'; import { ExecutionRepository, IExecutionResponse, User } from '@n8n/db'; import { Service } from '@n8n/di'; import type { Response } from 'express'; +import { sleep } from '@n8n/utils/sleep'; import { InstanceSettings } from 'n8n-core'; import { OperationalError, @@ -17,7 +18,6 @@ import { StructuredChunk, IRunExecutionData, WorkflowExecuteMode, - sleep, NodeConnectionTypes, INodeExecutionData, jsonStringify, diff --git a/packages/cli/src/modules/insights/insights-compaction.service.ts b/packages/cli/src/modules/insights/insights-compaction.service.ts index ae9634efb0b..5c19fff8898 100644 --- a/packages/cli/src/modules/insights/insights-compaction.service.ts +++ b/packages/cli/src/modules/insights/insights-compaction.service.ts @@ -1,7 +1,7 @@ import { Logger } from '@n8n/backend-common'; import { Time } from '@n8n/constants'; import { Service } from '@n8n/di'; -import { sleep } from 'n8n-workflow'; +import { sleep } from '@n8n/utils/sleep'; import { InsightsByPeriodRepository } from './database/repositories/insights-by-period.repository'; import { InsightsRawRepository } from './database/repositories/insights-raw.repository'; diff --git a/packages/cli/src/modules/instance-ai/__tests__/credit-counting.test.ts b/packages/cli/src/modules/instance-ai/__tests__/credit-counting.test.ts index c577957762d..2e099b562b6 100644 --- a/packages/cli/src/modules/instance-ai/__tests__/credit-counting.test.ts +++ b/packages/cli/src/modules/instance-ai/__tests__/credit-counting.test.ts @@ -6,8 +6,7 @@ import { InstanceAiCreditService } from '../instance-ai-credit.service'; import type { InstanceAiThreadRepository } from '../repositories/instance-ai-thread.repository'; // Skip the real backoff sleeps so retry tests run instantly. -vi.mock('n8n-workflow', async () => ({ - ...(await vi.importActual('n8n-workflow')), +vi.mock('@n8n/utils/sleep', () => ({ sleep: vi.fn().mockResolvedValue(undefined), })); diff --git a/packages/cli/src/modules/instance-ai/__tests__/instance-ai-sandbox.service.test.ts b/packages/cli/src/modules/instance-ai/__tests__/instance-ai-sandbox.service.test.ts index 050687e8648..b43153e3364 100644 --- a/packages/cli/src/modules/instance-ai/__tests__/instance-ai-sandbox.service.test.ts +++ b/packages/cli/src/modules/instance-ai/__tests__/instance-ai-sandbox.service.test.ts @@ -112,7 +112,7 @@ describe('InstanceAiSandboxService', () => { }); }); - it('forces Daytona traffic through the assistant proxy when enabled', async () => { + it('routes Daytona traffic through the assistant proxy when enabled', async () => { const getInstanceAiApiProxyToken = vi.fn(async () => ({ accessToken: 'token-1' })); const client = { getSandboxProxyConfig: vi.fn(async () => ({ image: 'proxy-image' })), @@ -120,7 +120,7 @@ describe('InstanceAiSandboxService', () => { getInstanceAiApiProxyToken, }; const { service } = createSandboxService({ - config: { sandboxEnabled: true, sandboxProvider: 'n8n-sandbox' }, + config: { sandboxEnabled: true, sandboxProvider: 'daytona' }, aiService: { isProxyEnabled: vi.fn(() => true), getClient: vi.fn(async () => client), @@ -145,6 +145,31 @@ describe('InstanceAiSandboxService', () => { } }); + it('keeps n8n Sandbox traffic direct when the assistant proxy is enabled', async () => { + const getClient = vi.fn(); + const resolveN8nSandboxConfig = vi.fn(async () => ({ + serviceUrl: 'https://admin.sandbox', + apiKey: 'admin-key', + })); + const { service } = createSandboxService({ + config: { + sandboxEnabled: true, + sandboxProvider: 'n8n-sandbox', + n8nSandboxServiceUrl: 'https://env.sandbox', + }, + settingsService: { resolveN8nSandboxConfig }, + aiService: { isProxyEnabled: vi.fn(() => true), getClient }, + }); + + await expect(service.resolveSandboxConfig(fakeUser)).resolves.toMatchObject({ + enabled: true, + provider: 'n8n-sandbox', + serviceUrl: 'https://admin.sandbox', + apiKey: 'admin-key', + }); + expect(getClient).not.toHaveBeenCalled(); + }); + it('merges admin n8n-sandbox credentials', async () => { const resolveN8nSandboxConfig = vi.fn(async () => ({ serviceUrl: 'https://admin.sandbox', @@ -522,7 +547,7 @@ describe('InstanceAiSandboxService', () => { getInstanceAiApiProxyToken: vi.fn(async () => ({ accessToken: 'token-1' })), }; const { service } = createSandboxService({ - config: { sandboxEnabled: true, sandboxProvider: 'n8n-sandbox' }, + config: { sandboxEnabled: true, sandboxProvider: 'daytona' }, aiService: { isProxyEnabled: vi.fn(() => true), getClient: vi.fn(async () => client), diff --git a/packages/cli/src/modules/instance-ai/__tests__/instance-ai-settings.service.test.ts b/packages/cli/src/modules/instance-ai/__tests__/instance-ai-settings.service.test.ts index 2dc91dad6ba..da071be18fe 100644 --- a/packages/cli/src/modules/instance-ai/__tests__/instance-ai-settings.service.test.ts +++ b/packages/cli/src/modules/instance-ai/__tests__/instance-ai-settings.service.test.ts @@ -766,32 +766,96 @@ describe('InstanceAiSettingsService', () => { ).rejects.toThrow('You do not have permission to manage provider connections'); }); - it.each(['cloud', 'proxy'] as const)( - 'should reject connection payloads on %s deployments', - async (deployment) => { - globalConfig.deployment.type = deployment === 'cloud' ? 'cloud' : 'default'; - aiService.isProxyEnabled.mockReturnValue(deployment === 'proxy'); + it('should reject connection payloads on cloud deployments', async () => { + globalConfig.deployment.type = 'cloud'; - for (const update of [ - { - modelConnection: { type: 'openAiApi', data: { apiKey: 'k' } }, - modelName: 'gpt-5', + for (const update of [ + { + modelConnection: { type: 'openAiApi', data: { apiKey: 'k' } }, + modelName: 'gpt-5', + }, + { + sandboxConnection: { + type: 'daytonaApi', + data: { apiUrl: 'https://daytona.example.com', apiKey: 'k' }, }, + }, + { searchConnection: { type: 'braveSearchApi', data: { apiKey: 'k' } } }, + ] as const) { + await expect(service.updateAdminSettings(update, adminUser)).rejects.toThrow( + UnprocessableRequestError, + ); + } + expect(credentialsService.runInstanceCredentialHooks).not.toHaveBeenCalled(); + }); + + it('should keep model, Daytona, and search connections proxy-managed', async () => { + aiService.isProxyEnabled.mockReturnValue(true); + + for (const update of [ + { + modelConnection: { type: 'openAiApi', data: { apiKey: 'k' } }, + modelName: 'gpt-5', + }, + { daytonaCredentialId: 'daytona-cred' }, + { + sandboxConnection: { + type: 'daytonaApi', + data: { apiUrl: 'https://daytona.example.com', apiKey: 'k' }, + }, + }, + { searchConnection: { type: 'braveSearchApi', data: { apiKey: 'k' } } }, + ] as const) { + await expect(service.updateAdminSettings(update, adminUser)).rejects.toThrow( + UnprocessableRequestError, + ); + } + expect(credentialsService.runInstanceCredentialHooks).not.toHaveBeenCalled(); + }); + + it('should clear the inactive Daytona slot when selecting n8n Sandbox behind the proxy', async () => { + aiService.isProxyEnabled.mockReturnValue(true); + + await expect( + service.updateAdminSettings({ + daytonaCredentialId: null, + n8nSandboxCredentialId: 'sandbox-cred', + sandboxProvider: 'n8n-sandbox', + }), + ).resolves.toMatchObject({ + daytonaCredentialId: null, + n8nSandboxCredentialId: 'sandbox-cred', + sandboxProvider: 'n8n-sandbox', + }); + expect(instanceCredentialBroker.clearForUse).toHaveBeenCalledWith( + expect.objectContaining({ id: 'instance-ai:sandbox:daytona' }), + operationContext, + ); + expect(instanceCredentialBroker.assignForUse).toHaveBeenCalledWith( + expect.objectContaining({ id: 'instance-ai:sandbox:n8n' }), + 'sandbox-cred', + operationContext, + ); + }); + + it('should accept n8n Sandbox connections on proxy deployments', async () => { + aiService.isProxyEnabled.mockReturnValue(true); + credentialsService.createInstanceCredential.mockResolvedValue({ + id: 'sandbox-cred', + } as never); + + await expect( + service.updateAdminSettings( { sandboxConnection: { - type: 'daytonaApi', - data: { apiUrl: 'https://daytona.example.com', apiKey: 'k' }, + type: 'httpHeaderAuth', + data: { name: 'x-api-key', value: 'k' }, }, }, - { searchConnection: { type: 'braveSearchApi', data: { apiKey: 'k' } } }, - ] as const) { - await expect(service.updateAdminSettings(update, adminUser)).rejects.toThrow( - UnprocessableRequestError, - ); - } - expect(credentialsService.runInstanceCredentialHooks).not.toHaveBeenCalled(); - }, - ); + adminUser, + ), + ).resolves.toMatchObject({ sandboxProvider: 'n8n-sandbox' }); + }); it('should reject a connection payload combined with a credential id', async () => { await expect( @@ -916,12 +980,17 @@ describe('InstanceAiSettingsService', () => { await expect(service.updateAdminSettings({ mcpServers: '[]' })).rejects.toThrow( UnprocessableRequestError, ); - await expect(service.updateAdminSettings({ sandboxProvider: 'daytona' })).rejects.toThrow( - UnprocessableRequestError, - ); }); - it('should ignore persisted sandbox providers when proxy routing is enabled', async () => { + it('should allow sandbox provider updates when proxy routing is enabled', async () => { + aiService.isProxyEnabled.mockReturnValue(true); + + await expect( + service.updateAdminSettings({ sandboxProvider: 'daytona' }), + ).resolves.toMatchObject({ sandboxProvider: 'daytona' }); + }); + + it('should honor persisted sandbox providers when proxy routing is enabled', async () => { aiService.isProxyEnabled.mockReturnValue(true); settingsRepository.findByKey.mockResolvedValueOnce({ key: 'instanceAi.settings', @@ -932,16 +1001,16 @@ describe('InstanceAiSettingsService', () => { await service.loadFromDb(); await expect(service.getAdminSettings()).resolves.toMatchObject({ - sandboxProvider: 'daytona', + sandboxProvider: 'n8n-sandbox', }); }); - it('should switch sandbox status to Daytona when proxy routing activates', () => { + it('should keep the selected sandbox provider when proxy routing activates', () => { aiService.isProxyEnabled.mockReturnValue(false); expect(service.getSandboxStatus().provider).toBe('n8n-sandbox'); aiService.isProxyEnabled.mockReturnValue(true); - expect(service.getSandboxStatus().provider).toBe('daytona'); + expect(service.getSandboxStatus().provider).toBe('n8n-sandbox'); }); it('should persist a sandbox provider override on self-hosted', async () => { @@ -1619,6 +1688,22 @@ describe('InstanceAiSettingsService', () => { }); }); + it('uses the resolved api key when the assistant proxy is enabled', async () => { + aiService.isProxyEnabled.mockReturnValue(true); + globalConfig.instanceAi.n8nSandboxServiceApiKey = 'env-key'; + instanceCredentialBroker.resolveForUse.mockResolvedValue({ + id: 'sandbox-credential', + name: 'Sandbox', + type: 'httpHeaderAuth', + data: { name: 'X-Api-Key', value: 'credential-key' }, + }); + + await expect(service.resolveN8nSandboxConfig()).resolves.toEqual({ + serviceUrl: 'http://sandbox-api:8080', + apiKey: 'credential-key', + }); + }); + it('falls back to environment config when the credential header is not x-api-key', async () => { globalConfig.instanceAi.n8nSandboxServiceApiKey = 'env-key'; instanceCredentialBroker.resolveForUse.mockResolvedValue({ @@ -1692,14 +1777,14 @@ describe('InstanceAiSettingsService', () => { expect((await service.getAdminSettings()).modelEnvConfigured).toBe(true); }); - it('checks the environment sandbox provider instead of the active override', async () => { + it('reports environment configuration for the selected sandbox provider', async () => { globalConfig.instanceAi.sandboxProvider = 'daytona'; globalConfig.instanceAi.daytonaApiKey = 'dtn-key'; - expect((await service.getAdminSettings()).sandboxEnvConfigured).toBe(false); - - globalConfig.instanceAi.n8nSandboxServiceUrl = 'http://sandbox-api:8080'; expect((await service.getAdminSettings()).sandboxEnvConfigured).toBe(true); + globalConfig.instanceAi.daytonaApiKey = ''; + expect((await service.getAdminSettings()).sandboxEnvConfigured).toBe(false); + globalConfig.instanceAi.sandboxProvider = 'n8n-sandbox'; globalConfig.instanceAi.n8nSandboxServiceUrl = 'http://sandbox-api:8080'; expect((await service.getAdminSettings()).sandboxEnvConfigured).toBe(true); @@ -1737,6 +1822,27 @@ describe('InstanceAiSettingsService', () => { searchCredentialId: 'search-cred', }); }); + + it('exposes n8n Sandbox assignments when the assistant proxy is enabled', async () => { + aiService.isProxyEnabled.mockReturnValue(true); + instanceCredentialBroker.getAssignedCredentialId.mockResolvedValue('sandbox-cred'); + instanceCredentialBroker.listForUse.mockResolvedValue([ + { id: 'sandbox-cred', name: 'n8n Sandbox', type: 'httpHeaderAuth' }, + ] as never); + + await expect(service.getAdminSettings()).resolves.toMatchObject({ + daytonaCredentialId: null, + n8nSandboxCredentialId: 'sandbox-cred', + searchCredentialId: null, + }); + await expect(service.listInstanceServiceCredentials()).resolves.toEqual([ + { id: 'sandbox-cred', name: 'n8n Sandbox', type: 'httpHeaderAuth' }, + ]); + expect(instanceCredentialBroker.listForUse).toHaveBeenCalledOnce(); + expect(instanceCredentialBroker.listForUse).toHaveBeenCalledWith( + INSTANCE_AI_N8N_SANDBOX_CREDENTIAL_POLICY, + ); + }); }); describe('executeMcpTool permission', () => { diff --git a/packages/cli/src/modules/instance-ai/__tests__/instance-ai.adapter.service.test.ts b/packages/cli/src/modules/instance-ai/__tests__/instance-ai.adapter.service.test.ts index 4289f5f827f..2591eadee84 100644 --- a/packages/cli/src/modules/instance-ai/__tests__/instance-ai.adapter.service.test.ts +++ b/packages/cli/src/modules/instance-ai/__tests__/instance-ai.adapter.service.test.ts @@ -1332,6 +1332,89 @@ function createNodeAdapterForTests( return createNodeAdapterServiceForTests(nodes, { nodeCatalogService }).nodeService; } +// --------------------------------------------------------------------------- +// Web-search provider selection +// --------------------------------------------------------------------------- + +import { braveSearch, searxngSearch } from '@n8n/ai-utilities'; + +describe('web-search provider selection', () => { + type SearchFn = (query: string, options?: Record) => Promise; + type ProxyConfig = { apiUrl: string; getAuthHeaders: () => Promise> }; + + /** `buildSearchMethod` is private, but the precedence it encodes is exactly what + * the `INSTANCE_AI_BRAVE_SEARCH_API_KEY` wiring relies on — assert it directly. */ + function buildSearch(args: { + apiKey?: string; + searxngUrl?: string; + proxyConfig?: ProxyConfig; + }): SearchFn | undefined { + const { service } = createNodeAdapterServiceForTests([]); + const cache = { get: vi.fn().mockReturnValue(undefined), set: vi.fn() }; + const withPrivate = service as unknown as { + buildSearchMethod: ( + apiKey: string, + searxngUrl: string, + cache: unknown, + proxyConfig?: ProxyConfig, + userId?: string, + ) => SearchFn | undefined; + }; + return withPrivate.buildSearchMethod.call( + service, + args.apiKey ?? '', + args.searxngUrl ?? '', + cache, + args.proxyConfig, + 'user-1', + ); + } + + beforeEach(() => { + vi.mocked(braveSearch).mockReset().mockResolvedValue({ query: 'q', results: [] }); + vi.mocked(searxngSearch).mockReset().mockResolvedValue({ query: 'q', results: [] }); + }); + + it('has no search method when neither a Brave key nor a SearXNG URL is set', () => { + // The adapter then serves `{ query, results: [] }`, which the agent cannot + // distinguish from "nothing found" — hence the key in the eval lanes. + expect(buildSearch({})).toBeUndefined(); + }); + + it('searches Brave with the configured key', async () => { + await buildSearch({ apiKey: 'BSA-key' })!('quakes', { maxResults: 3 }); + + expect(braveSearch).toHaveBeenCalledWith( + 'BSA-key', + 'quakes', + expect.objectContaining({ maxResults: 3 }), + ); + expect(searxngSearch).not.toHaveBeenCalled(); + }); + + it('routes through the AI-service proxy in preference to a configured key', async () => { + const proxyConfig: ProxyConfig = { + apiUrl: 'https://proxy.example.com/brave-search', + getAuthHeaders: async () => ({}), + }; + + await buildSearch({ apiKey: 'BSA-key', proxyConfig })!('quakes'); + + expect(braveSearch).toHaveBeenCalledWith( + '', + 'quakes', + expect.objectContaining({ proxyConfig }), + ); + }); + + it('falls back to SearXNG when only a URL is set', async () => { + await buildSearch({ searxngUrl: 'http://searxng:8080' })!('quakes'); + + expect(searxngSearch).toHaveBeenCalledWith('http://searxng:8080', 'quakes', expect.anything()); + expect(braveSearch).not.toHaveBeenCalled(); + }); +}); + describe('createNodeAdapter', () => { it('preserves credential displayOptions in getDescription()', async () => { const adapter = createNodeAdapterForTests([ @@ -2245,6 +2328,57 @@ describe('createWorkflowAdapter', () => { ); }); + it('defaults executionOrder to v1 when the SDK workflow declares no settings', async () => { + // Regression: the SDK omits the settings argument when empty, so AI-authored + // workflows persisted with `{}` and ran on legacy v0. + const { adapter, mockWorkflowRepository, mockWorkflowService } = + createWorkflowAdapterForTests(); + + await adapter.createFromWorkflowJSON(minimalWorkflowJSON); + + expect(mockWorkflowRepository.create).toHaveBeenNthCalledWith( + 1, + expect.objectContaining({ settings: { executionOrder: 'v1' } }), + ); + expect(mockWorkflowService.update).toHaveBeenCalledWith( + expect.anything(), + expect.objectContaining({ settings: { executionOrder: 'v1' } }), + expect.anything(), + expect.anything(), + ); + }); + + it('keeps an explicit executionOrder from the SDK workflow', async () => { + const { adapter, mockWorkflowService } = createWorkflowAdapterForTests(); + + await adapter.createFromWorkflowJSON({ + ...minimalWorkflowJSON, + settings: { executionOrder: 'v0' }, + } as unknown as WorkflowJSON); + + expect(mockWorkflowService.update).toHaveBeenCalledWith( + expect.anything(), + expect.objectContaining({ settings: { executionOrder: 'v0' } }), + expect.anything(), + expect.anything(), + ); + }); + + it('does not inject executionOrder on update, leaving the stored value to the merge', async () => { + // update merges over stored settings, so forcing v1 here would upgrade a + // workflow the user deliberately kept on v0. + const { adapter, mockWorkflowService } = createWorkflowAdapterForTests(); + + await adapter.updateFromWorkflowJSON('wf-existing', minimalWorkflowJSON); + + expect(mockWorkflowService.update).toHaveBeenCalledWith( + expect.anything(), + expect.objectContaining({ settings: {} }), + expect.anything(), + expect.anything(), + ); + }); + it('clears existing node groups when the SDK workflow declares none (update is authoritative)', async () => { // Regression: the SDK omits `nodeGroups` when no `.group(...)` is declared. The // update path must treat that as "no groups" and send [] so a removed group is @@ -2998,6 +3132,32 @@ describe('createExecutionAdapter run()', () => { expect(secondRun.workflowData.settings?.executionTimeout).toBe(600); }); + it('omits the listed connections on the ephemeral run copy only', async () => { + const workflow = { + id: 'wf-1', + nodes: [], + connections: { + Revise: { main: [[{ node: 'Format', type: 'main', index: 0 }]] }, + Format: { main: [[{ node: 'Gate', type: 'main', index: 0 }]] }, + }, + }; + const { adapter, mockWorkflowRunner } = createRunAdapterForTests(workflow); + + await adapter.run('wf-1', undefined, { + omitConnections: [{ source: 'Revise', target: 'Format' }], + }); + + const runData = mockWorkflowRunner.run.mock.calls[0][0]; + expect(runData.workflowData.connections.Revise.main).toEqual([[]]); + expect(runData.workflowData.connections.Format.main).toEqual([ + [{ node: 'Gate', type: 'main', index: 0 }], + ]); + // The saved workflow object is untouched. + expect(workflow.connections.Revise.main).toEqual([ + [{ node: 'Format', type: 'main', index: 0 }], + ]); + }); + it('attaches Instance AI execution telemetry metadata to workflow runs', async () => { const { adapter, mockWorkflowRunner } = createRunAdapterForTests({ id: 'wf-1', @@ -3255,6 +3415,59 @@ describe('createExecutionAdapter run()', () => { expect(firstStackItem?.data.main[0]?.[0]?.json).toEqual({}); }); + it('opts a verification run out of the error workflow, on the main process and on a worker', async () => { + const { adapter, mockWorkflowRunner } = createRunAdapterForTests({ + id: 'wf-1', + settings: { errorWorkflow: 'error-wf-1' }, + nodes: [ + { + id: 'node-1', + name: 'Schedule Trigger', + type: 'n8n-nodes-base.scheduleTrigger', + typeVersion: 1, + parameters: {}, + position: [0, 0], + }, + ], + }); + + await adapter.run('wf-1', undefined, { isVerificationRun: true }); + + const runData = mockWorkflowRunner.run.mock.calls[0][0]; + // A trigger-mode run is a production execution as far as the lifecycle + // hooks are concerned, so the opt-out is what keeps a failed build attempt + // from paging the user's error workflow. + expect(runData.executionMode).toBe('trigger'); + expect(runData.suppressErrorWorkflow).toBe(true); + // Queue mode rebuilds the run from persisted execution data. + expect(runData.executionData?.manualData?.suppressErrorWorkflow).toBe(true); + }); + + it('leaves the error workflow enabled for a run the user asked for', async () => { + const { adapter, mockWorkflowRunner } = createRunAdapterForTests({ + id: 'wf-1', + settings: { errorWorkflow: 'error-wf-1' }, + nodes: [ + { + id: 'node-1', + name: 'Schedule Trigger', + type: 'n8n-nodes-base.scheduleTrigger', + typeVersion: 1, + parameters: {}, + position: [0, 0], + }, + ], + }); + + await adapter.run('wf-1'); + + const runData = mockWorkflowRunner.run.mock.calls[0][0]; + expect(runData.suppressErrorWorkflow).toBeUndefined(); + expect(runData.executionData?.manualData?.suppressErrorWorkflow).toBeUndefined(); + // The user's setting is never stripped from the run itself. + expect(runData.workflowData.settings?.errorWorkflow).toBe('error-wf-1'); + }); + it('wraps manual metadata into executionData when offloading to workers so the worker can run it', async () => { const original = process.env.OFFLOAD_MANUAL_EXECUTIONS_TO_WORKERS; process.env.OFFLOAD_MANUAL_EXECUTIONS_TO_WORKERS = 'true'; diff --git a/packages/cli/src/modules/instance-ai/__tests__/instance-ai.service.test.ts b/packages/cli/src/modules/instance-ai/__tests__/instance-ai.service.test.ts index c3b46948959..3fe27088dee 100644 --- a/packages/cli/src/modules/instance-ai/__tests__/instance-ai.service.test.ts +++ b/packages/cli/src/modules/instance-ai/__tests__/instance-ai.service.test.ts @@ -653,12 +653,14 @@ type TerminalGuardOrderServiceInternals = { }; threadPushRef: Map; saveAgentTreeSnapshot: Mock; - backgroundTasks: { getRunningTasks: Mock }; + backgroundTasks: { getRunningTasks: Mock; getRunningTasksByParentCheckpoint?: Mock }; temporaryWorkflowService: { reapForRun: Mock }; creditService: { claimRunUsage: Mock }; failedInternalFollowUpStreaks: Map; schedulePlannedTasks: Mock; drainPendingCheckpointReentries: Mock; + createPlannedTaskState: Mock; + syncPlannedTasksToUi: Mock; taskProjector: { syncFromWorkflowLoop: Mock }; maybeStartWorkflowSetupFollowUp: Mock; finalizeRun: Mock; @@ -676,6 +678,7 @@ type TerminalGuardOrderServiceInternals = { abortController: AbortController; snapshotStorage: unknown; tracing?: InstanceAiTraceContext; + checkpoint?: { isCheckpointFollowUp: boolean; checkpointTaskId: string }; }, ) => Promise; }; @@ -3289,6 +3292,70 @@ describe('InstanceAiService — run error reporter lifecycle', () => { 'endRun', ]); }); + + it('skips post-run scheduling when the resumed run was cancelled', async () => { + const service = createTerminalGuardOrderService(); + const abortController = new AbortController(); + service.runState.hasSuspendedRun = vi.fn(() => false); + service.taskProjector = { syncFromWorkflowLoop: vi.fn(async () => {}) }; + service.maybeStartWorkflowSetupFollowUp = vi.fn(async () => {}); + vi.mocked(resumeAgentRun).mockResolvedValueOnce({ + status: 'cancelled', + agentRunId: 'agent-run-1', + text: Promise.resolve(''), + workSummary: { toolCalls: [], totalToolCalls: 0, totalToolErrors: 0 }, + }); + abortController.abort(); + + await service.processResumedStream({}, {}, resumedStreamOpts(abortController)); + + expect(service.schedulePlannedTasks).not.toHaveBeenCalled(); + expect(service.drainPendingCheckpointReentries).not.toHaveBeenCalled(); + // The UI projection still runs, so a stopped run's task states reach the client. + expect(service.taskProjector.syncFromWorkflowLoop).toHaveBeenCalledWith('thread-a', 'run-1'); + expect(service.instanceAiErrorReporter.endRun).toHaveBeenCalledWith('run-1'); + }); + + it('still marks an abandoned checkpoint terminal when the run was cancelled', async () => { + const service = createTerminalGuardOrderService(); + const abortController = new AbortController(); + service.runState.hasSuspendedRun = vi.fn(() => false); + service.taskProjector = { syncFromWorkflowLoop: vi.fn(async () => {}) }; + service.maybeStartWorkflowSetupFollowUp = vi.fn(async () => {}); + service.syncPlannedTasksToUi = vi.fn(async () => {}); + service.backgroundTasks.getRunningTasksByParentCheckpoint = vi.fn(() => []); + const markCheckpointFailed = vi.fn(async () => {}); + service.createPlannedTaskState = vi.fn(async () => ({ + plannedTaskService: { + getGraph: vi.fn(async () => ({ tasks: [{ id: 'cp-1', status: 'running' }] })), + markCheckpointFailed, + }, + })); + vi.mocked(resumeAgentRun).mockResolvedValueOnce({ + status: 'cancelled', + agentRunId: 'agent-run-1', + text: Promise.resolve(''), + workSummary: { toolCalls: [], totalToolCalls: 0, totalToolErrors: 0 }, + }); + abortController.abort(); + + await service.processResumedStream( + {}, + {}, + { + ...resumedStreamOpts(abortController), + checkpoint: { isCheckpointFollowUp: true, checkpointTaskId: 'cp-1' }, + }, + ); + + // Without this the checkpoint task stays `running` forever and the plan + // graph can never advance, because the cancelled run's context is the only + // thing that knows it was a checkpoint follow-up. + expect(markCheckpointFailed).toHaveBeenCalledWith('thread-a', 'cp-1', { + error: 'Checkpoint run ended without reporting completion', + }); + expect(service.schedulePlannedTasks).not.toHaveBeenCalled(); + }); }); describe('InstanceAiService — user message persistence on cancel', () => { @@ -3309,6 +3376,8 @@ describe('InstanceAiService — user message persistence on cancel', () => { runState: { clearActiveRun: Mock; hasSuspendedRun: Mock }; domainAccessTrackersByThread: Map; instanceAiErrorReporter: { beginRun: Mock; endRun: Mock }; + schedulePlannedTasks: Mock; + taskProjector: { syncFromWorkflowLoop: Mock }; }; function createCancelPersistenceService(): ExecuteRunInternals { @@ -3356,6 +3425,121 @@ describe('InstanceAiService — user message persistence on cancel', () => { expect(service.agentMemory.saveMessages).not.toHaveBeenCalled(); }); + + it('does not re-arm planned-task scheduling after a cancelled run, but still projects', async () => { + const service = createCancelPersistenceService(); + service.runState.hasSuspendedRun = vi.fn(() => false); + service.schedulePlannedTasks = vi.fn(async () => {}); + service.taskProjector = { syncFromWorkflowLoop: vi.fn(async () => {}) }; + const abortController = new AbortController(); + abortController.abort(); + + await service.executeRun(fakeUser, 'thread-1', 'run-1', 'banana', abortController); + + expect(service.schedulePlannedTasks).not.toHaveBeenCalled(); + expect(service.taskProjector.syncFromWorkflowLoop).toHaveBeenCalledWith('thread-1', 'run-1'); + }); +}); + +describe('InstanceAiService — planned task settlement', () => { + type SettlementService = { + cancelRun: (threadId: string, reason?: string) => void; + cancelBackgroundTask: (threadId: string, taskId: string) => void; + schedulePlannedTasks: Mock; + syncPlannedTasksToUi: Mock; + eventBus: { publish: Mock }; + createPlannedTaskState: Mock; + saveAgentTreeSnapshot: Mock; + cancelAwaitingApprovalPlan: Mock; + backgroundTasks: { + cancelThread: Mock; + cancelTask: Mock; + }; + runState: { + getThreadUser: Mock; + cancelThread: Mock; + }; + tracing: { finalizeBackgroundTaskTracing: Mock }; + terminalOutcome: { recordBackgroundTerminalOutcome: Mock }; + suspendedThreads: { dropPendingConfirmationsForThread: Mock }; + }; + + const task = { + plannedTaskId: 'task-1', + threadId: 'thread-a', + runId: 'task-run-1', + agentId: 'agent-1', + role: 'builder', + }; + + function createSettlementService() { + const service = Object.create(InstanceAiService.prototype) as unknown as SettlementService; + const graph = { planRunId: 'plan-run-1', tasks: [] }; + const plannedTaskService = { + markCancelled: vi.fn(async () => graph), + markFailed: vi.fn(async () => graph), + }; + Object.assign(service, { + createPlannedTaskState: vi.fn(async () => ({ plannedTaskService })), + syncPlannedTasksToUi: vi.fn(async () => {}), + schedulePlannedTasks: vi.fn(async () => {}), + saveAgentTreeSnapshot: vi.fn(async () => {}), + cancelAwaitingApprovalPlan: vi.fn(async () => {}), + backgroundTasks: { + cancelThread: vi.fn(() => [task]), + cancelTask: vi.fn(() => task), + }, + runState: { + getThreadUser: vi.fn(() => fakeUser), + cancelThread: vi.fn(() => ({ active: undefined, suspended: undefined })), + }, + tracing: { finalizeBackgroundTaskTracing: vi.fn(async () => {}) }, + eventBus: { publish: vi.fn() }, + terminalOutcome: { recordBackgroundTerminalOutcome: vi.fn(async () => {}) }, + suspendedThreads: { dropPendingConfirmationsForThread: vi.fn(async () => {}) }, + }); + return { service, plannedTaskService, graph }; + } + + /** cancelRun/cancelBackgroundTask fire settlement with `void`, so let it settle. */ + const flush = async () => await new Promise((resolve) => setTimeout(resolve, 0)); + + it('marks the planned task cancelled but does not re-tick when the whole thread is cancelled', async () => { + const { service, plannedTaskService, graph } = createSettlementService(); + + service.cancelRun('thread-a'); + await flush(); + + expect(plannedTaskService.markCancelled).toHaveBeenCalledWith('thread-a', 'task-1', { + error: undefined, + }); + expect(service.syncPlannedTasksToUi).toHaveBeenCalledWith('thread-a', graph); + expect(service.schedulePlannedTasks).not.toHaveBeenCalled(); + expect(service.eventBus.publish).toHaveBeenCalledWith( + 'thread-a', + expect.objectContaining({ + type: 'agent-completed', + payload: { role: 'builder', result: '', status: 'cancelled' }, + }), + ); + }); + + it('re-ticks the scheduler when a single background task is cancelled', async () => { + const { service, plannedTaskService } = createSettlementService(); + + service.cancelBackgroundTask('thread-a', 'task-1'); + await flush(); + + expect(plannedTaskService.markCancelled).toHaveBeenCalled(); + expect(service.schedulePlannedTasks).toHaveBeenCalledWith(fakeUser, 'thread-a'); + expect(service.eventBus.publish).toHaveBeenCalledWith( + 'thread-a', + expect.objectContaining({ + type: 'agent-completed', + payload: { role: 'builder', result: '', status: 'cancelled' }, + }), + ); + }); }); describe('InstanceAiService — agent preview handoff scopes', () => { diff --git a/packages/cli/src/modules/instance-ai/__tests__/node-definition-resolver.test.ts b/packages/cli/src/modules/instance-ai/__tests__/node-definition-resolver.test.ts index c27e3517541..e45425215cb 100644 --- a/packages/cli/src/modules/instance-ai/__tests__/node-definition-resolver.test.ts +++ b/packages/cli/src/modules/instance-ai/__tests__/node-definition-resolver.test.ts @@ -76,3 +76,40 @@ describe('resolveNodeTypeDefinition — resource/operation split', () => { expect(result.content).toBe('// slack post def'); }); }); + +describe('resolveNodeTypeDefinition — latest version resolution', () => { + let defsDir: string; + + beforeAll(() => { + defsDir = mkdtempSync(join(tmpdir(), 'node-defs-versions-')); + // Notion ships versions 1, 2, 2.1, 2.2 and 3, so on disk its version + // dirs are v1, v2, v21, v22, v3 (the dot is dropped when naming). + const notionDir = join(defsDir, 'nodes', 'n8n-nodes-base', 'notion'); + for (const [version, content] of [ + ['v1', '// notion v1 get def'], + ['v2', '// notion v2 get def'], + ['v21', '// notion v2.1 get def'], + ['v22', '// notion v2.2 get def'], + ['v3', '// notion v3 get def'], + ]) { + const opDir = join(notionDir, version, 'resource_database_page'); + mkdirSync(opDir, { recursive: true }); + writeFileSync(join(opDir, 'operation_get.ts'), content); + } + }); + + afterAll(() => { + rmSync(defsDir, { recursive: true, force: true }); + }); + + it('treats v3 as latest, not v2.2, when no version is requested', () => { + const result = resolveNodeTypeDefinition('n8n-nodes-base.notion', [defsDir], { + resource: 'databasePage', + operation: 'get', + }); + + expect(result.error).toBeUndefined(); + expect(result.version).toBe('v3'); + expect(result.content).toBe('// notion v3 get def'); + }); +}); diff --git a/packages/cli/src/modules/instance-ai/__tests__/run-debug-gating.test.ts b/packages/cli/src/modules/instance-ai/__tests__/run-debug-gating.test.ts index 0301da092d0..8a45ccc9f0b 100644 --- a/packages/cli/src/modules/instance-ai/__tests__/run-debug-gating.test.ts +++ b/packages/cli/src/modules/instance-ai/__tests__/run-debug-gating.test.ts @@ -18,6 +18,7 @@ type RunDebugGatingInternals = { runId: string, agentRunId: string, toolCallId: string, + signal: AbortSignal, ) => Record; getRunDebug: (runId: string) => ReturnType; }; @@ -50,16 +51,23 @@ describe('InstanceAiService run debug gating', () => { runId, 'agent-run-1', 'tool-call-1', + signal, ); expect(streamOptions.onStepStart).toBeUndefined(); + expect(streamOptions.onStepEnd).toBeUndefined(); expect(streamOptions.onStepFinish).toBeUndefined(); expect(resumeOptions.onStepStart).toBeUndefined(); + expect(resumeOptions.onStepEnd).toBeUndefined(); expect(resumeOptions.onStepFinish).toBeUndefined(); expect(service.getRunDebug(runId)).toBeUndefined(); // Both terminal paths opt into raw-usage recovery so stopped/errored runs bill. expect(streamOptions.recoverUsageOnAbort).toBe(true); expect(resumeOptions.recoverUsageOnAbort).toBe(true); + // Both paths must carry the run's signal, or a stop cannot reach the agent + // loop and its sub-agents (AGENT-453). + expect(streamOptions.abortSignal).toBe(signal); + expect(resumeOptions.abortSignal).toBe(signal); // Both paths must carry the request-level Anthropic cache directive; without it // on resume, HITL turns reprocess the whole conversation uncached (INS-759). const cacheDirective = { anthropic: { cacheControl: { type: 'ephemeral' } } }; @@ -82,11 +90,14 @@ describe('InstanceAiService run debug gating', () => { runId, 'agent-run-1', 'tool-call-1', + signal, ); expect(typeof streamOptions.onStepStart).toBe('function'); + expect(typeof streamOptions.onStepEnd).toBe('function'); expect(typeof streamOptions.onStepFinish).toBe('function'); expect(typeof resumeOptions.onStepStart).toBe('function'); + expect(typeof resumeOptions.onStepEnd).toBe('function'); expect(typeof resumeOptions.onStepFinish).toBe('function'); expect(service.getRunDebug(runId)).toEqual( expect.objectContaining({ diff --git a/packages/cli/src/modules/instance-ai/eval/__tests__/agent-execution.service.test.ts b/packages/cli/src/modules/instance-ai/eval/__tests__/agent-execution.service.test.ts index 761ca7f0b79..ca79be8fddc 100644 --- a/packages/cli/src/modules/instance-ai/eval/__tests__/agent-execution.service.test.ts +++ b/packages/cli/src/modules/instance-ai/eval/__tests__/agent-execution.service.test.ts @@ -526,6 +526,46 @@ describe('EvalAgentExecutionService.executeWithLlmMock', () => { expect(result.toolCalls[0].interceptedRequests).toHaveLength(1); }); + it('snapshots the tool ledger body so node-side mutation cannot rewrite the evidence', async () => { + // Same aliasing hazard as the workflow ledger: `callEvalMockHandler` returns + // this body to the node tool, and nodes like OpenAI's json_schema mode parse + // fields in place — the judge must still see what was actually served. + const served = { output: [{ content: [{ type: 'output_text', text: '{"a":1}' }] }] }; + vi.mocked(createLlmMockHandler).mockReturnValue( + vi.fn().mockResolvedValue({ body: served, statusCode: 200, headers: {} }), + ); + const generate = vi.fn().mockImplementation(async () => { + const instrumentation = reconstructFromAgentEntity.mock.calls[0][4] as { + configureToolAdditionalData: ( + additionalData: Record, + ctx: { toolName: string; toolKind: string }, + ) => void; + }; + const additionalData: Record = { credentialsHelper: {} }; + instrumentation.configureToolAdditionalData(additionalData, { + toolName: 'Slack_Tool', + toolKind: 'node', + }); + const handler = additionalData.evalLlmMockHandler as EvalLlmMockHandler; + const response = await handler({ url: 'https://slack.com/api/x', method: 'POST' }, { + name: 'Slack_Tool', + type: 'n8n-nodes-base.slackTool', + } as INode); + // Node code mutates the body it was handed. + (response?.body as typeof served).output[0].content[0].text = { a: 1 } as never; + return makeGenerateResult({ toolCalls: [] }); + }); + reconstructFromAgentEntity.mockResolvedValue({ + agent: { generate, close: vi.fn() }, + toolRegistry: {}, + }); + + const result = await buildService().executeWithLlmMock('agent-1', user, request); + + const recorded = result.toolCalls[0].interceptedRequests?.[0].mockResponse as typeof served; + expect(recorded.output[0].content[0].text).toBe('{"a":1}'); + }); + it('auto-approves suspended tool calls and flags them', async () => { const suspended = makeGenerateResult({ pendingSuspend: [ diff --git a/packages/cli/src/modules/instance-ai/eval/__tests__/execution.service.test.ts b/packages/cli/src/modules/instance-ai/eval/__tests__/execution.service.test.ts index b5fe3fbc730..03edb4f0f20 100644 --- a/packages/cli/src/modules/instance-ai/eval/__tests__/execution.service.test.ts +++ b/packages/cli/src/modules/instance-ai/eval/__tests__/execution.service.test.ts @@ -879,6 +879,51 @@ describe('EvalExecutionService', () => { expect(toolUrls).not.toContain('https://api.openai.com/v1/chat/completions'); }); + it('detaches the ledger entry from a node mutating the served response in place', async () => { + // The reported failure: the OpenAI node's json_schema mode parses + // `output[].content[].text` in place, so an aliased entry rewrote + // history and the judge blamed the mock. Asserted at this call site, + // not just on the helper, so a refactor can't bypass the snapshot. + const served = { output: [{ content: [{ type: 'output_text', text: '{"a":1}' }] }] }; + const innerMockHandler = vi.fn().mockResolvedValue({ + body: served, + headers: { 'content-type': 'application/json' }, + statusCode: 200, + }); + createLlmMockHandlerMock.mockReturnValue(innerMockHandler); + + let capturedAd: StubAdditionalData | undefined; + workflowRunner.run.mockImplementation(async (data) => { + const ad = makeMockedAdditionalData(); + await data.configureAdditionalData?.(ad as never); + capturedAd = ad; + return DB_EXECUTION_ID; + }); + + activeExecutions.getPostExecutePromise.mockImplementation(async () => { + const response = (await capturedAd?.evalLlmMockHandler?.( + { url: 'https://api.openai.com/v1/responses', method: 'POST' }, + { + id: 'node-2', + name: 'HTTP Request', + type: 'n8n-nodes-base.httpRequest', + typeVersion: 1, + position: [0, 0], + parameters: {}, + }, + )) as { body: typeof served } | undefined; + // Node code mutates the body it was handed. + response!.body.output[0].content[0].text = { a: 1 } as never; + return makeIRun(); + }); + + const result = await service.executeWithLlmMock('wf-1', makeUser()); + + const recorded = result.nodeResults['HTTP Request'].interceptedRequests[0] + .mockResponse as typeof served; + expect(recorded.output[0].content[0].text).toBe('{"a":1}'); + }); + it('records "(no URL)" when broken node routing emits a request without a URL', async () => { const innerMockHandler = vi.fn().mockResolvedValue({ body: { error: { message: 'no URL' } }, diff --git a/packages/cli/src/modules/instance-ai/eval/__tests__/ledger-snapshot.test.ts b/packages/cli/src/modules/instance-ai/eval/__tests__/ledger-snapshot.test.ts new file mode 100644 index 00000000000..50a34532574 --- /dev/null +++ b/packages/cli/src/modules/instance-ai/eval/__tests__/ledger-snapshot.test.ts @@ -0,0 +1,57 @@ +import { snapshotLedgerBody } from '../ledger-snapshot'; + +describe('snapshotLedgerBody', () => { + it('detaches the ledger entry from later in-place mutations by node code', () => { + const served = { output: [{ content: [{ type: 'output_text', text: '{"a":1}' }] }] }; + const snapshot = snapshotLedgerBody(served) as typeof served; + + // e.g. the OpenAI node's json_schema output mode parses text in place + served.output[0].content[0].text = { a: 1 } as never; + + expect(snapshot.output[0].content[0].text).toBe('{"a":1}'); + }); + + it('copies Buffers so a later write to the served bytes leaves the ledger intact', () => { + const served = Buffer.from('original'); + const snapshot = snapshotLedgerBody(served) as Buffer; + + // `callEvalMockHandler` hands this same Buffer to node code. + served.write('MUTATED!'); + + expect(snapshot).not.toBe(served); + expect(snapshot.toString()).toBe('original'); + expect(served.toString()).toBe('MUTATED!'); + }); + + it('passes nullish bodies and primitives through', () => { + expect(snapshotLedgerBody(undefined)).toBeUndefined(); + expect(snapshotLedgerBody(null)).toBeNull(); + expect(snapshotLedgerBody('plain text')).toBe('plain text'); + expect(snapshotLedgerBody(42)).toBe(42); + }); + + it('keeps keys a JSON-semantics copy would drop', () => { + // `deepCopy` skips own __proto__/constructor/prototype keys, which are + // legal JSON — losing them corrupts the evidence just as silently. + const served = JSON.parse( + '{"constructor":"kept","prototype":"kept","__proto__":{"a":1},"normal":"kept"}', + ) as Record; + + const snapshot = snapshotLedgerBody(served) as Record; + + expect(Object.keys(snapshot).sort()).toEqual( + ['__proto__', 'constructor', 'normal', 'prototype'].sort(), + ); + expect(snapshot.constructor).toBe('kept'); + expect(snapshot.prototype).toBe('kept'); + }); + + it('detaches circular bodies without throwing', () => { + const circular: Record = { tag: 'original' }; + circular.self = circular; + const snapshot = snapshotLedgerBody(circular) as Record; + circular.tag = 'mutated'; + expect(snapshot).not.toBe(circular); + expect(snapshot.tag).toBe('original'); + }); +}); diff --git a/packages/cli/src/modules/instance-ai/eval/__tests__/mock-handler.test.ts b/packages/cli/src/modules/instance-ai/eval/__tests__/mock-handler.test.ts index 03dfb0182bb..8eaf529ab8f 100644 --- a/packages/cli/src/modules/instance-ai/eval/__tests__/mock-handler.test.ts +++ b/packages/cli/src/modules/instance-ai/eval/__tests__/mock-handler.test.ts @@ -987,3 +987,158 @@ describe('get_endpoint_quirks tool', () => { expect(result).toContain('No specific quirks'); }); }); + +// --------------------------------------------------------------------------- +// Endpoint normalizers — binary fidelity (Drive resumable init, Gmail raw attachments) +// --------------------------------------------------------------------------- + +describe('normalizeDriveResumableInit', () => { + const driveNode = { name: 'Drive', type: 'n8n-nodes-base.googleDrive' } as INode; + + it('serves the resumable initiation deterministically with a location header', async () => { + llmSubmits({ type: 'json', body: { id: 'file123' } }); + const handler = createLlmMockHandler(); + const result = await callHandler( + handler, + { + url: 'https://www.googleapis.com/upload/drive/v3/files', + method: 'POST', + qs: { uploadType: 'resumable', supportsAllDrives: true }, + } as IHttpRequestOptions, + driveNode, + ); + + expect(result.body).toEqual({}); + expect(result.statusCode).toBe(200); + expect(result.headers.location).toBe( + 'https://www.googleapis.com/upload/drive/v3/files?upload_id=eval-resumable-upload', + ); + }); + + it('leaves the chunk PUT to the minted session URI to the model', async () => { + llmSubmits({ type: 'json', body: { id: 'file123', name: 'x.png' } }); + const handler = createLlmMockHandler(); + const result = await callHandler( + handler, + { + url: 'https://www.googleapis.com/upload/drive/v3/files?uploadType=resumable&upload_id=eval-resumable-upload', + method: 'PUT', + } as IHttpRequestOptions, + driveNode, + ); + + expect(result.body).toEqual({ id: 'file123', name: 'x.png' }); + expect(result.headers.location).toBeUndefined(); + }); + + it('does not touch multipart uploads', async () => { + llmSubmits({ type: 'json', body: { id: 'file123' } }); + const handler = createLlmMockHandler(); + const result = await callHandler( + handler, + { + url: 'https://www.googleapis.com/upload/drive/v3/files?uploadType=multipart', + method: 'POST', + } as IHttpRequestOptions, + driveNode, + ); + + expect(result.body).toEqual({ id: 'file123' }); + expect(result.headers.location).toBeUndefined(); + }); +}); + +describe('substituteBinaryMimeParts (gmail raw attachments)', () => { + const gmailNode = { name: 'Get Invoice Email', type: 'n8n-nodes-base.gmail' } as INode; + const gmailRequest = { + url: 'https://www.googleapis.com/gmail/v1/users/me/messages/m1', + method: 'GET', + qs: { format: 'raw' }, + } as IHttpRequestOptions; + + const rawWithPdfPart = [ + 'From: billing@acme.test', + 'To: me@example.test', + 'Subject: Invoice INV-88', + 'MIME-Version: 1.0', + 'Content-Type: multipart/mixed; boundary="b1"', + '', + '--b1', + 'Content-Type: text/plain; charset="UTF-8"', + '', + 'Invoice attached.', + '--b1', + 'Content-Type: application/pdf; name="invoice.pdf"', + 'Content-Disposition: attachment; filename="invoice.pdf"', + 'Content-Transfer-Encoding: base64', + '', + 'Invoice INV-88 total USD 42.50', + '--b1--', + ].join('\n'); + + it('replaces a plaintext-authored PDF part with real bytes carrying that text', async () => { + llmSubmits({ type: 'json', body: { id: 'm1', raw: rawWithPdfPart } }); + const handler = createLlmMockHandler(); + const result = await callHandler(handler, gmailRequest, gmailNode); + + const raw = (result.body as { raw: string }).raw; + const decoded = Buffer.from(raw, 'base64url').toString('utf8'); + expect(decoded).toContain('Invoice attached.'); // text part untouched + const partB64 = decoded + .split('Content-Transfer-Encoding: base64')[1] + .split('--b1--')[0] + .replace(/\s/g, ''); + const pdf = Buffer.from(partB64, 'base64'); + expect(pdf.subarray(0, 5).toString('latin1')).toBe('%PDF-'); + expect(pdf.toString('latin1')).toContain('(Invoice INV-88 total USD 42.50) Tj'); + }); + + it('replaces a garbage base64 PDF stub with a valid document', async () => { + const rawWithStub = rawWithPdfPart.replace('Invoice INV-88 total USD 42.50', 'JVBERi0xLjQ='); + llmSubmits({ type: 'json', body: { id: 'm1', raw: rawWithStub } }); + const handler = createLlmMockHandler(); + const result = await callHandler(handler, gmailRequest, gmailNode); + + const decoded = Buffer.from((result.body as { raw: string }).raw, 'base64url').toString('utf8'); + const partB64 = decoded + .split('Content-Transfer-Encoding: base64')[1] + .split('--b1--')[0] + .replace(/\s/g, ''); + const pdf = Buffer.from(partB64, 'base64'); + expect(pdf.subarray(0, 5).toString('latin1')).toBe('%PDF-'); + expect(pdf.toString('latin1')).toContain('%%EOF'); + }); + + it('substitutes image attachment parts with the standard fixture bytes', async () => { + const rawWithImage = rawWithPdfPart + .replace( + 'Content-Type: application/pdf; name="invoice.pdf"', + 'Content-Type: image/png; name="chart.png"', + ) + .replace( + 'Content-Disposition: attachment; filename="invoice.pdf"', + 'Content-Disposition: attachment; filename="chart.png"', + ); + llmSubmits({ type: 'json', body: { id: 'm1', raw: rawWithImage } }); + const handler = createLlmMockHandler(); + const result = await callHandler(handler, gmailRequest, gmailNode); + + const decoded = Buffer.from((result.body as { raw: string }).raw, 'base64url').toString('utf8'); + const partB64 = decoded + .split('Content-Transfer-Encoding: base64')[1] + .split('--b1--')[0] + .replace(/\s/g, ''); + const png = Buffer.from(partB64, 'base64'); + expect(png.subarray(1, 4).toString('latin1')).toBe('PNG'); + }); + + it('adds the base64 transfer-encoding header when the part lacked one', async () => { + const rawNoEncoding = rawWithPdfPart.replace('Content-Transfer-Encoding: base64\n', ''); + llmSubmits({ type: 'json', body: { id: 'm1', raw: rawNoEncoding } }); + const handler = createLlmMockHandler(); + const result = await callHandler(handler, gmailRequest, gmailNode); + + const decoded = Buffer.from((result.body as { raw: string }).raw, 'base64url').toString('utf8'); + expect(decoded).toContain('Content-Transfer-Encoding: base64'); + }); +}); diff --git a/packages/cli/src/modules/instance-ai/eval/__tests__/provider-shapes.test.ts b/packages/cli/src/modules/instance-ai/eval/__tests__/provider-shapes.test.ts index 6b14416473e..2c3cc18b69d 100644 --- a/packages/cli/src/modules/instance-ai/eval/__tests__/provider-shapes.test.ts +++ b/packages/cli/src/modules/instance-ai/eval/__tests__/provider-shapes.test.ts @@ -73,15 +73,31 @@ describe('applyProviderShapeNormalizers', () => { expect(body.created).toBe(123); }); - it('preserves an already-correct b64_json envelope', () => { + it('substitutes model-authored b64_json with decodable PNG bytes, preserving siblings', () => { const spec: Spec = { type: 'json', - body: { created: 1, data: [{ b64_json: 'AAAA', revised_prompt: 'p' }] }, + body: { created: 1, data: [{ b64_json: 'iVBORw0KGgo', revised_prompt: 'p' }] }, }; applyProviderShapeNormalizers(openAiImages, spec); - expect(spec.body).toMatchObject({ - data: [{ b64_json: 'AAAA', revised_prompt: 'p' }], - }); + const body = spec.body as { data: Array<{ b64_json: string; revised_prompt: string }> }; + expect(body.data[0].revised_prompt).toBe('p'); + const png = Buffer.from(body.data[0].b64_json, 'base64'); + // Real decodable canvas, not the old 8-byte truncated header. + expect(png.subarray(1, 4).toString()).toBe('PNG'); + expect(png.readUInt32BE(16)).toBe(512); + expect(png.readUInt32BE(20)).toBe(512); + }); + + it('preserves url entries while adding decodable b64_json alongside', () => { + const spec: Spec = { + type: 'json', + body: { created: 1, data: [{ url: 'https://example.invalid/img.png' }] }, + }; + applyProviderShapeNormalizers(openAiImages, spec); + const body = spec.body as { data: Array<{ url: string; b64_json: string }> }; + expect(body.data[0].url).toBe('https://example.invalid/img.png'); + const png = Buffer.from(body.data[0].b64_json, 'base64'); + expect(png.subarray(1, 4).toString()).toBe('PNG'); }); }); @@ -249,3 +265,51 @@ describe('findProviderShapeViolation', () => { expect(findProviderShapeViolation(googleDocs, { replies: [{}] })).toBeUndefined(); }); }); + +describe('Gmail messages.list', () => { + const gmailList = info({ + hostname: 'www.googleapis.com', + pathname: '/gmail/v1/users/me/messages', + method: 'GET', + }); + + it('wraps a bare array into the messages envelope', () => { + const spec: Spec = { + type: 'json', + body: [{ id: 'msg_1', threadId: 'thr_1' }], + }; + applyProviderShapeNormalizers(gmailList, spec); + expect(spec.body).toEqual({ + messages: [{ id: 'msg_1', threadId: 'thr_1' }], + resultSizeEstimate: 1, + }); + }); + + it('leaves a correct envelope untouched', () => { + const spec: Spec = { + type: 'json', + body: { messages: [{ id: 'msg_1' }], resultSizeEstimate: 1 }, + }; + const before = structuredClone(spec.body); + applyProviderShapeNormalizers(gmailList, spec); + expect(spec.body).toEqual(before); + }); + + it('does not touch the single-message GET', () => { + const single = info({ + hostname: 'www.googleapis.com', + pathname: '/gmail/v1/users/me/messages/msg_1', + method: 'GET', + }); + const spec: Spec = { type: 'json', body: [{ anything: true }] }; + applyProviderShapeNormalizers(single, spec); + expect(Array.isArray(spec.body)).toBe(true); + }); + + it('reports a violation for a bare array and passes the envelope', () => { + expect(findProviderShapeViolation(gmailList, [{ id: 'msg_1' }])).toContain('bare array'); + expect( + findProviderShapeViolation(gmailList, { messages: [], resultSizeEstimate: 0 }), + ).toBeUndefined(); + }); +}); diff --git a/packages/cli/src/modules/instance-ai/eval/agent-execution.service.ts b/packages/cli/src/modules/instance-ai/eval/agent-execution.service.ts index 678ff79a6ba..96a684f336c 100644 --- a/packages/cli/src/modules/instance-ai/eval/agent-execution.service.ts +++ b/packages/cli/src/modules/instance-ai/eval/agent-execution.service.ts @@ -39,6 +39,7 @@ import { createAiProxyFetch } from '@/utils/ai-proxy-fetch'; import { createAgentModelTurnRecorder } from './agent-model-turn-recorder'; import { generateAgentScenarioSeed, type AgentSeedToolSummary } from './agent-scenario-seed'; import { EvalMockedCredentialsHelper } from './eval-mocked-credentials-helper'; +import { snapshotLedgerBody } from './ledger-snapshot'; import { createMcpMockFetch, type McpMockCanonicalTool, @@ -533,7 +534,7 @@ export class EvalAgentExecutionService { method: requestOptions.method ?? 'GET', nodeType: node.type, requestBody: requestOptions.body, - mockResponse: response?.body, + mockResponse: snapshotLedgerBody(response?.body), }); this.logger.debug( `[EvalAgentMock] Intercepted ${requestOptions.method ?? 'GET'} ${requestOptions.url} from tool "${toolName}" (${node.type})`, diff --git a/packages/cli/src/modules/instance-ai/eval/execution.service.ts b/packages/cli/src/modules/instance-ai/eval/execution.service.ts index c07bb537bc2..2668f1d192d 100644 --- a/packages/cli/src/modules/instance-ai/eval/execution.service.ts +++ b/packages/cli/src/modules/instance-ai/eval/execution.service.ts @@ -52,6 +52,7 @@ import { WorkflowStaticDataService } from '@/workflows/workflow-static-data.serv import { createLlmCompletionMockHandler } from './llm-completion-mock'; import { EvalMockedCredentialsHelper } from './eval-mocked-credentials-helper'; import { EvalTimings } from './eval-timings'; +import { snapshotLedgerBody } from './ledger-snapshot'; import { type InterceptedTurn, LlmWireServer } from './llm-wire-server'; import { createLlmMockHandler } from './mock-handler'; import { @@ -886,7 +887,7 @@ export class EvalExecutionService { method: requestOptions.method ?? 'GET', nodeType: node.type, requestBody: requestOptions.body, - mockResponse: response?.body, + mockResponse: snapshotLedgerBody(response?.body), }); this.logger.debug( diff --git a/packages/cli/src/modules/instance-ai/eval/ledger-snapshot.ts b/packages/cli/src/modules/instance-ai/eval/ledger-snapshot.ts new file mode 100644 index 00000000000..061347cd883 --- /dev/null +++ b/packages/cli/src/modules/instance-ai/eval/ledger-snapshot.ts @@ -0,0 +1,29 @@ +/** + * Ledger entries must be immutable snapshots. The served body is handed to node + * code, and some nodes mutate it in place — e.g. the OpenAI node's json_schema + * output mode parses `output[].content[].text` into an object — so an aliased + * ledger entry rewrites history and the judge blames the mock for a body it + * never sent. + * + * `structuredClone` rather than `deepCopy`: the latter deliberately drops own + * `__proto__`/`constructor`/`prototype` keys (`n8n-workflow/utils.ts`), which are + * legal JSON response keys — dropping them is the same evidence corruption in a + * different disguise. Non-cloneable values (functions) throw here where + * `deepCopy` coped silently; mock bodies are JSON or Buffers, so that trade + * favours fidelity. + * + * Buffers are copied too: `callEvalMockHandler` hands the same Buffer to node + * code, so passing it by reference would preserve the exact aliasing this + * helper exists to remove. The copy is bounded — the largest synthesized binary + * fixture targets 1 MiB (`SIZE_TARGETS.large`) — which beats relying on every + * downstream parser staying read-only. + * + * Shared by both recording sites: the workflow eval ledger + * (`EvalExecutionService`) and the agent tool ledger + * (`EvalAgentExecutionService`). + */ +export function snapshotLedgerBody(body: unknown): unknown { + if (typeof body !== 'object' || body === null) return body; + if (Buffer.isBuffer(body)) return Buffer.from(body); + return structuredClone(body); +} diff --git a/packages/cli/src/modules/instance-ai/eval/mock-handler.ts b/packages/cli/src/modules/instance-ai/eval/mock-handler.ts index 8ca928c8f56..edd719649f4 100644 --- a/packages/cli/src/modules/instance-ai/eval/mock-handler.ts +++ b/packages/cli/src/modules/instance-ai/eval/mock-handler.ts @@ -12,7 +12,7 @@ import { Logger } from '@n8n/backend-common'; import { Container } from '@n8n/di'; import { createEvalAgent, extractText, Tool } from '@n8n/instance-ai'; import type { EvalLlmMockHandler, EvalMockHttpResponse, FixtureSizeHint } from 'n8n-core'; -import { synthesizeBinaryFixture } from 'n8n-core'; +import { buildPdfWithText, synthesizeBinaryFixture } from 'n8n-core'; import { z } from 'zod'; import { fetchApiDocs } from './api-docs'; @@ -124,6 +124,8 @@ interface MockResponseSpec { contentType?: string; filename?: string; sizeHint?: FixtureSizeHint; + /** Extra response headers, set only by endpoint normalizers (never model-authored) — e.g. the Drive resumable-session `location`. */ + headers?: Record; } // --------------------------------------------------------------------------- @@ -843,10 +845,47 @@ async function callLlm( * only mechanical encodings that models can't produce reliably belong here. */ function applyEndpointNormalizers( - request: { url: string; qs?: Record }, + request: { url: string; qs?: Record; method?: string }, spec: MockResponseSpec, ): void { normalizeGmailRawMessage(request, spec); + normalizeDriveResumableInit(request, spec); +} + +/** + * Google Drive resumable uploads read the session URI from the initiation + * response's `location` HEADER (the body is empty) — the model can only author + * bodies, so the node ends up PUTting to `undefined`. Serve the canonical + * empty-body + Location response deterministically; the follow-up PUT to the + * minted session URI is mocked as a normal turn (file resource JSON). + */ +function normalizeDriveResumableInit( + request: { url: string; qs?: Record; method?: string }, + spec: MockResponseSpec, +): void { + const url = request.url; + if (!/\/upload\/drive\/v[23]\/files/i.test(url)) return; + // The chunk upload itself PUTs to the minted session URI — leave it alone. + if ((request.method ?? 'GET').toUpperCase() === 'PUT') return; + if (url.includes('upload_id=')) return; + const uploadType = request.qs?.uploadType ?? extractQueryParam(url, 'uploadType'); + if (uploadType !== 'resumable') return; + + spec.type = 'json'; + spec.body = {}; + spec.statusCode = 200; + spec.headers = { + ...spec.headers, + location: `${url}${url.includes('?') ? '&' : '?'}upload_id=eval-resumable-upload`, + }; +} + +function extractQueryParam(url: string, name: string): string | undefined { + try { + return new URL(url).searchParams.get(name) ?? undefined; + } catch { + return undefined; + } } /** @@ -869,7 +908,64 @@ function normalizeGmailRawMessage( if (typeof body.raw !== 'string' || body.raw.length === 0) return; if (!looksLikeRfc822(body.raw)) return; - body.raw = Buffer.from(body.raw, 'utf8').toString('base64url'); + body.raw = Buffer.from(substituteBinaryMimeParts(body.raw), 'utf8').toString('base64url'); +} + +/** Binary attachment MIME types the harness substitutes real bytes for. */ +const BINARY_PART_RE = + /(content-type:\s*(application\/pdf|image\/(?:png|jpeg|jpg|gif|webp))[^]*?\r?\n\r?\n)([^]*?)(?=\r?\n--|$)/gi; + +const BASE64_BODY_RE = /^[A-Za-z0-9+/=\s]+$/; + +/** + * Replace binary attachment part bodies inside an RFC822 source with real + * encoded bytes. The model authors the MIME structure and the document's + * CONTENT but cannot author valid binary: a PDF part's body (plaintext per the + * quirk guidance, or a garbage base64 stub) becomes a real PDF carrying that + * text; image parts get the standard decodable fixture. Downstream parsers + * (Extract PDF Text via pdfjs, Edit Image via GraphicsMagick) then run on + * structurally valid bytes instead of crashing. + */ +export function substituteBinaryMimeParts(rfc822: string): string { + return rfc822.replace( + BINARY_PART_RE, + (_match, headerBlock: string, mime: string, partBody: string) => { + const lowerMime = mime.toLowerCase(); + const bytes = + lowerMime === 'application/pdf' + ? buildPdfWithText(recoverIntendedText(partBody)) + : synthesizeBinaryFixture(lowerMime, `attachment.${lowerMime.split('/')[1]}`); + + // Chunk to 76 columns per MIME convention; ensure the part headers + // declare base64 so the mail parser decodes what we wrote. + const encoded = bytes.toString('base64').replace(/(.{76})/g, '$1\n'); + const header = /content-transfer-encoding:/i.test(headerBlock) + ? headerBlock.replace( + /content-transfer-encoding:[^\r\n]*/i, + 'Content-Transfer-Encoding: base64', + ) + : headerBlock.replace(/(\r?\n\r?\n)$/, '\nContent-Transfer-Encoding: base64$1'); + return `${header}${encoded}`; + }, + ); +} + +/** + * Best-effort recovery of the text a PDF part was meant to carry: plaintext + * bodies (the quirk-guided path) pass through; base64 stubs are decoded and + * kept only when they decode to printable text rather than binary garbage. + */ +function recoverIntendedText(partBody: string): string { + const trimmed = partBody.trim(); + if (trimmed.length === 0) return 'Mock PDF document'; + const looksBase64 = BASE64_BODY_RE.test(trimmed) && !/\s[a-z]+\s/i.test(trimmed); + if (!looksBase64) return trimmed; + const decoded = Buffer.from(trimmed, 'base64').toString('utf8'); + // Printable ASCII (plus whitespace) and not itself a PDF stub → treat as the intended text. + if (decoded.length > 0 && !decoded.startsWith('%PDF') && /^[\x20-\x7e\s]+$/.test(decoded)) { + return decoded; + } + return 'Mock PDF document'; } /** @@ -891,7 +987,7 @@ function materializeSpec(spec: MockResponseSpec): EvalMockHttpResponse { // empty payload for 204/202 endpoints, which the schema explicitly allows). return { body: spec.body === undefined ? { ok: true } : spec.body, - headers: { 'content-type': 'application/json' }, + headers: { 'content-type': 'application/json', ...spec.headers }, statusCode: 200, }; diff --git a/packages/cli/src/modules/instance-ai/eval/mock-quirks.ts b/packages/cli/src/modules/instance-ai/eval/mock-quirks.ts index 63de19f875f..e3f3695ad6d 100644 --- a/packages/cli/src/modules/instance-ai/eval/mock-quirks.ts +++ b/packages/cli/src/modules/instance-ai/eval/mock-quirks.ts @@ -65,7 +65,7 @@ export const MOCK_QUIRKS: MockQuirk[] = [ 'OpenAI endpoints commonly seen in workflows:\n' + ' * `POST /v1/responses` (Responses API) → JSON `{ "id": "resp_abc123", "object": "response", "status": "completed", "model": "", "output": [{ "type": "message", "id": "msg_abc123", "status": "completed", "role": "assistant", "content": [{ "type": "output_text", "text": "", "annotations": [] }] }], "usage": { "input_tokens": 100, "output_tokens": 50, "total_tokens": 150 } }`. The `output` ARRAY is REQUIRED — consumers read `output[].content[].text` and crash without it. NEVER return `{ "content": ... }` or a chat-completions `choices` shape for this endpoint.\n' + ' * `POST /v1/audio/transcriptions` and `POST /v1/audio/translations` → JSON `{ "text": "", ... }`. The request multipart body has been redacted (you will see `__redacted: "multipart"`); derive the transcript from scenario/node hints when present, otherwise return a short generic English sentence.\n' + - ' * `POST /v1/images/generations` → ALWAYS `type: "json"`, NEVER a binary/Buffer response (this endpoint returns a JSON envelope, not raw image bytes — a Buffer crashes the node with "data is not iterable"). Body: `{ "created": , "data": [ ... ] }` where `data` is an ARRAY of image OBJECTS (never a flat byte array). The per-image shape depends on the request `model`: the `gpt-image-1` family (`gpt-image-1`, `gpt-image-1-mini`) ALWAYS returns base64 and NEVER a url (it does not support `response_format`), so each `data[]` entry MUST be `{ "b64_json": "iVBORw0KGgo", "revised_prompt": "..." }` — never `{ "url": ... }`. For `dall-e-2`/`dall-e-3`, return `{ "url": "https://example.invalid/img.png", "revised_prompt": "..." }` by default, or `{ "b64_json": "iVBORw0KGgo", "revised_prompt": "..." }` when the request body has `response_format: "b64_json"`. The literal base64 value `iVBORw0KGgo` is fine — the eval harness does not decode it.\n' + + ' * `POST /v1/images/generations` → ALWAYS `type: "json"`, NEVER a binary/Buffer response (this endpoint returns a JSON envelope, not raw image bytes — a Buffer crashes the node with "data is not iterable"). Body: `{ "created": , "data": [ ... ] }` where `data` is an ARRAY of image OBJECTS (never a flat byte array). The per-image shape depends on the request `model`: the `gpt-image-1` family (`gpt-image-1`, `gpt-image-1-mini`) ALWAYS returns base64 and NEVER a url (it does not support `response_format`), so each `data[]` entry MUST be `{ "b64_json": "iVBORw0KGgo", "revised_prompt": "..." }` — never `{ "url": ... }`. For `dall-e-2`/`dall-e-3`, return `{ "url": "https://example.invalid/img.png", "revised_prompt": "..." }` by default, or `{ "b64_json": "iVBORw0KGgo", "revised_prompt": "..." }` when the request body has `response_format: "b64_json"`. Any short placeholder string is fine for `b64_json` — the harness substitutes real decodable image bytes (workflows often pipe generated images into image-processing nodes).\n' + ' * `GET /v1/files/{file_id}/content` → BINARY (`type: "binary"`). Use `contentType` matching the file MIME if known, else `application/octet-stream`. The metadata sibling `GET /v1/files/{file_id}` is JSON.\n' + ' * Chat completions, embeddings, moderations, files-list, models-list → JSON only.', rationale: @@ -93,7 +93,8 @@ export const MOCK_QUIRKS: MockQuirk[] = [ ' * `GET /drive/v3/files/{id}?alt=media` → BINARY. Use `mimeType` from the request query/headers if provided, else default `application/pdf`. Set a sensible `filename` ending in the correct extension.\n' + ' * `GET /drive/v3/files/{id}/export?mimeType=` → BINARY with the requested `mimeType` (Google Docs/Sheets/Slides export).\n' + ' * `GET /drive/v3/files/{id}` (no `alt=media`) → JSON metadata.\n' + - ' * `POST /upload/drive/v3/files?uploadType=...` → JSON metadata about the uploaded file (the upload body is redacted multipart — just synthesize a plausible `{ id, name, mimeType, ... }`). NEVER binary.\n' + + ' * `POST /upload/drive/v3/files?uploadType=multipart|media` → JSON metadata about the uploaded file (the upload body is redacted multipart — just synthesize a plausible `{ id, name, mimeType, ... }`). NEVER binary.\n' + + ' * `uploadType=resumable` session initiation (POST/PATCH without `upload_id`) → handled deterministically by the harness (empty body + `Location` header); keep your body `{}`. The follow-up `PUT` carrying `upload_id=` in the URL is the chunk upload → return the complete created file resource JSON `{ "id", "name", "mimeType", ... }` — the node reads `id` from it.\n' + ' * Sheets/Calendar/Gmail endpoints under `/sheets/v4`, `/calendar/v3`, `/gmail/v1` → JSON.', rationale: 'Drive file download uses `alt=media` on the same URL as metadata; the LLM otherwise treats every `/files/{id}` as metadata JSON and breaks the download path.', @@ -113,7 +114,7 @@ export const MOCK_QUIRKS: MockQuirk[] = [ endpoint: 'GET /gmail/v1/users/*/messages/*', guidance: 'Gmail messages.get — the response shape depends on the `format` query param, and the n8n Gmail node crashes on the wrong one:\n' + - ' * `format=raw` (what the node sends whenever "Simplify" is off): the message JSON MUST include `raw` containing the FULL RFC822 email source — header lines (`From:`, `To:`, `Subject:`, `Date:`, `Content-Type:`), a blank line, then the body — alongside `id`, `threadId`, `labelIds`, `sizeEstimate`. Write `raw` as PLAIN multi-line text; the harness applies the base64 transport encoding for you. Prefer a simple single-part `Content-Type: text/plain; charset="UTF-8"` message unless the scenario needs attachments. NEVER answer a `format=raw` request with a `payload`-only message — the node decodes `raw` unconditionally.\n' + + ' * `format=raw` (what the node sends whenever "Simplify" is off): the message JSON MUST include `raw` containing the FULL RFC822 email source — header lines (`From:`, `To:`, `Subject:`, `Date:`, `Content-Type:`), a blank line, then the body — alongside `id`, `threadId`, `labelIds`, `sizeEstimate`. Write `raw` as PLAIN multi-line text; the harness applies the base64 transport encoding for you. Prefer a simple single-part `Content-Type: text/plain; charset="UTF-8"` message unless the scenario needs attachments. When the scenario needs a binary attachment (e.g. a PDF invoice), add a `multipart/mixed` part with the proper `Content-Type` (e.g. `application/pdf; name="invoice.pdf"`) and `Content-Disposition: attachment` headers, and write the part body as the document\'s PLAINTEXT content — the exact text the workflow should extract from the file; the harness replaces it with real encoded bytes of a document carrying that text. NEVER attempt to write base64 file bytes yourself. NEVER answer a `format=raw` request with a `payload`-only message — the node decodes `raw` unconditionally.\n' + ' * `format=metadata` → `{ id, threadId, labelIds, snippet, sizeEstimate, payload: { headers: [{ name, value }, ...] } }` — headers array only, no body data, no `raw`.\n' + ' * `format=full` → `payload` tree where EVERY leaf part carries `body: { size: , data: "" }`; multipart containers use `mimeType: "multipart/..."`, `body: { size: 0 }`, and a `parts` array. Never emit a leaf part whose `body` has only `size` — nodes decode `body.data` and crash on undefined.', rationale: diff --git a/packages/cli/src/modules/instance-ai/eval/provider-shapes.ts b/packages/cli/src/modules/instance-ai/eval/provider-shapes.ts index 23dc49718fc..b4f7033c223 100644 --- a/packages/cli/src/modules/instance-ai/eval/provider-shapes.ts +++ b/packages/cli/src/modules/instance-ai/eval/provider-shapes.ts @@ -20,6 +20,8 @@ * data before the normalizer falls back to a minimal envelope. */ +import { evalCanvasPng } from 'n8n-core'; + /** The subset of the mock response spec these normalizers read/mutate. */ interface NormalizableSpec { type: 'json' | 'text' | 'binary' | 'error'; @@ -33,10 +35,16 @@ export interface ProviderRequestInfo { hostname?: string; } -// A short, non-decodable base64 placeholder. The eval harness never decodes the -// image bytes — it only needs `Buffer.from(b64_json, 'base64')` to receive a -// string. Same literal the OpenAI quirk guidance suggests. -const PLACEHOLDER_B64 = 'iVBORw0KGgo'; +// Generated image bytes must be REAL: workflows routinely pipe them into +// image-processing nodes (Edit Image → GraphicsMagick), which crash on a +// truncated or 1×1 placeholder ("Insufficient image data"). The model cannot +// author valid image bytes, so every b64_json is substituted with a decodable +// drawable canvas. +let canvasPngB64: string | undefined; +function placeholderImageB64(): string { + canvasPngB64 ??= evalCanvasPng().toString('base64'); + return canvasPngB64; +} // A deterministic numeric HubSpot contact id used when the model didn't supply // one — the node interpolates it into a follow-up `/contact/vid/{vid}/profile` @@ -80,6 +88,14 @@ function isGoogleDocsBatchUpdate(host: string, path: string): boolean { ); } +function isGmailMessagesList(host: string, path: string, method: string): boolean { + return ( + method.toUpperCase() === 'GET' && + host.endsWith('googleapis.com') && + /\/gmail\/v1\/users\/[^/]+\/messages\/?$/.test(path) + ); +} + // --------------------------------------------------------------------------- // Tolerant text extraction (shared by the Gemini normalizer) // --------------------------------------------------------------------------- @@ -133,10 +149,10 @@ function normalizeOpenAiImages(spec: NormalizableSpec): void { } function coerceImageEntry(entry: unknown): Record { - if (typeof entry === 'string') return { b64_json: entry }; - if (!isPlainObject(entry)) return { b64_json: PLACEHOLDER_B64 }; - if (typeof entry.b64_json === 'string' && entry.b64_json.length > 0) return entry; - return { ...entry, b64_json: PLACEHOLDER_B64 }; + // Every entry gets real bytes (the node's default b64_json mode crashes on a + // missing field even when the model answered url-style); `url` is preserved. + const base = isPlainObject(entry) ? entry : {}; + return { ...base, b64_json: placeholderImageB64() }; } // --------------------------------------------------------------------------- @@ -234,6 +250,21 @@ function resolveHubspotVid(body: Record): number { return DEFAULT_HUBSPOT_VID; } +// --------------------------------------------------------------------------- +// Gmail — messages.list +// --------------------------------------------------------------------------- + +/** + * The Gmail node's list operation reads `responseData.messages`. A recurring + * model slip (despite quirk guidance) is answering with a bare ARRAY of + * message objects — the node then sees zero messages and the workflow's list + * branch silently does nothing. Wrap a bare array into the real envelope. + */ +function normalizeGmailMessagesList(spec: NormalizableSpec): void { + if (!Array.isArray(spec.body)) return; + spec.body = { messages: spec.body, resultSizeEstimate: spec.body.length }; +} + // --------------------------------------------------------------------------- // Google Docs — /documents/{id}:batchUpdate // --------------------------------------------------------------------------- @@ -273,6 +304,7 @@ export function applyProviderShapeNormalizers( if (redditKind) return normalizeReddit(spec, redditKind); if (isHubspotUpsert(host, path)) return normalizeHubspotUpsert(spec); if (isGoogleDocsBatchUpdate(host, path)) return normalizeGoogleDocsBatchUpdate(spec); + if (isGmailMessagesList(host, path, info.method)) return normalizeGmailMessagesList(spec); } /** @@ -293,10 +325,16 @@ export function findProviderShapeViolation( if (redditKind) return redditViolation(body, redditKind); if (isHubspotUpsert(host, path)) return hubspotViolation(body); if (isGoogleDocsBatchUpdate(host, path)) return googleDocsViolation(body); + if (isGmailMessagesList(host, path, info.method)) return gmailMessagesListViolation(body); return undefined; } +function gmailMessagesListViolation(body: unknown): string | undefined { + if (!Array.isArray(body)) return undefined; + return 'Invalid: Gmail messages.list returns an OBJECT envelope `{ "messages": [{ "id", "threadId" }, ...], "resultSizeEstimate": }` — the node reads response.messages, so a bare array yields zero messages. Resubmit wrapped in that envelope.'; +} + function openAiImagesViolation(body: unknown): string | undefined { const data = isPlainObject(body) ? body.data : undefined; if (!Array.isArray(data) || data.length === 0) { diff --git a/packages/cli/src/modules/instance-ai/event-bus/__tests__/interrupted-run-sweeper.test.ts b/packages/cli/src/modules/instance-ai/event-bus/__tests__/interrupted-run-sweeper.test.ts index df7b8ca9c1b..a1e9eb1dd71 100644 --- a/packages/cli/src/modules/instance-ai/event-bus/__tests__/interrupted-run-sweeper.test.ts +++ b/packages/cli/src/modules/instance-ai/event-bus/__tests__/interrupted-run-sweeper.test.ts @@ -351,7 +351,7 @@ describe('InterruptedRunSweeper.cancelUnfinishedRuns', () => { expect(metrics.sweep.runsCrashResumed).toBe(0); }); - it('terminalizes orphaned spawned children with the user-cancel wording', async () => { + it('terminalizes orphaned spawned children as cancelled without an error', async () => { const { sweeper, published } = buildSweeper({ events: [runStart(), agentSpawned('child-orphaned')], }); @@ -361,10 +361,11 @@ describe('InterruptedRunSweeper.cancelUnfinishedRuns', () => { expect(published.map((e) => e.type)).toEqual(['agent-completed', 'run-finish']); const completed = published[0]; expect(completed.type === 'agent-completed' && completed.agentId).toBe('child-orphaned'); - // Matches the live cancel path's wording for spawned agents. - expect(completed.type === 'agent-completed' && completed.payload.error).toBe( - 'Cancelled by user', - ); + expect(completed.type === 'agent-completed' && completed.payload).toEqual({ + role: 'agent-builder', + result: '', + status: 'cancelled', + }); }); it('is idempotent and scoped to the requested thread', async () => { diff --git a/packages/cli/src/modules/instance-ai/event-bus/interrupted-run-sweeper.ts b/packages/cli/src/modules/instance-ai/event-bus/interrupted-run-sweeper.ts index 6f67d654ce5..00108c166ac 100644 --- a/packages/cli/src/modules/instance-ai/event-bus/interrupted-run-sweeper.ts +++ b/packages/cli/src/modules/instance-ai/event-bus/interrupted-run-sweeper.ts @@ -263,8 +263,9 @@ export class InterruptedRunSweeper { payload: { role: agent.role, result: '', - // Matches the live cancel path's wording for spawned agents. - error: finish.status === 'cancelled' ? 'Cancelled by user' : AGENT_INTERRUPTED_MESSAGE, + ...(finish.status === 'cancelled' + ? { status: 'cancelled' as const } + : { error: AGENT_INTERRUPTED_MESSAGE }), }, }); } diff --git a/packages/cli/src/modules/instance-ai/instance-ai-credit.service.ts b/packages/cli/src/modules/instance-ai/instance-ai-credit.service.ts index e3856a76ea1..edc785ead1f 100644 --- a/packages/cli/src/modules/instance-ai/instance-ai-credit.service.ts +++ b/packages/cli/src/modules/instance-ai/instance-ai-credit.service.ts @@ -2,8 +2,9 @@ import { Logger } from '@n8n/backend-common'; import type { User } from '@n8n/db'; import { Service } from '@n8n/di'; import type { BuilderUsageItem, TraceStatus } from '@n8n/instance-ai'; +import { sleep } from '@n8n/utils/sleep'; import { InstanceSettings } from 'n8n-core'; -import { sleep, UnexpectedError } from 'n8n-workflow'; +import { UnexpectedError } from 'n8n-workflow'; import { nanoid } from 'nanoid'; import { Push } from '@/push'; diff --git a/packages/cli/src/modules/instance-ai/instance-ai-settings.service.ts b/packages/cli/src/modules/instance-ai/instance-ai-settings.service.ts index 20feeb9f500..61b9aac85d0 100644 --- a/packages/cli/src/modules/instance-ai/instance-ai-settings.service.ts +++ b/packages/cli/src/modules/instance-ai/instance-ai-settings.service.ts @@ -359,8 +359,7 @@ export class InstanceAiSettingsService { // ── Admin settings ──────────────────────────────────────────────────── async getAdminSettings(): Promise { - const isManaged = this.isCloud || this.aiService.isProxyEnabled(); - if (isManaged) { + if (this.isCloud) { return this.buildAdminSettingsResponse({ modelCredentialId: null, modelName: null, @@ -369,6 +368,18 @@ export class InstanceAiSettingsService { searchCredentialId: null, }); } + if (this.aiService.isProxyEnabled()) { + const n8nSandboxCredentialId = await this.instanceCredentialBroker.getAssignedCredentialId( + INSTANCE_AI_N8N_SANDBOX_CREDENTIAL_POLICY, + ); + return this.buildAdminSettingsResponse({ + modelCredentialId: null, + modelName: null, + daytonaCredentialId: null, + n8nSandboxCredentialId, + searchCredentialId: null, + }); + } const [modelSelection, daytonaCredentialId, n8nSandboxCredentialId, searchCredentialId] = await Promise.all([ @@ -394,17 +405,17 @@ export class InstanceAiSettingsService { ): InstanceAiAdminSettingsResponse { const c = this.config; const modelProviderApiKeyEnv = MODEL_PROVIDER_API_KEY_ENV.get(c.model.split('/', 1)[0] ?? ''); - const isManaged = this.isCloud || this.aiService.isProxyEnabled(); + const isProxyEnabled = this.aiService.isProxyEnabled(); + const isManaged = this.isCloud || isProxyEnabled; + const sandboxProvider = normalizeSandboxProvider(c.sandboxProvider); return { enabled: this.enabled, permissions: { ...this.permissions }, mcpAccessEnabled: this.mcpAccessEnabled, sandboxEnabled: c.sandboxEnabled, - sandboxProvider: this.aiService.isProxyEnabled() - ? 'daytona' - : normalizeSandboxProvider(c.sandboxProvider), + sandboxProvider, daytonaCredentialId: isManaged ? null : credentialSelection.daytonaCredentialId, - n8nSandboxCredentialId: isManaged ? null : credentialSelection.n8nSandboxCredentialId, + n8nSandboxCredentialId: this.isCloud ? null : credentialSelection.n8nSandboxCredentialId, searchCredentialId: isManaged ? null : credentialSelection.searchCredentialId, modelCredentialId: isManaged ? null : credentialSelection.modelCredentialId, modelName: isManaged ? null : credentialSelection.modelName, @@ -415,8 +426,8 @@ export class InstanceAiSettingsService { ), // n8n-sandbox needs only the URL; the service accepts keyless clients. sandboxEnvConfigured: - this.environmentSandboxProvider === 'daytona' - ? Boolean(c.daytonaApiKey.trim()) + sandboxProvider === 'daytona' + ? isProxyEnabled || Boolean(c.daytonaApiKey.trim()) : Boolean(c.n8nSandboxServiceUrl.trim()), searchEnvConfigured: Boolean(c.braveSearchApiKey.trim() || c.searxngUrl.trim()), localGatewayDisabled: this.isLocalGatewayDisabled(), @@ -433,13 +444,26 @@ export class InstanceAiSettingsService { InstanceAiSettingsService.MANAGED_ADMIN_FIELDS, this.deploymentLabel(), ); - if (this.isCloud || this.aiService.isProxyEnabled()) { + if (this.isCloud) { this.rejectManagedFields( update, InstanceAiSettingsService.INSTANCE_CREDENTIAL_FIELDS, this.deploymentLabel(), ); this.rejectManagedFields(update, ['modelName', 'sandboxProvider'], this.deploymentLabel()); + } else if (this.aiService.isProxyEnabled()) { + this.rejectManagedFields( + update, + ['modelCredentialId', 'searchCredentialId', 'modelConnection', 'searchConnection'], + this.deploymentLabel(), + ); + this.rejectManagedFields(update, ['modelName'], this.deploymentLabel()); + if (update.daytonaCredentialId !== null) { + this.rejectManagedFields(update, ['daytonaCredentialId'], this.deploymentLabel()); + } + if (update.sandboxConnection?.type === 'daytonaApi') { + this.rejectManagedFields(update, ['sandboxConnection'], this.deploymentLabel()); + } } const { modelCredentialId: initialModelCredentialId, @@ -976,12 +1000,17 @@ export class InstanceAiSettingsService { } async listInstanceServiceCredentials(): Promise { - if (this.isCloud || this.aiService.isProxyEnabled()) return []; - const credentials = await Promise.all([ - this.instanceCredentialBroker.listForUse(INSTANCE_AI_DAYTONA_CREDENTIAL_POLICY), - this.instanceCredentialBroker.listForUse(INSTANCE_AI_N8N_SANDBOX_CREDENTIAL_POLICY), - this.instanceCredentialBroker.listForUse(INSTANCE_AI_SEARCH_CREDENTIAL_POLICY), - ]); + if (this.isCloud) return []; + const policies = this.aiService.isProxyEnabled() + ? [INSTANCE_AI_N8N_SANDBOX_CREDENTIAL_POLICY] + : [ + INSTANCE_AI_DAYTONA_CREDENTIAL_POLICY, + INSTANCE_AI_N8N_SANDBOX_CREDENTIAL_POLICY, + INSTANCE_AI_SEARCH_CREDENTIAL_POLICY, + ]; + const credentials = await Promise.all( + policies.map(async (policy) => await this.instanceCredentialBroker.listForUse(policy)), + ); return credentials.flat().map((c) => ({ id: c.id, name: c.name, @@ -1088,7 +1117,12 @@ export class InstanceAiSettingsService { service: string, ctx?: OperationContext, ): Promise { - if (this.isCloud || this.aiService.isProxyEnabled()) return null; + if ( + this.isCloud || + (this.aiService.isProxyEnabled() && + policy.id !== INSTANCE_AI_N8N_SANDBOX_CREDENTIAL_POLICY.id) + ) + return null; const resolved = ctx ? this.instanceCredentialBroker.resolveForUse(policy, ctx) : this.instanceCredentialBroker.resolveForUse(policy); @@ -1142,9 +1176,7 @@ export class InstanceAiSettingsService { /** Whether workflow building can use the required sandbox workspace. */ getSandboxStatus(): InstanceAiSandboxStatus { - const provider = this.aiService.isProxyEnabled() - ? 'daytona' - : normalizeSandboxProvider(this.config.sandboxProvider); + const provider = normalizeSandboxProvider(this.config.sandboxProvider); const unavailableReason = this.getSandboxUnavailableReason( this.config.sandboxEnabled, provider, @@ -1387,14 +1419,11 @@ export class InstanceAiSettingsService { if (persisted.mcpAccessEnabled !== undefined) this.mcpAccessEnabled = persisted.mcpAccessEnabled; if (persisted.sandboxEnabled !== undefined) c.sandboxEnabled = persisted.sandboxEnabled; - const isProxyEnabled = this.aiService.isProxyEnabled(); this.sandboxProviderOverride = - this.isCloud || isProxyEnabled || !persisted.sandboxProvider + this.isCloud || !persisted.sandboxProvider ? undefined : normalizeSandboxProvider(persisted.sandboxProvider); - c.sandboxProvider = isProxyEnabled - ? 'daytona' - : (this.sandboxProviderOverride ?? this.environmentSandboxProvider); + c.sandboxProvider = this.sandboxProviderOverride ?? this.environmentSandboxProvider; if (persisted.sandboxImage !== undefined) c.sandboxImage = persisted.sandboxImage; if (persisted.sandboxTimeout !== undefined) c.sandboxTimeout = persisted.sandboxTimeout; if (persisted.modelName !== undefined) this.adminModelName = persisted.modelName; diff --git a/packages/cli/src/modules/instance-ai/instance-ai.adapter.service.ts b/packages/cli/src/modules/instance-ai/instance-ai.adapter.service.ts index 0b520ea06e2..d75b12bc696 100644 --- a/packages/cli/src/modules/instance-ai/instance-ai.adapter.service.ts +++ b/packages/cli/src/modules/instance-ai/instance-ai.adapter.service.ts @@ -745,9 +745,15 @@ export class InstanceAiAdapterService { assertNotReadOnly(); const projectId = await resolveBoundProjectId(['workflow:create']); + // Without an explicit order the engine falls back to legacy v0, which walks + // the graph breadth-first. Generated code still wins if it sets its own. + const settings = { + executionOrder: 'v1', + ...(json.settings ?? {}), + } as IWorkflowSettings; + // Strip redactionPolicy if the user lacks the required scope — // mirrors the check in WorkflowCreationService.createWorkflow(). - const settings = (json.settings ?? {}) as IWorkflowSettings; if (settings.redactionPolicy !== undefined && settings.redactionPolicy !== 'none') { const canUpdateRedaction = await userHasScopes( user, @@ -1138,6 +1144,12 @@ export class InstanceAiAdapterService { const timeoutMs = Math.min(options?.timeout ?? DEFAULT_TIMEOUT_MS, MAX_TIMEOUT_MS); + // Sever the listed edges on this run's ephemeral copy only — used by + // scripted wait-gate verification to keep each pass acyclic. + const connections = options?.omitConnections?.length + ? omitWorkflowConnections(workflow.connections, options.omitConnections) + : workflow.connections; + // Force-save AI-initiated executions so that follow-up // `executions(list/get/debug)` calls can read the result, regardless of // instance-wide or per-workflow save settings. Manual mode is gated by @@ -1149,6 +1161,7 @@ export class InstanceAiAdapterService { : ('manual' as WorkflowExecuteMode), workflowData: { ...workflow, + connections, settings: { ...workflow.settings, saveManualExecutions: true, @@ -1161,6 +1174,12 @@ export class InstanceAiAdapterService { }, userId: user.id, pushRef, + // A verification run picks a production execution mode above so the + // trigger behaves realistically. Without this opt-out, a failed build + // attempt would dispatch the user's error workflow as if production + // had broken — the one side effect node simulation cannot pin, since + // it is dispatched by the execution lifecycle, not by a node. + ...(options?.isVerificationRun ? { suppressErrorWorkflow: true } : {}), }; const pinDataPlan = buildInstanceAiRunPinDataPlan({ @@ -1193,12 +1212,16 @@ export class InstanceAiAdapterService { // In queue mode the worker rebuilds the run from persisted `execution.data`, // where top-level `runData.source` doesn't survive. Persist it in - // `manualData` so worker-side statistics can classify IAI runs. + // `manualData` so worker-side statistics can classify IAI runs, and so + // the worker's save hook honours `suppressErrorWorkflow`. A run without + // `executionData` has no trigger node, which means `executionMode` is + // 'manual' and the error workflow is already skipped. if (runData.executionData) { runData.executionData.manualData = { ...runData.executionData.manualData, userId: user.id, source: runData.source, + suppressErrorWorkflow: runData.suppressErrorWorkflow, }; } @@ -3461,6 +3484,25 @@ function findTriggerNode(nodes: INode[]): INode | undefined { return nodes.find((n) => isTriggerNodeType(n.type)); } +/** Copy of `connections` minus every listed source→target edge (any type). */ +function omitWorkflowConnections( + connections: IConnections, + omit: Array<{ source: string; target: string }>, +): IConnections { + const omitted = new Set(omit.map((edge) => `${edge.source}→${edge.target}`)); + const result: IConnections = {}; + for (const [source, byType] of Object.entries(connections)) { + const nextByType: IConnections[string] = {}; + for (const [type, groups] of Object.entries(byType)) { + nextByType[type] = (groups ?? []).map((group) => + group ? group.filter((connection) => !omitted.has(`${source}→${connection.node}`)) : group, + ); + } + result[source] = nextByType; + } + return result; +} + /** Get the execution mode based on the trigger node type. */ function getExecutionModeForTrigger(node: INode): WorkflowExecuteMode { switch (node.type) { diff --git a/packages/cli/src/modules/instance-ai/instance-ai.service.ts b/packages/cli/src/modules/instance-ai/instance-ai.service.ts index d5adb64d871..0f49d5ad58c 100644 --- a/packages/cli/src/modules/instance-ai/instance-ai.service.ts +++ b/packages/cli/src/modules/instance-ai/instance-ai.service.ts @@ -964,6 +964,7 @@ export class InstanceAiService { runId: string, agentRunId: string, toolCallId: string, + signal: AbortSignal, ): Record { if (this.isRunDebugEnabled()) { this.runDebugBuffer.ensure(runId, threadId); @@ -971,6 +972,7 @@ export class InstanceAiService { return { runId: agentRunId, toolCallId, + abortSignal: signal, // Keep billing stopped/errored resumed runs (see stream-options builder). recoverUsageOnAbort: true, persistence: { resourceId: user.id, threadId, hostRunId: runId }, @@ -1096,7 +1098,7 @@ export class InstanceAiService { payload: { role: task.role, result: '', - error: reason === INSTANCE_AI_RUN_TIMEOUT_REASON ? 'Timed out' : 'Cancelled by user', + status: 'cancelled', }, }); void this.terminalOutcome.recordBackgroundTerminalOutcome(task).finally(() => { @@ -1109,7 +1111,7 @@ export class InstanceAiService { ); }); if (user) { - void this.handlePlannedTaskSettlement(user, task, 'cancelled'); + void this.handlePlannedTaskSettlement(user, task, 'cancelled', { reschedule: false }); } } @@ -1160,7 +1162,7 @@ export class InstanceAiService { type: 'agent-completed', runId: task.runId, agentId: task.agentId, - payload: { role: task.role, result: '', error: 'Cancelled by user' }, + payload: { role: task.role, result: '', status: 'cancelled' }, }); // Persist the updated agent tree so cancelled status survives page reload. @@ -2595,6 +2597,7 @@ export class InstanceAiService { user: User, task: ManagedBackgroundTask, status: 'succeeded' | 'failed' | 'cancelled', + { reschedule = true }: { reschedule?: boolean } = {}, ): Promise { if (!task.plannedTaskId) return; @@ -2620,6 +2623,10 @@ export class InstanceAiService { await this.syncPlannedTasksToUi(task.threadId, graph); } + // Callers that tore the whole thread down (user stop, run timeout, + // shutdown) opt out: the scheduler must not start a follow-up run. The + // graph stays intact so the next user message can pick it back up. + if (!reschedule) return; await this.schedulePlannedTasks(user, task.threadId); } @@ -4043,24 +4050,36 @@ export class InstanceAiService { // 1. Checkpoint deadlock fallback — if this run was a checkpoint // follow-up and the orchestrator exited without calling // complete-checkpoint, mark the task failed so the scheduler - // can transition to awaiting_replan. - // 2. Unconditional reschedule — drive the next tick. This covers - // the case where a background task settled during an ordinary - // chat run: its schedulePlannedTasks call may have skipped the - // checkpoint branch because hasLiveRun was true. Ticking again - // now (with no live run) picks it up. schedulerLocks serializes - // this call, and tick() is a no-op when no graph exists. + // can transition to awaiting_replan. Runs even on a stop: the + // cancelled run's context is the only thing that knows about + // this follow-up, so skipping it strands the task at `running`. + // 2. Reschedule — drive the next tick, unless the run was stopped + // (a stop must not start another run). This covers the case + // where a background task settled during an ordinary chat run: + // its schedulePlannedTasks call may have skipped the checkpoint + // branch because hasLiveRun was true. Ticking again now (with + // no live run) picks it up. schedulerLocks serializes this + // call, and tick() is a no-op when no graph exists. + // 3. UI projection — always, so a stopped run's task states reach + // the client. if (!this.runState.hasSuspendedRun(threadId)) { + const reschedule = !signal.aborted; if (checkpoint?.isCheckpointFollowUp) { - await this.finalizeCheckpointFollowUp(user, threadId, checkpoint.checkpointTaskId); + await this.finalizeCheckpointFollowUp(user, threadId, checkpoint.checkpointTaskId, { + reschedule, + }); } else if (plannedBuild?.isPlannedBuildFollowUp) { - await this.finalizePlannedBuildFollowUp(user, threadId, plannedBuild); - } else { + await this.finalizePlannedBuildFollowUp(user, threadId, plannedBuild, { reschedule }); + } else if (reschedule) { await this.schedulePlannedTasks(user, threadId); } - await this.drainPendingCheckpointReentries(user, threadId); + if (reschedule) { + await this.drainPendingCheckpointReentries(user, threadId); + } await this.taskProjector.syncFromWorkflowLoop(threadId, runId); - await this.maybeStartWorkflowSetupFollowUp(user, threadId); + if (reschedule) { + await this.maybeStartWorkflowSetupFollowUp(user, threadId); + } } this.instanceAiErrorReporter.endRun(runId); } @@ -4201,6 +4220,7 @@ export class InstanceAiService { user: User, threadId: string, checkpointTaskId: string, + { reschedule = true }: { reschedule?: boolean } = {}, ): Promise { try { const { plannedTaskService } = await this.createPlannedTaskState(); @@ -4248,6 +4268,7 @@ export class InstanceAiService { }); } + if (!reschedule) return; await this.schedulePlannedTasks(user, threadId); } @@ -4255,6 +4276,7 @@ export class InstanceAiService { user: User, threadId: string, plannedBuild: PlannedBuildFollowUp, + { reschedule = true }: { reschedule?: boolean } = {}, ): Promise { try { const { plannedTaskService } = await this.createPlannedTaskState(); @@ -4288,6 +4310,7 @@ export class InstanceAiService { }); } + if (!reschedule) return; await this.schedulePlannedTasks(user, threadId); } @@ -4858,6 +4881,7 @@ export class InstanceAiService { opts.runId, opts.agentRunId, opts.toolCallId, + opts.signal, ); const runControl = createOrchestratorRunControlForState(opts.runHandoff); const stopSignal = (): OrchestratorRunStopSignal | undefined => runControl.getStopSignal(); @@ -5258,23 +5282,33 @@ export class InstanceAiService { // a background task settled while they were active or suspended and // the orchestrate-checkpoint branch was skipped because of hasLiveRun. if (!this.runState.hasSuspendedRun(opts.threadId)) { + const reschedule = !opts.signal.aborted; if (opts.checkpoint?.isCheckpointFollowUp) { await this.finalizeCheckpointFollowUp( opts.user, opts.threadId, opts.checkpoint.checkpointTaskId, + { reschedule }, ); } else if (opts.plannedBuild?.isPlannedBuildFollowUp) { - await this.finalizePlannedBuildFollowUp(opts.user, opts.threadId, opts.plannedBuild); - } else { + await this.finalizePlannedBuildFollowUp(opts.user, opts.threadId, opts.plannedBuild, { + reschedule, + }); + } else if (reschedule) { await this.schedulePlannedTasks(opts.user, opts.threadId); } - await this.drainPendingCheckpointReentries(opts.user, opts.threadId); + if (reschedule) { + await this.drainPendingCheckpointReentries(opts.user, opts.threadId); + } + // The setup claim must land even on a stop: it is what stops a later + // run from re-routing setup for a workflow the user already set up. if (completedSetupWorkflowId) { await this.markWorkflowSetupHandled(opts.threadId, completedSetupWorkflowId, opts.runId); } await this.taskProjector.syncFromWorkflowLoop(opts.threadId, opts.runId); - await this.maybeStartWorkflowSetupFollowUp(opts.user, opts.threadId); + if (reschedule) { + await this.maybeStartWorkflowSetupFollowUp(opts.user, opts.threadId); + } } this.instanceAiErrorReporter.endRun(opts.runId); } diff --git a/packages/cli/src/modules/instance-ai/node-definition-resolver.ts b/packages/cli/src/modules/instance-ai/node-definition-resolver.ts index 6bb4c3b3128..539b9f93480 100644 --- a/packages/cli/src/modules/instance-ai/node-definition-resolver.ts +++ b/packages/cli/src/modules/instance-ai/node-definition-resolver.ts @@ -6,7 +6,12 @@ * pure functions without LangChain dependencies. */ -import { parseNodeId, toSnakeCase, isValidPathComponent } from '@n8n/ai-utilities/node-catalog'; +import { + parseNodeId, + toSnakeCase, + isValidPathComponent, + versionDirToNumber, +} from '@n8n/ai-utilities/node-catalog'; import { safeJoinPath } from '@n8n/backend-common'; import { BUILTIN_NODES_PACKAGES } from '@n8n/constants'; import { readFileSync, existsSync, readdirSync, statSync } from 'node:fs'; @@ -69,11 +74,8 @@ function getNodeVersions(nodeId: string, nodeDefinitionDirs: string[]): string[] } } - versions.sort((a, b) => { - const aNum = parseInt(a.slice(1), 10); - const bNum = parseInt(b.slice(1), 10); - return bNum - aNum; - }); + // Sort by numeric version descending (v22 is 2.2, not 22, so it ranks below v3) + versions.sort((a, b) => versionDirToNumber(b) - versionDirToNumber(a)); return versions; } catch { diff --git a/packages/cli/src/modules/instance-ai/sandbox/instance-ai-sandbox.service.ts b/packages/cli/src/modules/instance-ai/sandbox/instance-ai-sandbox.service.ts index 048a2947fd2..0159914e4c5 100644 --- a/packages/cli/src/modules/instance-ai/sandbox/instance-ai-sandbox.service.ts +++ b/packages/cli/src/modules/instance-ai/sandbox/instance-ai-sandbox.service.ts @@ -198,9 +198,7 @@ export class InstanceAiSandboxService { sandboxAutoDeleteMinutes, daytonaTokenRefreshSkewMs, } = this.instanceAiConfig; - const provider = this.options.aiService.isProxyEnabled() - ? 'daytona' - : normalizeSandboxProvider(sandboxProvider); + const provider = normalizeSandboxProvider(sandboxProvider); if (!sandboxEnabled) { return { enabled: false, diff --git a/packages/cli/src/modules/mcp-registry/registry/mcp-registry-search.ts b/packages/cli/src/modules/mcp-registry/registry/mcp-registry-search.ts index b3e971ee90f..4db5824f458 100644 --- a/packages/cli/src/modules/mcp-registry/registry/mcp-registry-search.ts +++ b/packages/cli/src/modules/mcp-registry/registry/mcp-registry-search.ts @@ -56,6 +56,14 @@ function toSearchResult(server: McpRegistryServer): McpRegistrySearchResult | nu }; } +/** Map registry servers to the config-ready shape, skipping entries without a usable remote. */ +export function listMcpRegistryServers(servers: McpRegistryServer[]): McpRegistrySearchResult[] { + return servers.flatMap((server) => { + const result = toSearchResult(server); + return result ? [result] : []; + }); +} + function normalizeQueries(queries: string[]): string[] { return queries.map((query) => query.trim().toLowerCase()).filter((query) => query.length > 0); } @@ -80,9 +88,5 @@ export function searchMcpRegistryServers( ): McpRegistrySearchResult[] { const normalized = normalizeQueries(queries); if (normalized.length === 0) return []; - return servers.flatMap((server) => { - if (!matchesQuery(server, normalized)) return []; - const result = toSearchResult(server); - return result ? [result] : []; - }); + return listMcpRegistryServers(servers.filter((server) => matchesQuery(server, normalized))); } diff --git a/packages/cli/src/modules/mcp-registry/registry/mcp-registry.service.ts b/packages/cli/src/modules/mcp-registry/registry/mcp-registry.service.ts index 067ab9793f1..2cb52d9dd85 100644 --- a/packages/cli/src/modules/mcp-registry/registry/mcp-registry.service.ts +++ b/packages/cli/src/modules/mcp-registry/registry/mcp-registry.service.ts @@ -13,7 +13,11 @@ import { McpRegistryServerRepository } from './mcp-registry-server.repository'; import { McpRegistryNodeLoader } from '../mcp-registry-node-loader'; import type { McpRegistryServerMetadata } from './mcp-registry-api.client'; import { McpRegistryApiClient } from './mcp-registry-api.client'; -import { searchMcpRegistryServers, type McpRegistrySearchResult } from './mcp-registry-search'; +import { + listMcpRegistryServers, + searchMcpRegistryServers, + type McpRegistrySearchResult, +} from './mcp-registry-search'; import type { McpRegistryServer } from './mcp-registry.types'; import { toEntity, fromEntity } from './mcp-registry.types'; import { MCP_REGISTRY_PACKAGE_NAME } from '../node-description-transform'; @@ -114,6 +118,10 @@ export class McpRegistryService { return searchMcpRegistryServers(await this.getAll(), queries); } + async list(limit: number): Promise { + return listMcpRegistryServers(await this.getAll()).slice(0, limit); + } + private startPeriodicRefresh(): void { if (this.isShuttingDown || this.refreshInterval) { return; diff --git a/packages/cli/src/modules/mcp/__tests__/agent-tools.service.test.ts b/packages/cli/src/modules/mcp/__tests__/agent-tools.service.test.ts new file mode 100644 index 00000000000..423b827edf2 --- /dev/null +++ b/packages/cli/src/modules/mcp/__tests__/agent-tools.service.test.ts @@ -0,0 +1,1504 @@ +import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; +import type { AgentJsonConfig } from '@n8n/api-types'; +import { mockInstance } from '@n8n/backend-test-utils'; +import { OutboundHttp, SsrfProtectionService } from '@n8n/backend-network'; +import { SsrfProtectionConfig } from '@n8n/config'; +import { User } from '@n8n/db'; +import type { Mock } from 'vitest'; + +vi.mock('@/permissions.ee/check-access', () => ({ + userHasScopes: vi.fn(), +})); + +vi.mock('@n8n/agents', () => ({ + fetchProviderCatalog: vi.fn().mockResolvedValue({ + openai: { id: 'openai', name: 'OpenAI', models: { 'gpt-a': {}, 'gpt-b': {} } }, + 'not-offered': { id: 'not-offered', name: 'Not Offered', models: { x: {} } }, + }), +})); + +const { listMcpServerToolsMock } = vi.hoisted(() => ({ listMcpServerToolsMock: vi.fn() })); +vi.mock('@/modules/agents/json-config/mcp-client-factory', () => ({ + listMcpServerTools: listMcpServerToolsMock, +})); + +import { CredentialsService } from '@/credentials/credentials.service'; +import { AgentConfigService } from '@/modules/agents/agent-config.service'; +import { AgentCustomToolsService } from '@/modules/agents/agent-custom-tools.service'; +import { AgentIntegrationPersistenceService } from '@/modules/agents/agent-integration-persistence.service'; +import { AgentModelCatalogService } from '@/modules/agents/agent-model-catalog.service'; +import { AgentPublishService } from '@/modules/agents/agent-publish.service'; +import { AgentSkillsService } from '@/modules/agents/agent-skills.service'; +import { AgentTaskService } from '@/modules/agents/agent-task.service'; +import { AgentValidationService } from '@/modules/agents/agent-validation.service'; +import { AgentsService } from '@/modules/agents/agents.service'; +import { AttachableWorkflowsService } from '@/modules/agents/attachable-workflows.service'; +import type { Agent } from '@/modules/agents/entities/agent.entity'; +import { ChatIntegrationRegistry } from '@/modules/agents/integrations/agent-chat-integration'; +import { ChatIntegrationService } from '@/modules/agents/integrations/chat-integration.service'; +import { AgentSecureRuntime } from '@/modules/agents/runtime/agent-secure-runtime'; +import { getAgentConfigHash } from '@/modules/agents/utils/agent-config-hash'; +import { McpRegistryService } from '@/modules/mcp-registry/registry/mcp-registry.service'; +import { NodeTypes } from '@/node-types'; +import { OauthService } from '@/oauth/oauth.service'; +import { userHasScopes } from '@/permissions.ee/check-access'; +import { ProjectScopeService } from '@/permissions.ee/project-scope.service'; +import { UrlService } from '@/services/url.service'; +import { Telemetry } from '@/telemetry'; + +import { AGENT_TOOLS, TOOLS_BY_SCOPE } from '../mcp-scopes'; +import { USER_CALLED_MCP_TOOL_EVENT } from '../mcp.constants'; +import { McpAgentToolsService } from '../tools/agents/agent-tools.service'; + +const userHasScopesMock = userHasScopes as Mock; + +type ToolResult = { + content: Array<{ type: string; text: string }>; + structuredContent: Record; + isError?: boolean; +}; + +type RegisteredTool = { + config: { description?: string; annotations?: Record }; + handler: (input: Record) => Promise; +}; + +const user = Object.assign(new User(), { id: 'user-1' }); + +const baseConfig: AgentJsonConfig = { + name: 'My Agent', + model: 'anthropic/claude', + instructions: 'Help out.', + tools: [], + skills: [], +}; + +// What configFromEntity(agentEntity()) composes: schema + integrations. +const composedConfig: AgentJsonConfig = { ...baseConfig, integrations: [] }; + +const agentEntity = (overrides: Record = {}): Agent => + ({ + id: 'agent-1', + name: 'My Agent', + projectId: 'project-1', + versionId: 'v1', + activeVersionId: null, + availableInMCP: true, + createdAt: new Date('2026-01-01T00:00:00.000Z'), + updatedAt: new Date('2026-01-02T00:00:00.000Z'), + schema: baseConfig, + integrations: [], + ...overrides, + }) as unknown as Agent; + +describe('McpAgentToolsService', () => { + const telemetry = mockInstance(Telemetry); + const credentialsService = mockInstance(CredentialsService); + const agentsService = mockInstance(AgentsService); + const agentConfigService = mockInstance(AgentConfigService); + const agentValidationService = mockInstance(AgentValidationService); + const agentPublishService = mockInstance(AgentPublishService); + const agentSkillsService = mockInstance(AgentSkillsService); + const agentTaskService = mockInstance(AgentTaskService); + const agentCustomToolsService = mockInstance(AgentCustomToolsService); + const agentSecureRuntime = mockInstance(AgentSecureRuntime); + const integrationPersistenceService = mockInstance(AgentIntegrationPersistenceService); + const chatIntegrationService = mockInstance(ChatIntegrationService); + const chatIntegrationRegistry = mockInstance(ChatIntegrationRegistry); + const mcpRegistryService = mockInstance(McpRegistryService); + const outboundHttp = mockInstance(OutboundHttp); + const urlService = mockInstance(UrlService); + const projectScopeService = mockInstance(ProjectScopeService); + + const service = new McpAgentToolsService( + telemetry, + credentialsService, + agentsService, + agentConfigService, + agentValidationService, + agentPublishService, + agentSkillsService, + agentTaskService, + agentCustomToolsService, + agentSecureRuntime, + integrationPersistenceService, + chatIntegrationService, + chatIntegrationRegistry, + mockInstance(AgentModelCatalogService), + mockInstance(AttachableWorkflowsService), + mcpRegistryService, + mockInstance(NodeTypes), + mockInstance(OauthService), + outboundHttp, + mockInstance(SsrfProtectionConfig), + mockInstance(SsrfProtectionService), + urlService, + projectScopeService, + ); + + let tools: Map; + let registerResource: Mock; + + beforeEach(() => { + vi.clearAllMocks(); + userHasScopesMock.mockResolvedValue(true); + agentsService.findByIdForUser.mockResolvedValue(agentEntity()); + credentialsService.getCredentialsAUserCanUseInAWorkflow.mockResolvedValue([] as never); + urlService.getInstanceBaseUrl.mockReturnValue('https://n8n.test'); + projectScopeService.getProjectIds.mockResolvedValue(['project-1']); + + tools = new Map(); + registerResource = vi.fn(); + const server = { + registerTool: (name: string, config: RegisteredTool['config'], handler: unknown) => { + tools.set(name, { config, handler: handler as RegisteredTool['handler'] }); + }, + resource: registerResource, + } as unknown as McpServer; + service.registerTools(server, user); + }); + + const callTool = async (name: string, input: Record): Promise => { + const tool = tools.get(name); + if (!tool) throw new Error(`Tool "${name}" is not registered`); + return await tool.handler(input); + }; + + describe('registerTools', () => { + it('registers all agent tools and the reference resource', () => { + expect([...tools.keys()].sort()).toEqual( + [ + 'create_agent', + 'delete_agent', + 'discover_agent_assets', + 'get_agent', + 'get_agent_builder_reference', + 'list_agent_versions', + 'mutate_agent', + 'publish_agent', + 'revert_agent', + 'search_agents', + 'unpublish_agent', + 'update_agent_integration', + 'validate_agent', + 'verify_agent_mcp_server', + ].sort(), + ); + expect(registerResource).toHaveBeenCalledWith( + 'agent-builder-reference', + 'n8n://agents/reference', + expect.any(Object), + expect.any(Function), + ); + }); + + it('matches AGENT_TOOLS in the scope map (drift guard)', () => { + expect(new Set(tools.keys())).toEqual(new Set(AGENT_TOOLS)); + }); + + const registerFiltered = (allowedToolNames?: Set) => { + const filteredTools = new Map(); + const resource = vi.fn(); + const server = { + registerTool: (name: string, config: RegisteredTool['config'], handler: unknown) => { + filteredTools.set(name, { config, handler: handler as RegisteredTool['handler'] }); + }, + resource, + } as unknown as McpServer; + service.registerTools(server, user, allowedToolNames); + return { tools: filteredTools, resource }; + }; + + it('registers only the tools allowed by the granted scopes', () => { + const allowed = new Set(TOOLS_BY_SCOPE['agent:read']); + const { tools: filteredTools, resource } = registerFiltered(allowed); + + expect(new Set(filteredTools.keys())).toEqual(allowed); + expect(resource).toHaveBeenCalledTimes(1); + }); + + it('registers no tools and no resource for an empty allow-list', () => { + const { tools: filteredTools, resource } = registerFiltered(new Set()); + + expect(filteredTools.size).toBe(0); + expect(resource).not.toHaveBeenCalled(); + }); + + it('skips the reference resource when the reference tool is out of scope', () => { + const { tools: filteredTools, resource } = registerFiltered(new Set(['search_agents'])); + + expect([...filteredTools.keys()]).toEqual(['search_agents']); + expect(resource).not.toHaveBeenCalled(); + }); + }); + + describe('scope enforcement', () => { + const identity = { agentId: 'agent-1' }; + + test.each<[string, string, Record]>([ + ['search_agents', 'agent:list', { projectId: 'project-1' }], + ['get_agent', 'agent:read', identity], + ['create_agent', 'agent:create', { projectId: 'project-1', name: 'My Agent' }], + [ + 'mutate_agent', + 'agent:update', + { + ...identity, + baseConfigHash: 'hash', + operation: { type: 'config.replace', config: {} }, + }, + ], + ['validate_agent', 'agent:read', identity], + ['publish_agent', 'agent:publish', identity], + ['unpublish_agent', 'agent:unpublish', identity], + ['revert_agent', 'agent:update', identity], + ['list_agent_versions', 'agent:read', identity], + ['delete_agent', 'agent:delete', identity], + ['discover_agent_assets', 'agent:read', { projectId: 'project-1', kind: 'models' }], + [ + 'verify_agent_mcp_server', + 'agent:read', + { + projectId: 'project-1', + name: 'srv', + url: 'https://mcp.test', + transport: 'streamableHttp', + authentication: 'none', + }, + ], + [ + 'update_agent_integration', + 'agent:update', + { ...identity, action: 'connect', type: 'slack', credentialId: 'cred-1' }, + ], + ])('%s denies access without the %s scope', async (toolName, scope, input) => { + userHasScopesMock.mockResolvedValue(false); + + const result = await callTool(toolName, input); + + expect(userHasScopesMock).toHaveBeenCalledWith(user, [scope], false, { + projectId: 'project-1', + }); + expect(result.isError).toBe(true); + expect(result.structuredContent).toEqual({ + ok: false, + code: 'agent_tool_error', + error: 'You do not have permission to access agents in this project.', + }); + }); + }); + + describe('availableInMCP enforcement', () => { + const identity = { projectId: 'project-1', agentId: 'agent-1' }; + + test.each<[string, Record]>([ + ['get_agent', identity], + [ + 'mutate_agent', + { ...identity, baseConfigHash: 'hash', operation: { type: 'config.replace', config: {} } }, + ], + ['validate_agent', identity], + ['publish_agent', identity], + ['unpublish_agent', identity], + ['revert_agent', identity], + ['list_agent_versions', identity], + ['delete_agent', identity], + [ + 'update_agent_integration', + { ...identity, action: 'disconnect', type: 'slack', credentialId: 'cred-1' }, + ], + ])('%s refuses an agent that is not available in MCP', async (toolName, input) => { + agentsService.findByIdForUser.mockResolvedValue(agentEntity({ availableInMCP: false })); + + const result = await callTool(toolName, input); + + expect(result.isError).toBe(true); + expect(result.structuredContent).toMatchObject({ + error: expect.stringContaining('not available in MCP'), + }); + }); + }); + + describe('mutate_agent', () => { + const mutateInput = (operation: Record, baseConfigHash?: string) => ({ + projectId: 'project-1', + agentId: 'agent-1', + baseConfigHash: baseConfigHash ?? getAgentConfigHash(composedConfig), + operation, + }); + + it('rejects a stale baseConfigHash without applying the mutation', async () => { + const result = await callTool( + 'mutate_agent', + mutateInput({ type: 'config.replace', config: { name: 'B' } }, 'stale-hash'), + ); + + expect(result.isError).toBe(true); + expect(result.structuredContent).toEqual({ + ok: false, + code: 'stale_config', + agentId: 'agent-1', + configHash: getAgentConfigHash(composedConfig), + message: 'Call get_agent before retrying the mutation.', + }); + expect(agentConfigService.updateConfig).not.toHaveBeenCalled(); + expect(telemetry.track).toHaveBeenCalledWith( + USER_CALLED_MCP_TOOL_EVENT, + expect.objectContaining({ + user_id: 'user-1', + tool_name: 'mutate_agent', + results: { success: false }, + }), + ); + }); + + it('rejects a patch with entries the config sanitizer would silently drop', async () => { + const result = await callTool( + 'mutate_agent', + mutateInput({ + type: 'config.patch', + patch: [{ op: 'add', path: '/tools/-', value: { type: 'subagent', agentId: 'a2' } }], + }), + ); + + expect(result.isError).toBe(true); + expect(result.structuredContent.error).toContain('subAgents.agents'); + expect(agentConfigService.updateConfig).not.toHaveBeenCalled(); + }); + + it('rejects a config.replace with entries the config sanitizer would silently drop', async () => { + const result = await callTool( + 'mutate_agent', + mutateInput({ + type: 'config.replace', + config: { ...baseConfig, tools: [{ type: 'subagent', agentId: 'a2' }] }, + }), + ); + + expect(result.isError).toBe(true); + expect(result.structuredContent.error).toContain('in: tools'); + expect(agentConfigService.updateConfig).not.toHaveBeenCalled(); + }); + + it('applies config.replace and returns the hash of the stored config', async () => { + const stored = { ...baseConfig, name: 'Renamed' }; + agentConfigService.updateConfig.mockResolvedValue({ + config: stored, + updatedAt: 'now', + versionId: 'v2', + }); + + const result = await callTool( + 'mutate_agent', + mutateInput({ type: 'config.replace', config: { name: 'Renamed' } }), + ); + + expect(agentConfigService.updateConfig).toHaveBeenCalledWith( + 'agent-1', + 'project-1', + { name: 'Renamed' }, + user, + { clearOmittedOptionalFields: true }, + ); + // The resolved entity's config is reused; the response hash comes + // from updateConfig's return value, not a re-fetch. + expect(agentConfigService.getConfig).not.toHaveBeenCalled(); + expect(result.structuredContent).toEqual({ + ok: true, + agentId: 'agent-1', + operation: 'config.replace', + configHash: getAgentConfigHash(stored), + }); + expect(telemetry.track).toHaveBeenCalledWith( + USER_CALLED_MCP_TOOL_EVENT, + expect.objectContaining({ tool_name: 'mutate_agent', results: { success: true } }), + ); + }); + + it('applies a valid config.patch to the current config', async () => { + agentConfigService.updateConfig.mockResolvedValue({ + config: { ...baseConfig, name: 'Patched' }, + updatedAt: 'now', + versionId: 'v2', + }); + + const result = await callTool( + 'mutate_agent', + mutateInput({ + type: 'config.patch', + patch: [{ op: 'replace', path: '/name', value: 'Patched' }], + }), + ); + + expect(agentConfigService.updateConfig).toHaveBeenCalledWith( + 'agent-1', + 'project-1', + { ...composedConfig, name: 'Patched' }, + user, + { clearOmittedOptionalFields: true }, + ); + expect(result.structuredContent).toMatchObject({ ok: true, operation: 'config.patch' }); + }); + + it('rejects an invalid config.patch without writing', async () => { + const result = await callTool( + 'mutate_agent', + mutateInput({ + type: 'config.patch', + patch: [{ op: 'replace', path: '/missing/deep', value: 1 }], + }), + ); + + expect(result.isError).toBe(true); + expect(agentConfigService.updateConfig).not.toHaveBeenCalled(); + }); + + it('rejects a config.replace that carries integrations', async () => { + const result = await callTool( + 'mutate_agent', + mutateInput({ + type: 'config.replace', + config: { ...baseConfig, integrations: [{ type: 'telegram', credentialId: '' }] }, + }), + ); + + expect(result.isError).toBe(true); + expect(result.structuredContent).toMatchObject({ + error: expect.stringContaining('update_agent_integration'), + }); + expect(agentConfigService.updateConfig).not.toHaveBeenCalled(); + }); + + it('rejects a config.patch that targets integrations', async () => { + const result = await callTool( + 'mutate_agent', + mutateInput({ + type: 'config.patch', + patch: [ + { op: 'add', path: '/integrations/-', value: { type: 'telegram', credentialId: '' } }, + ], + }), + ); + + expect(result.isError).toBe(true); + expect(result.structuredContent).toMatchObject({ + error: expect.stringContaining('update_agent_integration'), + }); + expect(agentConfigService.updateConfig).not.toHaveBeenCalled(); + }); + + it('rejects a config.patch that injects integrations via a whole-document replace', async () => { + const result = await callTool( + 'mutate_agent', + mutateInput({ + type: 'config.patch', + patch: [ + { + op: 'replace', + path: '', + value: { ...baseConfig, integrations: [{ type: 'telegram', credentialId: '' }] }, + }, + ], + }), + ); + + expect(result.isError).toBe(true); + expect(result.structuredContent).toMatchObject({ + error: expect.stringContaining('update_agent_integration'), + }); + expect(agentConfigService.updateConfig).not.toHaveBeenCalled(); + }); + + it('creates and attaches a skill when no skillId is given', async () => { + agentSkillsService.createAndAttachSkill.mockResolvedValue({ id: 'skill-1' } as never); + + const result = await callTool( + 'mutate_agent', + mutateInput({ type: 'skill.upsert', skill: { name: 'Skill', body: 'do it' } }), + ); + + expect(agentSkillsService.createAndAttachSkill).toHaveBeenCalledWith('agent-1', 'project-1', { + name: 'Skill', + body: 'do it', + }); + expect(result.structuredContent).toMatchObject({ + ok: true, + resource: { type: 'skill', id: 'skill-1' }, + }); + }); + + it('updates an existing skill when skillId is given', async () => { + agentSkillsService.updateSkill.mockResolvedValue({ id: 'skill-1' } as never); + + const result = await callTool( + 'mutate_agent', + mutateInput({ + type: 'skill.upsert', + skillId: 'skill-1', + skill: { name: 'Skill', body: 'v2' }, + }), + ); + + expect(agentSkillsService.updateSkill).toHaveBeenCalledWith( + 'agent-1', + 'project-1', + 'skill-1', + { name: 'Skill', body: 'v2' }, + ); + expect(result.structuredContent).toMatchObject({ + resource: { type: 'skill', id: 'skill-1' }, + }); + }); + + it('creates a task enabled by default', async () => { + agentTaskService.create.mockResolvedValue({ id: 'task-1' } as never); + + const result = await callTool( + 'mutate_agent', + mutateInput({ + type: 'task.upsert', + task: { name: 'Daily', objective: 'report', cronExpression: '0 9 * * *' }, + }), + ); + + expect(agentTaskService.create).toHaveBeenCalledWith('agent-1', { + name: 'Daily', + objective: 'report', + cronExpression: '0 9 * * *', + enabled: true, + }); + expect(result.structuredContent).toMatchObject({ + resource: { type: 'task', id: 'task-1' }, + }); + }); + + it('builds a custom tool and attaches its config reference once', async () => { + const descriptor = { name: 'my_tool' }; + agentSecureRuntime.describeToolSecurely.mockResolvedValue(descriptor as never); + agentCustomToolsService.buildCustomTool.mockResolvedValue({ + ok: true, + id: 'my_tool', + descriptor, + } as never); + const stored = { ...baseConfig, tools: [{ type: 'custom' as const, id: 'my_tool' }] }; + agentConfigService.updateConfig.mockResolvedValue({ + config: stored, + updatedAt: 'now', + versionId: 'v2', + }); + + const result = await callTool( + 'mutate_agent', + mutateInput({ type: 'customTool.upsert', code: 'export default new Tool("my_tool")' }), + ); + + expect(agentCustomToolsService.buildCustomTool).toHaveBeenCalledWith( + 'agent-1', + 'project-1', + 'export default new Tool("my_tool")', + descriptor, + ); + expect(agentConfigService.updateConfig).toHaveBeenCalledWith( + 'agent-1', + 'project-1', + expect.objectContaining({ tools: [{ type: 'custom', id: 'my_tool' }] }), + user, + ); + expect(result.structuredContent).toMatchObject({ + resource: { type: 'customTool', id: 'my_tool' }, + configHash: getAgentConfigHash(stored), + }); + }); + + it('skips the config write when the custom tool reference already exists', async () => { + const configWithTool = { + ...composedConfig, + tools: [{ type: 'custom' as const, id: 'my_tool' }], + }; + agentsService.findByIdForUser.mockResolvedValue( + agentEntity({ schema: { ...baseConfig, tools: configWithTool.tools } }), + ); + agentSecureRuntime.describeToolSecurely.mockResolvedValue({ name: 'my_tool' } as never); + agentCustomToolsService.buildCustomTool.mockResolvedValue({ + ok: true, + id: 'my_tool', + descriptor: { name: 'my_tool' }, + } as never); + + const result = await callTool( + 'mutate_agent', + mutateInput( + { type: 'customTool.upsert', code: 'code' }, + getAgentConfigHash(configWithTool) ?? undefined, + ), + ); + + expect(agentConfigService.updateConfig).not.toHaveBeenCalled(); + expect(result.structuredContent).toMatchObject({ + ok: true, + configHash: getAgentConfigHash(configWithTool), + }); + }); + + test.each<[string, Record, () => unknown]>([ + [ + 'skill.delete', + { type: 'skill.delete', skillId: 'skill-1' }, + () => + expect(agentSkillsService.deleteSkill).toHaveBeenCalledWith( + 'agent-1', + 'project-1', + 'skill-1', + ), + ], + [ + 'task.delete', + { type: 'task.delete', taskId: 'task-1' }, + () => expect(agentTaskService.delete).toHaveBeenCalledWith('agent-1', 'task-1'), + ], + [ + 'customTool.delete', + { type: 'customTool.delete', toolId: 'tool-1' }, + () => + expect(agentCustomToolsService.deleteCustomTool).toHaveBeenCalledWith( + 'agent-1', + 'project-1', + 'tool-1', + ), + ], + ])('routes %s to its sidecar service', async (_name, operation, assertion) => { + const result = await callTool('mutate_agent', mutateInput(operation)); + + assertion(); + expect(result.structuredContent).toMatchObject({ ok: true }); + }); + }); + + describe('create_agent', () => { + beforeEach(() => { + agentsService.create.mockResolvedValue(agentEntity()); + }); + + it('validates, creates, and stores the initial config with the name injected', async () => { + const initialConfig = { model: 'anthropic/claude', instructions: 'Help.' }; + agentConfigService.validateConfig.mockResolvedValue({ + valid: true, + config: baseConfig, + }); + agentConfigService.updateConfig.mockResolvedValue({ + config: baseConfig, + updatedAt: 'now', + versionId: 'v2', + }); + + const result = await callTool('create_agent', { + projectId: 'project-1', + name: 'My Agent', + config: initialConfig, + }); + + expect(agentConfigService.validateConfig).toHaveBeenCalledWith({ + ...initialConfig, + name: 'My Agent', + }); + expect(agentsService.create).toHaveBeenCalledWith('project-1', 'My Agent', { + availableInMCP: true, + }); + expect(agentConfigService.updateConfig).toHaveBeenCalledWith( + 'agent-1', + 'project-1', + { ...initialConfig, name: 'My Agent' }, + user, + ); + expect(result.structuredContent).toEqual({ + ok: true, + agent: { + id: 'agent-1', + name: 'My Agent', + projectId: 'project-1', + published: false, + versionId: 'v2', + activeVersionId: null, + }, + configHash: getAgentConfigHash(baseConfig), + url: 'https://n8n.test/projects/project-1/agents/agent-1', + }); + }); + + it('rejects an invalid initial config before creating the agent', async () => { + agentConfigService.validateConfig.mockResolvedValue({ valid: false, error: 'bad model' }); + + const result = await callTool('create_agent', { + projectId: 'project-1', + name: 'My Agent', + config: { model: 'nope' }, + }); + + expect(result.isError).toBe(true); + expect(result.structuredContent).toMatchObject({ + error: 'Invalid initial Agent config: bad model', + }); + expect(agentsService.create).not.toHaveBeenCalled(); + }); + + it('deletes the created agent when storing the initial config fails', async () => { + agentConfigService.validateConfig.mockResolvedValue({ + valid: true, + config: baseConfig, + }); + agentConfigService.updateConfig.mockRejectedValue(new Error('write failed')); + + const result = await callTool('create_agent', { + projectId: 'project-1', + name: 'My Agent', + config: { model: 'anthropic/claude' }, + }); + + expect(agentsService.delete).toHaveBeenCalledWith('agent-1', 'project-1'); + expect(result.isError).toBe(true); + expect(result.structuredContent).toMatchObject({ error: 'write failed' }); + }); + }); + + describe('search_agents', () => { + it('only searches projects where the user has agent:list', async () => { + agentsService.findSummariesInProjects.mockResolvedValue([ + agentEntity({ id: 'agent-1', projectId: 'project-1' }), + ]); + + const result = await callTool('search_agents', {}); + + expect(projectScopeService.getProjectIds).toHaveBeenCalledWith(user, ['agent:list']); + expect(agentsService.findSummariesInProjects).toHaveBeenCalledWith( + ['project-1'], + expect.any(Object), + ); + expect(result.structuredContent).toMatchObject({ ok: true, count: 1 }); + expect(result.structuredContent.data).toEqual([ + expect.objectContaining({ id: 'agent-1', projectId: 'project-1', availableInMCP: true }), + ]); + }); + + it('searches without a project restriction for global agent:list', async () => { + projectScopeService.getProjectIds.mockResolvedValue(null); + agentsService.findSummariesInProjects.mockResolvedValue([ + agentEntity({ id: 'agent-1', projectId: 'project-2' }), + ]); + + const result = await callTool('search_agents', {}); + + expect(agentsService.findSummariesInProjects).toHaveBeenCalledWith(null, expect.any(Object)); + expect(result.structuredContent).toMatchObject({ ok: true, count: 1 }); + }); + + it('pushes query, publishedOnly, excludeAgentId, and limit filters into the lookup', async () => { + agentsService.findSummariesInProjects.mockResolvedValue([ + agentEntity({ id: 'agent-1', name: 'Sales Helper', activeVersionId: 'v1' }), + ]); + + const result = await callTool('search_agents', { + projectId: 'project-1', + query: ' sales ', + publishedOnly: true, + excludeAgentId: 'agent-3', + limit: 10, + }); + + expect(agentsService.findSummariesInProjects).toHaveBeenCalledWith(['project-1'], { + query: 'sales', + publishedOnly: true, + excludeAgentId: 'agent-3', + limit: 10, + }); + expect(result.structuredContent.data).toEqual([ + expect.objectContaining({ + id: 'agent-1', + published: true, + updatedAt: '2026-01-02T00:00:00.000Z', + }), + ]); + }); + }); + + describe('publish_agent', () => { + beforeEach(() => { + agentConfigService.validateConfig.mockResolvedValue({ valid: true, config: baseConfig }); + }); + + it('refuses to publish an agent that fails publish-scope validation', async () => { + agentValidationService.validateLoadedAgentConfiguration.mockResolvedValue({ + status: 'invalid', + issues: [{ code: 'missing_credential', path: 'credential', capability: { kind: 'agent' } }], + } as never); + + const result = await callTool('publish_agent', { + projectId: 'project-1', + agentId: 'agent-1', + }); + + expect(result.isError).toBe(true); + expect(result.structuredContent).toMatchObject({ + error: 'Agent is not runnable: credential', + }); + expect(agentPublishService.publishAgent).not.toHaveBeenCalled(); + }); + + it('publishes a valid agent', async () => { + agentValidationService.validateLoadedAgentConfiguration.mockResolvedValue({ + status: 'valid', + issues: [], + } as never); + agentPublishService.publishAgent.mockResolvedValue({ + agent: agentEntity({ versionId: 'v2', activeVersionId: 'v2' }), + }); + + const result = await callTool('publish_agent', { + projectId: 'project-1', + agentId: 'agent-1', + }); + + expect(agentPublishService.publishAgent).toHaveBeenCalledWith( + 'agent-1', + 'project-1', + user, + 'builder', + undefined, + ); + expect(result.structuredContent).toEqual({ + ok: true, + agentId: 'agent-1', + published: true, + versionId: 'v2', + activeVersionId: 'v2', + url: 'https://n8n.test/projects/project-1/agents/agent-1', + }); + }); + + it('republishes a previous version without validating the draft', async () => { + agentPublishService.publishAgent.mockResolvedValue({ + agent: agentEntity({ versionId: 'v3', activeVersionId: 'v1' }), + }); + + const result = await callTool('publish_agent', { agentId: 'agent-1', versionId: 'v1' }); + + expect(agentValidationService.validateLoadedAgentConfiguration).not.toHaveBeenCalled(); + expect(agentPublishService.publishAgent).toHaveBeenCalledWith( + 'agent-1', + 'project-1', + user, + 'builder', + 'v1', + ); + expect(result.structuredContent).toMatchObject({ + ok: true, + published: true, + versionId: 'v3', + activeVersionId: 'v1', + }); + }); + }); + + describe('validate_agent', () => { + it('reports a valid agent with its editor URL', async () => { + agentConfigService.validateConfig.mockResolvedValue({ valid: true, config: baseConfig }); + agentValidationService.validateLoadedAgentConfiguration.mockResolvedValue({ + status: 'valid', + issues: [], + } as never); + + const result = await callTool('validate_agent', { agentId: 'agent-1' }); + + expect(result.structuredContent).toEqual({ + ok: true, + valid: true, + errors: [], + missing: [], + url: 'https://n8n.test/projects/project-1/agents/agent-1', + }); + }); + + it('aggregates the schema error and deduplicated missing capability paths', async () => { + agentConfigService.validateConfig.mockResolvedValue({ + valid: false, + error: 'model is required', + }); + agentValidationService.validateLoadedAgentConfiguration.mockResolvedValue({ + status: 'invalid', + issues: [ + { code: 'missing_credential', path: 'credential', capability: { kind: 'agent' } }, + { code: 'missing_credential', path: 'credential', capability: { kind: 'tool' } }, + { code: 'missing_setting', path: 'model', capability: { kind: 'agent' } }, + ], + } as never); + + const result = await callTool('validate_agent', { agentId: 'agent-1' }); + + expect(result.structuredContent).toMatchObject({ + ok: true, + valid: false, + errors: ['model is required'], + missing: ['credential', 'model'], + }); + }); + }); + + describe('unpublish_agent', () => { + it('unpublishes the agent and reports the draft version', async () => { + agentPublishService.unpublishAgent.mockResolvedValue( + agentEntity({ versionId: 'v2', activeVersionId: null }), + ); + + const result = await callTool('unpublish_agent', { agentId: 'agent-1' }); + + expect(agentPublishService.unpublishAgent).toHaveBeenCalledWith( + 'agent-1', + 'project-1', + user, + 'builder', + ); + expect(result.structuredContent).toEqual({ + ok: true, + agentId: 'agent-1', + published: false, + versionId: 'v2', + activeVersionId: null, + }); + }); + }); + + describe('revert_agent', () => { + it('restores the draft from the currently published version by default', async () => { + agentPublishService.revertToPublishedAgent.mockResolvedValue( + agentEntity({ versionId: 'v1', activeVersionId: 'v1' }), + ); + + const result = await callTool('revert_agent', { agentId: 'agent-1' }); + + expect(agentPublishService.revertToPublishedAgent).toHaveBeenCalledWith( + 'agent-1', + 'project-1', + ); + expect(agentPublishService.revertToVersion).not.toHaveBeenCalled(); + expect(result.structuredContent).toEqual({ + ok: true, + agentId: 'agent-1', + versionId: 'v1', + activeVersionId: 'v1', + configHash: getAgentConfigHash({ ...baseConfig, integrations: [] }), + url: 'https://n8n.test/projects/project-1/agents/agent-1', + }); + }); + + it('restores the draft from a specific version', async () => { + agentPublishService.revertToVersion.mockResolvedValue( + agentEntity({ versionId: 'v4', activeVersionId: 'v2' }), + ); + + const result = await callTool('revert_agent', { agentId: 'agent-1', versionId: 'v0' }); + + expect(agentPublishService.revertToVersion).toHaveBeenCalledWith( + 'agent-1', + 'project-1', + 'v0', + ); + expect(result.structuredContent).toMatchObject({ + ok: true, + versionId: 'v4', + activeVersionId: 'v2', + }); + }); + + it('returns an error result when the version does not exist', async () => { + agentPublishService.revertToVersion.mockRejectedValue(new Error('Version "nope" not found')); + + const result = await callTool('revert_agent', { agentId: 'agent-1', versionId: 'nope' }); + + expect(result.isError).toBe(true); + expect(result.structuredContent).toMatchObject({ error: 'Version "nope" not found' }); + }); + }); + + describe('list_agent_versions', () => { + it('lists the publish history', async () => { + const versions = [ + { + versionId: 'v1', + agentId: 'agent-1', + createdAt: '2026-01-02T00:00:00.000Z', + updatedAt: '2026-01-02T00:00:00.000Z', + author: 'Ada Lovelace', + isActive: true, + }, + ]; + agentPublishService.listPublishHistory.mockResolvedValue(versions); + + const result = await callTool('list_agent_versions', { + agentId: 'agent-1', + limit: 20, + offset: 0, + }); + + expect(agentPublishService.listPublishHistory).toHaveBeenCalledWith( + 'agent-1', + 'project-1', + 20, + 0, + ); + expect(result.structuredContent).toEqual({ ok: true, data: versions, count: 1 }); + }); + }); + + describe('discover_agent_assets', () => { + it('returns a provider summary for kind=models without a provider', async () => { + const result = await callTool('discover_agent_assets', { + projectId: 'project-1', + kind: 'models', + }); + + expect(result.structuredContent).toEqual({ + ok: true, + kind: 'models', + data: { + providers: [{ provider: 'openai', name: 'OpenAI', modelCount: 2 }], + hint: expect.stringContaining('provider'), + }, + }); + }); + + it('lists MCP registry servers for kind=mcpServers without a query', async () => { + mcpRegistryService.list.mockResolvedValue([{ name: 'github' }] as never); + + const result = await callTool('discover_agent_assets', { + projectId: 'project-1', + kind: 'mcpServers', + }); + + expect(mcpRegistryService.list).toHaveBeenCalledWith(20); + expect(mcpRegistryService.search).not.toHaveBeenCalled(); + expect(result.structuredContent).toMatchObject({ + ok: true, + kind: 'mcpServers', + data: [{ name: 'github' }], + }); + }); + + it('searches the MCP registry for kind=mcpServers with a query', async () => { + mcpRegistryService.search.mockResolvedValue([]); + + await callTool('discover_agent_assets', { + projectId: 'project-1', + kind: 'mcpServers', + query: 'github', + }); + + expect(mcpRegistryService.search).toHaveBeenCalledWith(['github']); + expect(mcpRegistryService.list).not.toHaveBeenCalled(); + }); + }); + + describe('verify_agent_mcp_server', () => { + const input = { + projectId: 'project-1', + name: 'srv', + url: 'https://mcp.test', + transport: 'streamableHttp', + authentication: 'none', + }; + + it('connects and returns the server tool list', async () => { + outboundHttp.transport.mockReturnValue({ asCustomFetch: () => vi.fn() } as never); + listMcpServerToolsMock.mockResolvedValue([{ name: 'echo', description: 'Echo' }]); + + const result = await callTool('verify_agent_mcp_server', input); + + expect(listMcpServerToolsMock).toHaveBeenCalledWith( + expect.objectContaining({ name: 'srv', url: 'https://mcp.test', authentication: 'none' }), + expect.objectContaining({ projectId: 'project-1' }), + ); + expect(result.structuredContent).toEqual({ + ok: true, + tools: [{ name: 'echo', description: 'Echo' }], + }); + }); + + it('requires a credential for authenticated servers', async () => { + const result = await callTool('verify_agent_mcp_server', { + ...input, + authentication: 'bearerAuth', + }); + + expect(result.isError).toBe(true); + expect(result.structuredContent).toMatchObject({ + error: 'credential is required when authentication is not none', + }); + expect(listMcpServerToolsMock).not.toHaveBeenCalled(); + }); + + it('rejects a credential the user cannot access', async () => { + const result = await callTool('verify_agent_mcp_server', { + ...input, + authentication: 'bearerAuth', + credential: 'cred-x', + }); + + expect(result.isError).toBe(true); + expect(result.structuredContent).toMatchObject({ + error: 'Credential not found or not accessible', + }); + expect(listMcpServerToolsMock).not.toHaveBeenCalled(); + }); + }); + + describe('get_agent', () => { + it('returns the agent snapshot with sidecars and configHash', async () => { + const config = { + ...baseConfig, + tools: [{ type: 'custom' as const, id: 'my_tool' }], + tasks: [{ type: 'task' as const, id: 'task-1', enabled: true }], + }; + agentsService.findByIdForUser.mockResolvedValue( + agentEntity({ + schema: config, + tools: { my_tool: { code: 'code', descriptor: { name: 'my_tool' } } }, + }), + ); + agentValidationService.validateAgentIsRunnable.mockResolvedValue({ + missing: ['credential:model'], + } as never); + agentSkillsService.listSkills.mockResolvedValue([{ id: 'skill-1' }] as never); + agentTaskService.list.mockResolvedValue([ + { id: 'task-1', name: 'Daily' }, + { id: 'task-2', name: 'Detached' }, + ] as never); + + const result = await callTool('get_agent', { projectId: 'project-1', agentId: 'agent-1' }); + + expect(result.structuredContent).toMatchObject({ + ok: true, + agent: expect.objectContaining({ id: 'agent-1', published: false }), + configHash: getAgentConfigHash({ ...config, integrations: [] }), + isRunnable: false, + missing: ['credential:model'], + skills: [{ id: 'skill-1' }], + tasks: [ + { id: 'task-1', name: 'Daily', enabled: true }, + { id: 'task-2', name: 'Detached', enabled: false }, + ], + customTools: [{ id: 'my_tool', descriptor: { name: 'my_tool' } }], + }); + }); + + it('reports integrations separately and omits them from the editable config', async () => { + const integration = { type: 'telegram', credentialId: 'cred-1' }; + agentsService.findByIdForUser.mockResolvedValue( + agentEntity({ activeVersionId: 'v1', integrations: [integration] }), + ); + agentValidationService.validateAgentIsRunnable.mockResolvedValue({ missing: [] } as never); + agentSkillsService.listSkills.mockResolvedValue([] as never); + agentTaskService.list.mockResolvedValue([] as never); + + const result = await callTool('get_agent', { projectId: 'project-1', agentId: 'agent-1' }); + + expect(result.structuredContent.integrations).toEqual([integration]); + expect(result.structuredContent.config).not.toHaveProperty('integrations'); + }); + + it('returns an error result for an unknown agent', async () => { + agentsService.findByIdForUser.mockResolvedValue(null); + + const result = await callTool('get_agent', { projectId: 'project-1', agentId: 'nope' }); + + expect(result.isError).toBe(true); + expect(result.structuredContent).toMatchObject({ error: 'Agent "nope" not found' }); + }); + + it('returns a published version snapshot without a configHash when versionId is passed', async () => { + agentPublishService.getVersion.mockResolvedValue({ + agent: agentEntity({ activeVersionId: 'v0' }), + version: { + versionId: 'v0', + agentId: 'agent-1', + author: 'Ada Lovelace', + createdAt: new Date('2026-01-01T00:00:00.000Z'), + schema: { + ...baseConfig, + integrations: [{ type: 'slack', credentialId: 'cred-1' }], + }, + tools: { my_tool: { code: 'code', descriptor: { name: 'my_tool' } } }, + skills: { 'skill-1': { name: 'Skill' } }, + }, + tasks: [ + { + taskId: 'task-1', + name: 'Daily', + objective: 'Summarize', + cronExpression: '0 9 * * *', + enabled: true, + }, + ], + } as never); + + const result = await callTool('get_agent', { agentId: 'agent-1', versionId: 'v0' }); + + expect(agentPublishService.getVersion).toHaveBeenCalledWith('agent-1', 'project-1', 'v0'); + expect(result.structuredContent).toMatchObject({ + ok: true, + agent: expect.objectContaining({ id: 'agent-1', published: true }), + version: { + versionId: 'v0', + author: 'Ada Lovelace', + createdAt: '2026-01-01T00:00:00.000Z', + isActive: true, + }, + skills: { 'skill-1': { name: 'Skill' } }, + tasks: [expect.objectContaining({ id: 'task-1', enabled: true })], + customTools: [{ id: 'my_tool', descriptor: { name: 'my_tool' } }], + }); + expect(result.structuredContent.config).not.toHaveProperty('integrations'); + expect(result.structuredContent).not.toHaveProperty('configHash'); + }); + + it('returns an error result for an unknown versionId', async () => { + agentPublishService.getVersion.mockRejectedValue( + new Error('Version "v9" not found for agent "agent-1"'), + ); + + const result = await callTool('get_agent', { agentId: 'agent-1', versionId: 'v9' }); + + expect(result.isError).toBe(true); + expect(result.structuredContent).toMatchObject({ + error: 'Version "v9" not found for agent "agent-1"', + }); + }); + }); + + describe('project resolution', () => { + beforeEach(() => { + agentValidationService.validateAgentIsRunnable.mockResolvedValue({ missing: [] } as never); + agentSkillsService.listSkills.mockResolvedValue([] as never); + agentTaskService.list.mockResolvedValue([] as never); + }); + + it('resolves the project from the agentId', async () => { + agentsService.findByIdForUser.mockResolvedValue(agentEntity({ projectId: 'project-9' })); + + const result = await callTool('get_agent', { agentId: 'agent-1' }); + + expect(agentsService.findByIdForUser).toHaveBeenCalledWith('agent-1', user); + expect(userHasScopesMock).toHaveBeenCalledWith(user, ['agent:read'], false, { + projectId: 'project-9', + }); + expect(result.structuredContent).toMatchObject({ ok: true }); + }); + + it('returns an error when the agentId resolves to no accessible agent', async () => { + agentsService.findByIdForUser.mockResolvedValue(null); + + const result = await callTool('get_agent', { agentId: 'ghost' }); + + expect(result.isError).toBe(true); + expect(result.structuredContent).toMatchObject({ error: 'Agent "ghost" not found' }); + }); + + it('applies a mutation against the resolved project', async () => { + agentsService.findByIdForUser.mockResolvedValue(agentEntity({ projectId: 'project-9' })); + agentConfigService.updateConfig.mockResolvedValue({ + config: baseConfig, + updatedAt: 'now', + versionId: 'v2', + }); + + const result = await callTool('mutate_agent', { + agentId: 'agent-1', + baseConfigHash: getAgentConfigHash(composedConfig), + operation: { type: 'config.replace', config: { name: 'My Agent' } }, + }); + + expect(agentConfigService.updateConfig).toHaveBeenCalledWith( + 'agent-1', + 'project-9', + { name: 'My Agent' }, + user, + { clearOmittedOptionalFields: true }, + ); + expect(result.structuredContent).toMatchObject({ ok: true }); + }); + }); + + describe('update_agent_integration connect', () => { + const input = { + agentId: 'agent-1', + action: 'connect', + type: 'slack', + credentialId: 'cred-1', + }; + + beforeEach(() => { + credentialsService.getCredentialsAUserCanUseInAWorkflow.mockResolvedValue([ + { id: 'cred-1', type: 'slackApi', name: 'Slack cred' }, + ] as never); + chatIntegrationRegistry.require.mockReturnValue({ + credentialTypes: ['slackApi'], + displayLabel: 'Slack', + } as never); + integrationPersistenceService.saveCredentialIntegration.mockResolvedValue( + agentEntity({ integrations: [{ type: 'slack', credentialId: 'cred-1' }] }), + ); + agentPublishService.publishAgent.mockResolvedValue({ + agent: agentEntity({ activeVersionId: 'v2' }), + }); + }); + + it('persists the integration, publishes the draft, and connects the channel', async () => { + const result = await callTool('update_agent_integration', input); + + expect(integrationPersistenceService.saveCredentialIntegration).toHaveBeenCalledWith( + expect.objectContaining({ id: 'agent-1' }), + { type: 'slack', credentialId: 'cred-1' }, + { broadcast: false }, + ); + expect(agentPublishService.publishAgent).toHaveBeenCalledWith( + 'agent-1', + 'project-1', + user, + 'channel_connect', + undefined, + { syncIntegrations: false, ignoreDraftIntegrations: true }, + ); + expect(chatIntegrationService.connect).toHaveBeenCalledWith( + 'agent-1', + { type: 'slack', credentialId: 'cred-1' }, + 'project-1', + ); + expect(chatIntegrationService.broadcastIntegrationChange).toHaveBeenCalledWith( + 'agent-1', + { type: 'slack', credentialId: 'cred-1' }, + 'connect', + ); + expect(result.structuredContent).toMatchObject({ + ok: true, + connected: true, + published: true, + activeVersionId: 'v2', + }); + }); + + it('denies connect without the agent:publish scope even when update is allowed', async () => { + userHasScopesMock.mockImplementation( + async (_user: unknown, scopes: string[]) => !scopes.includes('agent:publish'), + ); + + const result = await callTool('update_agent_integration', input); + + expect(userHasScopesMock).toHaveBeenCalledWith(user, ['agent:publish'], false, { + projectId: 'project-1', + }); + expect(result.isError).toBe(true); + expect(integrationPersistenceService.saveCredentialIntegration).not.toHaveBeenCalled(); + expect(agentPublishService.publishAgent).not.toHaveBeenCalled(); + }); + + it('requires settings for telegram integrations', async () => { + const result = await callTool('update_agent_integration', { ...input, type: 'telegram' }); + + expect(result.isError).toBe(true); + expect(result.structuredContent).toMatchObject({ + error: 'Telegram integration settings are required', + }); + expect(integrationPersistenceService.saveCredentialIntegration).not.toHaveBeenCalled(); + }); + + it('rejects a credential whose type the integration does not support', async () => { + credentialsService.getCredentialsAUserCanUseInAWorkflow.mockResolvedValue([ + { id: 'cred-1', type: 'telegramApi', name: 'Telegram cred' }, + ] as never); + + const result = await callTool('update_agent_integration', input); + + expect(result.isError).toBe(true); + expect(result.structuredContent).toMatchObject({ + error: 'Slack integrations do not support telegramApi credentials', + }); + expect(integrationPersistenceService.saveCredentialIntegration).not.toHaveBeenCalled(); + }); + }); + + describe('update_agent_integration disconnect', () => { + const input = { + projectId: 'project-1', + agentId: 'agent-1', + action: 'disconnect', + type: 'slack', + credentialId: 'cred-1', + }; + + beforeEach(() => { + integrationPersistenceService.removeCredentialIntegration.mockResolvedValue( + agentEntity({ integrations: [] }), + ); + }); + + it('disconnects a persisted integration and removes its record', async () => { + const persisted = { type: 'slack', credentialId: 'cred-1' }; + agentsService.findByIdForUser.mockResolvedValue(agentEntity({ integrations: [persisted] })); + + const result = await callTool('update_agent_integration', input); + + expect(chatIntegrationService.disconnectChannel).toHaveBeenCalledWith('agent-1', persisted); + expect(chatIntegrationService.disconnect).not.toHaveBeenCalled(); + expect(integrationPersistenceService.removeCredentialIntegration).toHaveBeenCalledWith( + expect.objectContaining({ id: 'agent-1' }), + 'slack', + 'cred-1', + { broadcast: false }, + ); + expect(result.structuredContent).toMatchObject({ ok: true, connected: false }); + }); + + it('still tears down the runtime channel when no persisted integration matches', async () => { + agentsService.findByIdForUser.mockResolvedValue(agentEntity({ integrations: [] })); + + const result = await callTool('update_agent_integration', input); + + expect(chatIntegrationService.disconnectChannel).toHaveBeenCalledWith('agent-1', { + type: 'slack', + credentialId: 'cred-1', + }); + expect(integrationPersistenceService.removeCredentialIntegration).toHaveBeenCalled(); + expect(result.structuredContent).toMatchObject({ ok: true, connected: false }); + }); + + it('falls back to a plain disconnect for an unknown integration type', async () => { + agentsService.findByIdForUser.mockResolvedValue(agentEntity({ integrations: [] })); + + await callTool('update_agent_integration', { ...input, type: 'bogus' }); + + expect(chatIntegrationService.disconnectChannel).not.toHaveBeenCalled(); + expect(chatIntegrationService.disconnect).toHaveBeenCalledWith('agent-1', { + type: 'bogus', + credentialId: 'cred-1', + }); + expect(integrationPersistenceService.removeCredentialIntegration).toHaveBeenCalled(); + }); + }); + + describe('delete_agent', () => { + it('deletes an existing agent', async () => { + agentsService.delete.mockResolvedValue(true); + + const result = await callTool('delete_agent', { projectId: 'project-1', agentId: 'agent-1' }); + + expect(agentsService.delete).toHaveBeenCalledWith('agent-1', 'project-1'); + expect(result.structuredContent).toEqual({ ok: true, deleted: true, agentId: 'agent-1' }); + }); + + it('returns an error result when the agent does not exist', async () => { + agentsService.delete.mockResolvedValue(false); + + const result = await callTool('delete_agent', { projectId: 'project-1', agentId: 'nope' }); + + expect(result.isError).toBe(true); + expect(result.structuredContent).toMatchObject({ error: 'Agent "nope" not found' }); + }); + }); +}); diff --git a/packages/cli/src/modules/mcp/__tests__/create-workflow-from-code.tool.test.ts b/packages/cli/src/modules/mcp/__tests__/create-workflow-from-code.tool.test.ts index 14fec3c68ac..26d625b60c8 100644 --- a/packages/cli/src/modules/mcp/__tests__/create-workflow-from-code.tool.test.ts +++ b/packages/cli/src/modules/mcp/__tests__/create-workflow-from-code.tool.test.ts @@ -53,6 +53,7 @@ vi.mock('@n8n/ai-workflow-builder', () => ({ displayTitle: 'Create Workflow from Code', }, MCP_ARCHIVE_WORKFLOW_TOOL: { toolName: 'archive_workflow', displayTitle: 'Archive Workflow' }, + MCP_UPDATE_WORKFLOW_TOOL: { toolName: 'update_workflow', displayTitle: 'Update Workflow' }, CODE_BUILDER_SEARCH_NODES_TOOL: { toolName: 'search', displayTitle: 'Search' }, CODE_BUILDER_GET_NODE_TYPES_TOOL: { toolName: 'get', displayTitle: 'Get' }, CODE_BUILDER_GET_SUGGESTED_NODES_TOOL: { toolName: 'suggest', displayTitle: 'Suggest' }, @@ -124,12 +125,14 @@ describe('create-workflow-from-code MCP tool', () => { nodeTypes = mockInstance(NodeTypes); nodeTypes.getByNameAndVersion.mockImplementation(((type: string) => { if (type === '@n8n/n8n-nodes-langchain.agent') { - return { description: { outputs: [NodeConnectionTypes.Main] } }; + return { description: { group: ['transform'], outputs: [NodeConnectionTypes.Main] } }; } if (type === '@n8n/n8n-nodes-langchain.agentTool') { - return { description: { outputs: [NodeConnectionTypes.AiTool] } }; + return { description: { group: ['transform'], outputs: [NodeConnectionTypes.AiTool] } }; } - return { description: {} }; + // The group validator resolves trigger-ness via description.group; an + // empty (non-trigger) group keeps that check from crashing on `undefined`. + return { description: { group: ['transform'] } }; }) as typeof nodeTypes.getByNameAndVersion); mockParseAndValidate.mockResolvedValue({ workflow: mockWorkflowJson, warnings: [] }); @@ -978,7 +981,13 @@ describe('create-workflow-from-code MCP tool', () => { test('flag on: groups from the code are persisted on the created workflow', async () => { mockParseAndValidate.mockResolvedValue({ - workflow: { ...mockWorkflowJson, nodeGroups }, + workflow: { + ...mockWorkflowJson, + // Webhook -> Set, so { Webhook, Set } is a structurally valid group + // (a single connected subgraph) once structural rules are enforced. + connections: { Webhook: { main: [[{ node: 'Set', type: 'main', index: 0 }]] } }, + nodeGroups, + }, warnings: [], }); @@ -1003,32 +1012,267 @@ describe('create-workflow-from-code MCP tool', () => { expect(trackedData()).toEqual({ workflowId: 'wf-saved-1', nodeCount: 2, groupCount: 0 }); }); - test('flag on: invalid groups fail the creation with the save-path message', async () => { - // The save path rejects invalid groups (`validateWorkflowNodeGroups`); - // the tool intentionally lets that error fail the call so no workflow - // is silently created without the authored groups. - const saveError = new Error( - 'Node group "Ingestion" (g1) cannot contain trigger nodes: Webhook.', - ); - mockParseAndValidate.mockResolvedValue({ - workflow: { ...mockWorkflowJson, nodeGroups }, - warnings: [], + // Structural group rules (no triggers, single connected subgraph, no non-main connection + // crossing the group boundary) aren't checked before `workflowCreationService.createWorkflow` + // yet — that service's own `validateWorkflowNodeGroups` (a shared safety net used by the public + // API, editor controller, importer, etc.) still rejects the whole creation for an invalid group. + // These tests describe the target behavior for that follow-up: with canvasGroupsEnabled on, an + // invalid group should be dropped and reported in `skippedGroups` instead, while the rest of the + // workflow is still created. + describe('structural validation', () => { + beforeEach(() => { + // The group validator resolves trigger-ness via description.group. + nodeTypes.getByNameAndVersion.mockImplementation(((type: string) => { + if (type === 'n8n-nodes-base.webhook') { + return { description: { group: ['trigger'], outputs: [NodeConnectionTypes.Main] } }; + } + if (type === '@n8n/n8n-nodes-langchain.agent') { + return { description: { group: ['transform'], outputs: [NodeConnectionTypes.Main] } }; + } + if (type === '@n8n/n8n-nodes-langchain.agentTool') { + return { + description: { group: ['transform'], outputs: [NodeConnectionTypes.AiTool] }, + }; + } + return { description: { group: ['transform'], outputs: [NodeConnectionTypes.Main] } }; + }) as typeof nodeTypes.getByNameAndVersion); }); - createWorkflowMock.mockRejectedValue(saveError); - const result = await callHandler( - { code: 'const wf = ...' }, - createTool({ canvasGroupsEnabled: true }), - ); + test('a group with a trigger inside is skipped; the rest of the workflow is still created', async () => { + mockParseAndValidate.mockResolvedValue({ + workflow: { ...mockWorkflowJson, nodeGroups }, // Ingestion = [Webhook (trigger), Set] + warnings: [], + }); - expect(result.isError).toBe(true); - expect(parseResult(result).error).toBe(saveError.message); - expect(telemetry.track).toHaveBeenCalledWith( - 'User called mcp tool', - expect.objectContaining({ - results: expect.objectContaining({ success: false, error: saveError.message }), - }), - ); + const result = await callHandler( + { code: 'const wf = ...' }, + createTool({ canvasGroupsEnabled: true }), + ); + + expect(result.isError).toBeUndefined(); + expect(parseResult(result).workflowId).toBe('wf-saved-1'); + + const passedWorkflow = createWorkflowMock.mock.calls[0][1] as WorkflowEntity; + expect(passedWorkflow.nodeGroups).toEqual([]); + + const response = parseResult(result); + expect(response.skippedGroups).toEqual([ + expect.objectContaining({ + groupName: 'Ingestion', + reason: expect.stringContaining('cannot contain trigger nodes') as string, + }), + ]); + }); + + test('a group that splits an AI sub-node from its Agent is skipped', async () => { + mockParseAndValidate.mockResolvedValue({ + workflow: { + name: 'Code Workflow', + nodes: [ + { + id: 'agent', + name: 'Agent', + type: '@n8n/n8n-nodes-langchain.agent', + typeVersion: 1, + position: [0, 0], + parameters: {}, + }, + { + id: 'model', + name: 'Model', + type: '@n8n/n8n-nodes-langchain.agentTool', + typeVersion: 1, + position: [200, 0], + parameters: {}, + }, + ], + connections: { + Model: { + ai_languageModel: [[{ node: 'Agent', type: 'ai_languageModel', index: 0 }]], + }, + }, + settings: {}, + pinData: {}, + meta: {}, + nodeGroups: [{ id: 'g2', name: 'Group', nodeIds: ['agent'] }], + }, + warnings: [], + }); + + const result = await callHandler( + { code: 'const wf = ...' }, + createTool({ canvasGroupsEnabled: true }), + ); + + expect(result.isError).toBeUndefined(); + + const passedWorkflow = createWorkflowMock.mock.calls[0][1] as WorkflowEntity; + expect(passedWorkflow.nodeGroups).toEqual([]); + + const response = parseResult(result); + expect(response.skippedGroups).toEqual([ + expect.objectContaining({ + groupName: 'Group', + reason: expect.stringContaining('cannot cross the') as string, + }), + ]); + }); + + test('a group whose nodes form a disconnected subgraph is skipped', async () => { + mockParseAndValidate.mockResolvedValue({ + workflow: { + name: 'Code Workflow', + nodes: [ + { + id: 'a', + name: 'A', + type: 'n8n-nodes-base.set', + typeVersion: 1, + position: [0, 0], + parameters: {}, + }, + { + id: 'b', + name: 'B', + type: 'n8n-nodes-base.set', + typeVersion: 1, + position: [200, 0], + parameters: {}, + }, + ], + connections: {}, + settings: {}, + pinData: {}, + meta: {}, + nodeGroups: [{ id: 'g3', name: 'Group', nodeIds: ['a', 'b'] }], + }, + warnings: [], + }); + + const result = await callHandler( + { code: 'const wf = ...' }, + createTool({ canvasGroupsEnabled: true }), + ); + + expect(result.isError).toBeUndefined(); + + const passedWorkflow = createWorkflowMock.mock.calls[0][1] as WorkflowEntity; + expect(passedWorkflow.nodeGroups).toEqual([]); + + const response = parseResult(result); + expect(response.skippedGroups).toEqual([ + expect.objectContaining({ + groupName: 'Group', + reason: expect.stringContaining('single connected subgraph') as string, + }), + ]); + }); + + test('one invalid group among valid ones only drops the invalid one', async () => { + mockParseAndValidate.mockResolvedValue({ + workflow: { + name: 'Code Workflow', + nodes: [ + { + id: 'node-1', + name: 'Webhook', + type: 'n8n-nodes-base.webhook', + typeVersion: 1, + position: [0, 0], + parameters: {}, + }, + { + id: 'a', + name: 'A', + type: 'n8n-nodes-base.set', + typeVersion: 1, + position: [200, 0], + parameters: {}, + }, + { + id: 'b', + name: 'B', + type: 'n8n-nodes-base.set', + typeVersion: 1, + position: [400, 0], + parameters: {}, + }, + ], + connections: { Webhook: { main: [[{ node: 'A', type: 'main', index: 0 }]] } }, + settings: {}, + pinData: {}, + meta: {}, + nodeGroups: [ + { id: 'g1', name: 'Bad', nodeIds: ['node-1'] }, + { id: 'g2', name: 'Good', nodeIds: ['a'] }, + ], + }, + warnings: [], + }); + + const result = await callHandler( + { code: 'const wf = ...' }, + createTool({ canvasGroupsEnabled: true }), + ); + + expect(result.isError).toBeUndefined(); + + const passedWorkflow = createWorkflowMock.mock.calls[0][1] as WorkflowEntity; + expect(passedWorkflow.nodeGroups).toEqual([{ id: 'g2', name: 'Good', nodeIds: ['a'] }]); + + const response = parseResult(result); + expect(response.skippedGroups).toEqual([ + expect.objectContaining({ + groupName: 'Bad', + reason: expect.stringContaining('cannot contain trigger nodes') as string, + }), + ]); + }); + + test('all groups valid: no skippedGroups are reported', async () => { + mockParseAndValidate.mockResolvedValue({ + workflow: { + ...mockWorkflowJson, + nodeGroups: [{ id: 'g1', name: 'Group', nodeIds: ['node-2'] }], + }, + warnings: [], + }); + + const result = await callHandler( + { code: 'const wf = ...' }, + createTool({ canvasGroupsEnabled: true }), + ); + + expect(result.isError).toBeUndefined(); + + const passedWorkflow = createWorkflowMock.mock.calls[0][1] as WorkflowEntity; + expect(passedWorkflow.nodeGroups).toEqual([ + { id: 'g1', name: 'Group', nodeIds: ['node-2'] }, + ]); + + const response = parseResult(result); + expect(response.skippedGroups ?? []).toEqual([]); + }); + + test('the response reports skippedGroups with a human-readable reason', async () => { + mockParseAndValidate.mockResolvedValue({ + workflow: { ...mockWorkflowJson, nodeGroups }, + warnings: [], + }); + + const result = await callHandler( + { code: 'const wf = ...' }, + createTool({ canvasGroupsEnabled: true }), + ); + + const response = parseResult(result); + expect(response.skippedGroups).toEqual([ + { + groupName: 'Ingestion', + reason: expect.stringContaining('cannot contain trigger nodes') as string, + }, + ]); + }); }); }); }); diff --git a/packages/cli/src/modules/mcp/__tests__/credentials-auto-assign.test.ts b/packages/cli/src/modules/mcp/__tests__/credentials-auto-assign.test.ts index 48555a5cb26..cc549ea21cd 100644 --- a/packages/cli/src/modules/mcp/__tests__/credentials-auto-assign.test.ts +++ b/packages/cli/src/modules/mcp/__tests__/credentials-auto-assign.test.ts @@ -418,6 +418,209 @@ describe('autoPopulateNodeCredentials', () => { ]); }); + test('switches auth to a supported sibling type when the default auth type is unsupported', async () => { + // Node defaults to an unsupported auth (oAuth2 → serviceOAuth2Api) while a + // sibling (apiKey → serviceApiKey) is n8n-credits-eligible. + const desc = { + ...makeNodeTypeDescription({ name: 'n8n-nodes-base.service' }), + credentials: [ + { name: 'serviceOAuth2Api', displayOptions: { show: { authentication: ['oAuth2'] } } }, + { name: 'serviceApiKey', displayOptions: { show: { authentication: ['apiKey'] } } }, + ], + properties: [ + { + displayName: 'Authentication', + name: 'authentication', + type: 'options', + options: [ + { name: 'OAuth2', value: 'oAuth2' }, + { name: 'API Key', value: 'apiKey' }, + ], + default: 'oAuth2', + }, + ], + } as unknown as INodeTypeDescription; + const node = makeNode({ type: 'n8n-nodes-base.service', parameters: {} }); + const workflow = makeWorkflow([node]); + const { credentialsService, nodeTypes } = createMocks({ + usableCredentials: [], + nodeTypeDescriptions: new Map([['n8n-nodes-base.service', desc]]), + }); + const aiGatewayService = { + isAvailable: vi.fn().mockResolvedValue({ + available: true, + config: { + nodes: ['n8n-nodes-base.service'], + credentialTypes: ['serviceApiKey'], + providerConfig: { + serviceApiKey: { + gatewayPath: '/v1/gateway/service', + urlField: 'url', + apiKeyField: 'apiKey', + }, + }, + }, + }), + } as unknown as import('@/services/ai-gateway.service').AiGatewayService; + + const result = await autoPopulateNodeCredentials( + workflow, + user, + nodeTypes, + credentialsService, + projectId, + aiGatewayService, + ); + + expect(node.credentials).toEqual({ + serviceApiKey: { id: null, name: 'n8n credits', __aiGatewayManaged: true }, + }); + expect(node.parameters.authentication).toBe('apiKey'); + expect(result.assignments).toEqual([ + { + nodeName: 'Test Node', + credentialName: 'n8n credits', + credentialType: 'serviceApiKey', + source: 'aiGateway', + }, + ]); + }); + + test('switches auth when the controlling param is absent (relying on the type default)', async () => { + // Mirrors a real inserted node: `authentication` is NOT stored (defaults are + // not persisted), and an unrelated `operation` param IS present. + const desc = { + ...makeNodeTypeDescription({ name: 'n8n-nodes-base.service' }), + credentials: [ + { name: 'serviceOAuth2Api', displayOptions: { show: { authentication: ['oAuth2'] } } }, + { name: 'serviceApiKey', displayOptions: { show: { authentication: ['apiKey'] } } }, + ], + properties: [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + options: [{ name: 'Split PDF', value: 'Split PDF' }], + default: 'Split PDF', + }, + { + displayName: 'Authentication', + name: 'authentication', + type: 'options', + options: [ + { name: 'OAuth2', value: 'oAuth2' }, + { name: 'API Key', value: 'apiKey' }, + ], + default: 'oAuth2', + }, + ], + } as unknown as INodeTypeDescription; + const node = makeNode({ + type: 'n8n-nodes-base.service', + parameters: { operation: 'Split PDF' }, + }); + const workflow = makeWorkflow([node]); + const { credentialsService, nodeTypes } = createMocks({ + usableCredentials: [], + nodeTypeDescriptions: new Map([['n8n-nodes-base.service', desc]]), + }); + const aiGatewayService = { + isAvailable: vi.fn().mockResolvedValue({ + available: true, + config: { + nodes: ['n8n-nodes-base.service'], + credentialTypes: ['serviceApiKey'], + providerConfig: { + serviceApiKey: { + gatewayPath: '/v1/gateway/service', + urlField: 'url', + apiKeyField: 'apiKey', + }, + }, + }, + }), + } as unknown as import('@/services/ai-gateway.service').AiGatewayService; + + await autoPopulateNodeCredentials( + workflow, + user, + nodeTypes, + credentialsService, + projectId, + aiGatewayService, + ); + + expect(node.credentials).toEqual({ + serviceApiKey: { id: null, name: 'n8n credits', __aiGatewayManaged: true }, + }); + expect(node.parameters.authentication).toBe('apiKey'); + }); + + test('does not rewrite a parameter that already activates the assigned credential type', async () => { + // The credential is shown for several auth values; the node already uses + // the second one, so assigning n8n credits must not flip it to the first. + const desc = { + ...makeNodeTypeDescription({ name: 'n8n-nodes-base.service' }), + credentials: [ + { + name: 'serviceApiKey', + displayOptions: { show: { authentication: ['apiKey', 'apiKeyLegacy'] } }, + }, + ], + properties: [ + { + displayName: 'Authentication', + name: 'authentication', + type: 'options', + options: [ + { name: 'API Key', value: 'apiKey' }, + { name: 'API Key (legacy)', value: 'apiKeyLegacy' }, + ], + default: 'apiKey', + }, + ], + } as unknown as INodeTypeDescription; + const node = makeNode({ + type: 'n8n-nodes-base.service', + parameters: { authentication: 'apiKeyLegacy' }, + }); + const workflow = makeWorkflow([node]); + const { credentialsService, nodeTypes } = createMocks({ + usableCredentials: [], + nodeTypeDescriptions: new Map([['n8n-nodes-base.service', desc]]), + }); + const aiGatewayService = { + isAvailable: vi.fn().mockResolvedValue({ + available: true, + config: { + nodes: ['n8n-nodes-base.service'], + credentialTypes: ['serviceApiKey'], + providerConfig: { + serviceApiKey: { + gatewayPath: '/v1/gateway/service', + urlField: 'url', + apiKeyField: 'apiKey', + }, + }, + }, + }), + } as unknown as import('@/services/ai-gateway.service').AiGatewayService; + + await autoPopulateNodeCredentials( + workflow, + user, + nodeTypes, + credentialsService, + projectId, + aiGatewayService, + ); + + expect(node.credentials).toEqual({ + serviceApiKey: { id: null, name: 'n8n credits', __aiGatewayManaged: true }, + }); + expect(node.parameters.authentication).toBe('apiKeyLegacy'); + }); + test('prefers user credential when both exist', async () => { const node = makeNode(); const workflow = makeWorkflow([node]); diff --git a/packages/cli/src/modules/mcp/__tests__/get-execution.tool.test.ts b/packages/cli/src/modules/mcp/__tests__/get-execution.tool.test.ts index daf1d7d5511..4bb7474911a 100644 --- a/packages/cli/src/modules/mcp/__tests__/get-execution.tool.test.ts +++ b/packages/cli/src/modules/mcp/__tests__/get-execution.tool.test.ts @@ -42,10 +42,10 @@ describe('get-execution MCP tool', () => { telemetry, ); - expect(tool.name).toBe('get_execution'); + expect(tool.name).toBe('get_workflow_execution'); expect(tool.config).toBeDefined(); expect(typeof tool.config.description).toBe('string'); - expect(tool.config.description).toContain('Get execution details'); + expect(tool.config.description).toContain('Get workflow execution details'); expect(tool.config.inputSchema).toBeDefined(); expect(tool.config.outputSchema).toBeDefined(); expect(typeof tool.handler).toBe('function'); @@ -187,7 +187,7 @@ describe('get-execution MCP tool', () => { USER_CALLED_MCP_TOOL_EVENT, expect.objectContaining({ user_id: 'user-1', - tool_name: 'get_execution', + tool_name: 'get_workflow_execution', parameters: expect.objectContaining({ workflowId: 'workflow-1', executionId: 'execution-1', diff --git a/packages/cli/src/modules/mcp/__tests__/get-workflow-details.tool.test.ts b/packages/cli/src/modules/mcp/__tests__/get-workflow-details.tool.test.ts index 713b49965fd..29ee28de9d0 100644 --- a/packages/cli/src/modules/mcp/__tests__/get-workflow-details.tool.test.ts +++ b/packages/cli/src/modules/mcp/__tests__/get-workflow-details.tool.test.ts @@ -12,11 +12,14 @@ import { WorkflowFinderService } from '@/workflows/workflow-finder.service'; import { createWorkflow } from './mock.utils'; import { getWorkflowDetails, createWorkflowDetailsTool } from '../tools/get-workflow-details.tool'; +import { getTriggerDetails } from '../tools/webhook-utils'; vi.mock('../tools/webhook-utils', () => ({ getTriggerDetails: vi.fn().mockResolvedValue('MOCK_TRIGGER_DETAILS'), })); +const getTriggerDetailsMock = vi.mocked(getTriggerDetails); + describe('get-workflow-details MCP tool', () => { const user = Object.assign(new User(), { id: 'user-1' }); const baseWebhookUrl = 'https://example.test'; @@ -163,6 +166,55 @@ describe('get-workflow-details MCP tool', () => { expect(payload.workflow.tags).toEqual(tags); }); + test('passes triggers MCP cannot execute directly to getTriggerDetails', async () => { + getTriggerDetailsMock.mockClear(); + const workflow = createWorkflow({ + activeVersionId: uuid(), + nodes: [ + { + id: 'node-1', + name: 'Gmail Trigger', + type: 'n8n-nodes-base.gmailTrigger', + typeVersion: 1.4, + position: [0, 0], + disabled: false, + parameters: {}, + }, + { + id: 'node-2', + name: 'Disabled Trigger', + type: 'n8n-nodes-base.telegramTrigger', + typeVersion: 1, + position: [100, 0], + disabled: true, + parameters: {}, + }, + ], + }); + const workflowFinderService = mockInstance(WorkflowFinderService, { + findWorkflowForUser: vi.fn().mockResolvedValue(workflow), + }); + const credentialsService = mockInstance(CredentialsService, {}); + const endpoints = { webhook: 'webhook', webhookTest: 'webhook-test' }; + + await getWorkflowDetails( + user, + baseWebhookUrl, + workflowFinderService, + credentialsService, + nodeTypes, + endpoints, + roleService, + projectService, + { workflowId: 'wf-1' }, + ); + + const [, supportedTriggers, unsupportedTriggers] = getTriggerDetailsMock.mock.calls[0]; + expect(supportedTriggers).toEqual([]); + // Only the enabled unsupported trigger is passed; the disabled one is excluded. + expect(unsupportedTriggers.map((t) => t.name)).toEqual(['Gmail Trigger']); + }); + test('propagates errors from workflow validation', async () => { const credentialsService = mockInstance(CredentialsService, {}); const endpoints = { webhook: 'webhook', webhookTest: 'webhook-test' }; diff --git a/packages/cli/src/modules/mcp/__tests__/get-workflow-sdk-reference.tool.test.ts b/packages/cli/src/modules/mcp/__tests__/get-workflow-sdk-reference.tool.test.ts index ab22cf00e51..52af0e0f5d7 100644 --- a/packages/cli/src/modules/mcp/__tests__/get-workflow-sdk-reference.tool.test.ts +++ b/packages/cli/src/modules/mcp/__tests__/get-workflow-sdk-reference.tool.test.ts @@ -14,7 +14,7 @@ import { getSdkReferenceContent } from '../tools/workflow-builder/sdk-reference- vi.mock('@n8n/ai-workflow-builder', () => ({ SDK_IMPORT_STATEMENT: "import { workflow } from '@n8n/workflow-sdk';", MCP_GET_SDK_REFERENCE_TOOL: { - toolName: 'get_sdk_reference', + toolName: 'get_workflow_sdk_reference', displayTitle: 'Get SDK Reference', }, })); diff --git a/packages/cli/src/modules/mcp/__tests__/list-tags.tool.test.ts b/packages/cli/src/modules/mcp/__tests__/list-tags.tool.test.ts index 75b3c449281..a0ffba2e5a0 100644 --- a/packages/cli/src/modules/mcp/__tests__/list-tags.tool.test.ts +++ b/packages/cli/src/modules/mcp/__tests__/list-tags.tool.test.ts @@ -48,7 +48,7 @@ describe('list-tags MCP tool', () => { const tool = createListTagsTool(user, tagService, telemetry); - expect(tool.name).toBe('list_tags'); + expect(tool.name).toBe('list_workflow_tags'); expect(tool.config.description).toEqual(expect.any(String)); expect(tool.config.inputSchema).toBeDefined(); expect(tool.config.outputSchema).toBeDefined(); @@ -120,7 +120,7 @@ describe('list-tags MCP tool', () => { expect(telemetry.track).toHaveBeenCalledWith( expect.any(String), expect.objectContaining({ - tool_name: 'list_tags', + tool_name: 'list_workflow_tags', user_id: 'user-1', results: { success: true, data: { count: 1 } }, }), @@ -138,7 +138,7 @@ describe('list-tags MCP tool', () => { expect(telemetry.track).toHaveBeenCalledWith( expect.any(String), expect.objectContaining({ - tool_name: 'list_tags', + tool_name: 'list_workflow_tags', results: { success: false, error: 'boom' }, }), ); diff --git a/packages/cli/src/modules/mcp/__tests__/mcp-instructions.test.ts b/packages/cli/src/modules/mcp/__tests__/mcp-instructions.test.ts index 5f0ac984332..598284328b1 100644 --- a/packages/cli/src/modules/mcp/__tests__/mcp-instructions.test.ts +++ b/packages/cli/src/modules/mcp/__tests__/mcp-instructions.test.ts @@ -69,6 +69,8 @@ describe('getMcpInstructions', () => { }); expect(instructions).not.toContain('"groups"'); + expect(instructions).not.toContain('does not fail the whole update'); + expect(instructions).not.toContain('skippedOperations'); }); test('omits the groups pointer by default', () => { diff --git a/packages/cli/src/modules/mcp/__tests__/mcp-protected-resource.test.ts b/packages/cli/src/modules/mcp/__tests__/mcp-protected-resource.test.ts index 67da2e5607b..a51d25e295f 100644 --- a/packages/cli/src/modules/mcp/__tests__/mcp-protected-resource.test.ts +++ b/packages/cli/src/modules/mcp/__tests__/mcp-protected-resource.test.ts @@ -1,3 +1,4 @@ +import type { ModuleRegistry } from '@n8n/backend-common'; import type { GlobalConfig } from '@n8n/config'; import { mock } from 'vitest-mock-extended'; @@ -17,25 +18,31 @@ describe('McpProtectedResource', () => { const urlService = mock(); const mcpSettingsService = mock(); const mcpConfig = mock(); + const moduleRegistry = mock(); const resource = new McpProtectedResource( urlService, mcpSettingsService, mcpConfig, makeGlobalConfig(), + moduleRegistry, ); beforeEach(() => { vi.clearAllMocks(); mcpConfig.baseUrl = ''; + moduleRegistry.isActive.mockReturnValue(true); }); describe('getScopeTools', () => { it('should expose the full tool mapping when all features are enabled', () => { const scopeTools = resource.getScopeTools(); + expect(resource.scopes).toContain('agent:read'); + expect(resource.scopes).toContain('agent:write'); expect(scopeTools['workflow:read']).toContain('search_workflows'); expect(scopeTools['workflow:read']).toContain('search_nodes'); - expect(scopeTools['tag:read']).toContain('list_tags'); + expect(scopeTools['agent:read']).toContain('search_agents'); + expect(scopeTools['tag:read']).toContain('list_workflow_tags'); }); it('should drop tools this instance does not expose', () => { @@ -44,18 +51,32 @@ describe('McpProtectedResource', () => { mcpSettingsService, mcpConfig, makeGlobalConfig({ builderEnabled: false, tagsDisabled: true }), + moduleRegistry, ); const scopeTools = limitedResource.getScopeTools(); + expect(limitedResource.scopes).not.toContain('agent:read'); + expect(limitedResource.scopes).not.toContain('agent:write'); expect(scopeTools['workflow:read']).toContain('search_workflows'); // builder-only tools are hidden when the builder is off expect(scopeTools['workflow:read']).not.toContain('search_nodes'); expect(scopeTools['workflow:write']).not.toContain('create_workflow_from_code'); expect(scopeTools['project:read']).toEqual([]); - // list_tags is hidden when tags are disabled + expect(scopeTools['agent:read']).toBeUndefined(); + expect(scopeTools['agent:write']).toBeUndefined(); + // list_workflow_tags is hidden when tags are disabled expect(scopeTools['tag:read']).toEqual([]); }); + + it('should drop agent scopes and tools when the agents module is inactive', () => { + moduleRegistry.isActive.mockReturnValue(false); + + expect(resource.scopes).not.toContain('agent:read'); + expect(resource.scopes).not.toContain('agent:write'); + expect(resource.getScopeTools()).not.toHaveProperty('agent:read'); + expect(resource.getScopeTools()).not.toHaveProperty('agent:write'); + }); }); describe('getResourceUrl', () => { diff --git a/packages/cli/src/modules/mcp/__tests__/mcp-scopes.test.ts b/packages/cli/src/modules/mcp/__tests__/mcp-scopes.test.ts index 744c263949d..dc40b2b1526 100644 --- a/packages/cli/src/modules/mcp/__tests__/mcp-scopes.test.ts +++ b/packages/cli/src/modules/mcp/__tests__/mcp-scopes.test.ts @@ -1,4 +1,4 @@ -import { LicenseState } from '@n8n/backend-common'; +import { LicenseState, ModuleRegistry } from '@n8n/backend-common'; import { mockInstance, mockLogger } from '@n8n/backend-test-utils'; import { ExecutionsConfig, GlobalConfig, WorkflowsConfig } from '@n8n/config'; import { @@ -44,7 +44,7 @@ import { WorkflowHistoryService } from '@/workflows/workflow-history/workflow-hi import { WorkflowPublishedDataService } from '@/workflows/workflow-published-data.service'; import { WorkflowService } from '@/workflows/workflow.service'; -import { BUILDER_TOOLS, getAllowedToolNames, TOOLS_BY_SCOPE } from '../mcp-scopes'; +import { AGENT_TOOLS, BUILDER_TOOLS, getAllowedToolNames, TOOLS_BY_SCOPE } from '../mcp-scopes'; import { McpService, type McpFeatureFlags } from '../mcp.service'; const ALL_MAPPED_TOOLS = new Set(Object.values(TOOLS_BY_SCOPE).flat()); @@ -69,12 +69,22 @@ describe('getAllowedToolNames', () => { it('unions the tools of all granted scopes', () => { const allowed = getAllowedToolNames(['execution:read', 'tag:read']); - expect(allowed).toEqual(new Set(['get_execution', 'search_executions', 'list_tags'])); + expect(allowed).toEqual( + new Set(['get_workflow_execution', 'search_workflow_executions', 'list_workflow_tags']), + ); }); it('ignores unknown scopes', () => { expect(getAllowedToolNames(['tool:listWorkflows', 'openid'])).toEqual(new Set()); }); + + it('allows integration updates and publishing with agent:write', () => { + const allowed = getAllowedToolNames(['agent:write']); + + expect(allowed).toContain('update_agent_integration'); + expect(allowed).toContain('publish_agent'); + expect(allowed).toContain('unpublish_agent'); + }); }); describe('McpService scope enforcement', () => { @@ -125,6 +135,7 @@ describe('McpService scope enforcement', () => { mockInstance(AiGatewayService, { isAvailable: vi.fn().mockResolvedValue({ available: false }), }), + mockInstance(ModuleRegistry), ); beforeEach(() => { @@ -144,7 +155,11 @@ describe('McpService scope enforcement', () => { const server = await buildService().getServer(user, mcpFeatureFlags()); const registered = getRegisteredToolNames(server); - const unregistered = [...ALL_MAPPED_TOOLS].filter((name) => !registered.has(name)); + // Agent tools require the agents module (inactive here); their own + // drift guard lives in agent-tools.service.test.ts. + const unregistered = [...ALL_MAPPED_TOOLS].filter( + (name) => !registered.has(name) && !AGENT_TOOLS.has(name), + ); expect(unregistered).toEqual([]); }); diff --git a/packages/cli/src/modules/mcp/__tests__/mcp.controller.test.ts b/packages/cli/src/modules/mcp/__tests__/mcp.controller.test.ts index 4c18589d1be..5c6a9f5aa1c 100644 --- a/packages/cli/src/modules/mcp/__tests__/mcp.controller.test.ts +++ b/packages/cli/src/modules/mcp/__tests__/mcp.controller.test.ts @@ -328,27 +328,24 @@ describe('McpController', () => { expect(res.json).toHaveBeenCalledWith({ message: 'MCP access is disabled' }); }); - test('delegates to transport.handleRequest', async () => { + // The listen stream is unsupported in stateless mode: a GET routed into + // the transport would hang forever, so the route must answer 405 itself. + test('returns 405 without touching the MCP transport', async () => { (mcpSettingsService.getEnabled as Mock).mockResolvedValue(true); - (mcpService.resolveFeatureFlags as Mock).mockResolvedValue({ - mcpApps: { enabled: true, variant: 'variant' }, - canvasGroupsEnabled: false, - }); - (mcpService.getServer as unknown as Mock).mockReturnValue({ - connect: vi.fn().mockResolvedValue(undefined), - close: vi.fn().mockResolvedValue(undefined), - }); - const req = createReq(); const res = createRes(); - await controller.handleGet(req, res); - expect(mcpService.resolveFeatureFlags as Mock).toHaveBeenCalledTimes(1); - expect(mcpService.getServer as unknown as Mock).toHaveBeenCalledWith( - expect.objectContaining({ id: 'user-1' }), - { mcpApps: { enabled: true, variant: 'variant' }, canvasGroupsEnabled: false }, - undefined, - undefined, - ); - expect(mockHandleRequest).toHaveBeenCalledWith(req, res, undefined); + await controller.handleGet(createReq(), res); + expect(res.header).toHaveBeenCalledWith('Allow', 'POST'); + expect(res.status).toHaveBeenCalledWith(405); + expect(res.json).toHaveBeenCalledWith({ + jsonrpc: '2.0', + error: { + code: -32000, + message: 'Method not allowed.', + }, + id: null, + }); + expect(mcpService.getServer as unknown as Mock).not.toHaveBeenCalled(); + expect(mockHandleRequest).not.toHaveBeenCalled(); }); }); }); diff --git a/packages/cli/src/modules/mcp/__tests__/mcp.service.test.ts b/packages/cli/src/modules/mcp/__tests__/mcp.service.test.ts index d8a8da46e37..f3e8905eb17 100644 --- a/packages/cli/src/modules/mcp/__tests__/mcp.service.test.ts +++ b/packages/cli/src/modules/mcp/__tests__/mcp.service.test.ts @@ -1,4 +1,4 @@ -import { LicenseState, type Logger } from '@n8n/backend-common'; +import { LicenseState, ModuleRegistry, type Logger } from '@n8n/backend-common'; import { mockInstance, mockLogger } from '@n8n/backend-test-utils'; import { ExecutionsConfig, GlobalConfig, WorkflowsConfig } from '@n8n/config'; import { @@ -124,6 +124,7 @@ describe('McpService', () => { mockInstance(WorkflowPublishedDataService), mockInstance(SubworkflowPolicyChecker), mockAiGatewayService(), + mockInstance(ModuleRegistry), ); }); @@ -173,6 +174,7 @@ describe('McpService', () => { mockInstance(WorkflowPublishedDataService), mockInstance(SubworkflowPolicyChecker), mockAiGatewayService(), + mockInstance(ModuleRegistry), ); expect(queueMcpService.isQueueMode).toBe(true); @@ -377,6 +379,7 @@ describe('McpService', () => { mockInstance(WorkflowPublishedDataService), mockInstance(SubworkflowPolicyChecker), mockAiGatewayService(), + mockInstance(ModuleRegistry), ); const user = Object.assign(new User(), { id: 'user-1' }); @@ -571,6 +574,7 @@ describe('McpService', () => { mockInstance(WorkflowPublishedDataService), mockInstance(SubworkflowPolicyChecker), mockAiGatewayService(), + mockInstance(ModuleRegistry), ); const server = await service.getServer(user, mcpFeatureFlags()); @@ -622,6 +626,7 @@ describe('McpService', () => { mockInstance(WorkflowPublishedDataService), mockInstance(SubworkflowPolicyChecker), mockAiGatewayService(), + mockInstance(ModuleRegistry), ); const server = await service.getServer(user, mcpFeatureFlags()); @@ -698,6 +703,7 @@ describe('McpService', () => { mockInstance(WorkflowPublishedDataService), mockInstance(SubworkflowPolicyChecker), mockAiGatewayService(), + mockInstance(ModuleRegistry), ); }; diff --git a/packages/cli/src/modules/mcp/__tests__/prepare-workflow-pin-data.tool.test.ts b/packages/cli/src/modules/mcp/__tests__/prepare-workflow-pin-data.tool.test.ts index 459ebcfa88d..d8392747237 100644 --- a/packages/cli/src/modules/mcp/__tests__/prepare-workflow-pin-data.tool.test.ts +++ b/packages/cli/src/modules/mcp/__tests__/prepare-workflow-pin-data.tool.test.ts @@ -95,7 +95,7 @@ describe('prepare-workflow-pin-data MCP tool', () => { logger, ); - expect(tool.name).toBe('prepare_test_pin_data'); + expect(tool.name).toBe('prepare_workflow_pin_data'); expect(tool.config).toBeDefined(); expect(typeof tool.config.description).toBe('string'); expect(tool.config.inputSchema).toBeDefined(); @@ -804,7 +804,7 @@ describe('prepare-workflow-pin-data MCP tool', () => { 'User called mcp tool', expect.objectContaining({ user_id: 'user-1', - tool_name: 'prepare_test_pin_data', + tool_name: 'prepare_workflow_pin_data', parameters: { workflowId: 'wf-1' }, results: expect.objectContaining({ success: true, @@ -831,7 +831,7 @@ describe('prepare-workflow-pin-data MCP tool', () => { expect(telemetry.track).toHaveBeenCalledWith( 'User called mcp tool', expect.objectContaining({ - tool_name: 'prepare_test_pin_data', + tool_name: 'prepare_workflow_pin_data', results: expect.objectContaining({ success: false, error: expect.any(String), diff --git a/packages/cli/src/modules/mcp/__tests__/publish-workflow.tool.test.ts b/packages/cli/src/modules/mcp/__tests__/publish-workflow.tool.test.ts index 965652f57b6..6a793f57dad 100644 --- a/packages/cli/src/modules/mcp/__tests__/publish-workflow.tool.test.ts +++ b/packages/cli/src/modules/mcp/__tests__/publish-workflow.tool.test.ts @@ -4,6 +4,7 @@ import { v4 as uuid } from 'uuid'; import type { Mock } from 'vitest'; import { CollaborationService } from '@/collaboration/collaboration.service'; +import { WorkflowPublishBlockedError } from '@/errors/response-errors/workflow-publish-blocked.error'; import { Telemetry } from '@/telemetry'; import { WorkflowFinderService } from '@/workflows/workflow-finder.service'; import { WorkflowService } from '@/workflows/workflow.service'; @@ -248,6 +249,48 @@ describe('publish-workflow MCP tool', () => { }); describe('error handling', () => { + test.each([ + ['review_pending', 'review-1'], + ['changes_requested', 'review-2'], + ] as const)( + 'returns the review details when publication is blocked by %s', + async (reason, workflowReviewRequestId) => { + const workflow = createWorkflow({ settings: { availableInMCP: true } }); + (workflowFinderService.findWorkflowForUser as Mock).mockResolvedValue(workflow); + (workflowService.activateWorkflow as Mock).mockRejectedValue( + new WorkflowPublishBlockedError({ reason, workflowReviewRequestId }), + ); + + const tool = createPublishWorkflowTool( + user, + workflowFinderService, + workflowService, + telemetry, + collaborationService, + ); + + const result = await tool.handler( + { workflowId: 'wf-1', versionId: undefined }, + {} as Parameters[1], + ); + + expect(result.structuredContent).toEqual({ + success: false, + workflowId: 'wf-1', + activeVersionId: null, + error: expect.any(String), + reason, + workflowReviewRequestId, + }); + expect(telemetry.track).toHaveBeenCalledWith( + 'User called mcp tool', + expect.objectContaining({ + results: expect.objectContaining({ error_reason: reason }), + }), + ); + }, + ); + test('handles WorkflowService errors gracefully', async () => { const workflow = createWorkflow({ settings: { availableInMCP: true } }); (workflowFinderService.findWorkflowForUser as Mock).mockResolvedValue(workflow); @@ -268,7 +311,7 @@ describe('publish-workflow MCP tool', () => { {} as Parameters[1], ); - expect(result.structuredContent).toMatchObject({ + expect(result.structuredContent).toEqual({ success: false, workflowId: 'wf-1', activeVersionId: null, diff --git a/packages/cli/src/modules/mcp/__tests__/search-executions.tool.test.ts b/packages/cli/src/modules/mcp/__tests__/search-executions.tool.test.ts index 0457a7c3b55..318526b4790 100644 --- a/packages/cli/src/modules/mcp/__tests__/search-executions.tool.test.ts +++ b/packages/cli/src/modules/mcp/__tests__/search-executions.tool.test.ts @@ -60,7 +60,7 @@ describe('search-executions MCP tool', () => { test('creates tool with correct metadata', () => { const tool = createTool(); - expect(tool.name).toBe('search_executions'); + expect(tool.name).toBe('search_workflow_executions'); expect(tool.config.annotations?.readOnlyHint).toBe(true); expect(typeof tool.handler).toBe('function'); }); @@ -196,7 +196,7 @@ describe('search-executions MCP tool', () => { 'User called mcp tool', expect.objectContaining({ user_id: 'user-1', - tool_name: 'search_executions', + tool_name: 'search_workflow_executions', results: { success: true, data: { count: 1, estimated: false } }, }), ); @@ -220,7 +220,7 @@ describe('search-executions MCP tool', () => { expect(telemetry.track).toHaveBeenCalledWith( 'User called mcp tool', expect.objectContaining({ - tool_name: 'search_executions', + tool_name: 'search_workflow_executions', results: { success: false, error: 'DB connection lost' }, }), ); diff --git a/packages/cli/src/modules/mcp/__tests__/search-workflow-nodes.tool.test.ts b/packages/cli/src/modules/mcp/__tests__/search-workflow-nodes.tool.test.ts index 52496ac3eda..bc0a10bde5a 100644 --- a/packages/cli/src/modules/mcp/__tests__/search-workflow-nodes.tool.test.ts +++ b/packages/cli/src/modules/mcp/__tests__/search-workflow-nodes.tool.test.ts @@ -57,7 +57,7 @@ describe('search-workflow-nodes MCP tool', () => { const tool = createTool(); const result = await tool.handler({ queries: ['gmail', 'missing-node'] }, {} as never); - expect(nodeCatalogService.searchNodes).toHaveBeenCalledWith(['gmail', 'missing-node']); + expect(nodeCatalogService.searchNodes).toHaveBeenCalledWith(['gmail', 'missing-node'], {}); expect(result.content).toEqual([{ type: 'text', text: 'search-result' }]); expect(result.structuredContent).toEqual({ results: 'search-result' }); expect(telemetry.track).toHaveBeenCalledWith( diff --git a/packages/cli/src/modules/mcp/__tests__/update-workflow.tool.test.ts b/packages/cli/src/modules/mcp/__tests__/update-workflow.tool.test.ts index c156fed1223..170cd7b5b34 100644 --- a/packages/cli/src/modules/mcp/__tests__/update-workflow.tool.test.ts +++ b/packages/cli/src/modules/mcp/__tests__/update-workflow.tool.test.ts @@ -26,6 +26,7 @@ import { WorkflowPublishedDataService } from '@/workflows/workflow-published-dat import { WorkflowService } from '@/workflows/workflow.service'; import { createUpdateWorkflowTool } from '../tools/workflow-builder/update-workflow.tool'; +import { NON_FATAL_OPERATION_TYPES } from '../tools/workflow-builder/workflow-operations'; const mockAutoPopulateNodeCredentials = vi.fn(); const mockTrackAutoassignOutcomes = vi.fn(); @@ -92,7 +93,7 @@ describe('update-workflow MCP tool', () => { let dataTableOps: DataTableOpsMock; let tagService: TagService; let findOrCreateByNamesMock: Mock; - let findByNamesMock: Mock; + let getByNamesMock: Mock; let globalConfig: GlobalConfig; let subworkflowPolicyChecker: SubworkflowPolicyChecker; let policyCheckMock: Mock; @@ -142,12 +143,14 @@ describe('update-workflow MCP tool', () => { nodeTypes = mockInstance(NodeTypes); nodeTypes.getByNameAndVersion.mockImplementation(((type: string) => { if (type === '@n8n/n8n-nodes-langchain.agent') { - return { description: { outputs: [NodeConnectionTypes.Main] } }; + return { description: { group: ['transform'], outputs: [NodeConnectionTypes.Main] } }; } if (type === '@n8n/n8n-nodes-langchain.agentTool') { - return { description: { outputs: [NodeConnectionTypes.AiTool] } }; + return { description: { group: ['transform'], outputs: [NodeConnectionTypes.AiTool] } }; } - return { description: {} }; + // The group validator resolves trigger-ness via description.group; an + // empty (non-trigger) group keeps that check from crashing on `undefined`. + return { description: { group: ['transform'] } }; }) as typeof nodeTypes.getByNameAndVersion); collaborationService = mockInstance(CollaborationService, { ensureWorkflowEditable: vi.fn().mockResolvedValue(undefined), @@ -165,10 +168,10 @@ describe('update-workflow MCP tool', () => { }; findOrCreateByNamesMock = vi.fn(); - findByNamesMock = vi.fn(); + getByNamesMock = vi.fn(); tagService = mockInstance(TagService, { findOrCreateByNames: findOrCreateByNamesMock, - findByNames: findByNamesMock, + getByNames: getByNamesMock, }); globalConfig = mockInstance(GlobalConfig, { tags: { disabled: false }, @@ -248,6 +251,28 @@ describe('update-workflow MCP tool', () => { }); }); + describe('docs mention the non-fatal group exception only when canvasGroupsEnabled', () => { + const operationsDescription = (tool: ReturnType) => + (tool.config.inputSchema!.operations as z.ZodTypeAny).description; + + test('flag off: neither the tool description nor the operations field mention the exception', () => { + const tool = createTool(); + expect(tool.config.description).not.toContain('one exception to "atomically"'); + expect(operationsDescription(tool)).not.toContain('except node-group operations'); + }); + + test('flag on: both the tool description and the operations field name the non-fatal operation types', () => { + const tool = createTool({ canvasGroupsEnabled: true }); + const nonFatalTypesList = [...NON_FATAL_OPERATION_TYPES].join(', '); + expect(tool.config.description).toContain( + `Node-group operations (${nonFatalTypesList}) are the one exception to "atomically"`, + ); + expect(operationsDescription(tool)).toContain( + `except node-group operations (${nonFatalTypesList})`, + ); + }); + }); + describe('output schema conformance', () => { // Regression for ADO-5448 / GH #32503: the error path returned // `structuredContent: { error }`, which failed validation against the @@ -474,6 +499,807 @@ describe('update-workflow MCP tool', () => { }); }); + describe('group operation structural validation', () => { + const buildWorkflowWithTrigger = () => + Object.assign(new WorkflowEntity(), { + id: 'wf-1', + name: 'Existing', + settings: { availableInMCP: true }, + nodes: [ + makeNode({ id: 'trigger', name: 'Trigger', type: 'n8n-nodes-base.manualTrigger' }), + makeNode({ id: 'a', name: 'A', position: [200, 0] }), + makeNode({ id: 'b', name: 'B', position: [400, 0] }), + ], + connections: { + Trigger: { main: [[{ node: 'A', type: 'main', index: 0 }]] }, + A: { main: [[{ node: 'B', type: 'main', index: 0 }]] }, + } as IConnections, + }); + + beforeEach(() => { + // The group validator resolves trigger-ness via description.group. + nodeTypes.getByNameAndVersion.mockImplementation(((type: string) => { + if (type === 'n8n-nodes-base.manualTrigger') { + return { description: { group: ['trigger'], outputs: [NodeConnectionTypes.Main] } }; + } + + if (type === '@n8n/n8n-nodes-langchain.agent') { + return { description: { group: ['transform'], outputs: [NodeConnectionTypes.Main] } }; + } + + if (type === '@n8n/n8n-nodes-langchain.agentTool') { + return { description: { group: ['transform'], outputs: [NodeConnectionTypes.AiTool] } }; + } + return { description: { group: ['transform'], outputs: [NodeConnectionTypes.Main] } }; + }) as typeof nodeTypes.getByNameAndVersion); + }); + + describe('canvasGroupsEnabled off', () => { + test('a structurally invalid group is not pre-checked; a persistence-layer rejection still surfaces as isError', async () => { + findWorkflowMock.mockResolvedValue(buildWorkflowWithTrigger()); + updateMock.mockRejectedValueOnce( + new Error('Node group "Group" cannot contain trigger nodes: Trigger.'), + ); + + const result = await callHandler({ + workflowId: 'wf-1', + operations: [ + { + type: 'setNodeGroups', + nodeGroups: [{ id: 'g1', name: 'Group', nodeNames: ['Trigger', 'A'] }], + }, + ], + }); + + expect(result.isError).toBe(true); + const response = parseResult(result); + expect(response.error).toContain('cannot contain trigger nodes'); + }); + + test('a non-group operation that disconnects an existing group is not pre-checked either', async () => { + findWorkflowMock.mockResolvedValue( + Object.assign(new WorkflowEntity(), { + id: 'wf-1', + name: 'Existing', + settings: { availableInMCP: true }, + nodes: [makeNode({ id: 'a', name: 'A' }), makeNode({ id: 'b', name: 'B' })], + connections: { + A: { main: [[{ node: 'B', type: 'main', index: 0 }]] }, + } as IConnections, + nodeGroups: [{ id: 'g1', name: 'Group', nodeIds: ['a', 'b'] }], + }), + ); + + const result = await callHandler({ + workflowId: 'wf-1', + operations: [{ type: 'removeConnection', source: 'A', target: 'B' }], + }); + + expect(result.isError).toBeUndefined(); + // nodeGroups isn't touched or re-checked with the flag off — omitted from + // the persisted payload exactly as before this fix (preserve-on-omit). + const saved = updateMock.mock.calls[0][1] as WorkflowEntity; + expect('nodeGroups' in saved).toBe(false); + }); + }); + + describe('canvasGroupsEnabled on', () => { + const createOnTool = () => createTool({ canvasGroupsEnabled: true }); + + test('a group with a trigger inside is skipped while the rest of the update saves', async () => { + findWorkflowMock.mockResolvedValue(buildWorkflowWithTrigger()); + + const result = await callHandler( + { + workflowId: 'wf-1', + operations: [ + { type: 'updateNodeParameters', nodeName: 'B', parameters: { url: 'https://new' } }, + { + type: 'setNodeGroups', + nodeGroups: [{ id: 'g1', name: 'Group', nodeNames: ['Trigger', 'A'] }], + }, + ], + }, + createOnTool(), + ); + + expect(result.isError).toBeUndefined(); + + const saved = updateMock.mock.calls[0][1] as WorkflowEntity; + expect(saved.nodes.find((n) => n.name === 'B')!.parameters).toEqual({ + url: 'https://new', + }); + expect(saved.nodeGroups ?? []).toEqual([]); + + const response = parseResult(result); + expect(response.skippedOperations).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + reason: expect.stringContaining('cannot contain trigger nodes') as string, + }), + ]), + ); + }); + + test('a non-group operation that disconnects an existing group is caught: the group is removed and reported in removedGroups, the rest of the update still saves', async () => { + // A removeConnection never touches nodeGroups directly, so + // nodeGroupsChanged alone would miss this — the structural check must + // run whenever the workflow HAS groups, not only when a group op ran. + findWorkflowMock.mockResolvedValue( + Object.assign(new WorkflowEntity(), { + id: 'wf-1', + name: 'Existing', + settings: { availableInMCP: true }, + nodes: [makeNode({ id: 'a', name: 'A' }), makeNode({ id: 'b', name: 'B' })], + connections: { + A: { main: [[{ node: 'B', type: 'main', index: 0 }]] }, + } as IConnections, + nodeGroups: [{ id: 'g1', name: 'Group', nodeIds: ['a', 'b'] }], + }), + ); + + const result = await callHandler( + { + workflowId: 'wf-1', + operations: [{ type: 'removeConnection', source: 'A', target: 'B' }], + }, + createOnTool(), + ); + + expect(result.isError).toBeUndefined(); + + const saved = updateMock.mock.calls[0][1] as WorkflowEntity; + // The connection removal itself still applied... + expect(saved.connections.A?.main?.[0] ?? []).toEqual([]); + // ...and the now-disconnected group was dropped, not silently re-sent as-is. + expect(saved.nodeGroups).toEqual([]); + + const response = parseResult(result); + // No operation was skipped: the caller asked for a removeConnection and got + // it. The group is collateral damage, so it belongs in removedGroups. + expect(response.skippedOperations ?? []).toEqual([]); + expect(response.removedGroups).toEqual([ + { + groupName: 'Group', + reason: expect.stringContaining('single connected subgraph') as string, + }, + ]); + }); + + describe('a submitted group overlapping an existing one', () => { + const buildWorkflowWithGroup = () => + Object.assign(buildWorkflowWithTrigger(), { + nodeGroups: [{ id: 'g1', name: 'Existing group', nodeIds: ['a', 'b'] }], + }); + + test('is skipped without taking the existing group down with it', async () => { + // The validator flags both sides of an overlap; only the submitted one + // may go, since the operation that caused it was rejected. + findWorkflowMock.mockResolvedValue(buildWorkflowWithGroup()); + + const result = await callHandler( + { + workflowId: 'wf-1', + operations: [ + { type: 'addNodeGroup', name: 'Overlapping', nodeNames: ['Trigger', 'A'] }, + ], + }, + createOnTool(), + ); + + expect(result.isError).toBeUndefined(); + + const saved = updateMock.mock.calls[0][1] as WorkflowEntity; + expect(saved.nodeGroups).toEqual([ + { id: 'g1', name: 'Existing group', nodeIds: ['a', 'b'] }, + ]); + + const response = parseResult(result); + expect(response.skippedOperations).toEqual([ + { + opIndex: 0, + type: 'addNodeGroup', + reason: expect.stringContaining('belongs to multiple groups') as string, + }, + ]); + expect(response.removedGroups).toBeUndefined(); + }); + + test('does not stop the other operations in the same batch from saving', async () => { + findWorkflowMock.mockResolvedValue(buildWorkflowWithGroup()); + + const result = await callHandler( + { + workflowId: 'wf-1', + operations: [ + { type: 'renameNode', oldName: 'B', newName: 'B renamed' }, + { type: 'addNodeGroup', name: 'Overlapping', nodeNames: ['Trigger', 'A'] }, + ], + }, + createOnTool(), + ); + + expect(result.isError).toBeUndefined(); + + const saved = updateMock.mock.calls[0][1] as WorkflowEntity; + expect(saved.nodes.map((n) => n.name)).toContain('B renamed'); + expect(saved.nodeGroups).toHaveLength(1); + + const response = parseResult(result); + expect(response.appliedOperations).toBe(1); + expect(response.skippedOperations).toEqual([ + expect.objectContaining({ opIndex: 1, type: 'addNodeGroup' }), + ]); + expect(response.removedGroups).toBeUndefined(); + }); + + test('drops both when the same setNodeGroups submitted both of them', async () => { + // Neither group has priority here: the caller wrote both in one + // operation, so both belong in skippedOperations, not removedGroups. + findWorkflowMock.mockResolvedValue(buildWorkflowWithGroup()); + + const result = await callHandler( + { + workflowId: 'wf-1', + operations: [ + { + type: 'setNodeGroups', + nodeGroups: [ + { id: 'g1', name: 'First', nodeNames: ['A', 'B'] }, + { id: 'g2', name: 'Second', nodeNames: ['A'] }, + ], + }, + ], + }, + createOnTool(), + ); + + expect(result.isError).toBeUndefined(); + + const saved = updateMock.mock.calls[0][1] as WorkflowEntity; + expect(saved.nodeGroups).toEqual([]); + + const response = parseResult(result); + expect(response.removedGroups).toBeUndefined(); + expect(response.skippedOperations).toHaveLength(2); + }); + }); + + test('a group that splits an AI sub-node from its Agent is skipped', async () => { + findWorkflowMock.mockResolvedValue( + Object.assign(new WorkflowEntity(), { + id: 'wf-1', + name: 'Existing', + settings: { availableInMCP: true }, + nodes: [ + makeNode({ id: 'agent', name: 'Agent', type: '@n8n/n8n-nodes-langchain.agent' }), + makeNode({ + id: 'model', + name: 'Model', + type: '@n8n/n8n-nodes-langchain.agentTool', + position: [200, 0], + }), + ], + connections: { + Model: { + ai_languageModel: [[{ node: 'Agent', type: 'ai_languageModel', index: 0 }]], + }, + } as IConnections, + }), + ); + + const result = await callHandler( + { + workflowId: 'wf-1', + operations: [{ type: 'addNodeGroup', name: 'Group', nodeNames: ['Agent'] }], + }, + createOnTool(), + ); + + expect(result.isError).toBeUndefined(); + + const saved = updateMock.mock.calls[0][1] as WorkflowEntity; + expect(saved.nodeGroups ?? []).toEqual([]); + + const response = parseResult(result); + // The op that actually created this group was addNodeGroup, not setNodeGroups — + // the reported type must reflect that, not a hardcoded guess. + expect(response.skippedOperations).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + type: 'addNodeGroup', + reason: expect.stringContaining('cannot cross the') as string, + }), + ]), + ); + }); + + test('a group whose nodes form a disconnected subgraph is skipped', async () => { + findWorkflowMock.mockResolvedValue( + Object.assign(new WorkflowEntity(), { + id: 'wf-1', + name: 'Existing', + settings: { availableInMCP: true }, + nodes: [ + makeNode({ id: 'a', name: 'A' }), + makeNode({ id: 'b', name: 'B', position: [400, 0] }), + ], + connections: {} as IConnections, + }), + ); + + const result = await callHandler( + { + workflowId: 'wf-1', + operations: [{ type: 'addNodeGroup', name: 'Group', nodeNames: ['A', 'B'] }], + }, + createOnTool(), + ); + + expect(result.isError).toBeUndefined(); + + const saved = updateMock.mock.calls[0][1] as WorkflowEntity; + expect(saved.nodeGroups ?? []).toEqual([]); + + const response = parseResult(result); + expect(response.skippedOperations).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + reason: expect.stringContaining('single connected subgraph') as string, + }), + ]), + ); + }); + + test('one invalid group among valid ones in the same setNodeGroups only drops the invalid one', async () => { + findWorkflowMock.mockResolvedValue(buildWorkflowWithTrigger()); + + const result = await callHandler( + { + workflowId: 'wf-1', + operations: [ + { + type: 'setNodeGroups', + nodeGroups: [ + { id: 'g1', name: 'Bad', nodeNames: ['Trigger'] }, + { id: 'g2', name: 'Good', nodeNames: ['A', 'B'] }, + ], + }, + ], + }, + createOnTool(), + ); + + expect(result.isError).toBeUndefined(); + + const saved = updateMock.mock.calls[0][1] as WorkflowEntity; + expect(saved.nodeGroups).toEqual([{ id: 'g2', name: 'Good', nodeIds: ['a', 'b'] }]); + + const response = parseResult(result); + expect(response.skippedOperations).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + type: 'setNodeGroups', + reason: expect.stringContaining('cannot contain trigger nodes') as string, + }), + ]), + ); + // "Good" was persisted, so the operation did apply — just not in full. + expect(response.appliedOperations).toBe(1); + }); + + test('a setNodeGroups whose every group is dropped is discounted once, not per group', async () => { + // Trigger -> A -> B plus a detached C: one group fails on the trigger and + // the other on connectivity, with no member overlap between them. + findWorkflowMock.mockResolvedValue( + Object.assign(buildWorkflowWithTrigger(), { + nodes: [ + makeNode({ id: 'trigger', name: 'Trigger', type: 'n8n-nodes-base.manualTrigger' }), + makeNode({ id: 'a', name: 'A', position: [200, 0] }), + makeNode({ id: 'b', name: 'B', position: [400, 0] }), + makeNode({ id: 'c', name: 'C', position: [600, 200] }), + ], + }), + ); + + const result = await callHandler( + { + workflowId: 'wf-1', + operations: [ + { + type: 'setNodeGroups', + nodeGroups: [ + { id: 'g1', name: 'Bad', nodeNames: ['Trigger'] }, + { id: 'g2', name: 'AlsoBad', nodeNames: ['A', 'C'] }, + ], + }, + ], + }, + createOnTool(), + ); + + expect(result.isError).toBeUndefined(); + + const response = parseResult(result); + // Two violations, one operation: the count must not go negative. + expect(response.skippedOperations).toHaveLength(2); + expect(response.appliedOperations).toBe(0); + }); + + test('a group made invalid via updateNodeGroup reports updateNodeGroup, not a hardcoded type', async () => { + findWorkflowMock.mockResolvedValue( + Object.assign(buildWorkflowWithTrigger(), { + nodeGroups: [{ id: 'g1', name: 'Group', nodeIds: ['a', 'b'] }], + }), + ); + + const result = await callHandler( + { + workflowId: 'wf-1', + operations: [ + { type: 'updateNodeGroup', groupName: 'Group', nodeNames: ['Trigger', 'A', 'B'] }, + ], + }, + createOnTool(), + ); + + expect(result.isError).toBeUndefined(); + + const saved = updateMock.mock.calls[0][1] as WorkflowEntity; + expect(saved.nodeGroups ?? []).toEqual([]); + + const response = parseResult(result); + expect(response.skippedOperations).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + type: 'updateNodeGroup', + reason: expect.stringContaining('cannot contain trigger nodes') as string, + }), + ]), + ); + }); + + test('when addNodeGroup then updateNodeGroup touch the same group in one batch, the later op wins', async () => { + findWorkflowMock.mockResolvedValue(buildWorkflowWithTrigger()); + + const result = await callHandler( + { + workflowId: 'wf-1', + operations: [ + { type: 'addNodeGroup', name: 'Group', nodeNames: ['A', 'B'] }, + { type: 'updateNodeGroup', groupName: 'Group', nodeNames: ['Trigger', 'A', 'B'] }, + ], + }, + createOnTool(), + ); + + expect(result.isError).toBeUndefined(); + + const saved = updateMock.mock.calls[0][1] as WorkflowEntity; + expect(saved.nodeGroups ?? []).toEqual([]); + + const response = parseResult(result); + // Created by addNodeGroup, then modified by updateNodeGroup — the reported + // type must reflect the LAST op that touched it, not the one that created it. + expect(response.skippedOperations).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + type: 'updateNodeGroup', + reason: expect.stringContaining('cannot contain trigger nodes') as string, + }), + ]), + ); + }); + + test('a group made invalid by removeNode pruning its bridge node is reported in removedGroups, not as a skipped removeNode', async () => { + // Trigger -> A -> B -> C, group {A, B, C}. Removing the bridge node B + // prunes it from the group, leaving {A, C} with no path between them. + findWorkflowMock.mockResolvedValue( + Object.assign(new WorkflowEntity(), { + id: 'wf-1', + name: 'Existing', + settings: { availableInMCP: true }, + nodes: [ + makeNode({ id: 'trigger', name: 'Trigger', type: 'n8n-nodes-base.manualTrigger' }), + makeNode({ id: 'a', name: 'A', position: [200, 0] }), + makeNode({ id: 'b', name: 'B', position: [400, 0] }), + makeNode({ id: 'c', name: 'C', position: [600, 0] }), + ], + connections: { + Trigger: { main: [[{ node: 'A', type: 'main', index: 0 }]] }, + A: { main: [[{ node: 'B', type: 'main', index: 0 }]] }, + B: { main: [[{ node: 'C', type: 'main', index: 0 }]] }, + } as IConnections, + nodeGroups: [{ id: 'g1', name: 'Group', nodeIds: ['a', 'b', 'c'] }], + }), + ); + + const result = await callHandler( + { workflowId: 'wf-1', operations: [{ type: 'removeNode', nodeName: 'B' }] }, + createOnTool(), + ); + + expect(result.isError).toBeUndefined(); + + const saved = updateMock.mock.calls[0][1] as WorkflowEntity; + expect(saved.nodeGroups ?? []).toEqual([]); + + const response = parseResult(result); + // The node is gone: the removeNode applied in full. Pruning the group was + // a side effect, so the group's loss is not a skipped operation. + expect(response.skippedOperations ?? []).toEqual([]); + expect(response.appliedOperations).toBe(1); + expect(response.removedGroups).toEqual([ + { + groupName: 'Group', + reason: expect.stringContaining('single connected subgraph') as string, + }, + ]); + }); + + test('adding a node that branches out of an existing group removes the group and reports it as collateral', async () => { + // Trigger -> A -> B, group {A, B}. Branching a new node off A gives the + // group two outgoing boundary connections, breaking single-entry/exit. + findWorkflowMock.mockResolvedValue( + Object.assign(buildWorkflowWithTrigger(), { + nodeGroups: [{ id: 'g1', name: 'Chain', nodeIds: ['a', 'b'] }], + }), + ); + + const result = await callHandler( + { + workflowId: 'wf-1', + operations: [ + { type: 'addNode', node: { name: 'C', type: 'n8n-nodes-base.set', typeVersion: 1 } }, + { type: 'addConnection', source: 'A', target: 'C' }, + ], + }, + createOnTool(), + ); + + expect(result.isError).toBeUndefined(); + + const saved = updateMock.mock.calls[0][1] as WorkflowEntity; + // Both requested operations landed... + expect(saved.nodes.map((n) => n.name)).toContain('C'); + expect(saved.connections.A?.main?.[0]).toEqual( + expect.arrayContaining([expect.objectContaining({ node: 'C' })]), + ); + // ...and only the group was lost. + expect(saved.nodeGroups).toEqual([]); + + const response = parseResult(result); + expect(response.appliedOperations).toBe(2); + expect(response.skippedOperations ?? []).toEqual([]); + expect(response.removedGroups).toEqual([ + { + groupName: 'Chain', + reason: expect.stringContaining('single connected subgraph') as string, + }, + ]); + }); + + test('a group already invalid before this batch is removed and reported, whatever the operations were', async () => { + // Legacy data: basic-only validation (e.g. a git import) lets a group + // with a trigger through, so any later update has to clean it up — + // the same removal the canvas performs on load. + findWorkflowMock.mockResolvedValue( + Object.assign(buildWorkflowWithTrigger(), { + nodeGroups: [{ id: 'g1', name: 'Legacy', nodeIds: ['trigger', 'a'] }], + }), + ); + + const result = await callHandler( + { + workflowId: 'wf-1', + operations: [{ type: 'setNodePosition', nodeName: 'B', position: [50, 50] }], + }, + createOnTool(), + ); + + expect(result.isError).toBeUndefined(); + + const saved = updateMock.mock.calls[0][1] as WorkflowEntity; + expect(saved.nodeGroups).toEqual([]); + + const response = parseResult(result); + expect(response.appliedOperations).toBe(1); + expect(response.skippedOperations ?? []).toEqual([]); + expect(response.removedGroups).toEqual([ + { + groupName: 'Legacy', + reason: expect.stringContaining('cannot contain trigger nodes') as string, + }, + ]); + }); + + test('every skippedOperations entry carries the index of the operation it belongs to', async () => { + findWorkflowMock.mockResolvedValue(buildWorkflowWithTrigger()); + + const result = await callHandler( + { + workflowId: 'wf-1', + operations: [ + { type: 'setNodePosition', nodeName: 'A', position: [10, 10] }, + // Fails the basic checks: unknown member. + { type: 'addNodeGroup', name: 'Missing', nodeNames: ['Nope'] }, + // Passes them, then fails the structural check. + { type: 'addNodeGroup', name: 'WithTrigger', nodeNames: ['Trigger', 'A'] }, + ], + }, + createOnTool(), + ); + + expect(result.isError).toBeUndefined(); + + const response = parseResult(result); + expect(response.skippedOperations).toEqual([ + expect.objectContaining({ opIndex: 1, type: 'addNodeGroup' }), + expect.objectContaining({ opIndex: 2, type: 'addNodeGroup' }), + ]); + expect(response.appliedOperations).toBe(1); + }); + + test('all groups valid: no skipped operations are reported', async () => { + const result = await callHandler( + { + workflowId: 'wf-1', + operations: [{ type: 'addNodeGroup', name: 'Group', nodeNames: ['A', 'B'] }], + }, + createOnTool(), + ); + + expect(result.isError).toBeUndefined(); + + const saved = updateMock.mock.calls[0][1] as WorkflowEntity; + expect(saved.nodeGroups).toEqual([ + { id: expect.any(String) as string, name: 'Group', nodeIds: ['a', 'b'] }, + ]); + + const response = parseResult(result); + expect(response.skippedOperations ?? []).toEqual([]); + }); + + test('no group operation in the batch: structural validation does not run', async () => { + const result = await callHandler( + { + workflowId: 'wf-1', + operations: [{ type: 'setNodePosition', nodeName: 'A', position: [50, 50] }], + }, + createOnTool(), + ); + + expect(result.isError).toBeUndefined(); + + const saved = updateMock.mock.calls[0][1] as WorkflowEntity; + expect('nodeGroups' in saved).toBe(false); + }); + + test('the response reports skippedOperations with a human-readable reason', async () => { + findWorkflowMock.mockResolvedValue(buildWorkflowWithTrigger()); + + const result = await callHandler( + { + workflowId: 'wf-1', + operations: [ + { + type: 'setNodeGroups', + nodeGroups: [{ id: 'g1', name: 'Group', nodeNames: ['Trigger', 'A'] }], + }, + ], + }, + createOnTool(), + ); + + const response = parseResult(result); + expect(response.skippedOperations).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + reason: expect.stringContaining('cannot contain trigger nodes') as string, + }), + ]), + ); + }); + }); + }); + + describe('appliedOperations count', () => { + test('excludes an operation skipped for a basic validation failure (Part A)', async () => { + const result = await callHandler( + { + workflowId: 'wf-1', + operations: [ + { type: 'updateNodeParameters', nodeName: 'B', parameters: { url: 'https://new' } }, + { type: 'addNodeGroup', name: 'Group', nodeNames: ['Missing'] }, + ], + }, + createTool({ canvasGroupsEnabled: true }), + ); + + expect(result.isError).toBeUndefined(); + const response = parseResult(result); + expect(response.skippedOperations).toHaveLength(1); + expect(response.appliedOperations).toBe(1); + }); + + test('does not exclude a submitted operation whose unrelated side effect removed a group (Part B)', async () => { + // The removeConnection operation itself ran successfully — only the + // pre-existing, untouched group it indirectly broke gets dropped. + findWorkflowMock.mockResolvedValue( + Object.assign(new WorkflowEntity(), { + id: 'wf-1', + name: 'Existing', + settings: { availableInMCP: true }, + nodes: [makeNode({ id: 'a', name: 'A' }), makeNode({ id: 'b', name: 'B' })], + connections: { + A: { main: [[{ node: 'B', type: 'main', index: 0 }]] }, + } as IConnections, + nodeGroups: [{ id: 'g1', name: 'Group', nodeIds: ['a', 'b'] }], + }), + ); + + const result = await callHandler( + { + workflowId: 'wf-1', + operations: [{ type: 'removeConnection', source: 'A', target: 'B' }], + }, + createTool({ canvasGroupsEnabled: true }), + ); + + expect(result.isError).toBeUndefined(); + const response = parseResult(result); + expect(response.skippedOperations ?? []).toEqual([]); + expect(response.removedGroups).toHaveLength(1); + expect(response.appliedOperations).toBe(1); + }); + + test('excludes a group operation whose group the structural check dropped', async () => { + // The whole point of the addNodeGroup was that group; nothing of it + // survived the save, so it must not be counted as applied. + findWorkflowMock.mockResolvedValue( + Object.assign(new WorkflowEntity(), { + id: 'wf-1', + name: 'Existing', + settings: { availableInMCP: true }, + nodes: [makeNode({ id: 'a', name: 'A' }), makeNode({ id: 'b', name: 'B' })], + connections: {} as IConnections, + }), + ); + + const result = await callHandler( + { + workflowId: 'wf-1', + operations: [{ type: 'addNodeGroup', name: 'Group', nodeNames: ['A', 'B'] }], + }, + createTool({ canvasGroupsEnabled: true }), + ); + + expect(result.isError).toBeUndefined(); + const response = parseResult(result); + expect(response.skippedOperations).toEqual([ + { + opIndex: 0, + type: 'addNodeGroup', + reason: expect.stringContaining('single connected subgraph') as string, + }, + ]); + expect(response.removedGroups ?? []).toEqual([]); + expect(response.appliedOperations).toBe(0); + }); + + test('counts all operations when nothing is skipped', async () => { + const result = await callHandler({ + workflowId: 'wf-1', + operations: [ + { type: 'updateNodeParameters', nodeName: 'B', parameters: { url: 'https://new' } }, + { type: 'setNodePosition', nodeName: 'A', position: [50, 50] }, + ], + }); + + expect(parseResult(result).appliedOperations).toBe(2); + }); + }); + describe('handler', () => { test('applies updateNodeParameters and saves the workflow', async () => { const result = await callHandler({ @@ -2257,6 +3083,18 @@ describe('update-workflow MCP tool', () => { expect(updateOptions.tagIds.sort()).toEqual(['tag-0', 'tag-new']); }); + test('collapses case-duplicate tag names before resolving', async () => { + findWorkflowMock.mockResolvedValue(workflowWithTags([])); + findOrCreateByNamesMock.mockResolvedValue([{ id: 'tag-0', name: 'Critical' }]); + + await callHandler({ + workflowId: 'wf-1', + operations: [{ type: 'addTags', names: ['Critical', ' critical ', 'CRITICAL'] }], + }); + + expect(findOrCreateByNamesMock).toHaveBeenCalledWith(['Critical']); + }); + test('removeTags drops names from the resolved set', async () => { findWorkflowMock.mockResolvedValue(workflowWithTags(['production', 'critical'])); findOrCreateByNamesMock.mockResolvedValue([{ id: 'tag-0', name: 'production' }]); @@ -2320,7 +3158,7 @@ describe('update-workflow MCP tool', () => { test('without tag:create scope, attaches only existing tags', async () => { const memberUser = userWithScopes([]); findWorkflowMock.mockResolvedValue(workflowWithTags([])); - findByNamesMock.mockResolvedValue([{ id: 'tag-existing', name: 'production' }]); + getByNamesMock.mockResolvedValue([{ id: 'tag-existing', name: 'production' }]); const tool = createUpdateWorkflowTool( memberUser, @@ -2348,16 +3186,53 @@ describe('update-workflow MCP tool', () => { tool, ); - expect(findByNamesMock).toHaveBeenCalledWith(['production']); + expect(getByNamesMock).toHaveBeenCalledWith(['production']); expect(findOrCreateByNamesMock).not.toHaveBeenCalled(); const [, , , updateOptions] = updateMock.mock.calls[0]; expect(updateOptions.tagIds).toEqual(['tag-existing']); }); + test('without tag:create scope, case-duplicate input still attaches the existing tag', async () => { + const memberUser = userWithScopes([]); + findWorkflowMock.mockResolvedValue(workflowWithTags([])); + getByNamesMock.mockResolvedValue([{ id: 'tag-existing', name: 'Prod' }]); + + const tool = createUpdateWorkflowTool( + memberUser, + workflowFinderService, + workflowService, + urlService, + telemetry, + nodeTypes, + credentialsService, + sharedWorkflowRepository, + collaborationService, + dataTableOps as never, + tagService, + globalConfig, + subworkflowPolicyChecker, + workflowPublishedDataService, + aiGatewayService, + ); + + const result = await callHandler( + { + workflowId: 'wf-1', + operations: [{ type: 'addTags', names: ['Prod', 'prod'] }], + }, + tool, + ); + + expect(result.isError).toBeUndefined(); + expect(getByNamesMock).toHaveBeenCalledWith(['Prod']); + const [, , , updateOptions] = updateMock.mock.calls[0]; + expect(updateOptions.tagIds).toEqual(['tag-existing']); + }); + test('without tag:create scope, fails when a tag name does not exist', async () => { const memberUser = userWithScopes([]); findWorkflowMock.mockResolvedValue(workflowWithTags([])); - findByNamesMock.mockResolvedValue([{ id: 'tag-existing', name: 'production' }]); + getByNamesMock.mockResolvedValue([{ id: 'tag-existing', name: 'production' }]); const tool = createUpdateWorkflowTool( memberUser, diff --git a/packages/cli/src/modules/mcp/__tests__/validate-workflow-code.tool.test.ts b/packages/cli/src/modules/mcp/__tests__/validate-workflow-code.tool.test.ts index 036a8e1c4da..531d1c86920 100644 --- a/packages/cli/src/modules/mcp/__tests__/validate-workflow-code.tool.test.ts +++ b/packages/cli/src/modules/mcp/__tests__/validate-workflow-code.tool.test.ts @@ -381,11 +381,11 @@ describe('validate-workflow-code MCP tool', () => { expect(result.isError).toBe(true); expect(response).toEqual({ valid: false, - errors: ['Node group "Group" (g1) cannot contain trigger nodes: Trigger.'], + errors: ['Node group "Group" cannot contain trigger nodes: Trigger.'], }); expect(trackedResults()).toEqual({ success: false, - error: 'Node group "Group" (g1) cannot contain trigger nodes: Trigger.', + error: 'Node group "Group" cannot contain trigger nodes: Trigger.', data: { groupCount: 1, groupViolationCount: 1, diff --git a/packages/cli/src/modules/mcp/__tests__/webhook-utils.test.ts b/packages/cli/src/modules/mcp/__tests__/webhook-utils.test.ts index ead27ea3cbb..7b8c69dc194 100644 --- a/packages/cli/src/modules/mcp/__tests__/webhook-utils.test.ts +++ b/packages/cli/src/modules/mcp/__tests__/webhook-utils.test.ts @@ -14,7 +14,7 @@ import { mock } from 'vitest-mock-extended'; import { CredentialsService } from '@/credentials/credentials.service'; -import { getWebhookDetails } from '../tools/webhook-utils'; +import { getTriggerDetails, getWebhookDetails } from '../tools/webhook-utils'; const mockCredentialsService = ( impl: (id: string) => ICredentialDataDecryptedObject | Promise, @@ -308,3 +308,54 @@ describe('getWebhookDetails', () => { expect(resDefault).toContain('Returns the JSON data of the first entry of the last node'); }); }); + +describe('getTriggerDetails', () => { + const user = createUser(); + const baseUrl = 'https://example.com'; + const workflowId = 'wf-1'; + const endpoints = { webhook: 'webhook', webhookTest: 'webhook-test' }; + const credentialsService = mockCredentialsService(() => ({})); + + const createTriggerNode = (overrides: Partial = {}): INode => ({ + id: '1', + name: 'Gmail Trigger', + type: 'n8n-nodes-base.gmailTrigger', + typeVersion: 1.4, + position: [0, 0], + parameters: {}, + ...overrides, + }); + + it('reports manual-only when the workflow has no triggers at all', async () => { + const res = await getTriggerDetails( + user, + [], + [], + baseUrl, + credentialsService, + nodeTypes, + endpoints, + workflowId, + ); + expect(res).toBe( + 'This workflow has no production triggers (Schedule, Webhook, Form, or Chat). It can only be executed in manual mode.', + ); + }); + + it('clarifies when the workflow has triggers that MCP cannot execute directly', async () => { + const res = await getTriggerDetails( + user, + [], + [createTriggerNode()], + baseUrl, + credentialsService, + nodeTypes, + endpoints, + workflowId, + ); + expect(res).toContain('not supported for direct execution through MCP: Gmail Trigger'); + expect(res).toContain('cannot be executed through MCP'); + expect(res).not.toContain('no production triggers'); + expect(res).not.toContain('manual mode'); + }); +}); diff --git a/packages/cli/src/modules/mcp/__tests__/workflow-operations.test.ts b/packages/cli/src/modules/mcp/__tests__/workflow-operations.test.ts index 2cc670bd5f4..d9d8da06417 100644 --- a/packages/cli/src/modules/mcp/__tests__/workflow-operations.test.ts +++ b/packages/cli/src/modules/mcp/__tests__/workflow-operations.test.ts @@ -1395,6 +1395,32 @@ describe('applyOperations', () => { applyOperations(wf, [{ type: 'updateNodeGroup', groupName: 'Group', newName: 'Renamed' }]); expect(wf.nodeGroups[0].name).toBe('Group'); }); + + test('leaves the group untouched when a later field fails validation', () => { + // Valid nodeNames combined with a colliding newName: since a failing group + // op is skipped rather than fatal, applying the membership before checking + // the name would persist a change the skip report says never happened. + const result = applyOperations( + groupedWorkflow(), + [{ type: 'updateNodeGroup', groupName: 'Group', nodeNames: ['A', 'B'], newName: 'Other' }], + { canvasGroupsEnabled: true }, + ); + expect(result.success).toBe(true); + if (!result.success) return; + expect(result.skippedOperations).toEqual([ + { + opIndex: 0, + type: 'updateNodeGroup', + reason: "a node group named 'Other' already exists", + }, + ]); + expect(result.workflow.nodeGroups![0]).toEqual({ + id: 'g1', + name: 'Group', + nodeIds: ['a'], + description: 'Old description', + }); + }); }); describe('nodeGroupsChanged', () => { @@ -1415,6 +1441,230 @@ describe('applyOperations', () => { }); }); + describe('groupOperations', () => { + // Only groups the batch explicitly asked for belong here: it's what lets a + // caller tell "your group operation failed" from "an unrelated edit + // invalidated a group that was already there". + const withGroup = () => ({ + ...baseWorkflow(), + nodeGroups: [{ id: 'g1', name: 'Group', nodeIds: ['a', 'b'] }], + }); + + test('is empty when the workflow arrives with groups and no group operation runs', () => { + const result = applyOperations(withGroup(), [ + { type: 'setNodePosition', nodeName: 'A', position: [10, 10] }, + ]); + expect(result.success).toBe(true); + if (!result.success) return; + expect(result.groupOperations).toEqual({}); + }); + + test('is empty after a removeNode prunes an existing group', () => { + const result = applyOperations(withGroup(), [{ type: 'removeNode', nodeName: 'B' }]); + expect(result.success).toBe(true); + if (!result.success) return; + expect(result.workflow.nodeGroups).toEqual([{ id: 'g1', name: 'Group', nodeIds: ['a'] }]); + expect(result.groupOperations).toEqual({}); + }); + + test('records the operation index and type for addNodeGroup', () => { + const result = applyOperations(baseWorkflow(), [ + { type: 'setNodePosition', nodeName: 'A', position: [10, 10] }, + { type: 'addNodeGroup', id: 'g1', name: 'Group', nodeNames: ['A', 'B'] }, + ]); + expect(result.success).toBe(true); + if (!result.success) return; + expect(result.groupOperations).toEqual({ g1: { opIndex: 1, type: 'addNodeGroup' } }); + }); + + test('records every group a setNodeGroups produced against that operation', () => { + const result = applyOperations(baseWorkflow(), [ + { + type: 'setNodeGroups', + nodeGroups: [ + { id: 'g1', name: 'One', nodeNames: ['A'] }, + { id: 'g2', name: 'Two', nodeNames: ['B'] }, + ], + }, + ]); + expect(result.success).toBe(true); + if (!result.success) return; + expect(result.groupOperations).toEqual({ + g1: { opIndex: 0, type: 'setNodeGroups' }, + g2: { opIndex: 0, type: 'setNodeGroups' }, + }); + }); + + test('records updateNodeGroup on a pre-existing group', () => { + const result = applyOperations(withGroup(), [ + { type: 'updateNodeGroup', groupName: 'Group', nodeNames: ['A'] }, + ]); + expect(result.success).toBe(true); + if (!result.success) return; + expect(result.groupOperations).toEqual({ g1: { opIndex: 0, type: 'updateNodeGroup' } }); + }); + + test('the last operation to touch a group wins', () => { + const result = applyOperations(baseWorkflow(), [ + { type: 'addNodeGroup', id: 'g1', name: 'Group', nodeNames: ['A', 'B'] }, + { type: 'updateNodeGroup', groupName: 'Group', nodeNames: ['A'] }, + ]); + expect(result.success).toBe(true); + if (!result.success) return; + expect(result.groupOperations).toEqual({ g1: { opIndex: 1, type: 'updateNodeGroup' } }); + }); + + test('drops a group that removeNodeGroup deleted', () => { + const result = applyOperations(baseWorkflow(), [ + { type: 'addNodeGroup', id: 'g1', name: 'Group', nodeNames: ['A', 'B'] }, + { type: 'removeNodeGroup', groupName: 'Group' }, + ]); + expect(result.success).toBe(true); + if (!result.success) return; + expect(result.groupOperations).toEqual({}); + }); + }); + + describe('non-fatal operation types', () => { + describe('canvasGroupsEnabled off', () => { + test('a normal batch with no failures succeeds with no skipped operations', () => { + const result = applyOperations(baseWorkflow(), [ + { type: 'updateNodeParameters', nodeName: 'B', parameters: { url: 'https://new' } }, + ]); + expect(result.success).toBe(true); + if (!result.success) return; + expect(result.skippedOperations ?? []).toEqual([]); + }); + + test('a failing group operation still aborts the whole batch', () => { + const result = applyOperations(baseWorkflow(), [ + { type: 'addNodeGroup', name: 'Group', nodeNames: ['Missing'] }, + ]); + expect(result.success).toBe(false); + if (result.success) return; + expect(result.error).toContain("node 'Missing' in group 'Group' not found"); + expect(result.opIndex).toBe(0); + }); + + test('a failing non-group operation still aborts the whole batch', () => { + const result = applyOperations(baseWorkflow(), [ + { type: 'updateNodeParameters', nodeName: 'B', parameters: { url: 'https://new' } }, + { type: 'removeNode', nodeName: 'Missing' }, + ]); + expect(result.success).toBe(false); + if (result.success) return; + expect(result.opIndex).toBe(1); + }); + }); + + describe('canvasGroupsEnabled on', () => { + test('a failing addNodeGroup is skipped while surrounding operations still apply', () => { + const wf = baseWorkflow(); + const ops: PartialUpdateOperation[] = [ + { type: 'addNode', node: { name: 'C', type: 'n8n-nodes-base.set', typeVersion: 1 } }, + { type: 'addNodeGroup', name: 'Group', nodeNames: ['Missing'] }, + { type: 'addConnection', source: 'A', target: 'C' }, + ]; + const result = applyOperations(wf, ops, { canvasGroupsEnabled: true }); + expect(result.success).toBe(true); + if (!result.success) return; + expect(result.workflow.nodes.some((n) => n.name === 'C')).toBe(true); + expect(result.workflow.connections.A?.main?.[0]).toEqual( + expect.arrayContaining([expect.objectContaining({ node: 'C' })]), + ); + expect(result.workflow.nodeGroups ?? []).toEqual([]); + expect(result.skippedOperations).toEqual([ + { + opIndex: 1, + type: 'addNodeGroup', + reason: "node 'Missing' in group 'Group' not found", + }, + ]); + }); + + test('a failing non-group operation still aborts the whole batch even with the flag on', () => { + const result = applyOperations( + baseWorkflow(), + [{ type: 'removeNode', nodeName: 'Missing' }], + { canvasGroupsEnabled: true }, + ); + expect(result.success).toBe(false); + if (result.success) return; + expect(result.opIndex).toBe(0); + }); + + test('two non-fatal failures in the same batch are both skipped, in order', () => { + const wf = { + ...baseWorkflow(), + nodeGroups: [{ id: 'g1', name: 'Group', nodeIds: ['a'] }], + }; + const ops: PartialUpdateOperation[] = [ + { type: 'addNodeGroup', name: 'Group', nodeNames: ['B'] }, + { type: 'updateNodeGroup', groupName: 'Missing', newName: 'X' }, + ]; + const result = applyOperations(wf, ops, { canvasGroupsEnabled: true }); + expect(result.success).toBe(true); + if (!result.success) return; + expect(result.workflow.nodeGroups).toEqual([{ id: 'g1', name: 'Group', nodeIds: ['a'] }]); + expect(result.skippedOperations).toEqual([ + { + opIndex: 0, + type: 'addNodeGroup', + reason: "a node group named 'Group' already exists", + }, + { + opIndex: 1, + type: 'updateNodeGroup', + reason: "node group 'Missing' not found", + }, + ]); + }); + + test('a batch where every operation is a failing group op still succeeds with no groups persisted', () => { + const ops: PartialUpdateOperation[] = [ + { type: 'addNodeGroup', name: 'Group', nodeNames: ['Missing'] }, + { type: 'removeNodeGroup', groupName: 'Missing' }, + ]; + const result = applyOperations(baseWorkflow(), ops, { canvasGroupsEnabled: true }); + expect(result.success).toBe(true); + if (!result.success) return; + expect(result.workflow.nodeGroups ?? []).toEqual([]); + expect(result.nodeGroupsChanged).toBe(false); + expect(result.skippedOperations).toHaveLength(2); + }); + + test('skipping a non-fatal operation does not corrupt state for later operations', () => { + const ops: PartialUpdateOperation[] = [ + { type: 'addNode', node: { name: 'C', type: 'n8n-nodes-base.set', typeVersion: 1 } }, + { type: 'addNodeGroup', name: 'Group', nodeNames: ['Missing'] }, + { type: 'setNodeParameter', nodeName: 'C', path: '/foo', value: 'bar' }, + ]; + const result = applyOperations(baseWorkflow(), ops, { canvasGroupsEnabled: true }); + expect(result.success).toBe(true); + if (!result.success) return; + expect(result.workflow.nodes.find((n) => n.name === 'C')!.parameters).toEqual({ + foo: 'bar', + }); + expect(result.skippedOperations).toHaveLength(1); + }); + + test('opIndex in skippedOperations reflects the original position in the input array', () => { + const ops: PartialUpdateOperation[] = [ + { type: 'updateNodeParameters', nodeName: 'B', parameters: { url: 'https://new' } }, + { type: 'addNode', node: { name: 'C', type: 'n8n-nodes-base.set', typeVersion: 1 } }, + { type: 'addNodeGroup', name: 'Group', nodeNames: ['Missing'] }, + { type: 'setNodeParameter', nodeName: 'C', path: '/foo', value: 'bar' }, + ]; + const result = applyOperations(baseWorkflow(), ops, { canvasGroupsEnabled: true }); + expect(result.success).toBe(true); + if (!result.success) return; + expect(result.skippedOperations).toEqual([ + { opIndex: 2, type: 'addNodeGroup', reason: expect.any(String) as string }, + ]); + }); + }); + }); + describe('toWorkflowSlice', () => { test('carries nodeGroups through from the workflow entity', () => { const slice = toWorkflowSlice({ diff --git a/packages/cli/src/modules/mcp/__tests__/workflow-validation.utils.test.ts b/packages/cli/src/modules/mcp/__tests__/workflow-validation.utils.test.ts index 67792af326f..d8c4719a03d 100644 --- a/packages/cli/src/modules/mcp/__tests__/workflow-validation.utils.test.ts +++ b/packages/cli/src/modules/mcp/__tests__/workflow-validation.utils.test.ts @@ -8,7 +8,7 @@ import { WorkflowAccessError } from '../mcp.errors'; import { getMcpWorkflow, getSdkReferenceHint } from '../tools/workflow-validation.utils'; vi.mock('@n8n/ai-workflow-builder', () => ({ - MCP_GET_SDK_REFERENCE_TOOL: { toolName: 'get_sdk_reference', displayTitle: 'SDK Ref' }, + MCP_GET_SDK_REFERENCE_TOOL: { toolName: 'get_workflow_sdk_reference', displayTitle: 'SDK Ref' }, CODE_BUILDER_VALIDATE_TOOL: { toolName: 'validate_workflow', displayTitle: 'Validate' }, })); @@ -19,7 +19,7 @@ describe('getSdkReferenceHint', () => { const hint = getSdkReferenceHint(error); - expect(hint).toContain('get_sdk_reference'); + expect(hint).toContain('get_workflow_sdk_reference'); expect(hint).toContain('Workflow SDK reference'); expect(hint).toContain('validate_workflow'); }); @@ -29,7 +29,7 @@ describe('getSdkReferenceHint', () => { new SyntaxError('Code must export a workflow built with the workflow() SDK function.'), ); - expect(hint).toContain('get_sdk_reference'); + expect(hint).toContain('get_workflow_sdk_reference'); expect(hint).toContain('required SDK patterns'); }); diff --git a/packages/cli/src/modules/mcp/mcp-protected-resource.ts b/packages/cli/src/modules/mcp/mcp-protected-resource.ts index 894235a6833..e6a4ac28aec 100644 --- a/packages/cli/src/modules/mcp/mcp-protected-resource.ts +++ b/packages/cli/src/modules/mcp/mcp-protected-resource.ts @@ -1,8 +1,10 @@ -import { MCP_INSTANCE_SCOPES } from '@n8n/api-types'; +import { MCP_AGENT_SCOPES, MCP_INSTANCE_SCOPES } from '@n8n/api-types'; +import { ModuleRegistry } from '@n8n/backend-common'; import { GlobalConfig } from '@n8n/config'; import { Service } from '@n8n/di'; import { BUILDER_TOOLS, TOOLS_BY_SCOPE } from './mcp-scopes'; +import { areAgentToolsAvailable } from './mcp-tool-availability'; import { McpConfig } from './mcp.config'; import { McpSettingsService } from './mcp.settings.service'; import type { ProtectedResource } from '@/services/protected-resource.registry'; @@ -16,6 +18,7 @@ export const INSTANCE_MCP_RESOURCE_ID = 'instance-mcp'; * mapping in `mcp-scopes.ts` when the MCP server registers tools. */ export const SUPPORTED_SCOPES: string[] = [...MCP_INSTANCE_SCOPES]; +const AGENT_SCOPES = new Set(MCP_AGENT_SCOPES); const MCP_RESOURCE_PATH = '/mcp-server/http'; @@ -36,8 +39,6 @@ const LEGACY_MCP_AUDIENCE = 'mcp-server-api'; export class McpProtectedResource implements ProtectedResource { readonly id = INSTANCE_MCP_RESOURCE_ID; - readonly scopes = SUPPORTED_SCOPES; - /** * Fallback audience for token requests without an RFC 8707 resource * indicator — the instance MCP server predates resource indicators, so @@ -50,8 +51,14 @@ export class McpProtectedResource implements ProtectedResource { private readonly mcpSettingsService: McpSettingsService, private readonly mcpConfig: McpConfig, private readonly globalConfig: GlobalConfig, + private readonly moduleRegistry: ModuleRegistry, ) {} + get scopes(): string[] { + if (areAgentToolsAvailable(this.globalConfig, this.moduleRegistry)) return SUPPORTED_SCOPES; + return SUPPORTED_SCOPES.filter((scope) => !AGENT_SCOPES.has(scope)); + } + /** * Filtered to the tools this instance actually exposes, so the consent * screen never advertises tools a grant cannot deliver. @@ -59,15 +66,19 @@ export class McpProtectedResource implements ProtectedResource { getScopeTools(): Record { const builderEnabled = this.globalConfig.endpoints.mcpBuilderEnabled; const tagsDisabled = this.globalConfig.tags.disabled; + const supportedScopes = new Set(this.scopes); return Object.fromEntries( - Object.entries(TOOLS_BY_SCOPE).map(([scope, tools]) => [ - scope, - tools.filter( - (tool) => - (builderEnabled || !BUILDER_TOOLS.has(tool)) && (!tagsDisabled || tool !== 'list_tags'), - ), - ]), + Object.entries(TOOLS_BY_SCOPE) + .filter(([scope]) => supportedScopes.has(scope)) + .map(([scope, tools]) => [ + scope, + tools.filter( + (tool) => + (builderEnabled || !BUILDER_TOOLS.has(tool)) && + (!tagsDisabled || tool !== 'list_workflow_tags'), + ), + ]), ); } diff --git a/packages/cli/src/modules/mcp/mcp-scopes.ts b/packages/cli/src/modules/mcp/mcp-scopes.ts index ffe985b591c..afa74e82001 100644 --- a/packages/cli/src/modules/mcp/mcp-scopes.ts +++ b/packages/cli/src/modules/mcp/mcp-scopes.ts @@ -20,7 +20,7 @@ export const TOOLS_BY_SCOPE: Record = { 'search_nodes', 'get_node_types', 'get_workflow_best_practices', - 'get_sdk_reference', + 'get_workflow_sdk_reference', 'validate_workflow', 'validate_node_config', ], @@ -35,12 +35,39 @@ export const TOOLS_BY_SCOPE: Record = { 'search_nodes', 'get_node_types', 'get_workflow_best_practices', - 'get_sdk_reference', + 'get_workflow_sdk_reference', 'validate_workflow', 'validate_node_config', ], - 'workflow:execute': ['execute_workflow', 'test_workflow', 'prepare_test_pin_data'], - 'execution:read': ['get_execution', 'search_executions'], + 'workflow:execute': ['execute_workflow', 'test_workflow', 'prepare_workflow_pin_data'], + 'execution:read': ['get_workflow_execution', 'search_workflow_executions'], + 'agent:read': [ + 'search_agents', + 'get_agent', + 'list_agent_versions', + 'discover_agent_assets', + 'validate_agent', + 'get_agent_builder_reference', + ], + // The read tools ride along on a write-only grant: mutate_agent's + // configHash handshake starts at get_agent, and building needs search + // (sub-agents), asset discovery, validation, and the reference. + 'agent:write': [ + 'create_agent', + 'mutate_agent', + 'revert_agent', + 'delete_agent', + 'verify_agent_mcp_server', + 'search_agents', + 'get_agent', + 'list_agent_versions', + 'discover_agent_assets', + 'validate_agent', + 'get_agent_builder_reference', + 'update_agent_integration', + 'publish_agent', + 'unpublish_agent', + ], // explore_node_resources queries external services with stored credentials, // so it must sit behind the credential scope rather than a workflow one. 'credential:read': ['list_credentials', 'list_n8n_connect_services', 'explore_node_resources'], @@ -56,7 +83,7 @@ export const TOOLS_BY_SCOPE: Record = { 'add_data_table_rows', ], 'project:read': ['search_projects', 'search_folders'], - 'tag:read': ['list_tags'], + 'tag:read': ['list_workflow_tags'], }; /** @@ -68,7 +95,7 @@ export const BUILDER_TOOLS: ReadonlySet = new Set([ 'search_nodes', 'get_node_types', 'get_workflow_best_practices', - 'get_sdk_reference', + 'get_workflow_sdk_reference', 'validate_workflow', 'validate_node_config', 'create_workflow_from_code', @@ -80,6 +107,11 @@ export const BUILDER_TOOLS: ReadonlySet = new Set([ 'search_folders', ]); +export const AGENT_TOOLS: ReadonlySet = new Set([ + ...TOOLS_BY_SCOPE['agent:read'], + ...TOOLS_BY_SCOPE['agent:write'], +]); + function isMcpScope(scope: string): scope is McpScope { return (MCP_INSTANCE_SCOPES as readonly string[]).includes(scope); } diff --git a/packages/cli/src/modules/mcp/mcp-tool-availability.ts b/packages/cli/src/modules/mcp/mcp-tool-availability.ts new file mode 100644 index 00000000000..79468982185 --- /dev/null +++ b/packages/cli/src/modules/mcp/mcp-tool-availability.ts @@ -0,0 +1,9 @@ +import type { ModuleRegistry } from '@n8n/backend-common'; +import type { GlobalConfig } from '@n8n/config'; + +export function areAgentToolsAvailable( + globalConfig: GlobalConfig, + moduleRegistry: ModuleRegistry, +): boolean { + return globalConfig.endpoints.mcpBuilderEnabled && moduleRegistry.isActive('agents'); +} diff --git a/packages/cli/src/modules/mcp/mcp.constants.ts b/packages/cli/src/modules/mcp/mcp.constants.ts index 2af1fc7b0bb..fa0048ea831 100644 --- a/packages/cli/src/modules/mcp/mcp.constants.ts +++ b/packages/cli/src/modules/mcp/mcp.constants.ts @@ -26,6 +26,7 @@ export const MCP_ACCESS_DISABLED_ERROR_MESSAGE = 'MCP access is disabled'; * Tool name constants */ export const LIST_N8N_CONNECT_SERVICES_TOOL_NAME = 'list_n8n_connect_services'; +export const MCP_CREATE_AGENT_TOOL_NAME = 'create_agent'; /** * Triggers supported in production mode for MCP execution diff --git a/packages/cli/src/modules/mcp/mcp.controller.ts b/packages/cli/src/modules/mcp/mcp.controller.ts index 644e737d5ff..333d22e49ea 100644 --- a/packages/cli/src/modules/mcp/mcp.controller.ts +++ b/packages/cli/src/modules/mcp/mcp.controller.ts @@ -74,8 +74,12 @@ export class McpController { } /** - * GET endpoint for SSE stream (MCP Streamable HTTP spec) - * Allows clients like Gemini CLI to establish an SSE stream for server-to-client notifications. + * GET endpoint (MCP Streamable HTTP spec). The server runs in stateless mode + * (a fresh transport per request), so it can never deliver server-initiated + * messages on a GET listen stream: routing the request into the transport + * leaves the SSE stream open and silent forever, stalling clients during + * connection setup. The spec requires servers that don't offer the stream + * to respond with 405. */ @Get('/http', { ipRateLimit: createIpRateLimit(mcpConfig.rateLimitServer), @@ -83,7 +87,7 @@ export class McpController { skipAuth: true, usesTemplates: true, }) - async handleGet(req: AuthenticatedRequest, res: FlushableResponse) { + async handleGet(_req: AuthenticatedRequest, res: Response) { this.setCorsHeaders(res); const enabled = await this.mcpSettingsService.getEnabled(); @@ -92,22 +96,15 @@ export class McpController { return; } - try { - const featureFlags = await this.mcpService.resolveFeatureFlags(req.user); - await this.handleTransportRequest(req, res, featureFlags); - } catch (error) { - this.errorReporter.error(error); - if (!res.headersSent) { - res.status(500).json({ - jsonrpc: '2.0', - error: { - code: -32603, - message: INTERNAL_SERVER_ERROR_MESSAGE, - }, - id: null, - }); - } - } + res.header('Allow', 'POST'); + res.status(405).json({ + jsonrpc: '2.0', + error: { + code: -32000, + message: 'Method not allowed.', + }, + id: null, + }); } @Post('/http', { @@ -199,7 +196,7 @@ export class McpController { req: AuthenticatedRequest, res: FlushableResponse, featureFlags: McpFeatureFlags, - body?: unknown, + body: unknown, ) { const { StreamableHTTPServerTransport } = await import( '@modelcontextprotocol/sdk/server/streamableHttp.js' diff --git a/packages/cli/src/modules/mcp/mcp.service.ts b/packages/cli/src/modules/mcp/mcp.service.ts index 02b11725a5d..820e79ed6fa 100644 --- a/packages/cli/src/modules/mcp/mcp.service.ts +++ b/packages/cli/src/modules/mcp/mcp.service.ts @@ -5,7 +5,7 @@ import { MCP_APPS_VARIANT_ENABLED, MCP_CANVAS_GROUPS_FLAG, } from '@n8n/api-types'; -import { LicenseState, Logger } from '@n8n/backend-common'; +import { LicenseState, Logger, ModuleRegistry } from '@n8n/backend-common'; import { ExecutionsConfig, GlobalConfig, WorkflowsConfig } from '@n8n/config'; import { ExecutionRepository, @@ -14,7 +14,7 @@ import { SharedWorkflowRepository, User, } from '@n8n/db'; -import { Service } from '@n8n/di'; +import { Container, Service } from '@n8n/di'; import { registerMcpAppTool, registerWorkflowPreviewApp, @@ -50,8 +50,9 @@ import { WorkflowHistoryService } from '@/workflows/workflow-history/workflow-hi import { WorkflowPublishedDataService } from '@/workflows/workflow-published-data.service'; import { WorkflowService } from '@/workflows/workflow.service'; +import { MCP_CREATE_AGENT_TOOL_NAME, MCP_PREVIEW_RENDER_REQUESTED_EVENT } from './mcp.constants'; import { getAllowedToolNames } from './mcp-scopes'; -import { MCP_PREVIEW_RENDER_REQUESTED_EVENT } from './mcp.constants'; +import { areAgentToolsAvailable } from './mcp-tool-availability'; import type { McpAppsTelemetryVariant, McpClientInfo, RegisterToolFn } from './mcp.types'; import { createAddDataTableColumnTool, @@ -160,6 +161,7 @@ export class McpService { private readonly workflowPublishedDataService: WorkflowPublishedDataService, private readonly subworkflowPolicyChecker: SubworkflowPolicyChecker, private readonly aiGatewayService: AiGatewayService, + private readonly moduleRegistry: ModuleRegistry, ) {} /** @@ -268,16 +270,22 @@ export class McpService { const builderInstructionsEnabled = builderEnabled && (allowedToolNames?.has(MCP_CREATE_WORKFLOW_FROM_CODE_TOOL.toolName) ?? true); + const agentsEnabled = areAgentToolsAvailable(this.globalConfig, this.moduleRegistry); + // Same rationale as builderInstructionsEnabled: a grant that cannot call + // the agent tools gets no agent build walkthrough. + const agentInstructionsEnabled = + agentsEnabled && (allowedToolNames?.has(MCP_CREATE_AGENT_TOOL_NAME) ?? true); const server = new McpServer( { name: 'n8n MCP Server', - version: builderEnabled ? '1.1.0' : '1.0.0', + version: agentsEnabled ? '1.2.0' : builderEnabled ? '1.1.0' : '1.0.0', }, { instructions: getMcpInstructions({ isBuilderEnabled: builderInstructionsEnabled, isN8nConnectAvailable: n8nConnectAvailable, canvasGroupsEnabled: featureFlags.canvasGroupsEnabled, + isAgentsEnabled: agentInstructionsEnabled, }), }, ); @@ -459,6 +467,11 @@ export class McpService { ); } + if (agentsEnabled) { + const { McpAgentToolsService } = await import('./tools/agents/agent-tools.service.js'); + Container.get(McpAgentToolsService).registerTools(server, user, allowedToolNames); + } + return server; } diff --git a/packages/cli/src/modules/mcp/mcp.types.ts b/packages/cli/src/modules/mcp/mcp.types.ts index 2bc6192091c..148f81563da 100644 --- a/packages/cli/src/modules/mcp/mcp.types.ts +++ b/packages/cli/src/modules/mcp/mcp.types.ts @@ -1,4 +1,5 @@ import { type ToolCallback } from '@modelcontextprotocol/sdk/server/mcp.js'; +import type { WorkflowPublishBlockedReason } from '@n8n/api-types'; import type { INode } from 'n8n-workflow'; import type z from 'zod'; @@ -126,7 +127,7 @@ export type UserCalledMCPToolEventPayload = { success: boolean; data?: unknown; error?: string | Record; - error_reason?: WorkflowNotFoundReason; + error_reason?: WorkflowNotFoundReason | WorkflowPublishBlockedReason; }; }; diff --git a/packages/cli/src/modules/mcp/tools/agents/agent-reference.ts b/packages/cli/src/modules/mcp/tools/agents/agent-reference.ts new file mode 100644 index 00000000000..3ac22f70c4e --- /dev/null +++ b/packages/cli/src/modules/mcp/tools/agents/agent-reference.ts @@ -0,0 +1,171 @@ +import { AgentJsonConfigBaseSchema } from '@n8n/api-types'; +import { zodToJsonSchema } from 'zod-to-json-schema'; + +export const AGENT_BUILDER_REFERENCE_URI = 'n8n://agents/reference'; + +// Integrations are a published runtime surface managed only through +// update_agent_integration, so they are never part of the editable draft +// config the model reads and writes. +const EditableAgentJsonConfigSchema = AgentJsonConfigBaseSchema.omit({ integrations: true }); + +export const AGENT_CONFIG_JSON_SCHEMA = zodToJsonSchema(EditableAgentJsonConfigSchema, { + name: 'AgentJsonConfig', +}); + +export const AGENT_BUILDER_GUIDE = `# n8n Agent management + +Use the Agent MCP tools to create and edit persisted n8n Agents. The MCP client is the +orchestrator: there is no nested conversational Agent Builder. + +## Choose an Agent or workflow + +An n8n Agent is a first-class persisted resource with its own instructions, model, tools, skills, +tasks, memory, integrations, and lifecycle. An AI Agent node is a node inside a workflow whose +trigger, surrounding graph, and lifecycle are owned by that workflow. + +If the request is actually a fixed trigger or schedule with enumerable, repeatable steps, it is +probably a workflow — explain the mismatch and ask before building the alternative. Never substitute +a Chat Trigger plus an AI Agent node for a requested n8n Agent. + +## Build sequence + +1. Use search_projects to identify the project, or search_agents and get_agent for an existing Agent. + By-ID tools (get_agent, mutate_agent, validate_agent, publish_agent, unpublish_agent, + revert_agent, list_agent_versions, delete_agent, update_agent_integration) take an agentId alone + and resolve the project from it. +2. Use discover_agent_assets plus list_credentials, search_nodes, get_node_types, and + explore_node_resources to ground model, tool, workflow, integration, and credential choices. +3. For a new Agent, call create_agent with the initial config after discovering its assets. The + top-level name is injected into config; omit config only when the Agent ID is needed first. +4. Call mutate_agent once per logical sidecar or subsequent mutation, always passing the latest + configHash. Skills, tasks, and custom tools are created after the Agent because they need + server-generated IDs. +5. Call validate_agent and resolve every reported error. A valid Agent is a completed draft; do not + publish it merely to finish the build. +6. Report that the draft is ready, include a clickable link using the \`url\` returned by + validate_agent, and ask whether the user wants to publish it. +7. Call publish_agent only when the user explicitly requested publication, activation, deployment, + or making the Agent live, or confirms publication after the build. +8. Connecting a chat integration publishes the current draft and starts the integration runtime, so + it requires the same explicit publication confirmation. + +## Publication approval + +Building, editing, or validating an Agent never implies permission to publish or republish it. Leave +the Agent as a draft by default. An explicit request to publish, activate, deploy, make live, or +connect a chat integration counts as approval; otherwise ask after validation and wait for the +answer before calling publish_agent or connecting an integration. + +## Version history + +list_agent_versions lists an Agent's published versions; get_agent with a versionId inspects one +before acting on it. revert_agent restores the draft from a version without publishing, returning a +fresh configHash for further mutations. publish_agent with a versionId republishes that version +directly, leaving the draft untouched; as a (re)publication it requires the same explicit approval. + +In publish, unpublish, and revert responses, \`activeVersionId\` identifies the live published +version (null when unpublished) while \`versionId\` is the draft's internal pointer — do not report +\`versionId\` as a published version. + +## mutate_agent operations + +Pass a single \`operation\` object whose \`type\` selects the mutation. Each operation's fields sit +directly on that object — there is no \`value\` wrapper. For example: + +\`\`\`json +{ "type": "config.patch", "patch": [{ "op": "add", "path": "/tools/-", "value": { "type": "workflow", "workflow": "My Workflow", "name": "my_tool" } }] } +\`\`\` + +- config.replace: Set \`config\` to the complete editable Agent JSON configuration. Must not include + integrations; use update_agent_integration for those. +- config.patch: Set \`patch\` to an array of RFC 6902 operations (add, remove, replace, move, copy, + test). Paths under /integrations are rejected; use update_agent_integration for those. +- skill.upsert: Set \`skill\` to the complete skill body. Omit \`skillId\` to create and attach a new + skill, or pass it to replace an existing skill body. +- skill.delete: Set \`skillId\` to the skill to delete; its config reference is removed. +- task.upsert: Set \`task\` to the complete task body. Omit \`taskId\` to create and attach a new + scheduled task, or pass it to replace an existing one. \`enabled\` controls the task config reference. +- task.delete: Set \`taskId\` to the task to delete; its config reference is removed. +- customTool.upsert: Set \`code\` to the tool source; it is compiled, validated, stored, and attached. + Only \`@n8n/agents\` and \`zod\` imports are available. The default export must be a Tool builder + chain with \`description\`, \`input\` (a Zod schema), and \`handler\`; \`output\` is optional: + + \`\`\`typescript + import { Tool } from '@n8n/agents'; + import { z } from 'zod'; + + export default new Tool('get_current_datetime') + .description('Return the current date and time as an ISO 8601 string') + .input(z.object({})) + .handler(async () => new Date().toISOString()); + \`\`\` +- customTool.delete: Set \`toolId\` to the custom tool to delete; its config reference is removed. + +Every mutation requires baseConfigHash from get_agent or the previous successful mutation. Mutation +responses contain only the next configHash and the affected resource ID, not the full Agent. On a +stale_config response, call get_agent and retry against the returned snapshot. + +## Agent JSON configuration + +The required runnable fields are name, model, credential, and instructions. Common optional fields +include tools, skills, tasks, memory, subAgents, providerTools, mcpServers, vectorStores, +personalisation, and config. Integrations are not part of this config (see below). + +Tool references use these forms: + +- Custom tool: { "type": "custom", "id": "tool_name" } +- Workflow tool: { "type": "workflow", "workflow": "Workflow Name", "name": "tool_name" } +- Node tool: { "type": "node", "name": "tool_name", "node": { "nodeType": "...", + "nodeTypeVersion": 1, "nodeParameters": {}, "credentials": {} } } + +Creating a resource does not give the Agent access to it. For example, a data table created with +create_data_table is only usable by the Agent once it is attached as a node tool +(n8n-nodes-base.dataTable); discover it with search_nodes usage="agentTool" like any other node. + +Sub-agents are not tool entries. Configure them under the top-level \`subAgents\` field: +{ "subAgents": { "agents": [{ "agentId": "...", "useWhen": "..." }] } } + +Do not guess node parameters or stable resource IDs. Discover the node definition and live resource +options first. Never place credential secret data in Agent configuration or MCP tool arguments; use +credential IDs returned by list_credentials. + +Skills and tasks have separately persisted bodies. Always manage them through mutate_agent instead +of manually inventing their IDs. Saved sub-agents must be published Agents from the same project. +Use discover_agent_assets with kind=subagents to obtain valid IDs. + +Chat integrations are conversation surfaces, not ordinary node tools. Use an integration when users +should invoke and converse with the Agent in Slack, Telegram, or Linear. Use a node/workflow tool +when the Agent only needs to call that service as an API. + +Integrations are a published runtime surface, not editable config. get_agent reports them in a +read-only integrations field, but config.replace and config.patch cannot add, change, or remove +them, and they never appear in the config schema above. Manage them exclusively with +update_agent_integration, which validates the credential and connects the live channel. Connecting +publishes the current draft, so it needs the same explicit publication confirmation as publish_agent. + +## MCP servers + +The top-level \`mcpServers\` config array connects external MCP tool catalogs to the Agent. Discover +registry-backed servers with discover_agent_assets kind=mcpServers, or use a URL the user provides. +When the server requires authentication, resolve an accessible credential ID with list_credentials +first; the same ID is passed to verification and stored in the config entry. Credentials cannot be +created through these tools — when none exists, ask the user to create one in n8n. + +Before writing an entry into mcpServers, call verify_agent_mcp_server with the same name, url, +transport, authentication, and credential. The server does not need to be attached to the Agent +first: verification opens a temporary connection and returns the server's live tools. validate_agent +never performs this handshake, so an unverified entry can pass validation and still fail at runtime. +Confirm the returned tools cover the requested capability and use the list to populate toolFilter +instead of guessing tool names. If verification fails, report the error and resolve it with the user +instead of persisting a broken server. Only when the user cannot supply the URL or credential yet, +persist the known fields without inventing values and skip verification. +`; + +export const AGENT_BUILDER_REFERENCE = `${AGENT_BUILDER_GUIDE} + +## Canonical Agent configuration schema + +\`\`\`json +${JSON.stringify(AGENT_CONFIG_JSON_SCHEMA, null, 2)} +\`\`\` +`; diff --git a/packages/cli/src/modules/mcp/tools/agents/agent-tools.service.ts b/packages/cli/src/modules/mcp/tools/agents/agent-tools.service.ts new file mode 100644 index 00000000000..9a81d3a4308 --- /dev/null +++ b/packages/cli/src/modules/mcp/tools/agents/agent-tools.service.ts @@ -0,0 +1,1556 @@ +import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; +import type { CredentialProvider } from '@n8n/agents'; +import { + rejectIfDynamicSelectorUsesFromAi, + rejectIfEmptyInstructions, + rejectIfUnsupportedNativeWebSearch, + type AgentConfigValidationMessages, +} from '@n8n/ai-utilities/agent-config'; +import { + AGENT_MODEL_PROVIDERS, + AgentIntegrationSchema, + AgentJsonConfigBaseSchema, + AgentJsonConfigSchema, + isDraftAgentConfig, + AgentTelegramSettingsSchema, + McpAuthenticationSchemaTypes, + agentSkillSchema, + agentTaskSchema, + sanitizeAgentJsonConfig, + type AgentJsonConfig, +} from '@n8n/api-types'; +import { OutboundHttp, SsrfProtectionService } from '@n8n/backend-network'; +import { SsrfProtectionConfig } from '@n8n/config'; +import type { User } from '@n8n/db'; +import { Service } from '@n8n/di'; +import type { Scope } from '@n8n/permissions'; +import { isRecord } from '@n8n/utils/is-record'; +import { UserError } from 'n8n-workflow'; +import { z } from 'zod'; +import { zodToJsonSchema } from 'zod-to-json-schema'; + +import { CredentialsService } from '@/credentials/credentials.service'; +import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; +import { AgentConfigService } from '@/modules/agents/agent-config.service'; +import { AgentCustomToolsService } from '@/modules/agents/agent-custom-tools.service'; +import { AgentIntegrationPersistenceService } from '@/modules/agents/agent-integration-persistence.service'; +import { AgentModelCatalogService } from '@/modules/agents/agent-model-catalog.service'; +import { AgentPublishService } from '@/modules/agents/agent-publish.service'; +import { AgentSkillsService } from '@/modules/agents/agent-skills.service'; +import { AgentTaskService } from '@/modules/agents/agent-task.service'; +import { AgentValidationService } from '@/modules/agents/agent-validation.service'; +import { AgentsService } from '@/modules/agents/agents.service'; +import { AttachableWorkflowsService } from '@/modules/agents/attachable-workflows.service'; +import type { Agent } from '@/modules/agents/entities/agent.entity'; +import { ChatIntegrationRegistry } from '@/modules/agents/integrations/agent-chat-integration'; +import { ChatIntegrationService } from '@/modules/agents/integrations/chat-integration.service'; +import { composeJsonConfig } from '@/modules/agents/json-config/agent-config-composition'; +import { listMcpServerTools } from '@/modules/agents/json-config/mcp-client-factory'; +import { sanitizeUnknownAgentCredentials } from '@/modules/agents/json-config/sanitize-unknown-agent-credentials'; +import { filterOfferedAgentModelProviders } from '@/modules/agents/model-catalog'; +import { AgentSecureRuntime } from '@/modules/agents/runtime/agent-secure-runtime'; +import { getAgentConfigHash } from '@/modules/agents/utils/agent-config-hash'; +import { createAgentCredentialProvider } from '@/modules/agents/utils/agent-credential-provider'; +import { McpRegistryService } from '@/modules/mcp-registry/registry/mcp-registry.service'; +import { NodeTypes } from '@/node-types'; +import { OauthService } from '@/oauth/oauth.service'; +import { userHasScopes } from '@/permissions.ee/check-access'; +import { ProjectScopeService } from '@/permissions.ee/project-scope.service'; +import { UrlService } from '@/services/url.service'; +import { Telemetry } from '@/telemetry'; +import { createAiMcpFetch } from '@/utils/ai-proxy-fetch'; + +import { + AGENT_BUILDER_GUIDE, + AGENT_BUILDER_REFERENCE, + AGENT_BUILDER_REFERENCE_URI, + AGENT_CONFIG_JSON_SCHEMA, +} from './agent-reference'; +import { MCP_CREATE_AGENT_TOOL_NAME, USER_CALLED_MCP_TOOL_EVENT } from '../../mcp.constants'; +import type { ToolDefinition, UserCalledMCPToolEventPayload } from '../../mcp.types'; + +const MCP_SERVER_DISCOVERY_LIMIT = 20; + +const INTEGRATIONS_NOT_IN_CONFIG_MESSAGE = + 'Integrations are a published runtime surface, not editable config. Use update_agent_integration to connect or disconnect Slack, Telegram, or Linear.'; + +/** LLM-facing follow-up guidance for the MCP builder surface. */ +const MCP_AGENT_CONFIG_MESSAGES: AgentConfigValidationMessages = { + emptyInstructionsFollowUp: 'retrying the mutation.', + dynamicSelectorFollowUp: + 'Ask the user for the concrete value and write it into nodeParameters as a literal.', +}; + +function integrationsField(config: unknown): unknown { + return isRecord(config) ? config.integrations : undefined; +} + +/** + * Collects the credential IDs that `sanitizeUnknownAgentCredentials` rewrote + * to `''` — the only change that sanitizer ever makes. + */ +function collectClearedCredentialIds( + original: unknown, + sanitized: unknown, + ids: Set, +): void { + if (Array.isArray(original) && Array.isArray(sanitized)) { + original.forEach((entry, index) => collectClearedCredentialIds(entry, sanitized[index], ids)); + return; + } + if (!isRecord(original) || !isRecord(sanitized)) return; + for (const [key, value] of Object.entries(original)) { + if (typeof value === 'string' && value !== '' && sanitized[key] === '') { + ids.add(value); + continue; + } + collectClearedCredentialIds(value, sanitized[key], ids); + } +} + +/** + * True when `next` alters the integrations array relative to `current`. Compared + * structurally rather than by patch path so no operation — including an + * ancestor or whole-document replace — can slip an integrations change through. + */ +function integrationsChanged(current: AgentJsonConfig, next: unknown): boolean { + return ( + JSON.stringify(current.integrations ?? []) !== JSON.stringify(integrationsField(next) ?? []) + ); +} + +const TELEGRAM_SETTINGS_JSON_SCHEMA = zodToJsonSchema(AgentTelegramSettingsSchema); + +const httpUrlSchema = z + .string() + .url() + .refine((value) => /^https?:\/\//i.test(value), { message: 'Must be a valid HTTP(S) URL' }); + +const agentIdentityShape = { + agentId: z.string().min(1).describe('Agent ID'), +} satisfies z.ZodRawShape; + +const getAgentInput = { + ...agentIdentityShape, + versionId: z + .string() + .min(1) + .optional() + .describe( + 'Read a published version snapshot instead of the draft, e.g. the activeVersionId. Snapshots are read-only, so the response has no configHash.', + ), +} satisfies z.ZodRawShape; + +const publishAgentInput = { + ...agentIdentityShape, + versionId: z + .string() + .min(1) + .optional() + .describe( + 'Republish a previously published version instead of the current draft. The draft is left untouched.', + ), +} satisfies z.ZodRawShape; + +const revertAgentInput = { + ...agentIdentityShape, + versionId: z + .string() + .min(1) + .optional() + .describe( + 'Published version to restore the draft from; defaults to the currently published version', + ), +} satisfies z.ZodRawShape; + +const listAgentVersionsInput = { + ...agentIdentityShape, + limit: z.number().int().min(1).max(100).optional().default(20), + offset: z.number().int().min(0).optional().default(0), +} satisfies z.ZodRawShape; + +const searchAgentsInput = { + projectId: z.string().min(1).optional().describe('Restrict results to one project'), + query: z.string().optional().describe('Filter by Agent name'), + publishedOnly: z.boolean().optional().default(false), + excludeAgentId: z.string().optional().describe('Agent ID to omit, useful for sub-agent search'), + limit: z.number().int().min(1).max(100).optional().default(50), +} satisfies z.ZodRawShape; + +const initialAgentConfigSchema = AgentJsonConfigBaseSchema.omit({ + name: true, + skills: true, + tasks: true, + // Integrations are managed only through update_agent_integration. + integrations: true, +}).superRefine((config, ctx) => { + // Mirrors AgentJsonConfigSchema's own refinement, which is lost by + // deriving from the base schema (ZodEffects has no .omit). + if (config.credential?.trim() && isDraftAgentConfig(config)) { + ctx.addIssue({ + code: z.ZodIssueCode.custom, + path: ['credential'], + message: 'A credential requires a model to be set', + }); + } + if (config.tools?.some((tool) => tool.type === 'custom')) { + ctx.addIssue({ + code: z.ZodIssueCode.custom, + path: ['tools'], + message: 'Create custom tools with mutate_agent after creating the Agent', + }); + } +}); + +const createAgentInput = { + projectId: z.string().min(1), + name: z.string().trim().min(1).max(128), + config: initialAgentConfigSchema + .optional() + .describe( + 'Optional initial Agent config without name, skills, tasks, or custom tools. The top-level name is injected into the config.', + ), +} satisfies z.ZodRawShape; + +const jsonPatchValueSchema = z.union([ + z.null(), + z.boolean(), + z.number(), + z.string(), + z.array(z.unknown()), + z.record(z.unknown()), +]); + +const jsonPatchOperationSchema = z.discriminatedUnion('op', [ + z.object({ op: z.literal('add'), path: z.string(), value: jsonPatchValueSchema }), + z.object({ op: z.literal('remove'), path: z.string() }), + z.object({ op: z.literal('replace'), path: z.string(), value: jsonPatchValueSchema }), + z.object({ op: z.literal('move'), from: z.string(), path: z.string() }), + z.object({ op: z.literal('copy'), from: z.string(), path: z.string() }), + z.object({ op: z.literal('test'), path: z.string(), value: jsonPatchValueSchema }), +]); + +const mutationOperationSchema = z.discriminatedUnion('type', [ + z.object({ type: z.literal('config.replace'), config: z.record(z.unknown()) }), + z.object({ + type: z.literal('config.patch'), + patch: z.array(jsonPatchOperationSchema).min(1), + }), + z.object({ + type: z.literal('skill.upsert'), + skillId: z.string().optional(), + skill: agentSkillSchema, + }), + z.object({ type: z.literal('skill.delete'), skillId: z.string().min(1) }), + z.object({ + type: z.literal('task.upsert'), + taskId: z.string().optional(), + task: agentTaskSchema, + enabled: z.boolean().optional(), + }), + z.object({ type: z.literal('task.delete'), taskId: z.string().min(1) }), + z.object({ type: z.literal('customTool.upsert'), code: z.string().min(1) }), + z.object({ type: z.literal('customTool.delete'), toolId: z.string().min(1) }), +]); + +const mutateAgentInput = { + ...agentIdentityShape, + baseConfigHash: z + .string() + .min(1) + .describe('Latest configHash returned by get_agent or a successful mutation'), + operation: mutationOperationSchema, +} satisfies z.ZodRawShape; + +const discoverAssetsInput = { + projectId: z.string().min(1), + kind: z.enum(['models', 'integrations', 'workflows', 'subagents', 'mcpServers']), + query: z + .string() + .trim() + .min(1) + .optional() + .describe('Optional filter for workflows, subagents, or MCP servers'), + provider: z + .enum(AGENT_MODEL_PROVIDERS) + .optional() + .describe('Model provider for kind=models; omit to get a provider summary without model lists'), + credentialId: z + .string() + .min(1) + .optional() + .describe('Accessible credential used to verify models for the selected provider'), + excludeAgentId: z.string().optional().describe('Agent to omit when kind=subagents'), +} satisfies z.ZodRawShape; + +const verifyMcpServerInput = { + projectId: z.string().min(1), + name: z + .string() + .min(1) + .max(64) + .regex(/^[a-zA-Z0-9_-]+$/), + url: httpUrlSchema.describe('HTTP(S) MCP server endpoint'), + transport: z.enum(['sse', 'streamableHttp']).optional().default('streamableHttp'), + authentication: z + .union([McpAuthenticationSchemaTypes, z.string().endsWith('McpOAuth2Api')]) + .optional() + .default('none') + .describe('Authentication method; every value other than none requires credential'), + credential: z + .string() + .min(1) + .optional() + .describe('Accessible credential ID; required when authentication is not none'), + connectionTimeoutMs: z.number().int().min(1).max(120_000).optional(), +} satisfies z.ZodRawShape; + +const updateIntegrationInput = { + ...agentIdentityShape, + action: z.enum(['connect', 'disconnect']), + type: z.string().min(1).describe('Integration type returned by discover_agent_assets'), + credentialId: z.string().min(1).describe('Accessible credential for this integration'), + settings: z + .record(z.unknown()) + .optional() + .describe('Integration settings; required for Telegram connect operations'), +} satisfies z.ZodRawShape; + +const emptyInput = {} satisfies z.ZodRawShape; + +type SearchAgentsInput = z.infer>; +type CreateAgentInput = z.infer>; +type MutateAgentInput = z.infer>; +type DiscoverAssetsInput = z.infer>; +type VerifyMcpServerInput = z.infer>; +type UpdateIntegrationInput = z.infer>; + +type MutationResource = { + type: 'skill' | 'task' | 'customTool'; + id: string; +}; + +function toolResult(data: Record, isError = false) { + return { + content: [{ type: 'text' as const, text: JSON.stringify(data) }], + structuredContent: data, + ...(isError ? { isError: true } : {}), + }; +} + +@Service() +export class McpAgentToolsService { + constructor( + private readonly telemetry: Telemetry, + private readonly credentialsService: CredentialsService, + private readonly agentsService: AgentsService, + private readonly agentConfigService: AgentConfigService, + private readonly agentValidationService: AgentValidationService, + private readonly agentPublishService: AgentPublishService, + private readonly agentSkillsService: AgentSkillsService, + private readonly agentTaskService: AgentTaskService, + private readonly agentCustomToolsService: AgentCustomToolsService, + private readonly agentSecureRuntime: AgentSecureRuntime, + private readonly integrationPersistenceService: AgentIntegrationPersistenceService, + private readonly chatIntegrationService: ChatIntegrationService, + private readonly chatIntegrationRegistry: ChatIntegrationRegistry, + private readonly agentModelCatalogService: AgentModelCatalogService, + private readonly attachableWorkflowsService: AttachableWorkflowsService, + private readonly mcpRegistryService: McpRegistryService, + private readonly nodeTypes: NodeTypes, + private readonly oauthService: OauthService, + private readonly outboundHttp: OutboundHttp, + private readonly ssrfConfig: SsrfProtectionConfig, + private readonly ssrfProtectionService: SsrfProtectionService, + private readonly urlService: UrlService, + private readonly projectScopeService: ProjectScopeService, + ) {} + + /** + * `allowedToolNames` carries the OAuth grant's scope-derived allow-list + * (undefined means a non-scope-bearing credential with full access). + */ + registerTools(server: McpServer, user: User, allowedToolNames?: Set): void { + const registerIfAllowed = (tool: ToolDefinition): void => { + if (allowedToolNames && !allowedToolNames.has(tool.name)) return; + this.register(server, tool); + }; + + registerIfAllowed(this.searchAgentsTool(user)); + registerIfAllowed(this.getAgentTool(user)); + registerIfAllowed(this.createAgentTool(user)); + registerIfAllowed(this.mutateAgentTool(user)); + registerIfAllowed(this.validateAgentTool(user)); + registerIfAllowed(this.publishAgentTool(user)); + registerIfAllowed(this.unpublishAgentTool(user)); + registerIfAllowed(this.revertAgentTool(user)); + registerIfAllowed(this.listAgentVersionsTool(user)); + registerIfAllowed(this.deleteAgentTool(user)); + registerIfAllowed(this.discoverAssetsTool(user)); + registerIfAllowed(this.verifyMcpServerTool(user)); + registerIfAllowed(this.updateIntegrationTool(user)); + registerIfAllowed(this.referenceTool(user)); + + // The reference resource complements get_agent_builder_reference, so it + // follows that tool's scope gate. + if (allowedToolNames && !allowedToolNames.has('get_agent_builder_reference')) return; + + server.resource( + 'agent-builder-reference', + AGENT_BUILDER_REFERENCE_URI, + { description: 'Reference for creating and managing n8n Agents through MCP.' }, + () => ({ + contents: [ + { + uri: AGENT_BUILDER_REFERENCE_URI, + mimeType: 'text/markdown', + text: AGENT_BUILDER_REFERENCE, + }, + ], + }), + ); + } + + private register( + server: McpServer, + tool: ToolDefinition, + ): void { + server.registerTool(tool.name, tool.config, tool.handler); + } + + private searchAgentsTool(user: User): ToolDefinition { + return { + name: 'search_agents', + config: { + description: + 'Search Agents the current user can access. Use publishedOnly and excludeAgentId to discover saved sub-agents. Other agent tools only operate on agents with availableInMCP: true.', + inputSchema: searchAgentsInput, + annotations: { + title: 'Search Agents', + readOnlyHint: true, + destructiveHint: false, + idempotentHint: true, + openWorldHint: false, + }, + }, + handler: async (input: SearchAgentsInput) => + await this.run(user, 'search_agents', { projectId: input.projectId }, async () => { + let projectIds: string[] | null; + if (input.projectId) { + await this.assertScope(user, input.projectId, 'agent:list'); + projectIds = [input.projectId]; + } else { + projectIds = await this.projectScopeService.getProjectIds(user, ['agent:list']); + } + const agents = await this.agentsService.findSummariesInProjects(projectIds, { + query: input.query?.trim() || undefined, + publishedOnly: input.publishedOnly, + excludeAgentId: input.excludeAgentId, + limit: input.limit, + }); + const data = agents.map((agent) => ({ + id: agent.id, + name: agent.name, + projectId: agent.projectId, + published: agent.activeVersionId !== null, + availableInMCP: agent.availableInMCP, + updatedAt: agent.updatedAt.toISOString(), + })); + return { ok: true, data, count: data.length }; + }), + }; + } + + private getAgentTool(user: User): ToolDefinition { + return { + name: 'get_agent', + config: { + description: + 'Read an Agent draft, sidecar resources, runnable state, and configHash. Call before mutate_agent. Pass versionId to inspect a published version snapshot instead of the draft.', + inputSchema: getAgentInput, + annotations: { + title: 'Get Agent', + readOnlyHint: true, + destructiveHint: false, + idempotentHint: true, + openWorldHint: false, + }, + }, + handler: async ({ agentId, versionId }) => + await this.run( + user, + 'get_agent', + { agentId, ...(versionId ? { versionId } : {}) }, + async () => { + const agent = await this.resolveAgent(user, agentId); + await this.assertScope(user, agent.projectId, 'agent:read'); + const snapshot = versionId + ? await this.getAgentVersionSnapshot(agent.projectId, agentId, versionId) + : await this.getAgentSnapshot(user, agent); + return { ok: true, ...snapshot }; + }, + ), + }; + } + + private createAgentTool(user: User): ToolDefinition { + return { + name: MCP_CREATE_AGENT_TOOL_NAME, + config: { + description: + 'Create an Agent draft, optionally with its initial model, credential, instructions, and ordinary tool configuration. Returns its n8n editor URL. Use mutate_agent afterward for skills, tasks, and custom tools.', + inputSchema: createAgentInput, + annotations: { + title: 'Create Agent', + readOnlyHint: false, + destructiveHint: false, + idempotentHint: false, + openWorldHint: false, + }, + }, + handler: async ({ projectId, name, config }: CreateAgentInput) => + await this.run(user, 'create_agent', { projectId }, async () => { + await this.assertScope(user, projectId, 'agent:create'); + const initialConfig = config ? { ...config, name } : undefined; + if (initialConfig) { + const validation = await this.agentConfigService.validateConfig(initialConfig); + if (!validation.valid) { + throw new UserError(`Invalid initial Agent config: ${validation.error}`); + } + await this.assertAccessibleCredentials(initialConfig, user, projectId); + } + + // Agents created over MCP stay operable over MCP. + const agent = await this.agentsService.create(projectId, name, { + availableInMCP: true, + }); + let configHash: string | null; + let versionId = agent.versionId; + try { + if (initialConfig) { + const result = await this.agentConfigService.updateConfig( + agent.id, + projectId, + initialConfig, + user, + ); + configHash = getAgentConfigHash(result.config); + versionId = result.versionId; + } else { + configHash = await this.fetchConfigHash(projectId, agent.id); + } + } catch (error) { + await this.agentsService.delete(agent.id, projectId); + throw error; + } + + return { + ok: true, + agent: { + id: agent.id, + name: agent.name, + projectId: agent.projectId, + published: false, + versionId, + activeVersionId: agent.activeVersionId, + }, + configHash, + url: this.getAgentUrl(projectId, agent.id), + }; + }), + }; + } + + private mutateAgentTool(user: User): ToolDefinition { + return { + name: 'mutate_agent', + config: { + description: + 'Apply one config, skill, task, or custom-tool mutation to an Agent draft. The operation fields sit directly on the operation object (no value wrapper), e.g. { "type": "config.patch", "patch": [...] }. Returns the next configHash for subsequent mutations.', + inputSchema: mutateAgentInput, + annotations: { + title: 'Mutate Agent', + readOnlyHint: false, + destructiveHint: true, + idempotentHint: false, + openWorldHint: false, + }, + }, + handler: async (input: MutateAgentInput) => + await this.run( + user, + 'mutate_agent', + { agentId: input.agentId, type: input.operation.type }, + async () => { + const agent = await this.resolveAgent(user, input.agentId); + const projectId = agent.projectId; + await this.assertScope(user, projectId, 'agent:update'); + const config = this.configFromEntity(agent); + const configHash = getAgentConfigHash(config); + if (configHash !== input.baseConfigHash) { + return { + ok: false, + code: 'stale_config', + agentId: input.agentId, + configHash, + message: 'Call get_agent before retrying the mutation.', + }; + } + + const { resource, config: newConfig } = await this.applyMutation( + user, + input, + config, + projectId, + ); + return { + ok: true, + agentId: input.agentId, + operation: input.operation.type, + configHash: newConfig + ? getAgentConfigHash(newConfig) + : await this.fetchConfigHash(projectId, input.agentId), + ...(resource ? { resource } : {}), + }; + }, + ), + }; + } + + private validateAgentTool(user: User): ToolDefinition { + return { + name: 'validate_agent', + config: { + description: + 'Validate an Agent draft, sidecar references, and user-accessible credentials. Returns its n8n editor URL.', + inputSchema: agentIdentityShape, + annotations: { + title: 'Validate Agent', + readOnlyHint: true, + destructiveHint: false, + idempotentHint: true, + openWorldHint: false, + }, + }, + handler: async ({ agentId }) => + await this.run(user, 'validate_agent', { agentId }, async () => { + const agent = await this.resolveAgent(user, agentId); + await this.assertScope(user, agent.projectId, 'agent:read'); + return { + ok: true, + ...(await this.validateAgent(user, agent)), + url: this.getAgentUrl(agent.projectId, agentId), + }; + }), + }; + } + + private publishAgentTool(user: User): ToolDefinition { + return { + name: 'publish_agent', + config: { + description: + 'Publish a valid Agent draft and activate its tasks and integrations. Pass versionId to republish a previously published version instead. Only call after the user explicitly requests or confirms publication; completing a build does not imply approval.', + inputSchema: publishAgentInput, + annotations: { + title: 'Publish Agent', + readOnlyHint: false, + destructiveHint: false, + idempotentHint: true, + openWorldHint: true, + }, + }, + handler: async ({ agentId, versionId }) => + await this.run( + user, + 'publish_agent', + { agentId, ...(versionId ? { versionId } : {}) }, + async () => { + const agent = await this.resolveAgent(user, agentId); + const projectId = agent.projectId; + await this.assertScope(user, projectId, 'agent:publish'); + // Republishing a snapshot skips draft validation, mirroring the + // REST publish endpoint: the draft is not what goes live. + if (!versionId) { + const validation = await this.validateAgent(user, agent); + if (!validation.valid) { + throw new UserError( + `Agent is not runnable: ${[...validation.errors, ...validation.missing].join(', ')}`, + ); + } + } + const { agent: publishedAgent } = await this.agentPublishService.publishAgent( + agentId, + projectId, + user, + 'builder', + versionId, + ); + return { + ok: true, + agentId, + published: true, + versionId: publishedAgent.versionId, + activeVersionId: publishedAgent.activeVersionId, + url: this.getAgentUrl(projectId, agentId), + }; + }, + ), + }; + } + + private revertAgentTool(user: User): ToolDefinition { + return { + name: 'revert_agent', + config: { + description: + 'Restore an Agent draft from a published version, overwriting the draft config, skills, tasks, and custom tools. Does not publish. Inspect the version with get_agent first; the response returns the new configHash.', + inputSchema: revertAgentInput, + annotations: { + title: 'Revert Agent', + readOnlyHint: false, + destructiveHint: true, + idempotentHint: true, + openWorldHint: false, + }, + }, + handler: async ({ agentId, versionId }) => + await this.run( + user, + 'revert_agent', + { agentId, ...(versionId ? { versionId } : {}) }, + async () => { + const { projectId } = await this.resolveAgent(user, agentId); + await this.assertScope(user, projectId, 'agent:update'); + const agent = versionId + ? await this.agentPublishService.revertToVersion(agentId, projectId, versionId) + : await this.agentPublishService.revertToPublishedAgent(agentId, projectId); + return { + ok: true, + agentId, + versionId: agent.versionId, + activeVersionId: agent.activeVersionId, + configHash: getAgentConfigHash(this.configFromEntity(agent)), + url: this.getAgentUrl(projectId, agentId), + }; + }, + ), + }; + } + + private listAgentVersionsTool(user: User): ToolDefinition { + return { + name: 'list_agent_versions', + config: { + description: + 'List the publish history of an Agent, newest first. Pass a versionId to get_agent to inspect a version before revert_agent or publish_agent.', + inputSchema: listAgentVersionsInput, + annotations: { + title: 'List Agent Versions', + readOnlyHint: true, + destructiveHint: false, + idempotentHint: true, + openWorldHint: false, + }, + }, + handler: async ({ agentId, limit, offset }) => + await this.run(user, 'list_agent_versions', { agentId }, async () => { + const { projectId } = await this.resolveAgent(user, agentId); + await this.assertScope(user, projectId, 'agent:read'); + const versions = await this.agentPublishService.listPublishHistory( + agentId, + projectId, + limit, + offset, + ); + return { ok: true, data: versions, count: versions.length }; + }), + }; + } + + private unpublishAgentTool(user: User): ToolDefinition { + return { + name: 'unpublish_agent', + config: { + description: 'Unpublish an Agent and stop its live tasks and integrations.', + inputSchema: agentIdentityShape, + annotations: { + title: 'Unpublish Agent', + readOnlyHint: false, + destructiveHint: true, + idempotentHint: true, + openWorldHint: true, + }, + }, + handler: async ({ agentId }) => + await this.run(user, 'unpublish_agent', { agentId }, async () => { + const { projectId } = await this.resolveAgent(user, agentId); + await this.assertScope(user, projectId, 'agent:unpublish'); + const agent = await this.agentPublishService.unpublishAgent( + agentId, + projectId, + user, + 'builder', + ); + return { + ok: true, + agentId, + published: false, + versionId: agent.versionId, + activeVersionId: agent.activeVersionId, + }; + }), + }; + } + + private deleteAgentTool(user: User): ToolDefinition { + return { + name: 'delete_agent', + config: { + description: 'Permanently delete an Agent and its associated resources.', + inputSchema: agentIdentityShape, + annotations: { + title: 'Delete Agent', + readOnlyHint: false, + destructiveHint: true, + idempotentHint: true, + openWorldHint: true, + }, + }, + handler: async ({ agentId }) => + await this.run(user, 'delete_agent', { agentId }, async () => { + const { projectId } = await this.resolveAgent(user, agentId); + await this.assertScope(user, projectId, 'agent:delete'); + const deleted = await this.agentsService.delete(agentId, projectId); + if (!deleted) throw new UserError(`Agent "${agentId}" not found`); + return { ok: true, deleted: true, agentId }; + }), + }; + } + + private discoverAssetsTool(user: User): ToolDefinition { + return { + name: 'discover_agent_assets', + config: { + description: + 'Discover model catalogs, chat integrations, attachable workflows, published sub-agents, or MCP registry servers.', + inputSchema: discoverAssetsInput, + annotations: { + title: 'Discover Agent Assets', + readOnlyHint: true, + destructiveHint: false, + idempotentHint: true, + openWorldHint: true, + }, + }, + handler: async (input: DiscoverAssetsInput) => + await this.run( + user, + 'discover_agent_assets', + { projectId: input.projectId, kind: input.kind }, + async () => { + await this.assertScope(user, input.projectId, 'agent:read'); + return { + ok: true, + kind: input.kind, + data: await this.discoverAssets(user, input), + }; + }, + ), + }; + } + + private verifyMcpServerTool(user: User): ToolDefinition { + return { + name: 'verify_agent_mcp_server', + config: { + description: + 'Test an MCP server with a user-accessible credential and return its available tools. Call before writing an mcpServers config entry; validate_agent performs no live MCP check.', + inputSchema: verifyMcpServerInput, + annotations: { + title: 'Verify Agent MCP Server', + readOnlyHint: true, + destructiveHint: false, + idempotentHint: true, + openWorldHint: true, + }, + }, + handler: async (input: VerifyMcpServerInput) => + await this.run( + user, + 'verify_agent_mcp_server', + { projectId: input.projectId, authentication: input.authentication }, + async () => await this.verifyMcpServer(user, input), + ), + }; + } + + private updateIntegrationTool(user: User): ToolDefinition { + return { + name: 'update_agent_integration', + config: { + description: + 'Connect or disconnect a Slack, Telegram, or Linear conversation integration. This is the only way to manage integrations; config.replace and config.patch cannot touch them. Connecting publishes the current Agent draft, so only connect after explicit user confirmation.', + inputSchema: updateIntegrationInput, + annotations: { + title: 'Update Agent Integration', + readOnlyHint: false, + destructiveHint: true, + idempotentHint: true, + openWorldHint: true, + }, + }, + handler: async (input: UpdateIntegrationInput) => + await this.run( + user, + 'update_agent_integration', + { + agentId: input.agentId, + action: input.action, + type: input.type, + }, + async () => await this.updateIntegration(user, input), + ), + }; + } + + private referenceTool(user: User): ToolDefinition { + return { + name: 'get_agent_builder_reference', + config: { + description: + 'Return the required reference for Agent configuration and mutate_agent operations. Read before building an Agent.', + inputSchema: emptyInput, + annotations: { + title: 'Get Agent Builder Reference', + readOnlyHint: true, + destructiveHint: false, + idempotentHint: true, + openWorldHint: false, + }, + }, + handler: async () => + await this.run(user, 'get_agent_builder_reference', {}, () => ({ + ok: true, + uri: AGENT_BUILDER_REFERENCE_URI, + guide: AGENT_BUILDER_GUIDE, + configSchema: AGENT_CONFIG_JSON_SCHEMA, + })), + }; + } + + private async getAgentSnapshot(user: User, agent: Agent) { + const { id: agentId, projectId } = agent; + const config = this.configFromEntity(agent); + const credentialProvider = this.credentialProvider(user, projectId); + const [runnable, skills, tasks] = await Promise.all([ + this.agentValidationService.validateAgentIsRunnable(agentId, projectId, credentialProvider), + this.agentSkillsService.listSkills(agentId, projectId), + this.agentTaskService.list(agentId), + ]); + const taskEnabled = new Map((config.tasks ?? []).map((task) => [task.id, task.enabled])); + + // The hash covers the full persisted config (integrations included) so it + // stays consistent with mutate_agent and update_agent_integration, but the + // config exposed to the client omits integrations: they are a read-only + // runtime surface reported separately, not part of the editable config. + const { integrations: _integrations, ...editableConfig } = config; + + return { + agent: { + id: agent.id, + name: agent.name, + projectId: agent.projectId, + published: agent.activeVersionId !== null, + versionId: agent.versionId, + activeVersionId: agent.activeVersionId, + createdAt: agent.createdAt.toISOString(), + updatedAt: agent.updatedAt.toISOString(), + }, + config: editableConfig, + configHash: getAgentConfigHash(config), + isRunnable: runnable.missing.length === 0, + missing: runnable.missing, + skills, + tasks: tasks.map((task) => ({ ...task, enabled: taskEnabled.get(task.id) ?? false })), + customTools: Object.entries(agent.tools ?? {}).map(([id, tool]) => ({ + id, + descriptor: tool.descriptor, + })), + integrations: agent.integrations ?? [], + }; + } + + /** + * Read-only view of a published version snapshot. Deliberately returns no + * configHash: mutations only ever apply to the draft, so a snapshot hash + * must not be usable as a mutate_agent baseConfigHash. + */ + private async getAgentVersionSnapshot(projectId: string, agentId: string, versionId: string) { + const { agent, version, tasks } = await this.agentPublishService.getVersion( + agentId, + projectId, + versionId, + ); + if (!version.schema) throw new UserError(`Version "${versionId}" has no JSON config.`); + // Integrations are a read-only runtime surface, omitted from the config + // here for the same reason as in getAgentSnapshot. + const { integrations: _integrations, ...editableConfig } = version.schema; + + return { + agent: { + id: agent.id, + name: agent.name, + projectId: agent.projectId, + published: agent.activeVersionId !== null, + versionId: agent.versionId, + activeVersionId: agent.activeVersionId, + }, + version: { + versionId: version.versionId, + author: version.author, + createdAt: version.createdAt.toISOString(), + isActive: version.versionId === agent.activeVersionId, + }, + config: editableConfig, + skills: version.skills ?? {}, + tasks: tasks.map((task) => ({ + id: task.taskId, + name: task.name, + objective: task.objective, + cronExpression: task.cronExpression, + enabled: task.enabled, + })), + customTools: Object.entries(version.tools ?? {}).map(([id, tool]) => ({ + id, + descriptor: tool.descriptor, + })), + }; + } + + private async fetchConfigHash(projectId: string, agentId: string) { + return getAgentConfigHash(await this.agentConfigService.getConfig(agentId, projectId)); + } + + private configFromEntity(agent: Agent): AgentJsonConfig { + const config = composeJsonConfig(agent); + if (!config) throw new UserError('Agent has no JSON config yet.'); + return config; + } + + private getAgentUrl(projectId: string, agentId: string) { + return `${this.urlService.getInstanceBaseUrl()}/projects/${encodeURIComponent(projectId)}/agents/${encodeURIComponent(agentId)}`; + } + + /** + * Applies one mutation against the stale-checked `config`. Returns the + * post-mutation config when the mutation itself produced it; the caller + * re-fetches otherwise (sidecar services update config references + * internally). + */ + private async applyMutation( + user: User, + input: MutateAgentInput, + config: AgentJsonConfig, + projectId: string, + ): Promise<{ resource?: MutationResource; config?: AgentJsonConfig }> { + const { agentId, operation } = input; + switch (operation.type) { + case 'config.replace': { + if (operation.config.integrations !== undefined) { + throw new UserError(INTEGRATIONS_NOT_IN_CONFIG_MESSAGE); + } + this.assertSanitizeStable(operation.config, config); + this.assertPassesConfigGuards(operation.config, config); + await this.assertAccessibleCredentials(operation.config, user, projectId); + const result = await this.agentConfigService.updateConfig( + agentId, + projectId, + operation.config, + user, + { clearOmittedOptionalFields: true }, + ); + return { config: result.config }; + } + case 'config.patch': { + const jsonpatch = (await import('fast-json-patch')).default; + const patchError = jsonpatch.validate(operation.patch, config); + if (patchError) throw new UserError(patchError.message ?? 'Invalid JSON patch'); + const patched: unknown = jsonpatch.applyPatch( + jsonpatch.deepClone(config), + operation.patch, + ).newDocument; + if (integrationsChanged(config, patched)) { + throw new UserError(INTEGRATIONS_NOT_IN_CONFIG_MESSAGE); + } + this.assertSanitizeStable(patched, config); + this.assertPassesConfigGuards(patched, config); + await this.assertAccessibleCredentials(patched, user, projectId); + const result = await this.agentConfigService.updateConfig( + agentId, + projectId, + patched, + user, + { + clearOmittedOptionalFields: true, + }, + ); + return { config: result.config }; + } + case 'skill.upsert': + if (operation.skillId) { + const result = await this.agentSkillsService.updateSkill( + agentId, + projectId, + operation.skillId, + operation.skill, + ); + return { resource: { type: 'skill', id: result.id } }; + } else { + const result = await this.agentSkillsService.createAndAttachSkill( + agentId, + projectId, + operation.skill, + ); + return { resource: { type: 'skill', id: result.id } }; + } + case 'skill.delete': + await this.agentSkillsService.deleteSkill(agentId, projectId, operation.skillId); + return { resource: { type: 'skill', id: operation.skillId } }; + case 'task.upsert': + if (operation.taskId) { + const result = await this.agentTaskService.update( + agentId, + operation.taskId, + operation.task, + ); + if (operation.enabled !== undefined) { + const updated = await this.setTaskEnabled( + user, + agentId, + projectId, + operation.taskId, + operation.enabled, + config, + ); + return { resource: { type: 'task', id: result.id }, config: updated }; + } + return { resource: { type: 'task', id: result.id } }; + } else { + const result = await this.agentTaskService.create(agentId, { + ...operation.task, + enabled: operation.enabled ?? true, + }); + return { resource: { type: 'task', id: result.id } }; + } + case 'task.delete': + await this.agentTaskService.delete(agentId, operation.taskId); + return { resource: { type: 'task', id: operation.taskId } }; + case 'customTool.upsert': { + const descriptor = await this.agentSecureRuntime.describeToolSecurely(operation.code); + const built = await this.agentCustomToolsService.buildCustomTool( + agentId, + projectId, + operation.code, + descriptor, + ); + if ((config.tools ?? []).some((tool) => tool.type === 'custom' && tool.id === built.id)) { + return { resource: { type: 'customTool', id: built.id }, config }; + } + const next = { + ...config, + tools: [...(config.tools ?? []), { type: 'custom' as const, id: built.id }], + }; + await this.assertAccessibleCredentials(next, user, projectId); + const result = await this.agentConfigService.updateConfig(agentId, projectId, next, user); + return { resource: { type: 'customTool', id: built.id }, config: result.config }; + } + case 'customTool.delete': + await this.agentCustomToolsService.deleteCustomTool(agentId, projectId, operation.toolId); + return { resource: { type: 'customTool', id: operation.toolId } }; + } + } + + /** + * updateConfig sanitizes configs on write, silently dropping array entries + * whose `type` the schema does not recognize. Reject differences the + * mutation itself introduced loudly instead of reporting a success that + * persisted less than the client sent. Instability inherited unchanged from + * the stored config (e.g. a legacy field persisted before the schema + * dropped it) is tolerated — the write path cleans it up. + */ + private assertSanitizeStable(config: unknown, baseConfig: AgentJsonConfig): void { + const sanitized = sanitizeAgentJsonConfig(config); + if (JSON.stringify(sanitized) === JSON.stringify(config)) return; + if (!isRecord(config) || !isRecord(sanitized)) return; + const base: Record = baseConfig; + const fields = Object.keys(config).filter((key) => { + const submitted = JSON.stringify(config[key]); + return ( + submitted !== JSON.stringify(sanitized[key]) && submitted !== JSON.stringify(base[key]) + ); + }); + if (fields.length === 0) return; + throw new UserError( + `Config contains entries the schema does not support (in: ${fields.join(', ')}); saving would silently drop them. Compare against the config schema from get_agent_builder_reference — e.g. sub-agents belong under subAgents.agents, not tools.`, + ); + } + + /** + * Model-independent write guards shared with the in-app builder pipeline, + * so a config the builder refuses cannot be persisted through MCP. Schema + * failures are left for updateConfig's own validation to surface. + */ + private assertPassesConfigGuards(next: unknown, baseConfig: AgentJsonConfig): void { + const parsed = AgentJsonConfigSchema.safeParse(sanitizeAgentJsonConfig(next)); + if (!parsed.success) return; + const errors = + rejectIfEmptyInstructions(parsed.data, MCP_AGENT_CONFIG_MESSAGES) ?? + rejectIfUnsupportedNativeWebSearch(parsed.data) ?? + rejectIfDynamicSelectorUsesFromAi( + parsed.data, + baseConfig, + this.nodeTypes, + MCP_AGENT_CONFIG_MESSAGES, + ); + if (errors) { + throw new UserError(errors.map((error) => `${error.path}: ${error.message}`).join('; ')); + } + } + + /** + * updateConfig quietly rewrites credential references the calling user + * cannot use to `''` before saving. Surfacing that here turns silent + * credential loss — including references already stored on the agent by + * another user — into an explicit error the client can act on. + */ + private async assertAccessibleCredentials( + config: unknown, + user: User, + projectId: string, + ): Promise { + const accessibleIds = new Set( + (await this.credentialProvider(user, projectId).list()).map((credential) => credential.id), + ); + const sanitized = sanitizeUnknownAgentCredentials(config, accessibleIds); + if (JSON.stringify(sanitized) === JSON.stringify(config)) return; + const ids = new Set(); + collectClearedCredentialIds(config, sanitized, ids); + throw new UserError( + `Config references credentials you cannot use in this project: ${[...ids].join(', ')}. Saving would silently clear them. Use list_credentials to find an accessible credential, or remove the reference.`, + ); + } + + private async setTaskEnabled( + user: User, + agentId: string, + projectId: string, + taskId: string, + enabled: boolean, + config: AgentJsonConfig, + ): Promise { + let found = false; + const tasks = (config.tasks ?? []).map((task) => { + if (task.id !== taskId) return task; + found = true; + return { ...task, enabled }; + }); + if (!found) throw new UserError(`Task "${taskId}" is not attached to the Agent`); + const next = { ...config, tasks }; + await this.assertAccessibleCredentials(next, user, projectId); + const result = await this.agentConfigService.updateConfig(agentId, projectId, next, user); + return result.config; + } + + /** + * Publish-scope validation — the same pass `publishAgent` enforces — plus + * the node-tool JSON-Schema checks from `validateConfig`, so validate_agent + * and publish_agent cannot drift from the canonical validator. + */ + private async validateAgent(user: User, agent: Agent) { + const projectId = agent.projectId; + const config = this.configFromEntity(agent); + const credentialProvider = this.credentialProvider(user, projectId); + const [schema, configuration] = await Promise.all([ + this.agentConfigService.validateConfig(config), + this.agentValidationService.validateLoadedAgentConfiguration( + agent, + projectId, + credentialProvider, + 'publish', + ), + ]); + const errors = schema.valid ? [] : [schema.error]; + const missing = [...new Set(configuration.issues.map((issue) => issue.path))]; + return { + valid: errors.length === 0 && missing.length === 0, + errors, + missing, + }; + } + + private async discoverAssets(user: User, input: DiscoverAssetsInput): Promise { + switch (input.kind) { + case 'models': { + if (input.provider) { + return await this.agentModelCatalogService.getProviderModels( + user, + input.projectId, + input.provider, + input.credentialId, + ); + } + // The full catalog runs to hundreds of KB — far past MCP client + // token limits — so without a provider return a summary instead. + const catalog = filterOfferedAgentModelProviders( + await (await import('@n8n/agents')).fetchProviderCatalog(), + ); + return { + providers: Object.values(catalog).map((provider) => ({ + provider: provider.id, + name: provider.name, + modelCount: Object.keys(provider.models).length, + })), + hint: 'Pass provider (and optionally credentialId) to list the models of one provider.', + }; + } + case 'integrations': + return this.integrationPersistenceService.listChatIntegrations().map((integration) => ({ + ...integration, + settingsRequired: integration.type === 'telegram', + ...(integration.type === 'telegram' + ? { + settingsSchema: TELEGRAM_SETTINGS_JSON_SCHEMA, + settingsGuidance: + 'Pass accessMode=public with allowedUsers=[], or accessMode=private with at least one Telegram user ID or username.', + } + : {}), + })); + case 'workflows': + return await this.attachableWorkflowsService.list(user, input.projectId, input.query); + case 'subagents': { + const summaries = await this.agentsService.findSummariesInProjects([input.projectId], { + query: input.query?.trim() || undefined, + publishedOnly: true, + excludeAgentId: input.excludeAgentId, + }); + return summaries.map((agent) => ({ agentId: agent.id, name: agent.name })); + } + case 'mcpServers': + return input.query + ? await this.mcpRegistryService.search([input.query]) + : await this.mcpRegistryService.list(MCP_SERVER_DISCOVERY_LIMIT); + } + } + + private async verifyMcpServer(user: User, input: VerifyMcpServerInput) { + await this.assertScope(user, input.projectId, 'agent:read'); + const credentialProvider = this.credentialProvider(user, input.projectId); + if (input.authentication !== 'none') { + if (!input.credential) { + throw new UserError('credential is required when authentication is not none'); + } + await this.requireAccessibleCredential(credentialProvider, input.credential); + } + + const tools = await listMcpServerTools( + { + name: input.name, + url: input.url, + transport: input.transport, + authentication: input.authentication, + credential: input.credential, + ...(input.connectionTimeoutMs !== undefined + ? { connectionTimeoutMs: input.connectionTimeoutMs } + : {}), + }, + { + credentialProvider, + oauthService: this.oauthService, + projectId: input.projectId, + proxyFetch: createAiMcpFetch( + this.outboundHttp, + this.ssrfConfig, + this.ssrfProtectionService, + ), + }, + ); + return { ok: true, tools }; + } + + private async updateIntegration(user: User, input: UpdateIntegrationInput) { + const agent = await this.resolveAgent(user, input.agentId); + const projectId = agent.projectId; + await this.assertScope(user, projectId, 'agent:update'); + if (input.action === 'connect') { + // Connecting publishes the current draft, so it needs the publish + // scope on top of update (mirrors the builder's connect flow). + await this.assertScope(user, projectId, 'agent:publish'); + } + return input.action === 'disconnect' + ? await this.disconnectIntegration(input, agent) + : await this.connectIntegration(user, input, agent, projectId); + } + + private async disconnectIntegration(input: UpdateIntegrationInput, agent: Agent) { + const persisted = (agent.integrations ?? []).find( + (item) => item.type === input.type && item.credentialId === input.credentialId, + ); + // Mirrors AgentIntegrationsController.disconnectIntegration: tear down + // the runtime channel even when persistence has no matching record + // (e.g. the integration was removed via a config mutation). + const parsed = AgentIntegrationSchema.safeParse({ + type: input.type, + credentialId: input.credentialId, + }); + const integration = persisted ?? (parsed.success ? parsed.data : undefined); + if (integration) { + await this.chatIntegrationService.disconnectChannel(input.agentId, integration); + } else { + await this.chatIntegrationService.disconnect(input.agentId, { + type: input.type, + credentialId: input.credentialId, + }); + } + const saved = await this.integrationPersistenceService.removeCredentialIntegration( + agent, + input.type, + input.credentialId, + { broadcast: false }, + ); + return { + ok: true, + agentId: input.agentId, + integration: { type: input.type, credentialId: input.credentialId }, + connected: false, + published: saved.activeVersionId !== null, + activeVersionId: saved.activeVersionId, + configHash: getAgentConfigHash(this.configFromEntity(saved)), + }; + } + + private async connectIntegration( + user: User, + input: UpdateIntegrationInput, + agent: Agent, + projectId: string, + ) { + const candidate = { + type: input.type, + credentialId: input.credentialId, + ...(input.settings ? { settings: input.settings } : {}), + }; + const parsed = AgentIntegrationSchema.safeParse(candidate); + if (!parsed.success) throw new UserError(`Invalid integration: ${parsed.error.message}`); + if (parsed.data.type === 'telegram' && !parsed.data.settings) { + throw new UserError('Telegram integration settings are required'); + } + + const credential = await this.requireAccessibleCredential( + this.credentialProvider(user, projectId), + input.credentialId, + ); + const implementation = this.chatIntegrationRegistry.require(parsed.data.type); + if (!implementation.credentialTypes.includes(credential.type)) { + throw new UserError( + `${implementation.displayLabel} integrations do not support ${credential.type} credentials`, + ); + } + + const saved = await this.integrationPersistenceService.saveCredentialIntegration( + agent, + parsed.data, + { broadcast: false }, + ); + const { agent: publishedAgent } = await this.agentPublishService.publishAgent( + input.agentId, + projectId, + user, + 'channel_connect', + undefined, + { syncIntegrations: false, ignoreDraftIntegrations: true }, + ); + await this.chatIntegrationService.connect(input.agentId, parsed.data, projectId); + await this.chatIntegrationService.broadcastIntegrationChange( + input.agentId, + parsed.data, + 'connect', + ); + return { + ok: true, + agentId: input.agentId, + integration: { type: input.type, credentialId: input.credentialId }, + connected: true, + published: true, + activeVersionId: publishedAgent.activeVersionId, + // Publishing with syncIntegrations:false touches neither schema nor + // integrations, so the entity saved above still hashes correctly. + configHash: getAgentConfigHash(this.configFromEntity(saved)), + }; + } + + private credentialProvider(user: User, projectId: string): CredentialProvider { + return createAgentCredentialProvider(this.credentialsService, projectId, user); + } + + private async requireAccessibleCredential( + credentialProvider: CredentialProvider, + credentialId: string, + ) { + const credential = (await credentialProvider.list()).find((item) => item.id === credentialId); + if (!credential) throw new UserError('Credential not found or not accessible'); + return credential; + } + + /** + * An Agent belongs to exactly one project, so by-ID tools derive the project + * from the agentId rather than making the client supply it, scoped to the + * projects the user can access. Returns the loaded entity so callers don't + * re-fetch the same row. + * + * Mirrors the per-workflow `availableInMCP` guard in `getMcpWorkflow`: MCP + * tools may only operate on agents explicitly made available in MCP. + * `search_agents` intentionally still sees every accessible agent so + * clients can tell the user what exists. + */ + private async resolveAgent(user: User, agentId: string): Promise { + const agent = await this.agentsService.findByIdForUser(agentId, user); + if (!agent) throw new UserError(`Agent "${agentId}" not found`); + if (!agent.availableInMCP) { + throw new UserError( + 'Agent is not available in MCP. Enable MCP access from the agents list, or from the MCP settings page.', + ); + } + return agent; + } + + private async assertScope(user: User, projectId: string, scope: Scope): Promise { + if (!(await userHasScopes(user, [scope], false, { projectId }))) { + throw new ForbiddenError('You do not have permission to access agents in this project.'); + } + } + + private async run( + user: User, + toolName: string, + parameters: Record, + action: () => Record | Promise>, + ) { + const telemetryPayload: UserCalledMCPToolEventPayload = { + user_id: user.id, + tool_name: toolName, + parameters, + }; + try { + const data = await action(); + telemetryPayload.results = { success: data.ok !== false }; + this.telemetry.track(USER_CALLED_MCP_TOOL_EVENT, telemetryPayload); + return toolResult(data, data.ok === false); + } catch (error) { + const message = error instanceof Error ? error.message : String(error); + telemetryPayload.results = { success: false, error: message }; + this.telemetry.track(USER_CALLED_MCP_TOOL_EVENT, telemetryPayload); + return toolResult({ ok: false, code: 'agent_tool_error', error: message }, true); + } + } +} diff --git a/packages/cli/src/modules/mcp/tools/get-execution.tool.ts b/packages/cli/src/modules/mcp/tools/get-execution.tool.ts index fbfd4ea4853..659ba740b2b 100644 --- a/packages/cli/src/modules/mcp/tools/get-execution.tool.ts +++ b/packages/cli/src/modules/mcp/tools/get-execution.tool.ts @@ -68,14 +68,14 @@ export const createGetExecutionTool = ( workflowFinderService: WorkflowFinderService, telemetry: Telemetry, ): ToolDefinition => ({ - name: 'get_execution', + name: 'get_workflow_execution', config: { description: - 'Get execution details by execution ID and workflow ID. By default returns metadata only. Set includeData to true to include node execution data, optionally filtered by nodeNames and truncated by truncateData.', + 'Get workflow execution details by execution ID and workflow ID. By default returns metadata only. Set includeData to true to include node execution data, optionally filtered by nodeNames and truncated by truncateData.', inputSchema: inputSchema.shape, outputSchema, annotations: { - title: 'Get Execution', + title: 'Get Workflow Execution', readOnlyHint: true, destructiveHint: false, idempotentHint: true, @@ -91,7 +91,7 @@ export const createGetExecutionTool = ( }: z.infer) => { const telemetryPayload: UserCalledMCPToolEventPayload = { user_id: user.id, - tool_name: 'get_execution', + tool_name: 'get_workflow_execution', parameters: { workflowId, executionId, includeData, nodeNames, truncateData }, }; diff --git a/packages/cli/src/modules/mcp/tools/get-workflow-details.tool.ts b/packages/cli/src/modules/mcp/tools/get-workflow-details.tool.ts index 841c4eb31c1..643d4401d4a 100644 --- a/packages/cli/src/modules/mcp/tools/get-workflow-details.tool.ts +++ b/packages/cli/src/modules/mcp/tools/get-workflow-details.tool.ts @@ -1,5 +1,5 @@ import type { User } from '@n8n/db'; -import type { INodeTypes } from 'n8n-workflow'; +import { isTriggerNodeType, type INodeTypes } from 'n8n-workflow'; import z from 'zod'; import type { CredentialsService } from '@/credentials/credentials.service'; @@ -149,13 +149,18 @@ export async function getWorkflowDetails( : null; const supportedTriggers = Object.keys(SUPPORTED_MCP_TRIGGERS); - const triggers = nodes.filter( - (node) => supportedTriggers.includes(node.type) && node.disabled !== true, + const activeNodes = nodes.filter((node) => node.disabled !== true); + const triggers = activeNodes.filter((node) => supportedTriggers.includes(node.type)); + // Triggers the workflow does have but MCP can't execute directly (e.g. Gmail Trigger), + // so the notice can distinguish these from a workflow with no triggers at all. + const unsupportedTriggers = activeNodes.filter( + (node) => isTriggerNodeType(node.type) && !supportedTriggers.includes(node.type), ); const triggerNotice = await getTriggerDetails( user, triggers, + unsupportedTriggers, baseWebhookUrl, credentialsService, nodeTypes, diff --git a/packages/cli/src/modules/mcp/tools/list-credentials.tool.ts b/packages/cli/src/modules/mcp/tools/list-credentials.tool.ts index 71aef953bc6..f21bf2ab521 100644 --- a/packages/cli/src/modules/mcp/tools/list-credentials.tool.ts +++ b/packages/cli/src/modules/mcp/tools/list-credentials.tool.ts @@ -118,7 +118,7 @@ export const createListCredentialsTool = ( name: 'list_credentials', config: { description: - 'List credentials the current user can access. Use this to find a credential ID before referencing it from a workflow node. Never returns credential secret data.', + 'List credentials the current user can access. Use this to find a credential ID before referencing it anywhere one is required. Never returns credential secret data.', inputSchema, outputSchema, annotations: { diff --git a/packages/cli/src/modules/mcp/tools/list-tags.tool.ts b/packages/cli/src/modules/mcp/tools/list-tags.tool.ts index 632b3ee0538..6dc4e3da912 100644 --- a/packages/cli/src/modules/mcp/tools/list-tags.tool.ts +++ b/packages/cli/src/modules/mcp/tools/list-tags.tool.ts @@ -62,13 +62,13 @@ export const createListTagsTool = ( tagService: TagService, telemetry: Telemetry, ): ToolDefinition => ({ - name: 'list_tags', + name: 'list_workflow_tags', config: { description: 'List all workflow tags in the instance.', inputSchema, outputSchema, annotations: { - title: 'List Tags', + title: 'List Workflow Tags', readOnlyHint: true, destructiveHint: false, idempotentHint: true, @@ -78,7 +78,7 @@ export const createListTagsTool = ( handler: async ({ limit = MAX_RESULTS }: ListTagsParams) => { const telemetryPayload: UserCalledMCPToolEventPayload = { user_id: user.id, - tool_name: 'list_tags', + tool_name: 'list_workflow_tags', parameters: { limit }, }; diff --git a/packages/cli/src/modules/mcp/tools/prepare-workflow-pin-data.tool.ts b/packages/cli/src/modules/mcp/tools/prepare-workflow-pin-data.tool.ts index 1ddd6dfea85..b962db7fac4 100644 --- a/packages/cli/src/modules/mcp/tools/prepare-workflow-pin-data.tool.ts +++ b/packages/cli/src/modules/mcp/tools/prepare-workflow-pin-data.tool.ts @@ -62,14 +62,14 @@ export const createPrepareTestPinDataTool = ( telemetry: Telemetry, logger: Logger, ): ToolDefinition => ({ - name: 'prepare_test_pin_data', + name: 'prepare_workflow_pin_data', config: { description: 'Prepare test pin data for a workflow. Trigger nodes, nodes with credentials, and HTTP Request nodes need pin data. Logic nodes (Set, If, Code, etc.) and credential-free I/O nodes (Execute Command, file read/write) execute normally without pin data. Returns JSON Schemas describing the expected output shape for each node that needs pin data — schemas are derived from past execution output shapes or node type definitions. No actual user data is returned. You should generate realistic sample data for the schemas, use empty defaults for nodes without schema, merge everything into a single pinData object, and pass it to test_workflow.', inputSchema: inputSchema.shape, outputSchema, annotations: { - title: 'Prepare Test Pin Data', + title: 'Prepare Workflow Pin Data', readOnlyHint: true, destructiveHint: false, idempotentHint: true, @@ -79,7 +79,7 @@ export const createPrepareTestPinDataTool = ( handler: async ({ workflowId }: z.infer) => { const telemetryPayload: UserCalledMCPToolEventPayload = { user_id: user.id, - tool_name: 'prepare_test_pin_data', + tool_name: 'prepare_workflow_pin_data', parameters: { workflowId }, }; diff --git a/packages/cli/src/modules/mcp/tools/publish-workflow.tool.ts b/packages/cli/src/modules/mcp/tools/publish-workflow.tool.ts index ebb74fed86d..d4a9c493261 100644 --- a/packages/cli/src/modules/mcp/tools/publish-workflow.tool.ts +++ b/packages/cli/src/modules/mcp/tools/publish-workflow.tool.ts @@ -1,16 +1,26 @@ +import { + type WorkflowPublishBlockedDetails, + type WorkflowPublishBlockedReason, + workflowPublishBlockedDetailsSchema, +} from '@n8n/api-types'; import type { User } from '@n8n/db'; import { ensureError } from '@n8n/utils/errors/ensure-error'; import { jsonStringify } from 'n8n-workflow'; import z from 'zod'; import type { CollaborationService } from '@/collaboration/collaboration.service'; +import { WorkflowPublishBlockedError } from '@/errors/response-errors/workflow-publish-blocked.error'; import type { Telemetry } from '@/telemetry'; import type { WorkflowFinderService } from '@/workflows/workflow-finder.service'; import type { WorkflowService } from '@/workflows/workflow.service'; import { USER_CALLED_MCP_TOOL_EVENT } from '../mcp.constants'; import { WorkflowAccessError } from '../mcp.errors'; -import type { ToolDefinition, UserCalledMCPToolEventPayload } from '../mcp.types'; +import type { + ToolDefinition, + UserCalledMCPToolEventPayload, + WorkflowNotFoundReason, +} from '../mcp.types'; import { getMcpWorkflow } from './workflow-validation.utils'; const inputSchema = z.object({ @@ -28,15 +38,27 @@ type PublishWorkflowOutput = { workflowId: string; activeVersionId: string | null; error?: string; -}; +} & Partial; const outputSchema = { success: z.boolean(), workflowId: z.string(), activeVersionId: z.string().nullable(), error: z.string().optional(), + reason: workflowPublishBlockedDetailsSchema.shape.reason.optional(), + workflowReviewRequestId: + workflowPublishBlockedDetailsSchema.shape.workflowReviewRequestId.optional(), } satisfies z.ZodRawShape; +/** Only these two failures carry a machine-readable reason; anything else reports just its message. */ +const extractErrorReason = ( + error: Error, +): WorkflowPublishBlockedReason | WorkflowNotFoundReason | undefined => { + if (error instanceof WorkflowPublishBlockedError) return error.details.reason; + if (error instanceof WorkflowAccessError) return error.reason; + return undefined; +}; + export const createPublishWorkflowTool = ( user: User, workflowFinderService: WorkflowFinderService, @@ -98,19 +120,21 @@ export const createPublishWorkflowTool = ( }; } catch (er) { const error = ensureError(er); - const isAccessError = error instanceof WorkflowAccessError; + const publishBlockedDetails = + error instanceof WorkflowPublishBlockedError ? error.details : undefined; const output: PublishWorkflowOutput = { success: false, workflowId, activeVersionId: null, error: error.message, + ...publishBlockedDetails, }; telemetryPayload.results = { success: false, error: error.message, - error_reason: isAccessError ? error.reason : undefined, + error_reason: extractErrorReason(error), }; telemetry.track(USER_CALLED_MCP_TOOL_EVENT, telemetryPayload); diff --git a/packages/cli/src/modules/mcp/tools/search-executions.tool.ts b/packages/cli/src/modules/mcp/tools/search-executions.tool.ts index f0af42cd740..2a34c059ad3 100644 --- a/packages/cli/src/modules/mcp/tools/search-executions.tool.ts +++ b/packages/cli/src/modules/mcp/tools/search-executions.tool.ts @@ -67,14 +67,14 @@ export const createSearchExecutionsTool = ( workflowFinderService: WorkflowFinderService, telemetry: Telemetry, ): ToolDefinition => ({ - name: 'search_executions', + name: 'search_workflow_executions', config: { description: 'Search for workflow executions with optional filters. Returns execution metadata including status, timing, and workflow ID.', inputSchema, outputSchema, annotations: { - title: 'Search Executions', + title: 'Search Workflow Executions', readOnlyHint: true, destructiveHint: false, idempotentHint: true, @@ -99,7 +99,7 @@ export const createSearchExecutionsTool = ( const parameters = { workflowId, status, startedAfter, startedBefore, limit, lastId }; const telemetryPayload: UserCalledMCPToolEventPayload = { user_id: user.id, - tool_name: 'search_executions', + tool_name: 'search_workflow_executions', parameters, }; diff --git a/packages/cli/src/modules/mcp/tools/search-projects.tool.ts b/packages/cli/src/modules/mcp/tools/search-projects.tool.ts index e345a08897b..a1214cfb666 100644 --- a/packages/cli/src/modules/mcp/tools/search-projects.tool.ts +++ b/packages/cli/src/modules/mcp/tools/search-projects.tool.ts @@ -68,7 +68,7 @@ export const createSearchProjectsTool = ( name: 'search_projects', config: { description: - "Search for projects accessible to the current user. Call this whenever the user names a project — pass the name as the query, then use the resolved ID with create_workflow_from_code or update_workflow. Results are ranked with exact case-insensitive name matches first. If no exact match is found but multiple partials are returned, the response includes a `hint` field telling you to clarify with the user before acting; follow it instead of guessing. The response also includes `teamProjectsEnabled` — when false, team projects are not licensed on this instance, so default to creating workflows in the caller's personal project unless the user explicitly picks one of the returned accessible projects.", + "Search for projects accessible to the current user. Call this whenever the user names a project — pass the name as the query, then use the resolved ID with tools that take a projectId. Results are ranked with exact case-insensitive name matches first. If no exact match is found but multiple partials are returned, the response includes a `hint` field telling you to clarify with the user before acting; follow it instead of guessing. The response also includes `teamProjectsEnabled` — when false, team projects are not licensed on this instance, so default to creating in the caller's personal project unless the user explicitly picks one of the returned accessible projects.", inputSchema, outputSchema, annotations: { diff --git a/packages/cli/src/modules/mcp/tools/test-workflow.tool.ts b/packages/cli/src/modules/mcp/tools/test-workflow.tool.ts index b330cdfc92c..216f4732241 100644 --- a/packages/cli/src/modules/mcp/tools/test-workflow.tool.ts +++ b/packages/cli/src/modules/mcp/tools/test-workflow.tool.ts @@ -35,7 +35,7 @@ const inputSchema = z.object({ pinData: z .record(z.array(z.record(z.unknown()))) .describe( - 'Pin data for all workflow nodes. Use the prepare_test_pin_data tool to generate this. Keys are node names, values are arrays of items. Each item MUST be wrapped in a "json" property, e.g. [{"json": {"id": "123", "name": "test"}}]. Do NOT pass flat objects like [{"id": "123"}].', + 'Pin data for all workflow nodes. Use the prepare_workflow_pin_data tool to generate this. Keys are node names, values are arrays of items. Each item MUST be wrapped in a "json" property, e.g. [{"json": {"id": "123", "name": "test"}}]. Do NOT pass flat objects like [{"id": "123"}].', ), triggerNodeName: z .string() @@ -80,7 +80,7 @@ export const createTestWorkflowTool = ( name: 'test_workflow', config: { description: - 'Test a workflow using pin data to bypass external services. Trigger nodes, nodes with credentials, and HTTP Request nodes are pinned (use simulated data). Other nodes (Set, If, Code, etc.) execute normally — including credential-free I/O nodes like Execute Command or file read/write nodes. Use prepare_test_pin_data to generate the pin data first.', + 'Test a workflow using pin data to bypass external services. Trigger nodes, nodes with credentials, and HTTP Request nodes are pinned (use simulated data). Other nodes (Set, If, Code, etc.) execute normally — including credential-free I/O nodes like Execute Command or file read/write nodes. Use prepare_workflow_pin_data to generate the pin data first.', inputSchema: inputSchema.shape, outputSchema, annotations: { diff --git a/packages/cli/src/modules/mcp/tools/webhook-utils.ts b/packages/cli/src/modules/mcp/tools/webhook-utils.ts index 527fa68b4c6..c49c6a14b2c 100644 --- a/packages/cli/src/modules/mcp/tools/webhook-utils.ts +++ b/packages/cli/src/modules/mcp/tools/webhook-utils.ts @@ -69,6 +69,7 @@ const buildEndpointBaseUrl = (baseUrl: string, endpoint: string) => { export const getTriggerDetails = async ( user: User, supportedTriggers: INode[], + unsupportedTriggers: INode[], baseUrl: string, credentialsService: CredentialsService, nodeTypes: INodeTypes, @@ -77,6 +78,10 @@ export const getTriggerDetails = async ( testBaseUrl: string = baseUrl, ): Promise => { if (supportedTriggers.length === 0) { + if (unsupportedTriggers.length > 0) { + const names = unsupportedTriggers.map((trigger) => trigger.name).join(', '); + return `This workflow's trigger(s) are not supported for direct execution through MCP: ${names}. Only Schedule, Webhook, Form, and Chat triggers can be executed directly through MCP, so this workflow cannot be executed through MCP. Its trigger(s) still run normally when the workflow is active.`; + } return 'This workflow has no production triggers (Schedule, Webhook, Form, or Chat). It can only be executed in manual mode.'; } diff --git a/packages/cli/src/modules/mcp/tools/workflow-builder/create-workflow-from-code.tool.ts b/packages/cli/src/modules/mcp/tools/workflow-builder/create-workflow-from-code.tool.ts index 950397e1d08..6d1ce1a6d07 100644 --- a/packages/cli/src/modules/mcp/tools/workflow-builder/create-workflow-from-code.tool.ts +++ b/packages/cli/src/modules/mcp/tools/workflow-builder/create-workflow-from-code.tool.ts @@ -28,7 +28,11 @@ import type { NodeTypes } from '@/node-types'; import type { AiGatewayService } from '@/services/ai-gateway.service'; import type { UrlService } from '@/services/url.service'; import type { Telemetry } from '@/telemetry'; -import { resolveNodeWebhookIds } from '@/workflow-helpers'; +import { + dropInvalidNodeGroups, + makeGetNodeTypeForGrouping, + resolveNodeWebhookIds, +} from '@/workflow-helpers'; import type { WorkflowCreationService } from '@/workflows/workflow-creation.service'; import type { WorkflowFinderService } from '@/workflows/workflow-finder.service'; @@ -39,10 +43,11 @@ export type CreateWorkflowFromCodeToolOptions = { * `102_mcp_canvas_groups` rollout flag: when true, node groups authored in the * SDK code (`.group(...)`) are persisted on the created workflow. Off by * default — groups are then dropped at the entity assembly, exactly like - * before groups were supported. Note that with the flag on, invalid groups - * fail the creation: `WorkflowCreationService.createWorkflow` rejects them - * with the same messages the `validate_workflow` tool reports as errors, - * so agents can catch group problems before calling this tool. + * before groups were supported. With the flag on, an invalid group does not + * fail the creation: it is dropped and reported in `skippedGroups` instead, + * while the rest of the workflow is still created. This tool pre-validates + * with the same rules `WorkflowCreationService.createWorkflow` enforces, so + * that shared service's own (fatal) group check never actually triggers here. */ canvasGroupsEnabled?: boolean; }; @@ -139,6 +144,15 @@ const outputSchema = { .describe( 'Additional notes about the workflow creation, such as any nodes that were skipped during credential auto-assignment.', ), + skippedGroups: z + .array( + z.object({ + groupName: z.string(), + reason: z.string(), + }), + ) + .optional() + .describe('Node groups that were invalid and skipped instead of failing the whole creation.'), hint: z .string() .optional() @@ -186,7 +200,7 @@ export const createCreateWorkflowFromCodeTool = ( ): ToolDefinition => ({ name: MCP_CREATE_WORKFLOW_FROM_CODE_TOOL.toolName, config: { - description: `Create a workflow in n8n from validated SDK code. This tool expects code that already follows the n8n Workflow SDK patterns and has passed ${CODE_BUILDER_VALIDATE_TOOL.toolName}. If code fails to parse, call get_sdk_reference, rewrite the code using the reference, validate again, then retry creation. If the user named a target project, resolve it via search_projects before calling this tool; when projectId is omitted, the workflow is created in the user's personal project. If you used n8n skills while preparing this workflow, pass their identifiers in skillsUsed. After creation, always tell the user which project the workflow landed in (see the targetProject field in the response).`, + description: `Create a workflow in n8n from validated SDK code. This tool expects code that already follows the n8n Workflow SDK patterns and has passed ${CODE_BUILDER_VALIDATE_TOOL.toolName}. If code fails to parse, call get_workflow_sdk_reference, rewrite the code using the reference, validate again, then retry creation. If the user named a target project, resolve it via search_projects before calling this tool; when projectId is omitted, the workflow is created in the user's personal project. If you used n8n skills while preparing this workflow, pass their identifiers in skillsUsed. After creation, always tell the user which project the workflow landed in (see the targetProject field in the response).`, inputSchema, outputSchema, annotations: { @@ -287,6 +301,17 @@ export const createCreateWorkflowFromCodeTool = ( stripNullCredentialStubs(newWorkflow.nodes); + // Structural group rules (no triggers, single connected subgraph, no + // non-main connection crossing the group boundary) aren't checked by the + // parser above. Validate them here, before the shared persistence layer's + // own (fatal) check, so an invalid group is dropped and reported instead + // of aborting the whole creation. + const skippedGroups = options.canvasGroupsEnabled + ? dropInvalidNodeGroups(newWorkflow, makeGetNodeTypeForGrouping(nodeTypes)).map( + (violation) => ({ groupName: violation.groupName, reason: violation.message }), + ) + : []; + landingProject = projectId ? await projectRepository.findOneBy({ id: projectId }) : await projectRepository.getPersonalProjectForUserOrFail(user.id); @@ -395,6 +420,7 @@ export const createCreateWorkflowFromCodeTool = ( type: landingProject.type, }, note: notes.length ? notes.join(' ') : undefined, + skippedGroups: skippedGroups.length > 0 ? skippedGroups : undefined, }; const output = result.warnings.length > 0 ? { ...baseOutput, warnings: result.warnings } : baseOutput; diff --git a/packages/cli/src/modules/mcp/tools/workflow-builder/credentials-auto-assign.ts b/packages/cli/src/modules/mcp/tools/workflow-builder/credentials-auto-assign.ts index 4ed4a005912..04d83808051 100644 --- a/packages/cli/src/modules/mcp/tools/workflow-builder/credentials-auto-assign.ts +++ b/packages/cli/src/modules/mcp/tools/workflow-builder/credentials-auto-assign.ts @@ -1,7 +1,11 @@ import type { AiGatewayConfigDto } from '@n8n/api-types'; import type { User } from '@n8n/db'; import type { INode, INodeParameters, INodeTypeDescription, IWorkflowBase } from 'n8n-workflow'; -import { NodeHelpers } from 'n8n-workflow'; +import { + NodeHelpers, + getCredentialActivationParameters, + resolveSupportedCredentialActivation, +} from 'n8n-workflow'; import type { CredentialsService } from '@/credentials/credentials.service'; import type { NodeTypes } from '@/node-types'; @@ -17,6 +21,69 @@ import { MCP_CREDENTIALS_AUTOASSIGN_EVENT } from '../../mcp.constants'; /** Display name written into AI Gateway-managed credential sentinels. User-facing brand. */ const AI_GATEWAY_CREDENTIAL_NAME = 'n8n credits'; +/** + * Attach the managed (n8n-credits) sentinel to `credentialType` and, unless it's + * already the active slot, switch the node's parameters so it becomes one. The + * single path for every managed assignment, so auth and credential can't drift. + * An already-active slot is left untouched — rewriting a valid `show` value would + * change what the node does. + */ +function setManagedCredential( + node: INode, + nodeTypeDescription: INodeTypeDescription, + credentialType: string, + resolvedParameters: INodeParameters, +): void { + node.credentials = node.credentials ?? {}; + node.credentials[credentialType] = { + id: null, + name: AI_GATEWAY_CREDENTIAL_NAME, + __aiGatewayManaged: true, + }; + const credentialDescription = nodeTypeDescription.credentials?.find( + (cred) => cred.name === credentialType, + ); + if ( + credentialDescription && + !NodeHelpers.displayParameter( + resolvedParameters, + credentialDescription, + node, + nodeTypeDescription, + ) + ) { + node.parameters = { + ...node.parameters, + ...getCredentialActivationParameters(credentialDescription.displayOptions), + }; + } +} + +/** `setManagedCredential` plus the assignment/outcome bookkeeping shared by both assign sites. */ +function assignAiGatewayCredential( + node: INode, + nodeTypeDescription: INodeTypeDescription, + credentialType: string, + resolvedParameters: INodeParameters, + assignments: CredentialAssignment[], + outcomes: SlotOutcome[], +): void { + setManagedCredential(node, nodeTypeDescription, credentialType, resolvedParameters); + assignments.push({ + nodeName: node.name, + credentialName: AI_GATEWAY_CREDENTIAL_NAME, + credentialType, + source: 'aiGateway', + }); + outcomes.push({ + nodeName: node.name, + credentialType, + source: 'aiGateway', + hadUserCredential: false, + aiGatewayAvailable: true, + }); +} + export interface CredentialAssignment { nodeName: string; credentialName: string; @@ -86,8 +153,13 @@ export function reconcileAiGatewayMarkers( ); if (markerTypes.length === 0) continue; - const nodeParameters = aiGatewayConfig ? resolveNodeParameters(node, nodeTypes) : undefined; - if (!aiGatewayConfig || !nodeParameters) { + const nodeTypeDescription = aiGatewayConfig + ? resolveNodeTypeDescription(node, nodeTypes) + : undefined; + const nodeParameters = nodeTypeDescription + ? resolveNodeParameters(node, nodeTypeDescription) + : undefined; + if (!aiGatewayConfig || !nodeTypeDescription || !nodeParameters) { for (const credentialType of markerTypes) delete credentials[credentialType]; continue; } @@ -96,11 +168,7 @@ export function reconcileAiGatewayMarkers( if ( checkAiGatewayEligibility(node, credentialType, aiGatewayConfig, nodeParameters).eligible ) { - credentials[credentialType] = { - id: null, - name: AI_GATEWAY_CREDENTIAL_NAME, - __aiGatewayManaged: true, - }; + setManagedCredential(node, nodeTypeDescription, credentialType, nodeParameters); } else { delete credentials[credentialType]; } @@ -108,14 +176,20 @@ export function reconcileAiGatewayMarkers( } } -/** Resolves a node's parameters with defaults applied, or `undefined` if its type can't be resolved. */ -function resolveNodeParameters(node: INode, nodeTypes: NodeTypes): INodeParameters | undefined { - let description: INodeTypeDescription; +/** Resolves a node's type description, or `undefined` if its type can't be resolved. */ +function resolveNodeTypeDescription( + node: INode, + nodeTypes: NodeTypes, +): INodeTypeDescription | undefined { try { - description = nodeTypes.getByNameAndVersion(node.type, node.typeVersion).description; + return nodeTypes.getByNameAndVersion(node.type, node.typeVersion).description; } catch { return undefined; } +} + +/** Resolves a node's parameters with defaults applied so displayOptions can be evaluated. */ +function resolveNodeParameters(node: INode, description: INodeTypeDescription): INodeParameters { return ( NodeHelpers.getNodeParameters( description.properties, @@ -256,27 +330,48 @@ export async function autoPopulateNodeCredentials( nodeParametersWithDefaults, ); if (eligibility.eligible) { - node.credentials = node.credentials ?? {}; - node.credentials[credDesc.name] = { - id: null, - name: AI_GATEWAY_CREDENTIAL_NAME, - __aiGatewayManaged: true, - }; - assignments.push({ - nodeName: node.name, - credentialName: AI_GATEWAY_CREDENTIAL_NAME, - credentialType: credDesc.name, - source: 'aiGateway', - }); - outcomes.push({ - nodeName: node.name, - credentialType: credDesc.name, - source: 'aiGateway', - hadUserCredential: false, - aiGatewayAvailable: true, - }); + assignAiGatewayCredential( + node, + nodeTypeDescription, + credDesc.name, + nodeParametersWithDefaults, + assignments, + outcomes, + ); continue; } + + // Shown type not covered, but the node's default auth pointing at an + // unsupported type shouldn't block it: fall back to a supported sibling + // (unassigned, no stored user credential) and switch auth to match. + if (eligibility.reason === 'credentialTypeNotCovered') { + const activation = resolveSupportedCredentialActivation( + nodeTypeDescription, + { typeVersion: node.typeVersion, parameters: nodeParametersWithDefaults }, + (credentialType) => + credentialType !== credDesc.name && + !node.credentials?.[credentialType]?.id && + !credentialsByType.get(credentialType)?.length && + checkAiGatewayEligibility( + node, + credentialType, + aiGatewayConfig, + nodeParametersWithDefaults, + ).eligible, + ); + if (activation) { + assignAiGatewayCredential( + node, + nodeTypeDescription, + activation.credentialType, + nodeParametersWithDefaults, + assignments, + outcomes, + ); + continue; + } + } + outcomes.push({ nodeName: node.name, credentialType: credDesc.name, diff --git a/packages/cli/src/modules/mcp/tools/workflow-builder/get-workflow-best-practices.tool.ts b/packages/cli/src/modules/mcp/tools/workflow-builder/get-workflow-best-practices.tool.ts index c3d00f0456c..419a30c6d78 100644 --- a/packages/cli/src/modules/mcp/tools/workflow-builder/get-workflow-best-practices.tool.ts +++ b/packages/cli/src/modules/mcp/tools/workflow-builder/get-workflow-best-practices.tool.ts @@ -128,7 +128,7 @@ export const createGetWorkflowBestPracticesTool = ( name: MCP_GET_WORKFLOW_BEST_PRACTICES_TOOL.toolName, config: { description: - 'Required workflow-planning step. Get best-practices guidance (recommended nodes, patterns, and common pitfalls) for a specific workflow technique before searching for nodes or writing code. Call once per relevant technique. Use technique="list" first if unsure which techniques apply.', + 'Required planning step when building a workflow, and only then. Get best-practices guidance (recommended nodes, patterns, and common pitfalls) for a specific workflow technique before searching for nodes or writing code. Call once per relevant technique. Use technique="list" first if unsure which techniques apply.', inputSchema, outputSchema, annotations: { diff --git a/packages/cli/src/modules/mcp/tools/workflow-builder/get-workflow-node-types.tool.ts b/packages/cli/src/modules/mcp/tools/workflow-builder/get-workflow-node-types.tool.ts index d02a23711e1..ccbfa3dc173 100644 --- a/packages/cli/src/modules/mcp/tools/workflow-builder/get-workflow-node-types.tool.ts +++ b/packages/cli/src/modules/mcp/tools/workflow-builder/get-workflow-node-types.tool.ts @@ -66,7 +66,7 @@ export const createGetWorkflowNodeTypesTool = ( name: CODE_BUILDER_GET_NODE_TYPES_TOOL.toolName, config: { description: - 'Get TypeScript type definitions for n8n nodes. Returns exact parameter names and structures. MUST be called before writing workflow code — guessing parameter names creates invalid workflows. Pass nodeIds as an array of objects like { nodeId: "n8n-nodes-base.gmail" }. Include discriminators (resource/operation/mode) from search_nodes results.', + 'Get TypeScript type definitions for n8n nodes. Returns exact parameter names and structures. MUST be called before writing workflow code or configuring node-backed tools — guessing parameter names creates invalid configurations. Pass nodeIds as an array of objects like { nodeId: "n8n-nodes-base.gmail" }. Include discriminators (resource/operation/mode) from search_nodes results.', inputSchema, outputSchema, annotations: { diff --git a/packages/cli/src/modules/mcp/tools/workflow-builder/get-workflow-sdk-reference.tool.ts b/packages/cli/src/modules/mcp/tools/workflow-builder/get-workflow-sdk-reference.tool.ts index 8a674b19740..b57753e4f48 100644 --- a/packages/cli/src/modules/mcp/tools/workflow-builder/get-workflow-sdk-reference.tool.ts +++ b/packages/cli/src/modules/mcp/tools/workflow-builder/get-workflow-sdk-reference.tool.ts @@ -56,7 +56,7 @@ export const createGetWorkflowSdkReferenceTool = ( name: MCP_GET_SDK_REFERENCE_TOOL.toolName, config: { description: - 'Required reference for building n8n Workflow SDK code. Call this BEFORE writing workflow code to learn workflow(), trigger()/node(), .add()/.to(), expr(), and credential patterns.', + 'Required reference when building a workflow, and only then. Call this BEFORE writing workflow code to learn workflow(), trigger()/node(), .add()/.to(), expr(), and credential patterns.', inputSchema: buildInputSchema(canvasGroupsEnabled), outputSchema, annotations: { diff --git a/packages/cli/src/modules/mcp/tools/workflow-builder/mcp-instructions.ts b/packages/cli/src/modules/mcp/tools/workflow-builder/mcp-instructions.ts index e5b0a086bb2..8163673cda7 100644 --- a/packages/cli/src/modules/mcp/tools/workflow-builder/mcp-instructions.ts +++ b/packages/cli/src/modules/mcp/tools/workflow-builder/mcp-instructions.ts @@ -38,9 +38,20 @@ export type McpInstructionsOptions = { * If true, the builder instructions point the client at the groups docs. */ canvasGroupsEnabled?: boolean; + + /** + * Whether first-class Agent tools are enabled on this MCP server. + * If true, the instructions include Agent build guidance and artifact routing. + */ + isAgentsEnabled?: boolean; }; export function getMcpInstructions(options: McpInstructionsOptions): string { - const { isBuilderEnabled, isN8nConnectAvailable = false, canvasGroupsEnabled = false } = options; + const { + isBuilderEnabled, + isN8nConnectAvailable = false, + canvasGroupsEnabled = false, + isAgentsEnabled = false, + } = options; const INTRO = 'This is the official MCP server for n8n, a workflow automation platform.'; // Only appended when the flag is on; keeps the paid-per-session string short. @@ -56,9 +67,20 @@ Node groups: when a workflow has several distinct stages, organise it into named Explore nodes covered by n8n credits when the user has not specified a particular integration. n8n credits let users consume LLMs and third-party services directly through n8n with usage-based billing, so they can skip credential setup. Discovery tools (${CODE_BUILDER_SEARCH_NODES_TOOL.toolName}, ${CODE_BUILDER_GET_NODE_TYPES_TOOL.toolName}) and list_credentials return an optional \`n8nConnect.nodes\` array when the instance has n8n credits available. Nodes in that array can attach a managed credential automatically — the workflow runs without the user configuring keys. If the user asked for a specific integration or none of the covered nodes fit, use the requested integration with regular credentials. Call ${LIST_N8N_CONNECT_SERVICES_TOOL_NAME} if you need details (per-node supported resource+operation combos, min type versions, hidden properties).` : ''; + const ARTIFACT_ROUTING_INSTRUCTIONS = `Choose the artifact before choosing build tools. Treat an explicit artifact request as a routing instruction: if the user asks to build or create an "agent" or "assistant", default to a first-class n8n Agent; if they explicitly ask for a workflow or an AI Agent node, use the workflow tools. + +Only deviate when the requested artifact is an unmistakably poor fit. A fixed trigger or schedule followed by enumerable, repeatable steps and fixed actions is usually a workflow. Never silently substitute one artifact for another: explain the mismatch and ask before building the alternative. + +Prefer an Agent when the model owns runtime decisions, conversations, investigation, iteration, proactive work, memory, or learning. Prefer a workflow when control flow is fixed and any LLM usage is a bounded step. A Chat Trigger plus an AI Agent node is not a substitute for a requested first-class Agent. If the intended artifact is genuinely ambiguous, clarify it before creating anything.`; + + // Only reference Agents when the agent walkthrough is actually shown. + const WORKFLOWS_ONLY_CLAUSE = isAgentsEnabled + ? ' (not standalone n8n Agents — those never use the Workflow SDK; see the Agent instructions above)' + : ''; + const BUILDER_INSTRUCTIONS = `This MCP server provides tools to build n8n workflows programmatically using the n8n Workflow SDK. -To build n8n workflows, follow these steps in order: +To build n8n workflows${WORKFLOWS_ONLY_CLAUSE}, follow these steps in order: 1. Read the SDK reference: You MUST call ${MCP_GET_SDK_REFERENCE_TOOL.toolName} (or use the n8n://workflow-sdk/reference resource) before writing workflow code. Do not guess SDK syntax. @@ -84,5 +106,20 @@ To build n8n workflows, follow these steps in order: Error handling has two complementary layers. (1) Per-node: set onError ("continueRegularOutput" / "continueErrorOutput"), retryOnFail, and maxTries via setNodeSettings on ${MCP_UPDATE_WORKFLOW_TOOL.toolName}. (2) Failure notifications via an Error Trigger node, which can be wired two ways: (a) Dedicated/shared error workflow — point settings.errorWorkflow (via the setWorkflowSettings operation) at a SEPARATE workflow whose first node is an Error Trigger; this is the common best practice and lets one handler cover many workflows. (b) Same-workflow — add an Error Trigger node (→ a notification node such as Send Email or Slack) INTO this workflow; n8n runs it automatically when the workflow fails, with no settings change needed. Caveats: both fire only for production executions (not manual/test runs), and a configured settings.errorWorkflow takes precedence over a same-workflow Error Trigger for the failing run. When a user asks to "add error handling", "get notified on failure", or "make this reliable", briefly explain both patterns — most users do not know Error Triggers exist — and ask which they prefer before setting one up; do not enable error handling silently. For the shared pattern, reuse an existing handler (find its ID with search_workflows) or create a new one — but a dedicated error workflow must be PUBLISHED before it can be linked, in this order: (1) create it (${MCP_CREATE_WORKFLOW_FROM_CODE_TOOL.toolName}, first node = Error Trigger → a notification node), (2) publish it (publish_workflow), (3) set settings.errorWorkflow via ${MCP_UPDATE_WORKFLOW_TOOL.toolName}. Setting settings.errorWorkflow is rejected if the target has no published version, or no Error Trigger in that published version.${GROUPS_HINT}`; - return isBuilderEnabled ? `${INTRO}\n\n${BUILDER_INSTRUCTIONS}` : INTRO; + const AGENT_INSTRUCTIONS = `This MCP server provides tools to build and manage n8n Agents. + +An n8n Agent is a first-class persisted Agent artifact, not an AI Agent node inside a workflow. + +To build an Agent, first call get_agent_builder_reference (or read n8n://agents/reference), then discover the required assets, create the Agent, apply one mutation at a time using the latest configHash, and validate it. Agents are configured as JSON, not Workflow SDK code: the workflow-building steps do not apply to Agents, and ${MCP_GET_SDK_REFERENCE_TOOL.toolName} and ${MCP_GET_WORKFLOW_BEST_PRACTICES_TOOL.toolName} are never needed for an Agent build — get_agent_builder_reference is the only required reference. A completed build is a saved draft, not a published Agent. Report that the draft is ready, include a clickable link using the \`url\` returned by validate_agent, and ask whether to publish it. Call publish_agent only if the user explicitly requested publication or confirms it after the build; connecting a chat integration also publishes and requires the same confirmation. Use search_nodes with usage="agentTool" when configuring node-backed Agent tools. + +Agent conversations and runs are not workflow executions: get_workflow_execution and search_workflow_executions cover workflow executions only, including workflows an Agent invokes as tools.`; + + return [ + INTRO, + isBuilderEnabled && isAgentsEnabled ? ARTIFACT_ROUTING_INSTRUCTIONS : '', + isAgentsEnabled ? AGENT_INSTRUCTIONS : '', + isBuilderEnabled ? BUILDER_INSTRUCTIONS : '', + ] + .filter(Boolean) + .join('\n\n'); } diff --git a/packages/cli/src/modules/mcp/tools/workflow-builder/search-workflow-nodes.tool.ts b/packages/cli/src/modules/mcp/tools/workflow-builder/search-workflow-nodes.tool.ts index 347b556eb5b..7c778eacebb 100644 --- a/packages/cli/src/modules/mcp/tools/workflow-builder/search-workflow-nodes.tool.ts +++ b/packages/cli/src/modules/mcp/tools/workflow-builder/search-workflow-nodes.tool.ts @@ -24,6 +24,12 @@ const inputSchema = { .describe( 'Search queries for n8n nodes — service names (e.g. "gmail", "slack"), trigger types (e.g. "schedule trigger", "webhook"), or utility nodes (e.g. "set", "if", "merge", "code")', ), + usage: z + .enum(['workflow', 'agentTool']) + .optional() + .describe( + 'Use agentTool to return only nodes that can be configured as Agent tools; defaults to workflow', + ), } satisfies z.ZodRawShape; const outputSchema = { @@ -45,6 +51,21 @@ const outputSchema = { ), } satisfies z.ZodRawShape; +type SearchNodesInput = { + queries: string[]; + usage?: 'workflow' | 'agentTool'; +}; + +// The SDK's inferred handler input marks optional zod fields as required +// properties, so callers would have to pass `usage: undefined` explicitly. +type SearchNodesCallback = ToolDefinition['handler']; +type SearchNodesToolDefinition = Omit, 'handler'> & { + handler: ( + input: SearchNodesInput, + extra: Parameters[1], + ) => ReturnType; +}; + /** * MCP tool that searches for n8n nodes by keyword. * Wraps the code-builder's search tool. @@ -54,11 +75,11 @@ export const createSearchWorkflowNodesTool = ( nodeCatalogService: NodeCatalogService, telemetry: Telemetry, aiGatewayService: AiGatewayService, -): ToolDefinition => ({ +): SearchNodesToolDefinition => ({ name: CODE_BUILDER_SEARCH_NODES_TOOL.toolName, config: { description: - 'Search for n8n nodes by service name, trigger type, or utility function. Returns node IDs, discriminators (resource/operation/mode), and related nodes needed for get_node_types.', + 'Search for n8n nodes by service name, trigger type, or utility function. Set usage="agentTool" to return only Agent-compatible tool nodes. Returns node IDs, discriminators (resource/operation/mode), and related nodes needed for get_node_types.', inputSchema, outputSchema, annotations: { @@ -69,16 +90,23 @@ export const createSearchWorkflowNodesTool = ( openWorldHint: false, }, }, - handler: async ({ queries }: { queries: string[] }) => { + handler: async ({ queries, usage }) => { const telemetryPayload: UserCalledMCPToolEventPayload = { user_id: user.id, tool_name: CODE_BUILDER_SEARCH_NODES_TOOL.toolName, - parameters: { queries }, + parameters: { queries, usage }, }; try { + const options = + usage === 'agentTool' + ? { + nodeFilter: (await import('@/modules/agents/agents-tools.service.js')) + .isAgentToolNodeType, + } + : {}; const [{ results, queriesWithNoResults }, availability] = await Promise.all([ - nodeCatalogService.searchNodes(queries), + nodeCatalogService.searchNodes(queries, options), aiGatewayService.isAvailable(), ]); diff --git a/packages/cli/src/modules/mcp/tools/workflow-builder/update-workflow.tool.ts b/packages/cli/src/modules/mcp/tools/workflow-builder/update-workflow.tool.ts index 86ebd92f8dd..9cff2bfa1e3 100644 --- a/packages/cli/src/modules/mcp/tools/workflow-builder/update-workflow.tool.ts +++ b/packages/cli/src/modules/mcp/tools/workflow-builder/update-workflow.tool.ts @@ -26,10 +26,13 @@ import { } from './version-metadata'; import { applyOperations, + NON_FATAL_OPERATION_TYPES, partialUpdateOperationSchema, toWorkflowSlice, workflowSettingsObjectSchema, + type ApplyOperationsSuccess, type PartialUpdateOperation, + type SkippedOperation, } from './workflow-operations'; import type { CollaborationService } from '@/collaboration/collaboration.service'; @@ -43,7 +46,11 @@ import type { TagService } from '@/services/tag.service'; import type { AiGatewayService } from '@/services/ai-gateway.service'; import type { UrlService } from '@/services/url.service'; import type { Telemetry } from '@/telemetry'; -import { resolveNodeWebhookIds } from '@/workflow-helpers'; +import { + dropInvalidNodeGroups, + makeGetNodeTypeForGrouping, + resolveNodeWebhookIds, +} from '@/workflow-helpers'; import type { WorkflowFinderService } from '@/workflows/workflow-finder.service'; import type { WorkflowService } from '@/workflows/workflow.service'; @@ -219,6 +226,24 @@ type OperationInput = { const strictOperationsSchema = z.array(partialUpdateOperationSchema); +// LLM-supplied tag batches routinely repeat a name in different casings; collapse +// those (first-seen case wins) before hitting the tag API. Tag names are +// case-sensitively unique, so this is an MCP-only semantic — the service itself +// treats case-variant names as distinct tags. +function dedupeNamesPreservingCase(names: string[]): string[] { + const seen = new Set(); + const result: string[] = []; + for (const raw of names) { + const trimmed = raw.trim(); + if (trimmed.length === 0) continue; + const key = trimmed.toLowerCase(); + if (seen.has(key)) continue; + seen.add(key); + result.push(trimmed); + } + return result; +} + function parseStrictOperations(operations: OperationInput[]): PartialUpdateOperation[] { const parsed = strictOperationsSchema.safeParse(operations); if (parsed.success) return parsed.data; @@ -264,9 +289,53 @@ function collectTouchedNodes(operations: PartialUpdateOperation[]): Map>, + groupOperations: ApplyOperationsSuccess['groupOperations'], +): number => { + const producedPerOp = new Map(); + for (const { opIndex } of Object.values(groupOperations)) { + producedPerOp.set(opIndex, (producedPerOp.get(opIndex) ?? 0) + 1); + } + + const skippedPerOp = new Map(); + for (const { opIndex } of skippedOperations) { + skippedPerOp.set(opIndex, (skippedPerOp.get(opIndex) ?? 0) + 1); + } + + let count = 0; + for (const [opIndex, skipped] of skippedPerOp) { + if (skipped >= (producedPerOp.get(opIndex) ?? 0)) { + count++; + } + } + return count; +}; + // The concrete return type (not a widened z.ZodRawShape) keeps the tool's // generic coupled to the real schema shape, so the handler's argument // annotation is compile-checked against it via ToolCallback's parameter types. +const NON_FATAL_OPERATION_TYPES_LIST = [...NON_FATAL_OPERATION_TYPES].join(', '); + +const buildToolDescription = (canvasGroupsEnabled: boolean) => { + const base = + 'Atomically update an existing workflow with operation objects. Edits nodes/connections and also workflow-level settings via setWorkflowSettings — including the error workflow that runs automatically on failure to send alerts (e.g. when a user asks to "add error handling" or "notify me if this breaks"). Pass skillsUsed if n8n skills were used.'; + return canvasGroupsEnabled + ? `${base} Node-group operations (${NON_FATAL_OPERATION_TYPES_LIST}) are the one exception to "atomically": an invalid one is skipped and reported in skippedOperations instead of aborting the whole update. Separately, if other edits in the batch make an existing group invalid, that group is removed and reported in removedGroups.` + : base; +}; + const buildInputSchema = (canvasGroupsEnabled: boolean) => ({ workflowId: z.string().describe('The ID of the workflow to update.'), @@ -276,7 +345,9 @@ const buildInputSchema = (canvasGroupsEnabled: boolean) => .min(1) .max(MAX_OPERATIONS_PER_CALL) .describe( - `Ordered operations to apply atomically (max ${MAX_OPERATIONS_PER_CALL}). If any op fails, nothing is saved.`, + canvasGroupsEnabled + ? `Ordered operations to apply atomically (max ${MAX_OPERATIONS_PER_CALL}). If any op fails, nothing is saved — except node-group operations (${NON_FATAL_OPERATION_TYPES_LIST}): an invalid one is skipped and reported in skippedOperations, while the rest of the batch still saves. An existing group that these ops leave invalid is removed and reported in removedGroups.` + : `Ordered operations to apply atomically (max ${MAX_OPERATIONS_PER_CALL}). If any op fails, nothing is saved.`, ), versionName: versionNameInputSchema.describe( 'Short summary of what this update changes, shown in the workflow\'s version history (e.g. "Added Slack notification after HTTP request"). Always provide it.', @@ -297,7 +368,12 @@ const outputSchema = { name: z.string().optional(), nodeCount: z.number().optional(), url: z.string().optional(), - appliedOperations: z.number().optional().describe('Number of operations applied.'), + appliedOperations: z + .number() + .optional() + .describe( + 'Number of submitted operations that were applied. See skippedOperations for any that were not.', + ), autoAssignedCredentials: z .array( z.object({ @@ -328,6 +404,26 @@ const outputSchema = { 'Graph and JSON validation warnings on the resulting workflow. Warnings marked preExisting (also tagged [pre-existing] in the message) were already present before this update; only self-correct the rest on the next call.', ), note: z.string().optional(), + skippedOperations: z + .array( + z.object({ + opIndex: z.number(), + type: z.string(), + reason: z.string(), + }), + ) + .optional() + .describe( + 'Submitted group operations that did not take effect: either invalid, or their group broke the group rules.', + ), + removedGroups: z + .array( + z.object({ + groupName: z.string(), + reason: z.string(), + }), + ) + .optional(), settings: z .record(z.string(), z.unknown()) .optional() @@ -527,8 +623,7 @@ export const createUpdateWorkflowTool = ( ): ToolDefinition> => ({ name: MCP_UPDATE_WORKFLOW_TOOL.toolName, config: { - description: - 'Atomically update an existing workflow with operation objects. Edits nodes/connections and also workflow-level settings via setWorkflowSettings — including the error workflow that runs automatically on failure to send alerts (e.g. when a user asks to "add error handling" or "notify me if this breaks"). Pass skillsUsed if n8n skills were used.', + description: buildToolDescription(options.canvasGroupsEnabled === true), inputSchema: buildInputSchema(options.canvasGroupsEnabled === true), outputSchema, annotations: { @@ -604,12 +699,59 @@ export const createUpdateWorkflowTool = ( const result = applyOperations( toWorkflowSlice(existingWorkflow, { includeTags: hasTagOperations }), strictOperations, + { canvasGroupsEnabled: options.canvasGroupsEnabled }, ); if (!result.success) { throw new Error(result.error); } + // Group rules depend on how the workflow looks after the whole batch, + // so we check them here once rather than per operation. A broken group + // is dropped and reported; the update still goes through. + const skippedOperations: SkippedOperation[] = [...result.skippedOperations]; + // Groups the batch never asked for, removed because these operations made + // them invalid. Reported apart from skippedOperations: no submitted + // operation failed here, an existing group was destroyed as a side effect. + const removedGroups: Array<{ groupName: string; reason: string }> = []; + let nodeGroupsNeedPersisting = result.nodeGroupsChanged; + + if (options.canvasGroupsEnabled) { + const getNodeType = makeGetNodeTypeForGrouping(nodeTypes); + + // Two passes, ordered by blame: an overlap between a new and an existing + // group makes the validator flag both, so the batch's own groups go first + // and the innocent existing one survives the re-check. `groupOperations` + // records which groups this batch touched, not which one caused a given + // violation — two group ops that collide take each other down. + const ownGroups = dropInvalidNodeGroups( + result.workflow, + getNodeType, + (violation) => result.groupOperations[violation.groupId] !== undefined, + ); + const violations = [...ownGroups, ...dropInvalidNodeGroups(result.workflow, getNodeType)]; + + if (violations.length > 0) { + // A violation found here always changes what must be persisted, even if + // no group op ran this batch — otherwise the omitted `nodeGroups` key + // falls back to preserve-on-omit and the still-invalid stored groups get + // re-validated (and rejected) by WorkflowService.update right after. + nodeGroupsNeedPersisting = true; + + for (const violation of violations) { + const requestedBy = result.groupOperations[violation.groupId]; + if (requestedBy) { + skippedOperations.push({ ...requestedBy, reason: violation.message }); + } else { + removedGroups.push({ + groupName: violation.groupName, + reason: violation.message, + }); + } + } + } + } + const credentialCheck = await validateCredentialReferences( strictOperations, existingWorkflow, @@ -618,6 +760,7 @@ export const createUpdateWorkflowTool = ( nodeTypes, { workflowId: existingWorkflow.id }, ); + if (!credentialCheck.ok) { throw new Error(credentialCheck.error); } @@ -629,7 +772,10 @@ export const createUpdateWorkflowTool = ( telemetryPayload, telemetry, ); - if (invalidToolSourceResponse) return invalidToolSourceResponse; + + if (invalidToolSourceResponse) { + return invalidToolSourceResponse; + } const { projectId: workflowProjectId } = await sharedWorkflowRepository.findOneOrFail({ where: { workflowId, role: 'workflow:owner' }, @@ -642,6 +788,7 @@ export const createUpdateWorkflowTool = ( workflowProjectId, dataTableOps, ); + if (!dataTableCheck.ok) { throw new Error(dataTableCheck.error); } @@ -652,6 +799,7 @@ export const createUpdateWorkflowTool = ( const setsErrorWorkflow = strictOperations.some( (op) => op.type === 'setWorkflowSettings' && op.settings.errorWorkflow !== undefined, ); + if (setsErrorWorkflow) { await assertErrorWorkflowIsUsable({ errorWorkflowId: result.workflow.settings?.errorWorkflow, @@ -675,6 +823,7 @@ export const createUpdateWorkflowTool = ( op.type === 'setWorkflowSettings' && (op.settings.callerPolicy !== undefined || op.settings.callerIds !== undefined), ); + if (setsCallerConfig) { assertCallerPolicyConsistent(result.workflow.settings); } @@ -682,6 +831,7 @@ export const createUpdateWorkflowTool = ( const setsExecutionTimeout = strictOperations.some( (op) => op.type === 'setWorkflowSettings' && op.settings.executionTimeout !== undefined, ); + if (setsExecutionTimeout) { assertExecutionTimeoutWithinMax( result.workflow.settings?.executionTimeout, @@ -713,6 +863,7 @@ export const createUpdateWorkflowTool = ( const canPublish = await workflowFinderService.findWorkflowHeadForUser(workflowId, user, [ 'workflow:publish', ]); + if (!canPublish) { throw new Error( 'Changing settings on a published workflow reactivates it, which requires publish permission. Your account can edit but not publish this workflow. Ask the owner for publish access, or unpublish the workflow first.', @@ -731,10 +882,11 @@ export const createUpdateWorkflowTool = ( // Only attach settings when a settings op ran, so node-only edits // don't re-save (and re-clean) the existing settings object. ...(hasSettingsOperations ? { settings: result.workflow.settings } : {}), - // Only persist nodeGroups when the batch touched them (a group op ran - // or removing a node pruned a group); otherwise omit the key so + // Only persist nodeGroups when they actually need to change (a group op + // ran, removing a node pruned a group, or the structural check above + // dropped a group some other op invalidated); otherwise omit the key so // WorkflowService preserves the existing groups (preserve-on-omit). - ...(result.nodeGroupsChanged ? { nodeGroups: result.workflow.nodeGroups } : {}), + ...(nodeGroupsNeedPersisting ? { nodeGroups: result.workflow.nodeGroups } : {}), meta: hasNonTagOperations ? { ...(existingWorkflow.meta ?? {}), @@ -814,15 +966,14 @@ export const createUpdateWorkflowTool = ( let tagIds: string[] | undefined; if (result.tagNames !== undefined) { + const uniqueTagNames = dedupeNamesPreservingCase(result.tagNames); if (hasGlobalScope(user, 'tag:create')) { - const resolvedTags = await tagService.findOrCreateByNames(result.tagNames); + const resolvedTags = await tagService.findOrCreateByNames(uniqueTagNames); tagIds = resolvedTags.map((t) => t.id); } else { - const resolvedTags = await tagService.findByNames(result.tagNames); + const resolvedTags = await tagService.getByNames(uniqueTagNames); const resolvedNames = new Set(resolvedTags.map((t) => t.name)); - const missing = result.tagNames - .map((n) => n.trim()) - .filter((name) => name.length > 0 && !resolvedNames.has(name)); + const missing = uniqueTagNames.filter((name) => !resolvedNames.has(name)); if (missing.length > 0) { throw new Error( `Cannot apply the following tags because they don't exist and your account does not have permission to create them: ${missing.join(', ')}`, @@ -876,17 +1027,24 @@ export const createUpdateWorkflowTool = ( }; telemetry.track(USER_CALLED_MCP_TOOL_EVENT, telemetryPayload); + const notAppliedCount = countOperationsWithNoEffect( + skippedOperations, + result.groupOperations, + ); + const output = { workflowId: updatedWorkflow.id, name: updatedWorkflow.name, nodeCount: updatedWorkflow.nodes.length, url: workflowUrl, - appliedOperations: strictOperations.length, + appliedOperations: strictOperations.length - notAppliedCount, autoAssignedCredentials: credentialAssignments, validationWarnings, note: skippedHttpNodes.length ? `HTTP Request nodes (${skippedHttpNodes.join(', ')}) were skipped during credential auto-assignment. Their credentials must be configured manually.` : undefined, + skippedOperations: skippedOperations.length > 0 ? skippedOperations : undefined, + removedGroups: removedGroups.length > 0 ? removedGroups : undefined, settings: hasSettingsOperations ? (updatedWorkflow.settings ?? {}) : undefined, }; diff --git a/packages/cli/src/modules/mcp/tools/workflow-builder/validate-workflow-code.tool.ts b/packages/cli/src/modules/mcp/tools/workflow-builder/validate-workflow-code.tool.ts index 011d37665bd..099c272bf98 100644 --- a/packages/cli/src/modules/mcp/tools/workflow-builder/validate-workflow-code.tool.ts +++ b/packages/cli/src/modules/mcp/tools/workflow-builder/validate-workflow-code.tool.ts @@ -109,7 +109,7 @@ export const createValidateWorkflowCodeTool = ( name: CODE_BUILDER_VALIDATE_TOOL.toolName, config: { description: - 'Validate n8n Workflow SDK code. Required before creating or updating workflows from code. If you have not already read get_sdk_reference, call that first; guessing SDK syntax commonly creates invalid workflows.', + 'Validate n8n Workflow SDK code. Required before creating or updating workflows from code. If you have not already read get_workflow_sdk_reference, call that first; guessing SDK syntax commonly creates invalid workflows.', inputSchema, outputSchema, annotations: { diff --git a/packages/cli/src/modules/mcp/tools/workflow-builder/workflow-operations.ts b/packages/cli/src/modules/mcp/tools/workflow-builder/workflow-operations.ts index 4b2df76270c..4ae54fb5053 100644 --- a/packages/cli/src/modules/mcp/tools/workflow-builder/workflow-operations.ts +++ b/packages/cli/src/modules/mcp/tools/workflow-builder/workflow-operations.ts @@ -364,6 +364,18 @@ export const partialUpdateOperationSchema = z.discriminatedUnion('type', [ export type PartialUpdateOperation = z.infer; +/** + * Operation types whose failure `applyOperations` skips instead of aborting the whole + * batch. Groups are cosmetic — nothing about execution depends on them — so a single + * invalid group op shouldn't discard an otherwise-good batch of node/connection edits. + */ +export const NON_FATAL_OPERATION_TYPES: ReadonlySet = new Set([ + 'setNodeGroups', + 'addNodeGroup', + 'removeNodeGroup', + 'updateNodeGroup', +]); + interface WorkflowSlice { name: string; description?: string; @@ -377,6 +389,18 @@ interface WorkflowSlice { tagNames?: string[]; } +export interface SkippedOperation { + opIndex: number; + type: PartialUpdateOperation['type']; + reason: string; +} + +/** The submitted operation that put a group in its current state. */ +export interface GroupOperationRef { + opIndex: number; + type: PartialUpdateOperation['type']; +} + export interface ApplyOperationsSuccess { success: true; workflow: WorkflowSlice; @@ -388,6 +412,21 @@ export interface ApplyOperationsSuccess { * `workflow.nodeGroups` must be persisted rather than preserved-on-omit. */ nodeGroupsChanged: boolean; + /** + * Operations of a type in `NON_FATAL_OPERATION_TYPES` that failed and were + * skipped instead of aborting the batch. Empty when none were skipped. + */ + skippedOperations: SkippedOperation[]; + /** + * Group id -> the group operation that last put it in its current state. + * Contains only groups this batch explicitly asked for; a pre-existing group, + * or one merely affected as a side effect (e.g. `removeNode` pruning a + * member), is deliberately absent. Lets a caller doing its own post-loop group + * validation (structural rules aren't checked here — see + * `NON_FATAL_OPERATION_TYPES`'s doc comment) tell "the caller requested this + * group and it failed" from "an unrelated edit invalidated an existing group". + */ + groupOperations: Record; } export interface ApplyOperationsFailure { @@ -588,15 +627,26 @@ interface ApplyContext { // "tag ops applied to an empty set" at return time. tagSet: Set | null; nodeGroupsChanged: boolean; + /** + * Group id -> the group op that last put it in its current state. Only groups + * this batch asked for land here, so a post-loop structural violation (which + * only knows the group) can attribute the failure to a real submitted op — + * or recognise that no op asked for this group at all. + */ + groupOperations: Map; } /** * Handler for a single operation type. Returns null on success, or a raw error * message (without the `Operation N failed:` prefix, which `fail` adds). + * + * `opIndex` is the op's position in the submitted batch; only the group handlers + * need it, to record which op is answerable for a group. */ type OpHandler = ( op: Extract, ctx: ApplyContext, + opIndex: number, ) => string | null; /** @@ -719,7 +769,13 @@ const handleRemoveNode: OpHandler<'removeNode'> = (op, ctx) => { } ctx.nodeGroupsChanged = true; const remaining = group.nodeIds.filter((id) => id !== node.id); - if (remaining.length > 0) prunedGroups.push({ ...group, nodeIds: remaining }); + if (remaining.length > 0) { + // Not recorded in `groupOperations`: pruning is a side effect, so a group + // invalidated by it shouldn't discount this operation. + prunedGroups.push({ ...group, nodeIds: remaining }); + } else { + ctx.groupOperations.delete(group.id); + } } ctx.workflow.nodeGroups = prunedGroups; } @@ -897,7 +953,7 @@ const handleSetWorkflowSettings: OpHandler<'setWorkflowSettings'> = (op, ctx) => return null; }; -const handleSetNodeGroups: OpHandler<'setNodeGroups'> = (op, ctx) => { +const handleSetNodeGroups: OpHandler<'setNodeGroups'> = (op, ctx, opIndex) => { const nodeGroups: IWorkflowGroup[] = []; for (const group of op.nodeGroups) { const resolved = resolveGroupNodeIds(ctx.nodeByName, group.nodeNames, group.name); @@ -916,10 +972,14 @@ const handleSetNodeGroups: OpHandler<'setNodeGroups'> = (op, ctx) => { } ctx.workflow.nodeGroups = nodeGroups; ctx.nodeGroupsChanged = true; + // Wholesale replace: this op is answerable for every surviving group. + ctx.groupOperations = new Map( + nodeGroups.map((group) => [group.id, { opIndex, type: 'setNodeGroups' as const }]), + ); return null; }; -const handleAddNodeGroup: OpHandler<'addNodeGroup'> = (op, ctx) => { +const handleAddNodeGroup: OpHandler<'addNodeGroup'> = (op, ctx, opIndex) => { const groups = ctx.workflow.nodeGroups ?? []; if (groups.some((g) => g.name === op.name)) { return `a node group named '${op.name}' already exists`; @@ -935,15 +995,17 @@ const handleAddNodeGroup: OpHandler<'addNodeGroup'> = (op, ctx) => { } const description = op.description?.trim(); - groups.push({ + const newGroup = { id: op.id ?? uuid(), name: op.name, nodeIds: resolved.nodeIds, ...(description ? { description } : {}), - }); + }; + groups.push(newGroup); ctx.workflow.nodeGroups = groups; ctx.nodeGroupsChanged = true; + ctx.groupOperations.set(newGroup.id, { opIndex, type: 'addNodeGroup' }); return null; }; @@ -956,14 +1018,15 @@ const handleRemoveNodeGroup: OpHandler<'removeNodeGroup'> = (op, ctx) => { return `node group '${op.groupName}' not found`; } - groups.splice(index, 1); + const [removed] = groups.splice(index, 1); ctx.workflow.nodeGroups = groups; ctx.nodeGroupsChanged = true; + ctx.groupOperations.delete(removed.id); return null; }; -const handleUpdateNodeGroup: OpHandler<'updateNodeGroup'> = (op, ctx) => { +const handleUpdateNodeGroup: OpHandler<'updateNodeGroup'> = (op, ctx, opIndex) => { // Cross-field "at least one change" lives here because zod v3 discriminated // unions cannot carry a `.refine()` on their members. if (op.newName === undefined && op.nodeNames === undefined && op.description === undefined) { @@ -977,19 +1040,29 @@ const handleUpdateNodeGroup: OpHandler<'updateNodeGroup'> = (op, ctx) => { return `node group '${op.groupName}' not found`; } + // Validate every field before touching the group. A failing group op is + // skipped, not fatal, so a half-applied mutation would be persisted while the + // report says the operation never took effect. + let nodeIds: string[] | undefined; if (op.nodeNames !== undefined) { const resolved = resolveGroupNodeIds(ctx.nodeByName, op.nodeNames, op.groupName); if ('error' in resolved) { return resolved.error; } - group.nodeIds = resolved.nodeIds; + nodeIds = resolved.nodeIds; } - if (op.newName !== undefined && op.newName !== group.name) { - if (groups.some((g) => g !== group && g.name === op.newName)) { - return `a node group named '${op.newName}' already exists`; - } - group.name = op.newName; + const newName = op.newName !== undefined && op.newName !== group.name ? op.newName : undefined; + if (newName !== undefined && groups.some((g) => g !== group && g.name === newName)) { + return `a node group named '${newName}' already exists`; + } + + if (nodeIds !== undefined) { + group.nodeIds = nodeIds; + } + + if (newName !== undefined) { + group.name = newName; } if (op.description !== undefined) { @@ -1003,6 +1076,7 @@ const handleUpdateNodeGroup: OpHandler<'updateNodeGroup'> = (op, ctx) => { } ctx.nodeGroupsChanged = true; + ctx.groupOperations.set(group.id, { opIndex, type: 'updateNodeGroup' }); return null; }; @@ -1053,8 +1127,8 @@ const OPERATION_HANDLERS: { [K in PartialUpdateOperation['type']]: OpHandler updateNodeGroup: handleUpdateNodeGroup, // Thin wrappers narrow the shared handler to each slot's exact op type; a // direct `handleTagOp` assignment trips a variance check on the `Extract`. - addTags: (op, ctx) => handleTagOp(op, ctx), - removeTags: (op, ctx) => handleTagOp(op, ctx), + addTags: (op, ctx, opIndex) => handleTagOp(op, ctx, opIndex), + removeTags: (op, ctx, opIndex) => handleTagOp(op, ctx, opIndex), }; /** @@ -1062,11 +1136,20 @@ const OPERATION_HANDLERS: { [K in PartialUpdateOperation['type']]: OpHandler * Returns the mutated clone on success, or the first failure with the offending op index. * * The function never mutates the input. + * + * With `{ canvasGroupsEnabled: true }`, a failing operation of a type in + * `NON_FATAL_OPERATION_TYPES` does not abort the batch — it is skipped and recorded + * in the result's `skippedOperations` instead, and the remaining operations still + * apply. With the flag off (or omitted), every operation is fatal, matching the + * historical behavior exactly. */ export function applyOperations( input: WorkflowSlice, operations: PartialUpdateOperation[], + options: { canvasGroupsEnabled?: boolean } = {}, ): ApplyOperationsResult { + const skippedOperations: SkippedOperation[] = []; + const workflow = cloneWorkflow(input); const ctx: ApplyContext = { workflow, @@ -1074,6 +1157,9 @@ export function applyOperations( addedNodeNames: new Set(), tagSet: null, nodeGroupsChanged: false, + // Starts empty: the groups the workflow arrives with weren't requested by + // this batch, so no submitted op is answerable for them. + groupOperations: new Map(), }; for (let i = 0; i < operations.length; i++) { @@ -1083,8 +1169,12 @@ export function applyOperations( // `never`; the cast reconnects each op to its own handler. const handler = OPERATION_HANDLERS[op.type] as OpHandler; - const error = handler(op, ctx); + const error = handler(op, ctx, i); if (error) { + if (options.canvasGroupsEnabled && NON_FATAL_OPERATION_TYPES.has(op.type)) { + skippedOperations.push({ opIndex: i, type: op.type, reason: error }); + continue; + } return fail(i, error); } } @@ -1099,6 +1189,8 @@ export function applyOperations( addedNodeNames: [...ctx.addedNodeNames], tagNames: ctx.tagSet !== null ? [...ctx.tagSet] : undefined, nodeGroupsChanged: ctx.nodeGroupsChanged, + skippedOperations, + groupOperations: Object.fromEntries(ctx.groupOperations), }; } diff --git a/packages/cli/src/modules/n8n-packages/__tests__/export-workflow-with-tags.integration.test.ts b/packages/cli/src/modules/n8n-packages/__tests__/export-workflow-with-tags.integration.test.ts new file mode 100644 index 00000000000..84956e2e6aa --- /dev/null +++ b/packages/cli/src/modules/n8n-packages/__tests__/export-workflow-with-tags.integration.test.ts @@ -0,0 +1,226 @@ +import { LicenseState } from '@n8n/backend-common'; +import { createTeamProject, createWorkflow, testDb, testModules } from '@n8n/backend-test-utils'; +import { GlobalConfig } from '@n8n/config'; +import { Container } from '@n8n/di'; +import { jsonParse } from 'n8n-workflow'; + +import { EventService } from '@/events/event.service'; +import type { RelayEventMap } from '@/events/maps/relay.event-map'; +import { createFolder } from '@test-integration/db/folders'; +import { assignTagToWorkflow, createTag } from '@test-integration/db/tags'; +import { createOwner } from '@test-integration/db/users'; +import { LicenseMocker } from '@test-integration/license'; +import { initNodeTypes } from '@test-integration/utils'; + +import { N8nPackagesService } from '../n8n-packages.service'; +import { readExport } from './utils/tar-support'; +import type { UnpackedEntry } from './utils/tar-support'; +import { buildWorkflowCallingSubWorkflow } from './utils/test-builders'; + +const licenseMocker = new LicenseMocker(); + +beforeAll(async () => { + await testModules.loadModules(['n8n-packages']); + await testDb.init(); + await initNodeTypes(); + licenseMocker.mockLicenseState(Container.get(LicenseState)); +}); + +afterAll(async () => { + await testDb.terminate(); +}); + +beforeEach(async () => { + await testDb.truncate([ + 'WorkflowTagMapping', + 'TagEntity', + 'Folder', + 'WorkflowEntity', + 'SharedWorkflow', + 'ProjectRelation', + 'Project', + ]); +}); + +function tagFiles(entries: UnpackedEntry[]) { + return entries.filter((entry) => entry.name.endsWith('/tag.json')); +} + +function workflowJson(entries: UnpackedEntry[], target: string) { + const file = entries.find((entry) => entry.name === `${target}/workflow.json`); + if (!file) throw new Error(`missing ${target}/workflow.json`); + return jsonParse>(file.content.toString()); +} + +describe('workflow package export — with tags', () => { + let service: N8nPackagesService; + + beforeAll(() => { + service = Container.get(N8nPackagesService); + }); + + it('bundles referenced tags, writes tagIds and catalogs them in manifest and requirements', async () => { + const owner = await createOwner(); + const project = await createTeamProject('Project A', owner); + const workflow = await createWorkflow({ name: 'Tagged workflow' }, project); + // Created in reverse of sorted order so the exact-order assertions pin the sort. + const beta = await createTag({ name: 'beta' }, workflow); + const alpha = await createTag({ name: 'alpha' }, workflow); + + const emitSpy = vi.spyOn(Container.get(EventService), 'emit'); + try { + const { stream } = await service.exportPackage({ user: owner, workflowIds: [workflow.id] }); + const { manifest, entries } = await readExport(stream); + + expect(manifest.tags).toEqual([ + { id: alpha.id, name: 'alpha', target: 'tags/alpha' }, + { id: beta.id, name: 'beta', target: 'tags/beta' }, + ]); + expect(manifest.requirements!.tags).toEqual([ + { id: alpha.id, name: 'alpha', usedByWorkflows: [workflow.id] }, + { id: beta.id, name: 'beta', usedByWorkflows: [workflow.id] }, + ]); + + const serialized = workflowJson(entries, manifest.workflows![0].target); + expect(serialized.tagIds).toEqual([alpha.id, beta.id]); + + for (const entry of manifest.tags!) { + const file = entries.find((e) => e.name === `${entry.target}/tag.json`); + expect(file).toBeDefined(); + const parsed = jsonParse>(file!.content.toString()); + expect(parsed).toEqual({ id: entry.id, name: entry.name }); + } + + const exportedEvents = emitSpy.mock.calls.filter(([name]) => name === 'n8n-package-exported'); + expect(exportedEvents).toHaveLength(1); + const payload = exportedEvents[0][1] as RelayEventMap['n8n-package-exported']; + expect(payload.counts.tags).toBe(2); + } finally { + emitSpy.mockRestore(); + } + }); + + it('writes a tag shared by two workflows once', async () => { + const owner = await createOwner(); + const project = await createTeamProject('Project A', owner); + const wfA = await createWorkflow({ name: 'Workflow A' }, project); + const wfB = await createWorkflow({ name: 'Workflow B' }, project); + const tag = await createTag({ name: 'shared' }); + await assignTagToWorkflow(tag, wfA); + await assignTagToWorkflow(tag, wfB); + + const { stream } = await service.exportPackage({ user: owner, workflowIds: [wfA.id, wfB.id] }); + const { manifest, entries } = await readExport(stream); + + expect(manifest.tags).toEqual([{ id: tag.id, name: 'shared', target: 'tags/shared' }]); + expect(tagFiles(entries)).toHaveLength(1); + expect(manifest.requirements!.tags).toEqual([ + { id: tag.id, name: 'shared', usedByWorkflows: [wfA.id, wfB.id] }, + ]); + + for (const entry of manifest.workflows!) { + expect(workflowJson(entries, entry.target).tagIds).toEqual([tag.id]); + } + }); + + it('exports an untagged workflow with empty tagIds and no tag artifacts', async () => { + const owner = await createOwner(); + const project = await createTeamProject('Project A', owner); + const workflow = await createWorkflow({ name: 'Untagged workflow' }, project); + + const { stream } = await service.exportPackage({ user: owner, workflowIds: [workflow.id] }); + const { manifest, entries } = await readExport(stream); + + // An emitted `tagIds` key (vs absent) is how import knows tags were exported at all. + expect(workflowJson(entries, manifest.workflows![0].target).tagIds).toEqual([]); + expect(tagFiles(entries)).toEqual([]); + expect(manifest).not.toHaveProperty('tags'); + expect(manifest.requirements).toEqual({ nodeTypes: expect.any(Array) }); + }); + + it('with includeTags=false exports a tagged folder workflow without any tag artifacts', async () => { + const owner = await createOwner(); + const project = await createTeamProject('Project A', owner); + const folder = await createFolder(project, { name: 'ops' }); + const workflow = await createWorkflow({ name: 'In folder', parentFolder: folder }, project); + await createTag({ name: 'prod' }, workflow); + + const { stream } = await service.exportPackage({ + user: owner, + folderIds: [folder.id], + includeTags: false, + }); + const { manifest, entries } = await readExport(stream); + + expect(workflowJson(entries, manifest.workflows![0].target)).not.toHaveProperty('tagIds'); + expect(tagFiles(entries)).toEqual([]); + expect(manifest).not.toHaveProperty('tags'); + expect(manifest.requirements).toEqual({ nodeTypes: expect.any(Array) }); + }); + + it('writes tags at the package root for a project export', async () => { + const owner = await createOwner(); + const project = await createTeamProject('team-ligo', owner); + const folder = await createFolder(project, { name: 'ops' }); + const workflow = await createWorkflow({ name: 'Deep workflow', parentFolder: folder }, project); + const tag = await createTag({ name: 'prod' }, workflow); + + const { stream } = await service.exportPackage({ user: owner, projectIds: [project.id] }); + const { manifest, entries } = await readExport(stream); + + expect(manifest.tags).toEqual([{ id: tag.id, name: 'prod', target: 'tags/prod' }]); + expect(entries.find((e) => e.name === 'tags/prod/tag.json')).toBeDefined(); + + const workflowEntry = manifest.workflows!.find((entry) => entry.id === workflow.id)!; + expect(workflowEntry.target).toMatch(/^projects\//); + expect(workflowJson(entries, workflowEntry.target).tagIds).toEqual([tag.id]); + }); + + it('bundles the tags of auto-included sub-workflows', async () => { + const owner = await createOwner(); + const project = await createTeamProject('Project A', owner); + const sub = await createWorkflow({ name: 'Sub workflow' }, project); + const tag = await createTag({ name: 'prod' }, sub); + const parent = await buildWorkflowCallingSubWorkflow({ + name: 'Parent workflow', + project, + subWorkflowId: sub.id, + }); + + const { stream } = await service.exportPackage({ + user: owner, + workflowIds: [parent.id], + missingWorkflowDependencyPolicy: 'include-in-package', + }); + const { manifest, entries } = await readExport(stream); + + expect(manifest.tags).toEqual([{ id: tag.id, name: 'prod', target: 'tags/prod' }]); + expect(manifest.requirements!.tags).toEqual([ + { id: tag.id, name: 'prod', usedByWorkflows: [sub.id] }, + ]); + + const subEntry = manifest.workflows!.find((entry) => entry.id === sub.id)!; + expect(workflowJson(entries, subEntry.target).tagIds).toEqual([tag.id]); + }); + + it('skips tags silently when workflow tags are disabled on the instance', async () => { + const owner = await createOwner(); + const project = await createTeamProject('Project A', owner); + const workflow = await createWorkflow({ name: 'Tagged workflow' }, project); + await createTag({ name: 'prod' }, workflow); + + const globalConfig = Container.get(GlobalConfig); + globalConfig.tags.disabled = true; + try { + const { stream } = await service.exportPackage({ user: owner, workflowIds: [workflow.id] }); + const { manifest, entries } = await readExport(stream); + + expect(workflowJson(entries, manifest.workflows![0].target)).not.toHaveProperty('tagIds'); + expect(tagFiles(entries)).toEqual([]); + expect(manifest).not.toHaveProperty('tags'); + expect(manifest.requirements).toEqual({ nodeTypes: expect.any(Array) }); + } finally { + globalConfig.tags.disabled = false; + } + }); +}); diff --git a/packages/cli/src/modules/n8n-packages/__tests__/export-workflow.integration.test.ts b/packages/cli/src/modules/n8n-packages/__tests__/export-workflow.integration.test.ts index 8e08dc96dde..3e317d51ff9 100644 --- a/packages/cli/src/modules/n8n-packages/__tests__/export-workflow.integration.test.ts +++ b/packages/cli/src/modules/n8n-packages/__tests__/export-workflow.integration.test.ts @@ -23,6 +23,7 @@ import { buildWorkflowCallingSubWorkflow, buildWorkflowReferencingCredential, buildWorkflowUsingErrorWorkflow, + executeWorkflowNode, } from './utils/test-builders'; beforeAll(async () => { @@ -433,23 +434,6 @@ describe('workflow package export', () => { ).rejects.toThrow(PackageExportBlockedError); }); - it('rejects reference-only static sub-workflow dependency policy', async () => { - const owner = await createOwner(); - const project = await createTeamProject('Project A', owner); - const workflow = await createWorkflow( - { name: 'Parent', nodes: [], connections: {} }, - project, - ); - - await expect( - service.exportPackage({ - user: owner, - workflowIds: [workflow.id], - missingWorkflowDependencyPolicy: 'reference-only', - }), - ).rejects.toThrow('Reference-only static sub-workflow dependencies are not supported'); - }); - it('auto-includes transitive static sub-workflows and their requirements', async () => { const owner = await createOwner(); const project = await createTeamProject('Project A', owner); @@ -607,6 +591,231 @@ describe('workflow package export', () => { }); }); + describe('reference-only missing dependency policy', () => { + async function exportReferenceOnly(user: User, workflowIds: string[]) { + const { stream } = await service.exportPackage({ + user, + workflowIds, + missingWorkflowDependencyPolicy: 'reference-only', + }); + return await readExport(stream); + } + + it('lists a missing sub-workflow as a requirement instead of failing', async () => { + const owner = await createOwner(); + const project = await createTeamProject('Project A', owner); + const child = await createWorkflow({ name: 'Child', nodes: [], connections: {} }, project); + const parent = await buildWorkflowCallingSubWorkflow({ + name: 'Parent', + project, + subWorkflowId: child.id, + }); + + const { manifest, entries } = await exportReferenceOnly(owner, [parent.id]); + + expect(manifest.workflows).toEqual([ + { id: parent.id, name: 'Parent', target: expect.any(String) }, + ]); + expect(manifest.requirements?.workflows).toEqual([ + { id: child.id, name: child.name, usedByWorkflows: [parent.id] }, + ]); + // Only the parent's workflow.json travels; the referenced child stays out. + expect(entries.filter((e) => e.name.endsWith('/workflow.json'))).toHaveLength(1); + }); + + it('does not bundle or require credentials used only by a referenced sub-workflow', async () => { + const owner = await createOwner(); + const project = await createTeamProject('Project A', owner); + const credential = await saveCredential( + { name: 'Child API', type: 'httpHeaderAuth', data: { name: 'X-Auth', value: 'secret' } }, + { project, role: 'credential:owner' }, + ); + const child = await buildWorkflowReferencingCredential({ + name: 'Child', + project, + credential, + }); + const parent = await buildWorkflowCallingSubWorkflow({ + name: 'Parent', + project, + subWorkflowId: child.id, + }); + + const { manifest, entries } = await exportReferenceOnly(owner, [parent.id]); + + expect(manifest.credentials).toBeUndefined(); + expect(manifest.requirements?.credentials).toBeUndefined(); + expect(entries.some((e) => e.name.includes('credential'))).toBe(false); + expect(manifest.requirements?.workflows).toEqual([ + { id: child.id, name: child.name, usedByWorkflows: [parent.id] }, + ]); + }); + + it('keeps a credential shared with a referenced sub-workflow, attributed to the exported workflow only', async () => { + const owner = await createOwner(); + const project = await createTeamProject('Project A', owner); + const credential = await saveCredential( + { name: 'Shared API', type: 'httpHeaderAuth', data: { name: 'X-Auth', value: 'secret' } }, + { project, role: 'credential:owner' }, + ); + const child = await buildWorkflowReferencingCredential({ + name: 'Child', + project, + credential, + }); + const parent = await createWorkflow( + { + name: 'Parent', + nodes: [ + executeWorkflowNode(child.id), + { + id: 'http', + name: 'HTTP', + type: 'n8n-nodes-base.httpRequest', + typeVersion: 1, + position: [200, 0], + parameters: {}, + credentials: { + [credential.type]: { id: credential.id, name: credential.name }, + }, + }, + ], + connections: {}, + }, + project, + ); + + const { manifest } = await exportReferenceOnly(owner, [parent.id]); + + expect(manifest.requirements?.credentials).toEqual([ + { + id: credential.id, + name: credential.name, + type: credential.type, + usedByWorkflows: [parent.id], + }, + ]); + expect(manifest.requirements?.workflows).toEqual([ + { id: child.id, name: child.name, usedByWorkflows: [parent.id] }, + ]); + }); + + it('references a self-calling sub-workflow once and exports the requested workflow normally', async () => { + const owner = await createOwner(); + const project = await createTeamProject('Project A', owner); + const brie = await createWorkflow({ name: 'BRIE', nodes: [], connections: {} }, project); + brie.nodes = [executeWorkflowNode(brie.id)]; + await Container.get(WorkflowRepository).save(brie); + const cheddar = await buildWorkflowCallingSubWorkflow({ + name: 'CHEDDAR', + project, + subWorkflowId: brie.id, + }); + + const { manifest, entries } = await exportReferenceOnly(owner, [cheddar.id]); + + expect(manifest.workflows).toEqual([ + { id: cheddar.id, name: 'CHEDDAR', target: expect.any(String) }, + ]); + expect(manifest.requirements?.workflows).toEqual([ + { id: brie.id, name: 'BRIE', usedByWorkflows: [cheddar.id] }, + ]); + expect(entries.filter((e) => e.name.endsWith('/workflow.json'))).toHaveLength(1); + }); + + it('lists only direct references, not the referenced workflows own dependencies', async () => { + const owner = await createOwner(); + const project = await createTeamProject('Project A', owner); + const workflowC = await createWorkflow( + { name: 'Workflow C', nodes: [], connections: {} }, + project, + ); + const workflowB = await buildWorkflowCallingSubWorkflow({ + name: 'Workflow B', + project, + subWorkflowId: workflowC.id, + }); + const workflowA = await buildWorkflowCallingSubWorkflow({ + name: 'Workflow A', + project, + subWorkflowId: workflowB.id, + }); + + const { manifest } = await exportReferenceOnly(owner, [workflowA.id]); + + expect(manifest.requirements?.workflows).toEqual([ + { id: workflowB.id, name: workflowB.name, usedByWorkflows: [workflowA.id] }, + ]); + }); + + it('omits the name when the referenced workflow is not accessible to the exporting user', async () => { + const owner = await createOwner(); + const ownerProject = await createTeamProject('Owner Project', owner); + const foreignWorkflow = await createWorkflow( + { name: 'Foreign', nodes: [], connections: {} }, + ownerProject, + ); + const member = await createMember(); + const memberPersonal = await Container.get(ProjectRepository).getPersonalProjectForUserOrFail( + member.id, + ); + const parent = await buildWorkflowCallingSubWorkflow({ + name: 'Parent', + project: memberPersonal, + subWorkflowId: foreignWorkflow.id, + }); + + const { manifest } = await exportReferenceOnly(member, [parent.id]); + + expect(manifest.requirements?.workflows).toEqual([ + { id: foreignWorkflow.id, usedByWorkflows: [parent.id] }, + ]); + expect(manifest.requirements?.workflows?.[0]).not.toHaveProperty('name'); + }); + + it('still lists references that are satisfied inside the package', async () => { + const owner = await createOwner(); + const project = await createTeamProject('Project A', owner); + const child = await createWorkflow({ name: 'Child', nodes: [], connections: {} }, project); + const parent = await buildWorkflowCallingSubWorkflow({ + name: 'Parent', + project, + subWorkflowId: child.id, + }); + + const { manifest, entries } = await exportReferenceOnly(owner, [parent.id, child.id]); + + expect(manifest.workflows!.map(({ id }) => id).sort()).toEqual([parent.id, child.id].sort()); + expect(entries.filter((e) => e.name.endsWith('/workflow.json'))).toHaveLength(2); + expect(manifest.requirements?.workflows).toEqual([ + { id: child.id, name: child.name, usedByWorkflows: [parent.id] }, + ]); + }); + + it('lists a missing error workflow as a requirement instead of failing', async () => { + const owner = await createOwner(); + const project = await createTeamProject('Project A', owner); + const errorHandler = await createWorkflow( + { name: 'Error Handler', nodes: [], connections: {} }, + project, + ); + const parent = await buildWorkflowUsingErrorWorkflow({ + name: 'Parent', + project, + errorWorkflowId: errorHandler.id, + }); + + const { manifest } = await exportReferenceOnly(owner, [parent.id]); + + expect(manifest.workflows).toEqual([ + { id: parent.id, name: 'Parent', target: expect.any(String) }, + ]); + expect(manifest.requirements?.workflows).toEqual([ + { id: errorHandler.id, name: errorHandler.name, usedByWorkflows: [parent.id] }, + ]); + }); + }); + describe('authorization', () => { it('Lists count of workflows inaccessible', async () => { const owner = await createOwner(); diff --git a/packages/cli/src/modules/n8n-packages/__tests__/fixtures/package-fixtures.ts b/packages/cli/src/modules/n8n-packages/__tests__/fixtures/package-fixtures.ts index 9b961fa150a..5ef5b57e004 100644 --- a/packages/cli/src/modules/n8n-packages/__tests__/fixtures/package-fixtures.ts +++ b/packages/cli/src/modules/n8n-packages/__tests__/fixtures/package-fixtures.ts @@ -1,4 +1,6 @@ import { randomCredentialPayload, type CredentialPayload } from '@n8n/backend-test-utils'; +import type { WorkflowEntity } from '@n8n/db'; +import { EXECUTE_WORKFLOW_NODE_TYPE, getSubworkflowId, type INode } from 'n8n-workflow'; import { TarPackageWriter } from '../../io/tar/tar-package-writer'; import { FORMAT_VERSION } from '../../spec/constants'; @@ -6,6 +8,7 @@ import type { PackageManifest } from '../../spec/manifest.schema'; import type { PackageCredentialRequirement, PackageDataTableRequirement, + PackageWorkflowRequirement, } from '../../spec/requirements.schema'; import type { SerializedDataTable } from '../../spec/serialized/data-table.schema'; import type { SerializedFolder } from '../../spec/serialized/folder.schema'; @@ -78,6 +81,99 @@ export function serializedWorkflowWithCredential(options: { }); } +/** + * Builds a workflow whose Execute Sub-workflow node references another workflow by + * id, optionally carrying `callerIds`/`callerPolicy` settings. + */ +export function serializedWorkflowWithSubWorkflow(options: { + id: string; + name: string; + subWorkflowId: string; + mode?: 'id' | 'list'; + callerIds?: string; + callerPolicy?: string; +}): SerializedWorkflow { + const settings = + options.callerIds !== undefined || options.callerPolicy !== undefined + ? { + ...(options.callerPolicy !== undefined ? { callerPolicy: options.callerPolicy } : {}), + ...(options.callerIds !== undefined ? { callerIds: options.callerIds } : {}), + } + : undefined; + + return serializedWorkflow({ + id: options.id, + name: options.name, + nodes: [ + { + id: 'execute-workflow', + name: 'Execute Sub-workflow', + type: EXECUTE_WORKFLOW_NODE_TYPE, + typeVersion: 1, + position: [0, 0], + parameters: { + workflowId: { __rl: true, mode: options.mode ?? 'id', value: options.subWorkflowId }, + }, + }, + ], + ...(settings ? { settings } : {}), + }); +} + +/** + * Builds manifest workflow requirements from each workflow's sub-workflow node refs and + * `settings.errorWorkflow` (simulates export — mirrors `extractWorkflowRequirements`). + */ +export function workflowRequirementsFromWorkflows( + workflows: SerializedWorkflow[], +): PackageWorkflowRequirement[] { + const nameById = new Map(workflows.map((workflow) => [workflow.id, workflow.name])); + const byId = new Map(); + + const addRequirement = (referencedId: string | undefined, workflowId: string) => { + if (!referencedId) return; + const existing = byId.get(referencedId); + if (existing) { + if (!existing.usedByWorkflows.includes(workflowId)) existing.usedByWorkflows.push(workflowId); + return; + } + byId.set(referencedId, { + id: referencedId, + name: nameById.get(referencedId) ?? referencedId, + usedByWorkflows: [workflowId], + }); + }; + + for (const workflow of workflows) { + for (const node of workflow.nodes) addRequirement(getSubworkflowId(node as INode), workflow.id); + addRequirement(errorWorkflowRef(workflow), workflow.id); + } + + return [...byId.values()]; +} + +/** Static `settings.errorWorkflow` id referenced by a workflow, if any (mirrors the exporter). */ +function errorWorkflowRef(workflow: SerializedWorkflow): string | undefined { + const errorWorkflow = workflow.settings?.errorWorkflow; + if ( + typeof errorWorkflow !== 'string' || + errorWorkflow === 'DEFAULT' || + errorWorkflow.startsWith('=') + ) { + return undefined; + } + return errorWorkflow; +} + +/** Returns the id the workflow's Execute Sub-workflow node points at. */ +export function subWorkflowRefOf(workflow: WorkflowEntity): string | undefined { + for (const node of workflow.nodes) { + const referencedId = getSubworkflowId(node); + if (referencedId) return referencedId; + } + return undefined; +} + /** Builds manifest credential requirements from workflow node refs (simulates export). */ export function credentialRequirementsFromWorkflows( workflows: SerializedWorkflow[], diff --git a/packages/cli/src/modules/n8n-packages/__tests__/import-data-tables.integration.test.ts b/packages/cli/src/modules/n8n-packages/__tests__/import-data-tables.integration.test.ts index 95062b6b005..d10b69b42aa 100644 --- a/packages/cli/src/modules/n8n-packages/__tests__/import-data-tables.integration.test.ts +++ b/packages/cli/src/modules/n8n-packages/__tests__/import-data-tables.integration.test.ts @@ -76,6 +76,8 @@ async function importPackage(params: ImportParams) { dataTableSchemaConflictPolicy: 'keep-existing', variableMissingMode: 'do-nothing', variableParentPolicy: 'project', + tagMissingMode: 'create', + tagConflictPolicy: 'skip', ...params, }); } diff --git a/packages/cli/src/modules/n8n-packages/__tests__/import-folders.integration.test.ts b/packages/cli/src/modules/n8n-packages/__tests__/import-folders.integration.test.ts index 2e70ac06e57..6f6ce66c4ff 100644 --- a/packages/cli/src/modules/n8n-packages/__tests__/import-folders.integration.test.ts +++ b/packages/cli/src/modules/n8n-packages/__tests__/import-folders.integration.test.ts @@ -50,6 +50,8 @@ async function importFolders(params: FolderImportParams) { dataTableSchemaConflictPolicy: 'keep-existing', variableMissingMode: 'do-nothing', variableParentPolicy: 'project', + tagMissingMode: 'create', + tagConflictPolicy: 'skip', }; return await Container.get(N8nPackagesService).importPackage(request); } diff --git a/packages/cli/src/modules/n8n-packages/__tests__/import-package.integration.test.ts b/packages/cli/src/modules/n8n-packages/__tests__/import-package.integration.test.ts index 0daea3f205b..402f83b37dc 100644 --- a/packages/cli/src/modules/n8n-packages/__tests__/import-package.integration.test.ts +++ b/packages/cli/src/modules/n8n-packages/__tests__/import-package.integration.test.ts @@ -74,6 +74,8 @@ type ImportPackageParams = Omit< | 'dataTableSchemaConflictPolicy' | 'variableMissingMode' | 'variableParentPolicy' + | 'tagMissingMode' + | 'tagConflictPolicy' > & Partial< Pick< @@ -91,6 +93,8 @@ type ImportPackageParams = Omit< | 'dataTableSchemaConflictPolicy' | 'variableMissingMode' | 'variableParentPolicy' + | 'tagMissingMode' + | 'tagConflictPolicy' > >; @@ -108,6 +112,8 @@ async function importPackage(params: ImportPackageParams) { dataTableSchemaConflictPolicy: DataTableSchemaConflictPolicy.KeepExisting, variableMissingMode: 'do-nothing', variableParentPolicy: 'project', + tagMissingMode: 'create', + tagConflictPolicy: 'skip', ...params, }); } @@ -1215,6 +1221,13 @@ describe('Package import event emission', () => { created: 0, requirements: 0, }, + tags: { + matched: 0, + created: 0, + renamed: 0, + skipped: 0, + requirements: 0, + }, }); } finally { emitSpy.mockRestore(); @@ -1300,6 +1313,13 @@ describe('Package import event emission', () => { created: 0, requirements: 0, }, + tags: { + matched: 0, + created: 0, + renamed: 0, + skipped: 0, + requirements: 0, + }, }); } finally { emitSpy.mockRestore(); @@ -1354,6 +1374,13 @@ describe('Package import event emission', () => { created: 0, requirements: 0, }, + tags: { + matched: 0, + created: 0, + renamed: 0, + skipped: 0, + requirements: 0, + }, }); } finally { emitSpy.mockRestore(); @@ -1410,6 +1437,13 @@ describe('Package import event emission', () => { created: 0, requirements: 0, }, + tags: { + matched: 0, + created: 0, + renamed: 0, + skipped: 0, + requirements: 0, + }, }); } finally { emitSpy.mockRestore(); diff --git a/packages/cli/src/modules/n8n-packages/__tests__/import-projects.integration.test.ts b/packages/cli/src/modules/n8n-packages/__tests__/import-projects.integration.test.ts index c172de31e6c..adc7671c5d0 100644 --- a/packages/cli/src/modules/n8n-packages/__tests__/import-projects.integration.test.ts +++ b/packages/cli/src/modules/n8n-packages/__tests__/import-projects.integration.test.ts @@ -1,16 +1,25 @@ import { LicenseState } from '@n8n/backend-common'; -import { createTeamProject, linkUserToProject, testDb, testModules } from '@n8n/backend-test-utils'; -import type { User } from '@n8n/db'; +import { + createTeamProject, + linkUserToProject, + mockInstance, + testDb, + testModules, +} from '@n8n/backend-test-utils'; import { FolderRepository, ProjectRelationRepository, ProjectRepository, SharedWorkflowRepository, + TagRepository, VariablesRepository, WorkflowRepository, + WorkflowTagMappingRepository, } from '@n8n/db'; +import type { User } from '@n8n/db'; import { Container } from '@n8n/di'; +import { ActiveWorkflowManager } from '@/active-workflow-manager'; import { VariablesService } from '@/environments.ee/variables/variables.service.ee'; import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; import { UnprocessableRequestError } from '@/errors/response-errors/unprocessable.error'; @@ -30,6 +39,9 @@ import { serializedProject, serializedWorkflow, serializedWorkflowWithCredential, + serializedWorkflowWithSubWorkflow, + subWorkflowRefOf, + workflowRequirementsFromWorkflows, } from './fixtures/package-fixtures'; import { streamToBuffer } from './utils/tar-support'; @@ -54,6 +66,8 @@ async function importProjects( dataTableMissingMode: 'create', dataTableSchemaConflictPolicy: 'keep-existing', variableMissingMode: 'do-nothing', + tagMissingMode: 'create', + tagConflictPolicy: 'skip', ...overrides, }; return await Container.get(N8nPackagesService).importPackage(request); @@ -61,6 +75,9 @@ async function importProjects( const licenseMocker = new LicenseMocker(); +// Publishing tests exercise the real publish path; mock the trigger infra it touches. +mockInstance(ActiveWorkflowManager); + async function findProject(id: string) { return await Container.get(ProjectRepository).findOne({ where: { id } }); } @@ -105,6 +122,8 @@ afterAll(async () => { beforeEach(async () => { await testDb.truncate([ + 'WorkflowTagMapping', + 'TagEntity', 'Folder', 'Project', 'ProjectRelation', @@ -327,6 +346,44 @@ describe('project shell import', () => { expect((await findWorkflow(wfb.localId))?.parentFolder?.id).toBe('FB'); }); + it('creates a tag shared across two projects exactly once and attaches it in both', async () => { + const packageBuffer = await buildEntityPackageBuffer({ + projects: [ + { target: 'projects/brie', project: serializedProject({ id: 'P1', name: 'brie' }) }, + { target: 'projects/stilton', project: serializedProject({ id: 'P2', name: 'stilton' }) }, + ], + workflows: [ + { + target: 'projects/brie/workflows/wfa', + workflow: serializedWorkflow({ id: 'WFA', name: 'wfa', tagIds: ['TAG1'] }), + }, + { + target: 'projects/stilton/workflows/wfb', + workflow: serializedWorkflow({ id: 'WFB', name: 'wfb', tagIds: ['TAG1'] }), + }, + ], + manifestExtras: { + requirements: { + tags: [{ id: 'TAG1', name: 'shared', usedByWorkflows: ['WFA', 'WFB'] }], + }, + }, + }); + + const result = await importProjects(owner, packageBuffer); + + expect(result.tags).toEqual({ matched: [], created: ['shared'], renamed: [], skipped: [] }); + expect(await Container.get(TagRepository).find()).toEqual([ + expect.objectContaining({ id: 'TAG1', name: 'shared' }), + ]); + const mappings = await Container.get(WorkflowTagMappingRepository).find(); + expect(mappings.map(({ workflowId, tagId }) => ({ workflowId, tagId }))).toEqual( + expect.arrayContaining( + result.workflows.map(({ localId }) => ({ workflowId: localId, tagId: 'TAG1' })), + ), + ); + expect(mappings).toHaveLength(2); + }); + it('reuses the project and folder and updates the workflow on re-import', async () => { const pkg = async () => await buildEntityPackageBuffer({ @@ -1077,6 +1134,93 @@ describe('project shell import', () => { emitSpy.mockRestore(); } }); + + it('rewrites a sub-workflow reference that points into another project under the `new` policy', async () => { + // Project brie's workflow calls a sub-workflow that lives in project stilton. + const parent = serializedWorkflowWithSubWorkflow({ + id: 'CHEDDAR', + name: 'Parent', + subWorkflowId: 'BRIE', + }); + const subWorkflow = serializedWorkflow({ id: 'BRIE', name: 'Sub-workflow' }); + + const packageBuffer = await buildEntityPackageBuffer({ + projects: [ + { target: 'projects/brie', project: serializedProject({ id: 'P1', name: 'brie' }) }, + { target: 'projects/stilton', project: serializedProject({ id: 'P2', name: 'stilton' }) }, + ], + workflows: [ + { target: 'projects/brie/workflows/parent', workflow: parent }, + { target: 'projects/stilton/workflows/sub', workflow: subWorkflow }, + ], + manifestExtras: { + requirements: { workflows: workflowRequirementsFromWorkflows([parent, subWorkflow]) }, + }, + }); + + const result = await importProjects(owner, packageBuffer); + + const importedParent = result.workflows.find((w) => w.sourceWorkflowId === 'CHEDDAR')!; + const importedSub = result.workflows.find((w) => w.sourceWorkflowId === 'BRIE')!; + + // Each workflow landed in its own project with a freshly-minted id... + expect(importedParent.projectId).toBe('P1'); + expect(importedSub.projectId).toBe('P2'); + expect(importedSub.localId).not.toBe('BRIE'); + // ...and the cross-project reference resolves to the sub-workflow's imported id. + expect(subWorkflowRefOf((await findWorkflow(importedParent.localId))!)).toBe( + importedSub.localId, + ); + }); + + it('publishes a parent whose sub-workflow lives in a project written after it', async () => { + // Activation rejects a parent whose sub-workflow is not yet published. The calling project is + // listed first here, so only a publish phase that runs after every project is written — and + // orders dependencies first — can get both published. + const scheduleTrigger = { + id: 'schedule-trigger', + name: 'Schedule Trigger', + type: 'n8n-nodes-base.scheduleTrigger', + typeVersion: 1, + position: [0, 0] as [number, number], + parameters: {}, + }; + const parentBase = serializedWorkflowWithSubWorkflow({ + id: 'CHEDDAR', + name: 'Parent', + subWorkflowId: 'BRIE', + }); + const parent = { ...parentBase, nodes: [scheduleTrigger, ...parentBase.nodes] }; + const subWorkflow = serializedWorkflow({ + id: 'BRIE', + name: 'Sub-workflow', + nodes: [scheduleTrigger], + }); + + const packageBuffer = await buildEntityPackageBuffer({ + projects: [ + { target: 'projects/brie', project: serializedProject({ id: 'P1', name: 'brie' }) }, + { target: 'projects/stilton', project: serializedProject({ id: 'P2', name: 'stilton' }) }, + ], + workflows: [ + { target: 'projects/brie/workflows/parent', workflow: parent }, + { target: 'projects/stilton/workflows/sub', workflow: subWorkflow }, + ], + manifestExtras: { + requirements: { workflows: workflowRequirementsFromWorkflows([parent, subWorkflow]) }, + }, + }); + + const result = await importProjects(owner, packageBuffer, undefined, { + workflowPublishingPolicy: 'publish-all', + }); + + const summaryFor = (sourceWorkflowId: string) => + result.workflows.find((entry) => entry.sourceWorkflowId === sourceWorkflowId); + + expect(summaryFor('BRIE')?.publishing).toEqual({ state: 'published' }); + expect(summaryFor('CHEDDAR')?.publishing).toEqual({ state: 'published' }); + }); }); describe('project custom span attributes (LIGO-862)', () => { diff --git a/packages/cli/src/modules/n8n-packages/__tests__/import-subworkflows.integration.test.ts b/packages/cli/src/modules/n8n-packages/__tests__/import-subworkflows.integration.test.ts new file mode 100644 index 00000000000..2405b5f7887 --- /dev/null +++ b/packages/cli/src/modules/n8n-packages/__tests__/import-subworkflows.integration.test.ts @@ -0,0 +1,222 @@ +import { LicenseState } from '@n8n/backend-common'; +import { mockInstance, testDb, testModules } from '@n8n/backend-test-utils'; +import { WorkflowRepository } from '@n8n/db'; +import { Container } from '@n8n/di'; + +import { ActiveWorkflowManager } from '@/active-workflow-manager'; +import { createOwner } from '@test-integration/db/users'; +import { LicenseMocker } from '@test-integration/license'; +import { initNodeTypes } from '@test-integration/utils'; + +import { N8nPackagesService } from '../n8n-packages.service'; +import { + DataTableMatchingMode, + DataTableMissingMode, + DataTableSchemaConflictPolicy, + FolderConflictPolicy, + MissingNodeTypeMode, + TagConflictPolicy, + TagMissingMode, + VariableMissingMode, + WorkflowConflictPolicy, + WorkflowIdPolicy, + WorkflowPublishingPolicy, + type ImportPackageRequest, +} from '../n8n-packages.types'; +import { + buildImportPackageBuffer, + serializedWorkflow, + serializedWorkflowWithSubWorkflow, + subWorkflowRefOf, + workflowRequirementsFromWorkflows, +} from './fixtures/package-fixtures'; +import type { SerializedWorkflow } from '../spec/serialized/workflow.schema'; + +type ImportPackageParams = Pick & + Partial>; + +async function importPackage(params: ImportPackageParams) { + return await Container.get(N8nPackagesService).importPackage({ + credentialMatchingMode: 'id-only', + credentialMissingMode: 'must-preexist', + workflowConflictPolicy: WorkflowConflictPolicy.Fail, + workflowPublishingPolicy: WorkflowPublishingPolicy.PreservePublishedState, + workflowIdPolicy: WorkflowIdPolicy.New, + missingNodeTypeMode: MissingNodeTypeMode.Fail, + folderConflictPolicy: FolderConflictPolicy.Merge, + dataTableMatchingMode: DataTableMatchingMode.ById, + dataTableMissingMode: DataTableMissingMode.Create, + dataTableSchemaConflictPolicy: DataTableSchemaConflictPolicy.KeepExisting, + variableMissingMode: VariableMissingMode.DoNothing, + tagMissingMode: TagMissingMode.Create, + tagConflictPolicy: TagConflictPolicy.Skip, + ...params, + }); +} + +/** Builds a package where `workflows` carry Execute Sub-workflow refs + the derived requirements. */ +async function buildSubWorkflowPackage(workflows: SerializedWorkflow[]) { + return await buildImportPackageBuffer(workflows, { + manifestExtras: { + requirements: { workflows: workflowRequirementsFromWorkflows(workflows) }, + }, + }); +} + +const licenseMocker = new LicenseMocker(); + +// Import never activates these workflows (they are unpublished), but the publisher +// path touches the active workflow manager — mock it so no real infra is needed. +mockInstance(ActiveWorkflowManager); + +beforeAll(async () => { + await testModules.loadModules(['n8n-packages']); + await testDb.init(); + await initNodeTypes(); + licenseMocker.mockLicenseState(Container.get(LicenseState)); +}); + +afterAll(async () => { + await testDb.terminate(); +}); + +beforeEach(async () => { + await testDb.truncate(['WorkflowEntity', 'WorkflowHistory', 'SharedWorkflow', 'Project']); +}); + +describe('Package import of workflows with sub-workflows', () => { + async function findImported( + result: Awaited>, + sourceWorkflowId: string, + ) { + const summary = result.workflows.find((w) => w.sourceWorkflowId === sourceWorkflowId); + if (!summary) throw new Error(`No imported workflow for source id ${sourceWorkflowId}`); + return await Container.get(WorkflowRepository).findOneByOrFail({ id: summary.localId }); + } + + it('rewrites the sub-workflow reference to the imported id under the `new` policy', async () => { + const owner = await createOwner(); + + // Parent listed before its sub-workflow to prove ordering does not matter. + const parent = serializedWorkflowWithSubWorkflow({ + id: 'CHEDDAR', + name: 'Parent', + subWorkflowId: 'BRIE', + }); + const subWorkflow = serializedWorkflow({ id: 'BRIE', name: 'Sub-workflow' }); + + const result = await importPackage({ + user: owner, + packageBuffer: await buildSubWorkflowPackage([parent, subWorkflow]), + workflowIdPolicy: WorkflowIdPolicy.New, + }); + + const importedParent = await findImported(result, 'CHEDDAR'); + const importedSub = await findImported(result, 'BRIE'); + + expect(importedSub.id).not.toBe('BRIE'); + expect(subWorkflowRefOf(importedParent)).toBe(importedSub.id); + expect(result.bindings.workflows).toMatchObject({ + CHEDDAR: importedParent.id, + BRIE: importedSub.id, + }); + }); + + it('keeps the reference pointing at the imported sub-workflow under the `source` policy', async () => { + const owner = await createOwner(); + + const parent = serializedWorkflowWithSubWorkflow({ + id: 'CHEDDAR', + name: 'Parent', + subWorkflowId: 'BRIE', + }); + const subWorkflow = serializedWorkflow({ id: 'BRIE', name: 'Sub-workflow' }); + + const result = await importPackage({ + user: owner, + packageBuffer: await buildSubWorkflowPackage([parent, subWorkflow]), + workflowIdPolicy: WorkflowIdPolicy.Source, + }); + + const importedParent = await findImported(result, 'CHEDDAR'); + const importedSub = await findImported(result, 'BRIE'); + + expect(importedSub.id).toBe('BRIE'); + expect(subWorkflowRefOf(importedParent)).toBe('BRIE'); + }); + + it('remaps `settings.callerIds` to the imported caller ids under the `new` policy', async () => { + const owner = await createOwner(); + + const parent = serializedWorkflowWithSubWorkflow({ + id: 'CHEDDAR', + name: 'Parent', + subWorkflowId: 'BRIE', + }); + // The sub-workflow allows only CHEDDAR (a source id) to call it, plus an id + // for a workflow that is not part of this package. + const subWorkflow = serializedWorkflow({ + id: 'BRIE', + name: 'Sub-workflow', + settings: { callerPolicy: 'workflowsFromAList', callerIds: 'CHEDDAR,EDAM' }, + }); + + const result = await importPackage({ + user: owner, + packageBuffer: await buildSubWorkflowPackage([parent, subWorkflow]), + workflowIdPolicy: WorkflowIdPolicy.New, + }); + + const importedParent = await findImported(result, 'CHEDDAR'); + const importedSub = await findImported(result, 'BRIE'); + + // In-package caller id is remapped; the unknown id (EDAM) is left as-is. + expect(importedSub.settings?.callerIds).toBe(`${importedParent.id},EDAM`); + // Caller policy is preserved so the allowlist intent survives the import. + expect(importedSub.settings?.callerPolicy).toBe('workflowsFromAList'); + }); + + it('remaps `settings.errorWorkflow` to the imported id under the `new` policy', async () => { + const owner = await createOwner(); + + // The main workflow runs BRIE as its error workflow. + const main = serializedWorkflow({ + id: 'CHEDDAR', + name: 'Main', + settings: { errorWorkflow: 'BRIE' }, + }); + const errorHandler = serializedWorkflow({ id: 'BRIE', name: 'Error handler' }); + + const result = await importPackage({ + user: owner, + packageBuffer: await buildSubWorkflowPackage([main, errorHandler]), + workflowIdPolicy: WorkflowIdPolicy.New, + }); + + const importedMain = await findImported(result, 'CHEDDAR'); + const importedError = await findImported(result, 'BRIE'); + + expect(importedError.id).not.toBe('BRIE'); + expect(importedMain.settings?.errorWorkflow).toBe(importedError.id); + }); + + it('does not crash when a package carries a non-string settings.callerIds', async () => { + const owner = await createOwner(); + + // A malformed package could carry a non-string callerIds (settings is validated + // only as an opaque object); the import must degrade gracefully, not throw. + const malformed = serializedWorkflow({ + id: 'GORGONZOLA', + name: 'Malformed caller ids', + settings: { callerPolicy: 'workflowsFromAList', callerIds: 42 }, + }); + + const result = await importPackage({ + user: owner, + packageBuffer: await buildSubWorkflowPackage([malformed]), + }); + + const imported = await findImported(result, 'GORGONZOLA'); + expect(imported.name).toBe('Malformed caller ids'); + }); +}); diff --git a/packages/cli/src/modules/n8n-packages/__tests__/import-workflow-with-tags.integration.test.ts b/packages/cli/src/modules/n8n-packages/__tests__/import-workflow-with-tags.integration.test.ts new file mode 100644 index 00000000000..5f0c8b49221 --- /dev/null +++ b/packages/cli/src/modules/n8n-packages/__tests__/import-workflow-with-tags.integration.test.ts @@ -0,0 +1,659 @@ +import { LicenseState } from '@n8n/backend-common'; +import { createTeamProject, createWorkflow, testDb, testModules } from '@n8n/backend-test-utils'; +import { GlobalConfig } from '@n8n/config'; +import type { User } from '@n8n/db'; +import { TagRepository, WorkflowRepository, WorkflowTagMappingRepository } from '@n8n/db'; +import { Container } from '@n8n/di'; + +import { ConflictError } from '@/errors/response-errors/conflict.error'; +import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; +import { assignTagToWorkflow, createTag, updateTag } from '@test-integration/db/tags'; +import { createOwner } from '@test-integration/db/users'; +import { LicenseMocker } from '@test-integration/license'; +import { initNodeTypes } from '@test-integration/utils'; + +import { N8nPackagesService } from '../n8n-packages.service'; +import type { ImportPackageRequest } from '../n8n-packages.types'; +import { buildEntityPackageBuffer, serializedWorkflow } from './fixtures/package-fixtures'; +import { streamToBuffer } from './utils/tar-support'; + +let service: N8nPackagesService; +let tagRepository: TagRepository; +let mappingRepository: WorkflowTagMappingRepository; +let workflowRepository: WorkflowRepository; + +const licenseMocker = new LicenseMocker(); + +beforeAll(async () => { + await testModules.loadModules(['n8n-packages']); + await testDb.init(); + await initNodeTypes(); + licenseMocker.mockLicenseState(Container.get(LicenseState)); + service = Container.get(N8nPackagesService); + tagRepository = Container.get(TagRepository); + mappingRepository = Container.get(WorkflowTagMappingRepository); + workflowRepository = Container.get(WorkflowRepository); +}); + +afterAll(async () => { + await testDb.terminate(); +}); + +beforeEach(async () => { + await testDb.truncate([ + 'WorkflowTagMapping', + 'TagEntity', + 'WorkflowEntity', + 'SharedWorkflow', + 'ProjectRelation', + 'Project', + ]); +}); + +type ImportParams = { user: User; projectId?: string; packageBuffer: Buffer } & Partial< + Omit +>; + +async function importPackage(params: ImportParams) { + return await service.importPackage({ + credentialMatchingMode: 'id-only', + credentialMissingMode: 'must-preexist', + workflowConflictPolicy: 'fail', + workflowPublishingPolicy: 'preserve-published-state', + workflowIdPolicy: 'new', + folderConflictPolicy: 'merge', + dataTableMatchingMode: 'by-id', + dataTableMissingMode: 'create', + dataTableSchemaConflictPolicy: 'keep-existing', + variableMissingMode: 'do-nothing', + missingNodeTypeMode: 'fail', + tagMissingMode: 'create', + tagConflictPolicy: 'skip', + ...params, + }); +} + +async function exportWorkflowPackage( + user: User, + workflowIds: string[], + includeTags = true, +): Promise { + const { stream } = await service.exportPackage({ user, workflowIds, includeTags }); + return await streamToBuffer(stream); +} + +/** A workflow tagged with `tagName` in a fresh source project, packaged for import. */ +async function taggedWorkflowPackage(owner: User, tagName: string) { + const sourceProject = await createTeamProject('Source', owner); + const workflow = await createWorkflow({ name: 'Tagged workflow' }, sourceProject); + const tag = await createTag({ name: tagName }, workflow); + const packageBuffer = await exportWorkflowPackage(owner, [workflow.id]); + return { workflow, tag, packageBuffer }; +} + +async function tagIdsOf(workflowId: string): Promise { + const mappings = await mappingRepository.find({ where: { workflowId } }); + return mappings.map(({ tagId }) => tagId).sort(); +} + +describe('workflow package import — with tags', () => { + let owner: User; + + beforeEach(async () => { + owner = await createOwner(); + }); + + describe('create missing mode (default)', () => { + it('creates an absent shared tag once with its source id and name, attaching re-id’d workflows', async () => { + const sourceProject = await createTeamProject('Source', owner); + const wfA = await createWorkflow({ name: 'Workflow A' }, sourceProject); + const wfB = await createWorkflow({ name: 'Workflow B' }, sourceProject); + const tag = await createTag({ name: 'shared' }); + await assignTagToWorkflow(tag, wfA); + await assignTagToWorkflow(tag, wfB); + const packageBuffer = await exportWorkflowPackage(owner, [wfA.id, wfB.id]); + // Absent on the target: the import must recreate it under the source id. + await tagRepository.delete(tag.id); + + const targetProject = await createTeamProject('Target', owner); + const result = await importPackage({ + user: owner, + projectId: targetProject.id, + packageBuffer, + }); + + expect(result.tags).toEqual({ matched: [], created: ['shared'], renamed: [], skipped: [] }); + expect(await tagRepository.find()).toEqual([ + expect.objectContaining({ id: tag.id, name: 'shared' }), + ]); + for (const imported of result.workflows) { + expect(imported.localId).not.toBe(imported.sourceWorkflowId); + expect(await tagIdsOf(imported.localId)).toEqual([tag.id]); + } + }); + + it('attaches without creating when a tag with the same id and name exists', async () => { + const { tag, packageBuffer } = await taggedWorkflowPackage(owner, 'prod'); + const targetProject = await createTeamProject('Target', owner); + + const result = await importPackage({ + user: owner, + projectId: targetProject.id, + packageBuffer, + }); + + expect(result.tags).toEqual({ matched: ['prod'], created: [], renamed: [], skipped: [] }); + expect(await tagRepository.count()).toBe(1); + expect(await tagIdsOf(result.workflows[0].localId)).toEqual([tag.id]); + }); + + it('re-imports idempotently under new-version: nothing created or renamed twice', async () => { + const { tag, packageBuffer } = await taggedWorkflowPackage(owner, 'prod'); + await tagRepository.delete(tag.id); + const targetProject = await createTeamProject('Target', owner); + + const first = await importPackage({ + user: owner, + projectId: targetProject.id, + packageBuffer, + workflowConflictPolicy: 'new-version', + }); + const second = await importPackage({ + user: owner, + projectId: targetProject.id, + packageBuffer, + workflowConflictPolicy: 'new-version', + }); + + expect(first.tags.created).toEqual(['prod']); + expect(second.tags).toEqual({ matched: ['prod'], created: [], renamed: [], skipped: [] }); + expect(second.workflows[0].localId).toBe(first.workflows[0].localId); + expect(await tagRepository.count()).toBe(1); + expect(await tagIdsOf(first.workflows[0].localId)).toEqual([tag.id]); + }); + + it('rejects an import that would create a tag when the API key lacks tag:create', async () => { + const { tag, packageBuffer } = await taggedWorkflowPackage(owner, 'prod'); + await tagRepository.delete(tag.id); + const targetProject = await createTeamProject('Target', owner); + + await expect( + importPackage({ + user: owner, + projectId: targetProject.id, + packageBuffer, + apiKeyScopes: ['workflow:import'], + }), + ).rejects.toBeInstanceOf(ForbiddenError); + expect(await tagRepository.count()).toBe(0); + }); + }); + + describe('do-nothing missing mode', () => { + it('imports the workflow without the missing tag and creates nothing', async () => { + const { tag, packageBuffer } = await taggedWorkflowPackage(owner, 'prod'); + await tagRepository.delete(tag.id); + const targetProject = await createTeamProject('Target', owner); + + const result = await importPackage({ + user: owner, + projectId: targetProject.id, + packageBuffer, + tagMissingMode: 'do-nothing', + }); + + expect(result.workflows[0].status).toBe('created'); + expect(result.tags).toEqual({ matched: [], created: [], renamed: [], skipped: ['prod'] }); + expect(await tagRepository.count()).toBe(0); + expect(await tagIdsOf(result.workflows[0].localId)).toEqual([]); + }); + }); + + describe('rename drift (target tag with the same id carries a different name)', () => { + it('blocks under fail and writes nothing, leaving the drifted tag name alone', async () => { + const { workflow, tag, packageBuffer } = await taggedWorkflowPackage(owner, 'prod'); + await updateTag(tag, { name: 'production' }); + const targetProject = await createTeamProject('Target', owner); + const workflowsBefore = await workflowRepository.count(); + const tagsBefore = await tagRepository.count(); + + await expect( + importPackage({ + user: owner, + projectId: targetProject.id, + packageBuffer, + tagConflictPolicy: 'fail', + }), + ).rejects.toMatchObject({ + message: /Import blocked/, + meta: { + issues: [ + expect.objectContaining({ + type: 'tag-unresolved', + kind: 'rename-drift', + sourceId: tag.id, + name: 'prod', + existingName: 'production', + usedByWorkflows: [workflow.id], + }), + ], + }, + }); + + expect(await workflowRepository.count()).toBe(workflowsBefore); + expect(await tagRepository.count()).toBe(tagsBefore); + expect((await tagRepository.findOneByOrFail({ id: tag.id })).name).toBe('production'); + }); + + it('renames the target tag to the package name under rename and attaches it', async () => { + const { tag, packageBuffer } = await taggedWorkflowPackage(owner, 'prod'); + await updateTag(tag, { name: 'production' }); + const targetProject = await createTeamProject('Target', owner); + + const result = await importPackage({ + user: owner, + projectId: targetProject.id, + packageBuffer, + tagConflictPolicy: 'rename', + }); + + expect(result.tags).toEqual({ matched: [], created: [], renamed: ['prod'], skipped: [] }); + expect((await tagRepository.findOneByOrFail({ id: tag.id })).name).toBe('prod'); + expect(await tagIdsOf(result.workflows[0].localId)).toEqual([tag.id]); + }); + + it('blocks a rename whose wanted name is held by another tag', async () => { + const { tag, packageBuffer } = await taggedWorkflowPackage(owner, 'prod'); + await updateTag(tag, { name: 'production' }); + const holder = await createTag({ name: 'prod' }); + const targetProject = await createTeamProject('Target', owner); + + await expect( + importPackage({ + user: owner, + projectId: targetProject.id, + packageBuffer, + tagConflictPolicy: 'rename', + }), + ).rejects.toMatchObject({ + meta: { + issues: [ + expect.objectContaining({ + type: 'tag-unresolved', + kind: 'rename-drift', + sourceId: tag.id, + existingTagId: holder.id, + }), + ], + }, + }); + }); + + it('gates a rename-policy import on the tag:update API key scope', async () => { + const { tag, packageBuffer } = await taggedWorkflowPackage(owner, 'prod'); + await updateTag(tag, { name: 'production' }); + const targetProject = await createTeamProject('Target', owner); + + await expect( + importPackage({ + user: owner, + projectId: targetProject.id, + packageBuffer, + tagConflictPolicy: 'rename', + apiKeyScopes: ['workflow:import', 'tag:create'], + }), + ).rejects.toBeInstanceOf(ForbiddenError); + + const result = await importPackage({ + user: owner, + projectId: targetProject.id, + packageBuffer, + tagConflictPolicy: 'rename', + apiKeyScopes: ['workflow:import', 'tag:create', 'tag:update'], + }); + + expect(result.tags.renamed).toEqual(['prod']); + expect((await tagRepository.findOneByOrFail({ id: tag.id })).name).toBe('prod'); + }); + }); + + describe('name collision (name taken by a different tag, e.g. tag created manually on the target)', () => { + it('blocks under fail with a 409, deleting and altering nothing', async () => { + const { tag, packageBuffer } = await taggedWorkflowPackage(owner, 'prod'); + await tagRepository.delete(tag.id); + const holder = await createTag({ name: 'prod' }); + const targetProject = await createTeamProject('Target', owner); + const workflowsBefore = await workflowRepository.count(); + + let caught: unknown; + await importPackage({ + user: owner, + projectId: targetProject.id, + packageBuffer, + tagConflictPolicy: 'fail', + }).catch((error: unknown) => (caught = error)); + + expect(caught).toBeInstanceOf(ConflictError); + expect(caught).toMatchObject({ + meta: { + issues: [ + expect.objectContaining({ + type: 'tag-unresolved', + kind: 'name-collision', + sourceId: tag.id, + name: 'prod', + existingTagId: holder.id, + }), + ], + }, + }); + expect(await workflowRepository.count()).toBe(workflowsBefore); + // The colliding target tag is never deleted or renamed to make room. + expect(await tagRepository.find()).toEqual([ + expect.objectContaining({ id: holder.id, name: 'prod' }), + ]); + }); + + it('treats a case-variant target name as free and creates without conflict', async () => { + const { tag, packageBuffer } = await taggedWorkflowPackage(owner, 'prod'); + await tagRepository.delete(tag.id); + await createTag({ name: 'Prod' }); + const targetProject = await createTeamProject('Target', owner); + + const result = await importPackage({ + user: owner, + projectId: targetProject.id, + packageBuffer, + tagConflictPolicy: 'fail', + }); + + expect(result.tags).toEqual({ matched: [], created: ['prod'], renamed: [], skipped: [] }); + expect(await tagIdsOf(result.workflows[0].localId)).toEqual([tag.id]); + }); + + it('blocks two package tags whose trimmed names collide, creating nothing', async () => { + const workflow = serializedWorkflow({ + id: 'wf-0', + name: 'Workflow 0', + tagIds: ['tag-a', 'tag-b'], + }); + const packageBuffer = await buildEntityPackageBuffer({ + workflows: [{ target: 'workflows/wf-0', workflow }], + manifestExtras: { + requirements: { + tags: [ + { id: 'tag-a', name: 'prod', usedByWorkflows: ['wf-0'] }, + { id: 'tag-b', name: ' prod ', usedByWorkflows: ['wf-0'] }, + ], + }, + }, + }); + const targetProject = await createTeamProject('Target', owner); + + let caught: unknown; + await importPackage({ user: owner, projectId: targetProject.id, packageBuffer }).catch( + (error: unknown) => (caught = error), + ); + + expect(caught).toBeInstanceOf(ConflictError); + expect(caught).toMatchObject({ + meta: { + issues: [ + expect.objectContaining({ + type: 'tag-unresolved', + kind: 'name-collision', + sourceId: 'tag-a', + name: 'prod', + usedByWorkflows: ['wf-0'], + }), + expect.objectContaining({ + type: 'tag-unresolved', + kind: 'name-collision', + sourceId: 'tag-b', + name: 'prod', + usedByWorkflows: ['wf-0'], + }), + ], + }, + }); + expect(await tagRepository.count()).toBe(0); + }); + + it('drops the tag under the default skip policy and imports the workflow without it', async () => { + const { tag, packageBuffer } = await taggedWorkflowPackage(owner, 'prod'); + await tagRepository.delete(tag.id); + const holder = await createTag({ name: 'prod' }); + const targetProject = await createTeamProject('Target', owner); + + const result = await importPackage({ + user: owner, + projectId: targetProject.id, + packageBuffer, + }); + + expect(result.workflows[0].status).toBe('created'); + expect(result.tags).toEqual({ matched: [], created: [], renamed: [], skipped: ['prod'] }); + expect(await tagIdsOf(result.workflows[0].localId)).toEqual([]); + expect(await tagRepository.find()).toEqual([ + expect.objectContaining({ id: holder.id, name: 'prod' }), + ]); + }); + }); + + describe('update semantics', () => { + it('overwrites taggings to exactly the package set on a new-version re-import', async () => { + const { tag, packageBuffer } = await taggedWorkflowPackage(owner, 'prod'); + const targetProject = await createTeamProject('Target', owner); + const first = await importPackage({ + user: owner, + projectId: targetProject.id, + packageBuffer, + workflowConflictPolicy: 'new-version', + }); + const imported = await workflowRepository.findOneByOrFail({ + id: first.workflows[0].localId, + }); + const extra = await createTag({ name: 'extra' }); + await assignTagToWorkflow(extra, imported); + + await importPackage({ + user: owner, + projectId: targetProject.id, + packageBuffer, + workflowConflictPolicy: 'new-version', + }); + + expect(await tagIdsOf(imported.id)).toEqual([tag.id]); + }); + + it('strips all taggings when the package carries an empty tagIds set', async () => { + const sourceProject = await createTeamProject('Source', owner); + const workflow = await createWorkflow({ name: 'Untagged workflow' }, sourceProject); + const packageBuffer = await exportWorkflowPackage(owner, [workflow.id]); + const targetProject = await createTeamProject('Target', owner); + const first = await importPackage({ + user: owner, + projectId: targetProject.id, + packageBuffer, + workflowConflictPolicy: 'new-version', + }); + const imported = await workflowRepository.findOneByOrFail({ + id: first.workflows[0].localId, + }); + const extra = await createTag({ name: 'extra' }); + await assignTagToWorkflow(extra, imported); + + await importPackage({ + user: owner, + projectId: targetProject.id, + packageBuffer, + workflowConflictPolicy: 'new-version', + }); + + expect(await tagIdsOf(imported.id)).toEqual([]); + }); + + it('attaches a tag listed twice in the package tagIds exactly once on the update path', async () => { + const workflow = serializedWorkflow({ + id: 'wf-0', + name: 'Workflow 0', + tagIds: ['tag-1', 'tag-1'], + }); + const packageBuffer = await buildEntityPackageBuffer({ + workflows: [{ target: 'workflows/wf-0', workflow }], + manifestExtras: { + requirements: { tags: [{ id: 'tag-1', name: 'prod', usedByWorkflows: ['wf-0'] }] }, + }, + }); + const targetProject = await createTeamProject('Target', owner); + + const first = await importPackage({ + user: owner, + projectId: targetProject.id, + packageBuffer, + workflowConflictPolicy: 'new-version', + }); + const second = await importPackage({ + user: owner, + projectId: targetProject.id, + packageBuffer, + workflowConflictPolicy: 'new-version', + }); + + expect(second.workflows[0].status).toBe('updated'); + expect(second.workflows[0].localId).toBe(first.workflows[0].localId); + expect(await tagIdsOf(second.workflows[0].localId)).toEqual(['tag-1']); + }); + + it('leaves target taggings untouched when the package was exported without tags', async () => { + const sourceProject = await createTeamProject('Source', owner); + const workflow = await createWorkflow({ name: 'Tagged workflow' }, sourceProject); + await createTag({ name: 'prod' }, workflow); + const packageBuffer = await exportWorkflowPackage(owner, [workflow.id], false); + const targetProject = await createTeamProject('Target', owner); + const first = await importPackage({ + user: owner, + projectId: targetProject.id, + packageBuffer, + workflowConflictPolicy: 'new-version', + }); + const imported = await workflowRepository.findOneByOrFail({ + id: first.workflows[0].localId, + }); + const extra = await createTag({ name: 'extra' }); + await assignTagToWorkflow(extra, imported); + + await importPackage({ + user: owner, + projectId: targetProject.id, + packageBuffer, + workflowConflictPolicy: 'new-version', + }); + + expect(await tagIdsOf(imported.id)).toEqual([extra.id]); + }); + }); + + it('never gates or creates for a tag referenced only by skipped workflows', async () => { + const { tag, packageBuffer } = await taggedWorkflowPackage(owner, 'prod'); + const targetProject = await createTeamProject('Target', owner); + await importPackage({ user: owner, projectId: targetProject.id, packageBuffer }); + // A second import would now gate on this drift — unless its workflow is skipped. + await updateTag(tag, { name: 'production' }); + const tagsBefore = await tagRepository.count(); + + const result = await importPackage({ + user: owner, + projectId: targetProject.id, + packageBuffer, + workflowConflictPolicy: 'skip', + tagConflictPolicy: 'fail', + }); + + expect(result.workflows[0].status).toBe('skipped'); + expect(result.tags).toEqual({ matched: [], created: [], renamed: [], skipped: [] }); + expect(await tagRepository.count()).toBe(tagsBefore); + }); + + it('does not require tag scopes when every tag consumer is skipped', async () => { + const { tag, packageBuffer } = await taggedWorkflowPackage(owner, 'prod'); + const targetProject = await createTeamProject('Target', owner); + await importPackage({ user: owner, projectId: targetProject.id, packageBuffer }); + // The tag is now absent, so a create would be planned — unless its only consumer is skipped. + await tagRepository.delete(tag.id); + + const result = await importPackage({ + user: owner, + projectId: targetProject.id, + packageBuffer, + workflowConflictPolicy: 'skip', + apiKeyScopes: ['workflow:import'], + }); + + expect(result.workflows[0].status).toBe('skipped'); + expect(await tagRepository.count()).toBe(0); + }); + + it('silently ignores tags when workflow tags are disabled on the instance', async () => { + const { tag, packageBuffer } = await taggedWorkflowPackage(owner, 'prod'); + // A drift that would gate under fail — disabled tags must not even look at it. + await updateTag(tag, { name: 'production' }); + const targetProject = await createTeamProject('Target', owner); + const mappingsBefore = await mappingRepository.count(); + + const globalConfig = Container.get(GlobalConfig); + globalConfig.tags.disabled = true; + try { + const result = await importPackage({ + user: owner, + projectId: targetProject.id, + packageBuffer, + tagConflictPolicy: 'fail', + }); + + expect(result.workflows[0].status).toBe('created'); + expect(result.tags).toEqual({ matched: [], created: [], renamed: [], skipped: [] }); + expect((await tagRepository.findOneByOrFail({ id: tag.id })).name).toBe('production'); + expect(await mappingRepository.count()).toBe(mappingsBefore); + } finally { + globalConfig.tags.disabled = false; + } + }); + + it('does not require tag scopes when workflow tags are disabled', async () => { + const { packageBuffer } = await taggedWorkflowPackage(owner, 'prod'); + const targetProject = await createTeamProject('Target', owner); + + const globalConfig = Container.get(GlobalConfig); + globalConfig.tags.disabled = true; + try { + const result = await importPackage({ + user: owner, + projectId: targetProject.id, + packageBuffer, + apiKeyScopes: ['workflow:import'], + }); + + expect(result.workflows[0].status).toBe('created'); + expect(result.tags).toEqual({ matched: [], created: [], renamed: [], skipped: [] }); + } finally { + globalConfig.tags.disabled = false; + } + }); + + it('rejects a package whose workflow references a tag missing from the requirements', async () => { + const workflow = serializedWorkflow({ + id: 'wf-0', + name: 'Workflow 0', + tagIds: ['ghost-tag'], + }); + const packageBuffer = await buildEntityPackageBuffer({ + workflows: [{ target: 'workflows/wf-0', workflow }], + }); + const targetProject = await createTeamProject('Target', owner); + + await expect( + importPackage({ user: owner, projectId: targetProject.id, packageBuffer }), + ).rejects.toThrowError(/missing from the package's tag requirements/); + }); +}); diff --git a/packages/cli/src/modules/n8n-packages/__tests__/import-workflow-with-variables.integration.test.ts b/packages/cli/src/modules/n8n-packages/__tests__/import-workflow-with-variables.integration.test.ts index 89baa6738d1..55bdeec4c1a 100644 --- a/packages/cli/src/modules/n8n-packages/__tests__/import-workflow-with-variables.integration.test.ts +++ b/packages/cli/src/modules/n8n-packages/__tests__/import-workflow-with-variables.integration.test.ts @@ -74,6 +74,8 @@ async function importPackage(params: ImportParams) { variableMissingMode: 'do-nothing', variableParentPolicy: 'project', missingNodeTypeMode: 'fail', + tagMissingMode: 'create', + tagConflictPolicy: 'skip', ...params, }); } diff --git a/packages/cli/src/modules/n8n-packages/engine/__tests__/import-blocked.error.test.ts b/packages/cli/src/modules/n8n-packages/engine/__tests__/import-blocked.error.test.ts index 18801d70a4d..08077b785c5 100644 --- a/packages/cli/src/modules/n8n-packages/engine/__tests__/import-blocked.error.test.ts +++ b/packages/cli/src/modules/n8n-packages/engine/__tests__/import-blocked.error.test.ts @@ -26,6 +26,16 @@ const variableUnresolved: BlockingIssue = { usedByWorkflows: ['w1'], }; +const tagUnresolved = ( + kind: 'rename-drift' | 'name-collision' | 'invalid-name', +): BlockingIssue => ({ + type: 'tag-unresolved', + kind, + sourceId: 't1', + name: 'prod', + usedByWorkflows: ['w1'], +}); + describe('toImportBlockedError', () => { it('maps a folder-conflict to 409 Conflict', () => { const error = toImportBlockedError([folderConflict]); @@ -46,4 +56,14 @@ describe('toImportBlockedError', () => { const error = toImportBlockedError([credentialUnresolved, folderConflict]); expect(error).toBeInstanceOf(ConflictError); }); + + it.each(['rename-drift', 'name-collision'] as const)('maps a tag %s to 409 Conflict', (kind) => { + expect(toImportBlockedError([tagUnresolved(kind)])).toBeInstanceOf(ConflictError); + }); + + it('maps an invalid tag name to 422', () => { + expect(toImportBlockedError([tagUnresolved('invalid-name')])).toBeInstanceOf( + UnprocessableRequestError, + ); + }); }); diff --git a/packages/cli/src/modules/n8n-packages/engine/__tests__/import-telemetry.test.ts b/packages/cli/src/modules/n8n-packages/engine/__tests__/import-telemetry.test.ts index ca330bb8ae8..f3511d70bb0 100644 --- a/packages/cli/src/modules/n8n-packages/engine/__tests__/import-telemetry.test.ts +++ b/packages/cli/src/modules/n8n-packages/engine/__tests__/import-telemetry.test.ts @@ -6,23 +6,23 @@ import type { RelayEventMap } from '@/events/maps/relay.event-map'; import type { CredentialApplyResult } from '../../entities/credential/credential.types'; import type { DataTableImportRequest } from '../../entities/data-table/data-table.types'; -import type { WorkflowImportOutcome } from '../../entities/workflow/workflow-import.types'; +import type { PersistedWorkflowOutcome } from '../../entities/workflow/workflow-import.types'; import type { ImportContext, ImportPackageRequest } from '../../n8n-packages.types'; import type { PackageManifest } from '../../spec/manifest.schema'; import type { PackageCredentialRequirement } from '../../spec/requirements.schema'; -import type { ImportOrchestrationResult } from '../import-orchestrator'; +import type { ImportContentResult } from '../import-orchestrator'; import { emitPackageImportedEvent, type PackageImportScope } from '../import-telemetry'; const outcome = ( id: string, sourceWorkflowId: string, - status: WorkflowImportOutcome['status'], -): WorkflowImportOutcome => ({ - status, - sourceWorkflowId, - workflow: mock({ id }), - publishing: { state: 'unchanged' }, -}); + status: PersistedWorkflowOutcome['status'], +): PersistedWorkflowOutcome => { + const workflow = mock({ id }); + return status === 'skipped' + ? { status, sourceWorkflowId, workflow } + : { status, sourceWorkflowId, workflow, item: mock() }; +}; const requirement = (id: string): PackageCredentialRequirement => ({ id, @@ -34,7 +34,7 @@ const requirement = (id: string): PackageCredentialRequirement => ({ const scope = (input: { projectId: string; folderId?: string | null; - outcomes: WorkflowImportOutcome[]; + outcomes: PersistedWorkflowOutcome[]; credentialResult: CredentialApplyResult; requirements?: PackageCredentialRequirement[]; dataTable?: { matched: number; created: number; requirements: number }; @@ -45,6 +45,14 @@ const scope = (input: { created?: number; existing?: number; }; + /** Tag ids per resolution bucket; the same id may recur across scopes. */ + tags?: { + matched?: string[]; + created?: string[]; + renamed?: string[]; + skipped?: string[]; + requirementIds?: string[]; + }; }): PackageImportScope => { const context: ImportContext = { user: mock(), @@ -60,7 +68,9 @@ const scope = (input: { createdVariableCount, createdVariableCount + (vars.existing ?? 0), ); - const imported: ImportOrchestrationResult = { + const tags = input.tags ?? {}; + const toTagRefs = (ids: string[] = []) => ids.map((id) => ({ id, name: `name-of-${id}` })); + const imported: ImportContentResult = { workflowOutcomes: input.outcomes, folderSummaries: [], bindings: { workflows: new Map(), credentials: new Map() }, @@ -79,6 +89,13 @@ const scope = (input: { skippedExisting: existingVariableNames, createdCount: createdVariableCount, }, + tagPlan: { + matched: toTagRefs(tags.matched), + creations: toTagRefs(tags.created), + renames: (tags.renamed ?? []).map((id) => ({ id, from: 'old', to: `name-of-${id}` })), + dropped: toTagRefs(tags.skipped), + failures: [], + }, }; return { context, @@ -96,6 +113,15 @@ const scope = (input: { requirements: vars.requirements === 0 ? undefined : new Array(vars.requirements), missingMode: 'do-nothing', }, + tagRequest: { + requirements: (tags.requirementIds ?? []).map((id) => ({ + id, + name: `name-of-${id}`, + usedByWorkflows: ['ignored'], + })), + missingMode: 'create', + conflictPolicy: 'skip', + }, }; }; @@ -109,6 +135,8 @@ const request = mock({ variableMissingMode: 'create-stub', variableParentPolicy: 'global', missingNodeTypeMode: 'fail', + tagMissingMode: 'create', + tagConflictPolicy: 'rename', }); const manifest = mock({ sourceId: 'src-1', packageFormatVersion: '1' }); @@ -142,6 +170,7 @@ describe('emitPackageImportedEvent', () => { requirements: [requirement('credA')], dataTable: { matched: 1, created: 0, requirements: 1 }, variables: { matched: 1, missing: 0, requirements: 1 }, + tags: { matched: ['T1'], created: ['T2'], requirementIds: ['T1', 'T2'] }, }), scope({ projectId: 'P2', @@ -154,6 +183,13 @@ describe('emitPackageImportedEvent', () => { requirements: [requirement('credB')], dataTable: { matched: 0, created: 2, requirements: 2 }, variables: { matched: 0, missing: 2, requirements: 2, created: 2 }, + // T2 recurs from scope 1: tags are global, so it must count once. + tags: { + created: ['T2'], + renamed: ['T3'], + skipped: ['T4'], + requirementIds: ['T2', 'T3', 'T4'], + }, }), ], }); @@ -176,10 +212,14 @@ describe('emitPackageImportedEvent', () => { dataTables: { matched: 1, created: 2, requirements: 3 }, // scope 2's two missing requirements were created, so post-apply missing is 0. variables: { matched: 1, missing: 0, created: 2, requirements: 3 }, + // T2 and its requirement appear in both scopes but count once (unique tag ids). + tags: { matched: 1, created: 1, renamed: 1, skipped: 1, requirements: 4 }, }); expect(payload.packageSourceId).toBe('src-1'); expect(payload.options.variableMissingMode).toBe('create-stub'); expect(payload.options.variableParentPolicy).toBe('global'); + expect(payload.options.tagMissingMode).toBe('create'); + expect(payload.options.tagConflictPolicy).toBe('rename'); }); it('still counts a missing requirement the import left unfilled', () => { diff --git a/packages/cli/src/modules/n8n-packages/engine/import-blocked.error.ts b/packages/cli/src/modules/n8n-packages/engine/import-blocked.error.ts index f021912151e..47d02f6f5fc 100644 --- a/packages/cli/src/modules/n8n-packages/engine/import-blocked.error.ts +++ b/packages/cli/src/modules/n8n-packages/engine/import-blocked.error.ts @@ -18,7 +18,9 @@ export function toImportBlockedError( issue.type === 'workflow-folder-conflict' || issue.type === 'folder-conflict' || (issue.type === 'data-table-unresolved' && - (issue.kind === 'id-conflict' || issue.kind === 'name-conflict')), + (issue.kind === 'id-conflict' || issue.kind === 'name-conflict')) || + (issue.type === 'tag-unresolved' && + (issue.kind === 'rename-drift' || issue.kind === 'name-collision')), ) ) { return new ConflictError(message, undefined, { issues }); diff --git a/packages/cli/src/modules/n8n-packages/engine/import-gates.ts b/packages/cli/src/modules/n8n-packages/engine/import-gates.ts index f534817c30a..017117c7b98 100644 --- a/packages/cli/src/modules/n8n-packages/engine/import-gates.ts +++ b/packages/cli/src/modules/n8n-packages/engine/import-gates.ts @@ -3,6 +3,7 @@ import type { LicenseState } from '@n8n/backend-common'; import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; import { variableMissingModeCreates } from '../entities/variable/variable-missing-mode'; +import type { TagImportPlan } from '../entities/tag/tag.types'; import type { VariableMissingMode } from '../n8n-packages.types'; export function assertPackageImportApiKeyScopes( @@ -38,3 +39,21 @@ export function assertVariableCreationAllowed(options: { } assertPackageImportApiKeyScopes(apiKeyScopes, ['variable:create']); } + +/** + * Plan-derived, unlike the pre-plan variable/data-table gates: a tag must + * never block an import that would not write it (skipped consumers, disabled + * tags, dropped conflicts), so the assert looks at what the plans actually + * create or rename. + */ +export function assertTagWritesAllowed( + apiKeyScopes: string[] | undefined, + tagPlans: TagImportPlan[], +): void { + if (tagPlans.some((plan) => plan.creations.length > 0)) { + assertPackageImportApiKeyScopes(apiKeyScopes, ['tag:create']); + } + if (tagPlans.some((plan) => plan.renames.length > 0)) { + assertPackageImportApiKeyScopes(apiKeyScopes, ['tag:update']); + } +} diff --git a/packages/cli/src/modules/n8n-packages/engine/import-orchestrator.ts b/packages/cli/src/modules/n8n-packages/engine/import-orchestrator.ts index 291c110fe12..171f92a62b5 100644 --- a/packages/cli/src/modules/n8n-packages/engine/import-orchestrator.ts +++ b/packages/cli/src/modules/n8n-packages/engine/import-orchestrator.ts @@ -21,6 +21,9 @@ import type { PreparedFolder, } from '../entities/folder/folder-import.types'; import { FolderImporter } from '../entities/folder/folder-importer'; +import { TagImporter } from '../entities/tag/tag-importer'; +import { droppedTagIds } from '../entities/tag/tag.types'; +import type { TagImportPlan, TagImportRequest } from '../entities/tag/tag.types'; import { VariableImporter } from '../entities/variable/variable-importer'; import type { VariableApplyResult, @@ -34,8 +37,8 @@ import { type MissingNodeTypeRequirement, } from '../entities/workflow/missing-node-type-mode'; import type { + PersistedWorkflowOutcome, PreparedWorkflow, - WorkflowImportOutcome, WorkflowImportPlan, } from '../entities/workflow/workflow-import.types'; import { WorkflowImporter } from '../entities/workflow/workflow-importer'; @@ -44,6 +47,7 @@ import type { WorkflowPublishingBlockedReason } from '../entities/workflow/workf import { createBindings } from '../n8n-packages.types'; import type { BlockingIssue, + ImportBindingMap, ImportContext, ImportedFolderSummary, ImportFolderProperties, @@ -52,6 +56,7 @@ import type { PackageImportBindings, } from '../n8n-packages.types'; import { toImportBlockedError } from './import-blocked.error'; +import type { PackageWorkflowRequirement } from '../spec/requirements.schema'; export interface ImportOrchestrationInput { context: ImportContext; @@ -60,19 +65,27 @@ export interface ImportOrchestrationInput { credentialRequest: CredentialBindingRequest; dataTableRequest: DataTableImportRequest; variableRequest: VariableImportRequest; + tagRequest: TagImportRequest; options: ImportWorkflowProperties & ImportFolderProperties; /** The target project does not exist yet and will be created by this import (project packages). */ projectPendingCreation?: boolean; + /** Sub-workflow dependency graph from the manifest, used to order the import. */ + subWorkflowRequirements?: PackageWorkflowRequirement[]; } -export interface ImportOrchestrationResult { - workflowOutcomes: WorkflowImportOutcome[]; +/** + * Everything one scope's {@link ImportOrchestrator.apply} wrote, before the package-wide publish + * sweep runs. Telemetry consumes this shape directly — it only reads statuses and ids. + */ +export interface ImportContentResult { + workflowOutcomes: PersistedWorkflowOutcome[]; folderSummaries: ImportedFolderSummary[]; bindings: PackageImportBindings; credentialResult: CredentialApplyResult; dataTablePlan: DataTableImportPlan; variablePlan: VariableImportPlan; variableResult: VariableApplyResult; + tagPlan: TagImportPlan; } export interface ImportPlan { @@ -83,6 +96,7 @@ export interface ImportPlan { folderPlan: FolderImportPlan; dataTablePlan: DataTableImportPlan; variablePlan: VariableImportPlan; + tagPlan: TagImportPlan; missingNodeTypes: MissingNodeTypeRequirement[]; blockingIssues: BlockingIssue[]; } @@ -97,6 +111,7 @@ export class ImportOrchestrator { private readonly credentialImporter: CredentialImporter, private readonly dataTableImporter: DataTableImporter, private readonly variableImporter: VariableImporter, + private readonly tagImporter: TagImporter, private readonly folderImporter: FolderImporter, private readonly workflowImporter: WorkflowImporter, private readonly workflowPublisher: WorkflowPublisher, @@ -122,6 +137,7 @@ export class ImportOrchestrator { credentialRequest, dataTableRequest, variableRequest, + tagRequest, options, } = input; @@ -138,6 +154,12 @@ export class ImportOrchestrator { projectPendingCreation: input.projectPendingCreation, }); const workflowPlan = await this.workflowImporter.plan(context, workflows, options); + // Tags plan after workflows: only tags referenced by non-skipped workflows gate or create. + const tagPlan = await this.tagImporter.plan( + context, + tagRequest, + workflowPlan.items.filter((item) => item.action !== 'skip'), + ); const folderContext = { ...context, folderConflictPolicy: options.folderConflictPolicy }; const folderPlan = await this.folderImporter.plan(folderContext, folders); @@ -155,6 +177,7 @@ export class ImportOrchestrator { dataTablePlan, variableRequest, variablePlan, + tagPlan, missingNodeTypes, missingNodeTypeMode: options.missingNodeTypeMode, }); @@ -167,12 +190,21 @@ export class ImportOrchestrator { folderPlan, dataTablePlan, variablePlan, + tagPlan, missingNodeTypes, blockingIssues, }; } - async apply(plan: ImportPlan): Promise { + /** + * Writes this scope's content. Workflows land unpublished: publishing needs every workflow in + * the package present first, so the caller runs {@link WorkflowPublisher.applyToPackage} once + * all scopes have been applied. + */ + async apply( + plan: ImportPlan, + seedWorkflowBindings?: ImportBindingMap, + ): Promise { const { input, folderContext, @@ -181,13 +213,16 @@ export class ImportOrchestrator { folderPlan, dataTablePlan, variablePlan, + tagPlan, } = plan; - const { context, credentialRequest, options } = input; + const { context, credentialRequest } = input; - // Variables go first: stub creation is the only apply step that can still fail after the - // blocking-issue gate (a near-quota race), and it depends on nothing below — applying it - // before any other write keeps a quota-raced workflow-package import from persisting anything. + // Variables and tags go first: their creations are the apply steps that can still fail + // after the blocking-issue gate (a near-quota or unique-index race), and they depend on + // nothing below — applying them before any other write keeps a raced import from + // persisting anything else. const variableResult = await this.variableImporter.apply(context, variablePlan); + await this.tagImporter.apply(context, tagPlan); const folderSummaries = await this.folderImporter.apply(folderContext, folderPlan); @@ -198,36 +233,43 @@ export class ImportOrchestrator { ); await this.dataTableImporter.apply(context, dataTablePlan); - const publishBlocked = new Map(); + + // Which workflows the publish phase must leave inactive. Known only now, because it depends + // on which credentials actually ended up stubbed. + const blockedFromPublish = new Map(); for (const sourceWorkflowId of workflowsBlockedFromPublish( credentialRequest.requirements, new Set(credentialResult.stubbed), )) { - publishBlocked.set(sourceWorkflowId, 'stub-credential'); + blockedFromPublish.set(sourceWorkflowId, 'stub-credential'); } // A workflow blocked for both reasons reports missing-node-type: it physically can't run. for (const sourceWorkflowId of workflowsWithMissingNodeTypes(plan.missingNodeTypes)) { - publishBlocked.set(sourceWorkflowId, 'missing-node-type'); + blockedFromPublish.set(sourceWorkflowId, 'missing-node-type'); } const { outcomes, bindings } = await this.workflowImporter.apply( - { - ...context, - publishingPolicy: options.workflowPublishingPolicy, - publishBlocked, - }, + { ...context, droppedTagIds: droppedTagIds(tagPlan) }, workflowPlan, - createBindings({ credentials: credentialResult.bindings }), + createBindings({ + credentials: credentialResult.bindings, + // Seeds cross-scope workflow ids so a project package can resolve + // sub-workflow references that point into another project. + ...(seedWorkflowBindings ? { workflows: seedWorkflowBindings } : {}), + }), ); return { - workflowOutcomes: outcomes, + workflowOutcomes: outcomes.map((outcome) => + withBlockedFromPublish(outcome, blockedFromPublish.get(outcome.sourceWorkflowId)), + ), folderSummaries, bindings, credentialResult, dataTablePlan, variablePlan, variableResult, + tagPlan, }; } @@ -239,6 +281,7 @@ export class ImportOrchestrator { dataTablePlan, variableRequest, variablePlan, + tagPlan, missingNodeTypes, missingNodeTypeMode, }: { @@ -249,6 +292,7 @@ export class ImportOrchestrator { dataTablePlan: DataTableImportPlan; variableRequest: VariableImportRequest; variablePlan: VariableImportPlan; + tagPlan: TagImportPlan; missingNodeTypes: MissingNodeTypeRequirement[]; missingNodeTypeMode: MissingNodeTypeMode; }): BlockingIssue[] { @@ -268,6 +312,7 @@ export class ImportOrchestrator { ...dataTablePlan.failures.map( (failure): BlockingIssue => ({ type: 'data-table-unresolved', ...failure }), ), + ...tagPlan.failures.map((failure): BlockingIssue => ({ type: 'tag-unresolved', ...failure })), ...this.credentialImporter .blockingFailures(credentialRequest, credentialPlan) .map(toCredentialBlockingIssue), @@ -286,6 +331,14 @@ export class ImportOrchestrator { } } +function withBlockedFromPublish( + outcome: PersistedWorkflowOutcome, + blockedFromPublish: WorkflowPublishingBlockedReason | undefined, +): PersistedWorkflowOutcome { + if (outcome.status === 'skipped' || !blockedFromPublish) return outcome; + return { ...outcome, blockedFromPublish }; +} + function toCredentialBlockingIssue(failure: CredentialResolutionFailure): BlockingIssue { const { kind, sourceId, targetId, expectedType, actualType, usedByWorkflows } = failure; return { diff --git a/packages/cli/src/modules/n8n-packages/engine/import-result.ts b/packages/cli/src/modules/n8n-packages/engine/import-result.ts index 161638174bf..3dc80d1bc4a 100644 --- a/packages/cli/src/modules/n8n-packages/engine/import-result.ts +++ b/packages/cli/src/modules/n8n-packages/engine/import-result.ts @@ -1,8 +1,10 @@ +import type { TagImportPlan } from '../entities/tag/tag.types'; import type { VariableApplyResult, VariableImportPlan } from '../entities/variable/variable.types'; import type { + PersistedWorkflowOutcome, PreparedWorkflow, - WorkflowImportOutcome, } from '../entities/workflow/workflow-import.types'; +import type { PackagePublishingResults } from '../entities/workflow/workflow-publisher'; import { serializeBindings } from '../n8n-packages.types'; import type { ImportBindingMap, @@ -12,6 +14,7 @@ import type { ImportedWorkflowSummary, ImportPackageSummary, ImportResult, + ImportTagSummary, ImportVariableSummary, PackageImportBindings, } from '../n8n-packages.types'; @@ -26,20 +29,32 @@ export function toPackageSummary(manifest: PackageManifest): ImportPackageSummar }; } +/** + * One row per imported workflow, folding in what the publish phase decided for it. Ordered as the + * workflows were written, not as they were published (dependencies first), which is an + * implementation detail. Publishing reloads the workflow, so its copy wins where it has one; a + * workflow the phase never acted on — a skip — keeps the state it had and reports `unchanged`. + */ export function toImportedWorkflowSummaries( - outcomes: WorkflowImportOutcome[], + outcomes: PersistedWorkflowOutcome[], projectId: string, + published: PackagePublishingResults, ): ImportedWorkflowSummary[] { - return outcomes.map(({ workflow, sourceWorkflowId, status, publishing }) => ({ - sourceWorkflowId, - localId: workflow.id, - name: workflow.name, - projectId, - parentFolderId: workflow.parentFolder?.id ?? null, - activeVersionId: workflow.activeVersionId ?? null, - publishing, - status, - })); + return outcomes.map(({ workflow, sourceWorkflowId, status }) => { + const result = published.get(sourceWorkflowId); + const current = result?.workflow ?? workflow; + + return { + sourceWorkflowId, + localId: current.id, + name: current.name, + projectId, + parentFolderId: current.parentFolder?.id ?? null, + activeVersionId: current.activeVersionId ?? null, + publishing: result?.publishing ?? { state: 'unchanged' }, + status, + }; + }); } export function buildImportResult(input: { @@ -50,6 +65,7 @@ export function buildImportResult(input: { bindings: PackageImportBindings; credentials: ImportCredentialSummary; variables: ImportVariableSummary; + tags: ImportTagSummary; }): ImportResult { return { package: input.package, @@ -59,6 +75,7 @@ export function buildImportResult(input: { bindings: serializeBindings(input.bindings), credentials: input.credentials, variables: input.variables, + tags: input.tags, }; } @@ -97,6 +114,26 @@ export function toVariableSummary( }); } +/** Tag names (renames report the post-rename name). */ +export function toTagSummary(plan: TagImportPlan): ImportTagSummary { + return { + matched: plan.matched.map(({ name }) => name), + created: plan.creations.map(({ name }) => name), + renamed: plan.renames.map(({ to }) => to), + skipped: plan.dropped.map(({ name }) => name), + }; +} + +/** Set-unions per-scope tag summaries: one global tag planned by several scopes reports once. */ +export function unionTagSummaries(summaries: ImportTagSummary[]): ImportTagSummary { + return { + matched: [...new Set(summaries.flatMap(({ matched }) => matched))], + created: [...new Set(summaries.flatMap(({ created }) => created))], + renamed: [...new Set(summaries.flatMap(({ renamed }) => renamed))], + skipped: [...new Set(summaries.flatMap(({ skipped }) => skipped))], + }; +} + /** * Keeps only the requirements used by the imported workflows, trimming `usedByWorkflows` to match. */ diff --git a/packages/cli/src/modules/n8n-packages/engine/import-telemetry.ts b/packages/cli/src/modules/n8n-packages/engine/import-telemetry.ts index 9117562705c..f733a0e7de1 100644 --- a/packages/cli/src/modules/n8n-packages/engine/import-telemetry.ts +++ b/packages/cli/src/modules/n8n-packages/engine/import-telemetry.ts @@ -2,20 +2,23 @@ import type { EventService } from '@/events/event.service'; import type { CredentialBindingRequest } from '../entities/credential/credential.types'; import type { DataTableImportRequest } from '../entities/data-table/data-table.types'; +import type { TagImportPlan, TagImportRequest } from '../entities/tag/tag.types'; import type { VariableImportRequest } from '../entities/variable/variable.types'; -import type { WorkflowImportOutcome } from '../entities/workflow/workflow-import.types'; +import type { PersistedWorkflowOutcome } from '../entities/workflow/workflow-import.types'; import { VariableParentPolicy } from '../n8n-packages.types'; import type { ImportContext, ImportPackageRequest } from '../n8n-packages.types'; -import type { ImportOrchestrationResult } from './import-orchestrator'; +import type { ImportContentResult } from './import-orchestrator'; import { reconcileVariableSummary } from './import-result'; import type { PackageManifest } from '../spec/manifest.schema'; export interface PackageImportScope { context: ImportContext; - imported: ImportOrchestrationResult; + /** The apply phase's output — telemetry counts what was written, not what was published. */ + imported: ImportContentResult; credentialRequest: CredentialBindingRequest; dataTableRequest: DataTableImportRequest; variableRequest: VariableImportRequest; + tagRequest: TagImportRequest; } export function emitPackageImportedEvent( @@ -31,7 +34,7 @@ export function emitPackageImportedEvent( const workflowOutcomes = scopes.flatMap(({ imported }) => imported.workflowOutcomes); const credentialResults = scopes.map(({ imported }) => imported.credentialResult); const importedWorkflows = workflowOutcomes.filter(({ status }) => status !== 'skipped'); - const countByStatus = (status: WorkflowImportOutcome['status']) => + const countByStatus = (status: PersistedWorkflowOutcome['status']) => workflowOutcomes.filter((outcome) => outcome.status === status).length; const credentialRequirements = scopes.reduce( (total, { credentialRequest }) => total + (credentialRequest.requirements?.length ?? 0), @@ -75,6 +78,14 @@ export function emitPackageImportedEvent( 0, ); + // Tags are global, so several scopes may plan the same tag; count each id once. + const tagPlans = scopes.map(({ imported }) => imported.tagPlan); + const uniqueTagIds = (pick: (plan: TagImportPlan) => Array<{ id: string }>) => + new Set(tagPlans.flatMap((plan) => pick(plan).map(({ id }) => id))).size; + const tagRequirements = new Set( + scopes.flatMap(({ tagRequest }) => (tagRequest.requirements ?? []).map(({ id }) => id)), + ).size; + const folderId = scopes.length === 1 ? scopes[0].context.folderId : null; eventService.emit('n8n-package-imported', { @@ -95,6 +106,8 @@ export function emitPackageImportedEvent( variableMissingMode: request.variableMissingMode, // An omitted policy places variables in the project, so report what the import did. variableParentPolicy: request.variableParentPolicy ?? VariableParentPolicy.Project, + tagMissingMode: request.tagMissingMode, + tagConflictPolicy: request.tagConflictPolicy, }, packageSourceId: manifest.sourceId, packageVersion: manifest.packageFormatVersion, @@ -125,6 +138,13 @@ export function emitPackageImportedEvent( created: variablesCreated, requirements: variableRequirements, }, + tags: { + matched: uniqueTagIds((plan) => plan.matched), + created: uniqueTagIds((plan) => plan.creations), + renamed: uniqueTagIds((plan) => plan.renames), + skipped: uniqueTagIds((plan) => plan.dropped), + requirements: tagRequirements, + }, }, }); } diff --git a/packages/cli/src/modules/n8n-packages/engine/n8n-package-parser.ts b/packages/cli/src/modules/n8n-packages/engine/n8n-package-parser.ts index 12803162e35..5c7878e3333 100644 --- a/packages/cli/src/modules/n8n-packages/engine/n8n-package-parser.ts +++ b/packages/cli/src/modules/n8n-packages/engine/n8n-package-parser.ts @@ -113,6 +113,7 @@ export class N8nPackageParser { sourceWorkflowId: entry.id, sourcePublished: wire.isPublished, parentFolderId, + ...(wire.tagIds !== undefined ? { tagIds: wire.tagIds } : {}), }; } diff --git a/packages/cli/src/modules/n8n-packages/engine/project-package-importer.ts b/packages/cli/src/modules/n8n-packages/engine/project-package-importer.ts index 287bc443331..fe99557d45c 100644 --- a/packages/cli/src/modules/n8n-packages/engine/project-package-importer.ts +++ b/packages/cli/src/modules/n8n-packages/engine/project-package-importer.ts @@ -7,20 +7,30 @@ import { EventService } from '@/events/event.service'; import type { CredentialBindingRequest } from '../entities/credential/credential.types'; import type { DataTableImportRequest } from '../entities/data-table/data-table.types'; import { ProjectImporter } from '../entities/project/project-importer'; +import type { TagImportRequest } from '../entities/tag/tag.types'; import type { VariableImportRequest } from '../entities/variable/variable.types'; +import { collectPlannedWorkflowBindings } from '../entities/workflow/workflow-importer'; +import { WorkflowPublisher } from '../entities/workflow/workflow-publisher'; import type { PackageReader } from '../io/package-reader'; import type { + ImportBindingMap, ImportedFolderSummary, ImportedWorkflowSummary, ImportPackageRequest, ImportResult, + ImportTagSummary, PackageImportBindings, } from '../n8n-packages.types'; import { mergeBindings } from '../n8n-packages.types'; -import { assertPackageImportApiKeyScopes, assertVariableCreationAllowed } from './import-gates'; +import { + assertPackageImportApiKeyScopes, + assertTagWritesAllowed, + assertVariableCreationAllowed, +} from './import-gates'; import { deriveVariableScope } from './package-layout'; import { ImportOrchestrator, + type ImportContentResult, type ImportOrchestrationInput, type ImportPlan, } from './import-orchestrator'; @@ -31,6 +41,8 @@ import { scopeCredentialBindingsToRequirements, toImportedWorkflowSummaries, toPackageSummary, + toTagSummary, + unionTagSummaries, } from './import-result'; import { emitPackageImportedEvent, type PackageImportScope } from './import-telemetry'; import { N8nPackageParser } from './n8n-package-parser'; @@ -42,6 +54,7 @@ export class ProjectPackageImporter { private readonly packageParser: N8nPackageParser, private readonly projectImporter: ProjectImporter, private readonly importOrchestrator: ImportOrchestrator, + private readonly workflowPublisher: WorkflowPublisher, private readonly eventService: EventService, private readonly licenseState: LicenseState, ) {} @@ -76,10 +89,45 @@ export class ProjectPackageImporter { planned.push({ project, plan }); } + assertTagWritesAllowed( + request.apiKeyScopes, + planned.map(({ plan }) => plan.tagPlan), + ); await this.importOrchestrator.assertNotBlocked(planned.map(({ plan }) => plan)); const projectSummaries = await this.projectImporter.apply(request.user, projectPlan); + // Resolve every project's workflow ids up front so a sub-workflow reference + // that points into another project resolves when its parent is applied. + const packageWorkflowBindings: ImportBindingMap = new Map( + planned.flatMap(({ plan }) => [...collectPlannedWorkflowBindings(plan.workflowPlan.items)]), + ); + + // Write every project's content first. Publishing waits for the sweep below, so a project's + // position here does not decide whether a cross-project sub-workflow exists in time. + const applied: Array<{ + project: ManifestEntry; + plan: ImportPlan; + content: ImportContentResult; + }> = []; + for (const { project, plan } of planned) { + applied.push({ + project, + plan, + content: await this.importOrchestrator.apply(plan, packageWorkflowBindings), + }); + } + + // Publishing spans projects: activation rejects a parent whose sub-workflow is not yet + // published, and that dependency can point into any project — so it is one package-wide, + // dependency-ordered sweep over everything just written. + const published = await this.workflowPublisher.applyToPackage({ + user: request.user, + persisted: applied.flatMap(({ content }) => content.workflowOutcomes), + policy: request.workflowPublishingPolicy, + subWorkflowRequirements: manifest.requirements?.workflows, + }); + const workflows: ImportedWorkflowSummary[] = []; const folders: ImportedFolderSummary[] = []; const scopedBindings: PackageImportBindings[] = []; @@ -89,25 +137,29 @@ export class ProjectPackageImporter { const variablesMissing: string[] = []; const variablesStubbed: string[] = []; const variablesSkipped: string[] = []; + const tagSummaries: ImportTagSummary[] = []; const scopes: PackageImportScope[] = []; - for (const { project, plan } of planned) { - const imported = await this.importOrchestrator.apply(plan); - workflows.push(...toImportedWorkflowSummaries(imported.workflowOutcomes, project.id)); - folders.push(...imported.folderSummaries); - scopedBindings.push(imported.bindings); - matched.push(...imported.credentialResult.matched); - stubbed.push(...imported.credentialResult.stubbed); - variablesMatched.push(...imported.variablePlan.matched); - variablesMissing.push(...imported.variablePlan.missing.map(({ name }) => name)); - variablesStubbed.push(...imported.variableResult.stubbed); - variablesSkipped.push(...imported.variableResult.skippedExisting); + for (const { project, plan, content } of applied) { + workflows.push( + ...toImportedWorkflowSummaries(content.workflowOutcomes, project.id, published), + ); + folders.push(...content.folderSummaries); + scopedBindings.push(content.bindings); + matched.push(...content.credentialResult.matched); + stubbed.push(...content.credentialResult.stubbed); + variablesMatched.push(...content.variablePlan.matched); + variablesMissing.push(...content.variablePlan.missing.map(({ name }) => name)); + variablesStubbed.push(...content.variableResult.stubbed); + variablesSkipped.push(...content.variableResult.skippedExisting); + tagSummaries.push(toTagSummary(content.tagPlan)); scopes.push({ context: plan.input.context, - imported, + imported: content, credentialRequest: plan.input.credentialRequest, dataTableRequest: plan.input.dataTableRequest, variableRequest: plan.input.variableRequest, + tagRequest: plan.input.tagRequest, }); } @@ -126,6 +178,7 @@ export class ProjectPackageImporter { stubbed: variablesStubbed, skipped: variablesSkipped, }), + tags: unionTagSummaries(tagSummaries), }); } @@ -172,6 +225,14 @@ export class ProjectPackageImporter { missingMode: request.variableMissingMode, }; + // Untrimmed on purpose: the tag importer scopes by this project's workflows' own + // `tagIds`, so another project's requirements are simply never referenced here. + const tagRequest: TagImportRequest = { + requirements: manifest.requirements?.tags, + missingMode: request.tagMissingMode, + conflictPolicy: request.tagConflictPolicy, + }; + return { context: { user: request.user, @@ -183,6 +244,7 @@ export class ProjectPackageImporter { credentialRequest, dataTableRequest, variableRequest, + tagRequest, options: request, projectPendingCreation, }; diff --git a/packages/cli/src/modules/n8n-packages/engine/workflow-package-importer.ts b/packages/cli/src/modules/n8n-packages/engine/workflow-package-importer.ts index 76c99fbdfbe..9cb7215d98d 100644 --- a/packages/cli/src/modules/n8n-packages/engine/workflow-package-importer.ts +++ b/packages/cli/src/modules/n8n-packages/engine/workflow-package-importer.ts @@ -12,17 +12,24 @@ import { ProjectService } from '@/services/project.service.ee'; import type { CredentialBindingRequest } from '../entities/credential/credential.types'; import type { DataTableImportRequest } from '../entities/data-table/data-table.types'; +import type { TagImportRequest } from '../entities/tag/tag.types'; import type { VariableImportRequest } from '../entities/variable/variable.types'; +import { WorkflowPublisher } from '../entities/workflow/workflow-publisher'; import type { PackageReader } from '../io/package-reader'; import { VariableParentPolicy } from '../n8n-packages.types'; import type { ImportContext, ImportPackageRequest, ImportResult } from '../n8n-packages.types'; -import { assertPackageImportApiKeyScopes, assertVariableCreationAllowed } from './import-gates'; +import { + assertPackageImportApiKeyScopes, + assertTagWritesAllowed, + assertVariableCreationAllowed, +} from './import-gates'; import { ImportOrchestrator } from './import-orchestrator'; import { buildImportResult, identifyRequirements, toImportedWorkflowSummaries, toPackageSummary, + toTagSummary, toVariableSummary, } from './import-result'; import { emitPackageImportedEvent } from './import-telemetry'; @@ -38,6 +45,7 @@ export class WorkflowPackageImporter { constructor( private readonly packageParser: N8nPackageParser, private readonly importOrchestrator: ImportOrchestrator, + private readonly workflowPublisher: WorkflowPublisher, private readonly projectService: ProjectService, private readonly folderService: FolderService, private readonly eventService: EventService, @@ -101,6 +109,12 @@ export class WorkflowPackageImporter { missingMode: request.variableMissingMode, }; + const tagRequest: TagImportRequest = { + requirements: manifest.requirements?.tags, + missingMode: request.tagMissingMode, + conflictPolicy: request.tagConflictPolicy, + }; + const plan = await this.importOrchestrator.plan({ context, folders, @@ -108,30 +122,56 @@ export class WorkflowPackageImporter { credentialRequest, dataTableRequest, variableRequest, + tagRequest, options: request, + subWorkflowRequirements: identifyRequirements(manifest.requirements?.workflows, workflows), }); + assertTagWritesAllowed(request.apiKeyScopes, [plan.tagPlan]); await this.importOrchestrator.assertNotBlocked([plan]); - const imported = await this.importOrchestrator.apply(plan); + const content = await this.importOrchestrator.apply(plan); + + // Publishing waits until every workflow is written: activation rejects a parent whose + // referenced sub-workflow is not yet published, so the order is resolved across the batch. + const published = await this.workflowPublisher.applyToPackage({ + user: request.user, + persisted: content.workflowOutcomes, + policy: request.workflowPublishingPolicy, + subWorkflowRequirements: plan.input.subWorkflowRequirements, + }); emitPackageImportedEvent(this.eventService, { request, manifest, - scopes: [{ context, imported, credentialRequest, dataTableRequest, variableRequest }], + scopes: [ + { + context, + imported: content, + credentialRequest, + dataTableRequest, + variableRequest, + tagRequest, + }, + ], }); return buildImportResult({ package: toPackageSummary(manifest), - workflows: toImportedWorkflowSummaries(imported.workflowOutcomes, context.projectId), - folders: imported.folderSummaries, + workflows: toImportedWorkflowSummaries( + content.workflowOutcomes, + context.projectId, + published, + ), + folders: content.folderSummaries, projects: [], - bindings: imported.bindings, + bindings: content.bindings, credentials: { - matched: imported.credentialResult.matched, - stubbed: imported.credentialResult.stubbed, + matched: content.credentialResult.matched, + stubbed: content.credentialResult.stubbed, }, - variables: toVariableSummary(imported.variablePlan, imported.variableResult), + variables: toVariableSummary(content.variablePlan, content.variableResult), + tags: toTagSummary(content.tagPlan), }); } diff --git a/packages/cli/src/modules/n8n-packages/entities/__tests__/requirements.types.test.ts b/packages/cli/src/modules/n8n-packages/entities/__tests__/requirements.types.test.ts index 488d86012bb..dd8fa8c83cd 100644 --- a/packages/cli/src/modules/n8n-packages/entities/__tests__/requirements.types.test.ts +++ b/packages/cli/src/modules/n8n-packages/entities/__tests__/requirements.types.test.ts @@ -1,6 +1,7 @@ import type { WorkflowCredentialRequirement } from '../credential/credential.types'; import type { WorkflowDataTableRequirement } from '../data-table/data-table.types'; import { mergeRequirements } from '../requirements.types'; +import type { WorkflowTagUsage } from '../tag/tag.types'; import type { WorkflowVariableRequirement } from '../variable/variable.types'; import type { WorkflowNodeTypeSource } from '../workflow/node-type-usage'; @@ -21,6 +22,10 @@ function dataTable(dataTableId: string, workflowId: string): WorkflowDataTableRe return { workflowId, dataTableId }; } +function tagUsage(tagId: string, workflowId: string): WorkflowTagUsage { + return { workflowId, tag: { id: tagId, name: `Tag ${tagId}` } }; +} + function nodeTypeSource(workflowId: string): WorkflowNodeTypeSource { return { workflowId, nodes: [] }; } @@ -32,12 +37,14 @@ describe('mergeRequirements', () => { credentials: [cred('c1', 'w1')], dataTables: [dataTable('dt1', 'w1')], variables: [variable('V1', 'w1')], + tags: [tagUsage('t1', 'w1')], nodeTypes: [nodeTypeSource('w1')], }, { credentials: [cred('c2', 'w2'), cred('c3', 'w3')], dataTables: [dataTable('dt2', 'w2')], variables: [variable('V2', 'w2')], + tags: [tagUsage('t2', 'w2')], nodeTypes: [nodeTypeSource('w2')], }, ); @@ -45,6 +52,7 @@ describe('mergeRequirements', () => { expect(merged.credentials).toEqual([cred('c1', 'w1'), cred('c2', 'w2'), cred('c3', 'w3')]); expect(merged.dataTables).toEqual([dataTable('dt1', 'w1'), dataTable('dt2', 'w2')]); expect(merged.variables).toEqual([variable('V1', 'w1'), variable('V2', 'w2')]); + expect(merged.tags).toEqual([tagUsage('t1', 'w1'), tagUsage('t2', 'w2')]); expect(merged.nodeTypes).toEqual([nodeTypeSource('w1'), nodeTypeSource('w2')]); }); @@ -55,6 +63,7 @@ describe('mergeRequirements', () => { credentials: [cred('c1', 'w1')], dataTables: [dataTable('dt1', 'w1')], variables: [variable('V1', 'w1')], + tags: [tagUsage('t1', 'w1')], nodeTypes: [nodeTypeSource('w1')], }, undefined, @@ -63,6 +72,7 @@ describe('mergeRequirements', () => { expect(merged.credentials).toEqual([cred('c1', 'w1')]); expect(merged.dataTables).toEqual([dataTable('dt1', 'w1')]); expect(merged.variables).toEqual([variable('V1', 'w1')]); + expect(merged.tags).toEqual([tagUsage('t1', 'w1')]); expect(merged.nodeTypes).toEqual([nodeTypeSource('w1')]); }); @@ -71,6 +81,7 @@ describe('mergeRequirements', () => { credentials: [], dataTables: [], variables: [], + tags: [], nodeTypes: [], }); }); diff --git a/packages/cli/src/modules/n8n-packages/entities/folder/__tests__/folder.exporter.test.ts b/packages/cli/src/modules/n8n-packages/entities/folder/__tests__/folder.exporter.test.ts index 3a63acb2677..5a2bf44c62d 100644 --- a/packages/cli/src/modules/n8n-packages/entities/folder/__tests__/folder.exporter.test.ts +++ b/packages/cli/src/modules/n8n-packages/entities/folder/__tests__/folder.exporter.test.ts @@ -49,6 +49,7 @@ describe('FolderExporter', () => { user, folderIds: ['fld-1'], writer: new CapturingWriter(), + includeTags: true, basePrefix: 'projects/team-ligo', }); @@ -71,6 +72,7 @@ describe('FolderExporter', () => { ], dataTables: [], variables: [], + tags: [], nodeTypes: [], }, }); @@ -79,6 +81,7 @@ describe('FolderExporter', () => { user, folderIds: ['fld-1'], writer: new CapturingWriter(), + includeTags: true, }); // The folder's own target is passed as basePrefix, so workflows nest under it. @@ -106,7 +109,12 @@ describe('FolderExporter', () => { ); await expect( - exporter.export({ user, folderIds: ['fld-1'], writer: new CapturingWriter() }), + exporter.export({ + user, + folderIds: ['fld-1'], + writer: new CapturingWriter(), + includeTags: true, + }), ).rejects.toThrow(/not found or not accessible/); }); }); diff --git a/packages/cli/src/modules/n8n-packages/entities/folder/folder.exporter.ts b/packages/cli/src/modules/n8n-packages/entities/folder/folder.exporter.ts index 059c00024ae..aaaa343f194 100644 --- a/packages/cli/src/modules/n8n-packages/entities/folder/folder.exporter.ts +++ b/packages/cli/src/modules/n8n-packages/entities/folder/folder.exporter.ts @@ -18,6 +18,7 @@ export interface FolderExportRequest { user: User; folderIds: string[]; writer: PackageWriter; + includeTags: boolean; /** * Directory the folder tree is written under. Empty for a top-level folder * export (`folders/...`); a project exporter passes `projects/` so the @@ -181,6 +182,7 @@ export class FolderExporter { user: request.user, writer: request.writer, workflowIds, + includeTags: request.includeTags, basePrefix, }); } diff --git a/packages/cli/src/modules/n8n-packages/entities/project/__tests__/project.exporter.test.ts b/packages/cli/src/modules/n8n-packages/entities/project/__tests__/project.exporter.test.ts index 426b5979665..71db4856e79 100644 --- a/packages/cli/src/modules/n8n-packages/entities/project/__tests__/project.exporter.test.ts +++ b/packages/cli/src/modules/n8n-packages/entities/project/__tests__/project.exporter.test.ts @@ -97,7 +97,7 @@ describe('ProjectExporter', () => { const { exporter, projectService } = makeExporter({ projects: [project] }); const writer = new CapturingWriter(); - await exporter.export({ user, projectIds: [project.id], writer }); + await exporter.export({ user, projectIds: [project.id], writer, includeTags: true }); expect(projectService.findProjectsByIdsForUser).toHaveBeenCalledWith( user, @@ -111,9 +111,9 @@ describe('ProjectExporter', () => { const { exporter } = makeExporter({ projects: [] }); const writer = new CapturingWriter(); - await expect(exporter.export({ user, projectIds: [project.id], writer })).rejects.toThrow( - '1 project(s) not found or not accessible. Export aborted.', - ); + await expect( + exporter.export({ user, projectIds: [project.id], writer, includeTags: true }), + ).rejects.toThrow('1 project(s) not found or not accessible. Export aborted.'); }); it('throws PackageEntityNotFoundError when the missing project does not exist at all', async () => { @@ -121,9 +121,9 @@ describe('ProjectExporter', () => { projectService.findExistingProjectIds.mockResolvedValue(new Set()); const writer = new CapturingWriter(); - await expect(exporter.export({ user, projectIds: ['missing'], writer })).rejects.toBeInstanceOf( - PackageEntityNotFoundError, - ); + await expect( + exporter.export({ user, projectIds: ['missing'], writer, includeTags: true }), + ).rejects.toBeInstanceOf(PackageEntityNotFoundError); }); it('throws PackageEntityAccessDeniedError when the missing project exists but is inaccessible', async () => { @@ -132,7 +132,7 @@ describe('ProjectExporter', () => { const writer = new CapturingWriter(); await expect( - exporter.export({ user, projectIds: ['denied-1'], writer }), + exporter.export({ user, projectIds: ['denied-1'], writer, includeTags: true }), ).rejects.toBeInstanceOf(PackageEntityAccessDeniedError); }); @@ -141,7 +141,12 @@ describe('ProjectExporter', () => { const { exporter } = makeExporter({ projects: [project] }); const writer = new CapturingWriter(); - const { entries } = await exporter.export({ user, projectIds: [project.id], writer }); + const { entries } = await exporter.export({ + user, + projectIds: [project.id], + writer, + includeTags: true, + }); expect(entries).toEqual([ { @@ -179,6 +184,7 @@ describe('ProjectExporter', () => { user, projectIds: [newerProject.id, olderProject.id], writer, + includeTags: true, }); expect(entries).toEqual([ @@ -200,7 +206,12 @@ describe('ProjectExporter', () => { const { exporter } = makeExporter({ projects: [project] }); const writer = new CapturingWriter(); - const { entries } = await exporter.export({ user, projectIds: [project.id], writer }); + const { entries } = await exporter.export({ + user, + projectIds: [project.id], + writer, + includeTags: true, + }); expect(entries).toEqual([ { diff --git a/packages/cli/src/modules/n8n-packages/entities/project/project.exporter.ts b/packages/cli/src/modules/n8n-packages/entities/project/project.exporter.ts index 9814c5c6b06..ccd7c91a4e4 100644 --- a/packages/cli/src/modules/n8n-packages/entities/project/project.exporter.ts +++ b/packages/cli/src/modules/n8n-packages/entities/project/project.exporter.ts @@ -21,6 +21,7 @@ export interface ProjectExportRequest { user: User; projectIds: string[]; writer: PackageWriter; + includeTags: boolean; } interface ProjectExportResult { @@ -109,6 +110,7 @@ export class ProjectExporter { user: request.user, folderIds, writer: request.writer, + includeTags: request.includeTags, basePrefix: target, }); } @@ -127,6 +129,7 @@ export class ProjectExporter { user: request.user, workflowIds: rootWorkflowIds, writer: request.writer, + includeTags: request.includeTags, basePrefix: target, }); } diff --git a/packages/cli/src/modules/n8n-packages/entities/reference.ts b/packages/cli/src/modules/n8n-packages/entities/reference.ts new file mode 100644 index 00000000000..cf787435c34 --- /dev/null +++ b/packages/cli/src/modules/n8n-packages/entities/reference.ts @@ -0,0 +1,18 @@ +import type { WorkflowEntity } from '@n8n/db'; + +import type { RequirementsExtractor } from './requirements-extractor'; +import type { PackageImportBindings } from '../n8n-packages.types'; + +/** + * When we pull out a requirement, we also need to rebind on the other side. + * + * When we bring credentials/variables into this model we should be able to merge the interfaces. + **/ +export interface EntityReference extends RequirementsExtractor { + /** + * + * @param workflow The workflow which bindings should be injected + * @param bindings Bindings that should be injected into the workflow + */ + apply(workflow: WorkflowEntity, bindings: PackageImportBindings): void; +} diff --git a/packages/cli/src/modules/n8n-packages/entities/requirements.types.ts b/packages/cli/src/modules/n8n-packages/entities/requirements.types.ts index b7dafdcc183..8467b0d8a8f 100644 --- a/packages/cli/src/modules/n8n-packages/entities/requirements.types.ts +++ b/packages/cli/src/modules/n8n-packages/entities/requirements.types.ts @@ -1,5 +1,6 @@ import type { WorkflowCredentialRequirement } from './credential/credential.types'; import type { WorkflowDataTableRequirement } from './data-table/data-table.types'; +import type { WorkflowTagUsage } from './tag/tag.types'; import type { WorkflowVariableRequirement } from './variable/variable.types'; import type { WorkflowNodeTypeSource } from './workflow/node-type-usage'; @@ -7,6 +8,7 @@ export interface WorkflowExportRequirements { credentials: WorkflowCredentialRequirement[]; dataTables: WorkflowDataTableRequirement[]; variables: WorkflowVariableRequirement[]; + tags: WorkflowTagUsage[]; /** Per-workflow node lists; folded into unique pairs at manifest-assembly time. */ nodeTypes: WorkflowNodeTypeSource[]; } @@ -17,5 +19,6 @@ export const mergeRequirements = ( credentials: parts.flatMap((part) => part?.credentials ?? []), dataTables: parts.flatMap((part) => part?.dataTables ?? []), variables: parts.flatMap((part) => part?.variables ?? []), + tags: parts.flatMap((part) => part?.tags ?? []), nodeTypes: parts.flatMap((part) => part?.nodeTypes ?? []), }); diff --git a/packages/cli/src/modules/n8n-packages/entities/tag/__tests__/tag-import-decision.test.ts b/packages/cli/src/modules/n8n-packages/entities/tag/__tests__/tag-import-decision.test.ts new file mode 100644 index 00000000000..357d2b1784e --- /dev/null +++ b/packages/cli/src/modules/n8n-packages/entities/tag/__tests__/tag-import-decision.test.ts @@ -0,0 +1,266 @@ +import { decideTagImportAction } from '../tag-import-decision'; + +const source = { id: 'tag-1', name: 'prod' }; +const sameIdSameName = { id: 'tag-1', name: 'prod' }; +const sameIdOtherName = { id: 'tag-1', name: 'production' }; +const otherIdSameName = { id: 'tag-2', name: 'prod' }; + +describe('decideTagImportAction', () => { + describe('tag exists on the target with the same id and name (exact match)', () => { + it('attaches to the existing tag regardless of policy', () => { + expect(decideTagImportAction(source, sameIdSameName, undefined, 'create', 'skip')).toEqual({ + action: 'attach', + target: sameIdSameName, + }); + }); + + it('matches the trimmed package name', () => { + expect( + decideTagImportAction( + { id: 'tag-1', name: ' prod ' }, + sameIdSameName, + undefined, + 'create', + 'fail', + ), + ).toEqual({ action: 'attach', target: sameIdSameName }); + }); + }); + + describe('tag exists on the target with the same id but a different name (rename drift)', () => { + it('gates under fail, reporting both names', () => { + expect(decideTagImportAction(source, sameIdOtherName, undefined, 'create', 'fail')).toEqual({ + action: 'fail', + failure: { + kind: 'rename-drift', + sourceId: 'tag-1', + name: 'prod', + existingName: 'production', + }, + }); + }); + + it('drops under skip', () => { + expect(decideTagImportAction(source, sameIdOtherName, undefined, 'create', 'skip')).toEqual({ + action: 'drop', + tag: { id: 'tag-1', name: 'prod' }, + }); + }); + + it('renames the target tag to the package name under rename', () => { + expect(decideTagImportAction(source, sameIdOtherName, undefined, 'create', 'rename')).toEqual( + { + action: 'rename', + rename: { id: 'tag-1', from: 'production', to: 'prod' }, + }, + ); + }); + + it('degrades rename to a gate when another tag holds the wanted name', () => { + expect( + decideTagImportAction(source, sameIdOtherName, otherIdSameName, 'create', 'rename'), + ).toEqual({ + action: 'fail', + failure: { + kind: 'rename-drift', + sourceId: 'tag-1', + name: 'prod', + existingName: 'production', + existingTagId: 'tag-2', + }, + }); + }); + }); + + describe('tag missing from the target, name free (create happy path)', () => { + it('creates with the source id and trimmed name under create', () => { + expect( + decideTagImportAction( + { id: 'tag-1', name: ' prod ' }, + undefined, + undefined, + 'create', + 'fail', + ), + ).toEqual({ action: 'create', tag: { id: 'tag-1', name: 'prod' } }); + }); + + it('drops under do-nothing', () => { + expect(decideTagImportAction(source, undefined, undefined, 'do-nothing', 'fail')).toEqual({ + action: 'drop', + tag: { id: 'tag-1', name: 'prod' }, + }); + }); + }); + + describe('tag missing from the target, name taken by a different tag (name collision, e.g. tag created manually on the target)', () => { + it('drops under do-nothing, never conflicting', () => { + expect( + decideTagImportAction(source, undefined, otherIdSameName, 'do-nothing', 'fail'), + ).toEqual({ + action: 'drop', + tag: { id: 'tag-1', name: 'prod' }, + }); + }); + + it('gates under create + fail', () => { + expect(decideTagImportAction(source, undefined, otherIdSameName, 'create', 'fail')).toEqual({ + action: 'fail', + failure: { + kind: 'name-collision', + sourceId: 'tag-1', + name: 'prod', + existingTagId: 'tag-2', + }, + }); + }); + + it('gates under create + rename (renaming would need id reconciliation)', () => { + expect(decideTagImportAction(source, undefined, otherIdSameName, 'create', 'rename')).toEqual( + { + action: 'fail', + failure: { + kind: 'name-collision', + sourceId: 'tag-1', + name: 'prod', + existingTagId: 'tag-2', + }, + }, + ); + }); + + it('drops under create + skip', () => { + expect(decideTagImportAction(source, undefined, otherIdSameName, 'create', 'skip')).toEqual({ + action: 'drop', + tag: { id: 'tag-1', name: 'prod' }, + }); + }); + }); + + describe('tag name validation (only when the import would write the name)', () => { + it('creates a 24-character name but gates a 25-character one', () => { + const ok = 'a'.repeat(24); + const tooLong = 'a'.repeat(25); + expect( + decideTagImportAction({ id: 't', name: ok }, undefined, undefined, 'create', 'fail'), + ).toEqual({ + action: 'create', + tag: { id: 't', name: ok }, + }); + expect( + decideTagImportAction({ id: 't', name: tooLong }, undefined, undefined, 'create', 'fail'), + ).toEqual({ + action: 'fail', + failure: { kind: 'invalid-name', sourceId: 't', name: tooLong }, + }); + }); + + it('counts code points, not UTF-16 units: 24 emoji create, 25 gate', () => { + const ok = '😀'.repeat(24); + const tooLong = '😀'.repeat(25); + expect( + decideTagImportAction({ id: 't', name: ok }, undefined, undefined, 'create', 'fail'), + ).toEqual({ + action: 'create', + tag: { id: 't', name: ok }, + }); + expect( + decideTagImportAction({ id: 't', name: tooLong }, undefined, undefined, 'create', 'fail'), + ).toEqual({ + action: 'fail', + failure: { kind: 'invalid-name', sourceId: 't', name: tooLong }, + }); + }); + + it('gates a name containing a lone UTF-16 surrogate', () => { + const loneSurrogate = 'pr\ud800od'; + expect( + decideTagImportAction( + { id: 't', name: loneSurrogate }, + undefined, + undefined, + 'create', + 'fail', + ), + ).toEqual({ + action: 'fail', + failure: { kind: 'invalid-name', sourceId: 't', name: loneSurrogate }, + }); + }); + + it('accepts a ZWJ-joined emoji name', () => { + const family = '👨‍👩‍👧'; + expect( + decideTagImportAction({ id: 't', name: family }, undefined, undefined, 'create', 'fail'), + ).toEqual({ + action: 'create', + tag: { id: 't', name: family }, + }); + }); + + it('gates a whitespace-only name on create', () => { + expect( + decideTagImportAction({ id: 't', name: ' ' }, undefined, undefined, 'create', 'fail'), + ).toEqual({ + action: 'fail', + failure: { kind: 'invalid-name', sourceId: 't', name: '' }, + }); + }); + + it('gates a name with control characters on rename', () => { + expect( + decideTagImportAction( + { id: 'tag-1', name: 'pr' + String.fromCharCode(7) + 'od' }, + sameIdOtherName, + undefined, + 'create', + 'rename', + ), + ).toEqual({ + action: 'fail', + failure: { + kind: 'invalid-name', + sourceId: 'tag-1', + name: 'pr' + String.fromCharCode(7) + 'od', + }, + }); + }); + + it('does not validate a dropped tag', () => { + expect( + decideTagImportAction( + { id: 't', name: 'a'.repeat(25) }, + undefined, + undefined, + 'do-nothing', + 'fail', + ), + ).toEqual({ action: 'drop', tag: { id: 't', name: 'a'.repeat(25) } }); + }); + }); + + describe('tag id validation (only when the import would create the tag)', () => { + it('creates a 36-character id but gates a 37-character one', () => { + const ok = 'i'.repeat(36); + const tooLong = 'i'.repeat(37); + expect( + decideTagImportAction({ id: ok, name: 'prod' }, undefined, undefined, 'create', 'fail'), + ).toEqual({ + action: 'create', + tag: { id: ok, name: 'prod' }, + }); + expect( + decideTagImportAction( + { id: tooLong, name: 'prod' }, + undefined, + undefined, + 'create', + 'fail', + ), + ).toEqual({ + action: 'fail', + failure: { kind: 'invalid-id', sourceId: tooLong, name: 'prod' }, + }); + }); + }); +}); diff --git a/packages/cli/src/modules/n8n-packages/entities/tag/__tests__/tag-importer.test.ts b/packages/cli/src/modules/n8n-packages/entities/tag/__tests__/tag-importer.test.ts new file mode 100644 index 00000000000..f97d7f1797b --- /dev/null +++ b/packages/cli/src/modules/n8n-packages/entities/tag/__tests__/tag-importer.test.ts @@ -0,0 +1,79 @@ +import type { GlobalConfig } from '@n8n/config'; +import type { TagEntity, User } from '@n8n/db'; +import { mock } from 'vitest-mock-extended'; + +import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; +import type { TagService } from '@/services/tag.service'; + +import { TagImporter } from '../tag-importer'; +import type { TagImportPlan, TagImportRequest } from '../tag.types'; +import type { ImportContext } from '../../../n8n-packages.types'; + +const globalConfig = mock({ tags: { disabled: false } }); + +function contextFor(scopes: Array<'tag:create' | 'tag:update'>): ImportContext { + return { + user: mock({ id: 'user-1', role: { scopes: scopes.map((slug) => ({ slug })) } }), + projectId: 'project-1', + folderId: null, + }; +} + +function makeImporter() { + const tagService = mock(); + tagService.getByIds.mockResolvedValue([]); + tagService.getByNames.mockResolvedValue([]); + return { importer: new TagImporter(tagService, globalConfig), tagService }; +} + +const request: TagImportRequest = { + requirements: [{ id: 'tag-1', name: 'prod', usedByWorkflows: ['wf-1'] }], + missingMode: 'create', + conflictPolicy: 'skip', +}; +const appliedWorkflows = [{ sourceWorkflowId: 'wf-1', tagIds: ['tag-1'] }]; + +describe('TagImporter.plan', () => { + it('fails with permission-denied when the user lacks tag:create for a planned creation', async () => { + const { importer } = makeImporter(); + + const plan = await importer.plan(contextFor([]), request, appliedWorkflows); + + expect(plan.creations).toEqual([{ id: 'tag-1', name: 'prod' }]); + expect(plan.failures).toEqual([ + { kind: 'permission-denied', missingScope: 'tag:create', usedByWorkflows: ['wf-1'] }, + ]); + }); + + it('fails with permission-denied when the user lacks tag:update for a planned rename', async () => { + const { importer, tagService } = makeImporter(); + tagService.getByIds.mockResolvedValue([mock({ id: 'tag-1', name: 'production' })]); + + const plan = await importer.plan( + contextFor(['tag:create']), + { ...request, conflictPolicy: 'rename' }, + appliedWorkflows, + ); + + expect(plan.renames).toEqual([{ id: 'tag-1', from: 'production', to: 'prod' }]); + expect(plan.failures).toEqual([ + { kind: 'permission-denied', missingScope: 'tag:update', usedByWorkflows: ['wf-1'] }, + ]); + }); +}); + +describe('TagImporter.apply', () => { + it('re-checks the scope and throws before writing anything', async () => { + const { importer, tagService } = makeImporter(); + const plan: TagImportPlan = { + matched: [], + creations: [{ id: 'tag-1', name: 'prod' }], + renames: [], + dropped: [], + failures: [], + }; + + await expect(importer.apply(contextFor([]), plan)).rejects.toThrow(ForbiddenError); + expect(tagService.save).not.toHaveBeenCalled(); + }); +}); diff --git a/packages/cli/src/modules/n8n-packages/entities/tag/tag-import-decision.ts b/packages/cli/src/modules/n8n-packages/entities/tag/tag-import-decision.ts new file mode 100644 index 00000000000..c059d1bd64b --- /dev/null +++ b/packages/cli/src/modules/n8n-packages/entities/tag/tag-import-decision.ts @@ -0,0 +1,142 @@ +import type { + TagRef, + TagRename, + TagResolutionFailure, + TagResolutionFailureKind, +} from './tag.types'; +import { TagConflictPolicy, TagMissingMode } from '../../n8n-packages.types'; + +/** A per-requirement failure; `TagImporter.plan` fills in `usedByWorkflows`. */ +export type TagDecisionFailure = Omit< + TagResolutionFailure, + 'kind' | 'sourceId' | 'name' | 'usedByWorkflows' +> & { + kind: Exclude; + sourceId: string; + name: string; +}; + +export type TagEffect = + | { action: 'attach'; target: TagRef } + | { action: 'create'; tag: TagRef } + | { action: 'rename'; rename: TagRename } + | { action: 'drop'; tag: TagRef } + | { action: 'fail'; failure: TagDecisionFailure }; + +// Mirrors TagEntity's @Length(1, 24): an invalid name must gate the import at +// plan time instead of failing entity validation halfway through apply. +const TAG_NAME_MAX_LENGTH = 24; + +// tag_entity.id is varchar(36) on Postgres — a longer id would otherwise only +// fail at insert, after the gate. +const TAG_ID_MAX_LENGTH = 36; + +// \p{Cs}: a lone UTF-16 surrogate would collapse to U+FFFD in the database. +// Format chars (\p{Cf}) stay allowed: entity validation accepts them and +// ZWJ-joined emoji names are legitimate, so gating them would break re-import +// of a package n8n itself exported. +const FORBIDDEN_NAME_CHARS = /[\p{Cc}\p{Cs}]/u; + +/** + * Decides the fate of one package tag reference. Matching is by id; + * `targetTagWithSameId` is the target instance's occupant of the requirement's + * id and `nameHolder` is a *different* target tag currently holding the + * requirement's (trimmed) name. Name and id shape are only validated when the + * effect would write (create or rename) — a dropped tag never gates. + */ +export function decideTagImportAction( + requirement: TagRef, + targetTagWithSameId: TagRef | undefined, + nameHolder: TagRef | undefined, + missingMode: TagMissingMode, + conflictPolicy: TagConflictPolicy, +): TagEffect { + const sourceId = requirement.id; + const name = requirement.name.trim(); + + if (targetTagWithSameId) { + return decideForMatchedId(sourceId, name, targetTagWithSameId, nameHolder, conflictPolicy); + } + + return decideForAbsentId(sourceId, name, nameHolder, missingMode, conflictPolicy); +} + +/** The requirement's id is taken on the target: an exact name match attaches, a differing name is rename drift. */ +function decideForMatchedId( + sourceId: string, + name: string, + targetTag: TagRef, + nameHolder: TagRef | undefined, + conflictPolicy: TagConflictPolicy, +): TagEffect { + if (targetTag.name === name) return { action: 'attach', target: targetTag }; + + if (conflictPolicy === TagConflictPolicy.Skip) { + return { action: 'drop', tag: { id: sourceId, name } }; + } + + if (conflictPolicy === TagConflictPolicy.Rename && !nameHolder) { + return ( + invalidName(name, sourceId) ?? { + action: 'rename', + rename: { id: sourceId, from: targetTag.name, to: name }, + } + ); + } + + // `fail`, or `rename` degraded because another tag holds the wanted + // name — resolving that requires id reconciliation (LIGO-874). + return { + action: 'fail', + failure: { + kind: 'rename-drift', + sourceId, + name, + existingName: targetTag.name, + ...(nameHolder ? { existingTagId: nameHolder.id } : {}), + }, + }; +} + +/** The requirement's id is absent on the target: the tag is missing, unless its name is already taken. */ +function decideForAbsentId( + sourceId: string, + name: string, + nameHolder: TagRef | undefined, + missingMode: TagMissingMode, + conflictPolicy: TagConflictPolicy, +): TagEffect { + if (missingMode === TagMissingMode.DoNothing) { + return { action: 'drop', tag: { id: sourceId, name } }; + } + + if (nameHolder) { + if (conflictPolicy === TagConflictPolicy.Skip) { + return { action: 'drop', tag: { id: sourceId, name } }; + } + return { + action: 'fail', + failure: { kind: 'name-collision', sourceId, name, existingTagId: nameHolder.id }, + }; + } + + return ( + invalidName(name, sourceId) ?? + invalidId(sourceId, name) ?? { action: 'create', tag: { id: sourceId, name } } + ); +} + +function invalidName(name: string, sourceId: string): TagEffect | undefined { + if (FORBIDDEN_NAME_CHARS.test(name)) { + return { action: 'fail', failure: { kind: 'invalid-name', sourceId, name } }; + } + // Code points, not UTF-16 units: both @Length (validator.js) and varchar(24) count code points. + const length = [...name].length; + if (length >= 1 && length <= TAG_NAME_MAX_LENGTH) return undefined; + return { action: 'fail', failure: { kind: 'invalid-name', sourceId, name } }; +} + +function invalidId(sourceId: string, name: string): TagEffect | undefined { + if (sourceId.length <= TAG_ID_MAX_LENGTH) return undefined; + return { action: 'fail', failure: { kind: 'invalid-id', sourceId, name } }; +} diff --git a/packages/cli/src/modules/n8n-packages/entities/tag/tag-importer.ts b/packages/cli/src/modules/n8n-packages/entities/tag/tag-importer.ts new file mode 100644 index 00000000000..6d610de56e1 --- /dev/null +++ b/packages/cli/src/modules/n8n-packages/entities/tag/tag-importer.ts @@ -0,0 +1,207 @@ +import { GlobalConfig } from '@n8n/config'; +import { Service } from '@n8n/di'; +import { hasGlobalScope } from '@n8n/permissions'; +import { UserError } from 'n8n-workflow'; + +import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; +import { TagService } from '@/services/tag.service'; + +import { decideTagImportAction } from './tag-import-decision'; +import type { TagDecisionFailure } from './tag-import-decision'; +import type { TagImportPlan, TagImportRequest, TagRef, TagResolutionFailure } from './tag.types'; +import type { ImportContext } from '../../n8n-packages.types'; +import type { PreparedWorkflow } from '../workflow/workflow-import.types'; + +type ReferencingWorkflow = Pick; + +@Service() +export class TagImporter { + constructor( + private readonly tagService: TagService, + private readonly globalConfig: GlobalConfig, + ) {} + + /** + * Resolves the tags referenced by the applied (non-skipped) workflows' + * `tagIds` against the target instance, by source id. Read-only. A `tagIds` + * entry without a matching `requirements.tags` entry is a malformed package: + * apply would FK-fail on it, so it rejects outright instead of gating. + */ + async plan( + context: ImportContext, + request: TagImportRequest, + appliedWorkflows: ReferencingWorkflow[], + ): Promise { + const plan: TagImportPlan = { + matched: [], + creations: [], + renames: [], + dropped: [], + failures: [], + }; + // Disabled tags are a silent no-op: nothing gates, nothing is written. + if (this.globalConfig.tags.disabled) return plan; + + const referencedTagIds = [...new Set(appliedWorkflows.flatMap(({ tagIds }) => tagIds ?? []))]; + if (referencedTagIds.length === 0) return plan; + + const requirementsById = new Map( + (request.requirements ?? []).map((requirement) => [requirement.id, requirement]), + ); + const referencedRequirements = referencedTagIds.map((id) => { + const requirement = requirementsById.get(id); + if (!requirement) { + throw new UserError( + `Package workflows reference tag "${id}", which is missing from the package's tag requirements.`, + ); + } + return requirement; + }); + + const targetsById = new Map( + (await this.tagService.getByIds(referencedTagIds)).map((tag) => [tag.id, toTagRef(tag)]), + ); + const wantedNames = [...new Set(referencedRequirements.map(({ name }) => name.trim()))]; + const holdersByName = new Map( + (await this.tagService.getByNames(wantedNames)).map((tag) => [tag.name, toTagRef(tag)]), + ); + + const decisionFailures: TagDecisionFailure[] = []; + for (const requirement of referencedRequirements) { + const holder = holdersByName.get(requirement.name.trim()); + const effect = decideTagImportAction( + requirement, + targetsById.get(requirement.id), + holder && holder.id !== requirement.id ? holder : undefined, + request.missingMode, + request.conflictPolicy, + ); + if (effect.action === 'attach') plan.matched.push(effect.target); + else if (effect.action === 'create') plan.creations.push(effect.tag); + else if (effect.action === 'rename') plan.renames.push(effect.rename); + else if (effect.action === 'drop') plan.dropped.push(effect.tag); + else decisionFailures.push(effect.failure); + } + + decisionFailures.push(...duplicateWrittenNameFailures(plan)); + + const sourceWorkflowIdsReferencing = (tagId: string) => + appliedWorkflows + .filter(({ tagIds }) => tagIds?.includes(tagId)) + .map(({ sourceWorkflowId }) => sourceWorkflowId); + + plan.failures = decisionFailures.map((failure) => ({ + ...failure, + usedByWorkflows: sortedUnique(sourceWorkflowIdsReferencing(failure.sourceId)), + })); + + // Tags are global entities, so gate on the user's global scopes: project-level + // workflow:import alone must not grant instance-wide tag writes. + if (plan.creations.length > 0 && !hasGlobalScope(context.user, 'tag:create')) { + plan.failures.push( + permissionFailure( + 'tag:create', + plan.creations.map(({ id }) => id), + sourceWorkflowIdsReferencing, + ), + ); + } + if (plan.renames.length > 0 && !hasGlobalScope(context.user, 'tag:update')) { + plan.failures.push( + permissionFailure( + 'tag:update', + plan.renames.map(({ id }) => id), + sourceWorkflowIdsReferencing, + ), + ); + } + + return plan; + } + + /** + * Idempotent create-if-absent / rename-if-differs, so several scopes of a + * project package can carry the same plan for one global tag: the first + * apply writes, later ones no-op. + */ + async apply(context: ImportContext, plan: TagImportPlan): Promise { + if (plan.creations.length === 0 && plan.renames.length === 0) return; + + // Defense in depth: the plan phase already reports a missing scope as a + // blocking issue, but apply re-checks before writing anything. + if (plan.creations.length > 0 && !hasGlobalScope(context.user, 'tag:create')) { + throw new ForbiddenError('User is missing a scope required to create a tag'); + } + if (plan.renames.length > 0 && !hasGlobalScope(context.user, 'tag:update')) { + throw new ForbiddenError('User is missing a scope required to rename a tag'); + } + + const existingById = new Map( + ( + await this.tagService.getByIds([ + ...plan.creations.map(({ id }) => id), + ...plan.renames.map(({ id }) => id), + ]) + ).map((tag) => [tag.id, tag]), + ); + + for (const creation of plan.creations) { + if (existingById.has(creation.id)) continue; + await this.tagService.save(this.tagService.toEntity(creation), 'create'); + } + for (const rename of plan.renames) { + const current = existingById.get(rename.id); + if (!current || current.name === rename.to) continue; + await this.tagService.save( + this.tagService.toEntity({ id: rename.id, name: rename.to }), + 'update', + ); + } + } +} + +function toTagRef(tag: { id: string; name: string }): TagRef { + return { id: tag.id, name: tag.name }; +} + +function sortedUnique(values: string[]): string[] { + return [...new Set(values)].sort(); +} + +/** + * Two planned writes landing on one name (two creations, or a creation plus a + * rename) would hit the tag name's unique index at apply, after the gate — + * so they gate as name collisions instead. + */ +function duplicateWrittenNameFailures(plan: TagImportPlan): TagDecisionFailure[] { + const nameCounts = new Map(); + const writtenNames = [ + ...plan.creations.map(({ name }) => name), + ...plan.renames.map(({ to }) => to), + ]; + for (const name of writtenNames) nameCounts.set(name, (nameCounts.get(name) ?? 0) + 1); + + const duplicated = (name: string) => (nameCounts.get(name) ?? 0) > 1; + return [ + ...plan.creations + .filter(({ name }) => duplicated(name)) + .map(({ id, name }): TagDecisionFailure => ({ kind: 'name-collision', sourceId: id, name })), + ...plan.renames + .filter(({ to }) => duplicated(to)) + .map( + ({ id, to }): TagDecisionFailure => ({ kind: 'name-collision', sourceId: id, name: to }), + ), + ]; +} + +function permissionFailure( + missingScope: 'tag:create' | 'tag:update', + tagIds: string[], + sourceWorkflowIdsReferencing: (tagId: string) => string[], +): TagResolutionFailure { + return { + kind: 'permission-denied', + missingScope, + usedByWorkflows: sortedUnique(tagIds.flatMap(sourceWorkflowIdsReferencing)), + }; +} diff --git a/packages/cli/src/modules/n8n-packages/entities/tag/tag-requirements.extractor.ts b/packages/cli/src/modules/n8n-packages/entities/tag/tag-requirements.extractor.ts new file mode 100644 index 00000000000..444495bb206 --- /dev/null +++ b/packages/cli/src/modules/n8n-packages/entities/tag/tag-requirements.extractor.ts @@ -0,0 +1,15 @@ +import type { WorkflowEntity } from '@n8n/db'; +import { Service } from '@n8n/di'; + +import type { WorkflowTagUsage } from './tag.types'; +import type { RequirementsExtractor } from '../requirements-extractor'; + +@Service() +export class TagRequirementsExtractor implements RequirementsExtractor { + extract(workflow: WorkflowEntity): WorkflowTagUsage[] { + return (workflow.tags ?? []).map((tag) => ({ + workflowId: workflow.id, + tag: { id: tag.id, name: tag.name }, + })); + } +} diff --git a/packages/cli/src/modules/n8n-packages/entities/tag/tag.exporter.ts b/packages/cli/src/modules/n8n-packages/entities/tag/tag.exporter.ts new file mode 100644 index 00000000000..f564c545471 --- /dev/null +++ b/packages/cli/src/modules/n8n-packages/entities/tag/tag.exporter.ts @@ -0,0 +1,55 @@ +import { Service } from '@n8n/di'; + +import type { PackageWriter } from '../../io/package-writer'; +import { UniqueFilenameAllocator } from '../../io/unique-filename-allocator'; +import type { ManifestEntry } from '../../spec/manifest.schema'; +import type { PackageTagRequirement } from '../../spec/requirements.schema'; +import { serializedTagSchema } from '../../spec/serialized/tag.schema'; +import { compareTagsByName, type WorkflowTagUsage } from './tag.types'; + +export interface TagExportRequest { + usages: WorkflowTagUsage[]; + writer: PackageWriter; +} + +export interface TagExportResult { + entries: ManifestEntry[]; + requirements: PackageTagRequirement[]; +} + +@Service() +export class TagExporter { + export(request: TagExportRequest): TagExportResult { + const requirementsByTagId = new Map(); + + for (const { workflowId, tag } of request.usages) { + const requirement = requirementsByTagId.get(tag.id) ?? { + id: tag.id, + name: tag.name, + usedByWorkflows: [], + }; + if (!requirement.usedByWorkflows.includes(workflowId)) { + requirement.usedByWorkflows.push(workflowId); + } + requirementsByTagId.set(tag.id, requirement); + } + + const requirements = [...requirementsByTagId.values()].sort(compareTagsByName); + + const allocator = new UniqueFilenameAllocator('tags', 'tag'); + const entries: ManifestEntry[] = []; + + for (const { id, name } of requirements) { + const tagDirectory = allocator.allocate(name); + const serializedTag = serializedTagSchema.parse({ id, name }); + request.writer.writeDirectory(tagDirectory); + request.writer.writeFile( + `${tagDirectory}/tag.json`, + JSON.stringify(serializedTag, null, '\t'), + ); + entries.push({ id, name, target: tagDirectory }); + } + + return { entries, requirements }; + } +} diff --git a/packages/cli/src/modules/n8n-packages/entities/tag/tag.types.ts b/packages/cli/src/modules/n8n-packages/entities/tag/tag.types.ts new file mode 100644 index 00000000000..66649794709 --- /dev/null +++ b/packages/cli/src/modules/n8n-packages/entities/tag/tag.types.ts @@ -0,0 +1,69 @@ +import type { TagConflictPolicy, TagMissingMode } from '../../n8n-packages.types'; +import type { PackageTagRequirement } from '../../spec/requirements.schema'; + +export interface WorkflowTagUsage { + workflowId: string; + tag: { id: string; name: string }; +} + +/** Locale pinned so package output is byte-stable across environments. */ +export const compareTagsByName = ( + a: { id: string; name: string }, + b: { id: string; name: string }, +) => a.name.localeCompare(b.name, 'en') || a.id.localeCompare(b.id, 'en'); + +export interface TagRef { + id: string; + name: string; +} + +export interface TagRename { + id: string; + from: string; + to: string; +} + +export type TagResolutionFailureKind = + | 'rename-drift' + | 'name-collision' + | 'invalid-name' + | 'invalid-id' + | 'permission-denied'; + +export type TagResolutionFailure = { + kind: TagResolutionFailureKind; + /** Absent for `permission-denied`, which is import-wide rather than per-tag. */ + sourceId?: string; + /** Trimmed; absent for `permission-denied`. */ + name?: string; + /** Only set for `permission-denied`. */ + missingScope?: 'tag:create' | 'tag:update'; + /** The tag currently holding the wanted name. */ + existingTagId?: string; + /** For `rename-drift`: the current name of the same-id target tag. */ + existingName?: string; + usedByWorkflows: string[]; +}; + +export interface TagImportRequest { + requirements: PackageTagRequirement[] | undefined; + missingMode: TagMissingMode; + conflictPolicy: TagConflictPolicy; +} + +export interface TagImportPlan { + /** Exact same-id, same-name matches: attach only. */ + matched: TagRef[]; + /** Created on the target with their source id and name. */ + creations: TagRef[]; + /** Same-id target tags renamed to the package name. */ + renames: TagRename[]; + /** Neither created, renamed, nor attached (`skip` / `do-nothing`). */ + dropped: TagRef[]; + failures: TagResolutionFailure[]; +} + +/** Tag ids the workflow importer must strip from `tagIds` before attaching. */ +export function droppedTagIds(plan: TagImportPlan): ReadonlySet { + return new Set(plan.dropped.map(({ id }) => id)); +} diff --git a/packages/cli/src/modules/n8n-packages/entities/workflow/__tests__/auto-included-workflow-resolver.test.ts b/packages/cli/src/modules/n8n-packages/entities/workflow/__tests__/auto-included-workflow-resolver.test.ts index 52b134543b4..730bc586aea 100644 --- a/packages/cli/src/modules/n8n-packages/entities/workflow/__tests__/auto-included-workflow-resolver.test.ts +++ b/packages/cli/src/modules/n8n-packages/entities/workflow/__tests__/auto-included-workflow-resolver.test.ts @@ -104,6 +104,7 @@ function resolveInput(options: { folderWorkflowIds: options.folderWorkflowIds ?? [], projectWorkflowIds: options.projectWorkflowIds ?? [], requirements: options.requirements, + includeTags: true, }; } @@ -333,7 +334,7 @@ describe('AutoIncludedWorkflowResolver', () => { ['b'], user, ['workflow:export'], - { includeParentFolder: true }, + { includeParentFolder: true, includeTags: true }, ); }); }); diff --git a/packages/cli/src/modules/n8n-packages/entities/workflow/__tests__/auto-included-workflow.exporter.test.ts b/packages/cli/src/modules/n8n-packages/entities/workflow/__tests__/auto-included-workflow.exporter.test.ts index 7e8855af772..6a01d99c70c 100644 --- a/packages/cli/src/modules/n8n-packages/entities/workflow/__tests__/auto-included-workflow.exporter.test.ts +++ b/packages/cli/src/modules/n8n-packages/entities/workflow/__tests__/auto-included-workflow.exporter.test.ts @@ -9,6 +9,7 @@ import { CredentialRequirementsExtractor } from '../../credential/credential-req import { DataTableRequirementsExtractor } from '../../data-table/data-table-requirements.extractor'; import { FolderSerializer } from '../../folder/folder.serializer'; import { ProjectSerializer } from '../../project/project.serializer'; +import { TagRequirementsExtractor } from '../../tag/tag-requirements.extractor'; import { VariableRequirementsExtractor } from '../../variable/variable-requirements.extractor'; import type { AutoIncludedWorkflow } from '../auto-included-workflow-resolver'; import { AutoIncludedWorkflowExporter } from '../auto-included-workflow.exporter'; @@ -59,6 +60,7 @@ function makeExporter( credentialExtractor ?? new CredentialRequirementsExtractor(), dataTableExtractor ?? new DataTableRequirementsExtractor(), variableExtractor ?? new VariableRequirementsExtractor(), + new TagRequirementsExtractor(), ); } @@ -69,6 +71,7 @@ function emptyRequest(writer: CapturingWriter, workflows: AutoIncludedWorkflow[] existingWorkflowEntries: [] as ManifestEntry[], existingFolderEntries: [] as ManifestEntry[], existingProjectEntries: [] as ManifestEntry[], + includeTags: true, }; } diff --git a/packages/cli/src/modules/n8n-packages/entities/workflow/__tests__/sub-workflow-ordering.test.ts b/packages/cli/src/modules/n8n-packages/entities/workflow/__tests__/sub-workflow-ordering.test.ts new file mode 100644 index 00000000000..132e10c3f73 --- /dev/null +++ b/packages/cli/src/modules/n8n-packages/entities/workflow/__tests__/sub-workflow-ordering.test.ts @@ -0,0 +1,81 @@ +import type { PackageWorkflowRequirement } from '../../../spec/requirements.schema'; +import { orderBySubWorkflowDependencies } from '../sub-workflow-ordering'; + +/** Minimal shape the orderer needs; ids double as names for readable assertions. */ +const wf = (sourceWorkflowId: string) => ({ sourceWorkflowId }); + +/** `parent` depends on (calls) each id in `subWorkflowIds`. */ +const requiredBy = (id: string, ...parents: string[]): PackageWorkflowRequirement => ({ + id, + name: id, + usedByWorkflows: parents, +}); + +const idsOf = (workflows: Array<{ sourceWorkflowId: string }>) => + workflows.map((w) => w.sourceWorkflowId); + +describe('orderBySubWorkflowDependencies', () => { + it('returns the input unchanged when there are no requirements', () => { + const workflows = [wf('A'), wf('B')]; + + expect(orderBySubWorkflowDependencies(workflows, undefined)).toBe(workflows); + expect(orderBySubWorkflowDependencies(workflows, [])).toBe(workflows); + }); + + it('places a sub-workflow before the workflow that calls it', () => { + // CHEDDAR (listed first) calls BRIE. + const ordered = orderBySubWorkflowDependencies( + [wf('CHEDDAR'), wf('BRIE')], + [requiredBy('BRIE', 'CHEDDAR')], + ); + + expect(idsOf(ordered)).toEqual(['BRIE', 'CHEDDAR']); + }); + + it('orders a multi-level dependency chain deepest-first', () => { + // A calls B, B calls C. + const ordered = orderBySubWorkflowDependencies( + [wf('A'), wf('B'), wf('C')], + [requiredBy('B', 'A'), requiredBy('C', 'B')], + ); + + expect(idsOf(ordered)).toEqual(['C', 'B', 'A']); + }); + + it('places a shared dependency before every workflow that calls it', () => { + // A calls B and C; B and C both call D. + const ordered = orderBySubWorkflowDependencies( + [wf('A'), wf('B'), wf('C'), wf('D')], + [requiredBy('B', 'A'), requiredBy('C', 'A'), requiredBy('D', 'B', 'C')], + ); + + const positions = idsOf(ordered); + expect(positions.indexOf('D')).toBeLessThan(positions.indexOf('B')); + expect(positions.indexOf('D')).toBeLessThan(positions.indexOf('C')); + expect(positions.indexOf('B')).toBeLessThan(positions.indexOf('A')); + expect(positions.indexOf('C')).toBeLessThan(positions.indexOf('A')); + }); + + it('tolerates a workflow that calls itself', () => { + const ordered = orderBySubWorkflowDependencies([wf('BRIE')], [requiredBy('BRIE', 'BRIE')]); + + expect(idsOf(ordered)).toEqual(['BRIE']); + }); + + it('tolerates a mutual cycle without looping or dropping workflows', () => { + // A calls B and B calls A. + const ordered = orderBySubWorkflowDependencies( + [wf('A'), wf('B')], + [requiredBy('B', 'A'), requiredBy('A', 'B')], + ); + + expect(idsOf(ordered).sort()).toEqual(['A', 'B']); + }); + + it('ignores requirements for workflows outside the batch', () => { + // A depends on GOUDA, which is not part of this import. + const ordered = orderBySubWorkflowDependencies([wf('A'), wf('B')], [requiredBy('GOUDA', 'A')]); + + expect(idsOf(ordered)).toEqual(['A', 'B']); + }); +}); diff --git a/packages/cli/src/modules/n8n-packages/entities/workflow/__tests__/workflow-dependency-resolver.test.ts b/packages/cli/src/modules/n8n-packages/entities/workflow/__tests__/workflow-dependency-resolver.test.ts index e010e729e74..25dad312ca2 100644 --- a/packages/cli/src/modules/n8n-packages/entities/workflow/__tests__/workflow-dependency-resolver.test.ts +++ b/packages/cli/src/modules/n8n-packages/entities/workflow/__tests__/workflow-dependency-resolver.test.ts @@ -5,7 +5,6 @@ import { mock } from 'vitest-mock-extended'; import type { WorkflowFinderService } from '@/workflows/workflow-finder.service'; import { WorkflowDependencyResolver } from '../workflow-dependency-resolver'; -import { WorkflowRequirementsExtractor } from '../workflow-requirements.extractor'; const user = mock({ id: 'user-1' }); @@ -37,7 +36,7 @@ function makeResolver(workflows: WorkflowEntity[]) { ); return { - resolver: new WorkflowDependencyResolver(workflowFinder, new WorkflowRequirementsExtractor()), + resolver: new WorkflowDependencyResolver(workflowFinder), workflowFinder, }; } @@ -127,4 +126,44 @@ describe('WorkflowDependencyResolver', () => { ['workflow:export'], ); }); + + describe('direct traversal', () => { + it('extracts only the references of the requested workflows without fetching the referenced ones', async () => { + const { resolver, workflowFinder } = makeResolver([ + makeWorkflow('workflow-a', 'workflow-b'), + makeWorkflow('workflow-b', 'workflow-c'), + makeWorkflow('workflow-c'), + ]); + + const requirements = await resolver.resolve({ + user, + workflowIds: ['workflow-a'], + traversal: 'direct', + }); + + expect(requirements).toEqual([ + { workflowId: 'workflow-a', referencedWorkflowId: 'workflow-b' }, + ]); + expect(workflowFinder.findWorkflowsByIdsForUser).toHaveBeenCalledTimes(1); + }); + + it('extracts the references of every requested workflow, including ones between them', async () => { + const { resolver, workflowFinder } = makeResolver([ + makeWorkflow('workflow-a', 'workflow-b'), + makeWorkflow('workflow-b', 'workflow-c'), + ]); + + const requirements = await resolver.resolve({ + user, + workflowIds: ['workflow-a', 'workflow-b'], + traversal: 'direct', + }); + + expect(requirements).toEqual([ + { workflowId: 'workflow-a', referencedWorkflowId: 'workflow-b' }, + { workflowId: 'workflow-b', referencedWorkflowId: 'workflow-c' }, + ]); + expect(workflowFinder.findWorkflowsByIdsForUser).toHaveBeenCalledTimes(1); + }); + }); }); diff --git a/packages/cli/src/modules/n8n-packages/entities/workflow/__tests__/workflow-publisher.test.ts b/packages/cli/src/modules/n8n-packages/entities/workflow/__tests__/workflow-publisher.test.ts index 77911d5f136..dbf37a97d06 100644 --- a/packages/cli/src/modules/n8n-packages/entities/workflow/__tests__/workflow-publisher.test.ts +++ b/packages/cli/src/modules/n8n-packages/entities/workflow/__tests__/workflow-publisher.test.ts @@ -8,7 +8,7 @@ import { NotFoundError } from '@/errors/response-errors/not-found.error'; import type { ProjectService } from '@/services/project.service.ee'; import type { WorkflowService } from '@/workflows/workflow.service'; -import type { PersistedWorkflowPlanItem } from '../workflow-import.types'; +import type { PersistedWorkflowOutcome, PersistedWorkflowPlanItem } from '../workflow-import.types'; import { WorkflowPublisher } from '../workflow-publisher'; import { WorkflowPublishingPolicy } from '../workflow-publishing-policy.types'; @@ -102,7 +102,6 @@ describe('WorkflowPublisher', () => { createItem(false), workflow, WorkflowPublishingPolicy.PreservePublishedState, - new Map(), ); expect(result.workflow).toBe(workflow); @@ -126,7 +125,6 @@ describe('WorkflowPublisher', () => { createItem(true), workflow, WorkflowPublishingPolicy.PublishAll, - new Map(), ); expect(workflowService.activateWorkflow).toHaveBeenCalledWith(user, 'wf-1', { @@ -151,7 +149,6 @@ describe('WorkflowPublisher', () => { createItem(true), workflow, WorkflowPublishingPolicy.PublishAll, - new Map(), ); expect(result.workflow).toBe(workflow); @@ -189,7 +186,7 @@ describe('WorkflowPublisher', () => { updateItem, workflow, WorkflowPublishingPolicy.MatchSource, - new Map([['wf-stubbed', 'stub-credential']]), + 'stub-credential', ); expect(workflowService.activateWorkflow).not.toHaveBeenCalled(); @@ -213,7 +210,7 @@ describe('WorkflowPublisher', () => { createItem(true), workflow, WorkflowPublishingPolicy.PublishAll, - new Map([['wf-1', 'stub-credential']]), + 'stub-credential', ); expect(workflowService.activateWorkflow).not.toHaveBeenCalled(); @@ -246,7 +243,7 @@ describe('WorkflowPublisher', () => { updateItem, workflow, WorkflowPublishingPolicy.PreservePublishedState, - new Map([['wf-stubbed', 'stub-credential']]), + 'stub-credential', ); expect(workflowService.activateWorkflow).not.toHaveBeenCalled(); @@ -281,7 +278,7 @@ describe('WorkflowPublisher', () => { updateItem, workflow, WorkflowPublishingPolicy.UnpublishAll, - new Map([['wf-broken', 'missing-node-type']]), + 'missing-node-type', ); expect(workflowService.activateWorkflow).not.toHaveBeenCalled(); @@ -292,4 +289,81 @@ describe('WorkflowPublisher', () => { expect(result.publishing).toEqual({ state: 'unpublished' }); }); }); + describe('applyToPackage', () => { + const persisted = (sourceWorkflowId: string): PersistedWorkflowOutcome => ({ + status: 'created', + sourceWorkflowId, + workflow: mock({ + id: `local-${sourceWorkflowId}`, + versionId: 'v1', + activeVersionId: null, + isArchived: false, + }), + item: { + action: 'create', + sourceWorkflowId, + decidedId: `local-${sourceWorkflowId}`, + sourcePublished: false, + parentFolderId: null, + entity: mock(), + }, + }); + + /** Source workflow ids in the order the publisher activated them. */ + const activationOrder = () => + workflowService.activateWorkflow.mock.calls.map(([, workflowId]) => + String(workflowId).replace('local-', ''), + ); + + beforeEach(() => { + workflowService.activateWorkflow.mockImplementation(async (_user, workflowId) => + mock({ id: String(workflowId) }), + ); + }); + + it('publishes a sub-workflow before the workflow that calls it', async () => { + // CHEDDAR is written first but calls BRIE, so BRIE must be published first — activation + // rejects a parent whose referenced sub-workflow is not yet published. + const published = await publisher.applyToPackage({ + user, + persisted: [persisted('CHEDDAR'), persisted('BRIE')], + policy: WorkflowPublishingPolicy.PublishAll, + subWorkflowRequirements: [{ id: 'BRIE', name: 'BRIE', usedByWorkflows: ['CHEDDAR'] }], + }); + + expect(activationOrder()).toEqual(['BRIE', 'CHEDDAR']); + expect(published.get('BRIE')?.publishing).toEqual({ state: 'published' }); + expect(published.get('CHEDDAR')?.publishing).toEqual({ state: 'published' }); + }); + + it('keeps written order when no sub-workflow dependencies are declared', async () => { + await publisher.applyToPackage({ + user, + persisted: [persisted('CHEDDAR'), persisted('BRIE')], + policy: WorkflowPublishingPolicy.PublishAll, + subWorkflowRequirements: undefined, + }); + + expect(activationOrder()).toEqual(['CHEDDAR', 'BRIE']); + }); + + it('never publishes a skipped workflow and reports it as unchanged', async () => { + const published = await publisher.applyToPackage({ + user, + persisted: [ + { + status: 'skipped' as const, + sourceWorkflowId: 'BRIE', + workflow: mock({ id: 'local-BRIE' }), + }, + ], + policy: WorkflowPublishingPolicy.PublishAll, + subWorkflowRequirements: undefined, + }); + + expect(workflowService.activateWorkflow).not.toHaveBeenCalled(); + // Absent from the map; callers report `unchanged` for anything the phase left alone. + expect(published.has('BRIE')).toBe(false); + }); + }); }); diff --git a/packages/cli/src/modules/n8n-packages/entities/workflow/__tests__/workflow-requirement.exporter.test.ts b/packages/cli/src/modules/n8n-packages/entities/workflow/__tests__/workflow-requirement.exporter.test.ts index 39b872b7d72..58be94a1c0e 100644 --- a/packages/cli/src/modules/n8n-packages/entities/workflow/__tests__/workflow-requirement.exporter.test.ts +++ b/packages/cli/src/modules/n8n-packages/entities/workflow/__tests__/workflow-requirement.exporter.test.ts @@ -1,15 +1,33 @@ +import type { User, WorkflowEntity } from '@n8n/db'; +import { mock } from 'vitest-mock-extended'; + +import type { WorkflowFinderService } from '@/workflows/workflow-finder.service'; + import type { ManifestEntry } from '../../../spec/manifest.schema'; import { WorkflowRequirementExporter } from '../workflow-requirement.exporter'; +const user = mock({ id: 'user-1' }); + function workflowEntry(id: string, name: string): ManifestEntry { return { id, name, target: `workflows/${id}` }; } -describe('WorkflowRequirementExporter', () => { - it('groups requirements by referenced workflow and aggregates usedByWorkflows', () => { - const exporter = new WorkflowRequirementExporter(); +function makeExporter(accessibleWorkflows: Array<{ id: string; name: string }> = []) { + const workflowFinder = mock(); + workflowFinder.findWorkflowsByIdsForUser.mockImplementation( + async (workflowIds) => + accessibleWorkflows.filter(({ id }) => workflowIds.includes(id)) as WorkflowEntity[], + ); - const result = exporter.export({ + return { exporter: new WorkflowRequirementExporter(workflowFinder), workflowFinder }; +} + +describe('WorkflowRequirementExporter', () => { + it('groups requirements by referenced workflow and aggregates usedByWorkflows', async () => { + const { exporter, workflowFinder } = makeExporter(); + + const result = await exporter.export({ + user, requirements: [ { workflowId: 'wf-parent-a', referencedWorkflowId: 'wf-child' }, { workflowId: 'wf-parent-a', referencedWorkflowId: 'wf-child' }, @@ -36,16 +54,44 @@ describe('WorkflowRequirementExporter', () => { usedByWorkflows: ['wf-child'], }, ]); + // Every referenced workflow is in the package, so no name lookup is needed. + expect(workflowFinder.findWorkflowsByIdsForUser).not.toHaveBeenCalled(); }); - it('returns no package requirements when there are no workflow requirements', () => { - const exporter = new WorkflowRequirementExporter(); + it('fetches best-effort names for referenced workflows not in the package', async () => { + const { exporter, workflowFinder } = makeExporter([{ id: 'wf-known', name: 'Known Child' }]); - const result = exporter.export({ + const result = await exporter.export({ + user, + requirements: [ + { workflowId: 'wf-parent', referencedWorkflowId: 'wf-known' }, + { workflowId: 'wf-parent', referencedWorkflowId: 'wf-unknown' }, + ], + workflows: [workflowEntry('wf-parent', 'Parent')], + }); + + expect(result.requirements).toEqual([ + { id: 'wf-known', name: 'Known Child', usedByWorkflows: ['wf-parent'] }, + { id: 'wf-unknown', usedByWorkflows: ['wf-parent'] }, + ]); + expect(result.requirements[1]).not.toHaveProperty('name'); + expect(workflowFinder.findWorkflowsByIdsForUser).toHaveBeenCalledWith( + ['wf-known', 'wf-unknown'], + user, + ['workflow:export'], + ); + }); + + it('returns no package requirements when there are no workflow requirements', async () => { + const { exporter, workflowFinder } = makeExporter(); + + const result = await exporter.export({ + user, requirements: [], workflows: [workflowEntry('wf-parent', 'Parent')], }); expect(result.requirements).toEqual([]); + expect(workflowFinder.findWorkflowsByIdsForUser).not.toHaveBeenCalled(); }); }); diff --git a/packages/cli/src/modules/n8n-packages/entities/workflow/__tests__/workflow.exporter.test.ts b/packages/cli/src/modules/n8n-packages/entities/workflow/__tests__/workflow.exporter.test.ts index a0e3fee43d2..50b4acd7e75 100644 --- a/packages/cli/src/modules/n8n-packages/entities/workflow/__tests__/workflow.exporter.test.ts +++ b/packages/cli/src/modules/n8n-packages/entities/workflow/__tests__/workflow.exporter.test.ts @@ -14,6 +14,7 @@ import { PackageEntityAccessDeniedError, PackageEntityNotFoundError, } from '../../package-export.errors'; +import { TagRequirementsExtractor } from '../../tag/tag-requirements.extractor'; import { VariableRequirementsExtractor } from '../../variable/variable-requirements.extractor'; import type { WorkflowVariableRequirement } from '../../variable/variable.types'; import { WorkflowExporter } from '../workflow.exporter'; @@ -51,6 +52,7 @@ function makeExporter( credentialExtractor ?? new CredentialRequirementsExtractor(), dataTableExtractor ?? new DataTableRequirementsExtractor(), variableExtractor ?? new VariableRequirementsExtractor(), + new TagRequirementsExtractor(), ); return { exporter, finder }; } @@ -61,13 +63,13 @@ describe('WorkflowExporter', () => { const { exporter, finder } = makeExporter([workflow]); const writer = new CapturingWriter(); - await exporter.export({ user, workflowIds: [workflow.id], writer }); + await exporter.export({ user, workflowIds: [workflow.id], writer, includeTags: true }); expect(finder.findWorkflowsByIdsForUser).toHaveBeenCalledWith( [workflow.id], user, ['workflow:export'], - { includeParentFolder: true }, + { includeParentFolder: true, includeTags: true }, ); }); @@ -81,6 +83,7 @@ describe('WorkflowExporter', () => { user, workflowIds: ['present-1', 'missing-or-denied'], writer, + includeTags: true, }), ).rejects.toThrow('1 workflow(s) not found or not accessible. Export aborted.'); }); @@ -92,7 +95,7 @@ describe('WorkflowExporter', () => { const writer = new CapturingWriter(); await expect( - exporter.export({ user, workflowIds: ['present-1', 'missing'], writer }), + exporter.export({ user, workflowIds: ['present-1', 'missing'], writer, includeTags: true }), ).rejects.toBeInstanceOf(PackageEntityNotFoundError); }); @@ -103,7 +106,7 @@ describe('WorkflowExporter', () => { const writer = new CapturingWriter(); await expect( - exporter.export({ user, workflowIds: ['present-1', 'denied-1'], writer }), + exporter.export({ user, workflowIds: ['present-1', 'denied-1'], writer, includeTags: true }), ).rejects.toBeInstanceOf(PackageEntityAccessDeniedError); }); @@ -113,7 +116,7 @@ describe('WorkflowExporter', () => { const writer = new CapturingWriter(); await expect( - exporter.export({ user, workflowIds: ['present-1', 'missing'], writer }), + exporter.export({ user, workflowIds: ['present-1', 'missing'], writer, includeTags: true }), ).rejects.toThrow(); expect(finder.findExistingWorkflowIds).toHaveBeenCalledWith(['missing']); @@ -128,6 +131,7 @@ describe('WorkflowExporter', () => { user, workflowIds: [workflow.id, workflow.id], writer, + includeTags: true, }); expect(entries).toEqual([ @@ -148,6 +152,7 @@ describe('WorkflowExporter', () => { user, workflowIds: [a.id, b.id], writer, + includeTags: true, }); expect(entries.map(({ id }) => id)).toEqual([a.id, b.id]); @@ -176,7 +181,7 @@ describe('WorkflowExporter', () => { const { exporter } = makeExporter([workflow]); const writer = new CapturingWriter(); - await exporter.export({ user, workflowIds: [workflow.id], writer }); + await exporter.export({ user, workflowIds: [workflow.id], writer, includeTags: true }); const workflowFile = writer.files.find((f) => f.path === 'workflows/my-workflow/workflow.json'); expect(workflowFile).toBeDefined(); @@ -202,6 +207,7 @@ describe('WorkflowExporter', () => { user, workflowIds: [workflow.id], writer, + includeTags: true, basePrefix: 'folders/in_progress', }); @@ -217,7 +223,12 @@ describe('WorkflowExporter', () => { const { exporter } = makeExporter([a, b]); const writer = new CapturingWriter(); - const { entries } = await exporter.export({ user, workflowIds: [a.id, b.id], writer }); + const { entries } = await exporter.export({ + user, + workflowIds: [a.id, b.id], + writer, + includeTags: true, + }); const targets = entries.map((e) => e.target); expect(targets).toEqual(['workflows/same-name', 'workflows/same-name-2']); @@ -248,6 +259,7 @@ describe('WorkflowExporter', () => { user, workflowIds: [a.id, b.id], writer, + includeTags: true, }); expect(extractor.extract).toHaveBeenCalledTimes(2); @@ -281,6 +293,7 @@ describe('WorkflowExporter', () => { user, workflowIds: [a.id, b.id], writer, + includeTags: true, }); expect(extractor.extract).toHaveBeenCalledTimes(2); @@ -304,6 +317,7 @@ describe('WorkflowExporter', () => { user, workflowIds: [a.id, b.id], writer, + includeTags: true, }); expect(extractor.extract).toHaveBeenCalledTimes(2); @@ -331,6 +345,7 @@ describe('WorkflowExporter', () => { user, workflowIds: [a.id, b.id], writer, + includeTags: true, }); expect(requirements.nodeTypes).toEqual([ diff --git a/packages/cli/src/modules/n8n-packages/entities/workflow/auto-included-workflow-resolver.ts b/packages/cli/src/modules/n8n-packages/entities/workflow/auto-included-workflow-resolver.ts index d223e28169d..c4a55f0b7a9 100644 --- a/packages/cli/src/modules/n8n-packages/entities/workflow/auto-included-workflow-resolver.ts +++ b/packages/cli/src/modules/n8n-packages/entities/workflow/auto-included-workflow-resolver.ts @@ -40,6 +40,7 @@ export class AutoIncludedWorkflowResolver { topLevelWorkflowIds: string[]; folderWorkflowIds: string[]; projectWorkflowIds: string[]; + includeTags: boolean; }): Promise { const originsByWorkflowId = this.seedExportedOrigins({ topLevelWorkflowIds: options.topLevelWorkflowIds, @@ -58,6 +59,7 @@ export class AutoIncludedWorkflowResolver { user: options.user, workflowIds: autoIncludedWorkflowIds, originsByWorkflowId, + includeTags: options.includeTags, }); return { autoIncludedWorkflows }; @@ -145,10 +147,15 @@ export class AutoIncludedWorkflowResolver { user: User; workflowIds: string[]; originsByWorkflowId: Map>; + includeTags: boolean; }): Promise { if (options.workflowIds.length === 0) return []; - const workflows = await this.findExportableWorkflows(options.user, options.workflowIds); + const workflows = await this.findExportableWorkflows( + options.user, + options.workflowIds, + options.includeTags, + ); const workflowsById = new Map(workflows.map((workflow) => [workflow.id, workflow])); const ownersByWorkflowId = await this.sharedWorkflowRepository.findOwnerProjectsByWorkflowIds( options.workflowIds, @@ -221,12 +228,13 @@ export class AutoIncludedWorkflowResolver { private async findExportableWorkflows( user: User, workflowIds: string[], + includeTags: boolean, ): Promise { const workflows = await this.workflowFinder.findWorkflowsByIdsForUser( workflowIds, user, ['workflow:export'], - { includeParentFolder: true }, + { includeParentFolder: true, includeTags }, ); await assertEveryRequestedEntityAccessible( diff --git a/packages/cli/src/modules/n8n-packages/entities/workflow/auto-included-workflow.exporter.ts b/packages/cli/src/modules/n8n-packages/entities/workflow/auto-included-workflow.exporter.ts index 4dccc2a0dfe..0a4d07848c4 100644 --- a/packages/cli/src/modules/n8n-packages/entities/workflow/auto-included-workflow.exporter.ts +++ b/packages/cli/src/modules/n8n-packages/entities/workflow/auto-included-workflow.exporter.ts @@ -15,6 +15,8 @@ import type { WorkflowDataTableRequirement } from '../data-table/data-table.type import { FolderSerializer } from '../folder/folder.serializer'; import { ProjectSerializer } from '../project/project.serializer'; import type { WorkflowExportRequirements } from '../requirements.types'; +import { TagRequirementsExtractor } from '../tag/tag-requirements.extractor'; +import type { WorkflowTagUsage } from '../tag/tag.types'; import { VariableRequirementsExtractor } from '../variable/variable-requirements.extractor'; import type { WorkflowVariableRequirement } from '../variable/variable.types'; @@ -24,6 +26,7 @@ export interface AutoIncludedWorkflowExportRequest { existingWorkflowEntries: ManifestEntry[]; existingFolderEntries: ManifestEntry[]; existingProjectEntries: ManifestEntry[]; + includeTags: boolean; projectTargetsById?: Map; } @@ -54,6 +57,7 @@ export class AutoIncludedWorkflowExporter { private readonly credentialRequirementsExtractor: CredentialRequirementsExtractor, private readonly dataTableRequirementsExtractor: DataTableRequirementsExtractor, private readonly variableRequirementsExtractor: VariableRequirementsExtractor, + private readonly tagRequirementsExtractor: TagRequirementsExtractor, ) {} export(request: AutoIncludedWorkflowExportRequest): AutoIncludedWorkflowExportResult { @@ -93,6 +97,7 @@ export class AutoIncludedWorkflowExporter { const credentials: WorkflowCredentialRequirement[] = []; const dataTables: WorkflowDataTableRequirement[] = []; const variables: WorkflowVariableRequirement[] = []; + const tags: WorkflowTagUsage[] = []; const nodeTypes: WorkflowNodeTypeSource[] = []; for (const included of request.workflows) { @@ -113,12 +118,14 @@ export class AutoIncludedWorkflowExporter { baseDir, request.writer, allocators.workflows, + request.includeTags, ); workflowEntries.push(entry); workflowEntriesById.set(entry.id, entry); credentials.push(...this.credentialRequirementsExtractor.extract(included.workflow)); dataTables.push(...this.dataTableRequirementsExtractor.extract(included.workflow)); variables.push(...this.variableRequirementsExtractor.extract(included.workflow)); + tags.push(...this.tagRequirementsExtractor.extract(included.workflow)); nodeTypes.push({ workflowId: included.workflow.id, nodes: included.workflow.nodes ?? [], @@ -129,7 +136,7 @@ export class AutoIncludedWorkflowExporter { workflowEntries, folderEntries, projectEntries, - requirements: { credentials, dataTables, variables, nodeTypes }, + requirements: { credentials, dataTables, variables, tags, nodeTypes }, projectTargetsById, }; } @@ -261,9 +268,10 @@ export class AutoIncludedWorkflowExporter { baseDir: string, writer: PackageWriter, allocators: Map, + includeTags: boolean, ): ManifestEntry { const target = allocatorFor(allocators, baseDir, 'workflow').allocate(workflow.name); - const serialized = this.workflowSerializer.serialize(workflow); + const serialized = this.workflowSerializer.serialize(workflow, { includeTags }); writer.writeDirectory(target); writer.writeFile(`${target}/workflow.json`, JSON.stringify(serialized, null, '\t')); return { id: workflow.id, name: workflow.name, target }; diff --git a/packages/cli/src/modules/n8n-packages/entities/workflow/references/__tests__/caller-ids.reference.test.ts b/packages/cli/src/modules/n8n-packages/entities/workflow/references/__tests__/caller-ids.reference.test.ts new file mode 100644 index 00000000000..6928155ba26 --- /dev/null +++ b/packages/cli/src/modules/n8n-packages/entities/workflow/references/__tests__/caller-ids.reference.test.ts @@ -0,0 +1,37 @@ +import type { WorkflowEntity } from '@n8n/db'; + +import type { PackageImportBindings } from '../../../../n8n-packages.types'; +import { callerIdsReference } from '../caller-ids.reference'; + +const workflow = (callerIds: unknown): WorkflowEntity => + ({ id: 'parent', settings: { callerIds } }) as WorkflowEntity; +const bindings = (workflows: Map): PackageImportBindings => ({ + workflows, + credentials: new Map(), +}); + +describe('callerIdsReference', () => { + it('contributes no requirements (an allowlist is not a dependency)', () => { + expect(callerIdsReference.extract(workflow('a,b'))).toEqual([]); + }); + + it('remaps known caller ids, trims whitespace, and keeps unknown ids', () => { + const wf = workflow('a, b ,external'); + callerIdsReference.apply( + wf, + bindings( + new Map([ + ['a', 'A'], + ['b', 'B'], + ]), + ), + ); + expect(wf.settings?.callerIds).toBe('A,B,external'); + }); + + it('ignores a non-string callerIds', () => { + const wf = workflow(42); + callerIdsReference.apply(wf, bindings(new Map())); + expect(wf.settings?.callerIds).toBe(42); + }); +}); diff --git a/packages/cli/src/modules/n8n-packages/entities/workflow/references/__tests__/error-workflow.reference.test.ts b/packages/cli/src/modules/n8n-packages/entities/workflow/references/__tests__/error-workflow.reference.test.ts new file mode 100644 index 00000000000..644030afbf3 --- /dev/null +++ b/packages/cli/src/modules/n8n-packages/entities/workflow/references/__tests__/error-workflow.reference.test.ts @@ -0,0 +1,51 @@ +import type { WorkflowEntity } from '@n8n/db'; + +import type { PackageImportBindings } from '../../../../n8n-packages.types'; +import { errorWorkflowReference } from '../error-workflow.reference'; + +const workflow = (errorWorkflow: unknown, id = 'parent'): WorkflowEntity => + ({ id, settings: { errorWorkflow } }) as WorkflowEntity; +const bindings = (workflows: Map): PackageImportBindings => ({ + workflows, + credentials: new Map(), +}); + +describe('errorWorkflowReference', () => { + describe('extract', () => { + it('returns the static errorWorkflow id as a requirement', () => { + expect(errorWorkflowReference.extract(workflow('child'))).toEqual([ + { workflowId: 'parent', referencedWorkflowId: 'child' }, + ]); + }); + + it('ignores DEFAULT and expressions', () => { + expect(errorWorkflowReference.extract(workflow('DEFAULT'))).toEqual([]); + expect(errorWorkflowReference.extract(workflow('={{ $vars.ERROR_WF }}'))).toEqual([]); + }); + + it('ignores a missing or non-string errorWorkflow', () => { + expect( + errorWorkflowReference.extract({ id: 'parent', settings: {} } as WorkflowEntity), + ).toEqual([]); + expect(errorWorkflowReference.extract(workflow(42))).toEqual([]); + }); + }); + + describe('apply', () => { + it('remaps a known errorWorkflow id and keeps an unknown one', () => { + const known = workflow('child'); + errorWorkflowReference.apply(known, bindings(new Map([['child', 'C']]))); + expect(known.settings?.errorWorkflow).toBe('C'); + + const unknown = workflow('external'); + errorWorkflowReference.apply(unknown, bindings(new Map([['child', 'C']]))); + expect(unknown.settings?.errorWorkflow).toBe('external'); + }); + + it('leaves the DEFAULT sentinel untouched', () => { + const wf = workflow('DEFAULT'); + errorWorkflowReference.apply(wf, bindings(new Map([['DEFAULT', 'X']]))); + expect(wf.settings?.errorWorkflow).toBe('DEFAULT'); + }); + }); +}); diff --git a/packages/cli/src/modules/n8n-packages/entities/workflow/__tests__/workflow-requirements.extractor.test.ts b/packages/cli/src/modules/n8n-packages/entities/workflow/references/__tests__/extract-workflow-requirements.test.ts similarity index 75% rename from packages/cli/src/modules/n8n-packages/entities/workflow/__tests__/workflow-requirements.extractor.test.ts rename to packages/cli/src/modules/n8n-packages/entities/workflow/references/__tests__/extract-workflow-requirements.test.ts index a7ad0ddce3e..babde70898b 100644 --- a/packages/cli/src/modules/n8n-packages/entities/workflow/__tests__/workflow-requirements.extractor.test.ts +++ b/packages/cli/src/modules/n8n-packages/entities/workflow/references/__tests__/extract-workflow-requirements.test.ts @@ -1,7 +1,7 @@ import type { WorkflowEntity } from '@n8n/db'; import type { INode } from 'n8n-workflow'; -import { WorkflowRequirementsExtractor } from '../workflow-requirements.extractor'; +import { extractWorkflowRequirements } from '../extract-workflow-requirements'; function makeWorkflow(nodes: INode[]): WorkflowEntity { return { @@ -33,15 +33,13 @@ function executeWorkflowNode( }; } -describe('WorkflowRequirementsExtractor', () => { - const extractor = new WorkflowRequirementsExtractor(); - +describe('extractWorkflowRequirements', () => { it('extracts static Execute Workflow references', () => { const workflow = makeWorkflow([ executeWorkflowNode({ __rl: true, mode: 'list', value: 'wf-child' }), ]); - expect(extractor.extract(workflow)).toEqual([ + expect(extractWorkflowRequirements(workflow)).toEqual([ { workflowId: 'wf-parent', referencedWorkflowId: 'wf-child' }, ]); }); @@ -58,7 +56,7 @@ describe('WorkflowRequirementsExtractor', () => { ), ]); - expect(extractor.extract(workflow)).toEqual([ + expect(extractWorkflowRequirements(workflow)).toEqual([ { workflowId: 'wf-parent', referencedWorkflowId: 'wf-child' }, ]); }); @@ -69,7 +67,7 @@ describe('WorkflowRequirementsExtractor', () => { executeWorkflowNode({ __rl: true, mode: 'list', value: 'wf-child' }, { id: 'node-2' }), ]); - expect(extractor.extract(workflow)).toEqual([ + expect(extractWorkflowRequirements(workflow)).toEqual([ { workflowId: 'wf-parent', referencedWorkflowId: 'wf-child' }, ]); }); @@ -77,7 +75,7 @@ describe('WorkflowRequirementsExtractor', () => { it('extracts legacy plain-string Execute Workflow references', () => { const workflow = makeWorkflow([executeWorkflowNode('wf-child')]); - expect(extractor.extract(workflow)).toEqual([ + expect(extractWorkflowRequirements(workflow)).toEqual([ { workflowId: 'wf-parent', referencedWorkflowId: 'wf-child' }, ]); }); @@ -85,22 +83,26 @@ describe('WorkflowRequirementsExtractor', () => { it('extracts error workflow references', () => { const workflow = makeWorkflowWithSettings({ errorWorkflow: 'wf-error-handler' }); - expect(extractor.extract(workflow)).toEqual([ + expect(extractWorkflowRequirements(workflow)).toEqual([ { workflowId: 'wf-parent', referencedWorkflowId: 'wf-error-handler' }, ]); }); it('ignores default and empty error workflow settings', () => { - expect(extractor.extract(makeWorkflowWithSettings({ errorWorkflow: 'DEFAULT' }))).toEqual([]); - expect(extractor.extract(makeWorkflowWithSettings({ errorWorkflow: '' }))).toEqual([]); - expect(extractor.extract(makeWorkflowWithSettings({}))).toEqual([]); + expect( + extractWorkflowRequirements(makeWorkflowWithSettings({ errorWorkflow: 'DEFAULT' })), + ).toEqual([]); + expect(extractWorkflowRequirements(makeWorkflowWithSettings({ errorWorkflow: '' }))).toEqual( + [], + ); + expect(extractWorkflowRequirements(makeWorkflowWithSettings({}))).toEqual([]); }); it('ignores expression-based error workflow settings', () => { - // An expression resolves at runtime (often via a variable) so it is not a - // concrete workflow dependency; the variable extractor owns that case. + // An expression resolves at runtime (often via a variable) so it is not a concrete + // workflow dependency; the variable extractor owns that case. expect( - extractor.extract( + extractWorkflowRequirements( makeWorkflowWithSettings({ errorWorkflow: '={{ $vars.ERROR_WORKFLOW_ID }}' }), ), ).toEqual([]); @@ -114,7 +116,7 @@ describe('WorkflowRequirementsExtractor', () => { settings: { errorWorkflow: 'wf-child' }, } as WorkflowEntity; - expect(extractor.extract(workflow)).toEqual([ + expect(extractWorkflowRequirements(workflow)).toEqual([ { workflowId: 'wf-parent', referencedWorkflowId: 'wf-child' }, ]); }); @@ -122,7 +124,7 @@ describe('WorkflowRequirementsExtractor', () => { it('ignores dynamic workflow selectors', () => { const workflow = makeWorkflow([executeWorkflowNode('={{ $json.workflowId }}')]); - expect(extractor.extract(workflow)).toEqual([]); + expect(extractWorkflowRequirements(workflow)).toEqual([]); }); it('ignores a resource-locator in expression mode', () => { @@ -130,7 +132,7 @@ describe('WorkflowRequirementsExtractor', () => { executeWorkflowNode({ __rl: true, mode: 'id', value: '={{ $json.workflowId }}' }), ]); - expect(extractor.extract(workflow)).toEqual([]); + expect(extractWorkflowRequirements(workflow)).toEqual([]); }); it('ignores non Execute Workflow nodes', () => { @@ -141,6 +143,6 @@ describe('WorkflowRequirementsExtractor', () => { ), ]); - expect(extractor.extract(workflow)).toEqual([]); + expect(extractWorkflowRequirements(workflow)).toEqual([]); }); }); diff --git a/packages/cli/src/modules/n8n-packages/entities/workflow/__tests__/static-sub-workflow-id.test.ts b/packages/cli/src/modules/n8n-packages/entities/workflow/references/__tests__/sub-workflow-node.reference.test.ts similarity index 63% rename from packages/cli/src/modules/n8n-packages/entities/workflow/__tests__/static-sub-workflow-id.test.ts rename to packages/cli/src/modules/n8n-packages/entities/workflow/references/__tests__/sub-workflow-node.reference.test.ts index 3a03d62d446..2b0f6bd245c 100644 --- a/packages/cli/src/modules/n8n-packages/entities/workflow/__tests__/static-sub-workflow-id.test.ts +++ b/packages/cli/src/modules/n8n-packages/entities/workflow/references/__tests__/sub-workflow-node.reference.test.ts @@ -1,6 +1,12 @@ +import type { WorkflowEntity } from '@n8n/db'; import type { INode } from 'n8n-workflow'; -import { getStaticSubworkflowId } from '../static-sub-workflow-id'; +import type { PackageImportBindings } from '../../../../n8n-packages.types'; +import { + getStaticSubworkflowId, + setStaticSubworkflowId, + subWorkflowNodeReference, +} from '../sub-workflow-node.reference'; function node( type: string, @@ -25,6 +31,13 @@ const RETRIEVER_WORKFLOW = '@n8n/n8n-nodes-langchain.retrieverWorkflow'; const resourceLocator = (value: string, mode = 'list') => ({ __rl: true, mode, value }); +const workflow = (nodes: INode[], id = 'parent'): WorkflowEntity => + ({ id, nodes }) as WorkflowEntity; +const bindings = (workflows: Map): PackageImportBindings => ({ + workflows, + credentials: new Map(), +}); + describe('getStaticSubworkflowId', () => { describe('Execute Sub-workflow', () => { it('resolves the legacy v1 plain-string workflowId', () => { @@ -108,9 +121,7 @@ describe('getStaticSubworkflowId', () => { it('ignores a resource-locator in expression mode', () => { expect( getStaticSubworkflowId( - node(EXECUTE_WORKFLOW, 1.2, { - workflowId: resourceLocator('={{ $json.id }}', 'id'), - }), + node(EXECUTE_WORKFLOW, 1.2, { workflowId: resourceLocator('={{ $json.id }}', 'id') }), ), ).toBeUndefined(); }); @@ -150,3 +161,56 @@ describe('getStaticSubworkflowId', () => { ).toBeUndefined(); }); }); + +describe('setStaticSubworkflowId', () => { + it('writes back to a resource-locator value', () => { + const n = node(EXECUTE_WORKFLOW, 1.2, { workflowId: resourceLocator('wf-old', 'id') }); + setStaticSubworkflowId(n, 'wf-new'); + expect(n.parameters.workflowId).toEqual({ __rl: true, mode: 'id', value: 'wf-new' }); + expect(getStaticSubworkflowId(n)).toBe('wf-new'); + }); + + it('writes back to a legacy plain-string workflowId', () => { + const n = node(EXECUTE_WORKFLOW, 1, { workflowId: 'wf-old' }); + setStaticSubworkflowId(n, 'wf-new'); + expect(n.parameters.workflowId).toBe('wf-new'); + }); +}); + +describe('subWorkflowNodeReference', () => { + it('extracts referenced ids from selector nodes as requirements', () => { + const wf = workflow([ + node(EXECUTE_WORKFLOW, 1.2, { workflowId: resourceLocator('child-a', 'id') }), + node(EXECUTE_WORKFLOW, 1, { workflowId: 'child-b' }), + ]); + + expect(subWorkflowNodeReference.extract(wf)).toEqual([ + { workflowId: 'parent', referencedWorkflowId: 'child-a' }, + { workflowId: 'parent', referencedWorkflowId: 'child-b' }, + ]); + }); + + it('rewrites referenced ids through the workflow bindings, leaving unknowns untouched', () => { + const wf = workflow([ + node(EXECUTE_WORKFLOW, 1.2, { workflowId: resourceLocator('child-a', 'id') }), + node(EXECUTE_WORKFLOW, 1, { workflowId: 'child-b' }), + node(EXECUTE_WORKFLOW, 1.2, { workflowId: resourceLocator('external', 'id') }), + ]); + + subWorkflowNodeReference.apply( + wf, + bindings( + new Map([ + ['child-a', 'A'], + ['child-b', 'B'], + ]), + ), + ); + + expect(subWorkflowNodeReference.extract(wf).map((r) => r.referencedWorkflowId)).toEqual([ + 'A', + 'B', + 'external', + ]); + }); +}); diff --git a/packages/cli/src/modules/n8n-packages/entities/workflow/references/caller-ids.reference.ts b/packages/cli/src/modules/n8n-packages/entities/workflow/references/caller-ids.reference.ts new file mode 100644 index 00000000000..df2f909a781 --- /dev/null +++ b/packages/cli/src/modules/n8n-packages/entities/workflow/references/caller-ids.reference.ts @@ -0,0 +1,25 @@ +import type { EntityReference } from '../../reference'; +import type { WorkflowSubWorkflowRequirement } from '../workflow.types'; + +/** + * Replaces `callerId` references on workflow import based on the passed bindings + * + * CallerId's are not defined in requirements so nothing to extract + */ +export const callerIdsReference: EntityReference = { + extract() { + return []; + }, + apply(workflow, bindings) { + const { settings } = workflow; + // settings is opaque at the schema level, so a non-string callerIds would crash `.split`. + if (!settings?.callerIds || typeof settings.callerIds !== 'string') return; + + settings.callerIds = settings.callerIds + .split(',') + .map((raw) => raw.trim()) + .filter((id) => id !== '') + .map((id) => bindings.workflows.get(id) ?? id) + .join(','); + }, +}; diff --git a/packages/cli/src/modules/n8n-packages/entities/workflow/references/error-workflow.reference.ts b/packages/cli/src/modules/n8n-packages/entities/workflow/references/error-workflow.reference.ts new file mode 100644 index 00000000000..d6ea7bfc0e1 --- /dev/null +++ b/packages/cli/src/modules/n8n-packages/entities/workflow/references/error-workflow.reference.ts @@ -0,0 +1,31 @@ +import type { WorkflowEntity } from '@n8n/db'; + +import type { EntityReference } from '../../reference'; +import type { WorkflowSubWorkflowRequirement } from '../workflow.types'; + +/** + * Extract and replace error workflow id references + */ +export const errorWorkflowReference: EntityReference = { + extract(workflow) { + const referencedWorkflowId = staticErrorWorkflowId(workflow); + return referencedWorkflowId ? [{ workflowId: workflow.id, referencedWorkflowId }] : []; + }, + apply(workflow, bindings) { + const currentId = staticErrorWorkflowId(workflow); + if (!currentId || !workflow.settings) return; + workflow.settings.errorWorkflow = bindings.workflows.get(currentId) ?? currentId; + }, +}; + +function staticErrorWorkflowId(workflow: WorkflowEntity): string | undefined { + const errorWorkflow = workflow.settings?.errorWorkflow; + if ( + typeof errorWorkflow !== 'string' || + errorWorkflow === 'DEFAULT' || + errorWorkflow.startsWith('=') + ) { + return undefined; + } + return errorWorkflow; +} diff --git a/packages/cli/src/modules/n8n-packages/entities/workflow/references/extract-workflow-requirements.ts b/packages/cli/src/modules/n8n-packages/entities/workflow/references/extract-workflow-requirements.ts new file mode 100644 index 00000000000..d56240ac797 --- /dev/null +++ b/packages/cli/src/modules/n8n-packages/entities/workflow/references/extract-workflow-requirements.ts @@ -0,0 +1,23 @@ +import type { WorkflowEntity } from '@n8n/db'; + +import { workflowReferences } from './workflow-references'; +import type { WorkflowSubWorkflowRequirement } from '../workflow.types'; + +/** + * Every workflow the given workflow depends on, folded to one requirement per referenced id — the + * same workflow reached through two references (a sub-workflow node and `errorWorkflow`, say) is a + * single dependency. + */ +export function extractWorkflowRequirements( + workflow: WorkflowEntity, +): WorkflowSubWorkflowRequirement[] { + const byId = new Map(); + for (const reference of workflowReferences) { + for (const requirement of reference.extract(workflow)) { + if (!byId.has(requirement.referencedWorkflowId)) { + byId.set(requirement.referencedWorkflowId, requirement); + } + } + } + return [...byId.values()]; +} diff --git a/packages/cli/src/modules/n8n-packages/entities/workflow/references/sub-workflow-node.reference.ts b/packages/cli/src/modules/n8n-packages/entities/workflow/references/sub-workflow-node.reference.ts new file mode 100644 index 00000000000..7b6d3709809 --- /dev/null +++ b/packages/cli/src/modules/n8n-packages/entities/workflow/references/sub-workflow-node.reference.ts @@ -0,0 +1,62 @@ +import { + getSubworkflowId, + isNodeWithWorkflowSelector, + isResourceLocatorValue, + type INode, +} from 'n8n-workflow'; + +import type { EntityReference } from '../../reference'; +import type { WorkflowSubWorkflowRequirement } from '../workflow.types'; + +/** + * Find and extract subworkflow references from a workflow + * + * Update subworkflow references from the import bindings + */ +export const subWorkflowNodeReference: EntityReference = { + extract(workflow) { + const requirements: WorkflowSubWorkflowRequirement[] = []; + for (const node of workflow.nodes ?? []) { + const referencedWorkflowId = getStaticSubworkflowId(node); + if (referencedWorkflowId) { + requirements.push({ workflowId: workflow.id, referencedWorkflowId }); + } + } + return requirements; + }, + apply(workflow, bindings) { + for (const node of workflow.nodes ?? []) { + const currentId = getStaticSubworkflowId(node); + if (!currentId) continue; + const targetId = bindings.workflows.get(currentId); + if (targetId) setStaticSubworkflowId(node, targetId); + } + }, +}; + +export function getStaticSubworkflowId(node: INode): string | undefined { + if (!isNodeWithWorkflowSelector(node)) return undefined; + + const { source = 'database' } = node.parameters; + if (source !== 'database') return undefined; + + const { workflowId: storedWorkflowId } = node.parameters; + return toStaticId(getSubworkflowId(node) ?? storedWorkflowId); +} + +export function setStaticSubworkflowId(node: INode, workflowId: string): void { + const stored = node.parameters.workflowId; + if (isResourceLocatorValue(stored)) { + stored.value = workflowId; + } else { + node.parameters.workflowId = workflowId; + } +} + +function toStaticId(value: unknown): string | undefined { + if (typeof value !== 'string') return undefined; + const trimmed = value.trim(); + // Expressions are resolved at runtime, so they are not static dependencies. + if (trimmed === '' || trimmed.startsWith('=')) return undefined; + return trimmed; +} diff --git a/packages/cli/src/modules/n8n-packages/entities/workflow/references/workflow-references.ts b/packages/cli/src/modules/n8n-packages/entities/workflow/references/workflow-references.ts new file mode 100644 index 00000000000..b26078e0058 --- /dev/null +++ b/packages/cli/src/modules/n8n-packages/entities/workflow/references/workflow-references.ts @@ -0,0 +1,12 @@ +import { callerIdsReference } from './caller-ids.reference'; +import { errorWorkflowReference } from './error-workflow.reference'; +import { subWorkflowNodeReference } from './sub-workflow-node.reference'; +import type { EntityReference } from '../../reference'; +import type { WorkflowSubWorkflowRequirement } from '../workflow.types'; + +/** Every place a workflow can reference another workflow, for both extract and rebind. */ +export const workflowReferences: Array> = [ + subWorkflowNodeReference, + errorWorkflowReference, + callerIdsReference, +]; diff --git a/packages/cli/src/modules/n8n-packages/entities/workflow/static-sub-workflow-id.ts b/packages/cli/src/modules/n8n-packages/entities/workflow/static-sub-workflow-id.ts deleted file mode 100644 index 489df67cc0a..00000000000 --- a/packages/cli/src/modules/n8n-packages/entities/workflow/static-sub-workflow-id.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { getSubworkflowId, isNodeWithWorkflowSelector, type INode } from 'n8n-workflow'; - -/** - * Extract workflow id from a node that references a sub-workflow. - * - * It only supports static references to a sub-workflow from a resource-locator field - * or plain string workflowId for backwards compatibility with older node versions. - */ -export function getStaticSubworkflowId(node: INode): string | undefined { - if (!isNodeWithWorkflowSelector(node)) return undefined; - - // Older nodes may omit `source`; database is their implicit default. - // Only 'database' (or missing) sources references a workflow by id. - const { source = 'database' } = node.parameters; - if (source !== 'database') return undefined; - - // Newer versions store the id in a resource-locator (resolved by the shared - // helper); legacy versions store it as a plain string. - const { workflowId: storedWorkflowId } = node.parameters; - const workflowId = getSubworkflowId(node) ?? storedWorkflowId; - - return toStaticId(workflowId); -} - -function toStaticId(value: unknown): string | undefined { - if (typeof value !== 'string') return undefined; - const trimmed = value.trim(); - // Expressions are resolved at runtime, so they are not static dependencies. - if (trimmed === '' || trimmed.startsWith('=')) return undefined; - return trimmed; -} diff --git a/packages/cli/src/modules/n8n-packages/entities/workflow/sub-workflow-ordering.ts b/packages/cli/src/modules/n8n-packages/entities/workflow/sub-workflow-ordering.ts new file mode 100644 index 00000000000..5bbaecef7b5 --- /dev/null +++ b/packages/cli/src/modules/n8n-packages/entities/workflow/sub-workflow-ordering.ts @@ -0,0 +1,58 @@ +import type { PackageWorkflowRequirement } from '../../spec/requirements.schema'; + +/** + * Sort workflows so we import from "lowest" to "highest" + **/ +export function orderBySubWorkflowDependencies( + workflows: T[], + requirements: PackageWorkflowRequirement[] | undefined, +): T[] { + if (!requirements?.length) return workflows; + + const workflowsById = new Map(workflows.map((workflow) => [workflow.sourceWorkflowId, workflow])); + + // sub-workflow id → workflows that call it; caller id → count of its in-batch + // sub-workflow dependencies not yet placed. + const callers = new Map(); + const pendingDependencies = new Map( + workflows.map((workflow) => [workflow.sourceWorkflowId, 0]), + ); + + for (const requirement of requirements) { + if (!workflowsById.has(requirement.id)) continue; + for (const callerId of requirement.usedByWorkflows) { + // Ignore callers outside the batch and self-references (a workflow calling itself). + if (!workflowsById.has(callerId) || callerId === requirement.id) continue; + const existing = callers.get(requirement.id); + if (existing) existing.push(callerId); + else callers.set(requirement.id, [callerId]); + pendingDependencies.set(callerId, pendingDependencies.get(callerId)! + 1); + } + } + + // Seed with workflows that depend on nothing in the batch, preserving input order. + const queue = workflows.filter( + (workflow) => pendingDependencies.get(workflow.sourceWorkflowId) === 0, + ); + const ordered: T[] = []; + + for (let cursor = 0; cursor < queue.length; cursor++) { + const workflow = queue[cursor]; + ordered.push(workflow); + for (const callerId of callers.get(workflow.sourceWorkflowId) ?? []) { + const pending = pendingDependencies.get(callerId)! - 1; + pendingDependencies.set(callerId, pending); + if (pending === 0) queue.push(workflowsById.get(callerId)!); + } + } + + // Whatever never reached the queue is part of a cycle; append it in original order. + if (ordered.length < workflows.length) { + const placed = new Set(ordered.map((workflow) => workflow.sourceWorkflowId)); + for (const workflow of workflows) { + if (!placed.has(workflow.sourceWorkflowId)) ordered.push(workflow); + } + } + + return ordered; +} diff --git a/packages/cli/src/modules/n8n-packages/entities/workflow/workflow-dependency-resolver.ts b/packages/cli/src/modules/n8n-packages/entities/workflow/workflow-dependency-resolver.ts index 42825882b02..d3b355e9833 100644 --- a/packages/cli/src/modules/n8n-packages/entities/workflow/workflow-dependency-resolver.ts +++ b/packages/cli/src/modules/n8n-packages/entities/workflow/workflow-dependency-resolver.ts @@ -3,24 +3,28 @@ import { Service } from '@n8n/di'; import { WorkflowFinderService } from '@/workflows/workflow-finder.service'; -import { WorkflowRequirementsExtractor } from './workflow-requirements.extractor'; +import { extractWorkflowRequirements } from './references/extract-workflow-requirements'; import type { WorkflowSubWorkflowRequirement } from './workflow.types'; export interface WorkflowDependencyResolveRequest { user: User; workflowIds: string[]; + /** + * How far to follow static sub-workflow references: `transitive` (default) + * walks the whole reference graph, `direct` stops after the requested + * workflows' own references. + */ + traversal?: 'transitive' | 'direct'; } @Service() export class WorkflowDependencyResolver { - constructor( - private readonly workflowFinder: WorkflowFinderService, - private readonly workflowRequirementsExtractor: WorkflowRequirementsExtractor, - ) {} + constructor(private readonly workflowFinder: WorkflowFinderService) {} async resolve( request: WorkflowDependencyResolveRequest, ): Promise { + const traverse = (request.traversal ?? 'transitive') === 'transitive'; const queue = [...new Set(request.workflowIds)]; const seenWorkflowIds = new Set(queue); const requirements: WorkflowSubWorkflowRequirement[] = []; @@ -42,9 +46,11 @@ export class WorkflowDependencyResolver { // But the missing/inaccessible IDs are kept as direct requirements from their parent. if (!workflow) continue; - const extractedRequirements = this.workflowRequirementsExtractor.extract(workflow); + const extractedRequirements = extractWorkflowRequirements(workflow); requirements.push(...extractedRequirements); + if (!traverse) continue; + for (const { referencedWorkflowId } of extractedRequirements) { if (seenWorkflowIds.has(referencedWorkflowId)) continue; diff --git a/packages/cli/src/modules/n8n-packages/entities/workflow/workflow-import.types.ts b/packages/cli/src/modules/n8n-packages/entities/workflow/workflow-import.types.ts index 59fe8c97297..7e97e365c13 100644 --- a/packages/cli/src/modules/n8n-packages/entities/workflow/workflow-import.types.ts +++ b/packages/cli/src/modules/n8n-packages/entities/workflow/workflow-import.types.ts @@ -4,15 +4,13 @@ import type { WorkflowIdConflict } from './workflow-import-match.service'; import type { WorkflowPublishingBlockedReason, WorkflowPublishingOutcome, - WorkflowPublishingPolicy, } from './workflow-publishing-policy.types'; import type { ImportContext } from '../../n8n-packages.types'; /** Apply-time context for the workflow importer: the resolved import target plus apply-only inputs. */ export interface WorkflowImportContext extends ImportContext { - publishingPolicy: WorkflowPublishingPolicy; - /** Package workflow ids that must stay inactive, mapped to the reason why. */ - publishBlocked: ReadonlyMap; + /** Tag ids the tag plan dropped; stripped from every workflow's `tagIds` before attaching. */ + droppedTagIds: ReadonlySet; } export interface PreparedWorkflow { @@ -25,6 +23,12 @@ export interface PreparedWorkflow { * target folder. */ parentFolderId: string | null; + /** + * Source tag ids from the package's `workflow.json`. When present (even + * empty) an update overwrites the target workflow's taggings to exactly + * this set; when absent, taggings are left untouched. + */ + tagIds?: string[]; } export type WorkflowPlannedAction = 'create' | 'update' | 'skip'; @@ -84,3 +88,22 @@ export interface WorkflowImportOutcome { sourceWorkflowId: string; publishing: WorkflowPublishingOutcome; } + +/** + * A workflow written to the database, awaiting the package-wide publish sweep. Discriminated so + * only the written actions carry what the sweep needs; a skipped workflow is never published and + * keeps whatever state it already had. + */ +export type PersistedWorkflowOutcome = + | { status: 'skipped'; workflow: WorkflowEntity; sourceWorkflowId: string } + | { + status: 'created' | 'updated'; + workflow: WorkflowEntity; + sourceWorkflowId: string; + item: PersistedWorkflowPlanItem; + /** + * Why this workflow must stay inactive even if the policy wants it published — it depends on + * a credential that was stubbed, or a node type this instance does not have. + */ + blockedFromPublish?: WorkflowPublishingBlockedReason; + }; diff --git a/packages/cli/src/modules/n8n-packages/entities/workflow/workflow-importer.ts b/packages/cli/src/modules/n8n-packages/entities/workflow/workflow-importer.ts index 2d823080458..4e69eb0a043 100644 --- a/packages/cli/src/modules/n8n-packages/entities/workflow/workflow-importer.ts +++ b/packages/cli/src/modules/n8n-packages/entities/workflow/workflow-importer.ts @@ -4,6 +4,7 @@ import { Service } from '@n8n/di'; import { WorkflowCreationService } from '@/workflows/workflow-creation.service'; import { WorkflowService } from '@/workflows/workflow.service'; +import { workflowReferences } from './references/workflow-references'; import { decideWorkflowConflictAction } from './workflow-conflict-policy'; import { decideWorkflowId } from './workflow-id-policy'; import { @@ -11,18 +12,18 @@ import { type WorkflowIdConflict, } from './workflow-import-match.service'; import type { + PersistedWorkflowOutcome, PersistedWorkflowPlanItem, PreparedWorkflow, WorkflowConflict, WorkflowFolderConflict, WorkflowImportContext, - WorkflowImportOutcome, WorkflowImportPlan, WorkflowPlanItem, WorkflowPlannedAction, } from './workflow-import.types'; -import { WorkflowPublisher } from './workflow-publisher'; import type { + ImportBindingMap, ImportContext, ImportWorkflowProperties, PackageImportBindings, @@ -30,14 +31,14 @@ import type { } from '../../n8n-packages.types'; export interface WorkflowImportResult { - outcomes: WorkflowImportOutcome[]; + outcomes: PersistedWorkflowOutcome[]; bindings: PackageImportBindings; } /** * Imports a batch of prepared workflows in two phases: * {@link plan} matches each workflow against the destination project and decides what action create/update/skip - * {@link apply} writes that plan into n8n and publishes the workflows + * {@link apply} writes that plan into n8n. Publishing is a separate package-wide sweep. */ @Service() export class WorkflowImporter { @@ -45,7 +46,6 @@ export class WorkflowImporter { private readonly workflowImportMatchService: WorkflowImportMatchService, private readonly workflowCreationService: WorkflowCreationService, private readonly workflowService: WorkflowService, - private readonly workflowPublisher: WorkflowPublisher, ) {} async plan( @@ -133,59 +133,48 @@ export class WorkflowImporter { }); } + /** + * Writes the planned workflows. Publishing is deliberately not done here: activation rejects a + * parent whose sub-workflow is not yet published, so it has to wait until every workflow in the + * package exists — see `WorkflowPublisher.applyToPackage`. + */ async apply( context: WorkflowImportContext, plan: WorkflowImportPlan, bindings: PackageImportBindings, ): Promise { - const workflowBindings = new Map(bindings.workflows); - const outcomes: WorkflowImportOutcome[] = []; + const workflowBindings = new Map([ + ...bindings.workflows, + ...collectPlannedWorkflowBindings(plan.items), + ]); + const resolvedBindings: PackageImportBindings = { ...bindings, workflows: workflowBindings }; + const outcomes: PersistedWorkflowOutcome[] = []; for (const item of plan.items) { - const outcome = await this.applyItem(context, item, bindings); - outcomes.push(outcome); - // Works for every status: created/updated/skipped all resolve to a real target id. - workflowBindings.set(outcome.sourceWorkflowId, outcome.workflow.id); + outcomes.push(await this.applyItem(context, item, resolvedBindings)); } - return { outcomes, bindings: { ...bindings, workflows: workflowBindings } }; + return { outcomes, bindings: resolvedBindings }; } private async applyItem( context: WorkflowImportContext, item: WorkflowPlanItem, bindings: PackageImportBindings, - ): Promise { + ): Promise { if (item.action === 'skip') { return { status: 'skipped', workflow: item.existing, sourceWorkflowId: item.sourceWorkflowId, - publishing: { state: 'unchanged' }, }; } - const savedWorkflow = await this.persistWorkflow(context, item, bindings); - const { workflow, publishing } = await this.workflowPublisher.apply( - context.user, - item, - savedWorkflow, - context.publishingPolicy, - context.publishBlocked, - ); - - // Publish reloads the workflow without parentFolder; restore it for the import summary. - workflow.parentFolder = - workflow.parentFolder ?? - savedWorkflow.parentFolder ?? - (item.action === 'update' ? item.existing.parentFolder : null) ?? - null; - return { status: item.action === 'create' ? 'created' : 'updated', - workflow, + workflow: await this.persistWorkflow(context, item, bindings), sourceWorkflowId: item.sourceWorkflowId, - publishing, + item, }; } @@ -194,6 +183,9 @@ export class WorkflowImporter { item: PersistedWorkflowPlanItem, bindings: PackageImportBindings, ): Promise { + const tagIds = + item.tagIds && [...new Set(item.tagIds)].filter((id) => !context.droppedTagIds.has(id)); + if (item.action === 'create') { const entity = prepareEntityForPersist(item.entity, bindings, item.decidedId); return await this.workflowCreationService.createWorkflow(context.user, entity, { @@ -202,6 +194,7 @@ export class WorkflowImporter { publicApi: true, source: 'import', sourceWorkflowId: item.sourceWorkflowId, + ...(tagIds !== undefined ? { tagIds } : {}), }); } @@ -209,10 +202,19 @@ export class WorkflowImporter { return await this.workflowService.update(context.user, entity, item.existing.id, { publicApi: true, source: 'import', + ...(tagIds !== undefined ? { tagIds } : {}), }); } } +function planItemTargetId(item: WorkflowPlanItem): string { + return item.action === 'create' ? item.decidedId : item.existing.id; +} + +export function collectPlannedWorkflowBindings(items: WorkflowPlanItem[]): ImportBindingMap { + return new Map(items.map((item) => [item.sourceWorkflowId, planItemTargetId(item)])); +} + /** Clones package content for persistence without mutating the import plan. */ function prepareEntityForPersist( source: WorkflowEntity, @@ -221,16 +223,18 @@ function prepareEntityForPersist( ): WorkflowEntity { const entity = Object.assign(new WorkflowEntity(), source, { nodes: structuredClone(source.nodes), + ...(source.settings ? { settings: structuredClone(source.settings) } : {}), ...(decidedId !== undefined ? { id: decidedId } : {}), }); applyCredentialBindingsInPlace(entity, bindings.credentials); + for (const reference of workflowReferences) reference.apply(entity, bindings); return entity; } /** Mutates node credential ids on `entity` using the resolved import binding map. */ function applyCredentialBindingsInPlace( entity: WorkflowEntity, - credentialBindings: PackageImportBindings['credentials'], + credentialBindings: ImportBindingMap, ): void { for (const node of entity.nodes) { for (const details of Object.values(node.credentials ?? {})) { diff --git a/packages/cli/src/modules/n8n-packages/entities/workflow/workflow-publisher.ts b/packages/cli/src/modules/n8n-packages/entities/workflow/workflow-publisher.ts index da6e1931c42..4f70a422a97 100644 --- a/packages/cli/src/modules/n8n-packages/entities/workflow/workflow-publisher.ts +++ b/packages/cli/src/modules/n8n-packages/entities/workflow/workflow-publisher.ts @@ -9,7 +9,8 @@ import { NotFoundError } from '@/errors/response-errors/not-found.error'; import { ProjectService } from '@/services/project.service.ee'; import { WorkflowService } from '@/workflows/workflow.service'; -import type { PersistedWorkflowPlanItem } from './workflow-import.types'; +import { orderBySubWorkflowDependencies } from './sub-workflow-ordering'; +import type { PersistedWorkflowOutcome, PersistedWorkflowPlanItem } from './workflow-import.types'; import { decideWorkflowPublishingAction } from './workflow-publishing-policy'; import { WorkflowPublishingPolicy, @@ -17,12 +18,25 @@ import { type WorkflowPublishingContext, type WorkflowPublishingOutcome, } from './workflow-publishing-policy.types'; +import type { PackageWorkflowRequirement } from '../../spec/requirements.schema'; export interface WorkflowPublishingResult { workflow: WorkflowEntity; publishing: WorkflowPublishingOutcome; } +/** What the publish sweep decided for each workflow, keyed by source workflow id. */ +export type PackagePublishingResults = ReadonlyMap; + +export interface PackagePublishingRequest { + user: User; + /** Every workflow the package wrote, across all of its projects. */ + persisted: PersistedWorkflowOutcome[]; + policy: WorkflowPublishingPolicy; + /** Package-wide sub-workflow graph, used to publish dependencies first. */ + subWorkflowRequirements: PackageWorkflowRequirement[] | undefined; +} + /** * Owns the publish lifecycle of imported workflows: an upfront permission check for * {@link WorkflowPublishingPolicy.PublishAll}, and per-workflow publish/unpublish @@ -37,6 +51,43 @@ export class WorkflowPublisher { private readonly workflowService: WorkflowService, ) {} + /** + * Publishes a whole package's freshly written workflows, sub-workflows first. + * + * Activation rejects a parent whose referenced sub-workflow is not itself published, so this + * order is load-bearing — and it can only be resolved once every workflow in the package + * exists, which is why publishing is a package-wide sweep rather than part of each write. + */ + async applyToPackage({ + user, + persisted, + policy, + subWorkflowRequirements, + }: PackagePublishingRequest): Promise { + const results = new Map(); + + for (const outcome of orderBySubWorkflowDependencies(persisted, subWorkflowRequirements)) { + if (outcome.status === 'skipped') continue; + + const result = await this.apply( + user, + outcome.item, + outcome.workflow, + policy, + outcome.blockedFromPublish, + ); + // Publish reloads the workflow without parentFolder; restore it for the import summary. + result.workflow.parentFolder = + result.workflow.parentFolder ?? + outcome.workflow.parentFolder ?? + (outcome.item.action === 'update' ? outcome.item.existing.parentFolder : null) ?? + null; + results.set(outcome.sourceWorkflowId, result); + } + + return results; + } + /** * Fail the import before any writes when {@link WorkflowPublishingPolicy.PublishAll} * is selected and the actor lacks `workflow:publish`. Other policies skip this check; @@ -82,7 +133,7 @@ export class WorkflowPublisher { item: PersistedWorkflowPlanItem, workflow: WorkflowEntity, policy: WorkflowPublishingPolicy, - publishBlocked: ReadonlyMap, + blockedReason?: WorkflowPublishingBlockedReason, ): Promise { const action = decideWorkflowPublishingAction(policy, toPublishingContext(item, workflow)); @@ -90,7 +141,6 @@ export class WorkflowPublisher { return { workflow, publishing: { state: 'unchanged' } }; } - const blockedReason = publishBlocked.get(item.sourceWorkflowId); if (action === 'publish' && blockedReason) { // A prior published version may still be active after an update; report // that the live publish state is unchanged rather than "blocked". diff --git a/packages/cli/src/modules/n8n-packages/entities/workflow/workflow-requirement.exporter.ts b/packages/cli/src/modules/n8n-packages/entities/workflow/workflow-requirement.exporter.ts index bf7189e8b5e..cbd5f1061d8 100644 --- a/packages/cli/src/modules/n8n-packages/entities/workflow/workflow-requirement.exporter.ts +++ b/packages/cli/src/modules/n8n-packages/entities/workflow/workflow-requirement.exporter.ts @@ -1,10 +1,14 @@ +import type { User } from '@n8n/db'; import { Service } from '@n8n/di'; +import { WorkflowFinderService } from '@/workflows/workflow-finder.service'; + import type { WorkflowSubWorkflowRequirement } from './workflow.types'; import type { ManifestEntry } from '../../spec/manifest.schema'; import type { PackageWorkflowRequirement } from '../../spec/requirements.schema'; export interface WorkflowRequirementExportRequest { + user: User; requirements: WorkflowSubWorkflowRequirement[]; workflows: ManifestEntry[]; } @@ -15,7 +19,11 @@ export interface WorkflowRequirementExportResult { @Service() export class WorkflowRequirementExporter { - export(request: WorkflowRequirementExportRequest): WorkflowRequirementExportResult { + constructor(private readonly workflowFinder: WorkflowFinderService) {} + + async export( + request: WorkflowRequirementExportRequest, + ): Promise { const workflowsById = new Map(request.workflows.map((workflow) => [workflow.id, workflow])); const usedByWorkflowsByReferencedId = new Map(); @@ -30,14 +38,42 @@ export class WorkflowRequirementExporter { usedByWorkflowsByReferencedId.set(requirement.referencedWorkflowId, usedByWorkflows); } + const missingWorkflowNamesById = await this.findMissingReferencedWorkflowNames( + request.user, + [...usedByWorkflowsByReferencedId.keys()].filter((id) => !workflowsById.has(id)), + ); + const requirements = [...usedByWorkflowsByReferencedId].map( - ([referencedWorkflowId, usedByWorkflows]) => ({ - id: referencedWorkflowId, - name: workflowsById.get(referencedWorkflowId)?.name ?? '', - usedByWorkflows, - }), + ([referencedWorkflowId, usedByWorkflows]) => { + const name = + workflowsById.get(referencedWorkflowId)?.name ?? + missingWorkflowNamesById.get(referencedWorkflowId); + + return { + id: referencedWorkflowId, + ...(name ? { name } : {}), + usedByWorkflows, + }; + }, ); return { requirements }; } + + /** + * Best-effort names for referenced workflows that are not in the package + * (only possible under the reference-only policy); ids the user cannot + * access stay nameless. + */ + private async findMissingReferencedWorkflowNames( + user: User, + workflowIds: string[], + ): Promise> { + if (workflowIds.length === 0) return new Map(); + + const workflows = await this.workflowFinder.findWorkflowsByIdsForUser(workflowIds, user, [ + 'workflow:export', + ]); + return new Map(workflows.map((workflow) => [workflow.id, workflow.name])); + } } diff --git a/packages/cli/src/modules/n8n-packages/entities/workflow/workflow-requirements.extractor.ts b/packages/cli/src/modules/n8n-packages/entities/workflow/workflow-requirements.extractor.ts deleted file mode 100644 index c7a2d31f141..00000000000 --- a/packages/cli/src/modules/n8n-packages/entities/workflow/workflow-requirements.extractor.ts +++ /dev/null @@ -1,45 +0,0 @@ -import type { WorkflowEntity } from '@n8n/db'; -import { Service } from '@n8n/di'; - -import type { RequirementsExtractor } from '../requirements-extractor'; -import { getStaticSubworkflowId } from './static-sub-workflow-id'; -import type { WorkflowSubWorkflowRequirement } from './workflow.types'; - -@Service() -export class WorkflowRequirementsExtractor - implements RequirementsExtractor -{ - extract(workflow: WorkflowEntity): WorkflowSubWorkflowRequirement[] { - const byId = new Map(); - - const addRequirement = (referencedWorkflowId: string | undefined) => { - if (!referencedWorkflowId || byId.has(referencedWorkflowId)) return; - - byId.set(referencedWorkflowId, { - workflowId: workflow.id, - referencedWorkflowId, - }); - }; - - for (const node of workflow.nodes ?? []) { - addRequirement(getStaticSubworkflowId(node)); - } - - addRequirement(this.getErrorWorkflowId(workflow)); - - return [...byId.values()]; - } - - private getErrorWorkflowId(workflow: WorkflowEntity): string | undefined { - const errorWorkflow = workflow.settings?.errorWorkflow; - - // Only a static id is a workflow dependency. `DEFAULT` is a sentinel, and an - // expression (e.g. `={{ $vars.ERROR_WORKFLOW_ID }}`) is a variable dependency - // handled by the variable extractor, not a concrete referenced workflow. - if (!errorWorkflow || errorWorkflow === 'DEFAULT' || errorWorkflow.startsWith('=')) { - return undefined; - } - - return errorWorkflow; - } -} diff --git a/packages/cli/src/modules/n8n-packages/entities/workflow/workflow.exporter.ts b/packages/cli/src/modules/n8n-packages/entities/workflow/workflow.exporter.ts index c49b2c837f9..478c4fa8584 100644 --- a/packages/cli/src/modules/n8n-packages/entities/workflow/workflow.exporter.ts +++ b/packages/cli/src/modules/n8n-packages/entities/workflow/workflow.exporter.ts @@ -14,6 +14,8 @@ import type { WorkflowDataTableRequirement } from '../data-table/data-table.type import type { WorkflowNodeTypeSource } from './node-type-usage'; import { assertEveryRequestedEntityAccessible } from '../package-export.errors'; import type { WorkflowExportRequirements } from '../requirements.types'; +import { TagRequirementsExtractor } from '../tag/tag-requirements.extractor'; +import type { WorkflowTagUsage } from '../tag/tag.types'; import { VariableRequirementsExtractor } from '../variable/variable-requirements.extractor'; import type { WorkflowVariableRequirement } from '../variable/variable.types'; @@ -21,6 +23,7 @@ export interface WorkflowExportRequest { user: User; workflowIds: string[]; writer: PackageWriter; + includeTags: boolean; // Directory the workflow is written under. e.g. folders/{folderId}/ basePrefix?: string; @@ -39,6 +42,7 @@ export class WorkflowExporter { private readonly credentialRequirementsExtractor: CredentialRequirementsExtractor, private readonly dataTableRequirementsExtractor: DataTableRequirementsExtractor, private readonly variableRequirementsExtractor: VariableRequirementsExtractor, + private readonly tagRequirementsExtractor: TagRequirementsExtractor, ) {} async export(request: WorkflowExportRequest): Promise { @@ -46,7 +50,7 @@ export class WorkflowExporter { request.workflowIds, request.user, ['workflow:export'], - { includeParentFolder: true }, + { includeParentFolder: true, includeTags: request.includeTags }, ); await assertEveryRequestedEntityAccessible( @@ -61,6 +65,7 @@ export class WorkflowExporter { const credentials: WorkflowCredentialRequirement[] = []; const dataTables: WorkflowDataTableRequirement[] = []; const variables: WorkflowVariableRequirement[] = []; + const tags: WorkflowTagUsage[] = []; const nodeTypes: WorkflowNodeTypeSource[] = []; const fileNames = new UniqueFilenameAllocator( request.basePrefix ? `${request.basePrefix}/workflows` : 'workflows', @@ -69,7 +74,9 @@ export class WorkflowExporter { for (const workflow of workflowsForExport) { const target = fileNames.allocate(workflow.name); - const serialized = this.workflowSerializer.serialize(workflow); + const serialized = this.workflowSerializer.serialize(workflow, { + includeTags: request.includeTags, + }); request.writer.writeDirectory(target); request.writer.writeFile(`${target}/workflow.json`, JSON.stringify(serialized, null, '\t')); @@ -83,10 +90,11 @@ export class WorkflowExporter { credentials.push(...this.credentialRequirementsExtractor.extract(workflow)); dataTables.push(...this.dataTableRequirementsExtractor.extract(workflow)); variables.push(...this.variableRequirementsExtractor.extract(workflow)); + tags.push(...this.tagRequirementsExtractor.extract(workflow)); nodeTypes.push({ workflowId: workflow.id, nodes: workflow.nodes ?? [] }); } - return { entries, requirements: { credentials, dataTables, variables, nodeTypes } }; + return { entries, requirements: { credentials, dataTables, variables, tags, nodeTypes } }; } private orderWorkflowsByRequest( diff --git a/packages/cli/src/modules/n8n-packages/entities/workflow/workflow.serializer.ts b/packages/cli/src/modules/n8n-packages/entities/workflow/workflow.serializer.ts index e7f352975cb..668f3982f62 100644 --- a/packages/cli/src/modules/n8n-packages/entities/workflow/workflow.serializer.ts +++ b/packages/cli/src/modules/n8n-packages/entities/workflow/workflow.serializer.ts @@ -6,6 +6,7 @@ import { serializedWorkflowSchema, type SerializedWorkflow, } from '../../spec/serialized/workflow.schema'; +import { compareTagsByName } from '../tag/tag.types'; /** Fields restored from package workflow.json; the target instance assigns the rest. */ type WorkflowPackageContent = Pick< @@ -15,7 +16,13 @@ type WorkflowPackageContent = Pick< @Service() export class WorkflowSerializer { - serialize(workflow: WorkflowEntity): SerializedWorkflow { + serialize(workflow: WorkflowEntity, options: { includeTags: boolean }): SerializedWorkflow { + // Emitted even when empty: on import, a present `tagIds` (incl. `[]`) overwrites + // taggings to exactly that set, while an absent key leaves them untouched. + const tags = options.includeTags + ? [...(workflow.tags ?? [])].sort(compareTagsByName) + : undefined; + return serializedWorkflowSchema.parse({ id: workflow.id, name: workflow.name, @@ -26,6 +33,7 @@ export class WorkflowSerializer { parentFolderId: workflow.parentFolder?.id ?? null, isPublished: workflow.activeVersionId === workflow.versionId, isArchived: workflow.isArchived, + ...(tags ? { tagIds: tags.map((tag) => tag.id) } : {}), }); } diff --git a/packages/cli/src/modules/n8n-packages/n8n-packages.service.ts b/packages/cli/src/modules/n8n-packages/n8n-packages.service.ts index ef7716b2196..ade94045f97 100644 --- a/packages/cli/src/modules/n8n-packages/n8n-packages.service.ts +++ b/packages/cli/src/modules/n8n-packages/n8n-packages.service.ts @@ -1,3 +1,4 @@ +import { GlobalConfig } from '@n8n/config'; import { Service } from '@n8n/di'; import { InstanceSettings } from 'n8n-core'; @@ -12,9 +13,9 @@ import { WorkflowPackageImporter } from './engine/workflow-package-importer'; import { CredentialExporter } from './entities/credential/credential.exporter'; import { DataTableExporter } from './entities/data-table/data-table.exporter'; import { FolderExporter } from './entities/folder/folder.exporter'; -import { PackageExportBlockedError } from './entities/package-export.errors'; import { ProjectExporter } from './entities/project/project.exporter'; import { mergeRequirements } from './entities/requirements.types'; +import { TagExporter } from './entities/tag/tag.exporter'; import { VariableExporter } from './entities/variable/variable.exporter'; import { collectNodeTypeUsage } from './entities/workflow/node-type-usage'; import { assertStaticSubWorkflowsIncluded } from './entities/workflow/static-sub-workflow-requirements'; @@ -54,6 +55,8 @@ export class N8nPackagesService { private readonly credentialExporter: CredentialExporter, private readonly dataTableExporter: DataTableExporter, private readonly variableExporter: VariableExporter, + private readonly tagExporter: TagExporter, + private readonly globalConfig: GlobalConfig, private readonly instanceSettings: InstanceSettings, private readonly packageParser: N8nPackageParser, private readonly packageImportConfig: PackageImportConfig, @@ -67,18 +70,15 @@ export class N8nPackagesService { ) {} async exportPackage(request: ExportPackageRequest): Promise { - // TODO: remove this once reference-only is supported const { missingWorkflowDependencyPolicy } = request; - if (missingWorkflowDependencyPolicy === MissingWorkflowDependencyPolicy.ReferenceOnly) { - throw new PackageExportBlockedError( - 'Reference-only static sub-workflow dependencies are not supported. Export aborted.', - ); - } + const isReferenceOnly = + missingWorkflowDependencyPolicy === MissingWorkflowDependencyPolicy.ReferenceOnly; const writer = new TarPackageWriter(); const workflowIds = request.workflowIds ?? []; const folderIds = request.folderIds ?? []; const projectIds = request.projectIds ?? []; + const includeTags = (request.includeTags ?? true) && !this.globalConfig.tags.disabled; const folderExportResult = folderIds.length > 0 @@ -86,6 +86,7 @@ export class N8nPackagesService { user: request.user, folderIds, writer, + includeTags, }) : undefined; @@ -100,6 +101,7 @@ export class N8nPackagesService { user: request.user, workflowIds: workflowsForExport, writer, + includeTags, }) : undefined; @@ -109,6 +111,7 @@ export class N8nPackagesService { user: request.user, projectIds, writer, + includeTags, }) : undefined; @@ -123,9 +126,13 @@ export class N8nPackagesService { ...(projectExportResult?.workflowEntries ?? []), ]; + // Reference-only keeps missing dependencies out of the package, so only the + // direct references of packaged workflows matter — a referenced workflow's + // own dependency closure is assumed to exist on the target alongside it. const workflowRequirements = await this.workflowDependencyResolver.resolve({ user: request.user, workflowIds: allWorkflowsBeforeAutoInclude.map(({ id }) => id), + traversal: isReferenceOnly ? 'direct' : 'transitive', }); let autoIncludedExportResult: AutoIncludedWorkflowExportResult | undefined; @@ -137,6 +144,7 @@ export class N8nPackagesService { topLevelWorkflowIds: workflowExportResult?.entries.map(({ id }) => id) ?? [], folderWorkflowIds: folderExportResult?.workflowEntries.map(({ id }) => id) ?? [], projectWorkflowIds: projectExportResult?.workflowEntries.map(({ id }) => id) ?? [], + includeTags, }); autoIncludedExportResult = this.autoIncludedWorkflowExporter.export({ @@ -145,6 +153,7 @@ export class N8nPackagesService { existingWorkflowEntries: allWorkflowsBeforeAutoInclude, existingFolderEntries: allFoldersBeforeAutoInclude, existingProjectEntries: allProjectsBeforeAutoInclude, + includeTags, projectTargetsById: projectExportResult?.projectTargetsById, }); } @@ -180,10 +189,13 @@ export class N8nPackagesService { ...(autoIncludedExportResult?.workflowEntries ?? []), ]); - assertStaticSubWorkflowsIncluded( - workflowRequirements, - new Set(allWorkflowsInPackage.map(({ id }) => id)), - ); + // Reference-only records missing dependencies as requirements instead of aborting. + if (!isReferenceOnly) { + assertStaticSubWorkflowsIncluded( + workflowRequirements, + new Set(allWorkflowsInPackage.map(({ id }) => id)), + ); + } // The auto-include's projectTargetsById is a superset of the project targets from the project export result // that's why it takes precedence when both are present. @@ -206,7 +218,8 @@ export class N8nPackagesService { projectTargetsById, }); - const workflowRequirementExportResult = this.workflowRequirementExporter.export({ + const workflowRequirementExportResult = await this.workflowRequirementExporter.export({ + user: request.user, requirements: workflowRequirements, workflows: allWorkflowsInPackage, }); @@ -219,11 +232,17 @@ export class N8nPackagesService { projectTargetsById, }); + const tagExportResult = this.tagExporter.export({ + usages: requirements.tags, + writer, + }); + const manifestRequirements = this.buildManifestRequirements({ credentials: credentialExportResult.requirements, dataTables: dataTableExportResult.requirements, workflows: workflowRequirementExportResult.requirements, variables: variableExportResult.requirements, + tags: tagExportResult.requirements, nodeTypes: collectNodeTypeUsage(requirements.nodeTypes), }); @@ -241,6 +260,7 @@ export class N8nPackagesService { ...(variableExportResult.entries.length > 0 ? { variables: variableExportResult.entries } : {}), + ...(tagExportResult.entries.length > 0 ? { tags: tagExportResult.entries } : {}), ...(manifestRequirements ? { requirements: manifestRequirements } : {}), ...(allWorkflowsInPackage.length > 0 ? { workflows: allWorkflowsInPackage } : {}), ...(allFolders.length > 0 ? { folders: allFolders } : {}), @@ -257,6 +277,7 @@ export class N8nPackagesService { credentials: credentialExportResult.entries.length, dataTables: dataTableExportResult.entries.length, variables: variableExportResult.entries.length, + tags: tagExportResult.entries.length, }; this.eventService.emit('n8n-package-exported', { @@ -306,15 +327,17 @@ export class N8nPackagesService { dataTables: PackageRequirements['dataTables']; workflows: PackageRequirements['workflows']; variables: PackageRequirements['variables']; + tags: PackageRequirements['tags']; nodeTypes: PackageRequirements['nodeTypes']; }): PackageRequirements | undefined { - const { credentials, dataTables, workflows, variables, nodeTypes } = input; + const { credentials, dataTables, workflows, variables, tags, nodeTypes } = input; const requirements: PackageRequirements = { ...(credentials?.length ? { credentials } : {}), ...(dataTables?.length ? { dataTables } : {}), ...(workflows?.length ? { workflows } : {}), ...(variables?.length ? { variables } : {}), + ...(tags?.length ? { tags } : {}), ...(nodeTypes?.length ? { nodeTypes } : {}), }; return Object.keys(requirements).length > 0 ? requirements : undefined; diff --git a/packages/cli/src/modules/n8n-packages/n8n-packages.types.ts b/packages/cli/src/modules/n8n-packages/n8n-packages.types.ts index 20465136867..442dadcd283 100644 --- a/packages/cli/src/modules/n8n-packages/n8n-packages.types.ts +++ b/packages/cli/src/modules/n8n-packages/n8n-packages.types.ts @@ -2,6 +2,7 @@ import type { User } from '@n8n/db'; import type { Readable } from 'node:stream'; import type { DataTableResolutionFailure } from './entities/data-table/data-table.types'; +import type { TagResolutionFailure } from './entities/tag/tag.types'; import type { VariableLimitFailure, VariableResolutionFailure, @@ -59,9 +60,9 @@ export const MissingNodeTypeMode = { export const MissingWorkflowDependencyPolicy = { /** Fails the export when a workflow dependency is not included. */ Fail: 'fail', - /** Reserved for exporting missing workflow dependencies as requirements only. */ + /** Keeps missing workflow dependencies out of the package, listing them as requirements only. */ ReferenceOnly: 'reference-only', - /** Reserved for automatically adding missing workflow dependencies to the package. */ + /** Automatically adds missing workflow dependencies to the package. */ IncludeInPackage: 'include-in-package', } as const; @@ -99,6 +100,22 @@ export const VariableParentPolicy = { Project: 'project', Global: 'global', } as const; + +export const TagMissingMode = { + /** Creates absent tags with their package (source) id and name. */ + Create: 'create', + /** Workflows import without their missing tags; dropped tags are listed under `tags.skipped`. */ + DoNothing: 'do-nothing', +} as const; + +export const TagConflictPolicy = { + /** Drops conflicted tags from the import: not created, not renamed, not attached anywhere; listed under `tags.skipped`. */ + Skip: 'skip', + /** Blocks the import when any referenced tag conflicts. */ + Fail: 'fail', + /** Renames a drifted target tag (same id, different name) to the package name; a name held by another tag still fails. */ + Rename: 'rename', +} as const; /* eslint-enable @typescript-eslint/naming-convention */ export type WorkflowConflictPolicy = @@ -125,6 +142,10 @@ export type VariableMissingMode = (typeof VariableMissingMode)[keyof typeof Vari export type VariableParentPolicy = (typeof VariableParentPolicy)[keyof typeof VariableParentPolicy]; +export type TagMissingMode = (typeof TagMissingMode)[keyof typeof TagMissingMode]; + +export type TagConflictPolicy = (typeof TagConflictPolicy)[keyof typeof TagConflictPolicy]; + export interface ExportPackageRequest { user: User; workflowIds?: string[]; @@ -132,6 +153,7 @@ export interface ExportPackageRequest { projectIds?: string[]; includeVariableValues?: boolean; canExportVariableValues?: boolean; + includeTags?: boolean; missingWorkflowDependencyPolicy?: MissingWorkflowDependencyPolicy; } @@ -146,7 +168,8 @@ export type ImportPackageRequest = { ImportWorkflowProperties & ImportFolderProperties & ImportDataTableProperties & - ImportVariableProperties; + ImportVariableProperties & + ImportTagProperties; export type ImportCredentialProperties = { credentialMatchingMode: CredentialMatchingMode; @@ -175,6 +198,11 @@ export type ImportVariableProperties = { variableParentPolicy?: VariableParentPolicy; }; +export type ImportTagProperties = { + tagMissingMode: TagMissingMode; + tagConflictPolicy: TagConflictPolicy; +}; + /** * The actor and resolved destination an import writes into. Threaded through * each entity importer so they share one resolved target instead of re-deriving @@ -191,7 +219,8 @@ export interface ImportContext { export type ImportPackageEventOptions = ImportCredentialProperties & ImportWorkflowProperties & ImportDataTableProperties & - ImportVariableProperties; + ImportVariableProperties & + ImportTagProperties; /** Credential ids involved in a package import, shaped for forward-compatible audit events. */ export type ImportAuditCredentialIds = { @@ -226,6 +255,13 @@ export type ImportPackageEventCounts = { created: number; requirements: number; }; + tags: { + matched: number; + created: number; + renamed: number; + skipped: number; + requirements: number; + }; }; /** Per-entity counts for an export, carried on `n8n-package-exported` for telemetry. */ @@ -235,6 +271,7 @@ export type ExportPackageEventCounts = { credentials: number; dataTables: number; variables: number; + tags: number; }; /** @@ -247,12 +284,18 @@ export interface ExportPackageResult { counts: ExportPackageEventCounts; } +/** + * The outcome for one package workflow, folding in what the publish phase decided for it. Import + * writes and publishes in two separate phases, but a consumer cannot act on that distinction, so + * the response reports one row per workflow. + */ export interface ImportedWorkflowSummary { sourceWorkflowId: string; localId: string; name: string; projectId: string; parentFolderId: string | null; + /** Published version on the target instance, or `null` when not published after import. */ activeVersionId: string | null; publishing: WorkflowPublishingOutcome; status: 'created' | 'updated' | 'skipped'; @@ -295,6 +338,7 @@ export type BlockingIssue = } | ({ type: 'folder-conflict' } & FolderConflict) | ({ type: 'data-table-unresolved' } & DataTableResolutionFailure) + | ({ type: 'tag-unresolved' } & TagResolutionFailure) | ({ type: 'variable-unresolved' } & VariableResolutionFailure) | ({ type: 'variable-limit-exceeded' } & VariableLimitFailure) | { @@ -370,6 +414,14 @@ export interface ImportVariableSummary { stubbed: string[]; } +/** Tag names (not ids), grouped by how the import resolved them. */ +export interface ImportTagSummary { + matched: string[]; + created: string[]; + renamed: string[]; + skipped: string[]; +} + export interface ImportResult { package: ImportPackageSummary; workflows: ImportedWorkflowSummary[]; @@ -378,4 +430,5 @@ export interface ImportResult { bindings: SerializedBindings; credentials: ImportCredentialSummary; variables: ImportVariableSummary; + tags: ImportTagSummary; } diff --git a/packages/cli/src/modules/n8n-packages/spec/__tests__/manifest.schema.test.ts b/packages/cli/src/modules/n8n-packages/spec/__tests__/manifest.schema.test.ts index d362a3f5592..d6e25b7b6a6 100644 --- a/packages/cli/src/modules/n8n-packages/spec/__tests__/manifest.schema.test.ts +++ b/packages/cli/src/modules/n8n-packages/spec/__tests__/manifest.schema.test.ts @@ -142,6 +142,18 @@ describe('packageManifestSchema', () => { expect(packageManifestSchema.parse(manifest).variables).toEqual(manifest.variables); }); + it('rejects a manifest containing duplicate tag ids', () => { + const manifest = { + ...validManifest, + tags: [ + { id: 'tag-1', name: 'production', target: 'tags/production' }, + { id: 'tag-1', name: 'production', target: 'tags/production-2' }, + ], + }; + + expect(() => packageManifestSchema.parse(manifest)).toThrow(/duplicate tag id/i); + }); + it('accepts manifests with unknown sections for forward compatibility', () => { const manifest = { ...validManifest, diff --git a/packages/cli/src/modules/n8n-packages/spec/__tests__/requirements.schema.test.ts b/packages/cli/src/modules/n8n-packages/spec/__tests__/requirements.schema.test.ts index 5e017ab980b..79c87fab106 100644 --- a/packages/cli/src/modules/n8n-packages/spec/__tests__/requirements.schema.test.ts +++ b/packages/cli/src/modules/n8n-packages/spec/__tests__/requirements.schema.test.ts @@ -24,6 +24,18 @@ describe('packageRequirementsSchema', () => { expect(() => packageRequirementsSchema.parse(requirements)).not.toThrow(); }); + it('accepts a workflow requirement without a name', () => { + const requirements = { workflows: [{ id: 'wf-dep', usedByWorkflows: ['wf-1'] }] }; + + expect(() => packageRequirementsSchema.parse(requirements)).not.toThrow(); + }); + + it('rejects a workflow requirement with an empty name', () => { + const requirements = { workflows: [{ id: 'wf-dep', name: '', usedByWorkflows: ['wf-1'] }] }; + + expect(() => packageRequirementsSchema.parse(requirements)).toThrow(); + }); + it('rejects duplicate credential ids', () => { const requirements = { credentials: [credential('cred-1'), credential('cred-1')] }; @@ -32,6 +44,13 @@ describe('packageRequirementsSchema', () => { ); }); + it('rejects duplicate tag ids', () => { + const tag = (id: string) => ({ id, name: 'production', usedByWorkflows: ['wf-1'] }); + const requirements = { tags: [tag('tag-1'), tag('tag-1')] }; + + expect(() => packageRequirementsSchema.parse(requirements)).toThrow(/Duplicate tag id: tag-1/); + }); + it('rejects duplicate variable names', () => { const requirements = { variables: [variable('API_URL'), variable('API_URL')] }; diff --git a/packages/cli/src/modules/n8n-packages/spec/manifest.schema.ts b/packages/cli/src/modules/n8n-packages/spec/manifest.schema.ts index 613cb7d7880..1a435fc5f97 100644 --- a/packages/cli/src/modules/n8n-packages/spec/manifest.schema.ts +++ b/packages/cli/src/modules/n8n-packages/spec/manifest.schema.ts @@ -41,6 +41,7 @@ export const packageManifestSchema = z credentials: z.array(manifestEntrySchema).optional(), dataTables: z.array(manifestEntrySchema).optional(), variables: z.array(manifestEntrySchema).optional(), + tags: z.array(manifestEntrySchema).optional(), requirements: packageRequirementsSchema.optional(), }) .superRefine((manifest, ctx) => { @@ -50,6 +51,7 @@ export const packageManifestSchema = z assertNoDuplicateIds(manifest.credentials, 'credential', ctx); assertNoDuplicateIds(manifest.dataTables, 'data table', ctx); assertNoDuplicateIds(manifest.variables, 'variable', ctx); + assertNoDuplicateIds(manifest.tags, 'tag', ctx); }); export type ManifestEntry = z.infer; diff --git a/packages/cli/src/modules/n8n-packages/spec/requirements.schema.ts b/packages/cli/src/modules/n8n-packages/spec/requirements.schema.ts index 03cf5a0de66..7963ea29a90 100644 --- a/packages/cli/src/modules/n8n-packages/spec/requirements.schema.ts +++ b/packages/cli/src/modules/n8n-packages/spec/requirements.schema.ts @@ -13,7 +13,16 @@ export const packageDataTableRequirementSchema = z.object({ usedByWorkflows: z.array(z.string().min(1)).min(1), }); +// `name` is best-effort: a `reference-only` export lists workflows that are +// not in the package, and their name may not be resolvable by the exporting +// user — the id alone identifies the requirement. export const packageWorkflowRequirementSchema = z.object({ + id: z.string().min(1), + name: z.string().min(1).optional(), + usedByWorkflows: z.array(z.string().min(1)).min(1), +}); + +export const packageTagRequirementSchema = z.object({ id: z.string().min(1), name: z.string().min(1), usedByWorkflows: z.array(z.string().min(1)).min(1), @@ -84,6 +93,10 @@ export const packageRequirementsSchema = z.object({ .superRefine((variables, ctx) => assertNoDuplicateKey(variables, ({ name }) => name, 'variable name', ctx), ), + tags: z + .array(packageTagRequirementSchema) + .optional() + .superRefine((tags, ctx) => assertNoDuplicateKey(tags, ({ id }) => id, 'tag id', ctx)), nodeTypes: z .array(packageNodeTypeRequirementSchema) .optional() @@ -100,6 +113,7 @@ export const packageRequirementsSchema = z.object({ export type PackageCredentialRequirement = z.infer; export type PackageDataTableRequirement = z.infer; export type PackageWorkflowRequirement = z.infer; +export type PackageTagRequirement = z.infer; export type PackageVariableRequirement = z.infer; export type PackageNodeTypeRequirement = z.infer; export type PackageRequirements = z.infer; diff --git a/packages/cli/src/modules/n8n-packages/spec/serialized/__tests__/tag.schema.test.ts b/packages/cli/src/modules/n8n-packages/spec/serialized/__tests__/tag.schema.test.ts new file mode 100644 index 00000000000..1ff0a4e3e76 --- /dev/null +++ b/packages/cli/src/modules/n8n-packages/spec/serialized/__tests__/tag.schema.test.ts @@ -0,0 +1,27 @@ +import { serializedTagSchema } from '../tag.schema'; + +describe('serializedTagSchema', () => { + it('accepts an id/name pair', () => { + const tag = { id: 'tag-1', name: 'production' }; + + expect(() => serializedTagSchema.parse(tag)).not.toThrow(); + }); + + it('rejects unknown keys such as timestamps', () => { + const tag = { id: 'tag-1', name: 'production', createdAt: '2026-01-01T00:00:00.000Z' }; + + expect(() => serializedTagSchema.parse(tag)).toThrow(); + }); + + it('rejects an empty name', () => { + const tag = { id: 'tag-1', name: '' }; + + expect(() => serializedTagSchema.parse(tag)).toThrow(); + }); + + it('accepts a supplementary-plane name', () => { + const tag = { id: 'tag-1', name: '😀'.repeat(13) }; + + expect(() => serializedTagSchema.parse(tag)).not.toThrow(); + }); +}); diff --git a/packages/cli/src/modules/n8n-packages/spec/serialized/__tests__/workflow.schema.test.ts b/packages/cli/src/modules/n8n-packages/spec/serialized/__tests__/workflow.schema.test.ts index c8eaefd406a..4cde909f9dc 100644 --- a/packages/cli/src/modules/n8n-packages/spec/serialized/__tests__/workflow.schema.test.ts +++ b/packages/cli/src/modules/n8n-packages/spec/serialized/__tests__/workflow.schema.test.ts @@ -28,4 +28,18 @@ describe('serializedWorkflowSchema', () => { it('rejects a non-finite node typeVersion (JSON `1e999` parses to Infinity)', () => { expect(() => serializedWorkflowSchema.parse(workflow(Infinity))).toThrow(); }); + + it('accepts a non-empty tagIds array', () => { + expect(() => + serializedWorkflowSchema.parse({ ...workflow(1), tagIds: ['tag-1', 'tag-2'] }), + ).not.toThrow(); + }); + + it('accepts an empty tagIds array (an untagged workflow exported with tags)', () => { + expect(() => serializedWorkflowSchema.parse({ ...workflow(1), tagIds: [] })).not.toThrow(); + }); + + it('rejects an empty-string tag id', () => { + expect(() => serializedWorkflowSchema.parse({ ...workflow(1), tagIds: [''] })).toThrow(); + }); }); diff --git a/packages/cli/src/modules/n8n-packages/spec/serialized/tag.schema.ts b/packages/cli/src/modules/n8n-packages/spec/serialized/tag.schema.ts new file mode 100644 index 00000000000..017f540b0f1 --- /dev/null +++ b/packages/cli/src/modules/n8n-packages/spec/serialized/tag.schema.ts @@ -0,0 +1,10 @@ +import { z } from 'zod'; + +export const serializedTagSchema = z + .object({ + id: z.string().min(1), + name: z.string().min(1), + }) + .strict(); + +export type SerializedTag = z.infer; diff --git a/packages/cli/src/modules/n8n-packages/spec/serialized/workflow.schema.ts b/packages/cli/src/modules/n8n-packages/spec/serialized/workflow.schema.ts index bb699c90f0b..6272ff6406e 100644 --- a/packages/cli/src/modules/n8n-packages/spec/serialized/workflow.schema.ts +++ b/packages/cli/src/modules/n8n-packages/spec/serialized/workflow.schema.ts @@ -51,6 +51,7 @@ export const serializedWorkflowSchema = z.object({ parentFolderId: z.string().nullable(), isPublished: z.boolean(), isArchived: z.boolean(), + tagIds: z.array(z.string().min(1)).optional(), }); export type SerializedWorkflow = z.infer; diff --git a/packages/cli/src/modules/oauth-server/__tests__/oauth-consent.controller.api.test.ts b/packages/cli/src/modules/oauth-server/__tests__/oauth-consent.controller.api.test.ts index 23328ba7daf..b49e0706fde 100644 --- a/packages/cli/src/modules/oauth-server/__tests__/oauth-consent.controller.api.test.ts +++ b/packages/cli/src/modules/oauth-server/__tests__/oauth-consent.controller.api.test.ts @@ -1,4 +1,3 @@ -import { MCP_INSTANCE_SCOPES } from '@n8n/api-types'; import { testDb } from '@n8n/backend-test-utils'; import type { User } from '@n8n/db'; import { Container } from '@n8n/di'; @@ -18,6 +17,7 @@ const testServer = setupTestServer({ endpointGroups: ['mcp'], modules: ['oauth-s let owner: User; let member: User; let jwtService: JwtService; +let supportedScopes: string[]; const createSessionToken = (payload: OAuthSessionPayload): string => { return jwtService.sign(payload, { expiresIn: '10m' }); @@ -29,6 +29,7 @@ beforeAll(async () => { member = await createMember(); jwtService = Container.get(JwtService); oauthClientRepository = Container.get(OAuthClientRepository); + supportedScopes = Container.get(ProtectedResourceRegistry).getDefaultResource()?.scopes ?? []; }); afterEach(async () => { @@ -64,7 +65,7 @@ describe('GET /rest/consent/details', () => { clientName: 'Test OAuth Client', clientId: 'test-client-id', redirectUri: 'https://example.com/callback', - scopes: [...MCP_INSTANCE_SCOPES], + scopes: supportedScopes, scopeTools: expect.objectContaining({ 'workflow:read': expect.arrayContaining(['search_workflows']), }), diff --git a/packages/cli/src/modules/oauth-server/__tests__/oauth-server.service.test.ts b/packages/cli/src/modules/oauth-server/__tests__/oauth-server.service.test.ts index 66bec0e1d74..b88f44b7662 100644 --- a/packages/cli/src/modules/oauth-server/__tests__/oauth-server.service.test.ts +++ b/packages/cli/src/modules/oauth-server/__tests__/oauth-server.service.test.ts @@ -2,7 +2,7 @@ import { InvalidGrantError, InvalidTargetError, } from '@modelcontextprotocol/sdk/server/auth/errors.js'; -import { Logger } from '@n8n/backend-common'; +import { Logger, type ModuleRegistry } from '@n8n/backend-common'; import { mockInstance } from '@n8n/backend-test-utils'; import { GlobalConfig } from '@n8n/config'; import type { Response } from 'express'; @@ -1348,6 +1348,7 @@ describe('OAuthServerService', () => { mock(), mcpConfig, mock(), + mock(), ); expect(mcpResource.getResourceUrl()).toBe('https://n8n-mcp.example.com/mcp-server/http'); diff --git a/packages/cli/src/modules/oauth-server/__tests__/oauth-token.service.test.ts b/packages/cli/src/modules/oauth-server/__tests__/oauth-token.service.test.ts index f148575d682..b777de851a1 100644 --- a/packages/cli/src/modules/oauth-server/__tests__/oauth-token.service.test.ts +++ b/packages/cli/src/modules/oauth-server/__tests__/oauth-token.service.test.ts @@ -1,5 +1,5 @@ import type { Mocked } from 'vitest'; -import { Logger } from '@n8n/backend-common'; +import { Logger, type ModuleRegistry } from '@n8n/backend-common'; import { mockInstance } from '@n8n/backend-test-utils'; import type { GlobalConfig } from '@n8n/config'; import type { OperationContext, TransactionRunner, User } from '@n8n/db'; @@ -771,6 +771,7 @@ describe('OAuthTokenService', () => { mock(), mcpConfig, mock(), + mock(), ); const configuredRegistry = new ProtectedResourceRegistry(mock()); diff --git a/packages/cli/src/modules/oauth-server/__tests__/oauth.controller.api.test.ts b/packages/cli/src/modules/oauth-server/__tests__/oauth.controller.api.test.ts index caab7f69921..0ac5a2a4ed9 100644 --- a/packages/cli/src/modules/oauth-server/__tests__/oauth.controller.api.test.ts +++ b/packages/cli/src/modules/oauth-server/__tests__/oauth.controller.api.test.ts @@ -4,13 +4,12 @@ import type { User } from '@n8n/db'; import { ControllerRegistryMetadata, type Controller } from '@n8n/decorators'; import { Container } from '@n8n/di'; +import { McpSettingsService } from '@/modules/mcp/mcp.settings.service'; +import { ProtectedResourceRegistry } from '@/services/protected-resource.registry'; +import { UrlService } from '@/services/url.service'; import { createOwner } from '@test-integration/db/users'; import { setupTestServer } from '@test-integration/utils'; -import { SUPPORTED_SCOPES } from '@/modules/mcp/mcp-protected-resource'; -import { McpSettingsService } from '@/modules/mcp/mcp.settings.service'; -import { UrlService } from '@/services/url.service'; - import { OAuthServerConfig } from '../oauth-server.config'; import type { OAuthController as OAuthControllerClass } from '../oauth.controller'; @@ -18,10 +17,12 @@ const testServer = setupTestServer({ modules: ['oauth-server', 'mcp'], endpointG let owner: User; let mcpSettingsService: McpSettingsService; +let supportedScopes: string[]; beforeAll(async () => { owner = await createOwner(); mcpSettingsService = Container.get(McpSettingsService); + supportedScopes = Container.get(ProtectedResourceRegistry).getAllScopes(); }); afterEach(async () => { @@ -44,7 +45,7 @@ describe('GET /.well-known/oauth-authorization-server', () => { token_endpoint_auth_methods_supported: ['none', 'client_secret_post', 'client_secret_basic'], code_challenge_methods_supported: ['S256'], authorization_response_iss_parameter_supported: true, - ...(SUPPORTED_SCOPES.length > 0 && { scopes_supported: SUPPORTED_SCOPES }), + ...(supportedScopes.length > 0 && { scopes_supported: supportedScopes }), }); }); @@ -101,7 +102,7 @@ describe('GET /.well-known/oauth-protected-resource/mcp-server/http', () => { resource: expect.stringContaining('/mcp-server/http'), bearer_methods_supported: ['header'], authorization_servers: [expect.any(String)], - ...(SUPPORTED_SCOPES.length > 0 && { scopes_supported: SUPPORTED_SCOPES }), + ...(supportedScopes.length > 0 && { scopes_supported: supportedScopes }), }); }); @@ -135,7 +136,7 @@ describe('GET /.well-known/oauth-protected-resource/mcp-server/http', () => { ); expect(response.statusCode).toBe(200); - expect(response.body.scopes_supported).toEqual(SUPPORTED_SCOPES); + expect(response.body.scopes_supported).toEqual(supportedScopes); }); test('should be accessible without authentication', async () => { @@ -156,7 +157,7 @@ describe('GET /.well-known/oauth-protected-resource (bare path)', () => { resource: expect.stringContaining('/mcp-server/http'), bearer_methods_supported: ['header'], authorization_servers: [expect.any(String)], - ...(SUPPORTED_SCOPES.length > 0 && { scopes_supported: SUPPORTED_SCOPES }), + ...(supportedScopes.length > 0 && { scopes_supported: supportedScopes }), }); }); @@ -663,7 +664,7 @@ describe('Full authorization-code flow (PKCE)', () => { authAgent.jar.setCookie(sessionCookie ?? ''); const consentResponse = await authAgent .post('/consent/approve') - .send({ approved: true, scopes: SUPPORTED_SCOPES }); + .send({ approved: true, scopes: supportedScopes }); expect(consentResponse.statusCode).toBe(200); const redirectUrl = new URL(consentResponse.body.data.redirectUrl); @@ -690,7 +691,7 @@ describe('Full authorization-code flow (PKCE)', () => { token_type: 'Bearer', expires_in: 3600, refresh_token: expect.stringMatching(/^[a-f0-9]{64}$/), - scope: SUPPORTED_SCOPES.join(' '), + scope: supportedScopes.join(' '), }); expect(tokenResponse.statusCode).toBe(200); @@ -842,7 +843,7 @@ describe('OAuth server decoupled from MCP access (IAM-798)', () => { authAgent.jar.setCookie(sessionCookie ?? ''); const consentResponse = await authAgent .post('/consent/approve') - .send({ approved: true, scopes: SUPPORTED_SCOPES }); + .send({ approved: true, scopes: supportedScopes }); expect(consentResponse.statusCode).toBe(200); const redirectUrl = new URL(consentResponse.body.data.redirectUrl); @@ -866,7 +867,7 @@ describe('OAuth server decoupled from MCP access (IAM-798)', () => { token_type: 'Bearer', expires_in: 3600, refresh_token: expect.stringMatching(/^[a-f0-9]{64}$/), - scope: SUPPORTED_SCOPES.join(' '), + scope: supportedScopes.join(' '), }); }); }); diff --git a/packages/cli/src/modules/provisioning.ee/__tests__/role-deletion-checker.ee.test.ts b/packages/cli/src/modules/provisioning.ee/__tests__/role-deletion-checker.ee.test.ts index 07cd60b89da..9e1875d0bd2 100644 --- a/packages/cli/src/modules/provisioning.ee/__tests__/role-deletion-checker.ee.test.ts +++ b/packages/cli/src/modules/provisioning.ee/__tests__/role-deletion-checker.ee.test.ts @@ -1,19 +1,27 @@ +import type { ProvisioningConfigDto } from '@n8n/api-types'; import type { RoleMappingRuleRepository } from '@n8n/db'; import { mock } from 'vitest-mock-extended'; import { ProvisioningRoleDeletionChecker } from '../role-deletion-checker.ee'; +import type { ProvisioningService } from '../provisioning.service.ee'; describe('ProvisioningRoleDeletionChecker', () => { const roleMappingRuleRepository = mock(); - const checker = new ProvisioningRoleDeletionChecker(roleMappingRuleRepository); + const provisioningService = mock(); + const checker = new ProvisioningRoleDeletionChecker( + roleMappingRuleRepository, + provisioningService, + ); beforeEach(() => { vi.clearAllMocks(); + roleMappingRuleRepository.count.mockResolvedValue(0); + provisioningService.getConfig.mockResolvedValue( + mock({ defaultInstanceRole: undefined }), + ); }); - it('reports no blockers when no mapping rule references the role', async () => { - roleMappingRuleRepository.count.mockResolvedValue(0); - + it('reports no blockers when the role is unreferenced', async () => { const blockers = await checker.findRoleDeletionBlockers('global:auditor'); expect(roleMappingRuleRepository.count).toHaveBeenCalledWith({ @@ -37,4 +45,28 @@ describe('ProvisioningRoleDeletionChecker', () => { expect(blockers).toEqual(['referenced by 3 role mapping rules']); }); + + it('reports a blocker when the role is the default condition role', async () => { + provisioningService.getConfig.mockResolvedValue( + mock({ defaultInstanceRole: 'global:auditor' }), + ); + + const blockers = await checker.findRoleDeletionBlockers('global:auditor'); + + expect(blockers).toEqual(['configured as the role mapping default condition role']); + }); + + it('reports both blockers when the role is referenced by a mapping rule and is the default condition role', async () => { + roleMappingRuleRepository.count.mockResolvedValue(1); + provisioningService.getConfig.mockResolvedValue( + mock({ defaultInstanceRole: 'global:auditor' }), + ); + + const blockers = await checker.findRoleDeletionBlockers('global:auditor'); + + expect(blockers).toEqual([ + 'referenced by 1 role mapping rule', + 'configured as the role mapping default condition role', + ]); + }); }); diff --git a/packages/cli/src/modules/provisioning.ee/role-deletion-checker.ee.ts b/packages/cli/src/modules/provisioning.ee/role-deletion-checker.ee.ts index 1441f5762f5..298fe22954c 100644 --- a/packages/cli/src/modules/provisioning.ee/role-deletion-checker.ee.ts +++ b/packages/cli/src/modules/provisioning.ee/role-deletion-checker.ee.ts @@ -3,20 +3,36 @@ import { Service } from '@n8n/di'; import type { RoleDeletionChecker } from '@/services/role-deletion-check-proxy.service'; +import { ProvisioningService } from './provisioning.service.ee'; + /** * Blocks deletion of a custom role while it is still targeted by an SSO role - * mapping rule. Registered on the core `RoleDeletionCheckProxy` by the - * provisioning module so `RoleService` stays decoupled from this module. + * mapping rule or configured as the role mapping default condition. + * Registered on the core `RoleDeletionCheckProxy` by the provisioning module + * so `RoleService` stays decoupled from this module. */ @Service() export class ProvisioningRoleDeletionChecker implements RoleDeletionChecker { - constructor(private readonly roleMappingRuleRepository: RoleMappingRuleRepository) {} + constructor( + private readonly roleMappingRuleRepository: RoleMappingRuleRepository, + private readonly provisioningService: ProvisioningService, + ) {} async findRoleDeletionBlockers(roleSlug: string): Promise { + const blockers: string[] = []; + const count = await this.roleMappingRuleRepository.count({ where: { role: { slug: roleSlug } }, }); - if (count === 0) return []; - return [`referenced by ${count} role mapping rule${count === 1 ? '' : 's'}`]; + if (count > 0) { + blockers.push(`referenced by ${count} role mapping rule${count === 1 ? '' : 's'}`); + } + + const { defaultInstanceRole } = await this.provisioningService.getConfig(); + if (defaultInstanceRole === roleSlug) { + blockers.push('configured as the role mapping default condition role'); + } + + return blockers; } } diff --git a/packages/cli/src/modules/source-control.ee/__tests__/source-control-import.service.ee.test.ts b/packages/cli/src/modules/source-control.ee/__tests__/source-control-import.service.ee.test.ts index 05136131c10..f9e735e02e4 100644 --- a/packages/cli/src/modules/source-control.ee/__tests__/source-control-import.service.ee.test.ts +++ b/packages/cli/src/modules/source-control.ee/__tests__/source-control-import.service.ee.test.ts @@ -35,12 +35,14 @@ import type { ActiveWorkflowManager } from '@/active-workflow-manager'; import type { CredentialsService } from '@/credentials/credentials.service'; import type { VariablesService } from '@/environments.ee/variables/variables.service.ee'; import type { ExecutionPersistence } from '@/executions/execution-persistence'; +import { WorkflowPublishBlockedError } from '@/errors/response-errors/workflow-publish-blocked.error'; import type { DataTableColumnRepository } from '@/modules/data-table/data-table-column.repository'; import type { DataTableDDLService } from '@/modules/data-table/data-table-ddl.service'; import type { DataTableSizeValidator } from '@/modules/data-table/data-table-size-validator.service'; import type { DataTableRepository } from '@/modules/data-table/data-table.repository'; import type { RedactionEnforcementService } from '@/modules/redaction/redaction-enforcement.service'; import type { WorkflowHistoryService } from '@/workflows/workflow-history/workflow-history.service'; +import type { WorkflowPublishGuardProxy } from '@/workflows/workflow-publish-guard-proxy.service'; import type { WorkflowService } from '@/workflows/workflow.service'; import type { SourceControlContextFactory } from '../source-control-context.factory'; @@ -73,6 +75,7 @@ describe('SourceControlImportService', () => { const variablesRepository = mock(); const workflowService = mock(); const workflowHistoryService = mock(); + const workflowPublishGuard = mock(); const dataTableRepository = mock(); const dataTableColumnRepository = mock(); const dataTableDDLService = mock(); @@ -123,6 +126,7 @@ describe('SourceControlImportService', () => { dataTableSizeValidator, activeWorkflowManager, executionPersistence, + workflowPublishGuard, ); const globMock = fastGlob.default as unknown as Mock<(...args: string[]) => Promise>; @@ -130,6 +134,7 @@ describe('SourceControlImportService', () => { beforeEach(() => { vi.clearAllMocks(); + workflowPublishGuard.assertCanPublish.mockResolvedValue(undefined); credentialsRepository.find.mockResolvedValue([]); transactionManager.upsert.mockImplementation( async (_entity, value, conflictPaths) => @@ -975,6 +980,106 @@ describe('SourceControlImportService', () => { ); }); + it('keeps the published version running when an open review blocks auto-publish', async () => { + const mockWorkflowFile = '/mock/workflow1.json'; + const mockWorkflowData = { + id: 'workflow1', + name: 'Reviewed Workflow', + versionId: 'v2', + nodes: [], + connections: {}, + parentFolderId: null, + }; + const existingWorkflow = Object.assign(new WorkflowEntity(), { + id: 'workflow1', + active: true, + activeVersionId: 'v1', + versionId: 'v1', + }); + workflowRepository.findByIds.mockResolvedValue([existingWorkflow]); + fsReadFile.mockResolvedValue(JSON.stringify(mockWorkflowData)); + workflowPublishGuard.assertCanPublish.mockRejectedValue( + new WorkflowPublishBlockedError({ + reason: 'review_pending', + workflowReviewRequestId: 'review-1', + }), + ); + + const result = await service.importWorkflowFromWorkFolder( + [mock({ file: mockWorkflowFile, id: 'workflow1' })], + mockUserId, + 'all', + ); + + expect(workflowService.deactivateWorkflow).not.toHaveBeenCalled(); + expect(workflowService.activateWorkflow).not.toHaveBeenCalled(); + expect(workflowRepository.upsert).toHaveBeenCalledWith( + expect.objectContaining({ + versionId: 'v2', + active: true, + activeVersionId: 'v1', + }), + ['id'], + ); + expect(result).toEqual([ + { + id: 'workflow1', + name: mockWorkflowFile, + publishingError: expect.stringContaining('review is open'), + publishingErrorDetails: { + reason: 'review_pending', + workflowReviewRequestId: 'review-1', + }, + }, + ]); + }); + + it('returns review details when publication is blocked after a new workflow is imported', async () => { + const mockWorkflowFile = '/mock/workflow1.json'; + const mockWorkflowData = { + id: 'workflow1', + name: 'New Reviewed Workflow', + versionId: 'v1', + nodes: [], + connections: {}, + parentFolderId: null, + }; + workflowRepository.findByIds.mockResolvedValue([]); + fsReadFile.mockResolvedValue(JSON.stringify(mockWorkflowData)); + workflowService.activateWorkflow.mockRejectedValue( + new WorkflowPublishBlockedError({ + reason: 'changes_requested', + workflowReviewRequestId: 'review-2', + }), + ); + + const result = await service.importWorkflowFromWorkFolder( + [mock({ file: mockWorkflowFile, id: 'workflow1' })], + mockUserId, + 'all', + ); + + expect(workflowRepository.upsert).toHaveBeenCalledWith( + expect.objectContaining({ + versionId: 'v1', + active: false, + activeVersionId: null, + }), + ['id'], + ); + expect(result).toEqual([ + { + id: 'workflow1', + name: mockWorkflowFile, + publishingError: expect.stringContaining('requested changes'), + publishingErrorDetails: { + reason: 'changes_requested', + workflowReviewRequestId: 'review-2', + }, + }, + ]); + }); + it('should publish only previously published workflows with autoPublish="published"', async () => { const mockWorkflowFile1 = '/mock/workflow1.json'; const mockWorkflowFile2 = '/mock/workflow2.json'; diff --git a/packages/cli/src/modules/source-control.ee/__tests__/source-control.service.test.ts b/packages/cli/src/modules/source-control.ee/__tests__/source-control.service.test.ts index 7dbcd28290d..7bbc9509b4f 100644 --- a/packages/cli/src/modules/source-control.ee/__tests__/source-control.service.test.ts +++ b/packages/cli/src/modules/source-control.ee/__tests__/source-control.service.test.ts @@ -514,6 +514,42 @@ describe('SourceControlService', () => { }); describe('pullWorkfolder', () => { + it('adds workflow review details to the pull result when auto-publish is blocked', async () => { + const user = mock({ id: 'user-1' }); + const workflowStatus = mock({ + id: 'workflow-1', + type: 'workflow', + status: 'modified', + location: 'remote', + conflict: false, + }); + mockStatusService.getStatus.mockResolvedValueOnce([workflowStatus]); + sourceControlImportService.importWorkflowFromWorkFolder.mockResolvedValue([ + { + id: 'workflow-1', + name: 'workflow-1.json', + publishingError: 'Workflow review is open', + publishingErrorDetails: { + reason: 'review_pending', + workflowReviewRequestId: 'review-1', + }, + }, + ]); + + const result = await sourceControlService.pullWorkfolder(user, { + force: true, + autoPublish: 'all', + }); + + expect(result.statusResult[0]).toMatchObject({ + publishingError: 'Workflow review is open', + publishingErrorDetails: { + reason: 'review_pending', + workflowReviewRequestId: 'review-1', + }, + }); + }); + it('does not filter locally created credentials', async () => { // ARRANGE const user = mock(); diff --git a/packages/cli/src/modules/source-control.ee/source-control-import.service.ee.ts b/packages/cli/src/modules/source-control.ee/source-control-import.service.ee.ts index d6cfff712f8..2ccd9f8f8f9 100644 --- a/packages/cli/src/modules/source-control.ee/source-control-import.service.ee.ts +++ b/packages/cli/src/modules/source-control.ee/source-control-import.service.ee.ts @@ -1,4 +1,4 @@ -import type { SourceControlledFile } from '@n8n/api-types'; +import type { SourceControlledFile, WorkflowPublishBlockedDetails } from '@n8n/api-types'; import { Logger } from '@n8n/backend-common'; import type { FindOptionsWhere, @@ -40,6 +40,7 @@ import path from 'path'; import { ActiveWorkflowManager } from '@/active-workflow-manager'; import { CredentialsService } from '@/credentials/credentials.service'; import { ExecutionPersistence } from '@/executions/execution-persistence'; +import { WorkflowPublishBlockedError } from '@/errors/response-errors/workflow-publish-blocked.error'; import type { IWorkflowToImport } from '@/interfaces'; import { DataTableColumn } from '@/modules/data-table/data-table-column.entity'; import { DataTableColumnRepository } from '@/modules/data-table/data-table-column.repository'; @@ -54,6 +55,7 @@ import { TagService } from '@/services/tag.service'; import { assertNever } from '@/utils'; import { validateWorkflowNodeGroups, sanitizeNodeGroupDescriptions } from '@/workflow-helpers'; import { WorkflowHistoryService } from '@/workflows/workflow-history/workflow-history.service'; +import { WorkflowPublishGuardProxy } from '@/workflows/workflow-publish-guard-proxy.service'; import { WorkflowService } from '@/workflows/workflow.service'; import { @@ -150,6 +152,7 @@ export class SourceControlImportService { private readonly dataTableSizeValidator: DataTableSizeValidator, private readonly activeWorkflowManager: ActiveWorkflowManager, private readonly executionPersistence: ExecutionPersistence, + private readonly workflowPublishGuard: WorkflowPublishGuardProxy, ) { this.gitFolder = path.join(instanceSettings.n8nFolder, SOURCE_CONTROL_GIT_FOLDER); this.workflowExportFolder = path.join(this.gitFolder, SOURCE_CONTROL_WORKFLOW_EXPORT_FOLDER); @@ -830,14 +833,16 @@ export class SourceControlImportService { importedWorkflow.settings?.redactionPolicy, ); - const { shouldPublishAfterImport, publishingError } = await this.preparePublishStateForImport( - existingWorkflow, - importedWorkflow, - autoPublish, - userId, - ); + const { shouldPublishAfterImport, publishingError, publishingErrorDetails } = + await this.preparePublishStateForImport( + existingWorkflow, + importedWorkflow, + autoPublish, + userId, + ); let finalPublishingError = publishingError; + let finalPublishingErrorDetails = publishingErrorDetails; const parentFolderId = importedWorkflow.parentFolderId ?? ''; @@ -886,6 +891,7 @@ export class SourceControlImportService { const publishResult = await this.publishWorkflow(id, versionId, userId); if (!publishResult.success) { finalPublishingError = publishResult.error; + finalPublishingErrorDetails = publishResult.errorDetails; } } @@ -893,6 +899,9 @@ export class SourceControlImportService { id, name: candidate.file, publishingError: finalPublishingError, + ...(finalPublishingErrorDetails && { + publishingErrorDetails: finalPublishingErrorDetails, + }), }; } @@ -960,7 +969,11 @@ export class SourceControlImportService { workflowId: string, versionId: string, userId: string, - ): Promise<{ success: boolean; error?: string }> { + ): Promise<{ + success: boolean; + error?: string; + errorDetails?: WorkflowPublishBlockedDetails; + }> { const user = await this.userRepository.findOne({ where: { id: userId }, relations: ['role'] }); if (!user) { const errorMessage = `User ${userId} not found, cannot publish workflow ${workflowId}`; @@ -977,7 +990,11 @@ export class SourceControlImportService { } catch (e) { const error = ensureError(e); this.logger.error(`Failed to publish workflow ${workflowId}`, { error }); - return { success: false, error: error.message }; + return { + success: false, + error: error.message, + errorDetails: e instanceof WorkflowPublishBlockedError ? e.details : undefined, + }; } } @@ -2034,7 +2051,11 @@ export class SourceControlImportService { importedWorkflow: IWorkflowToImport, autoPublish: AutoPublishMode, userId: string, - ): Promise<{ shouldPublishAfterImport: boolean; publishingError?: string }> { + ): Promise<{ + shouldPublishAfterImport: boolean; + publishingError?: string; + publishingErrorDetails?: WorkflowPublishBlockedDetails; + }> { const shouldAutoPublishRemote = this.shouldAutoPublishWorkflow( existingWorkflow, importedWorkflow, @@ -2052,7 +2073,19 @@ export class SourceControlImportService { let unpublishedLocal = false; let publishingError: string | undefined; - if (mustUnpublishLocal && existingWorkflow) { + let publishingErrorDetails: WorkflowPublishBlockedDetails | undefined; + if (mustUnpublishLocal && shouldAutoPublishRemote && existingWorkflow) { + try { + await this.workflowPublishGuard.assertCanPublish(existingWorkflow.id); + } catch (error) { + if (!(error instanceof WorkflowPublishBlockedError)) throw error; + + publishingError = error.message; + publishingErrorDetails = error.details; + } + } + + if (mustUnpublishLocal && existingWorkflow && !publishingErrorDetails) { unpublishedLocal = await this.unpublishWorkflow(existingWorkflow.id, userId); if (!unpublishedLocal) { publishingError = 'Failed to unpublish workflow before import'; @@ -2060,7 +2093,9 @@ export class SourceControlImportService { } const shouldPublishAfterImport = - shouldAutoPublishRemote && (!mustUnpublishLocal || unpublishedLocal); + !publishingErrorDetails && + shouldAutoPublishRemote && + (!mustUnpublishLocal || unpublishedLocal); this.resolvePublishedStatus( importedWorkflow, @@ -2069,7 +2104,7 @@ export class SourceControlImportService { unpublishedLocal, ); - return { shouldPublishAfterImport, publishingError }; + return { shouldPublishAfterImport, publishingError, publishingErrorDetails }; } /** diff --git a/packages/cli/src/modules/source-control.ee/source-control.service.ee.ts b/packages/cli/src/modules/source-control.ee/source-control.service.ee.ts index b8aa1043cbd..4c60aa59134 100644 --- a/packages/cli/src/modules/source-control.ee/source-control.service.ee.ts +++ b/packages/cli/src/modules/source-control.ee/source-control.service.ee.ts @@ -579,12 +579,15 @@ export class SourceControlService { statusResult.filter((item) => item.type === 'workflow').map((item) => [item.id, item]), ); - for (const { id, publishingError } of workflowImportResults) { - if (!publishingError) continue; + for (const { id, publishingError, publishingErrorDetails } of workflowImportResults) { + if (!publishingError && !publishingErrorDetails) continue; const statusItem = statusByWorkflowId.get(id); if (statusItem) { statusItem.publishingError = publishingError; + if (publishingErrorDetails) { + statusItem.publishingErrorDetails = publishingErrorDetails; + } } } diff --git a/packages/cli/src/modules/source-control.ee/types/import-result.ts b/packages/cli/src/modules/source-control.ee/types/import-result.ts index ceddb84703c..8d6ff49cc31 100644 --- a/packages/cli/src/modules/source-control.ee/types/import-result.ts +++ b/packages/cli/src/modules/source-control.ee/types/import-result.ts @@ -1,9 +1,11 @@ +import type { WorkflowPublishBlockedDetails } from '@n8n/api-types'; import type { TagEntity, WorkflowTagMapping } from '@n8n/db'; export interface WorkflowImportResult { id: string; name: string; publishingError?: string; + publishingErrorDetails?: WorkflowPublishBlockedDetails; } export interface ImportResult { diff --git a/packages/cli/src/modules/sso-oidc/__tests__/oidc.controller.ee.test.ts b/packages/cli/src/modules/sso-oidc/__tests__/oidc.controller.ee.test.ts index 2dabd66bd64..7ddb62e64ad 100644 --- a/packages/cli/src/modules/sso-oidc/__tests__/oidc.controller.ee.test.ts +++ b/packages/cli/src/modules/sso-oidc/__tests__/oidc.controller.ee.test.ts @@ -235,6 +235,36 @@ describe('OidcController', () => { expect(res.redirect).toHaveBeenCalledWith('/'); }); + test('Should still complete login when storing the ID token throws', async () => { + const req = mock({ + originalUrl: '/sso/oidc/callback?code=auth_code&state=state_value', + browserId: 'browser-id-123', + cookies: { + [OIDC_STATE_COOKIE_NAME]: 'state_value', + [OIDC_NONCE_COOKIE_NAME]: 'nonce_value', + }, + }); + const res = mock(); + + oidcService.loginUser.mockResolvedValueOnce({ user, idToken: 'raw-id-token' }); + oidcService.encryptIdToken.mockRejectedValueOnce(new Error('encryption failed')); + + await controller.callbackHandler(req, res); + + // The ID token cookie is skipped, but login still completes. + expect(res.cookie).not.toHaveBeenCalledWith( + OIDC_ID_TOKEN_COOKIE_NAME, + expect.any(String), + expect.any(Object), + ); + expect(logger.warn).toHaveBeenCalled(); + expect(eventService.emit).toHaveBeenCalledWith('user-logged-in', { + user, + authenticationMethod: 'oidc', + }); + expect(res.redirect).toHaveBeenCalledWith('/'); + }); + test('Should render success page in test mode without creating session', async () => { const req = mock({ originalUrl: '/sso/oidc/callback?code=auth_code&state=state_value', diff --git a/packages/cli/src/modules/sso-oidc/__tests__/oidc.service.ee.test.ts b/packages/cli/src/modules/sso-oidc/__tests__/oidc.service.ee.test.ts index 4d3c9e63bcf..b363192426c 100644 --- a/packages/cli/src/modules/sso-oidc/__tests__/oidc.service.ee.test.ts +++ b/packages/cli/src/modules/sso-oidc/__tests__/oidc.service.ee.test.ts @@ -211,6 +211,7 @@ describe('OidcService', () => { discoveryEndpoint: expect.any(URL), authenticationContextClassReference: expect.any(Array), additionalScopes: '', + rpInitiatedLogoutEnabled: false, }); }); @@ -231,6 +232,7 @@ describe('OidcService', () => { discoveryEndpoint: expect.any(URL), authenticationContextClassReference: [], additionalScopes: '', + rpInitiatedLogoutEnabled: false, }); }); @@ -326,6 +328,7 @@ describe('OidcService', () => { discoveryEndpoint: expect.any(URL), authenticationContextClassReference: expect.any(Array), additionalScopes: '', + rpInitiatedLogoutEnabled: false, }); expect(logger.warn).not.toHaveBeenCalled(); }); @@ -893,4 +896,62 @@ describe('OidcService', () => { expect(body).toEqual({ ok: true, path: '/userinfo' }); }); }); + + describe('generateEndSessionUrl', () => { + const idToken = 'stored-id-token'; + + const setRpInitiatedLogoutEnabled = (enabled: boolean) => { + // Replace (not mutate) the runtime config so the shared default object + // isn't polluted across tests. updateConfig would require live discovery. + const service = oidcService as unknown as { oidcConfig: Record }; + service.oidcConfig = { ...service.oidcConfig, rpInitiatedLogoutEnabled: enabled }; + }; + + it('returns undefined and does not contact the provider when RP-initiated logout is disabled', async () => { + setRpInitiatedLogoutEnabled(false); + // @ts-expect-error - getOidcConfiguration is private + oidcService.getOidcConfiguration = vi.fn(); + + const url = await oidcService.generateEndSessionUrl(idToken); + + expect(url).toBeUndefined(); + // @ts-expect-error - getOidcConfiguration is private + expect(oidcService.getOidcConfiguration).not.toHaveBeenCalled(); + }); + + it('returns undefined when the provider does not advertise an end_session_endpoint', async () => { + setRpInitiatedLogoutEnabled(true); + // @ts-expect-error - getOidcConfiguration is private + oidcService.getOidcConfiguration = vi.fn().mockResolvedValue({ + serverMetadata: () => ({}), + } as unknown as client.Configuration); + + const url = await oidcService.generateEndSessionUrl(idToken); + + expect(url).toBeUndefined(); + }); + + it('builds the RP-initiated logout URL with the id_token_hint when enabled', async () => { + setRpInitiatedLogoutEnabled(true); + // @ts-expect-error - getOidcConfiguration is private + oidcService.getOidcConfiguration = vi.fn().mockResolvedValue({ + serverMetadata: () => ({ end_session_endpoint: 'https://example.com/logout' }), + } as unknown as client.Configuration); + const expectedUrl = new URL('https://example.com/logout?id_token_hint=stored-id-token'); + const buildEndSessionUrl = vi + .spyOn(client, 'buildEndSessionUrl') + .mockReturnValue(expectedUrl); + + const url = await oidcService.generateEndSessionUrl(idToken); + + expect(url).toEqual(expectedUrl); + expect(buildEndSessionUrl).toHaveBeenCalledWith( + expect.anything(), + expect.objectContaining({ + id_token_hint: idToken, + post_logout_redirect_uri: expect.stringMatching(/\/signin$/), + }), + ); + }); + }); }); diff --git a/packages/cli/src/modules/sso-oidc/oidc.controller.ee.ts b/packages/cli/src/modules/sso-oidc/oidc.controller.ee.ts index 3720969827e..1bbdf8fc3ad 100644 --- a/packages/cli/src/modules/sso-oidc/oidc.controller.ee.ts +++ b/packages/cli/src/modules/sso-oidc/oidc.controller.ee.ts @@ -149,20 +149,30 @@ export class OidcController { // RP-Initiated Logout with the required `id_token_hint`. The cookie's // presence also marks this session as OIDC-established, as opposed to // e.g. an email session of the instance owner. + // + // The user is already authenticated (`issueCookie` above), so any failure + // storing the token must not fail the login: it only degrades sign-out to a + // local (n8n-only) logout, same as the oversized-token branch below. if (idToken) { - const encryptedIdToken = await this.oidcService.encryptIdToken(idToken); - if (Buffer.byteLength(encryptedIdToken, 'utf8') <= OIDC_ID_TOKEN_COOKIE_MAX_BYTES) { - const { samesite, secure } = this.globalConfig.auth.cookie; - res.cookie(OIDC_ID_TOKEN_COOKIE_NAME, encryptedIdToken, { - maxAge: this.authService.jwtExpiration * Time.seconds.toMilliseconds, - httpOnly: true, - sameSite: samesite, - secure, + try { + const encryptedIdToken = await this.oidcService.encryptIdToken(idToken); + if (Buffer.byteLength(encryptedIdToken, 'utf8') <= OIDC_ID_TOKEN_COOKIE_MAX_BYTES) { + const { samesite, secure } = this.globalConfig.auth.cookie; + res.cookie(OIDC_ID_TOKEN_COOKIE_NAME, encryptedIdToken, { + maxAge: this.authService.jwtExpiration * Time.seconds.toMilliseconds, + httpOnly: true, + sameSite: samesite, + secure, + }); + } else { + this.logger.warn( + 'The OIDC ID token is too large to be stored in a cookie. Signing out will terminate the n8n session but not the OIDC provider session. Consider reducing the claims included in the ID token.', + ); + } + } catch (error) { + this.logger.warn('Failed to store the OIDC ID token; sign-out will be local only', { + error, }); - } else { - this.logger.warn( - 'The OIDC ID token is too large to be stored in a cookie. Signing out will terminate the n8n session but not the OIDC provider session. Consider reducing the claims included in the ID token.', - ); } } this.eventService.emit('user-logged-in', { @@ -180,6 +190,12 @@ export class OidcController { * the client to redirect to. The n8n session is always invalidated first, * so whatever happens at the provider afterwards cannot leave a valid n8n * session behind. + * + * Trade-off: the returned URL carries the ID token as a URL-encoded + * `id_token_hint`, so it is handed to page JS (the SPA sets `window.location` + * after running its logout hooks / clearing local storage). This is accepted + * for client-side RP-logout; moving the hint into a server-issued redirect so + * it never reaches a JS-readable body is a possible future hardening. */ @Post('/logout') @Licensed('feat:oidc') diff --git a/packages/cli/src/modules/sso-oidc/oidc.service.ee.ts b/packages/cli/src/modules/sso-oidc/oidc.service.ee.ts index 999527a9752..3e962ea3110 100644 --- a/packages/cli/src/modules/sso-oidc/oidc.service.ee.ts +++ b/packages/cli/src/modules/sso-oidc/oidc.service.ee.ts @@ -43,6 +43,7 @@ const DEFAULT_OIDC_CONFIG: OidcConfigDto = { prompt: 'select_account', authenticationContextClassReference: [], additionalScopes: '', + rpInitiatedLogoutEnabled: false, }; type OidcRuntimeConfig = Pick< @@ -53,6 +54,7 @@ type OidcRuntimeConfig = Pick< | 'prompt' | 'authenticationContextClassReference' | 'additionalScopes' + | 'rpInitiatedLogoutEnabled' > & { discoveryEndpoint: URL; }; @@ -428,6 +430,11 @@ export class OidcService { * `end_session_endpoint`, in which case sign-out is local to n8n only. */ async generateEndSessionUrl(idToken: string): Promise { + // RP-Initiated Logout is opt-in: when disabled, sign-out stays local to n8n. + if (!this.oidcConfig.rpInitiatedLogoutEnabled) { + return undefined; + } + await this.loadOpenIdClient(); const configuration = await this.getOidcConfiguration(); diff --git a/packages/cli/src/modules/workflow-reviews.ee/__tests__/workflow-review-feature-gate.service.test.ts b/packages/cli/src/modules/workflow-reviews.ee/__tests__/workflow-review-feature-gate.service.test.ts new file mode 100644 index 00000000000..f834a46b933 --- /dev/null +++ b/packages/cli/src/modules/workflow-reviews.ee/__tests__/workflow-review-feature-gate.service.test.ts @@ -0,0 +1,53 @@ +import type { LicenseState } from '@n8n/backend-common'; +import { mock } from 'vitest-mock-extended'; + +import { WORKFLOW_REVIEWS_ENV_FEATURE_FLAG } from '@/constants/workflow-reviews'; +import type { WorkflowReviewPolicyService } from '@/services/workflow-review-policy.service'; + +import { WorkflowReviewFeatureGate } from '../workflow-review-feature-gate.service'; + +describe('WorkflowReviewFeatureGate', () => { + const originalEnvValue = process.env[WORKFLOW_REVIEWS_ENV_FEATURE_FLAG]; + const licenseState = mock(); + const policyService = mock(); + const featureGate = new WorkflowReviewFeatureGate(licenseState, policyService); + + beforeEach(() => { + vi.resetAllMocks(); + process.env[WORKFLOW_REVIEWS_ENV_FEATURE_FLAG] = 'true'; + licenseState.isWorkflowReviewsLicensed.mockReturnValue(true); + policyService.get.mockResolvedValue({ enabled: true }); + }); + + afterAll(() => { + if (originalEnvValue === undefined) { + delete process.env[WORKFLOW_REVIEWS_ENV_FEATURE_FLAG]; + } else { + process.env[WORKFLOW_REVIEWS_ENV_FEATURE_FLAG] = originalEnvValue; + } + }); + + test('is unavailable when the environment flag is off', async () => { + delete process.env[WORKFLOW_REVIEWS_ENV_FEATURE_FLAG]; + + await expect(featureGate.isAvailable()).resolves.toBe(false); + expect(policyService.get).not.toHaveBeenCalled(); + }); + + test('is unavailable when the license is missing', async () => { + licenseState.isWorkflowReviewsLicensed.mockReturnValue(false); + + await expect(featureGate.isAvailable()).resolves.toBe(false); + expect(policyService.get).not.toHaveBeenCalled(); + }); + + test('is unavailable when the instance policy is disabled', async () => { + policyService.get.mockResolvedValue({ enabled: false }); + + await expect(featureGate.isAvailable()).resolves.toBe(false); + }); + + test('is available when the environment, license, and policy all allow reviews', async () => { + await expect(featureGate.isAvailable()).resolves.toBe(true); + }); +}); diff --git a/packages/cli/src/modules/workflow-reviews.ee/__tests__/workflow-review-inbox.detail.service.test.ts b/packages/cli/src/modules/workflow-reviews.ee/__tests__/workflow-review-inbox.detail.service.test.ts new file mode 100644 index 00000000000..37b28b3f5d0 --- /dev/null +++ b/packages/cli/src/modules/workflow-reviews.ee/__tests__/workflow-review-inbox.detail.service.test.ts @@ -0,0 +1,299 @@ +import type { LicenseState } from '@n8n/backend-common'; +import type { + User, + UserRepository, + WorkflowEntity, + WorkflowHistory, + WorkflowPublishedVersionRepository, + WorkflowReviewRequest, + WorkflowReviewRequestRepository, + WorkflowReviewRequestReviewerRepository, + WorkflowReviewRequestWorkflowRepository, +} from '@n8n/db'; +import { mock } from 'vitest-mock-extended'; + +import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; +import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import type { ProjectService } from '@/services/project.service.ee'; +import type { WorkflowReviewPolicyService } from '@/services/workflow-review-policy.service'; +import type { WorkflowFinderService } from '@/workflows/workflow-finder.service'; +import type { WorkflowHistoryService } from '@/workflows/workflow-history/workflow-history.service'; + +import { WorkflowReviewFeatureGate } from '../workflow-review-feature-gate.service'; +import { WorkflowReviewInboxService } from '../workflow-review-inbox.service'; + +const requestId = 'req-1'; +const workflowId = 'wf-1'; + +/** Plain member: no global scopes, so visibility falls through to project scopes. */ +const member = mock({ id: 'user-1', role: { slug: 'global:member', scopes: [] } }); +const requester = mock({ id: 'requester-1', role: { slug: 'global:member', scopes: [] } }); +const globalPublisher = mock({ + id: 'admin-1', + role: { slug: 'global:admin', scopes: [{ slug: 'workflow:publish' }] }, +}); + +function reviewRequest(overrides: Partial = {}) { + return mock({ + id: requestId, + projectId: 'proj-1', + state: 'open', + decision: 'pending', + title: 'Please review', + description: 'Some context', + createdById: requester.id, + createdAt: new Date('2026-07-01T00:00:00.000Z'), + updatedAt: new Date('2026-07-02T00:00:00.000Z'), + ...overrides, + }); +} + +// A plain object, not a deep mock: the snapshot mapper copies `connections` +// through verbatim, and a proxy there is not comparable with toEqual. +function historyVersion(versionId: string) { + return { + versionId, + workflowId, + nodes: [{ name: `node-${versionId}` }], + connections: {}, + nodeGroups: [], + authors: 'Some Author', + name: 'My workflow', + createdAt: new Date('2026-06-01T00:00:00.000Z'), + } as unknown as WorkflowHistory; +} + +describe('WorkflowReviewInboxService.getDetail', () => { + const workflowReviewPolicyService = mock(); + const workflowFinderService = mock(); + const workflowHistoryService = mock(); + const publishedVersionRepository = mock(); + const requestRepository = mock(); + const workflowRepository = mock(); + const reviewerRepository = mock(); + const userRepository = mock(); + const projectService = mock(); + const licenseState = mock(); + + const service = new WorkflowReviewInboxService( + new WorkflowReviewFeatureGate(licenseState, workflowReviewPolicyService), + workflowFinderService, + workflowHistoryService, + publishedVersionRepository, + requestRepository, + workflowRepository, + reviewerRepository, + userRepository, + projectService, + ); + + beforeEach(() => { + vi.resetAllMocks(); + process.env.N8N_ENV_FEAT_WORKFLOW_REVIEWS = 'true'; + licenseState.isWorkflowReviewsLicensed.mockReturnValue(true); + workflowReviewPolicyService.get.mockResolvedValue({ enabled: true }); + requestRepository.findById.mockResolvedValue(reviewRequest()); + // By default the caller can still read every workflow the review covers + workflowFinderService.findWorkflowForUser.mockResolvedValue(mock()); + workflowRepository.findLinkedWorkflowDetailsByRequestId.mockResolvedValue([]); + reviewerRepository.findByRequestIds.mockResolvedValue([]); + userRepository.findManyByIds.mockResolvedValue([]); + publishedVersionRepository.getPublishedVersionId.mockResolvedValue(null); + workflowHistoryService.findVersion.mockResolvedValue(null); + }); + + /** One child row pinned to `pinnedVersionId`. */ + function mockChildRow( + pinnedVersionId: string | null = 'ver-pinned', + workflowName = 'My workflow', + ) { + workflowRepository.findLinkedWorkflowDetailsByRequestId.mockResolvedValue([ + { workflowId, workflowName, workflowVersionId: pinnedVersionId }, + ]); + } + + describe('when reviews are unavailable', () => { + it('refuses to open a review on an instance without a workflow reviews licence', async () => { + licenseState.isWorkflowReviewsLicensed.mockReturnValue(false); + + await expect(service.getDetail(requester, requestId)).rejects.toThrow(ForbiddenError); + expect(requestRepository.findById).not.toHaveBeenCalled(); + }); + + it('refuses to open a review when an admin has turned reviews off', async () => { + workflowReviewPolicyService.get.mockResolvedValue({ enabled: false }); + + await expect(service.getDetail(requester, requestId)).rejects.toThrow(ForbiddenError); + expect(requestRepository.findById).not.toHaveBeenCalled(); + }); + }); + + describe('who is allowed to open a review', () => { + it('reports a review that does not exist as not found', async () => { + requestRepository.findById.mockResolvedValue(null); + + await expect(service.getDetail(requester, requestId)).rejects.toThrow(NotFoundError); + }); + + it('hides a review from someone who cannot publish in its project, without revealing that it exists', async () => { + projectService.getProjectIdsWithScope.mockResolvedValue(['other-proj']); + + // Same error as a review that does not exist: existence must not leak + await expect(service.getDetail(member, requestId)).rejects.toThrow(NotFoundError); + await expect(service.getDetail(member, requestId)).rejects.toThrow( + 'Could not find review request', + ); + // Nothing beyond the record itself is loaded for someone who cannot see it + expect(workflowRepository.findLinkedWorkflowDetailsByRequestId).not.toHaveBeenCalled(); + }); + + it('lets someone who can publish in the review project open it', async () => { + projectService.getProjectIdsWithScope.mockResolvedValue(['proj-1']); + + const detail = await service.getDetail(member, requestId); + + expect(detail.id).toBe(requestId); + }); + + it('always lets the person who asked for the review open it', async () => { + const detail = await service.getDetail(requester, requestId); + + expect(detail.id).toBe(requestId); + expect(projectService.getProjectIdsWithScope).not.toHaveBeenCalled(); + }); + + it('lets someone who can publish anywhere on the instance open any review', async () => { + const detail = await service.getDetail(globalPublisher, requestId); + + expect(detail.id).toBe(requestId); + expect(projectService.getProjectIdsWithScope).not.toHaveBeenCalled(); + }); + + it('leaves out a workflow the person who asked for the review can no longer read', async () => { + mockChildRow('ver-pinned'); + workflowFinderService.findWorkflowForUser.mockResolvedValue(null); + + const detail = await service.getDetail(requester, requestId); + + // They keep their own review, but none of the workflow content + expect(detail.id).toBe(requestId); + expect(detail.workflows).toEqual([]); + expect(workflowHistoryService.findVersion).not.toHaveBeenCalled(); + }); + + it('hides the review when someone else can read none of the workflows it covers', async () => { + projectService.getProjectIdsWithScope.mockResolvedValue(['proj-1']); + mockChildRow('ver-pinned'); + workflowFinderService.findWorkflowForUser.mockResolvedValue(null); + + await expect(service.getDetail(member, requestId)).rejects.toThrow(NotFoundError); + }); + }); + + describe('what the response contains', () => { + it('returns the review together with the workflows it covers', async () => { + mockChildRow('ver-pinned'); + workflowHistoryService.findVersion.mockResolvedValue(historyVersion('ver-pinned')); + + const detail = await service.getDetail(requester, requestId); + + expect(detail).toMatchObject({ + id: requestId, + projectId: 'proj-1', + state: 'open', + decision: 'pending', + title: 'Please review', + description: 'Some context', + workflowName: 'My workflow', + workflowVersionId: 'ver-pinned', + createdAt: '2026-07-01T00:00:00.000Z', + updatedAt: '2026-07-02T00:00:00.000Z', + }); + expect(detail.workflows).toHaveLength(1); + expect(detail.workflows[0]).toMatchObject({ workflowId, workflowName: 'My workflow' }); + }); + + // A covered workflow is removed along with the workflow itself, so a review can end up with none + it('returns no workflows when the review no longer covers any', async () => { + const detail = await service.getDetail(requester, requestId); + + expect(detail.workflows).toEqual([]); + expect(detail.workflowName).toBeNull(); + expect(detail.workflowVersionId).toBeNull(); + }); + }); + + describe('the two versions to compare', () => { + it('returns the version under review and the published version to compare it against', async () => { + mockChildRow('ver-pinned'); + publishedVersionRepository.getPublishedVersionId.mockResolvedValue('ver-published'); + workflowHistoryService.findVersion.mockImplementation(async (_workflowId, versionId) => + historyVersion(versionId), + ); + + const detail = await service.getDetail(requester, requestId); + + expect(detail.workflows[0]?.pinnedVersion).toEqual({ + versionId: 'ver-pinned', + nodes: [expect.objectContaining({ name: 'node-ver-pinned' })], + connections: {}, + nodeGroups: [], + createdAt: '2026-06-01T00:00:00.000Z', + }); + expect(detail.workflows[0]?.baselineVersion).toMatchObject({ versionId: 'ver-published' }); + }); + + it('has nothing to compare against when the workflow was never published', async () => { + mockChildRow('ver-pinned'); + publishedVersionRepository.getPublishedVersionId.mockResolvedValue(null); + workflowHistoryService.findVersion.mockResolvedValue(historyVersion('ver-pinned')); + + const detail = await service.getDetail(requester, requestId); + + expect(detail.workflows[0]?.pinnedVersion).toMatchObject({ versionId: 'ver-pinned' }); + expect(detail.workflows[0]?.baselineVersion).toBeNull(); + }); + + it('has nothing to compare against when the published version is no longer stored', async () => { + mockChildRow('ver-pinned'); + publishedVersionRepository.getPublishedVersionId.mockResolvedValue('ver-published'); + workflowHistoryService.findVersion.mockImplementation(async (_workflowId, versionId) => + versionId === 'ver-pinned' ? historyVersion(versionId) : null, + ); + + const detail = await service.getDetail(requester, requestId); + + expect(detail.workflows[0]?.baselineVersion).toBeNull(); + }); + + it('returns no version under review when the review does not point at one', async () => { + mockChildRow(null); + + const detail = await service.getDetail(requester, requestId); + + expect(detail.workflows[0]?.pinnedVersion).toBeNull(); + expect(detail.workflows[0]?.workflowVersionId).toBeNull(); + // Nothing is looked up for a workflow that points at no version + expect(workflowHistoryService.findVersion).not.toHaveBeenCalled(); + }); + + it('returns no version under review when it is no longer stored', async () => { + mockChildRow('ver-pruned'); + workflowHistoryService.findVersion.mockResolvedValue(null); + + const detail = await service.getDetail(requester, requestId); + + expect(detail.workflows[0]?.workflowVersionId).toBe('ver-pruned'); + expect(detail.workflows[0]?.pinnedVersion).toBeNull(); + }); + + it('does not reveal who edited a version', async () => { + mockChildRow('ver-pinned'); + workflowHistoryService.findVersion.mockResolvedValue(historyVersion('ver-pinned')); + + const detail = await service.getDetail(requester, requestId); + + expect(detail.workflows[0]?.pinnedVersion).not.toHaveProperty('authors'); + }); + }); +}); diff --git a/packages/cli/src/modules/workflow-reviews.ee/__tests__/workflow-review-request.list.service.test.ts b/packages/cli/src/modules/workflow-reviews.ee/__tests__/workflow-review-inbox.list.service.test.ts similarity index 82% rename from packages/cli/src/modules/workflow-reviews.ee/__tests__/workflow-review-request.list.service.test.ts rename to packages/cli/src/modules/workflow-reviews.ee/__tests__/workflow-review-inbox.list.service.test.ts index e69b0814269..bb064d0813f 100644 --- a/packages/cli/src/modules/workflow-reviews.ee/__tests__/workflow-review-request.list.service.test.ts +++ b/packages/cli/src/modules/workflow-reviews.ee/__tests__/workflow-review-inbox.list.service.test.ts @@ -1,51 +1,38 @@ import { mockInstance } from '@n8n/backend-test-utils'; -import { LicenseState, type Logger } from '@n8n/backend-common'; +import { LicenseState } from '@n8n/backend-common'; import type { - DbLockService, - ProjectRelationRepository, - SharedWorkflowRepository, User, UserRepository, + WorkflowPublishedVersionRepository, WorkflowReviewRequest, WorkflowReviewRequestReviewerRepository, } from '@n8n/db'; -import { - WorkflowReviewRequestAuthorRepository, - WorkflowReviewRequestRepository, - WorkflowReviewRequestWorkflowRepository, -} from '@n8n/db'; +import { WorkflowReviewRequestRepository, WorkflowReviewRequestWorkflowRepository } from '@n8n/db'; import { mock } from 'vitest-mock-extended'; -import type { CollaborationService } from '@/collaboration/collaboration.service'; import { ProjectService } from '@/services/project.service.ee'; -import type { RoleService } from '@/services/role.service'; import { WorkflowReviewPolicyService } from '@/services/workflow-review-policy.service'; import type { WorkflowFinderService } from '@/workflows/workflow-finder.service'; import type { WorkflowHistoryService } from '@/workflows/workflow-history/workflow-history.service'; -import { WorkflowReviewRequestService } from '../workflow-review-request.service'; +import { WorkflowReviewFeatureGate } from '../workflow-review-feature-gate.service'; +import { WorkflowReviewInboxService } from '../workflow-review-inbox.service'; -describe('WorkflowReviewRequestService list', () => { - const logger = mock(); +describe('WorkflowReviewInboxService', () => { const workflowReviewPolicyService = mockInstance(WorkflowReviewPolicyService); const workflowFinderService = mock(); const workflowHistoryService = mock(); - const sharedWorkflowRepository = mock(); + const publishedVersionRepository = mock(); const workflowReviewRequestRepository = mockInstance(WorkflowReviewRequestRepository); const workflowReviewRequestWorkflowRepository = mockInstance( WorkflowReviewRequestWorkflowRepository, ); - const workflowReviewRequestAuthorRepository = mockInstance(WorkflowReviewRequestAuthorRepository); const reviewerRepository = mock(); const userRepository = mock(); - const projectRelationRepository = mock(); - const roleService = mock(); const projectService = mockInstance(ProjectService); const licenseState = mockInstance(LicenseState); - const dbLockService = mock(); - const collaborationService = mock(); - let service: WorkflowReviewRequestService; + let service: WorkflowReviewInboxService; const user = mock({ id: 'user-1', role: { slug: 'global:member', scopes: [] } }); @@ -57,23 +44,16 @@ describe('WorkflowReviewRequestService list', () => { reviewerRepository.findByRequestIds.mockResolvedValue([]); userRepository.findManyByIds.mockResolvedValue([]); - service = new WorkflowReviewRequestService( - logger, - workflowReviewPolicyService, + service = new WorkflowReviewInboxService( + new WorkflowReviewFeatureGate(licenseState, workflowReviewPolicyService), workflowFinderService, workflowHistoryService, - sharedWorkflowRepository, + publishedVersionRepository, workflowReviewRequestRepository, workflowReviewRequestWorkflowRepository, - workflowReviewRequestAuthorRepository, reviewerRepository, userRepository, - projectRelationRepository, - roleService, projectService, - licenseState, - dbLockService, - collaborationService, ); }); diff --git a/packages/cli/src/modules/workflow-reviews.ee/__tests__/workflow-review-publish-guard.service.test.ts b/packages/cli/src/modules/workflow-reviews.ee/__tests__/workflow-review-publish-guard.service.test.ts new file mode 100644 index 00000000000..c8e99df91a6 --- /dev/null +++ b/packages/cli/src/modules/workflow-reviews.ee/__tests__/workflow-review-publish-guard.service.test.ts @@ -0,0 +1,81 @@ +import type { WorkflowReviewRequest, WorkflowReviewRequestRepository } from '@n8n/db'; +import { mock } from 'vitest-mock-extended'; + +import type { WorkflowPublishBlockedError } from '@/errors/response-errors/workflow-publish-blocked.error'; + +import type { WorkflowReviewFeatureGate } from '../workflow-review-feature-gate.service'; +import { WorkflowReviewPublishGuard } from '../workflow-review-publish-guard.service'; + +describe('WorkflowReviewPublishGuard', () => { + const featureGate = mock(); + const requestRepository = mock(); + const guard = new WorkflowReviewPublishGuard(featureGate, requestRepository); + + beforeEach(() => { + vi.resetAllMocks(); + }); + + test('does not query reviews when workflow reviews are unavailable', async () => { + featureGate.isAvailable.mockResolvedValue(false); + + await expect(guard.assertCanPublish('workflow-1')).resolves.toBeUndefined(); + + expect(requestRepository.findOpenRequestForWorkflow).not.toHaveBeenCalled(); + }); + + test('allows publication when the workflow has no open review', async () => { + featureGate.isAvailable.mockResolvedValue(true); + requestRepository.findOpenRequestForWorkflow.mockResolvedValue(null); + + await expect(guard.assertCanPublish('workflow-1')).resolves.toBeUndefined(); + }); + + test('blocks publication while a review is waiting for a decision', async () => { + featureGate.isAvailable.mockResolvedValue(true); + requestRepository.findOpenRequestForWorkflow.mockResolvedValue( + mock({ id: 'review-1', decision: 'pending' }), + ); + + await expect(guard.assertCanPublish('workflow-1')).rejects.toMatchObject({ + httpStatusCode: 409, + details: { + reason: 'review_pending', + workflowReviewRequestId: 'review-1', + }, + }); + }); + + test('explains when requested changes are blocking publication', async () => { + featureGate.isAvailable.mockResolvedValue(true); + requestRepository.findOpenRequestForWorkflow.mockResolvedValue( + mock({ id: 'review-2', decision: 'changes_requested' }), + ); + + await expect(guard.assertCanPublish('workflow-1')).rejects.toMatchObject({ + httpStatusCode: 409, + details: { + reason: 'changes_requested', + workflowReviewRequestId: 'review-2', + }, + }); + }); + + test('treats an unfamiliar open-review decision as still waiting for review', async () => { + featureGate.isAvailable.mockResolvedValue(true); + requestRepository.findOpenRequestForWorkflow.mockResolvedValue( + mock({ + id: 'review-3', + decision: 'future_decision' as WorkflowReviewRequest['decision'], + }), + ); + + await expect(guard.assertCanPublish('workflow-1')).rejects.toEqual( + expect.objectContaining>({ + details: { + reason: 'review_pending', + workflowReviewRequestId: 'review-3', + }, + }), + ); + }); +}); diff --git a/packages/cli/src/modules/workflow-reviews.ee/__tests__/workflow-review-request.decide.service.test.ts b/packages/cli/src/modules/workflow-reviews.ee/__tests__/workflow-review-request.decide.service.test.ts index 65f99aa1cf5..6064c5c9526 100644 --- a/packages/cli/src/modules/workflow-reviews.ee/__tests__/workflow-review-request.decide.service.test.ts +++ b/packages/cli/src/modules/workflow-reviews.ee/__tests__/workflow-review-request.decide.service.test.ts @@ -7,6 +7,7 @@ import type { User, UserRepository, WorkflowEntity, + WorkflowPublishHistoryRepository, WorkflowReviewRequest, WorkflowReviewRequestAuthorRepository, WorkflowReviewRequestRepository, @@ -22,12 +23,12 @@ import type { CollaborationService } from '@/collaboration/collaboration.service import { ConflictError } from '@/errors/response-errors/conflict.error'; import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; import { NotFoundError } from '@/errors/response-errors/not-found.error'; -import type { ProjectService } from '@/services/project.service.ee'; import type { RoleService } from '@/services/role.service'; import type { WorkflowReviewPolicyService } from '@/services/workflow-review-policy.service'; import type { WorkflowFinderService } from '@/workflows/workflow-finder.service'; import type { WorkflowHistoryService } from '@/workflows/workflow-history/workflow-history.service'; +import { WorkflowReviewFeatureGate } from '../workflow-review-feature-gate.service'; import { WorkflowReviewRequestService } from '../workflow-review-request.service'; const memberUser = (id = 'user-1') => mock({ id, role: { slug: 'global:member' } }); @@ -42,6 +43,7 @@ describe('WorkflowReviewRequestService.decide', () => { const workflowFinderService = mock(); const workflowHistoryService = mock(); const sharedWorkflowRepository = mock(); + const publishHistoryRepository = mock(); const requestRepository = mock(); const workflowRepository = mock(); const authorRepository = mock(); @@ -49,7 +51,6 @@ describe('WorkflowReviewRequestService.decide', () => { const userRepository = mock(); const projectRelationRepository = mock(); const roleService = mock(); - const projectService = mock(); const licenseState = mock(); const dbLockService = mock(); const collaborationService = mock(); @@ -58,10 +59,11 @@ describe('WorkflowReviewRequestService.decide', () => { const service = new WorkflowReviewRequestService( logger, - workflowReviewPolicyService, + new WorkflowReviewFeatureGate(licenseState, workflowReviewPolicyService), workflowFinderService, workflowHistoryService, sharedWorkflowRepository, + publishHistoryRepository, requestRepository, workflowRepository, authorRepository, @@ -69,8 +71,6 @@ describe('WorkflowReviewRequestService.decide', () => { userRepository, projectRelationRepository, roleService, - projectService, - licenseState, dbLockService, collaborationService, ); diff --git a/packages/cli/src/modules/workflow-reviews.ee/__tests__/workflow-review-request.service.test.ts b/packages/cli/src/modules/workflow-reviews.ee/__tests__/workflow-review-request.service.test.ts index 9e20224c099..6e0ab066982 100644 --- a/packages/cli/src/modules/workflow-reviews.ee/__tests__/workflow-review-request.service.test.ts +++ b/packages/cli/src/modules/workflow-reviews.ee/__tests__/workflow-review-request.service.test.ts @@ -12,9 +12,11 @@ import type { SharedWorkflowRepository, UserRepository, WorkflowEntity, + WorkflowPublishHistoryRepository, WorkflowReviewRequest, WorkflowReviewRequestAuthorRepository, WorkflowReviewRequestRepository, + WorkflowReviewRequestForWorkflowRow, WorkflowReviewRequestReviewerRepository, WorkflowReviewRequestWorkflowRepository, } from '@n8n/db'; @@ -27,12 +29,12 @@ import { BadRequestError } from '@/errors/response-errors/bad-request.error'; import { ConflictError } from '@/errors/response-errors/conflict.error'; import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; import { NotFoundError } from '@/errors/response-errors/not-found.error'; -import type { ProjectService } from '@/services/project.service.ee'; import type { RoleService } from '@/services/role.service'; import type { WorkflowReviewPolicyService } from '@/services/workflow-review-policy.service'; import type { WorkflowFinderService } from '@/workflows/workflow-finder.service'; import type { WorkflowHistoryService } from '@/workflows/workflow-history/workflow-history.service'; +import { WorkflowReviewFeatureGate } from '../workflow-review-feature-gate.service'; import { WorkflowReviewRequestService } from '../workflow-review-request.service'; const user = mock({ id: 'user-1' }); @@ -55,6 +57,7 @@ describe('WorkflowReviewRequestService', () => { const workflowFinderService = mock(); const workflowHistoryService = mock(); const sharedWorkflowRepository = mock(); + const publishHistoryRepository = mock(); const requestRepository = mock(); const workflowRepository = mock(); const authorRepository = mock(); @@ -62,7 +65,6 @@ describe('WorkflowReviewRequestService', () => { const userRepository = mock(); const projectRelationRepository = mock(); const roleService = mock(); - const projectService = mock(); const licenseState = mock(); const dbLockService = mock(); const collaborationService = mock(); @@ -71,10 +73,11 @@ describe('WorkflowReviewRequestService', () => { const service = new WorkflowReviewRequestService( logger, - workflowReviewPolicyService, + new WorkflowReviewFeatureGate(licenseState, workflowReviewPolicyService), workflowFinderService, workflowHistoryService, sharedWorkflowRepository, + publishHistoryRepository, requestRepository, workflowRepository, authorRepository, @@ -82,8 +85,6 @@ describe('WorkflowReviewRequestService', () => { userRepository, projectRelationRepository, roleService, - projectService, - licenseState, dbLockService, collaborationService, ); @@ -411,11 +412,38 @@ describe('WorkflowReviewRequestService', () => { describe('list', () => { const query = mock({ workflowId: 'wf-1', - state: 'open', skip: 0, take: 1, }); + const latestReviewRow = ( + overrides: Partial = {}, + ): WorkflowReviewRequestForWorkflowRow => ({ + id: 'req-1', + state: 'open', + decision: 'pending', + updatedById: 'user-2', + workflowVersionId: 'ver-1', + createdAt: new Date('2024-01-01T00:00:00.000Z'), + updatedAt: new Date('2024-01-02T00:00:00.000Z'), + ...overrides, + }); + + const mockLatestReview = (overrides: Partial = {}) => { + workflowFinderService.findWorkflowForUser.mockResolvedValue(mock()); + requestRepository.findRequestsForWorkflow.mockResolvedValue([ + [latestReviewRow(overrides)], + 1, + ]); + }; + + const reviewer = loadedUser({ + id: 'user-2', + email: 'reviewer@example.com', + firstName: 'Rey', + lastName: 'Viewer', + }); + it('throws NotFoundError when the user has no read access to the workflow', async () => { workflowFinderService.findWorkflowForUser.mockResolvedValue(null); @@ -426,5 +454,158 @@ describe('WorkflowReviewRequestService', () => { ]); expect(requestRepository.findRequestsForWorkflow).not.toHaveBeenCalled(); }); + + it('throws when the instance policy is disabled, before any lookup', async () => { + workflowReviewPolicyService.get.mockResolvedValue({ enabled: false }); + + await expect(service.list(user, query)).rejects.toThrow(ForbiddenError); + + expect(workflowFinderService.findWorkflowForUser).not.toHaveBeenCalled(); + expect(requestRepository.findRequestsForWorkflow).not.toHaveBeenCalled(); + }); + + it('resolves the decision actor of a changes-requested review', async () => { + mockLatestReview({ decision: 'changes_requested' }); + userRepository.findManyByIds.mockResolvedValue([reviewer]); + + const { count, data } = await service.list(user, query); + + expect(userRepository.findManyByIds).toHaveBeenCalledWith(['user-2']); + expect(count).toBe(1); + expect(data).toEqual([ + { + id: 'req-1', + state: 'open', + decision: 'changes_requested', + workflowVersionId: 'ver-1', + createdAt: '2024-01-01T00:00:00.000Z', + updatedAt: '2024-01-02T00:00:00.000Z', + decisionBy: { + id: 'user-2', + email: 'reviewer@example.com', + firstName: 'Rey', + lastName: 'Viewer', + }, + approvedVersionPublicationState: null, + }, + ]); + expect(publishHistoryRepository.getVersionPublicationStates).not.toHaveBeenCalled(); + }); + + it('falls back to no actor when the deciding user was deleted', async () => { + mockLatestReview({ decision: 'changes_requested' }); + userRepository.findManyByIds.mockResolvedValue([]); + + const { data } = await service.list(user, query); + + expect(data[0]?.decisionBy).toBeNull(); + }); + + it('resolves no actor when the decision records none', async () => { + mockLatestReview({ decision: 'changes_requested', updatedById: null }); + + const { data } = await service.list(user, query); + + expect(userRepository.findManyByIds).not.toHaveBeenCalled(); + expect(data[0]?.decisionBy).toBeNull(); + }); + + it('derives the publication state of an approved review only', async () => { + mockLatestReview({ state: 'closed', decision: 'approved' }); + publishHistoryRepository.getVersionPublicationStates.mockResolvedValue( + new Map([['ver-1', 'not_published']]), + ); + + const { data } = await service.list(user, query); + + // Batched across rows so the list cannot become an N+1 + expect(publishHistoryRepository.getVersionPublicationStates).toHaveBeenCalledWith('wf-1', [ + 'ver-1', + ]); + expect(data[0]).toMatchObject({ + decisionBy: null, + approvedVersionPublicationState: 'not_published', + }); + expect(userRepository.findManyByIds).not.toHaveBeenCalled(); + }); + + it('reports unknown for an approved review whose pinned version was pruned', async () => { + mockLatestReview({ state: 'closed', decision: 'approved', workflowVersionId: null }); + + const { data } = await service.list(user, query); + + // No version to reason about, so the state is settled without a lookup + expect(publishHistoryRepository.getVersionPublicationStates).not.toHaveBeenCalled(); + expect(data[0]).toMatchObject({ approvedVersionPublicationState: 'unknown' }); + }); + + it('derives neither field for a pending review', async () => { + mockLatestReview(); + + const { data } = await service.list(user, query); + + expect(userRepository.findManyByIds).not.toHaveBeenCalled(); + expect(publishHistoryRepository.getVersionPublicationStates).not.toHaveBeenCalled(); + expect(data[0]).toMatchObject({ + decisionBy: null, + approvedVersionPublicationState: null, + }); + }); + + // R1 (P3): enriching row by row made this list an N+1 — see LIGO-607_review.md + it('enriches many rows with one lookup per derived field', async () => { + workflowFinderService.findWorkflowForUser.mockResolvedValue(mock()); + requestRepository.findRequestsForWorkflow.mockResolvedValue([ + [ + latestReviewRow({ id: 'req-1', decision: 'changes_requested', updatedById: 'user-2' }), + latestReviewRow({ id: 'req-2', decision: 'changes_requested', updatedById: 'user-3' }), + latestReviewRow({ + id: 'req-3', + state: 'closed', + decision: 'approved', + workflowVersionId: 'ver-3', + }), + latestReviewRow({ + id: 'req-4', + state: 'closed', + decision: 'approved', + workflowVersionId: 'ver-4', + }), + ], + 4, + ]); + userRepository.findManyByIds.mockResolvedValue([ + reviewer, + loadedUser({ id: 'user-3', email: 'other@example.com' }), + ]); + publishHistoryRepository.getVersionPublicationStates.mockResolvedValue( + new Map([ + ['ver-3', 'not_published'], + ['ver-4', 'superseded'], + ]), + ); + + const { data } = await service.list(user, query); + + expect(userRepository.findManyByIds).toHaveBeenCalledTimes(1); + expect(userRepository.findManyByIds).toHaveBeenCalledWith(['user-2', 'user-3']); + expect(publishHistoryRepository.getVersionPublicationStates).toHaveBeenCalledTimes(1); + expect(publishHistoryRepository.getVersionPublicationStates).toHaveBeenCalledWith('wf-1', [ + 'ver-3', + 'ver-4', + ]); + expect(data.map((item) => item.decisionBy?.email ?? null)).toEqual([ + 'reviewer@example.com', + 'other@example.com', + null, + null, + ]); + expect(data.map((item) => item.approvedVersionPublicationState)).toEqual([ + null, + null, + 'not_published', + 'superseded', + ]); + }); }); }); diff --git a/packages/cli/src/modules/workflow-reviews.ee/__tests__/workflow-review-request.update.service.test.ts b/packages/cli/src/modules/workflow-reviews.ee/__tests__/workflow-review-request.update.service.test.ts index 362b3315646..cd205413483 100644 --- a/packages/cli/src/modules/workflow-reviews.ee/__tests__/workflow-review-request.update.service.test.ts +++ b/packages/cli/src/modules/workflow-reviews.ee/__tests__/workflow-review-request.update.service.test.ts @@ -7,6 +7,7 @@ import type { User, UserRepository, WorkflowEntity, + WorkflowPublishHistoryRepository, WorkflowReviewRequest, WorkflowReviewRequestAuthorRepository, WorkflowReviewRequestRepository, @@ -23,12 +24,12 @@ import { BadRequestError } from '@/errors/response-errors/bad-request.error'; import { ConflictError } from '@/errors/response-errors/conflict.error'; import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; import { NotFoundError } from '@/errors/response-errors/not-found.error'; -import type { ProjectService } from '@/services/project.service.ee'; import type { RoleService } from '@/services/role.service'; import type { WorkflowReviewPolicyService } from '@/services/workflow-review-policy.service'; import type { WorkflowFinderService } from '@/workflows/workflow-finder.service'; import type { WorkflowHistoryService } from '@/workflows/workflow-history/workflow-history.service'; +import { WorkflowReviewFeatureGate } from '../workflow-review-feature-gate.service'; import { WorkflowReviewRequestService } from '../workflow-review-request.service'; const user = mock({ id: 'user-1' }); @@ -44,6 +45,7 @@ describe('WorkflowReviewRequestService.updateVersion', () => { const workflowFinderService = mock(); const workflowHistoryService = mock(); const sharedWorkflowRepository = mock(); + const publishHistoryRepository = mock(); const requestRepository = mock(); const workflowRepository = mock(); const authorRepository = mock(); @@ -51,7 +53,6 @@ describe('WorkflowReviewRequestService.updateVersion', () => { const userRepository = mock(); const projectRelationRepository = mock(); const roleService = mock(); - const projectService = mock(); const licenseState = mock(); const dbLockService = mock(); const collaborationService = mock(); @@ -60,10 +61,11 @@ describe('WorkflowReviewRequestService.updateVersion', () => { const service = new WorkflowReviewRequestService( logger, - workflowReviewPolicyService, + new WorkflowReviewFeatureGate(licenseState, workflowReviewPolicyService), workflowFinderService, workflowHistoryService, sharedWorkflowRepository, + publishHistoryRepository, requestRepository, workflowRepository, authorRepository, @@ -71,8 +73,6 @@ describe('WorkflowReviewRequestService.updateVersion', () => { userRepository, projectRelationRepository, roleService, - projectService, - licenseState, dbLockService, collaborationService, ); diff --git a/packages/cli/src/modules/workflow-reviews.ee/__tests__/workflow-review-requests.controller.integration.test.ts b/packages/cli/src/modules/workflow-reviews.ee/__tests__/workflow-review-requests.controller.integration.test.ts index 286908874e3..4312700823b 100644 --- a/packages/cli/src/modules/workflow-reviews.ee/__tests__/workflow-review-requests.controller.integration.test.ts +++ b/packages/cli/src/modules/workflow-reviews.ee/__tests__/workflow-review-requests.controller.integration.test.ts @@ -5,26 +5,36 @@ import { createWorkflow, getPersonalProject, linkUserToProject, + mockInstance, testDb, } from '@n8n/backend-test-utils'; import type { Project, User } from '@n8n/db'; import { UserRepository, + WorkflowPublishHistoryRepository, + WorkflowPublishedVersionRepository, + WorkflowRepository, WorkflowReviewRequestAuthorRepository, WorkflowReviewRequestRepository, WorkflowReviewRequestReviewerRepository, WorkflowReviewRequestWorkflowRepository, } from '@n8n/db'; import { Container } from '@n8n/di'; +import { v4 as uuid } from 'uuid'; +import { ActiveWorkflowManager } from '@/active-workflow-manager'; import { WorkflowReviewPolicyService } from '@/services/workflow-review-policy.service'; +import { WorkflowValidationService } from '@/workflows/workflow-validation.service'; import { createAdmin, createMember, createOwner, createUser } from '@test-integration/db/users'; import { createWorkflowHistoryItem } from '@test-integration/db/workflow-history'; import type { SuperAgentTest } from '@test-integration/types'; import * as utils from '@test-integration/utils'; +mockInstance(ActiveWorkflowManager); +const workflowValidationService = mockInstance(WorkflowValidationService); + const testServer = utils.setupTestServer({ - endpointGroups: ['workflow-reviews'], + endpointGroups: ['workflow-reviews', 'workflows'], enabledFeatures: ['feat:workflowReviews'], modules: ['workflow-reviews'], }); @@ -43,14 +53,21 @@ let workflowRepository: WorkflowReviewRequestWorkflowRepository; let authorRepository: WorkflowReviewRequestAuthorRepository; let reviewerRepository: WorkflowReviewRequestReviewerRepository; let userRepository: UserRepository; +let publishedVersionRepository: WorkflowPublishedVersionRepository; +let publishHistoryRepository: WorkflowPublishHistoryRepository; +let workflowEntityRepository: WorkflowRepository; let policyService: WorkflowReviewPolicyService; -beforeAll(() => { +beforeAll(async () => { + await utils.initNodeTypes(); requestRepository = Container.get(WorkflowReviewRequestRepository); workflowRepository = Container.get(WorkflowReviewRequestWorkflowRepository); authorRepository = Container.get(WorkflowReviewRequestAuthorRepository); reviewerRepository = Container.get(WorkflowReviewRequestReviewerRepository); userRepository = Container.get(UserRepository); + publishedVersionRepository = Container.get(WorkflowPublishedVersionRepository); + publishHistoryRepository = Container.get(WorkflowPublishHistoryRepository); + workflowEntityRepository = Container.get(WorkflowRepository); policyService = Container.get(WorkflowReviewPolicyService); }); @@ -64,8 +81,12 @@ beforeEach(async () => { 'WorkflowReviewRequestWorkflow', 'WorkflowReviewRequest', 'SharedWorkflow', - 'WorkflowHistory', + // Before WorkflowHistory: the published pointer FKs onto it with onDelete RESTRICT + 'WorkflowPublishedVersion', + 'WorkflowPublicationOutbox', + 'WorkflowPublishHistory', 'WorkflowEntity', + 'WorkflowHistory', 'ProjectRelation', 'Project', 'User', @@ -74,6 +95,9 @@ beforeEach(async () => { // The instance policy defaults to disabled; enable it so the feature is // available. Individual tests may disable it again to assert the gate. await policyService.set(true); + workflowValidationService.validateForActivation.mockReturnValue({ isValid: true }); + workflowValidationService.validateDynamicCredentials.mockResolvedValue({ isValid: true }); + workflowValidationService.validateSubWorkflowReferences.mockResolvedValue({ isValid: true }); owner = await createOwner(); member = await createMember(); @@ -89,12 +113,35 @@ beforeEach(async () => { }); /** Create a workflow owned by `owner` with a pinned history version. */ -async function createReviewableWorkflow(versionId = 'version-1') { +async function createReviewableWorkflow(versionId = uuid()) { const workflow = await createWorkflow({}, owner); await createWorkflowHistoryItem(workflow.id, { versionId }); return { workflow, versionId }; } +async function createOpenReview( + workflowId: string, + versionId: string, + decision: 'pending' | 'changes_requested' = 'pending', +) { + const request = await requestRepository.createRequest({ + projectId: ownerProject.id, + title: 'Review before publishing', + createdById: owner.id, + decision, + }); + await workflowRepository.createWorkflowRow({ + workflowReviewRequestId: request.id, + workflowId, + workflowVersionId: versionId, + }); + await authorRepository.addAuthor({ + workflowReviewRequestId: request.id, + userId: owner.id, + }); + return request; +} + describe('POST /workflow-review-requests', () => { test('creates a review request with its workflow reference and author', async () => { const { workflow, versionId } = await createReviewableWorkflow(); @@ -484,6 +531,82 @@ describe('POST /workflow-review-requests', () => { }); }); +describe('publishing a workflow under review', () => { + test.each([ + ['waiting for a decision', 'pending', 'review_pending'], + ['waiting for requested changes', 'changes_requested', 'changes_requested'], + ] as const)( + 'blocks publication while the review is %s', + async (_reviewState, decision, expectedReason) => { + const { workflow, versionId } = await createReviewableWorkflow(); + const request = await createOpenReview(workflow.id, versionId, decision); + + const response = await ownerAgent + .post(`/workflows/${workflow.id}/activate`) + .send({ versionId }) + .expect(409); + + expect(response.body.meta).toEqual({ + reason: expectedReason, + workflowReviewRequestId: request.id, + validationError: true, + }); + expect( + (await workflowEntityRepository.findOneByOrFail({ id: workflow.id })).activeVersionId, + ).toBeNull(); + }, + ); + + test('allows publication after the review is approved and closed', async () => { + const { workflow, versionId } = await createReviewableWorkflow(); + const request = await createOpenReview(workflow.id, versionId); + + await ownerAgent + .post(`/workflow-review-requests/${request.id}/decision`) + .send({ decision: 'approved' }) + .expect(200); + + await ownerAgent.post(`/workflows/${workflow.id}/activate`).send({ versionId }).expect(200); + + expect( + (await workflowEntityRepository.findOneByOrFail({ id: workflow.id })).activeVersionId, + ).toBe(versionId); + }); + + test.each(['policy disabled', 'license unavailable'] as const)( + 'allows publication when workflow reviews are %s', + async (unavailableReason) => { + const { workflow, versionId } = await createReviewableWorkflow(); + await createOpenReview(workflow.id, versionId); + + if (unavailableReason === 'policy disabled') { + await policyService.set(false); + } else { + testServer.license.disable('feat:workflowReviews'); + } + + await ownerAgent.post(`/workflows/${workflow.id}/activate`).send({ versionId }).expect(200); + + expect( + (await workflowEntityRepository.findOneByOrFail({ id: workflow.id })).activeVersionId, + ).toBe(versionId); + }, + ); + + test('allows unpublishing while a review is open', async () => { + const { workflow, versionId } = await createReviewableWorkflow(); + + await ownerAgent.post(`/workflows/${workflow.id}/activate`).send({ versionId }).expect(200); + await createOpenReview(workflow.id, versionId); + + await ownerAgent.post(`/workflows/${workflow.id}/deactivate`).send({}).expect(200); + + expect( + (await workflowEntityRepository.findOneByOrFail({ id: workflow.id })).activeVersionId, + ).toBeNull(); + }); +}); + describe('POST /workflow-review-requests/:workflowReviewRequestId/update-version', () => { /** Seed an open review request pinned to `versionId`, authored by `author`. */ async function seedOpenRequest( @@ -1150,6 +1273,144 @@ describe('GET /workflow-review-requests', () => { workflowVersionId: versionId, createdAt: expect.any(String), updatedAt: expect.any(String), + // Neither applies to a pending review + decisionBy: null, + approvedVersionPublicationState: null, + }); + }); + + test('returns the newest review, closed included, with take=1 and no state filter', async () => { + const { workflow, versionId } = await createReviewableWorkflow(); + const older = await requestRepository.createRequest({ + projectId: ownerProject.id, + state: 'closed', + title: 'Older', + createdById: owner.id, + }); + await linkRequestToWorkflow(older.id, workflow.id, versionId); + const newest = await requestRepository.createRequest({ + projectId: ownerProject.id, + state: 'open', + title: 'Newest', + createdById: owner.id, + }); + await linkRequestToWorkflow(newest.id, workflow.id, versionId); + // Both rows are created within the same millisecond, so state the age + // explicitly instead of asserting against a timestamp tie. + await requestRepository.update(older.id, { createdAt: new Date('2026-01-01T00:00:00.000Z') }); + await requestRepository.update(newest.id, { createdAt: new Date('2026-01-02T00:00:00.000Z') }); + + const response = await ownerAgent + .get('/workflow-review-requests') + .query({ workflowId: workflow.id, take: 1 }) + .expect(200); + + expect(response.body.data.data).toHaveLength(1); + expect(response.body.data.data[0]).toMatchObject({ id: newest.id, state: 'open' }); + }); + + test('resolves the actor of a changes-requested decision', async () => { + const { workflow, versionId } = await createReviewableWorkflow(); + const request = await requestRepository.createRequest({ + projectId: ownerProject.id, + decision: 'changes_requested', + title: 'Needs work', + createdById: owner.id, + updatedById: member.id, + }); + await linkRequestToWorkflow(request.id, workflow.id, versionId); + + const response = await ownerAgent + .get('/workflow-review-requests') + .query({ workflowId: workflow.id, take: 1 }) + .expect(200); + + expect(response.body.data.data[0]).toMatchObject({ + decision: 'changes_requested', + decisionBy: { + id: member.id, + email: member.email, + firstName: member.firstName, + lastName: member.lastName, + }, + approvedVersionPublicationState: null, + }); + }); + + test('returns no actor when the deciding user was deleted', async () => { + const { workflow, versionId } = await createReviewableWorkflow(); + const reviewer = await createMember(); + const request = await requestRepository.createRequest({ + projectId: ownerProject.id, + decision: 'changes_requested', + title: 'Needs work', + createdById: owner.id, + updatedById: reviewer.id, + }); + await linkRequestToWorkflow(request.id, workflow.id, versionId); + await userRepository.delete(reviewer.id); + + const response = await ownerAgent + .get('/workflow-review-requests') + .query({ workflowId: workflow.id, take: 1 }) + .expect(200); + + expect(response.body.data.data[0]).toMatchObject({ + decision: 'changes_requested', + decisionBy: null, + }); + }); + + test('reports an approved version that was never published', async () => { + const { workflow, versionId } = await createReviewableWorkflow(); + const request = await requestRepository.createRequest({ + projectId: ownerProject.id, + state: 'closed', + decision: 'approved', + title: 'Approved', + createdById: owner.id, + updatedById: member.id, + }); + await linkRequestToWorkflow(request.id, workflow.id, versionId); + + const response = await ownerAgent + .get('/workflow-review-requests') + .query({ workflowId: workflow.id, take: 1 }) + .expect(200); + + expect(response.body.data.data[0]).toMatchObject({ + state: 'closed', + decision: 'approved', + // Approval is never attributed in the canvas banner + decisionBy: null, + approvedVersionPublicationState: 'not_published', + }); + }); + + test('reports an approved version that has been published', async () => { + const { workflow, versionId } = await createReviewableWorkflow(); + const request = await requestRepository.createRequest({ + projectId: ownerProject.id, + state: 'closed', + decision: 'approved', + title: 'Approved', + createdById: owner.id, + }); + await linkRequestToWorkflow(request.id, workflow.id, versionId); + await publishHistoryRepository.addRecord({ + workflowId: workflow.id, + versionId, + event: 'activated', + userId: owner.id, + }); + + const response = await ownerAgent + .get('/workflow-review-requests') + .query({ workflowId: workflow.id, take: 1 }) + .expect(200); + + expect(response.body.data.data[0]).toMatchObject({ + approvedVersionPublicationState: 'published', }); }); @@ -1567,3 +1828,206 @@ describe('GET /workflow-review-requests/inbox', () => { .expect(400); }); }); + +describe('GET /workflow-review-requests/:workflowReviewRequestId', () => { + /** Seed a review request in `projectId` pinned to `versionId`, authored by `author`. */ + async function seedRequest( + workflowId: string, + versionId: string | null, + author: User, + projectId = teamProject.id, + ) { + const request = await requestRepository.createRequest({ + projectId, + title: 'Please review', + description: 'Some context', + createdById: author.id, + }); + await workflowRepository.createWorkflowRow({ + workflowReviewRequestId: request.id, + workflowId, + workflowVersionId: versionId, + }); + await authorRepository.addAuthor({ workflowReviewRequestId: request.id, userId: author.id }); + return request; + } + + test('returns the review, the workflows it covers, and both versions to compare', async () => { + const workflow = await createWorkflow({ name: 'Reviewed workflow' }, teamProject); + const baseline = await createWorkflowHistoryItem(workflow.id, { + versionId: 'version-published', + }); + await createWorkflowHistoryItem(workflow.id, { versionId: 'version-pinned' }); + await publishedVersionRepository.setPublishedVersion(workflow.id, baseline.versionId); + const reviewer = await createAdmin(); + const request = await seedRequest(workflow.id, 'version-pinned', owner); + await reviewerRepository.addReviewers({ + workflowReviewRequestId: request.id, + userIds: [reviewer.id], + }); + + const response = await ownerAgent.get(`/workflow-review-requests/${request.id}`).expect(200); + + expect(response.body.data).toMatchObject({ + id: request.id, + projectId: teamProject.id, + state: 'open', + decision: 'pending', + title: 'Please review', + description: 'Some context', + workflowName: 'Reviewed workflow', + workflowVersionId: 'version-pinned', + requester: { id: owner.id, email: owner.email }, + reviewers: [{ id: reviewer.id, email: reviewer.email }], + }); + + expect(response.body.data.workflows).toHaveLength(1); + const [child] = response.body.data.workflows; + expect(child).toMatchObject({ + workflowId: workflow.id, + workflowName: 'Reviewed workflow', + workflowVersionId: 'version-pinned', + }); + expect(child.pinnedVersion).toMatchObject({ + versionId: 'version-pinned', + connections: {}, + nodeGroups: [], + }); + expect(child.pinnedVersion.nodes).toHaveLength(1); + expect(child.pinnedVersion.nodes[0]).toMatchObject({ name: 'Start' }); + expect(child.pinnedVersion).not.toHaveProperty('authors'); + expect(child.baselineVersion).toMatchObject({ versionId: 'version-published' }); + }); + + test('has nothing to compare against when the workflow was never published', async () => { + const workflow = await createWorkflow({}, teamProject); + await createWorkflowHistoryItem(workflow.id, { versionId: 'version-pinned' }); + const request = await seedRequest(workflow.id, 'version-pinned', owner); + + const response = await ownerAgent.get(`/workflow-review-requests/${request.id}`).expect(200); + + expect(response.body.data.workflows[0].pinnedVersion).toMatchObject({ + versionId: 'version-pinned', + }); + expect(response.body.data.workflows[0].baselineVersion).toBeNull(); + }); + + test('returns no version under review when the review does not point at one', async () => { + const workflow = await createWorkflow({}, teamProject); + const request = await seedRequest(workflow.id, null, owner); + + const response = await ownerAgent.get(`/workflow-review-requests/${request.id}`).expect(200); + + expect(response.body.data.workflows[0]).toMatchObject({ + workflowVersionId: null, + pinnedVersion: null, + baselineVersion: null, + }); + }); + + test('still opens the review after its workflow was deleted', async () => { + const workflow = await createWorkflow({}, teamProject); + await createWorkflowHistoryItem(workflow.id, { versionId: 'version-pinned' }); + const request = await seedRequest(workflow.id, 'version-pinned', owner); + + // Deleting the workflow removes the review's reference to it as well + await workflowEntityRepository.delete({ id: workflow.id }); + + const response = await ownerAgent.get(`/workflow-review-requests/${request.id}`).expect(200); + + expect(response.body.data.id).toBe(request.id); + expect(response.body.data.workflows).toEqual([]); + expect(response.body.data.workflowName).toBeNull(); + expect(response.body.data.workflowVersionId).toBeNull(); + }); + + test('lets an editor in the review project open it', async () => { + const workflow = await createWorkflow({}, teamProject); + const request = await seedRequest(workflow.id, null, owner); + + const response = await memberAgent.get(`/workflow-review-requests/${request.id}`).expect(200); + + expect(response.body.data.id).toBe(request.id); + }); + + test('hides the review from a viewer who cannot publish in the project', async () => { + const workflow = await createWorkflow({}, teamProject); + const request = await seedRequest(workflow.id, null, owner); + + await viewerAgent.get(`/workflow-review-requests/${request.id}`).expect(404); + }); + + test('hides the review from someone outside its project', async () => { + const otherProject = await createTeamProject('Unrelated Project', owner); + const workflow = await createWorkflow({}, otherProject); + const request = await seedRequest(workflow.id, null, owner, otherProject.id); + + await memberAgent.get(`/workflow-review-requests/${request.id}`).expect(404); + }); + + test('hides the review once its workflow moves to a project the user cannot see', async () => { + // The review still points at `teamProject`, where member is allowed to publish, + // while the workflow itself has moved to a project member has no access to + const destinationProject = await createTeamProject('Destination Project', owner); + const workflow = await createWorkflow({}, destinationProject); + await createWorkflowHistoryItem(workflow.id, { versionId: 'version-pinned' }); + const request = await seedRequest(workflow.id, 'version-pinned', owner, teamProject.id); + + await memberAgent.get(`/workflow-review-requests/${request.id}`).expect(404); + }); + + test('leaves out a workflow the requester can no longer see, but still opens the review', async () => { + // Viewer asked for the review while the workflow was reachable; it has since moved + // to a project they have no access to, so the content must not come back with it + const destinationProject = await createTeamProject('Moved Away', owner); + const workflow = await createWorkflow({}, destinationProject); + await createWorkflowHistoryItem(workflow.id, { versionId: 'version-pinned' }); + const request = await seedRequest(workflow.id, 'version-pinned', viewer, teamProject.id); + + const response = await viewerAgent.get(`/workflow-review-requests/${request.id}`).expect(200); + + expect(response.body.data.id).toBe(request.id); + expect(response.body.data.workflows).toEqual([]); + expect(response.body.data.workflowName).toBeNull(); + expect(response.body.data.workflowVersionId).toBeNull(); + }); + + test('always shows people the review they asked for themselves', async () => { + const workflow = await createWorkflow({}, teamProject); + const request = await seedRequest(workflow.id, null, viewer); + + const response = await viewerAgent.get(`/workflow-review-requests/${request.id}`).expect(200); + + expect(response.body.data.id).toBe(request.id); + }); + + test('reports a review that does not exist as not found', async () => { + await ownerAgent.get('/workflow-review-requests/unknown-request').expect(404); + }); + + test('does not shadow the inbox, summary, and eligible-reviewers endpoints', async () => { + const workflow = await createWorkflow({}, teamProject); + await seedRequest(workflow.id, null, owner); + + await ownerAgent.get('/workflow-review-requests/inbox').expect(200); + await ownerAgent.get('/workflow-review-requests/summary').expect(200); + // 400 (missing workflowId), not 404 — proves it still reaches its own handler + await ownerAgent.get('/workflow-review-requests/eligible-reviewers').expect(400); + }); + + test('refuses to open a review when an admin has turned reviews off', async () => { + const workflow = await createWorkflow({}, teamProject); + const request = await seedRequest(workflow.id, null, owner); + await policyService.set(false); + + await ownerAgent.get(`/workflow-review-requests/${request.id}`).expect(403); + }); + + test('refuses to open a review on an instance without a workflow reviews licence', async () => { + testServer.license.disable('feat:workflowReviews'); + + await ownerAgent.get('/workflow-review-requests/some-request').expect(403); + + testServer.license.enable('feat:workflowReviews'); + }); +}); diff --git a/packages/cli/src/modules/workflow-reviews.ee/__tests__/workflow-review-requests.env-gate.integration.test.ts b/packages/cli/src/modules/workflow-reviews.ee/__tests__/workflow-review-requests.env-gate.integration.test.ts index 38da8c43c2f..08412761d72 100644 --- a/packages/cli/src/modules/workflow-reviews.ee/__tests__/workflow-review-requests.env-gate.integration.test.ts +++ b/packages/cli/src/modules/workflow-reviews.ee/__tests__/workflow-review-requests.env-gate.integration.test.ts @@ -53,6 +53,10 @@ describe('workflow-review-requests (env flag off)', () => { test('GET summary is unreachable (404)', async () => { await ownerAgent.get('/workflow-review-requests/summary').expect(404); }); + + test('GET detail is unreachable (404)', async () => { + await ownerAgent.get('/workflow-review-requests/some-id').expect(404); + }); }); describe('GET /workflow-review-requests (env flag off)', () => { diff --git a/packages/cli/src/modules/workflow-reviews.ee/workflow-review-feature-gate.service.ts b/packages/cli/src/modules/workflow-reviews.ee/workflow-review-feature-gate.service.ts new file mode 100644 index 00000000000..52232646a5e --- /dev/null +++ b/packages/cli/src/modules/workflow-reviews.ee/workflow-review-feature-gate.service.ts @@ -0,0 +1,52 @@ +import { LicenseState } from '@n8n/backend-common'; +import { Service } from '@n8n/di'; + +import { isWorkflowReviewsFeatureAvailable } from '@/constants/workflow-reviews'; +import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; +import { WorkflowReviewPolicyService } from '@/services/workflow-review-policy.service'; + +type WorkflowReviewAvailability = { available: true } | { available: false; errorMessage: string }; + +/** + * Single gate for the workflow reviews feature: license (plus env flag) and the + * instance policy. Every entry point of the review services calls it, so a + * non-HTTP caller is gated too — the controller's `@Licensed` decorator only + * covers the routes. + */ +@Service() +export class WorkflowReviewFeatureGate { + constructor( + private readonly licenseState: LicenseState, + private readonly workflowReviewPolicyService: WorkflowReviewPolicyService, + ) {} + + async isAvailable(): Promise { + return (await this.getAvailability()).available; + } + + async assertAvailable(): Promise { + const availability = await this.getAvailability(); + if (!availability.available) { + throw new ForbiddenError(availability.errorMessage); + } + } + + private async getAvailability(): Promise { + if (!isWorkflowReviewsFeatureAvailable(this.licenseState.isWorkflowReviewsLicensed())) { + return { + available: false, + errorMessage: 'Workflow reviews are not available on this instance', + }; + } + + const policy = await this.workflowReviewPolicyService.get(); + if (!policy.enabled) { + return { + available: false, + errorMessage: 'Workflow reviews are disabled on this instance', + }; + } + + return { available: true }; + } +} diff --git a/packages/cli/src/modules/workflow-reviews.ee/workflow-review-inbox.service.ts b/packages/cli/src/modules/workflow-reviews.ee/workflow-review-inbox.service.ts new file mode 100644 index 00000000000..4a1a911b259 --- /dev/null +++ b/packages/cli/src/modules/workflow-reviews.ee/workflow-review-inbox.service.ts @@ -0,0 +1,353 @@ +import type { + GetWorkflowReviewInboxSummaryResponse, + ListWorkflowReviewInboxQueryDto, + ListWorkflowReviewInboxResponse, + WorkflowReviewEligibleReviewer, + WorkflowReviewInboxItem, + WorkflowReviewRequestDetail, + WorkflowReviewRequestWorkflowDetail, + WorkflowReviewVersionSnapshot, +} from '@n8n/api-types'; +import { + UserRepository, + WorkflowPublishedVersionRepository, + WorkflowReviewRequestRepository, + WorkflowReviewRequestReviewerRepository, + WorkflowReviewRequestWorkflowRepository, + type InboxCursor, + type User, + type WorkflowHistory, + type WorkflowReviewRequest, + type WorkflowReviewRequestLinkedWorkflow, + type WorkflowReviewRequestReviewer, + type WorkflowReviewRequestWorkflowDetailRow, +} from '@n8n/db'; +import { Service } from '@n8n/di'; +import { hasGlobalScope } from '@n8n/permissions'; + +import { BadRequestError } from '@/errors/response-errors/bad-request.error'; +import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { ProjectService } from '@/services/project.service.ee'; +import { WorkflowFinderService } from '@/workflows/workflow-finder.service'; +import { WorkflowHistoryService } from '@/workflows/workflow-history/workflow-history.service'; + +import { WorkflowReviewFeatureGate } from './workflow-review-feature-gate.service'; +import { toEligibleReviewer } from './workflow-review.mapper'; + +/** + * The reviewer-facing read side of workflow reviews: the cross-project inbox, its + * counts, and the detail view a reviewer opens from it. Unlike the workflow-scoped + * lifecycle service, visibility here starts from the user's projects rather than + * from a known workflow. + */ +@Service() +export class WorkflowReviewInboxService { + constructor( + private readonly featureGate: WorkflowReviewFeatureGate, + private readonly workflowFinderService: WorkflowFinderService, + private readonly workflowHistoryService: WorkflowHistoryService, + private readonly workflowPublishedVersionRepository: WorkflowPublishedVersionRepository, + private readonly workflowReviewRequestRepository: WorkflowReviewRequestRepository, + private readonly workflowReviewRequestWorkflowRepository: WorkflowReviewRequestWorkflowRepository, + private readonly workflowReviewRequestReviewerRepository: WorkflowReviewRequestReviewerRepository, + private readonly userRepository: UserRepository, + private readonly projectService: ProjectService, + ) {} + + async listForInbox( + user: User, + query: ListWorkflowReviewInboxQueryDto, + ): Promise { + await this.featureGate.assertAvailable(); + + const projectIds = await this.resolveAccessibleProjectIds(user); + const { limit } = query; + const rows = await this.workflowReviewRequestRepository.findManyForInbox({ + projectIds, + requesterId: user.id, + state: query.state ?? 'open', + limit: limit + 1, + cursor: query.cursor ? this.decodeInboxCursor(query.cursor) : undefined, + }); + + const hasMore = rows.length > limit; + const data = rows.slice(0, limit); + const lastRow = data.at(-1); + const nextCursor = hasMore && lastRow ? this.encodeInboxCursor(lastRow) : null; + const requestIds = data.map((row) => row.id); + const [linkedWorkflowByRequestId, reviewerRows] = await Promise.all([ + this.workflowReviewRequestWorkflowRepository.findLinkedWorkflowsByRequestIds(requestIds), + this.workflowReviewRequestReviewerRepository.findByRequestIds(requestIds), + ]); + + const participantsByRequestId = await this.hydrateParticipants(data, reviewerRows); + + return { + data: data.map((row) => { + const { requester, reviewers } = participantsByRequestId.get(row.id) ?? { + requester: null, + reviewers: [], + }; + return this.toInboxItem( + row, + linkedWorkflowByRequestId.get(row.id) ?? null, + requester, + reviewers, + ); + }), + nextCursor, + hasMore, + }; + } + + async getInboxSummaryForUser(user: User): Promise { + await this.featureGate.assertAvailable(); + + const projectIds = await this.resolveAccessibleProjectIds(user); + return await this.workflowReviewRequestRepository.countByStateForInbox({ + projectIds, + requesterId: user.id, + }); + } + + /** + * Visibility starts from the inbox rule (requester OR `workflow:publish` in the + * review's project OR globally), then narrows per workflow to what the caller can + * currently read — see {@link filterReadableWorkflowRows}. + */ + async getDetail( + user: User, + workflowReviewRequestId: string, + ): Promise { + await this.featureGate.assertAvailable(); + + const request = await this.workflowReviewRequestRepository.findById(workflowReviewRequestId); + if (!request || !(await this.canAccessRequest(user, request))) { + throw new NotFoundError('Could not find review request'); + } + + const [workflowRows, reviewerRows] = await Promise.all([ + this.workflowReviewRequestWorkflowRepository.findLinkedWorkflowDetailsByRequestId(request.id), + this.workflowReviewRequestReviewerRepository.findByRequestIds([request.id]), + ]); + + const readableRows = await this.filterReadableWorkflowRows(user, workflowRows); + // Someone who reaches this review through its project has no reason to learn it + // exists once they can read none of the workflows it covers. The requester already + // knows, and their inbox still lists it, so they keep the record — narrowed to the + // workflows they can currently read. + if (request.createdById !== user.id && workflowRows.length > 0 && readableRows.length === 0) { + throw new NotFoundError('Could not find review request'); + } + + const [workflows, participantsByRequestId] = await Promise.all([ + Promise.all(readableRows.map(async (row) => await this.toWorkflowDetail(row))), + this.hydrateParticipants([request], reviewerRows), + ]); + + const { requester, reviewers } = participantsByRequestId.get(request.id) ?? { + requester: null, + reviewers: [], + }; + return { + // One workflow per review for now, so the summary fields mirror the first row + ...this.toInboxItem(request, workflows.at(0) ?? null, requester, reviewers), + description: request.description, + workflows, + }; + } + + /** + * Project IDs for inbox queries. `null` means "all projects, unfiltered" — + * correct for users with `workflow:publish` scoped globally. Requesters always + * see their own reviews regardless (repository OR-matches `requesterId`), so no + * personal-project fallback is needed. + */ + async resolveAccessibleProjectIds(user: User): Promise { + if (hasGlobalScope(user, 'workflow:publish')) { + return null; + } + + return await this.projectService.getProjectIdsWithScope(user, ['workflow:publish']); + } + + /** Inbox visibility rule: requester, or `workflow:publish` in the review's project. */ + private async canAccessRequest(user: User, request: WorkflowReviewRequest): Promise { + if (request.createdById === user.id) { + return true; + } + + const projectIds = await this.resolveAccessibleProjectIds(user); + return projectIds === null || projectIds.includes(request.projectId); + } + + /** + * A review's `projectId` is fixed at creation and nothing closes open reviews when a + * workflow is transferred, so the stored project does not prove the caller may still + * read a covered workflow. Re-check every row against the workflow's *current* owner + * before returning its content. + * + * This applies to the requester too. They held publish rights when they opened the + * review, but may have lost them since — and because the baseline is resolved at read + * time, an exemption would leave them reading versions published after they lost + * access. + */ + private async filterReadableWorkflowRows( + user: User, + rows: WorkflowReviewRequestWorkflowDetailRow[], + ): Promise { + const readable = await Promise.all( + rows.map(async (row) => + (await this.workflowFinderService.findWorkflowForUser(row.workflowId, user, [ + 'workflow:read', + ])) + ? row + : null, + ), + ); + + return readable.filter((row): row is WorkflowReviewRequestWorkflowDetailRow => row !== null); + } + + /** + * Both diff sides for one child row. The baseline is resolved at read time, so + * a publish during an open review moves what reviewers are diffing against. + */ + private async toWorkflowDetail( + row: WorkflowReviewRequestWorkflowDetailRow, + ): Promise { + const publishedVersionId = await this.workflowPublishedVersionRepository.getPublishedVersionId( + row.workflowId, + ); + + const [pinnedVersion, baselineVersion] = await Promise.all([ + this.findVersionSnapshot(row.workflowId, row.workflowVersionId), + this.findVersionSnapshot(row.workflowId, publishedVersionId), + ]); + + return { + workflowId: row.workflowId, + workflowName: row.workflowName, + workflowVersionId: row.workflowVersionId, + pinnedVersion, + baselineVersion, + }; + } + + /** `null` version id, or a version whose history row was pruned, both mean "no content". */ + private async findVersionSnapshot( + workflowId: string, + versionId: string | null, + ): Promise { + if (!versionId) { + return null; + } + + const version = await this.workflowHistoryService.findVersion(workflowId, versionId); + return version ? this.toVersionSnapshot(version) : null; + } + + private toVersionSnapshot(version: WorkflowHistory): WorkflowReviewVersionSnapshot { + return { + versionId: version.versionId, + nodes: version.nodes, + connections: version.connections, + nodeGroups: version.nodeGroups, + createdAt: version.createdAt.toISOString(), + }; + } + + /** + * Batch-resolve the requester and requested reviewers for each request row, + * keyed by request id. Deleted users simply drop out of the result. + */ + private async hydrateParticipants( + rows: WorkflowReviewRequest[], + reviewerRows: WorkflowReviewRequestReviewer[], + ): Promise< + Map< + string, + { + requester: WorkflowReviewEligibleReviewer | null; + reviewers: WorkflowReviewEligibleReviewer[]; + } + > + > { + const reviewerIdsByRequestId = new Map(); + for (const { workflowReviewRequestId, userId } of reviewerRows) { + const ids = reviewerIdsByRequestId.get(workflowReviewRequestId) ?? []; + ids.push(userId); + reviewerIdsByRequestId.set(workflowReviewRequestId, ids); + } + + const userIds = new Set([ + ...rows.map((row) => row.createdById).filter((id) => id !== null), + ...reviewerRows.map((row) => row.userId), + ]); + + const usersById = new Map(); + if (userIds.size > 0) { + for (const user of await this.userRepository.findManyByIds([...userIds])) { + usersById.set(user.id, toEligibleReviewer(user)); + } + } + + return new Map( + rows.map((row) => [ + row.id, + { + requester: row.createdById ? (usersById.get(row.createdById) ?? null) : null, + reviewers: (reviewerIdsByRequestId.get(row.id) ?? []) + .map((userId) => usersById.get(userId)) + .filter((reviewer) => reviewer !== undefined), + }, + ]), + ); + } + + /** + * Encode the keyset boundary (createdAt + id) into an opaque cursor so the + * next page is resolved without re-reading the anchor row — a review deleted + * between requests no longer truncates the rest of the inbox. + */ + private encodeInboxCursor(row: WorkflowReviewRequest): string { + return Buffer.from(`${row.createdAt.toISOString()}|${row.id}`, 'utf8').toString('base64url'); + } + + private decodeInboxCursor(cursor: string): InboxCursor { + const decoded = Buffer.from(cursor, 'base64url').toString('utf8'); + const separatorIndex = decoded.indexOf('|'); + if (separatorIndex === -1) { + throw new BadRequestError('Invalid pagination cursor'); + } + + const createdAt = new Date(decoded.slice(0, separatorIndex)); + const id = decoded.slice(separatorIndex + 1); + if (id.length === 0 || Number.isNaN(createdAt.getTime())) { + throw new BadRequestError('Invalid pagination cursor'); + } + + return { createdAt, id }; + } + + private toInboxItem( + entity: WorkflowReviewRequest, + linkedWorkflow: WorkflowReviewRequestLinkedWorkflow | null, + requester: WorkflowReviewEligibleReviewer | null, + reviewers: WorkflowReviewEligibleReviewer[], + ): WorkflowReviewInboxItem { + return { + id: entity.id, + projectId: entity.projectId, + title: entity.title, + workflowName: linkedWorkflow?.workflowName ?? null, + workflowVersionId: linkedWorkflow?.workflowVersionId ?? null, + decision: entity.decision, + state: entity.state, + createdAt: entity.createdAt.toISOString(), + updatedAt: entity.updatedAt.toISOString(), + requester, + reviewers, + }; + } +} diff --git a/packages/cli/src/modules/workflow-reviews.ee/workflow-review-publish-guard.service.ts b/packages/cli/src/modules/workflow-reviews.ee/workflow-review-publish-guard.service.ts new file mode 100644 index 00000000000..1ce873104fc --- /dev/null +++ b/packages/cli/src/modules/workflow-reviews.ee/workflow-review-publish-guard.service.ts @@ -0,0 +1,32 @@ +import type { WorkflowPublishBlockedReason } from '@n8n/api-types'; +import { WorkflowReviewRequestRepository } from '@n8n/db'; +import { Service } from '@n8n/di'; + +import { WorkflowPublishBlockedError } from '@/errors/response-errors/workflow-publish-blocked.error'; +import type { WorkflowPublishGuard } from '@/workflows/workflow-publish-guard-proxy.service'; + +import { WorkflowReviewFeatureGate } from './workflow-review-feature-gate.service'; + +@Service() +export class WorkflowReviewPublishGuard implements WorkflowPublishGuard { + constructor( + private readonly featureGate: WorkflowReviewFeatureGate, + private readonly workflowReviewRequestRepository: WorkflowReviewRequestRepository, + ) {} + + async assertCanPublish(workflowId: string): Promise { + if (!(await this.featureGate.isAvailable())) return; + + const request = + await this.workflowReviewRequestRepository.findOpenRequestForWorkflow(workflowId); + if (!request) return; + + const reason: WorkflowPublishBlockedReason = + request.decision === 'changes_requested' ? 'changes_requested' : 'review_pending'; + + throw new WorkflowPublishBlockedError({ + reason, + workflowReviewRequestId: request.id, + }); + } +} diff --git a/packages/cli/src/modules/workflow-reviews.ee/workflow-review-request.service.ts b/packages/cli/src/modules/workflow-reviews.ee/workflow-review-request.service.ts index 9d2f023d779..37b395b043f 100644 --- a/packages/cli/src/modules/workflow-reviews.ee/workflow-review-request.service.ts +++ b/packages/cli/src/modules/workflow-reviews.ee/workflow-review-request.service.ts @@ -4,60 +4,63 @@ import type { GetWorkflowReviewEligibleReviewersQueryDto, ListWorkflowReviewRequestsQueryDto, UpdateWorkflowReviewRequestVersionDto, + WorkflowReviewApprovedPublicationState, + WorkflowReviewEligibleReviewer, WorkflowReviewEligibleReviewersList, + WorkflowReviewRequestForWorkflow, WorkflowReviewRequestList, WorkflowReviewRequestSummary, - ListWorkflowReviewInboxQueryDto, - ListWorkflowReviewInboxResponse, - GetWorkflowReviewInboxSummaryResponse, - WorkflowReviewInboxItem, - WorkflowReviewEligibleReviewer, } from '@n8n/api-types'; -import { LicenseState, Logger } from '@n8n/backend-common'; +import { Logger } from '@n8n/backend-common'; import { DbLock, DbLockService, ProjectRelationRepository, SharedWorkflowRepository, UserRepository, + WorkflowPublishHistoryRepository, WorkflowReviewRequestAuthorRepository, WorkflowReviewRequestRepository, WorkflowReviewRequestReviewerRepository, WorkflowReviewRequestWorkflowRepository, - type InboxCursor, type User, type WorkflowReviewRequest, - type WorkflowReviewRequestLinkedWorkflow, - type WorkflowReviewRequestReviewer, + type WorkflowReviewRequestForWorkflowRow, } from '@n8n/db'; import { Service } from '@n8n/di'; import { GLOBAL_ADMIN_ROLE_SLUG, GLOBAL_OWNER_ROLE_SLUG, PROJECT_ADMIN_ROLE_SLUG, - hasGlobalScope, } from '@n8n/permissions'; import { CollaborationService } from '@/collaboration/collaboration.service'; -import { isWorkflowReviewsFeatureAvailable } from '@/constants/workflow-reviews'; import { BadRequestError } from '@/errors/response-errors/bad-request.error'; import { ConflictError } from '@/errors/response-errors/conflict.error'; import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; import { NotFoundError } from '@/errors/response-errors/not-found.error'; -import { ProjectService } from '@/services/project.service.ee'; import { RoleService } from '@/services/role.service'; -import { WorkflowReviewPolicyService } from '@/services/workflow-review-policy.service'; import { WorkflowFinderService } from '@/workflows/workflow-finder.service'; import { WorkflowHistoryService } from '@/workflows/workflow-history/workflow-history.service'; +import { WorkflowReviewFeatureGate } from './workflow-review-feature-gate.service'; +import { toEligibleReviewer } from './workflow-review.mapper'; + +/** + * The workflow-scoped review request lifecycle: listing a workflow's reviews, + * resolving its eligible reviewers, opening a review, re-pinning it to a new + * version, and deciding it. The reviewer-facing read side lives in + * `WorkflowReviewInboxService`. + */ @Service() export class WorkflowReviewRequestService { constructor( private readonly logger: Logger, - private readonly workflowReviewPolicyService: WorkflowReviewPolicyService, + private readonly featureGate: WorkflowReviewFeatureGate, private readonly workflowFinderService: WorkflowFinderService, private readonly workflowHistoryService: WorkflowHistoryService, private readonly sharedWorkflowRepository: SharedWorkflowRepository, + private readonly workflowPublishHistoryRepository: WorkflowPublishHistoryRepository, private readonly workflowReviewRequestRepository: WorkflowReviewRequestRepository, private readonly workflowReviewRequestWorkflowRepository: WorkflowReviewRequestWorkflowRepository, private readonly workflowReviewRequestAuthorRepository: WorkflowReviewRequestAuthorRepository, @@ -65,8 +68,6 @@ export class WorkflowReviewRequestService { private readonly userRepository: UserRepository, private readonly projectRelationRepository: ProjectRelationRepository, private readonly roleService: RoleService, - private readonly projectService: ProjectService, - private readonly licenseState: LicenseState, private readonly dbLockService: DbLockService, private readonly collaborationService: CollaborationService, ) {} @@ -92,10 +93,7 @@ export class WorkflowReviewRequestService { user: User, query: ListWorkflowReviewRequestsQueryDto, ): Promise { - const policy = await this.workflowReviewPolicyService.get(); - if (!policy.enabled) { - throw new ForbiddenError('Workflow reviews are not enabled for this instance'); - } + await this.featureGate.assertAvailable(); const workflow = await this.workflowFinderService.findWorkflowForUser(query.workflowId, user, [ 'workflow:read', @@ -111,25 +109,115 @@ export class WorkflowReviewRequestService { return { count, - data: requests.map((request) => ({ - id: request.id, - state: request.state, - decision: request.decision, - workflowVersionId: request.workflowVersionId, - createdAt: request.createdAt.toISOString(), - updatedAt: request.updatedAt.toISOString(), - })), + data: await this.toWorkflowScopedItems(query.workflowId, requests), }; } + private async toWorkflowScopedItems( + workflowId: string, + requests: WorkflowReviewRequestForWorkflowRow[], + ): Promise { + const [decisionActors, publicationStates] = await Promise.all([ + this.resolveDecisionActors(requests), + this.resolveApprovedPublicationStates(workflowId, requests), + ]); + + return requests.map((request) => ({ + id: request.id, + state: request.state, + decision: request.decision, + workflowVersionId: request.workflowVersionId, + createdAt: request.createdAt.toISOString(), + updatedAt: request.updatedAt.toISOString(), + decisionBy: this.pickDecisionActor(request, decisionActors), + approvedVersionPublicationState: this.pickApprovedPublicationState( + request, + publicationStates, + ), + })); + } + + /** + * `updatedById` is whoever last wrote the request, which for a + * `changes_requested` review is the reviewer who made that decision. Any other + * decision has no actor to name: `pending` may just be a version re-pin, and + * approval is not surfaced with an actor. + */ + private async resolveDecisionActors( + requests: WorkflowReviewRequestForWorkflowRow[], + ): Promise> { + const actorIds = [ + ...new Set( + requests.flatMap((request) => + request.decision === 'changes_requested' && request.updatedById + ? [request.updatedById] + : [], + ), + ), + ]; + if (actorIds.length === 0) { + return new Map(); + } + + const actors = await this.userRepository.findManyByIds(actorIds); + return new Map(actors.map((actor) => [actor.id, toEligibleReviewer(actor)])); + } + + private pickDecisionActor( + request: WorkflowReviewRequestForWorkflowRow, + actors: Map, + ): WorkflowReviewEligibleReviewer | null { + if (request.decision !== 'changes_requested' || !request.updatedById) { + return null; + } + + return actors.get(request.updatedById) ?? null; + } + + private async resolveApprovedPublicationStates( + workflowId: string, + requests: WorkflowReviewRequestForWorkflowRow[], + ): Promise> { + const versionIds = [ + ...new Set( + requests.flatMap((request) => + request.decision === 'approved' && request.workflowVersionId + ? [request.workflowVersionId] + : [], + ), + ), + ]; + if (versionIds.length === 0) { + return new Map(); + } + + return await this.workflowPublishHistoryRepository.getVersionPublicationStates( + workflowId, + versionIds, + ); + } + + private pickApprovedPublicationState( + request: WorkflowReviewRequestForWorkflowRow, + states: Map, + ): WorkflowReviewApprovedPublicationState | null { + if (request.decision !== 'approved') { + return null; + } + + // A pruned pin has no version to reason about, so it stays 'unknown' + if (!request.workflowVersionId) { + return 'unknown'; + } + + return states.get(request.workflowVersionId) ?? 'unknown'; + } + async getEligibleReviewers( user: User, query: GetWorkflowReviewEligibleReviewersQueryDto, ): Promise { - const policy = await this.workflowReviewPolicyService.get(); - if (!policy.enabled) { - throw new ForbiddenError('Workflow reviews are not enabled for this instance'); - } + await this.featureGate.assertAvailable(); const workflow = await this.workflowFinderService.findWorkflowForUser(query.workflowId, user, [ 'workflow:publish', @@ -148,17 +236,7 @@ export class WorkflowReviewRequestService { // No pagination: the set is bounded by the project's members plus instance admins return { count: reviewers.length, - data: reviewers.map((reviewer) => this.toEligibleReviewer(reviewer)), - }; - } - - /** Project a user onto the boundary shape the review endpoints are allowed to expose. */ - private toEligibleReviewer(user: User): WorkflowReviewEligibleReviewer { - return { - id: user.id, - email: user.email, - firstName: user.firstName ?? null, - lastName: user.lastName ?? null, + data: reviewers.map(toEligibleReviewer), }; } @@ -168,10 +246,7 @@ export class WorkflowReviewRequestService { ): Promise { const { workflowId, workflowVersionId } = dto.workflows[0]; - const policy = await this.workflowReviewPolicyService.get(); - if (!policy.enabled) { - throw new ForbiddenError('Workflow reviews are not enabled for this instance'); - } + await this.featureGate.assertAvailable(); const workflow = await this.workflowFinderService.findWorkflowForUser(workflowId, user, [ 'workflow:publish', @@ -280,10 +355,7 @@ export class WorkflowReviewRequestService { workflowReviewRequestId: string, dto: UpdateWorkflowReviewRequestVersionDto, ): Promise { - const policy = await this.workflowReviewPolicyService.get(); - if (!policy.enabled) { - throw new ForbiddenError('Workflow reviews are not enabled for this instance'); - } + await this.featureGate.assertAvailable(); const request = await this.workflowReviewRequestRepository.findById(workflowReviewRequestId); if (!request) { @@ -396,10 +468,7 @@ export class WorkflowReviewRequestService { workflowReviewRequestId: string, dto: DecideWorkflowReviewRequestDto, ): Promise { - const policy = await this.workflowReviewPolicyService.get(); - if (!policy.enabled) { - throw new ForbiddenError('Workflow reviews are not enabled for this instance'); - } + await this.featureGate.assertAvailable(); const request = await this.workflowReviewRequestRepository.findById(workflowReviewRequestId); if (!request) { @@ -555,185 +624,4 @@ export class WorkflowReviewRequestService { updatedAt: request.updatedAt.toISOString(), }; } - - /** - * Cross-project inbox. - * - * Defaults to open requests when `state` is omitted. Deferred (LIGO-594): - * `projectId`, `reviewer`, and `author` filters. - */ - async listForInbox( - user: User, - query: ListWorkflowReviewInboxQueryDto, - ): Promise { - await this.assertFeatureAvailable(); - - const projectIds = await this.resolveAccessibleProjectIds(user); - const { limit } = query; - const rows = await this.workflowReviewRequestRepository.findManyForInbox({ - projectIds, - requesterId: user.id, - state: query.state ?? 'open', - limit: limit + 1, - cursor: query.cursor ? this.decodeInboxCursor(query.cursor) : undefined, - }); - - const hasMore = rows.length > limit; - const data = rows.slice(0, limit); - const lastRow = data.at(-1); - const nextCursor = hasMore && lastRow ? this.encodeInboxCursor(lastRow) : null; - const requestIds = data.map((row) => row.id); - const [linkedWorkflowByRequestId, reviewerRows] = await Promise.all([ - this.workflowReviewRequestWorkflowRepository.findLinkedWorkflowsByRequestIds(requestIds), - this.workflowReviewRequestReviewerRepository.findByRequestIds(requestIds), - ]); - - const participantsByRequestId = await this.hydrateParticipants(data, reviewerRows); - - return { - data: data.map((row) => { - const { requester, reviewers } = participantsByRequestId.get(row.id) ?? { - requester: null, - reviewers: [], - }; - return this.toInboxItem( - row, - linkedWorkflowByRequestId.get(row.id) ?? null, - requester, - reviewers, - ); - }), - nextCursor, - hasMore, - }; - } - - /** - * Batch-resolve the requester and requested reviewers for each request row, - * keyed by request id. Deleted users simply drop out of the result. - */ - private async hydrateParticipants( - rows: WorkflowReviewRequest[], - reviewerRows: WorkflowReviewRequestReviewer[], - ): Promise< - Map< - string, - { - requester: WorkflowReviewEligibleReviewer | null; - reviewers: WorkflowReviewEligibleReviewer[]; - } - > - > { - const reviewerIdsByRequestId = new Map(); - for (const { workflowReviewRequestId, userId } of reviewerRows) { - const ids = reviewerIdsByRequestId.get(workflowReviewRequestId) ?? []; - ids.push(userId); - reviewerIdsByRequestId.set(workflowReviewRequestId, ids); - } - - const userIds = new Set([ - ...rows.map((row) => row.createdById).filter((id) => id !== null), - ...reviewerRows.map((row) => row.userId), - ]); - - const usersById = new Map(); - if (userIds.size > 0) { - for (const user of await this.userRepository.findManyByIds([...userIds])) { - usersById.set(user.id, this.toEligibleReviewer(user)); - } - } - - return new Map( - rows.map((row) => [ - row.id, - { - requester: row.createdById ? (usersById.get(row.createdById) ?? null) : null, - reviewers: (reviewerIdsByRequestId.get(row.id) ?? []) - .map((userId) => usersById.get(userId)) - .filter((reviewer) => reviewer !== undefined), - }, - ]), - ); - } - - async getInboxSummaryForUser(user: User): Promise { - await this.assertFeatureAvailable(); - - const projectIds = await this.resolveAccessibleProjectIds(user); - return await this.workflowReviewRequestRepository.countByStateForInbox({ - projectIds, - requesterId: user.id, - }); - } - - private async assertFeatureAvailable(): Promise { - if (!isWorkflowReviewsFeatureAvailable(this.licenseState.isWorkflowReviewsLicensed())) { - throw new ForbiddenError('Workflow reviews are not available on this instance'); - } - - const policy = await this.workflowReviewPolicyService.get(); - if (!policy.enabled) { - throw new ForbiddenError('Workflow reviews are disabled on this instance'); - } - } - - /** - * Project IDs for inbox queries. `null` means "all projects, unfiltered" — - * correct for users with `workflow:publish` scoped globally. Requesters always - * see their own reviews regardless (repository OR-matches `requesterId`), so no - * personal-project fallback is needed. - */ - async resolveAccessibleProjectIds(user: User): Promise { - if (hasGlobalScope(user, 'workflow:publish')) { - return null; - } - - return await this.projectService.getProjectIdsWithScope(user, ['workflow:publish']); - } - - /** - * Encode the keyset boundary (createdAt + id) into an opaque cursor so the - * next page is resolved without re-reading the anchor row — a review deleted - * between requests no longer truncates the rest of the inbox. - */ - private encodeInboxCursor(row: WorkflowReviewRequest): string { - return Buffer.from(`${row.createdAt.toISOString()}|${row.id}`, 'utf8').toString('base64url'); - } - - private decodeInboxCursor(cursor: string): InboxCursor { - const decoded = Buffer.from(cursor, 'base64url').toString('utf8'); - const separatorIndex = decoded.indexOf('|'); - if (separatorIndex === -1) { - throw new BadRequestError('Invalid pagination cursor'); - } - - const createdAt = new Date(decoded.slice(0, separatorIndex)); - const id = decoded.slice(separatorIndex + 1); - if (id.length === 0 || Number.isNaN(createdAt.getTime())) { - throw new BadRequestError('Invalid pagination cursor'); - } - - return { createdAt, id }; - } - - private toInboxItem( - entity: WorkflowReviewRequest, - linkedWorkflow: WorkflowReviewRequestLinkedWorkflow | null, - requester: WorkflowReviewEligibleReviewer | null, - reviewers: WorkflowReviewEligibleReviewer[], - ): WorkflowReviewInboxItem { - return { - id: entity.id, - projectId: entity.projectId, - title: entity.title, - workflowName: linkedWorkflow?.workflowName ?? null, - workflowVersionId: linkedWorkflow?.workflowVersionId ?? null, - decision: entity.decision, - state: entity.state, - createdAt: entity.createdAt.toISOString(), - updatedAt: entity.updatedAt.toISOString(), - requester, - reviewers, - }; - } } diff --git a/packages/cli/src/modules/workflow-reviews.ee/workflow-review-requests.controller.ts b/packages/cli/src/modules/workflow-reviews.ee/workflow-review-requests.controller.ts index 9d87749f84c..5bbbe593c93 100644 --- a/packages/cli/src/modules/workflow-reviews.ee/workflow-review-requests.controller.ts +++ b/packages/cli/src/modules/workflow-reviews.ee/workflow-review-requests.controller.ts @@ -7,16 +7,21 @@ import { type GetWorkflowReviewInboxSummaryResponse, type ListWorkflowReviewInboxResponse, ListWorkflowReviewInboxQueryDto, + type WorkflowReviewRequestDetail, } from '@n8n/api-types'; import { AuthenticatedRequest } from '@n8n/db'; import { Body, Get, Licensed, Param, Post, Query, RestController } from '@n8n/decorators'; import type { Response } from 'express'; +import { WorkflowReviewInboxService } from './workflow-review-inbox.service'; import { WorkflowReviewRequestService } from './workflow-review-request.service'; @RestController('/workflow-review-requests') export class WorkflowReviewRequestsController { - constructor(private readonly workflowReviewRequestService: WorkflowReviewRequestService) {} + constructor( + private readonly workflowReviewRequestService: WorkflowReviewRequestService, + private readonly workflowReviewInboxService: WorkflowReviewInboxService, + ) {} @Get('/') @Licensed('feat:workflowReviews') @@ -89,7 +94,7 @@ export class WorkflowReviewRequestsController { _res: Response, @Query query: ListWorkflowReviewInboxQueryDto, ): Promise { - return await this.workflowReviewRequestService.listForInbox(req.user, query); + return await this.workflowReviewInboxService.listForInbox(req.user, query); } @Get('/summary') @@ -98,6 +103,23 @@ export class WorkflowReviewRequestsController { req: AuthenticatedRequest, _res: Response, ): Promise { - return await this.workflowReviewRequestService.getInboxSummaryForUser(req.user); + return await this.workflowReviewInboxService.getInboxSummaryForUser(req.user); + } + + /** + * Review detail, including the diff inputs per covered workflow. + * + * Keep this last: routes register in declaration order, so a `/:id` pattern + * declared earlier would swallow the literal paths above. Authorization lives + * in the service — `@ProjectScope` cannot resolve a project from a review id. + */ + @Get('/:workflowReviewRequestId') + @Licensed('feat:workflowReviews') + async getDetail( + req: AuthenticatedRequest, + _res: Response, + @Param('workflowReviewRequestId') workflowReviewRequestId: string, + ): Promise { + return await this.workflowReviewInboxService.getDetail(req.user, workflowReviewRequestId); } } diff --git a/packages/cli/src/modules/workflow-reviews.ee/workflow-review.mapper.ts b/packages/cli/src/modules/workflow-reviews.ee/workflow-review.mapper.ts new file mode 100644 index 00000000000..ed9bf9995b8 --- /dev/null +++ b/packages/cli/src/modules/workflow-reviews.ee/workflow-review.mapper.ts @@ -0,0 +1,16 @@ +import type { WorkflowReviewEligibleReviewer } from '@n8n/api-types'; +import type { User } from '@n8n/db'; + +/** + * Project a user onto the boundary shape the review endpoints are allowed to expose. + * Shared by every review response that carries users, so the eligible-reviewer, + * inbox and detail payloads cannot drift apart. + */ +export function toEligibleReviewer(user: User): WorkflowReviewEligibleReviewer { + return { + id: user.id, + email: user.email, + firstName: user.firstName ?? null, + lastName: user.lastName ?? null, + }; +} diff --git a/packages/cli/src/modules/workflow-reviews.ee/workflow-reviews.module.ts b/packages/cli/src/modules/workflow-reviews.ee/workflow-reviews.module.ts index 99fbc8a477f..df559f213cf 100644 --- a/packages/cli/src/modules/workflow-reviews.ee/workflow-reviews.module.ts +++ b/packages/cli/src/modules/workflow-reviews.ee/workflow-reviews.module.ts @@ -1,8 +1,10 @@ import { LICENSE_FEATURES } from '@n8n/constants'; import type { ModuleInterface } from '@n8n/decorators'; import { BackendModule } from '@n8n/decorators'; +import { Container } from '@n8n/di'; import { isWorkflowReviewsEnvFeatureFlagEnabled } from '@/constants/workflow-reviews'; +import { WorkflowPublishGuardProxy } from '@/workflows/workflow-publish-guard-proxy.service'; @BackendModule({ name: 'workflow-reviews', licenseFlag: LICENSE_FEATURES.WORKFLOW_REVIEWS }) export class WorkflowReviewsModule implements ModuleInterface { @@ -10,5 +12,11 @@ export class WorkflowReviewsModule implements ModuleInterface { if (!isWorkflowReviewsEnvFeatureFlagEnabled()) return; await import('./workflow-review-requests.controller.js'); + const { WorkflowReviewPublishGuard } = await import( + './workflow-review-publish-guard.service.js' + ); + Container.get(WorkflowPublishGuardProxy).registerProvider( + Container.get(WorkflowReviewPublishGuard), + ); } } diff --git a/packages/cli/src/node-catalog/__tests__/node-catalog.service.test.ts b/packages/cli/src/node-catalog/__tests__/node-catalog.service.test.ts index a5f1796db05..368bdec9ddb 100644 --- a/packages/cli/src/node-catalog/__tests__/node-catalog.service.test.ts +++ b/packages/cli/src/node-catalog/__tests__/node-catalog.service.test.ts @@ -409,6 +409,39 @@ describe('NodeCatalogService', () => { ); }); + test('synthesizes a hidden built-in node instead of the on-disk lookup', async () => { + // The build-time generator skips hidden nodes, so no on-disk artifact + // exists for them even though search surfaces them (e.g. messageAnAgent). + loadNodesAndCredentials.collectTypes.mockResolvedValue({ + nodes: [ + { + name: 'n8n-nodes-base.messageAnAgent', + version: 2, + hidden: true, + group: ['transform'], + properties: [], + inputs: ['main'], + outputs: ['main'], + }, + { name: 'n8n-nodes-base.set' }, + ], + } as never); + await service.initialize(); + + const result = await service.getNodeTypes([ + 'n8n-nodes-base.messageAnAgent', + 'n8n-nodes-base.set', + ]); + + expect(mockGenerateNodeTypeFile).toHaveBeenCalledWith( + expect.objectContaining({ name: 'n8n-nodes-base.messageAnAgent' }), + ); + expect(mockGetNodeTypes).toHaveBeenCalledWith(['n8n-nodes-base.set'], expect.anything()); + expect(result).toContain('synth-result'); + expect(result).toContain('get-result'); + expect(result).not.toContain('# Errors'); + }); + test('reports an error for an unknown requested version instead of downgrading', async () => { loadNodesAndCredentials.collectTypes.mockResolvedValue({ nodes: [ @@ -514,6 +547,33 @@ describe('NodeCatalogService', () => { ); }); + test('synthesizes a hidden built-in node instead of the on-disk lookup', async () => { + loadNodesAndCredentials.collectTypes.mockResolvedValue({ + nodes: [ + { + name: 'n8n-nodes-base.messageAnAgent', + version: 2, + hidden: true, + group: ['transform'], + properties: [], + inputs: ['main'], + outputs: ['main'], + }, + ], + } as never); + await service.initialize(); + + const result = await service.getNodeTypeDefinition({ + nodeId: 'n8n-nodes-base.messageAnAgent', + }); + + expect(result).toEqual({ content: 'synth-result', version: '2' }); + expect(mockGenerateNodeTypeFile).toHaveBeenCalledWith( + expect.objectContaining({ name: 'n8n-nodes-base.messageAnAgent' }), + ); + expect(mockGetNodeTypeDefinition).not.toHaveBeenCalled(); + }); + test('synthesizes type definitions for a community node', async () => { loadNodesAndCredentials.collectTypes.mockResolvedValue({ nodes: [ diff --git a/packages/cli/src/node-catalog/node-catalog.service.ts b/packages/cli/src/node-catalog/node-catalog.service.ts index a847f7e7f00..374bfbff532 100644 --- a/packages/cli/src/node-catalog/node-catalog.service.ts +++ b/packages/cli/src/node-catalog/node-catalog.service.ts @@ -16,11 +16,6 @@ import { synthesizeNodeTypeDef } from '@/modules/mcp-registry/synthesize-type-de export type NodeFilter = (nodeId: string) => boolean; -/** - * Built-in node IDs resolve through the richer, discriminator-aware on-disk - * lookup; everything else (MCP registry, custom and community nodes) is - * synthesized from its in-memory description. - */ const isBuiltinNodeId = (nodeId: string): boolean => BUILTIN_NODES_PACKAGES.some((pkg) => nodeId.startsWith(`${pkg}.`)); @@ -211,15 +206,10 @@ export class NodeCatalogService { const cached = this.getCache.get(cacheKey); if (cached) return cached; - // Built-in nodes resolve through the on-disk type defs (richer, - // discriminator-aware). Everything else (MCP registry, custom and - // community nodes) has no on-disk artifact, so synthesize from the - // in-memory description collected from the loaders. const onDiskIds: NodeRequest[] = []; const synthesizeIds: NodeRequest[] = []; for (const id of nodeIds) { - const nodeId = typeof id === 'string' ? id : id.nodeId; - if (isBuiltinNodeId(nodeId)) { + if (this.resolvesFromDisk(this.toDefinitionRequest(id))) { onDiskIds.push(id); } else { synthesizeIds.push(id); @@ -260,7 +250,7 @@ export class NodeCatalogService { const cached = this.getDefinitionCache.get(cacheKey); if (cached) return cached; - const result = isBuiltinNodeId(request.nodeId) + const result = this.resolvesFromDisk(request) ? await this.getBuiltinNodeTypeDefinition(request) : this.getSynthesizedNodeTypeDefinition(request); @@ -332,6 +322,23 @@ export class NodeCatalogService { } } + /** + * Built-in node IDs resolve through the richer, discriminator-aware on-disk + * type defs; everything else (MCP registry, custom and community nodes) has + * no on-disk artifact and is synthesized from its in-memory description. + * Hidden built-ins (e.g. messageAnAgent, surfaced in search but skipped by + * the build-time generator) are synthesized too — the on-disk lookup would + * report them as not found. + */ + private resolvesFromDisk(request: NodeTypeDefinitionRequest): boolean { + if (!isBuiltinNodeId(request.nodeId)) return false; + const candidates = this.descriptionsById.get(request.nodeId); + // Unknown built-in ids stay on the on-disk path so its lookup reports the error. + if (!candidates?.length) return true; + const description = this.selectDescription(candidates, request.version); + return description?.hidden !== true; + } + private toDefinitionRequest(nodeRequest: NodeRequest): NodeTypeDefinitionRequest { if (typeof nodeRequest === 'string') return { nodeId: nodeRequest }; diff --git a/packages/cli/src/node-execution/ephemeral-node-executor.ts b/packages/cli/src/node-execution/ephemeral-node-executor.ts index b291554faef..a132c2c2de6 100644 --- a/packages/cli/src/node-execution/ephemeral-node-executor.ts +++ b/packages/cli/src/node-execution/ephemeral-node-executor.ts @@ -100,8 +100,11 @@ function stripAgentToolSuffix(nodeType: string): string { * marked `usableAsTool`. They expose the full provider API (image generation, * audio, files, embeddings, etc.) — not just chat completion — and are * designed to back LangChain agents rather than be called as agent tools, so - * they ship without the flag. Surfacing them here lets the agent builder use - * e.g. OpenAI image generation as a tool. + * they ship without the flag. This is a backward-compatibility allowance: + * agents that already have a provider node attached keep executing. Discovery + * deliberately excludes provider nodes via + * `AGENT_BUILDER_HIDDEN_AVAILABLE_TOOL_NODE_TYPES`, so no new agent can pick + * one up. * * Scope this list to *provider* nodes only. Don't add the agent node itself, * the LM Chat sub-models (`lmChatOpenAi`, etc.), or generic LangChain helpers diff --git a/packages/cli/src/permissions.ee/__tests__/project-scope.service.test.ts b/packages/cli/src/permissions.ee/__tests__/project-scope.service.test.ts new file mode 100644 index 00000000000..c7040089a10 --- /dev/null +++ b/packages/cli/src/permissions.ee/__tests__/project-scope.service.test.ts @@ -0,0 +1,52 @@ +import { mockInstance } from '@n8n/backend-test-utils'; +import { ProjectRelationRepository, User } from '@n8n/db'; + +import { RoleService } from '@/services/role.service'; + +import { ProjectScopeService } from '../project-scope.service'; + +const makeUser = (globalScopes: string[] = []) => + Object.assign(new User(), { + id: 'user-1', + role: { + slug: 'global:member', + scopes: globalScopes.map((slug) => ({ slug })), + }, + }); + +describe('ProjectScopeService', () => { + const roleService = mockInstance(RoleService); + const projectRelationRepository = mockInstance(ProjectRelationRepository); + const service = new ProjectScopeService(roleService, projectRelationRepository); + + beforeEach(() => { + vi.clearAllMocks(); + }); + + it('returns no project restriction when the user has the global scope', async () => { + const result = await service.getProjectIds(makeUser(['agent:update']), ['agent:update']); + + expect(result).toBeNull(); + expect(roleService.rolesWithScope).not.toHaveBeenCalled(); + expect(projectRelationRepository.getAccessibleProjectsByRoles).not.toHaveBeenCalled(); + }); + + it('resolves scoped project access with one project-relation query', async () => { + roleService.rolesWithScope.mockResolvedValue(['project:admin', 'project:editor']); + projectRelationRepository.getAccessibleProjectsByRoles.mockResolvedValue([ + 'project-1', + 'project-2', + ]); + + const result = await service.getProjectIds(makeUser(), ['agent:update']); + + expect(roleService.rolesWithScope).toHaveBeenCalledOnce(); + expect(roleService.rolesWithScope).toHaveBeenCalledWith('project', ['agent:update']); + expect(projectRelationRepository.getAccessibleProjectsByRoles).toHaveBeenCalledOnce(); + expect(projectRelationRepository.getAccessibleProjectsByRoles).toHaveBeenCalledWith('user-1', [ + 'project:admin', + 'project:editor', + ]); + expect(result).toEqual(['project-1', 'project-2']); + }); +}); diff --git a/packages/cli/src/permissions.ee/project-scope.service.ts b/packages/cli/src/permissions.ee/project-scope.service.ts new file mode 100644 index 00000000000..e9daf8c8eff --- /dev/null +++ b/packages/cli/src/permissions.ee/project-scope.service.ts @@ -0,0 +1,24 @@ +import { ProjectRelationRepository, type User } from '@n8n/db'; +import { Service } from '@n8n/di'; +import { hasGlobalScope, type Scope } from '@n8n/permissions'; + +import { RoleService } from '@/services/role.service'; + +/** + * Resolves the project restriction for a scope-aware query. + * `null` means the user's global role grants access to every project. + */ +@Service() +export class ProjectScopeService { + constructor( + private readonly roleService: RoleService, + private readonly projectRelationRepository: ProjectRelationRepository, + ) {} + + async getProjectIds(user: User, scopes: Scope[]): Promise { + if (hasGlobalScope(user, scopes, { mode: 'allOf' })) return null; + + const roles = await this.roleService.rolesWithScope('project', scopes); + return await this.projectRelationRepository.getAccessibleProjectsByRoles(user.id, roles); + } +} diff --git a/packages/cli/src/public-api/v1/__tests__/public-api-error-response.test.ts b/packages/cli/src/public-api/v1/__tests__/public-api-error-response.test.ts index d011ebd4e7d..b2c5554a82b 100644 --- a/packages/cli/src/public-api/v1/__tests__/public-api-error-response.test.ts +++ b/packages/cli/src/public-api/v1/__tests__/public-api-error-response.test.ts @@ -5,6 +5,7 @@ import { UnexpectedError, UserError, OperationalError } from 'n8n-workflow'; import { BadRequestError } from '@/errors/response-errors/bad-request.error'; import { ConflictError } from '@/errors/response-errors/conflict.error'; import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { WorkflowPublishBlockedError } from '@/errors/response-errors/workflow-publish-blocked.error'; import { sendPublicApiErrorResponse } from '../public-api-error-response'; @@ -48,6 +49,24 @@ describe('sendPublicApiErrorResponse', () => { expect(res._payload.body).toEqual({ message: 'managed declaratively' }); }); + it('returns the review request that is blocking publication', () => { + const res = createMockRes(); + sendPublicApiErrorResponse( + res, + new WorkflowPublishBlockedError({ + reason: 'review_pending', + workflowReviewRequestId: 'review-1', + }), + ); + + expect(res._payload.statusCode).toBe(409); + expect(res._payload.body).toEqual({ + message: expect.stringContaining('review is open'), + reason: 'review_pending', + workflowReviewRequestId: 'review-1', + }); + }); + it('maps UserError to 400', () => { const res = createMockRes(); sendPublicApiErrorResponse(res, new UserError('bad input')); diff --git a/packages/cli/src/public-api/v1/handlers/n8n-packages/__tests__/n8n-packages.handler.test.ts b/packages/cli/src/public-api/v1/handlers/n8n-packages/__tests__/n8n-packages.handler.test.ts index d36fe7bf0ba..806b5a17032 100644 --- a/packages/cli/src/public-api/v1/handlers/n8n-packages/__tests__/n8n-packages.handler.test.ts +++ b/packages/cli/src/public-api/v1/handlers/n8n-packages/__tests__/n8n-packages.handler.test.ts @@ -45,6 +45,7 @@ const EXPORT_COUNTS = { credentials: 0, dataTables: 0, variables: 0, + tags: 0, }; describe('n8n-packages handler', () => { @@ -57,6 +58,7 @@ describe('n8n-packages handler', () => { folderIds?: string[]; projectIds?: string[]; includeVariableValues?: boolean; + includeTags?: boolean; missingWorkflowDependencyPolicy?: string; }, apiKeyScopes?: string[], @@ -248,6 +250,7 @@ describe('n8n-packages handler', () => { projectIds: [], includeVariableValues: true, canExportVariableValues: false, + includeTags: true, missingWorkflowDependencyPolicy: 'fail', }); }); @@ -272,6 +275,7 @@ describe('n8n-packages handler', () => { projectIds: [], includeVariableValues: false, canExportVariableValues: false, + includeTags: true, missingWorkflowDependencyPolicy: 'fail', }); }); @@ -370,6 +374,7 @@ describe('n8n-packages handler', () => { projectIds: [], includeVariableValues: true, canExportVariableValues: true, + includeTags: true, missingWorkflowDependencyPolicy: 'fail', }); expect(res.setHeader).toHaveBeenCalledWith('Content-Type', 'application/gzip'); @@ -414,6 +419,7 @@ describe('n8n-packages handler', () => { projectIds: [], includeVariableValues: true, canExportVariableValues: false, + includeTags: true, missingWorkflowDependencyPolicy: 'reference-only', }); }); @@ -438,6 +444,7 @@ describe('n8n-packages handler', () => { projectIds: ['project-1'], includeVariableValues: true, canExportVariableValues: true, + includeTags: true, missingWorkflowDependencyPolicy: 'fail', }); }); @@ -462,6 +469,7 @@ describe('n8n-packages handler', () => { projectIds: [], includeVariableValues: true, canExportVariableValues: true, + includeTags: true, missingWorkflowDependencyPolicy: 'fail', }); }); @@ -486,6 +494,32 @@ describe('n8n-packages handler', () => { projectIds: [], includeVariableValues: false, canExportVariableValues: false, + includeTags: true, + missingWorkflowDependencyPolicy: 'fail', + }); + }); + + it('forwards includeTags=false to the service', async () => { + const stream = new PassThrough(); + mockService.exportPackage.mockResolvedValue({ stream, counts: EXPORT_COUNTS }); + const res = makeResponse(); + + const resultPromise = run( + makeRequest({ workflowIds: ['wf-1'], includeTags: false }, ['workflow:export']), + res, + ); + stream.end(Buffer.from('package-bytes')); + const caught = await resultPromise; + + expect(caught).toBeUndefined(); + expect(mockService.exportPackage).toHaveBeenCalledWith({ + user: { id: 'user-1' }, + workflowIds: ['wf-1'], + folderIds: [], + projectIds: [], + includeVariableValues: true, + canExportVariableValues: false, + includeTags: false, missingWorkflowDependencyPolicy: 'fail', }); }); diff --git a/packages/cli/src/public-api/v1/handlers/n8n-packages/n8n-packages.handler.ts b/packages/cli/src/public-api/v1/handlers/n8n-packages/n8n-packages.handler.ts index c90f5556311..56cd07b55e9 100644 --- a/packages/cli/src/public-api/v1/handlers/n8n-packages/n8n-packages.handler.ts +++ b/packages/cli/src/public-api/v1/handlers/n8n-packages/n8n-packages.handler.ts @@ -34,6 +34,7 @@ type ExportPackageRequest = AuthenticatedRequest< folderIds?: string[]; projectIds?: string[]; includeVariableValues?: boolean; + includeTags?: boolean; missingWorkflowDependencyPolicy?: 'fail' | 'reference-only' | 'include-in-package'; } >; @@ -147,6 +148,7 @@ const n8nPackagesHandlers: N8nPackagesHandlers = { projectIds, includeVariableValues, canExportVariableValues: apiKeyScopes.includes('variable:list'), + includeTags: payload.data.includeTags, missingWorkflowDependencyPolicy: payload.data.missingWorkflowDependencyPolicy, }); @@ -209,6 +211,8 @@ const n8nPackagesHandlers: N8nPackagesHandlers = { dataTableSchemaConflictPolicy: payload.data.dataTableSchemaConflictPolicy, variableMissingMode: payload.data.variableMissingMode, variableParentPolicy: payload.data.variableParentPolicy, + tagMissingMode: payload.data.tagMissingMode, + tagConflictPolicy: payload.data.tagConflictPolicy, packageBuffer: packageFile.buffer, }); return res.status(200).json(result); diff --git a/packages/cli/src/public-api/v1/handlers/n8n-packages/spec/paths/n8n-packages.import.yml b/packages/cli/src/public-api/v1/handlers/n8n-packages/spec/paths/n8n-packages.import.yml index 1d6f9fdddef..903a82f52d4 100644 --- a/packages/cli/src/public-api/v1/handlers/n8n-packages/spec/paths/n8n-packages.import.yml +++ b/packages/cli/src/public-api/v1/handlers/n8n-packages/spec/paths/n8n-packages.import.yml @@ -11,8 +11,8 @@ post: Imports a gzip-compressed tar package (`.n8np`) into the target project. Send the archive as the multipart field `package`. Optional routing uses form fields `projectId` and `folderId` (omit or send empty for defaults). Every optional - mode/policy field (credential, workflow, folder, data table, and variable) takes - its default when omitted. The required + mode/policy field (credential, workflow, folder, data table, variable, and tag) + takes its default when omitted. The required `workflowConflictPolicy` field controls what happens when a package workflow matches an existing workflow by source id in the target project. Maximum upload size is `N8N_ENDPOINTS_PAYLOAD_SIZE_MAX` MB (default 16). @@ -236,6 +236,40 @@ post: placement follows the package layout, where a variable bundled under a project is created in that project and one bundled at the top level is created globally. + tagMissingMode: + type: string + enum: + - create + - do-nothing + default: create + description: > + What to do when a tag referenced by the package's workflows has no + tag with the same id on the target instance (tags are matched by + source id, never by name). `create` (default) creates the tag + globally with its package (source) id and name; when the import + would create a tag this needs an API key carrying the + `tag:create` scope. `do-nothing` imports the workflows without the + missing tags — nothing is created and the dropped names are listed + under `tags.skipped`. + tagConflictPolicy: + type: string + enum: + - skip + - fail + - rename + default: skip + description: > + What to do when a referenced tag conflicts on the target instance — + either the same-id target tag carries a different name (rename + drift), or the tag's name is already held by a different tag (name + collision). `skip` (default) drops the conflicted tags from the + import (not created, not renamed, not attached anywhere; the import + proceeds and lists them under `tags.skipped`). `fail` rejects the + import with a 409. `rename` renames a drifted target tag to the + package name and needs an API key carrying the `tag:update` scope + when the import would rename a tag; a name collision — including + a drifted tag whose package name is held by another tag — still + rejects the import (rename degrades to fail). responses: '200': description: Import succeeded; the listed workflows were written to the target project. @@ -251,6 +285,7 @@ post: - bindings - credentials - variables + - tags properties: package: type: object @@ -474,6 +509,46 @@ post: description: > Variable names created with an empty value by this import under `variableMissingMode=create-stub`. Empty for other modes. + tags: + type: object + description: > + Resolution of the tags referenced by the imported workflows, + matched by source id. Tag names only. + required: + - matched + - created + - renamed + - skipped + properties: + matched: + type: array + items: + type: string + description: > + Tags that resolved to an existing tag with the same id and + name; attached as-is. + created: + type: array + items: + type: string + description: > + Tags created by this import with their package (source) id + and name, under `tagMissingMode=create`. + renamed: + type: array + items: + type: string + description: > + Target tags renamed to the package name under + `tagConflictPolicy=rename`; listed by their new name. + skipped: + type: array + items: + type: string + description: > + Tags dropped from the import — not created, not renamed, and + not attached to any imported workflow — under + `tagMissingMode=do-nothing` or `tagConflictPolicy=skip`. bindings: type: object description: > @@ -498,8 +573,9 @@ post: '409': description: > Import blocked by at least one conflict among the issues — a workflow - source-id conflict or a folder conflict (id under a different parent, id - owned by another project, or a `fail`-policy match). + source-id conflict, a folder conflict (id under a different parent, id + owned by another project, or a `fail`-policy match), or a tag conflict + (rename drift or name collision). content: application/json: schema: diff --git a/packages/cli/src/public-api/v1/handlers/n8n-packages/spec/schemas/exportPackageRequest.yml b/packages/cli/src/public-api/v1/handlers/n8n-packages/spec/schemas/exportPackageRequest.yml index d74f9a8c1fd..e861d12873c 100644 --- a/packages/cli/src/public-api/v1/handlers/n8n-packages/spec/schemas/exportPackageRequest.yml +++ b/packages/cli/src/public-api/v1/handlers/n8n-packages/spec/schemas/exportPackageRequest.yml @@ -41,6 +41,13 @@ properties: 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: + type: boolean + default: true + 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. missingWorkflowDependencyPolicy: type: string enum: @@ -51,6 +58,8 @@ properties: Policy for missing static sub-workflow dependencies. `fail` aborts the export when any static sub-workflow is missing from the package; `include-in-package` automatically adds missing static sub-workflows. - `reference-only` is reserved for a future export mode. + `reference-only` keeps missing static sub-workflows out of the package + and lists them in the package requirements as workflows expected to + already exist on the target. example: fail default: fail diff --git a/packages/cli/src/public-api/v1/handlers/n8n-packages/spec/schemas/importBlockingIssue.yml b/packages/cli/src/public-api/v1/handlers/n8n-packages/spec/schemas/importBlockingIssue.yml index 464f8d34fb6..276c7d74a80 100644 --- a/packages/cli/src/public-api/v1/handlers/n8n-packages/spec/schemas/importBlockingIssue.yml +++ b/packages/cli/src/public-api/v1/handlers/n8n-packages/spec/schemas/importBlockingIssue.yml @@ -187,6 +187,60 @@ oneOf: type: array items: type: string + - type: object + description: > + A tag referenced by the package's workflows that could not be resolved on + the target instance. `kind` distinguishes the cause: `rename-drift` (the + same-id target tag carries a different name — under `tagConflictPolicy=fail`, + or `rename` when the package name is held by another tag), `name-collision` + (the id is free but the name belongs to a different tag, under + `tagMissingMode=create` with `tagConflictPolicy=fail` or `rename`), + `invalid-name` / `invalid-id` (the package tag's name or id cannot be + written on this instance), or `permission-denied` (the importing user lacks + the global `tag:create` / `tag:update` scope the plan needs). + required: + - type + - kind + - usedByWorkflows + properties: + type: + type: string + enum: + - tag-unresolved + kind: + type: string + enum: + - rename-drift + - name-collision + - invalid-name + - invalid-id + - permission-denied + sourceId: + type: string + description: Tag id as it appears in the package. Absent for `permission-denied`. + name: + type: string + description: The (trimmed) package tag name. Absent for `permission-denied`. + missingScope: + type: string + enum: + - 'tag:create' + - 'tag:update' + description: 'For `permission-denied`: the global scope the importing user lacks.' + existingTagId: + type: string + description: > + Id of the different target tag currently holding the wanted name, when + one exists. Absent when two package tags collide with each other rather + than with a target tag. + existingName: + type: string + description: 'For `rename-drift`: the current name of the same-id target tag.' + usedByWorkflows: + type: array + items: + type: string + description: Package workflow ids (non-skipped) that reference the tag. - type: object description: > A variable reference that could not be resolved in the target project or the diff --git a/packages/cli/src/public-api/v1/handlers/sso-oidc/spec/schemas/oidc-configuration.update.yml b/packages/cli/src/public-api/v1/handlers/sso-oidc/spec/schemas/oidc-configuration.update.yml index e6f34d7ed43..ecfffe94252 100644 --- a/packages/cli/src/public-api/v1/handlers/sso-oidc/spec/schemas/oidc-configuration.update.yml +++ b/packages/cli/src/public-api/v1/handlers/sso-oidc/spec/schemas/oidc-configuration.update.yml @@ -12,6 +12,7 @@ required: - prompt - authenticationContextClassReference - additionalScopes + - rpInitiatedLogoutEnabled properties: clientId: type: string @@ -53,3 +54,9 @@ properties: Additional scopes to request, space separated. n8n always requests `openid`, `profile` and `email`. Use an empty string when unused. example: groups roles + rpInitiatedLogoutEnabled: + type: boolean + description: > + Whether signing out of n8n also ends the session at the OIDC provider via RP-Initiated + Logout. When disabled, sign-out is local to n8n only. + example: false diff --git a/packages/cli/src/public-api/v1/handlers/sso-oidc/spec/schemas/oidc-configuration.yml b/packages/cli/src/public-api/v1/handlers/sso-oidc/spec/schemas/oidc-configuration.yml index 736c7641b73..e85502646c9 100644 --- a/packages/cli/src/public-api/v1/handlers/sso-oidc/spec/schemas/oidc-configuration.yml +++ b/packages/cli/src/public-api/v1/handlers/sso-oidc/spec/schemas/oidc-configuration.yml @@ -8,6 +8,7 @@ required: - prompt - authenticationContextClassReference - additionalScopes + - rpInitiatedLogoutEnabled properties: clientId: type: string @@ -47,3 +48,9 @@ properties: Additional scopes to request, space separated. n8n always requests `openid`, `profile` and `email`. example: groups roles + rpInitiatedLogoutEnabled: + type: boolean + description: > + Whether signing out of n8n also ends the session at the OIDC provider via RP-Initiated + Logout. When disabled, sign-out is local to n8n only. + example: false diff --git a/packages/cli/src/public-api/v1/handlers/workflows/__tests__/workflow-publish-blocked.openapi.test.ts b/packages/cli/src/public-api/v1/handlers/workflows/__tests__/workflow-publish-blocked.openapi.test.ts new file mode 100644 index 00000000000..3f8e82a8dc7 --- /dev/null +++ b/packages/cli/src/public-api/v1/handlers/workflows/__tests__/workflow-publish-blocked.openapi.test.ts @@ -0,0 +1,58 @@ +import fs from 'fs'; +import path from 'path'; +import { parse } from 'yaml'; + +describe('workflow publication blocker in OpenAPI', () => { + const specRoot = path.join(__dirname, '../spec'); + const blockerSchemaRef = '../schemas/workflowPublishBlockedError.yml'; + + test('documents the blocker for publish, deprecated activate, and active workflow updates', () => { + const publishPath = parse( + fs.readFileSync(path.join(specRoot, 'paths/workflows.id.publish.yml'), 'utf8'), + ); + const activatePath = parse( + fs.readFileSync(path.join(specRoot, 'paths/workflows.id.activate.yml'), 'utf8'), + ); + const workflowPath = parse( + fs.readFileSync(path.join(specRoot, 'paths/workflows.id.yml'), 'utf8'), + ); + + expect(publishPath.post.responses['409'].content['application/json'].schema.$ref).toBe( + blockerSchemaRef, + ); + expect(activatePath.post.responses['409'].content['application/json'].schema.$ref).toBe( + blockerSchemaRef, + ); + expect(workflowPath.put.responses['409'].content['application/json'].schema.$ref).toBe( + blockerSchemaRef, + ); + }); + + test('documents the review reason and request ID as optional', () => { + const schema = parse( + fs.readFileSync(path.join(specRoot, 'schemas/workflowPublishBlockedError.yml'), 'utf8'), + ); + + expect(schema).toMatchObject({ + properties: { + reason: { + enum: ['review_pending', 'changes_requested'], + }, + workflowReviewRequestId: { + type: 'string', + }, + }, + }); + }); + + // the same 409 also carries webhook conflicts, whose body is + // `message`-only + test('stays satisfiable by a message-only conflict body', () => { + const schema = parse( + fs.readFileSync(path.join(specRoot, 'schemas/workflowPublishBlockedError.yml'), 'utf8'), + ); + + expect(schema.required).toEqual(['message']); + expect(schema.additionalProperties).toBeUndefined(); + }); +}); diff --git a/packages/cli/src/public-api/v1/handlers/workflows/spec/paths/workflows.id.activate.yml b/packages/cli/src/public-api/v1/handlers/workflows/spec/paths/workflows.id.activate.yml index 36b4ef8954f..4f0ff4a6568 100644 --- a/packages/cli/src/public-api/v1/handlers/workflows/spec/paths/workflows.id.activate.yml +++ b/packages/cli/src/public-api/v1/handlers/workflows/spec/paths/workflows.id.activate.yml @@ -41,3 +41,12 @@ post: $ref: '../../../../shared/spec/responses/unauthorized.yml' '404': $ref: '../../../../shared/spec/responses/notFound.yml' + '409': + description: >- + Conflict, e.g. publication blocked by an open workflow review (then + `reason` and `workflowReviewRequestId` are present) or a webhook path + conflict. + content: + application/json: + schema: + $ref: '../schemas/workflowPublishBlockedError.yml' diff --git a/packages/cli/src/public-api/v1/handlers/workflows/spec/paths/workflows.id.publish.yml b/packages/cli/src/public-api/v1/handlers/workflows/spec/paths/workflows.id.publish.yml index 599710e7652..3ea39fc19d8 100644 --- a/packages/cli/src/public-api/v1/handlers/workflows/spec/paths/workflows.id.publish.yml +++ b/packages/cli/src/public-api/v1/handlers/workflows/spec/paths/workflows.id.publish.yml @@ -38,3 +38,12 @@ post: $ref: '../../../../shared/spec/responses/unauthorized.yml' '404': $ref: '../../../../shared/spec/responses/notFound.yml' + '409': + description: >- + Conflict, e.g. publication blocked by an open workflow review (then + `reason` and `workflowReviewRequestId` are present) or a webhook path + conflict. + content: + application/json: + schema: + $ref: '../schemas/workflowPublishBlockedError.yml' diff --git a/packages/cli/src/public-api/v1/handlers/workflows/spec/paths/workflows.id.yml b/packages/cli/src/public-api/v1/handlers/workflows/spec/paths/workflows.id.yml index 0d74fc15ac5..1823e5f6b6e 100644 --- a/packages/cli/src/public-api/v1/handlers/workflows/spec/paths/workflows.id.yml +++ b/packages/cli/src/public-api/v1/handlers/workflows/spec/paths/workflows.id.yml @@ -77,3 +77,12 @@ put: $ref: '../../../../shared/spec/responses/unauthorized.yml' '404': $ref: '../../../../shared/spec/responses/notFound.yml' + '409': + description: >- + Conflict, e.g. re-publication blocked by an open workflow review (then + `reason` and `workflowReviewRequestId` are present; the update itself is + still saved as a draft) or a webhook path conflict. + content: + application/json: + schema: + $ref: '../schemas/workflowPublishBlockedError.yml' diff --git a/packages/cli/src/public-api/v1/handlers/workflows/spec/schemas/workflowPublishBlockedError.yml b/packages/cli/src/public-api/v1/handlers/workflows/spec/schemas/workflowPublishBlockedError.yml new file mode 100644 index 00000000000..78303775f62 --- /dev/null +++ b/packages/cli/src/public-api/v1/handlers/workflows/spec/schemas/workflowPublishBlockedError.yml @@ -0,0 +1,16 @@ +type: object +description: >- + Conflict response. `reason` and `workflowReviewRequestId` are present only + when the conflict is an open workflow review blocking publication. +required: + - message +properties: + message: + type: string + reason: + type: string + enum: + - review_pending + - changes_requested + workflowReviewRequestId: + type: string diff --git a/packages/cli/src/scaling/__tests__/job-processor.service.test.ts b/packages/cli/src/scaling/__tests__/job-processor.service.test.ts index 3bd36d5728c..30489d6ae8f 100644 --- a/packages/cli/src/scaling/__tests__/job-processor.service.test.ts +++ b/packages/cli/src/scaling/__tests__/job-processor.service.test.ts @@ -26,6 +26,7 @@ import { mock } from 'vitest-mock-extended'; import { CredentialsHelper } from '@/credentials-helper'; import { VariablesService } from '@/environments.ee/variables/variables.service.ee'; +import { WebhookResponseTooLargeError } from '@/errors/webhook-response-too-large.error'; import { ExternalHooks } from '@/external-hooks'; import type { ExecutionPersistence } from '@/executions/execution-persistence'; import type { ManualExecutionService } from '@/manual-execution.service'; @@ -39,6 +40,7 @@ import { WorkflowStaticDataService } from '@/workflows/workflow-static-data.serv import { JobProcessor } from '../job-processor'; import type { Job } from '../scaling.types'; +import { ENCODED_BUFFER_KEY } from '../webhook-response-relay'; mockInstance(WorkflowPublishHistoryRepository); mockInstance(VariablesService, { @@ -72,10 +74,14 @@ const logger = mock({ scoped: vi.fn().mockImplementation(() => logger), }); -const executionsConfig = mock({ - timeout: -1, - maxTimeout: 3600, -}); +const executionsConfigWith = (overrides: Partial = {}) => + mock({ + timeout: -1, + maxTimeout: 3600, + ...overrides, + }); + +const executionsConfig = executionsConfigWith(); const successRun = (): IRun => mock({ @@ -1967,4 +1973,92 @@ describe('JobProcessor', () => { expect(startedCall![1]).not.toHaveProperty('projectName'); }); }); + + describe('webhook response relay', () => { + const processJobAndCaptureHooks = async ( + webhookResponseRelaySizeMaxMiB: number, + jobData: Partial = {}, + ) => { + const executionPersistence = mock(); + executionPersistence.findSingleExecution.mockResolvedValue( + mock({ + mode: 'manual', + workflowData: { nodes: [], staticData: {} }, + data: mock({ executionData: undefined }), + }), + ); + + const additionalData = mock(); + vi.spyOn(WorkflowExecuteAdditionalData, 'getBase').mockResolvedValue(additionalData); + + const jobProcessor = new JobProcessor( + logger, + mock(), + executionPersistence, + mock(), + mock(), + mock(), + createManualExecutionServiceMock(), + executionsConfigWith({ webhookResponseRelaySizeMaxMiB }), + mock(), + ); + + const job = mock({ + data: { + workflowId: 'wf-1', + executionId: 'exec-1', + loadStaticData: false, + isMcpExecution: undefined, + mcpSessionId: undefined, + ...jobData, + }, + }); + await jobProcessor.processJob(job); + + return { hooks: additionalData.hooks!, job }; + }; + + it('should relay a response within the size limit, encoding a buffer body', async () => { + const { hooks, job } = await processJobAndCaptureHooks(1); + + await hooks.runHook('sendResponse', [ + { body: Buffer.from('hello'), headers: {}, statusCode: 200 }, + ]); + + expect(job.progress).toHaveBeenCalledWith( + expect.objectContaining({ + kind: 'respond-to-webhook', + executionId: 'exec-1', + response: expect.objectContaining({ body: { [ENCODED_BUFFER_KEY]: 'aGVsbG8=' } }), + }), + ); + }); + + it('should refuse to relay a response over the size limit', async () => { + const { hooks, job } = await processJobAndCaptureHooks(1); + const relayedBefore = (job.progress as Mock).mock.calls.length; + + await expect( + hooks.runHook('sendResponse', [ + { body: { blob: 'x'.repeat(2 * 1024 * 1024) }, headers: {}, statusCode: 200 }, + ]), + ).rejects.toThrow(WebhookResponseTooLargeError); + + expect((job.progress as Mock).mock.calls).toHaveLength(relayedBefore); + }); + + it('should refuse to relay an MCP response over the size limit', async () => { + const { hooks, job } = await processJobAndCaptureHooks(1, { + isMcpExecution: true, + mcpSessionId: 'session-1', + }); + const relayedBefore = (job.progress as Mock).mock.calls.length; + + await expect( + hooks.runHook('sendResponse', [{ toolResult: 'x'.repeat(2 * 1024 * 1024) }]), + ).rejects.toThrow(WebhookResponseTooLargeError); + + expect((job.progress as Mock).mock.calls).toHaveLength(relayedBefore); + }); + }); }); diff --git a/packages/cli/src/scaling/__tests__/webhook-response-relay.test.ts b/packages/cli/src/scaling/__tests__/webhook-response-relay.test.ts new file mode 100644 index 00000000000..3873e2ffeb3 --- /dev/null +++ b/packages/cli/src/scaling/__tests__/webhook-response-relay.test.ts @@ -0,0 +1,183 @@ +import type { IExecuteResponsePromiseData } from 'n8n-workflow'; +import { Readable } from 'node:stream'; + +import { WebhookResponseTooLargeError } from '@/errors/webhook-response-too-large.error'; + +import { + assertRelayableSize, + decodeRelayedWebhookResponse, + ENCODED_BUFFER_KEY, + encodeRelayedWebhookResponse, +} from '../webhook-response-relay'; + +const fullResponse = (body: unknown): IExecuteResponsePromiseData => + ({ body, headers: {}, statusCode: 200 }) as IExecuteResponsePromiseData; + +const ONE_MIB = 1024 * 1024; + +describe('encodeRelayedWebhookResponse', () => { + it('wraps a Buffer body in a base64 envelope', () => { + const response = fullResponse(Buffer.from('hello')); + + const encoded = encodeRelayedWebhookResponse(response); + + expect(encoded).toBe(response); + expect(encoded).toEqual( + expect.objectContaining({ body: { [ENCODED_BUFFER_KEY]: 'aGVsbG8=' } }), + ); + }); + + it.each([ + ['a JSON body', { hello: 'world' }], + ['a string body', 'hello'], + ['a null body', null], + ['a binary-data reference', { binaryData: { id: 'database:abc' } }], + ])('leaves %s untouched', (_label, body) => { + const encoded = encodeRelayedWebhookResponse(fullResponse(body)); + + expect(encoded).toEqual(expect.objectContaining({ body })); + }); + + it('leaves a stream body untouched', () => { + const stream = Readable.from('hello'); + + const encoded = encodeRelayedWebhookResponse(fullResponse(stream)); + + expect(encoded).toEqual(expect.objectContaining({ body: stream })); + }); + + it('leaves a payload that is not a full response untouched', () => { + const payload = { toolResult: 'done' }; + + expect(encodeRelayedWebhookResponse(payload)).toBe(payload); + }); +}); + +describe('decodeRelayedWebhookResponse', () => { + it('restores a base64 envelope to a Buffer', () => { + const response = fullResponse({ [ENCODED_BUFFER_KEY]: 'aGVsbG8=' }); + + const decoded = decodeRelayedWebhookResponse(response); + + expect(decoded).toBe(response); + expect((decoded as { body: Buffer }).body).toEqual(Buffer.from('hello')); + }); + + it('round-trips a Buffer body', () => { + const body = Buffer.from([0x00, 0xff, 0x10]); + + const decoded = decodeRelayedWebhookResponse(encodeRelayedWebhookResponse(fullResponse(body))); + + expect((decoded as { body: Buffer }).body).toEqual(body); + }); + + it.each([ + ['a JSON body', { hello: 'world' }], + ['a string body', 'hello'], + ['a null body', null], + ['an envelope whose payload is not a string', { [ENCODED_BUFFER_KEY]: 42 }], + ])('leaves %s untouched', (_label, body) => { + const decoded = decodeRelayedWebhookResponse(fullResponse(body)); + + expect(decoded).toEqual(expect.objectContaining({ body })); + }); + + it('leaves a payload that is not a full response untouched', () => { + const payload = { toolResult: 'done' }; + + expect(decodeRelayedWebhookResponse(payload)).toBe(payload); + }); +}); + +describe('assertRelayableSize', () => { + describe('within the limit', () => { + it.each([ + ['a Buffer body', Buffer.alloc(ONE_MIB)], + ['a string body', 'x'.repeat(ONE_MIB)], + ['a JSON body', { blob: 'x'.repeat(ONE_MIB) }], + ['a nested JSON body', { outer: { inner: ['x'.repeat(ONE_MIB)] } }], + ['a null body', null], + ['a stream body', Readable.from('hello')], + ['a binary-data reference', { binaryData: { id: 'database:abc' } }], + ])('accepts %s', (_label, body) => { + expect(() => assertRelayableSize(fullResponse(body), 2)).not.toThrow(); + }); + + it('accepts a payload that is not a full response', () => { + expect(() => assertRelayableSize({ toolResult: 'done' }, 2)).not.toThrow(); + }); + + it('accepts a cyclic body it cannot serialize', () => { + const body: Record = { name: 'cycle' }; + body.self = body; + + expect(() => assertRelayableSize(fullResponse(body), 2)).not.toThrow(); + }); + + it('accepts a body and headers each within the limit, measuring them separately', () => { + const response: IExecuteResponsePromiseData = { + body: 'x'.repeat(ONE_MIB), + headers: { 'x-data': 'y'.repeat(ONE_MIB) }, + statusCode: 200, + }; + + expect(() => assertRelayableSize(response, 2)).not.toThrow(); + }); + }); + + describe('over the limit', () => { + it.each([ + ['a Buffer body', Buffer.alloc(3 * ONE_MIB)], + ['a string body', 'x'.repeat(3 * ONE_MIB)], + ['a multi-byte string body', 'é'.repeat(2 * ONE_MIB)], + ['a JSON body', { blob: 'x'.repeat(3 * ONE_MIB) }], + ['a JSON body nested in an array', { items: [{ blob: 'x'.repeat(3 * ONE_MIB) }] }], + ])('rejects %s', (_label, body) => { + expect(() => assertRelayableSize(fullResponse(body), 2)).toThrow( + WebhookResponseTooLargeError, + ); + }); + + it('rejects a Buffer body only its base64 expansion pushes over the limit', () => { + const body = Buffer.alloc(1.75 * ONE_MIB); + + expect(() => assertRelayableSize(fullResponse(body), 2)).toThrow( + WebhookResponseTooLargeError, + ); + }); + + it('rejects a payload that is not a full response', () => { + expect(() => assertRelayableSize({ toolResult: 'x'.repeat(3 * ONE_MIB) }, 2)).toThrow( + WebhookResponseTooLargeError, + ); + }); + + it('rejects oversized headers', () => { + const response: IExecuteResponsePromiseData = { + body: null, + headers: { 'x-data': 'x'.repeat(3 * ONE_MIB) }, + statusCode: 200, + }; + + expect(() => assertRelayableSize(response, 2)).toThrow(WebhookResponseTooLargeError); + }); + + it('rejects an oversized value beside the body of a payload that is not a full response', () => { + const payload = { body: 'small', extra: 'x'.repeat(3 * ONE_MIB) }; + + expect(() => assertRelayableSize(payload, 2)).toThrow(WebhookResponseTooLargeError); + }); + + it('names the limit and how to raise it', () => { + let error: WebhookResponseTooLargeError | undefined; + try { + assertRelayableSize(fullResponse('x'.repeat(3 * ONE_MIB)), 2); + } catch (e) { + error = e as WebhookResponseTooLargeError; + } + + expect(error?.message).toContain('limit is 2 MiB'); + expect(error?.description).toContain('N8N_WEBHOOK_RESPONSE_RELAY_SIZE_MAX'); + }); + }); +}); diff --git a/packages/cli/src/scaling/job-processor.ts b/packages/cli/src/scaling/job-processor.ts index d8adb4484b7..6aab615df42 100644 --- a/packages/cli/src/scaling/job-processor.ts +++ b/packages/cli/src/scaling/job-processor.ts @@ -15,7 +15,6 @@ import type { IDataObject, IExecuteData, IExecuteFunctions, - IExecuteResponsePromiseData, IExecutionContext, INodeExecutionData, IRun, @@ -26,7 +25,6 @@ import type { GenericValue, } from 'n8n-workflow'; import { - BINARY_ENCODING, ManualExecutionCancelledError, NodeConnectionTypes, NodeOperationError, @@ -59,6 +57,7 @@ import type { RunningJob, SendChunkMessage, } from './scaling.types'; +import { assertRelayableSize, encodeRelayedWebhookResponse } from './webhook-response-relay'; /** * Responsible for processing jobs from the queue, i.e. running enqueued executions. @@ -185,6 +184,7 @@ export class JobProcessor { pushRef, userId: execution.data.manualData?.userId, source: execution.data.manualData?.source, + suppressErrorWorkflow: execution.data.manualData?.suppressErrorWorkflow, }, executionId, ); @@ -197,6 +197,8 @@ export class JobProcessor { } lifecycleHooks.addHandler('sendResponse', async (response): Promise => { + assertRelayableSize(response, this.executionsConfig.webhookResponseRelaySizeMaxMiB); + // Check if this is an MCP execution - broadcast response to all mains if (job.data.isMcpExecution && job.data.mcpSessionId) { const msg: McpResponseMessage = { @@ -217,7 +219,7 @@ export class JobProcessor { const msg: RespondToWebhookMessage = { kind: 'respond-to-webhook', executionId, - response: this.encodeWebhookResponse(response), + response: encodeRelayedWebhookResponse(response), workerId: this.instanceSettings.hostId, }; @@ -492,18 +494,6 @@ export class JobProcessor { return Object.values(this.runningJobs).map(({ run, ...summary }) => summary); } - private encodeWebhookResponse( - response: IExecuteResponsePromiseData, - ): IExecuteResponsePromiseData { - if (typeof response === 'object' && Buffer.isBuffer(response.body)) { - response.body = { - '__@N8nEncodedBuffer@__': response.body.toString(BINARY_ENCODING), - }; - } - - return response; - } - /** * Invoke a tool directly for MCP Trigger in queue mode. * For nodes with supplyData (e.g. native langchain tool nodes), creates a diff --git a/packages/cli/src/scaling/scaling.service.ts b/packages/cli/src/scaling/scaling.service.ts index bfa5826703d..dde5695cd0f 100644 --- a/packages/cli/src/scaling/scaling.service.ts +++ b/packages/cli/src/scaling/scaling.service.ts @@ -6,8 +6,9 @@ import { OnLeaderStepdown, OnLeaderTakeover, OnShutdown } from '@n8n/decorators' import { Container, Service } from '@n8n/di'; import { ErrorReporter, InstanceSettings } from 'n8n-core'; import { ensureError } from '@n8n/utils/errors/ensure-error'; -import { BINARY_ENCODING, sleep, jsonStringify, UnexpectedError } from 'n8n-workflow'; -import type { IExecuteResponsePromiseData, IRun } from 'n8n-workflow'; +import { sleep } from '@n8n/utils/sleep'; +import { jsonStringify, UnexpectedError } from 'n8n-workflow'; +import type { IRun } from 'n8n-workflow'; import assert, { strict } from 'node:assert'; import { ActiveExecutions } from '@/active-executions'; @@ -30,6 +31,7 @@ import type { JobMessage, JobFailedMessage, } from './scaling.types'; +import { decodeRelayedWebhookResponse } from './webhook-response-relay'; @Service() export class ScalingService { @@ -351,10 +353,11 @@ export class ScalingService { case 'send-chunk': this.activeExecutions.sendChunk(msg.executionId, msg.chunkText); break; - case 'respond-to-webhook': - const decodedResponse = this.decodeWebhookResponse(msg.response); + case 'respond-to-webhook': { + const decodedResponse = decodeRelayedWebhookResponse(msg.response); this.activeExecutions.resolveResponsePromise(msg.executionId, decodedResponse); break; + } case 'job-finished': if (msg.success) { this.activeExecutions.resolveResponsePromise(msg.executionId, {}); @@ -494,22 +497,6 @@ export class ScalingService { // #endregion - private decodeWebhookResponse( - response: IExecuteResponsePromiseData, - ): IExecuteResponsePromiseData { - if ( - typeof response === 'object' && - typeof response.body === 'object' && - response.body !== null && - '__@N8nEncodedBuffer@__' in response.body && - typeof response.body['__@N8nEncodedBuffer@__'] === 'string' - ) { - response.body = Buffer.from(response.body['__@N8nEncodedBuffer@__'], BINARY_ENCODING); - } - - return response; - } - private assertQueue() { if (this.queue) return; diff --git a/packages/cli/src/scaling/webhook-response-relay.ts b/packages/cli/src/scaling/webhook-response-relay.ts new file mode 100644 index 00000000000..2519b5b9b5d --- /dev/null +++ b/packages/cli/src/scaling/webhook-response-relay.ts @@ -0,0 +1,135 @@ +/** + * Moves webhook response bodies from a worker back to main in scaling mode. + * + * A relayed body travels inline inside a single queue message, so its size + * bounds what the queue must hold while that message is processed. + */ + +import { jsonSizeExceeds } from '@n8n/utils/json/json-size-exceeds'; +import { BINARY_ENCODING } from 'n8n-workflow'; +import type { IDataObject, IExecuteResponsePromiseData, IN8nHttpFullResponse } from 'n8n-workflow'; +import { Readable } from 'node:stream'; + +import { WebhookResponseTooLargeError } from '@/errors/webhook-response-too-large.error'; + +/** Sentinel key marking a base64-encoded Buffer body relayed inline through the queue. */ +export const ENCODED_BUFFER_KEY = '__@N8nEncodedBuffer@__'; + +/** + * Asserts that a payload is small enough to be relayed through the queue. + * + * @throws {WebhookResponseTooLargeError} When the payload exceeds `maxSizeInMiB`. + * + * @remarks The whole payload counts, headers included, the body and the rest + * being measured separately, each against the limit. A Buffer body counts + * base64-encoded, the form it travels in, and JSON content counts as an upper + * bound of what it serializes to. A string body counts as its raw bytes, so the + * escapes serialization adds to it are not counted, and neither is the queue + * envelope around the payload. + */ +export function assertRelayableSize( + payload: IExecuteResponsePromiseData, + maxSizeInMiB: number, +): void { + if (exceedsRelayableSize(payload, maxSizeInMiB * 1024 * 1024)) { + throw new WebhookResponseTooLargeError(maxSizeInMiB); + } +} + +/** Whether any part of the payload would serialize to more than `maxBytes`. */ +function exceedsRelayableSize(payload: IExecuteResponsePromiseData, maxBytes: number): boolean { + if (!isFullResponse(payload)) { + return exceedsSize(payload, maxBytes); + } + + const { body, ...rest } = payload; + return exceedsSize(body, maxBytes) || jsonSizeExceeds(rest, maxBytes); +} + +/** Whether the body would serialize to more than `maxBytes`. */ +function exceedsSize(body: IN8nHttpFullResponse['body'], maxBytes: number): boolean { + if (Buffer.isBuffer(body)) { + return base64Size(body.length) > maxBytes; + } + + if (typeof body === 'string') { + return Buffer.byteLength(body) > maxBytes; + } + + if (isJsonObject(body)) { + return jsonSizeExceeds(body, maxBytes); + } + + return false; +} + +function base64Size(byteLength: number): number { + return Math.ceil(byteLength / 3) * 4; +} + +function isJsonObject(body: IN8nHttpFullResponse['body']): body is IDataObject { + return ( + typeof body === 'object' && + body !== null && + !Buffer.isBuffer(body) && + !(body instanceof Readable) + ); +} + +/** + * Encodes a worker's webhook response into the form it travels in: + * - a Buffer body is base64-encoded + * - every other body passes through untouched. + * + * @param response Worker response. Mutated and returned. + * @returns The same `response`, with a Buffer body wrapped in a base64 envelope. + */ +export function encodeRelayedWebhookResponse( + response: IExecuteResponsePromiseData, +): IExecuteResponsePromiseData { + if (!isFullResponse(response)) { + return response; + } + + if (Buffer.isBuffer(response.body)) { + response.body = { [ENCODED_BUFFER_KEY]: response.body.toString(BINARY_ENCODING) }; + } + + return response; +} + +/** + * Reverses {@link encodeRelayedWebhookResponse} on main, decoding a base64 + * envelope back into a Buffer. Every other body passes through untouched. + * + * @param response Relayed response. Mutated and returned. + * @returns The same `response`, with an encoded-buffer body restored to a Buffer. + */ +export function decodeRelayedWebhookResponse( + response: IExecuteResponsePromiseData, +): IExecuteResponsePromiseData { + if (!isFullResponse(response)) { + return response; + } + + const encoded = encodedBufferIn(response.body); + if (encoded !== undefined) { + response.body = Buffer.from(encoded, BINARY_ENCODING); + } + + return response; +} + +function isFullResponse(response: IExecuteResponsePromiseData): response is IN8nHttpFullResponse { + return typeof response === 'object' && response !== null && 'body' in response; +} + +/** The base64 payload of an {@link ENCODED_BUFFER_KEY} envelope, if the body is one. */ +function encodedBufferIn(body: IN8nHttpFullResponse['body']): string | undefined { + if (typeof body !== 'object' || body === null || !(ENCODED_BUFFER_KEY in body)) { + return undefined; + } + + const encoded = body[ENCODED_BUFFER_KEY]; + return typeof encoded === 'string' ? encoded : undefined; +} diff --git a/packages/cli/src/scheduling/__tests__/durable-job-provisioner.test.ts b/packages/cli/src/scheduling/__tests__/durable-job-provisioner.test.ts index c6e50b56c80..74e58c133fd 100644 --- a/packages/cli/src/scheduling/__tests__/durable-job-provisioner.test.ts +++ b/packages/cli/src/scheduling/__tests__/durable-job-provisioner.test.ts @@ -9,6 +9,7 @@ import type { import type { DesiredJob, ScheduleDefinition } from '@n8n/scheduler'; import type { EntityManager } from '@n8n/typeorm'; import type { Tracing } from 'n8n-core'; +import { ScheduledJobMisfirePolicy } from '@n8n/constants'; import { mock } from 'vitest-mock-extended'; import { DurableJobProvisioner } from '../durable-job-provisioner'; @@ -41,6 +42,8 @@ const jobRow = (over: Partial = {}): ScheduledJob => intervalSeconds: null, fireAt: null, nextRunAt: CLOCK, + misfirePolicy: ScheduledJobMisfirePolicy.Coalesce, + misfireGraceSeconds: 90, ...over, }); @@ -73,7 +76,7 @@ describe('DurableJobProvisioner', () => { created: [], })); const globalConfig = mock({ - scheduler: { materializationWindowSeconds: 60, maxAttempts: 5 }, + scheduler: { materializationWindowSeconds: 60, maxAttempts: 5, misfireGraceSeconds: 90 }, generic: { timezone: 'UTC' }, }); provisioner = new DurableJobProvisioner( @@ -96,6 +99,7 @@ describe('DurableJobProvisioner', () => { 'schedule-trigger', { foo: 'bar' }, [desiredJob('wf:node:0')], + ScheduledJobMisfirePolicy.Coalesce, ); expect(jobs.insertMany).toHaveBeenCalledWith(manager, [ @@ -114,6 +118,8 @@ describe('DurableJobProvisioner', () => { fireAt: null, nextRunAt: CLOCK, maxAttempts: 5, + misfirePolicy: ScheduledJobMisfirePolicy.Coalesce, + misfireGraceSeconds: 90, }, ]); expect(summary.inserted).toEqual([{ id: 100, name: 'wf:node:0' }]); @@ -122,26 +128,111 @@ describe('DurableJobProvisioner', () => { it('leaves an unchanged job untouched, keeping its id', async () => { jobs.findManyByWorkflowNode.mockResolvedValue([jobRow()]); - const summary = await provisioner.provision('wf', 'node', 'schedule-trigger', {}, [ - desiredJob('wf:node:0'), - ]); + const summary = await provisioner.provision( + 'wf', + 'node', + 'schedule-trigger', + {}, + [desiredJob('wf:node:0')], + ScheduledJobMisfirePolicy.Coalesce, + ); expect(jobs.insertMany).toHaveBeenCalledWith(manager, []); expect(jobs.updateDefinition).not.toHaveBeenCalled(); expect(tasks.deletePendingByJobIds).toHaveBeenCalledWith(manager, []); + expect(jobs.updateMisfirePolicy).toHaveBeenCalledWith(manager, [], expect.anything()); + expect(summary.unchanged).toEqual([{ id: 10, name: 'wf:node:0' }]); + }); + + it('reconciles the policy of a job whose schedule is unchanged', async () => { + jobs.findManyByWorkflowNode.mockResolvedValue([jobRow()]); + + const summary = await provisioner.provision( + 'wf', + 'node', + 'poll-trigger', + {}, + [desiredJob('wf:node:0')], + ScheduledJobMisfirePolicy.Skip, + ); + + expect(jobs.updateMisfirePolicy).toHaveBeenCalledWith(manager, [10], { + misfirePolicy: ScheduledJobMisfirePolicy.Skip, + misfireGraceSeconds: 90, + }); + // The schedule is untouched, so the job keeps its queued tasks. + expect(jobs.updateDefinition).not.toHaveBeenCalled(); + expect(tasks.deletePendingByJobIds).toHaveBeenCalledWith(manager, []); expect(summary.unchanged).toEqual([{ id: 10, name: 'wf:node:0' }]); }); + it('reconciles the grace of a job whose schedule is unchanged', async () => { + jobs.findManyByWorkflowNode.mockResolvedValue([jobRow({ misfireGraceSeconds: 30 })]); + + await provisioner.provision( + 'wf', + 'node', + 'schedule-trigger', + {}, + [desiredJob('wf:node:0')], + ScheduledJobMisfirePolicy.Coalesce, + ); + + expect(jobs.updateMisfirePolicy).toHaveBeenCalledWith(manager, [10], { + misfirePolicy: ScheduledJobMisfirePolicy.Coalesce, + misfireGraceSeconds: 90, + }); + }); + + it("recomputes the deadline of a reconciled job's already-queued tasks", async () => { + // The row's grace is now current; tasks queued under the old grace + // would keep honouring it until claimed or reaped. + jobs.findManyByWorkflowNode.mockResolvedValue([jobRow({ misfireGraceSeconds: 30 })]); + + await provisioner.provision( + 'wf', + 'node', + 'schedule-trigger', + {}, + [desiredJob('wf:node:0')], + ScheduledJobMisfirePolicy.Coalesce, + ); + + expect(tasks.updateMissedAfterForJobs).toHaveBeenCalledWith(manager, [10], 90); + }); + + it("does not touch other jobs' queued tasks when nothing is outdated", async () => { + jobs.findManyByWorkflowNode.mockResolvedValue([jobRow()]); + + await provisioner.provision( + 'wf', + 'node', + 'schedule-trigger', + {}, + [desiredJob('wf:node:0')], + ScheduledJobMisfirePolicy.Coalesce, + ); + + expect(tasks.updateMissedAfterForJobs).toHaveBeenCalledWith(manager, [], 90); + }); + it('rewrites a changed job in place and withdraws its pending tasks', async () => { jobs.findManyByWorkflowNode.mockResolvedValue([jobRow()]); - const summary = await provisioner.provision('wf', 'node', 'schedule-trigger', {}, [ - desiredJob('wf:node:0', { - kind: 'cron', - cronExpression: '0 0 18 * * *', - timezone: 'UTC', - }), - ]); + const summary = await provisioner.provision( + 'wf', + 'node', + 'schedule-trigger', + {}, + [ + desiredJob('wf:node:0', { + kind: 'cron', + cronExpression: '0 0 18 * * *', + timezone: 'UTC', + }), + ], + ScheduledJobMisfirePolicy.Coalesce, + ); expect(jobs.updateDefinition).toHaveBeenCalledWith(manager, 10, { kind: 'cron', @@ -152,6 +243,8 @@ describe('DurableJobProvisioner', () => { intervalSeconds: null, fireAt: null, nextRunAt: CLOCK, + misfirePolicy: ScheduledJobMisfirePolicy.Coalesce, + misfireGraceSeconds: 90, }); expect(tasks.deletePendingByJobIds).toHaveBeenCalledWith(manager, [10]); expect(summary.redefined).toEqual([{ id: 10, name: 'wf:node:0' }]); @@ -160,9 +253,14 @@ describe('DurableJobProvisioner', () => { it('treats a job whose stored clock died as changed', async () => { jobs.findManyByWorkflowNode.mockResolvedValue([jobRow({ nextRunAt: null })]); - const summary = await provisioner.provision('wf', 'node', 'schedule-trigger', {}, [ - desiredJob('wf:node:0'), - ]); + const summary = await provisioner.provision( + 'wf', + 'node', + 'schedule-trigger', + {}, + [desiredJob('wf:node:0')], + ScheduledJobMisfirePolicy.Coalesce, + ); expect(jobs.updateDefinition).toHaveBeenCalledWith( manager, @@ -175,14 +273,49 @@ describe('DurableJobProvisioner', () => { it('deletes a job no longer desired', async () => { jobs.findManyByWorkflowNode.mockResolvedValue([jobRow({ id: 11, name: 'wf:node:1' })]); - const summary = await provisioner.provision('wf', 'node', 'schedule-trigger', {}, []); + const summary = await provisioner.provision( + 'wf', + 'node', + 'schedule-trigger', + {}, + [], + ScheduledJobMisfirePolicy.Coalesce, + ); expect(jobs.deleteManyByIds).toHaveBeenCalledWith(manager, [11]); expect(summary.removed).toEqual([{ id: 11, name: 'wf:node:1' }]); }); + it('stamps the given policy and the configured grace onto inserted rows', async () => { + jobs.insertMany.mockResolvedValue([100]); + + await provisioner.provision( + 'wf', + 'node', + 'poll-trigger', + {}, + [desiredJob('wf:node:0')], + ScheduledJobMisfirePolicy.Skip, + ); + + // Non-default values, so a hardcoded coalesce/60 would fail here. + expect(jobs.insertMany).toHaveBeenCalledWith(manager, [ + expect.objectContaining({ + misfirePolicy: ScheduledJobMisfirePolicy.Skip, + misfireGraceSeconds: 90, + }), + ]); + }); + it('runs all writes inside a single transaction', async () => { - await provisioner.provision('wf', 'node', 'schedule-trigger', {}, [desiredJob('wf:node:0')]); + await provisioner.provision( + 'wf', + 'node', + 'schedule-trigger', + {}, + [desiredJob('wf:node:0')], + ScheduledJobMisfirePolicy.Coalesce, + ); expect(dataSource.transaction).toHaveBeenCalledTimes(1); }); @@ -213,6 +346,8 @@ describe('DurableJobProvisioner', () => { taskType: 'schedule-trigger', payload: {}, maxAttempts: 1, + misfirePolicy: ScheduledJobMisfirePolicy.Coalesce, + misfireGraceSeconds: 60, }) as unknown as ScheduledJob; // The first fire (30s out) plus every fire up to the 60s window, each a task @@ -225,6 +360,8 @@ describe('DurableJobProvisioner', () => { scheduledFor: at(30), runAt: at(30), maxAttempts: 1, + // Its own instant plus the job's 60s grace. + missedAfter: at(90), }, { jobId, @@ -233,6 +370,7 @@ describe('DurableJobProvisioner', () => { scheduledFor: at(60), runAt: at(60), maxAttempts: 1, + missedAfter: at(120), }, ]; @@ -247,9 +385,14 @@ describe('DurableJobProvisioner', () => { jobs.findManyByIds.mockResolvedValue([intervalRow(100, firstRunAt)]); jobs.insertMany.mockResolvedValue([100]); - await provisioner.provision('wf', 'node', 'schedule-trigger', {}, [ - desiredJob('wf:node:0', { kind: 'interval', intervalSeconds: 30 }, firstRunAt), - ]); + await provisioner.provision( + 'wf', + 'node', + 'schedule-trigger', + {}, + [desiredJob('wf:node:0', { kind: 'interval', intervalSeconds: 30 }, firstRunAt)], + ScheduledJobMisfirePolicy.Coalesce, + ); // The whole first window is recorded now, at provision time. All fires lie // in the future, so the executor fires them on time rather than discovering @@ -283,9 +426,14 @@ describe('DurableJobProvisioner', () => { ]); jobs.findManyByIds.mockResolvedValue([intervalRow(10, firstRunAt)]); - await provisioner.provision('wf', 'node', 'schedule-trigger', {}, [ - desiredJob('wf:node:0', { kind: 'interval', intervalSeconds: 30 }, firstRunAt), - ]); + await provisioner.provision( + 'wf', + 'node', + 'schedule-trigger', + {}, + [desiredJob('wf:node:0', { kind: 'interval', intervalSeconds: 30 }, firstRunAt)], + ScheduledJobMisfirePolicy.Coalesce, + ); // The redefine's stale tasks are withdrawn before the fresh window is seeded, // so the new occurrences are the last word. @@ -309,9 +457,14 @@ describe('DurableJobProvisioner', () => { jobs.findManyByIds.mockResolvedValue([deadRow]); jobs.insertMany.mockResolvedValue([101]); - await provisioner.provision('wf', 'node', 'schedule-trigger', {}, [ - desiredJob('wf:node:0', cronSchedule, null), - ]); + await provisioner.provision( + 'wf', + 'node', + 'schedule-trigger', + {}, + [desiredJob('wf:node:0', cronSchedule, null)], + ScheduledJobMisfirePolicy.Coalesce, + ); expect(tasks.insertIgnoringDuplicates).not.toHaveBeenCalled(); expect(jobs.advanceMany).not.toHaveBeenCalled(); @@ -352,9 +505,14 @@ describe('DurableJobProvisioner', () => { columns: { fireAt: FIRE_AT }, }, ])('flattens a $name schedule onto the inserted row', async ({ name, schedule, columns }) => { - await provisioner.provision('wf', 'node', 'schedule-trigger', {}, [ - desiredJob('wf:node:0', schedule), - ]); + await provisioner.provision( + 'wf', + 'node', + 'schedule-trigger', + {}, + [desiredJob('wf:node:0', schedule)], + ScheduledJobMisfirePolicy.Coalesce, + ); expect(jobs.insertMany).toHaveBeenCalledWith(manager, [ { @@ -372,6 +530,8 @@ describe('DurableJobProvisioner', () => { fireAt: null, nextRunAt: CLOCK, maxAttempts: 5, + misfirePolicy: ScheduledJobMisfirePolicy.Coalesce, + misfireGraceSeconds: 90, ...columns, }, ]); @@ -452,9 +612,14 @@ describe('DurableJobProvisioner', () => { it.each(cases)('leaves an unchanged $name job untouched', async ({ row, same }) => { jobs.findManyByWorkflowNode.mockResolvedValue([row()]); - const summary = await provisioner.provision('wf', 'node', 'schedule-trigger', {}, [ - desiredJob('wf:node:0', same), - ]); + const summary = await provisioner.provision( + 'wf', + 'node', + 'schedule-trigger', + {}, + [desiredJob('wf:node:0', same)], + ScheduledJobMisfirePolicy.Coalesce, + ); expect(jobs.updateDefinition).not.toHaveBeenCalled(); expect(summary.unchanged).toEqual([{ id: 10, name: 'wf:node:0' }]); @@ -463,9 +628,14 @@ describe('DurableJobProvisioner', () => { it.each(cases)('rewrites a changed $name job in place', async ({ row, changed }) => { jobs.findManyByWorkflowNode.mockResolvedValue([row()]); - const summary = await provisioner.provision('wf', 'node', 'schedule-trigger', {}, [ - desiredJob('wf:node:0', changed), - ]); + const summary = await provisioner.provision( + 'wf', + 'node', + 'schedule-trigger', + {}, + [desiredJob('wf:node:0', changed)], + ScheduledJobMisfirePolicy.Coalesce, + ); expect(jobs.updateDefinition).toHaveBeenCalledWith( manager, @@ -481,7 +651,14 @@ describe('DurableJobProvisioner', () => { ]); await expect( - provisioner.provision('wf', 'node', 'schedule-trigger', {}, [desiredJob('wf:node:0')]), + provisioner.provision( + 'wf', + 'node', + 'schedule-trigger', + {}, + [desiredJob('wf:node:0')], + ScheduledJobMisfirePolicy.Coalesce, + ), ).rejects.toThrow('Unexpected scheduled job kind'); }); }); diff --git a/packages/cli/src/scheduling/__tests__/durable-scheduler.test.ts b/packages/cli/src/scheduling/__tests__/durable-scheduler.test.ts index 44a5ea28fb2..cd092ab59aa 100644 --- a/packages/cli/src/scheduling/__tests__/durable-scheduler.test.ts +++ b/packages/cli/src/scheduling/__tests__/durable-scheduler.test.ts @@ -22,7 +22,16 @@ vi.mock('@n8n/scheduler', async (importOriginal) => ({ })); describe('DurableScheduler', () => { - function makeScheduler({ enabled = true, instanceType = 'main', dbType = 'sqlite' } = {}) { + function makeScheduler({ + enabled = true, + instanceType = 'main', + dbType = 'sqlite', + materializationIntervalSeconds = 10, + minIntervalSeconds = 0, + executorIntervalSeconds = 5, + materializationWindowSeconds = 60, + misfireGraceSeconds = 60, + } = {}) { const inner = mock(); vi.mocked(createScheduler).mockReturnValue(inner); const logger = mockLogger(); @@ -44,7 +53,15 @@ describe('DurableScheduler', () => { mock({ generic: { timezone: 'UTC' }, database: { type: dbType as 'sqlite' | 'postgresdb' }, - scheduler: { enabled, executorIntervalSeconds: 5, jitterRatio: 0.1 }, + scheduler: { + enabled, + executorIntervalSeconds, + jitterRatio: 0.1, + materializationIntervalSeconds, + minIntervalSeconds, + materializationWindowSeconds, + misfireGraceSeconds, + }, }), tracing, scheduleTriggerTaskHandler, @@ -80,6 +97,74 @@ describe('DurableScheduler', () => { }); }); + describe('drain rate warning', () => { + it('warns when a pass cannot drain the fastest possible schedule before the next one is due', () => { + // maxPerJob is 1000: a schedule as fast as this instance allows (the default + // floor of one second) can outrun a 1001s materialization interval. + const { logger } = makeScheduler({ materializationIntervalSeconds: 1001 }); + + expect(logger.warn).toHaveBeenCalledWith( + expect.stringContaining('materialization interval'), + expect.objectContaining({ materializationIntervalSeconds: 1001 }), + ); + }); + + it('does not warn at the default materialization interval', () => { + const { logger } = makeScheduler(); + + expect(logger.warn).not.toHaveBeenCalledWith( + expect.stringContaining('materialization interval'), + expect.anything(), + ); + }); + + it('scales the threshold by an operator-configured minimum interval floor', () => { + // A 60s floor makes 1001s safe again: even the fastest schedule now + // produces at most one occurrence per 60s, well within maxPerJob's reach. + const { logger } = makeScheduler({ + materializationIntervalSeconds: 1001, + minIntervalSeconds: 60, + }); + + expect(logger.warn).not.toHaveBeenCalledWith( + expect.stringContaining('materialization interval'), + expect.anything(), + ); + }); + }); + + describe('misfire grace warning', () => { + it('warns when the grace is at or below the executor interval', () => { + const { logger } = makeScheduler({ misfireGraceSeconds: 5, executorIntervalSeconds: 5 }); + + expect(logger.warn).toHaveBeenCalledWith( + expect.stringContaining('executor interval'), + expect.objectContaining({ misfireGraceSeconds: 5, executorIntervalSeconds: 5 }), + ); + }); + + it('warns when the grace is below the materialization window', () => { + const { logger } = makeScheduler({ + misfireGraceSeconds: 30, + materializationWindowSeconds: 60, + }); + + expect(logger.warn).toHaveBeenCalledWith( + expect.stringContaining('materialization window'), + expect.objectContaining({ misfireGraceSeconds: 30, materializationWindowSeconds: 60 }), + ); + }); + + it('does not warn at the default grace', () => { + const { logger } = makeScheduler(); + + expect(logger.warn).not.toHaveBeenCalledWith( + expect.stringContaining('misfire grace'), + expect.anything(), + ); + }); + }); + describe('tracer', () => { // A fire span is opened from inside a timer callback armed while the claim // span was active, so it needs a fresh trace instead of parenting under a diff --git a/packages/cli/src/scheduling/durable-job-provisioner.ts b/packages/cli/src/scheduling/durable-job-provisioner.ts index 4afa53f2a5e..956ed4581b0 100644 --- a/packages/cli/src/scheduling/durable-job-provisioner.ts +++ b/packages/cli/src/scheduling/durable-job-provisioner.ts @@ -1,5 +1,6 @@ import { Logger } from '@n8n/backend-common'; import { GlobalConfig } from '@n8n/config'; +import type { ScheduledJobMisfirePolicy } from '@n8n/constants'; import type { EntityManager, NewScheduledJob, ScheduledJob } from '@n8n/db'; import { DataSource, ScheduledJobRepository, ScheduledTaskRepository } from '@n8n/db'; import { Service } from '@n8n/di'; @@ -26,6 +27,7 @@ interface ProvisionScope { nodeId: string; taskType: string; payload: Record; + misfirePolicy: ScheduledJobMisfirePolicy; } /** Identifies jobs for deletion: one node's jobs, or one workflow's jobs of a task type. */ @@ -104,8 +106,12 @@ export class DurableJobProvisioner { taskType: string, payload: Record, desired: DesiredJob[], + misfirePolicy: ScheduledJobMisfirePolicy, ): Promise { - return await this.provisioner.provision({ workflowId, nodeId, taskType, payload }, desired); + return await this.provisioner.provision( + { workflowId, nodeId, taskType, payload, misfirePolicy }, + desired, + ); } /** Delete all of a node's jobs; their queued tasks cascade away. */ @@ -142,15 +148,26 @@ export class DurableJobProvisioner { nodeId, taskType, payload, + misfirePolicy, }: ProvisionScope): RunInProvisionTransaction { + const misfireGraceSeconds = this.globalConfig.scheduler.misfireGraceSeconds; return async (work) => await this.dataSource.transaction(async (manager) => { // Jobs freshly inserted or redefined this pass; their first window is // seeded before the transaction commits (see `seedInitialOccurrences`). const seededJobIds = new Set(); + const outdatedPolicyJobIds: number[] = []; const result = await work({ findExisting: async () => { const rows = await this.jobs.findManyByWorkflowNode(manager, workflowId, nodeId); + for (const row of rows) { + if ( + row.misfirePolicy !== misfirePolicy || + row.misfireGraceSeconds !== misfireGraceSeconds + ) { + outdatedPolicyJobIds.push(row.id); + } + } return rows.map( (row): ExistingJob => ({ id: row.id, @@ -171,6 +188,8 @@ export class DurableJobProvisioner { ...scheduleColumns(job.schedule), nextRunAt: job.firstRunAt, maxAttempts: this.globalConfig.scheduler.maxAttempts, + misfirePolicy, + misfireGraceSeconds, }), ); const ids = await this.jobs.insertMany(manager, rows); @@ -181,6 +200,8 @@ export class DurableJobProvisioner { await this.jobs.updateDefinition(manager, jobId, { ...scheduleColumns(schedule), nextRunAt, + misfirePolicy, + misfireGraceSeconds, }); seededJobIds.add(jobId); }, @@ -188,6 +209,18 @@ export class DurableJobProvisioner { await this.tasks.deletePendingByJobIds(manager, jobIds), deleteJobs: async (jobIds) => await this.jobs.deleteManyByIds(manager, jobIds), }); + // Only `redefine` touches a job's misfire policy and grace, so an unchanged + // schedule needs this to pick up a policy/grace change on its own. + await this.jobs.updateMisfirePolicy(manager, outdatedPolicyJobIds, { + misfirePolicy, + misfireGraceSeconds, + }); + // Queued tasks were stamped with the previous grace; recompute their deadline. + await this.tasks.updateMissedAfterForJobs( + manager, + outdatedPolicyJobIds, + misfireGraceSeconds, + ); // After all of provisioning's own writes (including withdrawing a // redefined job's stale tasks) so the seeded occurrences are the last word. await this.seedInitialOccurrences(manager, seededJobIds); @@ -229,6 +262,8 @@ export class DurableJobProvisioner { }, recordOccurrences: async (occurrences) => await this.tasks.insertIgnoringDuplicates(manager, occurrences), + retireSuperseded: async (superseded) => + await this.tasks.updateToMissed(manager, superseded), advanceJobs: async (planned) => await this.jobs.advanceMany( manager, diff --git a/packages/cli/src/scheduling/durable-scheduler.ts b/packages/cli/src/scheduling/durable-scheduler.ts index 29f623a720b..1e02528c088 100644 --- a/packages/cli/src/scheduling/durable-scheduler.ts +++ b/packages/cli/src/scheduling/durable-scheduler.ts @@ -4,7 +4,11 @@ import { DataSource, ScheduledJobRepository, ScheduledTaskRepository } from '@n8 import { OnShutdown } from '@n8n/decorators'; import { Service } from '@n8n/di'; import type { RunInTransaction, Scheduler, TaskHandler } from '@n8n/scheduler'; -import { createScheduler, pollLookaheadSeconds } from '@n8n/scheduler'; +import { + createScheduler, + pollLookaheadSeconds, + DEFAULT_MATERIALIZER_OPTIONS, +} from '@n8n/scheduler'; import { InstanceSettings, Tracing } from 'n8n-core'; import { PrometheusSchedulerMetricsService } from '@/metrics/prometheus/scheduler-metrics.service'; @@ -84,6 +88,10 @@ export class DurableScheduler implements Scheduler { tracer, }) : undefined; + if (enabled) { + warnOnMisfireGrace(logger, config); + warnOnDrainRate(logger, config); + } this.registerTaskHandler(scheduleTriggerTaskHandler.taskType, scheduleTriggerTaskHandler); this.registerTaskHandler(pollTriggerTaskHandler.taskType, pollTriggerTaskHandler); } @@ -111,6 +119,48 @@ export class DurableScheduler implements Scheduler { } } +/** Warns when the configured grace window can't tolerate a normal restart or a short outage. */ +function warnOnMisfireGrace(logger: Logger, config: GlobalConfig['scheduler']): void { + const { misfireGraceSeconds, executorIntervalSeconds, materializationWindowSeconds } = config; + if (misfireGraceSeconds <= executorIntervalSeconds) { + logger.warn( + 'Scheduler misfire grace is at or below the executor interval; late runs may expire before they can be claimed', + { misfireGraceSeconds, executorIntervalSeconds }, + ); + } + // A short outage (shorter than the window) doesn't create new unrecorded backlog + // for the misfire policy to act on: the affected occurrences were already + // recorded ahead of the outage, so they're retired once their grace passes + // rather than coalesced. + if (misfireGraceSeconds < materializationWindowSeconds) { + logger.warn( + 'Scheduler misfire grace is below the materialization window; runs missed during a short outage are dropped rather than caught up', + { misfireGraceSeconds, materializationWindowSeconds }, + ); + } +} + +/** + * Warn when a materialization pass can't drain backlog as fast as the fastest + * schedule on the instance can produce it. Under `coalesce`, a pass that always + * hits `maxPerJob` before reaching the current occurrence never catches up: the + * job's clock advances past a backlog it can never close, so it silently stops + * running. + */ +function warnOnDrainRate(logger: Logger, config: GlobalConfig['scheduler']): void { + const { materializationIntervalSeconds, minIntervalSeconds } = config; + // The fastest a schedule can legally fire: the operator's floor if they set + // one, otherwise the fastest an interval can be at all. + const fastestIntervalSeconds = minIntervalSeconds > 0 ? minIntervalSeconds : 1; + const drainSeconds = DEFAULT_MATERIALIZER_OPTIONS.maxPerJob * fastestIntervalSeconds; + if (materializationIntervalSeconds > drainSeconds) { + logger.warn( + 'Scheduler materialization interval is long enough that a pass may never fully drain the busiest possible schedule; under the coalesce misfire policy such a schedule could stop producing catch-up runs entirely', + { materializationIntervalSeconds, fastestIntervalSeconds }, + ); + } +} + export function buildMaterializerTransaction( dataSource: DataSource, jobs: ScheduledJobRepository, @@ -124,6 +174,7 @@ export function buildMaterializerTransaction( await jobs.claimDue(manager, limit, lookaheadMs), recordOccurrences: async (occurrences) => await tasks.insertIgnoringDuplicates(manager, occurrences), + retireSuperseded: async (superseded) => await tasks.updateToMissed(manager, superseded), advanceJobs: async (planned) => { await jobs.advanceMany( manager, diff --git a/packages/cli/src/scheduling/poll-trigger-node/__tests__/poll-trigger-job-registrar.test.ts b/packages/cli/src/scheduling/poll-trigger-node/__tests__/poll-trigger-job-registrar.test.ts index beee2aea673..9b69bc4c45a 100644 --- a/packages/cli/src/scheduling/poll-trigger-node/__tests__/poll-trigger-job-registrar.test.ts +++ b/packages/cli/src/scheduling/poll-trigger-node/__tests__/poll-trigger-job-registrar.test.ts @@ -1,4 +1,5 @@ /* eslint-disable @typescript-eslint/unbound-method */ +import { ScheduledJobMisfirePolicy } from '@n8n/constants'; import { mockLogger } from '@n8n/backend-test-utils'; import type { GlobalConfig } from '@n8n/config'; import type { EntityManager } from '@n8n/db'; @@ -81,6 +82,7 @@ describe('PollTriggerJobRegistrar', () => { firstRunAt: NEXT_TEN, }, ], + ScheduledJobMisfirePolicy.Skip, ); }); @@ -205,6 +207,7 @@ describe('PollTriggerJobRegistrar', () => { POLL_TRIGGER_TASK_TYPE, { workflowId: WORKFLOW_ID, nodeId: NODE_ID }, [], + ScheduledJobMisfirePolicy.Skip, ); }); diff --git a/packages/cli/src/scheduling/poll-trigger-node/poll-trigger-job-registrar.ts b/packages/cli/src/scheduling/poll-trigger-node/poll-trigger-job-registrar.ts index 228b207a435..69217ab35c8 100644 --- a/packages/cli/src/scheduling/poll-trigger-node/poll-trigger-job-registrar.ts +++ b/packages/cli/src/scheduling/poll-trigger-node/poll-trigger-job-registrar.ts @@ -1,5 +1,6 @@ import { Logger } from '@n8n/backend-common'; import { GlobalConfig } from '@n8n/config'; +import { ScheduledJobMisfirePolicy } from '@n8n/constants'; import type { EntityManager } from '@n8n/db'; import { Service } from '@n8n/di'; import type { DesiredJob, Schedule } from '@n8n/scheduler'; @@ -52,12 +53,15 @@ export class PollTriggerJobRegistrar extends PollJobManager { const desired = this.toDesiredJobs(workflowId, node, pollTimes, seed, resolvedTimezone); const payload: PollTriggerTaskPayload = { workflowId, nodeId: node.id }; + // Skip, not coalesce: a poll fetches everything new since it last ran, so + // replaying missed polls would just repeat the same fetch. const summary = await this.jobProvisioner.provision( workflowId, node.id, POLL_TRIGGER_TASK_TYPE, { ...payload }, desired, + ScheduledJobMisfirePolicy.Skip, ); this.logger.debug('Provisioned scheduler jobs for poll trigger node', { diff --git a/packages/cli/src/scheduling/schedule-trigger-node/__tests__/schedule-trigger-job-registrar.deviations.test.ts b/packages/cli/src/scheduling/schedule-trigger-node/__tests__/schedule-trigger-job-registrar.deviations.test.ts index c32fd95cced..2240ba4ee28 100644 --- a/packages/cli/src/scheduling/schedule-trigger-node/__tests__/schedule-trigger-job-registrar.deviations.test.ts +++ b/packages/cli/src/scheduling/schedule-trigger-node/__tests__/schedule-trigger-job-registrar.deviations.test.ts @@ -258,4 +258,46 @@ describe('ScheduleTriggerJobRegistrar deviations', () => { ).toThrow(); }); }); + + describe('D6: a non-dividing minutes cadence is an interval job in both modes', () => { + // every 50 minutes (50 does not divide 60): the node compiles an + // every-minute cron gated by an elapsed-time recurrence, which + // recurring_cron can't express (no minutes unit). + const cron: Cron = { + expression: '7 * * * * *' as CronExpression, + recurrence: { activated: true, index: 0, intervalSize: 50, typeInterval: 'minutes' }, + source: { field: 'minutes', size: 50 }, + }; + + it('persists as a steady 50-minute interval in `legacy` and `new` mode', async () => { + const legacy = await collect('legacy', cron); + const fresh = await collect('new', cron); + + for (const row of [legacy, fresh]) { + expect(row.kind).toBe('interval'); + expect(row.intervalSeconds).toBe(50 * 60); + } + + // legacy seeds the first fire from the cron (the in-memory engine fires + // ungated at its next minute tick); new anchors to activation + 50 min + expect(legacy.nextRunAt).toEqual(new Date('2026-01-05T12:01:07.000Z')); + expect(fresh.nextRunAt).toEqual(new Date('2026-01-05T12:50:07.000Z')); + + // steady cadence after the seed: every gap is exactly 50 minutes + expect(gaps([legacy.nextRunAt!, ...fires(scheduleOf(legacy), legacy.nextRunAt!, 3)])).toEqual( + Array(3).fill(50 * 60 * 1000), + ); + }); + + it('a dividing minutes cadence stays a clock-aligned cron in `legacy` mode', async () => { + const dividing: Cron = { + expression: '7 */30 * * * *' as CronExpression, + recurrence: { activated: false }, + source: { field: 'minutes', size: 30 }, + }; + const legacy = await collect('legacy', dividing); + expect(legacy.kind).toBe('cron'); + expect(legacy.cronExpression).toBe('7 */30 * * * *'); + }); + }); }); diff --git a/packages/cli/src/scheduling/schedule-trigger-node/__tests__/schedule-trigger-job-registrar.test.ts b/packages/cli/src/scheduling/schedule-trigger-node/__tests__/schedule-trigger-job-registrar.test.ts index faa68b73591..0bf33675a46 100644 --- a/packages/cli/src/scheduling/schedule-trigger-node/__tests__/schedule-trigger-job-registrar.test.ts +++ b/packages/cli/src/scheduling/schedule-trigger-node/__tests__/schedule-trigger-job-registrar.test.ts @@ -1,4 +1,5 @@ /* eslint-disable @typescript-eslint/unbound-method */ +import { ScheduledJobMisfirePolicy } from '@n8n/constants'; import type { Logger } from '@n8n/backend-common'; import { mockLogger } from '@n8n/backend-test-utils'; import type { GlobalConfig, WorkflowsConfig } from '@n8n/config'; @@ -153,6 +154,7 @@ describe('ScheduleTriggerJobRegistrar', () => { firstRunAt: NEXT_NINE, }, ], + ScheduledJobMisfirePolicy.Coalesce, ); }); @@ -353,6 +355,7 @@ describe('ScheduleTriggerJobRegistrar', () => { SCHEDULE_TRIGGER_TASK_TYPE, { workflowId: WORKFLOW_ID, nodeId: NODE_ID }, [], + ScheduledJobMisfirePolicy.Coalesce, ); }); diff --git a/packages/cli/src/scheduling/schedule-trigger-node/schedule-trigger-job-registrar.ts b/packages/cli/src/scheduling/schedule-trigger-node/schedule-trigger-job-registrar.ts index 5efec8978d9..1d8c41b8c20 100644 --- a/packages/cli/src/scheduling/schedule-trigger-node/schedule-trigger-job-registrar.ts +++ b/packages/cli/src/scheduling/schedule-trigger-node/schedule-trigger-job-registrar.ts @@ -1,5 +1,6 @@ import { Logger } from '@n8n/backend-common'; import { GlobalConfig, WorkflowsConfig } from '@n8n/config'; +import { ScheduledJobMisfirePolicy } from '@n8n/constants'; import type { EntityManager } from '@n8n/db'; import { Service } from '@n8n/di'; import type { Schedule } from '@n8n/scheduler'; @@ -181,9 +182,16 @@ export class ScheduleTriggerJobRegistrar { ); collected.push({ schedule, firstRunAt: null }); } else { + // Legacy interval jobs (gated minutes) first fire at their next + // cron tick, not activation + interval — seed from the cron. + const seedSchedule: Schedule = + this.triggerNodeMode === 'legacy' && schedule.kind === 'interval' + ? { kind: 'cron', cronExpression: expression, timezone } + : schedule; + // Validates the expression/timezone and returns the first instant. const computed = computeFirstRunAt( - withResolvedTimezone(schedule, this.defaultTimezone), + withResolvedTimezone(seedSchedule, this.defaultTimezone), new Date(), ); @@ -213,7 +221,10 @@ export class ScheduleTriggerJobRegistrar { * * - A second/minute cadence becomes an `interval` job in `new` mode (a steady * elapsed-time cadence); in `legacy` mode it stays the node's plain cron so - * fires remain clock-aligned. + * fires remain clock-aligned. Exception: a minutes cadence that does not + * divide 60 (e.g. every 50 min) is an `interval` job in both modes — the + * legacy engine also runs it by elapsed time (every-minute cron gated by + * `recurrenceCheck`), and `recurring_cron` can't express a minutes unit. * - "Every N days/weeks/months" with N >= 2 becomes a `recurring_cron` job: * the cron expression names the candidate instants and the job fires on * every Nth of them. @@ -232,8 +243,9 @@ export class ScheduleTriggerJobRegistrar { recurrence: Cron['recurrence'], source: Cron['source'], ): Schedule { + const isGatedMinutes = source?.field === 'minutes' && recurrence?.activated === true; if ( - this.triggerNodeMode === 'new' && + (this.triggerNodeMode === 'new' || isGatedMinutes) && source?.size !== undefined && (source.field === 'seconds' || source.field === 'minutes') ) { @@ -247,7 +259,11 @@ export class ScheduleTriggerJobRegistrar { // engine rejects a recurrenceSize of 1 to keep one representation per // rule). N = 0/NaN never fires (see isDegenerateRecurrence) and a negative // N fires on every instant in the legacy engine; both are plain crons here. - if (recurrence?.activated && recurrence.intervalSize >= 2) { + if ( + recurrence?.activated && + recurrence.typeInterval !== 'minutes' && + recurrence.intervalSize >= 2 + ) { return { kind: 'recurring_cron', cronExpression: expression, @@ -292,6 +308,7 @@ export class ScheduleTriggerJobRegistrar { SCHEDULE_TRIGGER_TASK_TYPE, { ...payload }, desired, + ScheduledJobMisfirePolicy.Coalesce, ); this.logger.debug('Provisioned durable schedules for trigger node', { diff --git a/packages/cli/src/security-audit/security-audit.service.ts b/packages/cli/src/security-audit/security-audit.service.ts index bb983e08e10..bb9c21306fb 100644 --- a/packages/cli/src/security-audit/security-audit.service.ts +++ b/packages/cli/src/security-audit/security-audit.service.ts @@ -63,11 +63,10 @@ export class SecurityAuditService { }; // `@vite-ignore` keeps vite's dynamic-import-vars plugin from analyzing this - // runtime-only import: without a static file extension it otherwise errors, - // which surfaces fatally during v8 coverage's uncovered-file walk. + // runtime-only import, which surfaces fatally during v8 coverage's uncovered-file walk. // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment const RiskReporterModule = await import( - /* @vite-ignore */ `./risk-reporters/${toFilename[className]}` + /* @vite-ignore */ `./risk-reporters/${toFilename[className]}.js` ); // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access diff --git a/packages/cli/src/services/__tests__/ai-gateway.service.test.ts b/packages/cli/src/services/__tests__/ai-gateway.service.test.ts index 628cde68c04..c7aaae316e5 100644 --- a/packages/cli/src/services/__tests__/ai-gateway.service.test.ts +++ b/packages/cli/src/services/__tests__/ai-gateway.service.test.ts @@ -1,16 +1,16 @@ -import type { Mock, Mocked } from 'vitest'; import type { LicenseState } from '@n8n/backend-common'; import type { HttpRequestClient, OutboundHttp } from '@n8n/backend-network'; import type { GlobalConfig } from '@n8n/config'; -import { mock } from 'vitest-mock-extended'; +import type { Project, User, UserRepository } from '@n8n/db'; import type { InstanceSettings } from 'n8n-core'; import { UserError } from 'n8n-workflow'; +import type { Mock, Mocked } from 'vitest'; +import { mock } from 'vitest-mock-extended'; import { N8N_VERSION, AI_ASSISTANT_SDK_VERSION } from '@/constants'; import { FeatureNotLicensedError } from '@/errors/feature-not-licensed.error'; import type { License } from '@/license'; import { AiGatewayService } from '@/services/ai-gateway.service'; -import type { Project, User, UserRepository } from '@n8n/db'; import type { OwnershipService } from '@/services/ownership.service'; import type { UrlService } from '@/services/url.service'; @@ -869,4 +869,100 @@ describe('AiGatewayService', () => { expect(credentialsCalls).toHaveLength(1); }); }); + + describe('getCredentialTypeForProvider()', () => { + const MULTI_PROVIDER_CONFIG = { + nodes: [], + credentialTypes: ['openAiApi', 'anthropicApi', 'googlePalmApi'], + providerConfig: { + openAiApi: { gatewayPath: '/v1/gateway/openai/v1', urlField: 'url', apiKeyField: 'apiKey' }, + anthropicApi: { + gatewayPath: '/v1/gateway/anthropic', + urlField: 'url', + apiKeyField: 'apiKey', + }, + googlePalmApi: { + gatewayPath: '/v1/gateway/google', + urlField: 'host', + apiKeyField: 'apiKey', + }, + }, + }; + + it.each([ + ['openai', 'openAiApi'], + ['anthropic', 'anthropicApi'], + ['google', 'googlePalmApi'], + ])( + 'maps model provider prefix "%s" to gateway credential type "%s"', + async (provider, credType) => { + requestMock.mockResolvedValueOnce(ok(MULTI_PROVIDER_CONFIG)); + const service = makeService(); + + await expect(service.getCredentialTypeForProvider(provider)).resolves.toBe(credType); + }, + ); + + it('returns undefined for a provider the gateway does not serve', async () => { + requestMock.mockResolvedValueOnce(ok(MULTI_PROVIDER_CONFIG)); + const service = makeService(); + + await expect(service.getCredentialTypeForProvider('xai')).resolves.toBeUndefined(); + }); + + it('returns undefined (without fetching config) when n8n Connect is unlicensed', async () => { + const service = makeService({ isAiGatewayLicensed: false }); + + await expect(service.getCredentialTypeForProvider('openai')).resolves.toBeUndefined(); + expect(requestMock).not.toHaveBeenCalled(); + }); + }); + + describe('getCredentialTypeForProviderCached()', () => { + const MULTI_PROVIDER_CONFIG = { + nodes: [], + credentialTypes: ['openAiApi', 'anthropicApi'], + providerConfig: { + openAiApi: { gatewayPath: '/v1/gateway/openai/v1', urlField: 'url', apiKeyField: 'apiKey' }, + anthropicApi: { + gatewayPath: '/v1/gateway/anthropic', + urlField: 'url', + apiKeyField: 'apiKey', + }, + }, + }; + + it('returns undefined (unknown) without fetching when no config is cached', async () => { + const service = makeService(); + + expect(service.getCredentialTypeForProviderCached('openai')).toBeUndefined(); + expect(requestMock).not.toHaveBeenCalled(); + }); + + it('returns the credential type from the cached config without fetching', async () => { + requestMock.mockResolvedValueOnce(ok(MULTI_PROVIDER_CONFIG)); + const service = makeService(); + // Warm the cache once. + await service.getGatewayConfig(); + requestMock.mockClear(); + + expect(service.getCredentialTypeForProviderCached('openai')).toBe('openAiApi'); + expect(requestMock).not.toHaveBeenCalled(); + }); + + it('returns null (definitive no) when the cached config does not serve the provider', async () => { + requestMock.mockResolvedValueOnce(ok(MULTI_PROVIDER_CONFIG)); + const service = makeService(); + await service.getGatewayConfig(); + + expect(service.getCredentialTypeForProviderCached('xai')).toBeNull(); + }); + + it('returns null (definitive no) when n8n Connect is unlicensed', async () => { + const service = makeService({ isAiGatewayLicensed: false }); + + expect(service.getCredentialTypeForProviderCached('openai')).toBeNull(); + expect(requestMock).not.toHaveBeenCalled(); + }); + }); }); diff --git a/packages/cli/src/services/__tests__/frontend.service.test.ts b/packages/cli/src/services/__tests__/frontend.service.test.ts index d9c31e72df9..f7603deb9af 100644 --- a/packages/cli/src/services/__tests__/frontend.service.test.ts +++ b/packages/cli/src/services/__tests__/frontend.service.test.ts @@ -236,6 +236,25 @@ describe('FrontendService', () => { process.env = originalEnv; }); + describe('credentials overwrite reload handler', () => { + it('registers a reload handler that regenerates types', async () => { + const { service } = createMockService(); + + expect(credentialsOverwrites.registerReloadHandler).toHaveBeenCalledWith( + expect.any(Function), + ); + + // The registered handler is what CredentialsOverwrites invokes after a + // reload; it must regenerate the credential types. + const handler = vi.mocked(credentialsOverwrites.registerReloadHandler).mock.calls[0][0]; + const generateTypesSpy = vi.spyOn(service, 'generateTypes').mockResolvedValue(); + + await handler(); + + expect(generateTypesSpy).toHaveBeenCalled(); + }); + }); + describe('getSettings', () => { it('should return frontend settings', async () => { const { service } = createMockService(); diff --git a/packages/cli/src/services/__tests__/project.service.ee.test.ts b/packages/cli/src/services/__tests__/project.service.ee.test.ts index 605c1a55a71..37c4123cb85 100644 --- a/packages/cli/src/services/__tests__/project.service.ee.test.ts +++ b/packages/cli/src/services/__tests__/project.service.ee.test.ts @@ -17,6 +17,7 @@ import type { Mocked } from 'vitest'; import { mock } from 'vitest-mock-extended'; import type { ICredentialConnectionStatusProvider } from '@/credentials/credential-connection-status-provider.interface'; +import type { AgentChatAttachmentService } from '@/modules/agents/agent-chat-attachment.service'; import type { AgentExecutionService } from '@/modules/agents/agent-execution.service'; import type { AgentKnowledgeService } from '@/modules/agents/agent-knowledge.service'; import type { AgentRepository } from '@/modules/agents/repositories/agent.repository'; @@ -36,6 +37,7 @@ describe('ProjectService', () => { const agentRepository = mock(); const agentKnowledgeService = mock(); const agentExecutionService = mock(); + const agentChatAttachmentService = mock(); const ownershipService = mock(); const logger = mock(); const projectService = new ProjectService( @@ -611,6 +613,10 @@ describe('ProjectService', () => { configurable: true, get: async () => agentExecutionService, }); + Object.defineProperty(projectService, 'agentChatAttachmentService', { + configurable: true, + get: async () => agentChatAttachmentService, + }); manager.findOne.mockResolvedValueOnce(project); projectRepository.remove.mockResolvedValueOnce(project); sharedWorkflowRepository.find.mockResolvedValueOnce([]); @@ -625,6 +631,11 @@ describe('ProjectService', () => { await projectService.deleteProject(user, project.id); expect(agentRepository.findByProjectId).toHaveBeenCalledWith(project.id); + expect(agentChatAttachmentService.deleteByAgent).toHaveBeenCalledWith('agent-1'); + expect(agentChatAttachmentService.deleteByAgent).toHaveBeenCalledWith('agent-2'); + expect(agentChatAttachmentService.deleteByAgent.mock.invocationCallOrder[1]).toBeLessThan( + projectRepository.remove.mock.invocationCallOrder[0], + ); expect(agentKnowledgeService.deleteAllFilesForAgent).toHaveBeenCalledWith( project.id, 'agent-1', @@ -656,6 +667,10 @@ describe('ProjectService', () => { configurable: true, get: async () => agentExecutionService, }); + Object.defineProperty(projectService, 'agentChatAttachmentService', { + configurable: true, + get: async () => agentChatAttachmentService, + }); manager.findOne.mockResolvedValueOnce(project); projectRepository.remove.mockResolvedValueOnce(project); sharedWorkflowRepository.find.mockResolvedValueOnce([]); @@ -664,6 +679,7 @@ describe('ProjectService', () => { projectRelationRepository.findBy.mockResolvedValueOnce([]); agentRepository.findByProjectId.mockResolvedValueOnce([{ id: 'agent-1' }] as never); agentKnowledgeService.deleteAllFilesForAgent.mockRejectedValueOnce(new Error('storage down')); + agentChatAttachmentService.deleteByAgent.mockRejectedValueOnce(new Error('storage down')); await expect(projectService.deleteProject(user, project.id)).resolves.toBeUndefined(); diff --git a/packages/cli/src/services/__tests__/tag.service.test.ts b/packages/cli/src/services/__tests__/tag.service.test.ts index 2143eef64b6..fd8e27af9e2 100644 --- a/packages/cli/src/services/__tests__/tag.service.test.ts +++ b/packages/cli/src/services/__tests__/tag.service.test.ts @@ -1,6 +1,6 @@ import type { Mock } from 'vitest'; import type { TagEntity, TagRepository } from '@n8n/db'; -import { FindOperator, QueryFailedError } from '@n8n/typeorm'; +import { QueryFailedError } from '@n8n/typeorm'; import { mock } from 'vitest-mock-extended'; import type { ExternalHooks } from '@/external-hooks'; @@ -71,41 +71,19 @@ describe('TagService', () => { }); }); - describe('findByNames', () => { - test('returns existing tags by name, preserving input order', async () => { - const tags = [ - makeTag({ id: 'tag-1', name: 'production' }), - makeTag({ id: 'tag-2', name: 'critical' }), - ]; - tagRepository.find.mockResolvedValue(tags); - - const result = await tagService.findByNames(['critical', 'production', 'missing']); - - expect(result.map((t) => t.name)).toEqual(['critical', 'production']); - expect(tagRepository.save).not.toHaveBeenCalled(); - }); - - test('returns empty for whitespace-only inputs without querying', async () => { - const result = await tagService.findByNames(['', ' ']); - - expect(result).toEqual([]); - expect(tagRepository.find).not.toHaveBeenCalled(); - }); - }); - describe('findOrCreateByNames', () => { test('returns empty array for empty input', async () => { const result = await tagService.findOrCreateByNames([]); expect(result).toEqual([]); - expect(tagRepository.find).not.toHaveBeenCalled(); + expect(tagRepository.findManyByName).not.toHaveBeenCalled(); }); test('returns empty array when all inputs are whitespace', async () => { const result = await tagService.findOrCreateByNames(['', ' ']); expect(result).toEqual([]); - expect(tagRepository.find).not.toHaveBeenCalled(); + expect(tagRepository.findManyByName).not.toHaveBeenCalled(); }); test('returns existing tags without creating', async () => { @@ -113,7 +91,7 @@ describe('TagService', () => { makeTag({ id: 'tag-1', name: 'production' }), makeTag({ id: 'tag-2', name: 'critical' }), ]; - tagRepository.find.mockResolvedValue(existing); + tagRepository.findManyByName.mockResolvedValue(existing); const result = await tagService.findOrCreateByNames(['production', 'critical']); @@ -121,28 +99,35 @@ describe('TagService', () => { expect(tagRepository.save).not.toHaveBeenCalled(); }); - test('deduplicates the input case-insensitively before lookup', async () => { - tagRepository.find.mockResolvedValue([]); - const createdTag = makeTag({ id: 'tag-new', name: 'Prod' }); + test('trims names and collapses exact duplicates before lookup', async () => { + tagRepository.findManyByName.mockResolvedValue([]); + const createdTag = makeTag({ id: 'tag-new', name: 'prod' }); tagRepository.create.mockReturnValue(createdTag); tagRepository.save.mockResolvedValue(createdTag); - const result = await tagService.findOrCreateByNames(['Prod', 'prod', 'PROD']); + const result = await tagService.findOrCreateByNames([' prod ', 'prod']); - expect(tagRepository.find).toHaveBeenCalledTimes(1); - const findArg = tagRepository.find.mock.calls[0][0] as unknown as { - where: { name: FindOperator }; - }; - expect(findArg.where.name).toBeInstanceOf(FindOperator); - expect(findArg.where.name.value).toEqual(['Prod']); - // One create using the first-seen original case. + expect(tagRepository.findManyByName).toHaveBeenCalledTimes(1); + expect(tagRepository.findManyByName).toHaveBeenCalledWith(['prod']); expect(tagRepository.save).toHaveBeenCalledTimes(1); - expect(tagRepository.create).toHaveBeenCalledWith({ name: 'Prod' }); - expect(result.map((t) => t.name)).toEqual(['Prod']); + expect(result.map((t) => t.name)).toEqual(['prod']); + }); + + test('creates case-variant names as distinct tags', async () => { + tagRepository.findManyByName.mockResolvedValue([]); + tagRepository.create.mockImplementation((attrs) => makeTag(attrs as Partial)); + tagRepository.save.mockImplementation(async (tag) => tag as TagEntity); + + const result = await tagService.findOrCreateByNames(['Prod', 'prod']); + + expect(tagRepository.save).toHaveBeenCalledTimes(2); + expect(result.map((t) => t.name)).toEqual(['Prod', 'prod']); }); test('matches the DB case-sensitively (parity with REST tags API)', async () => { - tagRepository.find.mockResolvedValue([makeTag({ id: 'tag-1', name: 'Production' })]); + tagRepository.findManyByName.mockResolvedValue([ + makeTag({ id: 'tag-1', name: 'Production' }), + ]); const createdTag = makeTag({ id: 'tag-new', name: 'production' }); tagRepository.create.mockReturnValue(createdTag); tagRepository.save.mockResolvedValue(createdTag); @@ -162,7 +147,7 @@ describe('TagService', () => { }; test('returns the now-existing row when a concurrent caller wins the create race (postgres)', async () => { - tagRepository.find.mockResolvedValue([]); + tagRepository.findManyByName.mockResolvedValue([]); const racedTag = makeTag({ id: 'tag-raced', name: 'critical' }); tagRepository.create.mockReturnValue(racedTag); @@ -176,7 +161,7 @@ describe('TagService', () => { }); test('recognises sqlite unique-constraint code', async () => { - tagRepository.find.mockResolvedValue([]); + tagRepository.findManyByName.mockResolvedValue([]); const racedTag = makeTag({ id: 'tag-raced', name: 'critical' }); tagRepository.create.mockReturnValue(racedTag); @@ -189,7 +174,7 @@ describe('TagService', () => { }); test('rethrows unrelated QueryFailedError instead of masking it as a race', async () => { - tagRepository.find.mockResolvedValue([]); + tagRepository.findManyByName.mockResolvedValue([]); tagRepository.create.mockReturnValue(makeTag({ name: 'critical' })); const unrelated = new QueryFailedError('insert', undefined, new Error('connection lost')); tagRepository.save.mockRejectedValueOnce(unrelated); @@ -199,7 +184,7 @@ describe('TagService', () => { }); test('rethrows when the loser of the race cannot find the row afterwards', async () => { - tagRepository.find.mockResolvedValue([]); + tagRepository.findManyByName.mockResolvedValue([]); tagRepository.create.mockReturnValue(makeTag({ name: 'critical' })); const err = uniqueViolationError('23505'); tagRepository.save.mockRejectedValueOnce(err); @@ -209,7 +194,9 @@ describe('TagService', () => { }); test('creates missing tags and merges with existing', async () => { - tagRepository.find.mockResolvedValue([makeTag({ id: 'tag-1', name: 'production' })]); + tagRepository.findManyByName.mockResolvedValue([ + makeTag({ id: 'tag-1', name: 'production' }), + ]); const createdTag = makeTag({ id: 'tag-new', name: 'critical' }); tagRepository.create.mockReturnValue(createdTag); tagRepository.save.mockResolvedValue(createdTag); diff --git a/packages/cli/src/services/ai-gateway.service.ts b/packages/cli/src/services/ai-gateway.service.ts index 96b79df7fe9..acce965038f 100644 --- a/packages/cli/src/services/ai-gateway.service.ts +++ b/packages/cli/src/services/ai-gateway.service.ts @@ -348,6 +348,57 @@ export class AiGatewayService { } } + /** + * Resolves the n8n credential type the gateway serves for a model-provider + * prefix (e.g. `openai` → `openAiApi`), by matching the provider slug in each + * `providerConfig` entry's `gatewayPath` (the segment right after + * `/v1/gateway/`). Returns `undefined` when n8n Connect is unlicensed or the + * gateway does not serve that provider. This is the authoritative n8n Connect + * provider → credential-type mapping and support gate. + */ + async getCredentialTypeForProvider(provider: string): Promise { + if (!this.licenseState.isAiGatewayLicensed()) return undefined; + const config = await this.getGatewayConfig(); + return AiGatewayService.matchCredentialTypeForProvider(config, provider); + } + + /** + * Cache-only counterpart to {@link getCredentialTypeForProvider}, for the + * static agent validator which must never trigger a network fetch. Returns: + * - the credential type when the cached config serves the provider, + * - `null` when support is definitively unavailable (unlicensed, or the + * cached config does not serve the provider) — a real "gateway says no", + * - `undefined` when it can't be determined (no config cached yet) — a + * "could not ask", so callers must not fail closed on it. + * + * Uses the last cached config even if past its refresh TTL: a slightly stale + * answer is preferable to a network call here. + */ + getCredentialTypeForProviderCached(provider: string): string | null | undefined { + if (!this.licenseState.isAiGatewayLicensed()) return null; + if (!this.gatewayConfig) return undefined; + return AiGatewayService.matchCredentialTypeForProvider(this.gatewayConfig, provider) ?? null; + } + + /** + * Matches a model-provider prefix (e.g. `openai`) to the n8n credential type + * the gateway serves it under, by comparing the provider slug in each + * `providerConfig` entry's `gatewayPath` (the segment right after + * `/v1/gateway/`). Returns `undefined` when the gateway does not serve it. + */ + private static matchCredentialTypeForProvider( + config: AiGatewayConfigDto, + provider: string, + ): string | undefined { + const prefix = `${AiGatewayService.GATEWAY_PATH_PREFIX}/`; + for (const [credentialType, providerConfig] of Object.entries(config.providerConfig)) { + if (!providerConfig.gatewayPath.startsWith(prefix)) continue; + const slug = providerConfig.gatewayPath.slice(prefix.length).split('/')[0]; + if (slug === provider) return credentialType; + } + return undefined; + } + /** * Headers required by the AI Gateway credentials endpoint (`HeadersMetadataDto`). */ diff --git a/packages/cli/src/services/cache/__tests__/cache.service.test.ts b/packages/cli/src/services/cache/__tests__/cache.service.test.ts index 475667532c9..31119daaf55 100644 --- a/packages/cli/src/services/cache/__tests__/cache.service.test.ts +++ b/packages/cli/src/services/cache/__tests__/cache.service.test.ts @@ -1,7 +1,7 @@ import { GlobalConfig } from '@n8n/config'; import { Container } from '@n8n/di'; +import { sleep } from '@n8n/utils/sleep'; import random from 'lodash/random'; -import { sleep } from 'n8n-workflow'; import config from '@/config'; import { CacheService } from '@/services/cache/cache.service'; diff --git a/packages/cli/src/services/frontend.service.ts b/packages/cli/src/services/frontend.service.ts index f78a574ad13..b1347ad8873 100644 --- a/packages/cli/src/services/frontend.service.ts +++ b/packages/cli/src/services/frontend.service.ts @@ -143,6 +143,7 @@ export class FrontendService { private readonly workflowReviewPolicyService: WorkflowReviewPolicyService, ) { loadNodesAndCredentials.addPostProcessor(async () => await this.generateTypes()); + credentialsOverwrites.registerReloadHandler(async () => await this.generateTypes()); void this.generateTypes(); // @TODO: Move to community-packages module if (Container.get(CommunityPackagesConfig).enabled) { diff --git a/packages/cli/src/services/ownership-transfer/ownership-transfer.manifest.json b/packages/cli/src/services/ownership-transfer/ownership-transfer.manifest.json index b090fa81387..d1fef090970 100644 --- a/packages/cli/src/services/ownership-transfer/ownership-transfer.manifest.json +++ b/packages/cli/src/services/ownership-transfer/ownership-transfer.manifest.json @@ -49,6 +49,11 @@ "path": "packages/cli/src/modules/agents/entities/agent-execution-thread.entity.ts", "reason": "Dropped via FK cascade together with its Agent." }, + { + "name": "AgentChatAttachment", + "path": "packages/cli/src/modules/agents/entities/agent-chat-attachment.entity.ts", + "reason": "Conversation attachments; dropped via FK cascade together with their Agent/project, like the threads and messages they belong to." + }, { "name": "InstanceAiThread", "path": "packages/cli/src/modules/instance-ai/entities/instance-ai-thread.entity.ts", diff --git a/packages/cli/src/services/project.service.ee.ts b/packages/cli/src/services/project.service.ee.ts index fecd1499a34..5a5485bd961 100644 --- a/packages/cli/src/services/project.service.ee.ts +++ b/packages/cli/src/services/project.service.ee.ts @@ -130,6 +130,12 @@ export class ProjectService { ); } + private get agentChatAttachmentService() { + return import('@/modules/agents/agent-chat-attachment.service.js').then( + ({ AgentChatAttachmentService }) => Container.get(AgentChatAttachmentService), + ); + } + private get connectionStatusProxy() { return import('@/credentials/credential-connection-status-proxy.js').then( ({ CredentialConnectionStatusProxy }) => Container.get(CredentialConnectionStatusProxy), @@ -239,11 +245,13 @@ export class ProjectService { // 8. delete agent knowledge files before project removal cascades delete agent_files rows. if (this.moduleRegistry.isActive('agents')) { - const [agentRepository, agentKnowledgeService, agentExecutionService] = await Promise.all([ - this.agentRepository, - this.agentKnowledgeService, - this.agentExecutionService, - ]); + const [agentRepository, agentKnowledgeService, agentExecutionService, agentChatAttachments] = + await Promise.all([ + this.agentRepository, + this.agentKnowledgeService, + this.agentExecutionService, + this.agentChatAttachmentService, + ]); const agents = await agentRepository.findByProjectId(project.id); for (const agent of agents) { try { @@ -256,6 +264,18 @@ export class ProjectService { }); } + // Delete chat attachment bytes before the project removal cascades away + // the agent_chat_attachments rows that hold the binaryDataId handles. + try { + await agentChatAttachments.deleteByAgent(agent.id); + } catch (error) { + this.logger.warn('Failed to delete chat attachments on project delete', { + agentId: agent.id, + projectId: project.id, + error: error instanceof Error ? error.message : error, + }); + } + await agentKnowledgeService.destroySandbox(project.id, agent.id); await agentExecutionService.deleteExecutionLogsForAgent(agent.id); } diff --git a/packages/cli/src/services/pruning/workflow-history-compaction.service.ts b/packages/cli/src/services/pruning/workflow-history-compaction.service.ts index 95a212c3a38..6de4b49cb56 100644 --- a/packages/cli/src/services/pruning/workflow-history-compaction.service.ts +++ b/packages/cli/src/services/pruning/workflow-history-compaction.service.ts @@ -6,7 +6,8 @@ import { OnLeaderStepdown, OnLeaderTakeover, OnShutdown } from '@n8n/decorators' import { Service } from '@n8n/di'; import { InstanceSettings } from 'n8n-core'; import { ensureError } from '@n8n/utils/errors/ensure-error'; -import { DiffMetaData, DiffRule, RULES, SKIP_RULES, sleep } from 'n8n-workflow'; +import { sleep } from '@n8n/utils/sleep'; +import { DiffMetaData, DiffRule, RULES, SKIP_RULES } from 'n8n-workflow'; import { strict } from 'node:assert'; import { EventService } from '@/events/event.service'; diff --git a/packages/cli/src/services/tag.service.ts b/packages/cli/src/services/tag.service.ts index 1ba8c5f5dbc..b7377b1d2e5 100644 --- a/packages/cli/src/services/tag.service.ts +++ b/packages/cli/src/services/tag.service.ts @@ -1,7 +1,7 @@ import type { TagEntity, ITagWithCountDb } from '@n8n/db'; import { TagRepository } from '@n8n/db'; import { Service } from '@n8n/di'; -import { In, QueryFailedError } from '@n8n/typeorm'; +import { QueryFailedError } from '@n8n/typeorm'; import { ExternalHooks } from '@/external-hooks'; import { validateEntity } from '@/generic-helpers'; @@ -10,24 +10,6 @@ type GetAllResult = T extends { withUsageCount: true } ? ITagWithCountDb[] : type Action = 'Create' | 'Update'; -// Trim and dedupe input names case-insensitively, keeping the first-seen case. -// Inputs are matched against the DB exactly, so this only collapses obvious -// duplicates like ['Prod','prod','PROD'] within a single batch — it does NOT -// change the case-sensitive contract that the REST tag API exposes. -function dedupeNamesPreservingCase(names: string[]): string[] { - const seen = new Set(); - const result: string[] = []; - for (const raw of names) { - const trimmed = raw.trim(); - if (trimmed.length === 0) continue; - const key = trimmed.toLowerCase(); - if (seen.has(key)) continue; - seen.add(key); - result.push(trimmed); - } - return result; -} - // n8n supports postgres (SQLSTATE 23505) and sqlite (SQLITE_CONSTRAINT_UNIQUE, // or the older SQLITE_CONSTRAINT with a "UNIQUE constraint" message). function isUniqueConstraintViolation(error: unknown): error is QueryFailedError { @@ -120,6 +102,16 @@ export class TagService { }); } + async getByIds(ids: string[]): Promise { + if (ids.length === 0) return []; + return await this.tagRepository.findMany(ids); + } + + async getByNames(names: string[]): Promise { + if (names.length === 0) return []; + return await this.tagRepository.findManyByName(names); + } + /** * Paginated tags with non-archived usage counts plus the total count, both * via DB-level queries. Runs the data query and `count` in parallel. @@ -148,34 +140,18 @@ export class TagService { } /** - * Look up tags by name; never creates. Input is deduped case-insensitively - * but matched against the DB exactly (REST tag API contract). - */ - async findByNames(names: string[]): Promise { - const uniqueNames = dedupeNamesPreservingCase(names); - if (uniqueNames.length === 0) return []; - - const existing = await this.tagRepository.find({ where: { name: In(uniqueNames) } }); - const existingByName = new Map(existing.map((t) => [t.name, t])); - - const result: TagEntity[] = []; - for (const name of uniqueNames) { - const hit = existingByName.get(name); - if (hit) result.push(hit); - } - return result; - } - - /** - * Resolve names to tag entities, creating any missing. Input is deduped - * case-insensitively (first-seen case wins) but matched against the DB - * exactly. Race-safe against concurrent same-name creates. + * Resolve names to tag entities, creating any missing. Names are trimmed + * and exact duplicates collapsed, matching how tags are stored; case-variant + * names resolve as distinct tags. Race-safe against concurrent same-name + * creates. */ async findOrCreateByNames(names: string[]): Promise { - const uniqueNames = dedupeNamesPreservingCase(names); + const uniqueNames = [ + ...new Set(names.map((name) => name.trim()).filter((name) => name.length > 0)), + ]; if (uniqueNames.length === 0) return []; - const existing = await this.tagRepository.find({ where: { name: In(uniqueNames) } }); + const existing = await this.tagRepository.findManyByName(uniqueNames); const existingByName = new Map(existing.map((t) => [t.name, t])); const result: TagEntity[] = []; diff --git a/packages/cli/src/task-runners/task-broker/auth/__tests__/task-broker-auth.service.test.ts b/packages/cli/src/task-runners/task-broker/auth/__tests__/task-broker-auth.service.test.ts index 32602ffc4bf..b9df4725f52 100644 --- a/packages/cli/src/task-runners/task-broker/auth/__tests__/task-broker-auth.service.test.ts +++ b/packages/cli/src/task-runners/task-broker/auth/__tests__/task-broker-auth.service.test.ts @@ -1,7 +1,7 @@ import { mockInstance } from '@n8n/backend-test-utils'; import { GlobalConfig } from '@n8n/config'; import { Time } from '@n8n/constants'; -import { sleep } from 'n8n-workflow'; +import { sleep } from '@n8n/utils/sleep'; import config from '@/config'; import { CacheService } from '@/services/cache/cache.service'; diff --git a/packages/cli/src/task-runners/task-broker/task-broker-ws-server.ts b/packages/cli/src/task-runners/task-broker/task-broker-ws-server.ts index ec7484efff9..012919486da 100644 --- a/packages/cli/src/task-runners/task-broker/task-broker-ws-server.ts +++ b/packages/cli/src/task-runners/task-broker/task-broker-ws-server.ts @@ -3,7 +3,8 @@ import { GlobalConfig, TaskRunnersConfig } from '@n8n/config'; import { Time } from '@n8n/constants'; import { Service } from '@n8n/di'; import type { BrokerMessage, RunnerMessage } from '@n8n/task-runner'; -import { jsonStringify, sleep, UserError } from 'n8n-workflow'; +import { sleep } from '@n8n/utils/sleep'; +import { jsonStringify, UserError } from 'n8n-workflow'; import type WebSocket from 'ws'; import { WsStatusCodes } from '@/constants'; diff --git a/packages/cli/src/task-runners/task-runner-module.ts b/packages/cli/src/task-runners/task-runner-module.ts index ffb829f5631..b4185b8a988 100644 --- a/packages/cli/src/task-runners/task-runner-module.ts +++ b/packages/cli/src/task-runners/task-runner-module.ts @@ -3,8 +3,8 @@ import { TaskRunnersConfig } from '@n8n/config'; import { OnShutdown } from '@n8n/decorators'; import { Container, Service } from '@n8n/di'; import type { ServiceIdentifier } from '@n8n/di'; +import { sleep } from '@n8n/utils/sleep'; import { ErrorReporter } from 'n8n-core'; -import { sleep } from 'n8n-workflow'; import * as a from 'node:assert/strict'; import { EventService } from '@/events/event.service'; diff --git a/packages/cli/src/utils/ai-service-retry.ts b/packages/cli/src/utils/ai-service-retry.ts index 50c1901849b..895f8b10cda 100644 --- a/packages/cli/src/utils/ai-service-retry.ts +++ b/packages/cli/src/utils/ai-service-retry.ts @@ -1,3 +1,4 @@ +import { sleep } from '@n8n/utils/sleep'; import { OperationalError } from 'n8n-workflow'; const AI_SERVICE_MAX_ATTEMPTS = 3; @@ -22,10 +23,6 @@ type RetryOptions = { class AiServiceCallTimeoutError extends Error {} -async function sleep(ms: number): Promise { - await new Promise((resolve) => setTimeout(resolve, ms)); -} - // The SDK's fetch calls carry no timeout, so a stalled connection would hang the caller // forever and never reach the retry loop; the race unblocks the caller (the underlying // request cannot be aborted without SDK AbortSignal support). diff --git a/packages/cli/src/wait-tracker.ts b/packages/cli/src/wait-tracker.ts index 82962f38498..5ce05d0be46 100644 --- a/packages/cli/src/wait-tracker.ts +++ b/packages/cli/src/wait-tracker.ts @@ -4,8 +4,8 @@ import { OnLeaderStepdown, OnLeaderTakeover } from '@n8n/decorators'; import { Service } from '@n8n/di'; import { InstanceSettings } from 'n8n-core'; import { ensureError } from '@n8n/utils/errors/ensure-error'; +import { sleep } from '@n8n/utils/sleep'; import { - sleep, UnexpectedError, UserError, type IRun, @@ -152,7 +152,10 @@ export class WaitTracker { // Start the execution again try { - await this.workflowRunner.run(data, false, false, executionId); + await this.workflowRunner.run(data, false, false, { + executionId, + expectedStatus: 'waiting', + }); } catch (error) { if (error instanceof ExecutionAlreadyResumingError) { // This execution is already being resumed by another child execution diff --git a/packages/cli/src/webhooks/__tests__/webhook-helpers.test.ts b/packages/cli/src/webhooks/__tests__/webhook-helpers.test.ts index 4f8797e4e05..a138c3afc92 100644 --- a/packages/cli/src/webhooks/__tests__/webhook-helpers.test.ts +++ b/packages/cli/src/webhooks/__tests__/webhook-helpers.test.ts @@ -290,6 +290,28 @@ describe('setupResponseNodePromise', () => { expect(responseCallback).toHaveBeenCalledWith(null, { noWebhookResponse: true }); }); + test('should apply the status code to binary data responses', async () => { + binaryDataService.getAsStream.mockResolvedValue(mock()); + + setupResponseNodePromise( + responsePromise, + res, + responseCallback, + workflowStartNode, + executionId, + workflow, + ); + + responsePromise.resolve({ + body: { binaryData: { id: 'binary-123' } }, + headers: {}, + statusCode: 201, + }); + await new Promise(process.nextTick); + + expect(res.status).toHaveBeenCalledWith(201); + }); + test('should not set sandbox CSP header on binary stream responses when sandboxing is disabled', async () => { vi.mocked(isWebhookHtmlSandboxingDisabled).mockReturnValue(true); const mockStream = mock(); @@ -338,6 +360,26 @@ describe('setupResponseNodePromise', () => { expect(responseCallback).toHaveBeenCalledWith(null, { noWebhookResponse: true }); }); + test('should apply the status code to buffer responses', async () => { + setupResponseNodePromise( + responsePromise, + res, + responseCallback, + workflowStartNode, + executionId, + workflow, + ); + + responsePromise.resolve({ + body: Buffer.from('created'), + headers: {}, + statusCode: 201, + }); + await new Promise(process.nextTick); + + expect(res.status).toHaveBeenCalledWith(201); + }); + test('should not set sandbox CSP header on buffer responses when sandboxing is disabled', async () => { vi.mocked(isWebhookHtmlSandboxingDisabled).mockReturnValue(true); @@ -759,6 +801,111 @@ describe('prepareExecutionData', () => { ]); }); + test('should replace the seeded stack (not merge) for a Webhook node using n8nOAuth2 auth, preserving runtimeData', () => { + const identityWebhookNode = mock({ + name: 'Webhook', + type: 'n8n-nodes-base.webhook', + parameters: { authentication: 'n8nOAuth2' }, + }); + + // After the node's webhook() call the identity is already established: the + // seeder's placeholder item has been consumed by the hook (leaving an empty + // item) and the resolved credentials live on executionData.runtimeData. + const existingNodeExecutionStack: IExecuteData[] = [ + { + node: mock({ name: 'ExistingNode' }), + data: { + main: [[{ json: {} }]], + }, + source: null, + }, + ]; + + const existingRunExecutionData: IRunExecutionData = { + version: 1, + startData: {}, + resultData: { runData: {} }, + executionData: { + contextData: {}, + metadata: {}, + nodeExecutionStack: existingNodeExecutionStack, + waitingExecution: {}, + waitingExecutionSource: {}, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + runtimeData: { version: 1, credentials: { source: 'n8n-oauth' } } as any, + }, + } as IRunExecutionData; + + const { runExecutionData } = prepareExecutionData( + 'trigger', + identityWebhookNode, + webhookResultData, + existingRunExecutionData, + ); + + expect(runExecutionData.executionData?.nodeExecutionStack).toHaveLength(1); + // The seeded placeholder is discarded; only the webhook's real output remains. + expect(runExecutionData.executionData?.nodeExecutionStack[0].data.main).toEqual([ + [{ json: { data: 'test' } }], + ]); + // The established identity (runtimeData) is preserved across the replace. + expect(runExecutionData.executionData?.runtimeData).toEqual({ + version: 1, + credentials: { source: 'n8n-oauth' }, + }); + }); + + test('should not leak the seeded placeholder into output slot 0 for a multi-method n8nOAuth2 webhook', () => { + const identityWebhookNode = mock({ + name: 'Webhook', + type: 'n8n-nodes-base.webhook', + parameters: { authentication: 'n8nOAuth2' }, + }); + + // Seeded placeholder sits in output slot 0. + const existingNodeExecutionStack: IExecuteData[] = [ + { + node: mock({ name: 'ExistingNode' }), + data: { + main: [[{ json: {} }]], + }, + source: null, + }, + ]; + + const existingRunExecutionData: IRunExecutionData = { + version: 1, + startData: {}, + resultData: { runData: {} }, + executionData: { + contextData: {}, + metadata: {}, + nodeExecutionStack: existingNodeExecutionStack, + waitingExecution: {}, + waitingExecutionSource: {}, + }, + } as IRunExecutionData; + + // A multi-method webhook routes the request to a non-first output slot; e.g. + // a POST on a ['GET','POST'] node puts the item in slot 1, slot 0 stays empty. + const multiMethodResult: IWebhookResponseData = { + workflowData: [[], [{ json: { method: 'POST' } }]], + }; + + const { runExecutionData } = prepareExecutionData( + 'trigger', + identityWebhookNode, + multiMethodResult, + existingRunExecutionData, + ); + + // Slot 0 must be empty (no phantom placeholder firing the GET branch). + expect(runExecutionData.executionData?.nodeExecutionStack[0].data.main).toEqual([ + [], + [{ json: { method: 'POST' } }], + ]); + }); + test('should merge existing data with new data for MICROSOFT_AGENT365_TRIGGER_NODE_TYPE', () => { const microsoftAgentNode = mock({ name: 'Microsoft Agent 365', diff --git a/packages/cli/src/webhooks/webhook-helpers.ts b/packages/cli/src/webhooks/webhook-helpers.ts index 3b0331402fa..213a057fc84 100644 --- a/packages/cli/src/webhooks/webhook-helpers.ts +++ b/packages/cli/src/webhooks/webhook-helpers.ts @@ -52,6 +52,7 @@ import { tryToParseUrl, UnexpectedError, WAIT_NODE_TYPE, + WEBHOOK_NODE_TYPE, WorkflowConfigurationError, } from 'n8n-workflow'; import { finished } from 'stream/promises'; @@ -322,6 +323,9 @@ export function setupResponseNodePromise( .then(async (response: IN8nHttpFullResponse) => { const binaryData = (response.body as IDataObject)?.binaryData as IBinaryData; if (binaryData?.id) { + if (response.statusCode) { + res.status(response.statusCode); + } WebhookResponseHeaders.fromObject(response.headers).applyToResponse(res); applySandboxCSP(res); const stream = await Container.get(BinaryDataService).getAsStream(binaryData.id); @@ -329,6 +333,9 @@ export function setupResponseNodePromise( await finished(stream); responseCallback(null, { noWebhookResponse: true }); } else if (Buffer.isBuffer(response.body)) { + if (response.statusCode) { + res.status(response.statusCode); + } WebhookResponseHeaders.fromObject(response.headers).applyToResponse(res); applySandboxCSP(res); res.end(response.body); @@ -360,6 +367,53 @@ export function setupResponseNodePromise( }); } +/** + * Predicate (not an action): checks whether the start node will establish a + * triggering-user identity from within its `webhook()` method (via + * `context.establishTriggerIdentity`). Such nodes need their `runExecutionData` + * created before the webhook runs, and the webhook output merged into the seeded + * execution stack afterwards. + * + * The Webhook node does this only when its opt-in "n8n User Auth (OAuth2)" mode + * (`n8nOAuth2`) is selected; the MCP / chat / Agent365 triggers always do. + */ +function shouldEstablishTriggerIdentity(workflowStartNode: INode): boolean { + return ( + workflowStartNode.type === WEBHOOK_NODE_TYPE && + workflowStartNode.parameters?.authentication === 'n8nOAuth2' + ); +} + +/** + * Reconciles a pre-seeded execution stack (identity/context trigger flows) with the + * webhook node's real output. No-op unless the start node seeded execution data. + * + * - MCP / chat / Agent365 (single-output triggers): index-merge the webhook output + * into the seeded item so seeded input data is preserved. + * - n8n Identity webhook: the identity was already established from the seeded + * placeholder during the node's `webhook()` call (credentials now live on + * `executionData.runtimeData`, a sibling that survives the reassignment). Replace + * the seeded stack with the real output instead of index-merging — a naive merge + * keeps the empty placeholder in output slot 0 and would spuriously fire that + * branch on multi-method webhooks. + */ +function reconcileSeededExecutionStack( + workflowStartNode: INode, + runExecutionData: IRunExecutionData | undefined, + nodeExecutionStack: IExecuteData[], +): void { + const executionData = runExecutionData?.executionData; + if (!executionData?.nodeExecutionStack) return; + + if ( + [MICROSOFT_AGENT365_TRIGGER_NODE_TYPE, CHAT_TRIGGER_NODE_TYPE].includes(workflowStartNode.type) + ) { + merge(executionData.nodeExecutionStack, nodeExecutionStack); + } else if (shouldEstablishTriggerIdentity(workflowStartNode)) { + executionData.nodeExecutionStack = nodeExecutionStack; + } +} + export function prepareExecutionData( executionMode: WorkflowExecuteMode, workflowStartNode: INode, @@ -382,14 +436,7 @@ export function prepareExecutionData( }, ]; - if ( - [MICROSOFT_AGENT365_TRIGGER_NODE_TYPE, CHAT_TRIGGER_NODE_TYPE].includes( - workflowStartNode.type, - ) && - runExecutionData?.executionData?.nodeExecutionStack - ) { - merge(runExecutionData.executionData.nodeExecutionStack, nodeExecutionStack); - } + reconcileSeededExecutionStack(workflowStartNode, runExecutionData, nodeExecutionStack); runExecutionData ??= createRunExecutionData({ executionData: { @@ -619,7 +666,8 @@ export async function executeWebhook( MCP_TRIGGER_NODE_TYPE, MICROSOFT_AGENT365_TRIGGER_NODE_TYPE, CHAT_TRIGGER_NODE_TYPE, - ].includes(workflowStartNode.type) + ].includes(workflowStartNode.type) || + shouldEstablishTriggerIdentity(workflowStartNode) ) { // Initialize the data of the webhook node const nodeExecutionStack: IExecuteData[] = []; @@ -860,7 +908,8 @@ export async function executeWebhook( runData, true, !didSendResponse && !shouldDeferOnReceivedResponse, - executionId, + // An execution id here means we are resuming one that is waiting on this webhook + executionId ? { executionId, expectedStatus: 'waiting' } : undefined, responsePromise as IDeferredPromise | undefined, ); diff --git a/packages/cli/src/workflow-helpers.ts b/packages/cli/src/workflow-helpers.ts index e527ed729ad..ec85336456d 100644 --- a/packages/cli/src/workflow-helpers.ts +++ b/packages/cli/src/workflow-helpers.ts @@ -22,6 +22,7 @@ import { type IWorkflowBase, type IWorkflowSettings, type RelatedExecution, + type WorkflowGroupViolation, type WorkflowStructureIssue, } from 'n8n-workflow'; import { v4 as uuid } from 'uuid'; @@ -196,6 +197,48 @@ export function validateWorkflowNodeGroups( } } +/** + * Non-fatal counterpart of `validateWorkflowNodeGroups`: drops every offending + * group instead of throwing, returning the violations of those it dropped. + * Mutates `nodeGroups`. Groups are cosmetic, so the MCP builder tools drop them + * to keep the rest of the change; the save path still throws. + * + * `shouldDrop` filters which violating groups are removed, letting a caller + * drop the groups it can blame first and re-check the rest afterwards. + */ +export function dropInvalidNodeGroups( + workflow: Pick & { + connections?: IWorkflowBase['connections']; + }, + getNodeType: GetNodeTypeForGrouping | null, + shouldDrop: (violation: WorkflowGroupViolation) => boolean = () => true, +): WorkflowGroupViolation[] { + if (!workflow.nodeGroups?.length) { + return []; + } + + const result = validateWorkflowGroups({ + nodes: workflow.nodes, + connectionsBySourceNode: workflow.connections, + nodeGroups: workflow.nodeGroups, + getNodeType, + }); + + if (result.valid) { + return []; + } + + const dropped = result.violations.filter(shouldDrop); + if (dropped.length === 0) { + return []; + } + + const droppedGroupIds = new Set(dropped.map((violation) => violation.groupId)); + workflow.nodeGroups = workflow.nodeGroups.filter((group) => !droppedGroupIds.has(group.id)); + + return dropped; +} + /** * Normalizes group descriptions on import, mutating in place. * diff --git a/packages/cli/src/workflow-runner.ts b/packages/cli/src/workflow-runner.ts index ab31b54b939..ac5f1a0b43a 100644 --- a/packages/cli/src/workflow-runner.ts +++ b/packages/cli/src/workflow-runner.ts @@ -45,6 +45,7 @@ import { ExecutionPersistence } from '@/executions/execution-persistence'; import { FailedRunFactory } from '@/executions/failed-run-factory'; import { CredentialsPermissionChecker } from '@/executions/pre-execution-checks'; import { ExternalHooks } from '@/external-hooks'; +import type { ResumableExecution } from '@/interfaces'; import { ManualExecutionService } from '@/manual-execution.service'; import { NodeTypes } from '@/node-types'; import type { ScalingService } from '@/scaling/scaling.service'; @@ -186,7 +187,7 @@ export class WorkflowRunner { data: IWorkflowExecutionDataProcess, loadStaticData?: boolean, realtime?: boolean, - restartExecutionId?: string, + existingExecution?: ResumableExecution, responsePromise?: IDeferredPromise, ): Promise { // Establish the execution context before persisting to the DB. @@ -234,7 +235,7 @@ export class WorkflowRunner { } // Register a new execution - const executionId = await this.activeExecutions.add(data, restartExecutionId); + const executionId = await this.activeExecutions.add(data, existingExecution); if (establishContextError) { await this.failExecution(data, executionId, establishContextError, responsePromise); @@ -281,10 +282,10 @@ export class WorkflowRunner { data, loadStaticData, realtime, - restartExecutionId, + existingExecution?.executionId, ); } else { - await this.runMainProcess(executionId, data, loadStaticData, restartExecutionId); + await this.runMainProcess(executionId, data, loadStaticData, existingExecution?.executionId); } // only run these when not in queue mode or when the execution is manual, diff --git a/packages/cli/src/workflows/__tests__/workflow-publish-guard-proxy.service.test.ts b/packages/cli/src/workflows/__tests__/workflow-publish-guard-proxy.service.test.ts new file mode 100644 index 00000000000..2040922767e --- /dev/null +++ b/packages/cli/src/workflows/__tests__/workflow-publish-guard-proxy.service.test.ts @@ -0,0 +1,25 @@ +import { mock } from 'vitest-mock-extended'; + +import { + type WorkflowPublishGuard, + WorkflowPublishGuardProxy, +} from '../workflow-publish-guard-proxy.service'; + +describe('WorkflowPublishGuardProxy', () => { + test('allows publication when no review provider is registered', async () => { + const proxy = new WorkflowPublishGuardProxy(); + + await expect(proxy.assertCanPublish('workflow-1')).resolves.toBeUndefined(); + }); + + test('asks the registered review provider before publication', async () => { + const proxy = new WorkflowPublishGuardProxy(); + const provider = mock(); + proxy.registerProvider(provider); + + await proxy.assertCanPublish('workflow-1'); + + expect(provider.assertCanPublish).toHaveBeenCalledOnce(); + expect(provider.assertCanPublish).toHaveBeenCalledWith('workflow-1'); + }); +}); diff --git a/packages/cli/src/workflows/__tests__/workflow.service.test.ts b/packages/cli/src/workflows/__tests__/workflow.service.test.ts index 837d924a17b..e5fecb0faae 100644 --- a/packages/cli/src/workflows/__tests__/workflow.service.test.ts +++ b/packages/cli/src/workflows/__tests__/workflow.service.test.ts @@ -1,4 +1,3 @@ -import type { Mock } from 'vitest'; import type { LicenseState } from '@n8n/backend-common'; import type { GlobalConfig, WorkflowsConfig } from '@n8n/config'; import type { @@ -13,32 +12,35 @@ import type { import { WorkflowEntity, WorkflowHistory } from '@n8n/db'; import type { Scope } from '@n8n/permissions'; import type { EntityManager } from '@n8n/typeorm'; -import type { MockProxy } from 'vitest-mock-extended'; -import { mock } from 'vitest-mock-extended'; import type { IConnections, INode } from 'n8n-workflow'; +import type { Mock } from 'vitest'; +import { mock } from 'vitest-mock-extended'; +import type { MockProxy } from 'vitest-mock-extended'; import type { ActiveWorkflowManager } from '@/active-workflow-manager'; -import type { ExecutionPersistence } from '@/executions/execution-persistence'; -import type { PollTriggerJobRegistrar } from '@/scheduling/poll-trigger-node/poll-trigger-job-registrar'; -import type { ScheduleTriggerJobRegistrar } from '@/scheduling/schedule-trigger-node/schedule-trigger-job-registrar'; import { BadRequestError } from '@/errors/response-errors/bad-request.error'; import { ConflictError } from '@/errors/response-errors/conflict.error'; import { UnprocessableRequestError } from '@/errors/response-errors/unprocessable.error'; import { WorkflowActivationBadRequestError } from '@/errors/response-errors/workflow-activation-bad-request.error'; import { WorkflowDeactivationBadRequestError } from '@/errors/response-errors/workflow-deactivation-bad-request.error'; +import { WorkflowPublishBlockedError } from '@/errors/response-errors/workflow-publish-blocked.error'; import type { EventService } from '@/events/event.service'; +import type { ExecutionPersistence } from '@/executions/execution-persistence'; import type { ExternalHooks, WorkflowLifecycleHookActor } from '@/external-hooks'; import type { RedactionEnforcementService } from '@/modules/redaction/redaction-enforcement.service'; import { userHasScopes } from '@/permissions.ee/check-access'; +import type { PollTriggerJobRegistrar } from '@/scheduling/poll-trigger-node/poll-trigger-job-registrar'; +import type { ScheduleTriggerJobRegistrar } from '@/scheduling/schedule-trigger-node/schedule-trigger-job-registrar'; import type { OwnershipService } from '@/services/ownership.service'; import type { RoleService } from '@/services/role.service'; import type { WebhookService } from '@/webhooks/webhook.service'; +import * as WorkflowHelpers from '@/workflow-helpers'; import type { WorkflowHookContextService } from '@/workflow-hook-context.service'; import type { WorkflowFinderService } from '@/workflows/workflow-finder.service'; import type { WorkflowHistoryService } from '@/workflows/workflow-history/workflow-history.service'; -import { WorkflowService } from '@/workflows/workflow.service'; +import type { WorkflowPublishGuardProxy } from '@/workflows/workflow-publish-guard-proxy.service'; import type { WorkflowValidationService } from '@/workflows/workflow-validation.service'; -import * as WorkflowHelpers from '@/workflow-helpers'; +import { WorkflowService } from '@/workflows/workflow.service'; vi.mock('@/permissions.ee/check-access'); vi.mock('@/workflow-helpers'); @@ -102,6 +104,7 @@ describe('WorkflowService', () => { mock(), // pollTriggerJobRegistrar mock(), // workflowPublishedVersionRepository mock(), // workflowHookContextService + mock(), // workflowPublishGuard ); }); @@ -370,6 +373,7 @@ describe('WorkflowService', () => { mock(), // pollTriggerJobRegistrar mock(), // workflowPublishedVersionRepository workflowHookContextServiceMock, // workflowHookContextService + mock(), // workflowPublishGuard ); vi.clearAllMocks(); @@ -1017,6 +1021,7 @@ describe('WorkflowService', () => { let scheduleTriggerJobRegistrarMock: MockProxy; let workflowHookContextServiceMock: MockProxy; let pollTriggerJobRegistrarMock: MockProxy; + let workflowPublishGuardMock: MockProxy; const WORKFLOW_ID = 'workflow-1'; const PREVIOUS_VERSION_ID = 'v1'; @@ -1070,6 +1075,7 @@ describe('WorkflowService', () => { scheduleTriggerJobRegistrarMock = mock(); workflowHookContextServiceMock = mock(); pollTriggerJobRegistrarMock = mock(); + workflowPublishGuardMock = mock(); workflowRepositoryMock.create.mockImplementation( (data) => Object.assign(new WorkflowEntity(), data) as WorkflowEntity, @@ -1107,6 +1113,7 @@ describe('WorkflowService', () => { pollTriggerJobRegistrarMock, // pollTriggerJobRegistrar mock(), // workflowPublishedVersionRepository workflowHookContextServiceMock, // workflowHookContextService + workflowPublishGuardMock, // workflowPublishGuard ); // Bypass validation internals @@ -1122,6 +1129,112 @@ describe('WorkflowService', () => { vi.spyOn(internals, '_validateSubWorkflowReferences').mockResolvedValue(undefined); }); + test.each([ + ['current publication path', false], + ['outbox publication path', true], + ] as const)( + 'does not start the %s when an open review blocks a first publication', + async (_path, useWorkflowPublicationService) => { + globalConfigMock.workflows.useWorkflowPublicationService = useWorkflowPublicationService; + const workflow = makeWorkflowEntity({ active: false, activeVersionId: null }); + workflowFinderServiceMock.findWorkflowForUser.mockResolvedValue(workflow); + workflowHistoryServiceMock.getVersion.mockResolvedValue(makeVersionToActivate()); + workflowPublishGuardMock.assertCanPublish.mockRejectedValue( + new WorkflowPublishBlockedError({ + reason: 'review_pending', + workflowReviewRequestId: 'review-1', + }), + ); + + await expect( + workflowService.activateWorkflow(mock(), WORKFLOW_ID, { + versionId: TARGET_VERSION_ID, + }), + ).rejects.toBeInstanceOf(WorkflowPublishBlockedError); + + expect(workflowRepositoryMock.update).not.toHaveBeenCalled(); + expect(activeWorkflowManagerMock.add).not.toHaveBeenCalled(); + expect(outboxRepositoryMock.enqueue).not.toHaveBeenCalled(); + expect(workflowPublishHistoryRepositoryMock.addRecord).not.toHaveBeenCalled(); + }, + ); + + test('keeps the previous version running when an open review blocks a replacement', async () => { + const workflow = makeWorkflowEntity({ activeVersionId: PREVIOUS_VERSION_ID }); + workflowFinderServiceMock.findWorkflowForUser.mockResolvedValue(workflow); + workflowHistoryServiceMock.getVersion.mockResolvedValue(makeVersionToActivate()); + workflowPublishGuardMock.assertCanPublish.mockRejectedValue( + new WorkflowPublishBlockedError({ + reason: 'changes_requested', + workflowReviewRequestId: 'review-1', + }), + ); + + await expect( + workflowService.activateWorkflow(mock(), WORKFLOW_ID, { + versionId: TARGET_VERSION_ID, + }), + ).rejects.toBeInstanceOf(WorkflowPublishBlockedError); + + expect(workflow.activeVersionId).toBe(PREVIOUS_VERSION_ID); + expect(activeWorkflowManagerMock.remove).not.toHaveBeenCalled(); + expect(workflowRepositoryMock.update).not.toHaveBeenCalled(); + }); + + test('checks for an open review after the activation hook has accepted the version', async () => { + const workflow = makeWorkflowEntity({ active: false, activeVersionId: null }); + workflowFinderServiceMock.findWorkflowForUser.mockResolvedValue(workflow); + workflowHistoryServiceMock.getVersion.mockResolvedValue(makeVersionToActivate()); + workflowPublishGuardMock.assertCanPublish.mockRejectedValue( + new WorkflowPublishBlockedError({ + reason: 'review_pending', + workflowReviewRequestId: 'review-1', + }), + ); + + await expect( + workflowService.activateWorkflow(mock(), WORKFLOW_ID), + ).rejects.toBeInstanceOf(WorkflowPublishBlockedError); + + expect(externalHooksMock.run).toHaveBeenCalledWith('workflow.activate', expect.any(Array)); + expect(externalHooksMock.run.mock.invocationCallOrder[0]).toBeLessThan( + workflowPublishGuardMock.assertCanPublish.mock.invocationCallOrder[0], + ); + }); + + test('does not check workflow reviews when re-applying the published version', async () => { + const workflow = makeWorkflowEntity({ activeVersionId: PREVIOUS_VERSION_ID }); + workflowFinderServiceMock.findWorkflowForUser.mockResolvedValue(workflow); + workflowHistoryServiceMock.getVersion.mockResolvedValue(makeActiveVersion()); + workflowRepositoryMock.findOne.mockResolvedValue(workflow); + externalHooksMock.run.mockResolvedValue(undefined); + vi.spyOn( + workflowService as unknown as { _addToActiveWorkflowManager: () => Promise }, + '_addToActiveWorkflowManager', + ).mockResolvedValue(undefined); + workflowPublishGuardMock.assertCanPublish.mockRejectedValue( + new WorkflowPublishBlockedError({ + reason: 'review_pending', + workflowReviewRequestId: 'review-1', + }), + ); + + await workflowService.activateWorkflow(mock(), WORKFLOW_ID, { + versionId: PREVIOUS_VERSION_ID, + }); + + expect(workflowPublishGuardMock.assertCanPublish).not.toHaveBeenCalled(); + }); + + test('does not check workflow reviews while unpublishing', async () => { + const workflow = makeWorkflowEntity({ activeVersionId: PREVIOUS_VERSION_ID }); + workflowFinderServiceMock.findWorkflowForUser.mockResolvedValue(workflow); + + await workflowService.deactivateWorkflow(mock(), WORKFLOW_ID); + + expect(workflowPublishGuardMock.assertCanPublish).not.toHaveBeenCalled(); + }); + test('republish blocked by hook leaves previous active version untouched', async () => { const workflow = makeWorkflowEntity({ activeVersionId: PREVIOUS_VERSION_ID }); const versionToActivate = makeVersionToActivate(); @@ -1186,6 +1299,7 @@ describe('WorkflowService', () => { versionId: TARGET_VERSION_ID, }); + expect(workflowPublishGuardMock.assertCanPublish).toHaveBeenCalledWith(WORKFLOW_ID); expect(externalHooksMock.run).toHaveBeenCalledTimes(1); const [hookName, hookArgs] = externalHooksMock.run.mock.calls[0] as [ string, @@ -1433,6 +1547,124 @@ describe('WorkflowService', () => { }); }); + describe('deactivateWorkflowAsSystem()', () => { + let workflowService: WorkflowService; + let workflowRepositoryMock: MockProxy; + let workflowPublishHistoryRepositoryMock: MockProxy; + let globalConfigMock: MockProxy; + let activeWorkflowManagerMock: MockProxy; + let externalHooksMock: MockProxy; + + const WORKFLOW_ID = 'workflow-1'; + const ACTIVE_VERSION_ID = 'v1'; + + function makeWorkflowEntity(overrides: Partial = {}): WorkflowEntity { + const workflow = new WorkflowEntity(); + workflow.id = WORKFLOW_ID; + workflow.name = 'My workflow'; + workflow.versionId = 'v2'; + workflow.activeVersionId = ACTIVE_VERSION_ID; + workflow.active = true; + workflow.nodes = []; + workflow.connections = {}; + workflow.updatedAt = new Date(); + Object.assign(workflow, overrides); + return workflow; + } + + beforeEach(() => { + workflowRepositoryMock = mock(); + workflowPublishHistoryRepositoryMock = mock(); + globalConfigMock = mock({ + workflows: mock({ useWorkflowPublicationService: false }), + }); + activeWorkflowManagerMock = mock(); + externalHooksMock = mock(); + + workflowRepositoryMock.create.mockImplementation( + (data) => Object.assign(new WorkflowEntity(), data) as WorkflowEntity, + ); + + workflowService = new WorkflowService( + mock(), // logger + mock(), // sharedWorkflowRepository + workflowRepositoryMock, // workflowRepository + mock(), // workflowTagMappingRepository + mock(), // ownershipService + mock(), // tagService + mock(), // workflowHistoryService + externalHooksMock, // externalHooks + activeWorkflowManagerMock, // activeWorkflowManager + mock(), // roleService + mock(), // projectService + mock(), // executionPersistence + mock(), // eventService + globalConfigMock, // globalConfig + mock(), // folderRepository + mock(), // workflowFinderService + workflowPublishHistoryRepositoryMock, // workflowPublishHistoryRepository + mock(), // outboxRepository + mock(), // workflowValidationService + mock(), // nodeTypes + mock(), // webhookService + mock(), // licenseState + mock(), // projectRepository + mock(), // redactionEnforcementService + mock(), // workflowPublicationNotifier + mock(), // scheduleTriggerJobRegistrar + mock(), // pollTriggerJobRegistrar + mock(), // workflowPublishedVersionRepository + mock(), // workflowHookContextService + mock(), // workflowPublishGuard + ); + }); + + test('proceeds with deactivation when the workflow.deactivate hook fails', async () => { + workflowRepositoryMock.findOne.mockResolvedValue(makeWorkflowEntity()); + externalHooksMock.run.mockRejectedValue(new Error('Code freeze in effect')); + + await expect( + workflowService.deactivateWorkflowAsSystem(WORKFLOW_ID), + ).resolves.toBeUndefined(); + + expect(activeWorkflowManagerMock.remove).toHaveBeenCalledWith(WORKFLOW_ID); + expect(workflowRepositoryMock.update).toHaveBeenCalledWith( + WORKFLOW_ID, + expect.objectContaining({ active: false, activeVersionId: null }), + ); + expect(workflowPublishHistoryRepositoryMock.addRecord).toHaveBeenCalledWith({ + workflowId: WORKFLOW_ID, + versionId: ACTIVE_VERSION_ID, + event: 'deactivated', + userId: null, + }); + }); + + test('does nothing when the workflow does not exist', async () => { + workflowRepositoryMock.findOne.mockResolvedValue(null); + + await workflowService.deactivateWorkflowAsSystem(WORKFLOW_ID); + + expect(externalHooksMock.run).not.toHaveBeenCalled(); + expect(activeWorkflowManagerMock.remove).not.toHaveBeenCalled(); + expect(workflowRepositoryMock.update).not.toHaveBeenCalled(); + expect(workflowPublishHistoryRepositoryMock.addRecord).not.toHaveBeenCalled(); + }); + + test('does nothing when the workflow is not published', async () => { + workflowRepositoryMock.findOne.mockResolvedValue( + makeWorkflowEntity({ active: false, activeVersionId: null }), + ); + + await workflowService.deactivateWorkflowAsSystem(WORKFLOW_ID); + + expect(externalHooksMock.run).not.toHaveBeenCalled(); + expect(activeWorkflowManagerMock.remove).not.toHaveBeenCalled(); + expect(workflowRepositoryMock.update).not.toHaveBeenCalled(); + expect(workflowPublishHistoryRepositoryMock.addRecord).not.toHaveBeenCalled(); + }); + }); + describe('delete()', () => { let workflowService: WorkflowService; let workflowFinderServiceMock: MockProxy; @@ -1498,6 +1730,7 @@ describe('WorkflowService', () => { mock(), // pollTriggerJobRegistrar workflowPublishedVersionRepositoryMock, // workflowPublishedVersionRepository mock(), // workflowHookContextService + mock(), // workflowPublishGuard ); }); @@ -1642,6 +1875,7 @@ describe('WorkflowService', () => { mock(), // pollTriggerJobRegistrar mock(), // workflowPublishedVersionRepository mock(), // workflowHookContextService + mock(), // workflowPublishGuard ); }); @@ -1761,6 +1995,7 @@ describe('WorkflowService', () => { mock(), // pollTriggerJobRegistrar mock(), // workflowPublishedVersionRepository mock(), // workflowHookContextService + mock(), // workflowPublishGuard ); }); diff --git a/packages/cli/src/workflows/publication/__tests__/published-workflow-trigger-deactivator.test.ts b/packages/cli/src/workflows/publication/__tests__/published-workflow-trigger-deactivator.test.ts index b421f4cb7f0..15cd9365e17 100644 --- a/packages/cli/src/workflows/publication/__tests__/published-workflow-trigger-deactivator.test.ts +++ b/packages/cli/src/workflows/publication/__tests__/published-workflow-trigger-deactivator.test.ts @@ -1,12 +1,18 @@ import type { Logger } from '@n8n/backend-common'; import type { WorkflowsConfig } from '@n8n/config'; import { mock } from 'vitest-mock-extended'; -import type { ActiveWorkflowTriggers, ErrorReporter } from 'n8n-core'; +import type { ActiveWorkflowTriggers, ErrorReporter, InstanceSettings } from 'n8n-core'; +import type { EventService } from '@/events/event.service'; import { PublishedWorkflowTriggerDeactivator } from '@/workflows/publication/published-workflow-trigger-deactivator'; import type { WorkflowPublicationLifecycleLock } from '@/workflows/publication/workflow-publication-lifecycle-lock'; import type { WorkflowPublicationOutboxConsumer } from '@/workflows/publication/workflow-publication-outbox-consumer'; +const RECONCILE_INTERVAL_SECONDS = 10; + +/** Consecutive skipped sweeps after which a held lock is reported as stuck. */ +const STUCK_LOCK_WARN_AFTER_SWEEPS = 3; + describe('PublishedWorkflowTriggerDeactivator', () => { const logger = mock(); logger.scoped.mockReturnValue(logger); @@ -15,9 +21,16 @@ describe('PublishedWorkflowTriggerDeactivator', () => { const lifecycleLock = mock(); const activeWorkflowTriggers = mock(); const outboxConsumer = mock(); + const eventService = mock(); + + // A follower by default; tests flip `isLeader` to simulate (mid-run) promotion. + let instanceSettings: InstanceSettings; function createDeactivator(useWorkflowPublicationService = true) { - const workflowsConfig = mock({ useWorkflowPublicationService }); + const workflowsConfig = mock({ + useWorkflowPublicationService, + publicationReconcileIntervalSeconds: RECONCILE_INTERVAL_SECONDS, + }); return new PublishedWorkflowTriggerDeactivator( logger, workflowsConfig, @@ -25,96 +38,350 @@ describe('PublishedWorkflowTriggerDeactivator', () => { lifecycleLock, activeWorkflowTriggers, outboxConsumer, + instanceSettings, + eventService, ); } beforeEach(() => { vi.clearAllMocks(); + instanceSettings = { isLeader: false } as InstanceSettings; lifecycleLock.isLocked.mockReturnValue(false); - lifecycleLock.getLockedWorkflowIds.mockReturnValue([]); - // By default the lock runs the teardown immediately without timing out. - lifecycleLock.runExclusiveOrTimeout.mockImplementation(async (_workflowId, fn) => { - await fn(); - return { timedOut: false }; - }); - }); - - test('does nothing when the publication service is disabled', async () => { - await createDeactivator(false).deactivateAllNonWebhookTriggers(); - - expect(activeWorkflowTriggers.getNonWebhookTriggerWorkflowIds).not.toHaveBeenCalled(); - expect(activeWorkflowTriggers.remove).not.toHaveBeenCalled(); - expect(lifecycleLock.runExclusiveOrTimeout).not.toHaveBeenCalled(); - expect(outboxConsumer.stopPolling).not.toHaveBeenCalled(); - }); - - test('deactivates each workflow under its lock', async () => { - activeWorkflowTriggers.getNonWebhookTriggerWorkflowIds.mockReturnValue(['wf-1', 'wf-2']); - - await createDeactivator(true).deactivateAllNonWebhookTriggers(); - - expect(outboxConsumer.stopPolling).toHaveBeenCalledTimes(1); - expect(lifecycleLock.runExclusiveOrTimeout).toHaveBeenCalledWith( - 'wf-1', - expect.any(Function), - 30_000, + lifecycleLock.runExclusive.mockImplementation( + async (_workflowId, fn) => await (fn as () => Promise)(), ); - expect(lifecycleLock.runExclusiveOrTimeout).toHaveBeenCalledWith( - 'wf-2', - expect.any(Function), - 30_000, - ); - expect(activeWorkflowTriggers.remove).toHaveBeenCalledWith('wf-1'); - expect(activeWorkflowTriggers.remove).toHaveBeenCalledWith('wf-2'); - expect(errorReporter.error).not.toHaveBeenCalled(); - }); - - test('deactivates a workflow that only exists in the lifecycle lock', async () => { activeWorkflowTriggers.getNonWebhookTriggerWorkflowIds.mockReturnValue([]); - lifecycleLock.getLockedWorkflowIds.mockReturnValue(['wf-in-flight']); - lifecycleLock.isLocked.mockImplementation((workflowId) => workflowId === 'wf-in-flight'); - - await createDeactivator(true).deactivateAllNonWebhookTriggers(); - - expect(lifecycleLock.runExclusiveOrTimeout).toHaveBeenCalledWith( - 'wf-in-flight', - expect.any(Function), - 30_000, - ); - expect(activeWorkflowTriggers.remove).toHaveBeenCalledWith('wf-in-flight'); + activeWorkflowTriggers.remove.mockResolvedValue(true); }); - test('defers a locked workflow and deactivates it last', async () => { - activeWorkflowTriggers.getNonWebhookTriggerWorkflowIds.mockReturnValue([ - 'wf-free', - 'wf-locked', - ]); - lifecycleLock.isLocked.mockImplementation((workflowId) => workflowId === 'wf-locked'); + describe('stepdown/shutdown teardown', () => { + test('does nothing when the publication service is disabled', async () => { + await createDeactivator(false).deactivateAllNonWebhookTriggers(); - const removed: string[] = []; - activeWorkflowTriggers.remove.mockImplementation(async (workflowId) => { - removed.push(workflowId); - return true; + expect(activeWorkflowTriggers.getNonWebhookTriggerWorkflowIds).not.toHaveBeenCalled(); + expect(activeWorkflowTriggers.remove).not.toHaveBeenCalled(); + expect(outboxConsumer.stopPolling).not.toHaveBeenCalled(); }); - await createDeactivator(true).deactivateAllNonWebhookTriggers(); + test('deactivates each workflow under its lock, silently', async () => { + activeWorkflowTriggers.getNonWebhookTriggerWorkflowIds.mockReturnValue(['wf-1', 'wf-2']); - // Both go through the lock, but the locked workflow is handled last. - expect(removed).toEqual(['wf-free', 'wf-locked']); - expect(lifecycleLock.runExclusiveOrTimeout).toHaveBeenCalledTimes(2); + await createDeactivator().deactivateAllNonWebhookTriggers(); + + expect(outboxConsumer.stopPolling).toHaveBeenCalledTimes(1); + expect(lifecycleLock.runExclusive).toHaveBeenCalledWith('wf-1', expect.any(Function)); + expect(lifecycleLock.runExclusive).toHaveBeenCalledWith('wf-2', expect.any(Function)); + expect(activeWorkflowTriggers.remove).toHaveBeenCalledWith('wf-1'); + expect(activeWorkflowTriggers.remove).toHaveBeenCalledWith('wf-2'); + expect(errorReporter.error).not.toHaveBeenCalled(); + // Stepdown removals are routine lifecycle, not incidents. + expect(logger.warn).not.toHaveBeenCalled(); + expect(eventService.emit).not.toHaveBeenCalled(); + }); + + test('skips a locked workflow instead of waiting or forcing', async () => { + // No wait, no lockless teardown: the follower sweep retries it every + // interval once the lock is released. + activeWorkflowTriggers.getNonWebhookTriggerWorkflowIds.mockReturnValue([ + 'wf-free', + 'wf-locked', + ]); + lifecycleLock.isLocked.mockImplementation((workflowId) => workflowId === 'wf-locked'); + + await createDeactivator().deactivateAllNonWebhookTriggers(); + + expect(activeWorkflowTriggers.remove).toHaveBeenCalledWith('wf-free'); + expect(activeWorkflowTriggers.remove).not.toHaveBeenCalledWith('wf-locked'); + expect(lifecycleLock.runExclusive).not.toHaveBeenCalledWith( + 'wf-locked', + expect.any(Function), + ); + }); + + test('keeps deactivating the remaining workflows when one teardown throws', async () => { + activeWorkflowTriggers.getNonWebhookTriggerWorkflowIds.mockReturnValue(['wf-bad', 'wf-good']); + activeWorkflowTriggers.remove.mockImplementation(async (workflowId) => { + if (workflowId === 'wf-bad') throw new Error('closeFunction failed'); + return true; + }); + + // A single workflow's teardown failure must not abort demotion cleanup + // for every workflow after it. + await createDeactivator().deactivateAllNonWebhookTriggers(); + + expect(activeWorkflowTriggers.remove).toHaveBeenCalledWith('wf-good'); + expect(errorReporter.error).toHaveBeenCalledWith(expect.any(Error), { + shouldBeLogged: true, + }); + }); + + test('tears down at shutdown even while still leader', async () => { + // A leader shutting down gracefully never loses leadership first — + // the leader guard must not disable the shutdown teardown. + instanceSettings = { isLeader: true } as InstanceSettings; + activeWorkflowTriggers.getNonWebhookTriggerWorkflowIds.mockReturnValue(['wf-1']); + + const deactivator = createDeactivator(); + deactivator.shutdown(); + await deactivator.deactivateAllNonWebhookTriggers(); + + expect(activeWorkflowTriggers.remove).toHaveBeenCalledWith('wf-1'); + }); + + test('aborts the remaining teardown when promoted back to leader mid-stepdown', async () => { + activeWorkflowTriggers.getNonWebhookTriggerWorkflowIds.mockReturnValue(['wf-1', 'wf-2']); + lifecycleLock.runExclusive.mockImplementation(async (_workflowId, fn) => { + // Promotion lands while the teardown is waiting on the first lock — + // deactivating now would race the takeover's own re-activation. + instanceSettings = Object.assign(instanceSettings, { isLeader: true }); + return await (fn as () => Promise)(); + }); + + await createDeactivator().deactivateAllNonWebhookTriggers(); + + expect(activeWorkflowTriggers.remove).not.toHaveBeenCalled(); + }); }); - test('reports when a locked workflow times out but still tears it down', async () => { - activeWorkflowTriggers.getNonWebhookTriggerWorkflowIds.mockReturnValue(['wf-stuck']); - lifecycleLock.isLocked.mockReturnValue(true); - lifecycleLock.runExclusiveOrTimeout.mockImplementation(async (_workflowId, fn) => { - await fn(); - return { timedOut: true }; + describe('ghost-trigger janitor (follower)', () => { + describe('sweepGhostTriggers', () => { + test('removes registered workflows under their lock and returns the count', async () => { + activeWorkflowTriggers.getNonWebhookTriggerWorkflowIds.mockReturnValue(['wf-1', 'wf-2']); + + const removed = await createDeactivator().sweepGhostTriggers(); + + expect(removed).toBe(2); + expect(lifecycleLock.runExclusive).toHaveBeenCalledWith('wf-1', expect.any(Function)); + expect(lifecycleLock.runExclusive).toHaveBeenCalledWith('wf-2', expect.any(Function)); + expect(activeWorkflowTriggers.remove).toHaveBeenCalledWith('wf-1'); + expect(activeWorkflowTriggers.remove).toHaveBeenCalledWith('wf-2'); + // A ghost on a follower is evidence of a zombie writer — loud, not debug. + expect(logger.warn).toHaveBeenCalled(); + expect(eventService.emit).toHaveBeenCalledWith('workflow-publication-ghost-trigger-sweep', { + removedCount: 2, + }); + }); + + test('does nothing on the leader', async () => { + instanceSettings = { isLeader: true } as InstanceSettings; + activeWorkflowTriggers.getNonWebhookTriggerWorkflowIds.mockReturnValue(['wf-1']); + + const removed = await createDeactivator().sweepGhostTriggers(); + + expect(removed).toBe(0); + expect(activeWorkflowTriggers.remove).not.toHaveBeenCalled(); + }); + + test('skips a workflow whose lifecycle lock is held and sweeps the rest', async () => { + activeWorkflowTriggers.getNonWebhookTriggerWorkflowIds.mockReturnValue([ + 'wf-busy', + 'wf-free', + ]); + lifecycleLock.isLocked.mockImplementation((workflowId) => workflowId === 'wf-busy'); + + const removed = await createDeactivator().sweepGhostTriggers(); + + expect(removed).toBe(1); + expect(activeWorkflowTriggers.remove).toHaveBeenCalledWith('wf-free'); + expect(activeWorkflowTriggers.remove).not.toHaveBeenCalledWith('wf-busy'); + expect(lifecycleLock.runExclusive).not.toHaveBeenCalledWith( + 'wf-busy', + expect.any(Function), + ); + }); + + test('aborts inside the lock when promoted to leader mid-sweep', async () => { + activeWorkflowTriggers.getNonWebhookTriggerWorkflowIds.mockReturnValue(['wf-1']); + lifecycleLock.runExclusive.mockImplementation(async (_workflowId, fn) => { + // Promotion lands while this sweep is waiting on the lock. + instanceSettings = Object.assign(instanceSettings, { isLeader: true }); + return await (fn as () => Promise)(); + }); + + const removed = await createDeactivator().sweepGhostTriggers(); + + expect(removed).toBe(0); + expect(activeWorkflowTriggers.remove).not.toHaveBeenCalled(); + }); + + test('resolves and reports when a sweep listener throws', async () => { + activeWorkflowTriggers.getNonWebhookTriggerWorkflowIds.mockReturnValue(['wf-1', 'wf-2']); + eventService.emit.mockImplementation(() => { + throw new Error('listener blew up'); + }); + + // Must resolve, not reject: the interval callback has nobody awaiting it. + const removed = await createDeactivator().sweepGhostTriggers(); + + expect(removed).toBe(2); + expect(errorReporter.error).toHaveBeenCalledWith(expect.any(Error), { + shouldBeLogged: true, + }); + }); + + test('reports a failing teardown and keeps sweeping the remaining workflows', async () => { + activeWorkflowTriggers.getNonWebhookTriggerWorkflowIds.mockReturnValue([ + 'wf-stuck', + 'wf-fine', + ]); + activeWorkflowTriggers.remove.mockImplementation(async (workflowId) => { + if (workflowId === 'wf-stuck') throw new Error('closeFunction failed'); + return true; + }); + + // Must resolve, not reject: the interval callback has nobody awaiting it, + // so a rejection here would crash the process. + const removed = await createDeactivator().sweepGhostTriggers(); + + expect(removed).toBe(1); + expect(activeWorkflowTriggers.remove).toHaveBeenCalledWith('wf-fine'); + expect(errorReporter.error).toHaveBeenCalledWith(expect.any(Error), { + shouldBeLogged: true, + }); + }); + + test('does not count workflows that were no longer active and stays quiet when nothing was removed', async () => { + activeWorkflowTriggers.getNonWebhookTriggerWorkflowIds.mockReturnValue(['wf-gone']); + activeWorkflowTriggers.remove.mockResolvedValue(false); + + const removed = await createDeactivator().sweepGhostTriggers(); + + expect(removed).toBe(0); + expect(logger.warn).not.toHaveBeenCalled(); + expect(eventService.emit).not.toHaveBeenCalled(); + }); }); - await createDeactivator(true).deactivateAllNonWebhookTriggers(); + describe('stuck-lock reporting', () => { + test('stays quiet about a briefly held lock', async () => { + // A lock held for one pass is normal (an apply in flight) — not an incident. + activeWorkflowTriggers.getNonWebhookTriggerWorkflowIds.mockReturnValue(['wf-busy']); + lifecycleLock.isLocked.mockReturnValue(true); - expect(activeWorkflowTriggers.remove).toHaveBeenCalledWith('wf-stuck'); - expect(errorReporter.error).toHaveBeenCalledWith(expect.any(Error), { shouldBeLogged: true }); + await createDeactivator().sweepGhostTriggers(); + + expect(logger.warn).not.toHaveBeenCalled(); + }); + + test('warns about a workflow whose lock stays held across consecutive sweeps', async () => { + activeWorkflowTriggers.getNonWebhookTriggerWorkflowIds.mockReturnValue(['wf-wedged']); + lifecycleLock.isLocked.mockReturnValue(true); + const deactivator = createDeactivator(); + + for (let pass = 0; pass < STUCK_LOCK_WARN_AFTER_SWEEPS; pass++) { + await deactivator.sweepGhostTriggers(); + } + + // Nothing was removed (always skipped), so the only warn is the stuck lock. + expect(activeWorkflowTriggers.remove).not.toHaveBeenCalled(); + expect(logger.warn).toHaveBeenCalled(); + }); + + test('an unlocked pass resets the streak even when the teardown itself fails', async () => { + // The warn claims the LOCK was held for N consecutive passes — a pass + // that found the lock free must break the streak, whatever remove() did. + activeWorkflowTriggers.getNonWebhookTriggerWorkflowIds.mockReturnValue(['wf-1']); + const deactivator = createDeactivator(); + + lifecycleLock.isLocked.mockReturnValue(true); + await deactivator.sweepGhostTriggers(); + await deactivator.sweepGhostTriggers(); + + // Lock free, but the close itself fails. + lifecycleLock.isLocked.mockReturnValue(false); + activeWorkflowTriggers.remove.mockRejectedValueOnce(new Error('closeFunction failed')); + await deactivator.sweepGhostTriggers(); + + lifecycleLock.isLocked.mockReturnValue(true); + await deactivator.sweepGhostTriggers(); + + expect(logger.warn).not.toHaveBeenCalled(); + }); + + test('resets the stuck-lock tracking once the workflow is swept', async () => { + activeWorkflowTriggers.getNonWebhookTriggerWorkflowIds.mockReturnValue(['wf-flaky']); + const deactivator = createDeactivator(); + + // Two skipped passes, then the lock releases and the sweep handles it. + lifecycleLock.isLocked.mockReturnValue(true); + await deactivator.sweepGhostTriggers(); + await deactivator.sweepGhostTriggers(); + lifecycleLock.isLocked.mockReturnValue(false); + await deactivator.sweepGhostTriggers(); + + // Locked again: the previous skip streak must not carry over. + logger.warn.mockClear(); + lifecycleLock.isLocked.mockReturnValue(true); + await deactivator.sweepGhostTriggers(); + await deactivator.sweepGhostTriggers(); + + expect(logger.warn).not.toHaveBeenCalled(); + }); + }); + + describe('interval lifecycle', () => { + beforeEach(() => { + vi.useFakeTimers(); + }); + + afterEach(() => { + vi.useRealTimers(); + }); + + const advanceOneInterval = async () => + await vi.advanceTimersByTimeAsync(RECONCILE_INTERVAL_SECONDS * 1000); + + test('sweeps every reconcile interval once started', async () => { + const deactivator = createDeactivator(); + deactivator.startGhostTriggerJanitor(); + + await advanceOneInterval(); + await advanceOneInterval(); + + expect(activeWorkflowTriggers.getNonWebhookTriggerWorkflowIds).toHaveBeenCalledTimes(2); + + deactivator.stopGhostTriggerJanitor(); + }); + + test('does not start when the publication service is disabled', async () => { + createDeactivator(false).startGhostTriggerJanitor(); + + await advanceOneInterval(); + + expect(activeWorkflowTriggers.getNonWebhookTriggerWorkflowIds).not.toHaveBeenCalled(); + }); + + test('starting twice schedules only one interval', async () => { + const deactivator = createDeactivator(); + deactivator.startGhostTriggerJanitor(); + deactivator.startGhostTriggerJanitor(); + + await advanceOneInterval(); + + expect(activeWorkflowTriggers.getNonWebhookTriggerWorkflowIds).toHaveBeenCalledTimes(1); + + deactivator.stopGhostTriggerJanitor(); + }); + + test('stops sweeping once stopped', async () => { + const deactivator = createDeactivator(); + deactivator.startGhostTriggerJanitor(); + deactivator.stopGhostTriggerJanitor(); + + await advanceOneInterval(); + + expect(activeWorkflowTriggers.getNonWebhookTriggerWorkflowIds).not.toHaveBeenCalled(); + }); + + test('does not restart after shutdown', async () => { + const deactivator = createDeactivator(); + deactivator.shutdown(); + deactivator.startGhostTriggerJanitor(); + + await advanceOneInterval(); + + expect(activeWorkflowTriggers.getNonWebhookTriggerWorkflowIds).not.toHaveBeenCalled(); + }); + }); }); }); diff --git a/packages/cli/src/workflows/publication/__tests__/workflow-publication-lifecycle-lock.test.ts b/packages/cli/src/workflows/publication/__tests__/workflow-publication-lifecycle-lock.test.ts index a7b612f395a..2844e7757cf 100644 --- a/packages/cli/src/workflows/publication/__tests__/workflow-publication-lifecycle-lock.test.ts +++ b/packages/cli/src/workflows/publication/__tests__/workflow-publication-lifecycle-lock.test.ts @@ -88,78 +88,4 @@ describe('WorkflowPublicationLifecycleLock', () => { expect(lock.isLocked('wf-1')).toBe(false); }); }); - - describe('getLockedWorkflowIds', () => { - test('returns workflows currently holding lifecycle locks', async () => { - const lock = new WorkflowPublicationLifecycleLock(); - let releaseFirst!: () => void; - let releaseSecond!: () => void; - - const first = lock.runExclusive('wf-1', async () => { - await new Promise((resolve) => { - releaseFirst = resolve; - }); - }); - const second = lock.runExclusive('wf-2', async () => { - await new Promise((resolve) => { - releaseSecond = resolve; - }); - }); - await flushMacrotasks(); - - expect(lock.getLockedWorkflowIds()).toEqual(['wf-1', 'wf-2']); - - releaseFirst(); - releaseSecond(); - await Promise.all([first, second]); - - expect(lock.getLockedWorkflowIds()).toEqual([]); - }); - }); - - describe('runExclusiveOrTimeout', () => { - test('runs the function under the lock and reports no timeout when uncontended', async () => { - const lock = new WorkflowPublicationLifecycleLock(); - let ran = false; - - const result = await lock.runExclusiveOrTimeout( - 'wf-1', - async () => { - ran = true; - }, - 1000, - ); - - expect(result).toEqual({ timedOut: false }); - expect(ran).toBe(true); - }); - - test('falls through and runs the function unlocked when the holder never releases', async () => { - vi.useFakeTimers(); - try { - const lock = new WorkflowPublicationLifecycleLock(); - - // Acquire and hold wf-1 indefinitely. - const holder = lock.runExclusive('wf-1', async () => await new Promise(() => {})); - - let ran = false; - const resultPromise = lock.runExclusiveOrTimeout( - 'wf-1', - async () => { - ran = true; - }, - 30_000, - ); - - vi.advanceTimersByTime(30_000); - - await expect(resultPromise).resolves.toEqual({ timedOut: true }); - expect(ran).toBe(true); - - void holder; - } finally { - vi.useRealTimers(); - } - }); - }); }); diff --git a/packages/cli/src/workflows/publication/__tests__/workflow-publication-outbox-consumer.test.ts b/packages/cli/src/workflows/publication/__tests__/workflow-publication-outbox-consumer.test.ts index 13fa1dd09cf..62c6d7090b0 100644 --- a/packages/cli/src/workflows/publication/__tests__/workflow-publication-outbox-consumer.test.ts +++ b/packages/cli/src/workflows/publication/__tests__/workflow-publication-outbox-consumer.test.ts @@ -407,6 +407,16 @@ describe('WorkflowPublicationOutboxConsumer', () => { expect(outboxRepository.claimNextPendingRecord).not.toHaveBeenCalled(); expect(vi.getTimerCount()).toBe(0); }); + + test('on a follower, neither polls nor claims a record', async () => { + outboxRepository.claimNextPendingRecord.mockResolvedValue(makeRecord({ id: 1 })); + consumer = createConsumer(true, false); + + await consumer.wakeUp(); + + expect(outboxRepository.claimNextPendingRecord).not.toHaveBeenCalled(); + expect(vi.getTimerCount()).toBe(0); + }); }); describe('poll cycle', () => { diff --git a/packages/cli/src/workflows/publication/__tests__/workflow-publication-reconciler.service.test.ts b/packages/cli/src/workflows/publication/__tests__/workflow-publication-reconciler.service.test.ts index 00cbc11791e..accba96642b 100644 --- a/packages/cli/src/workflows/publication/__tests__/workflow-publication-reconciler.service.test.ts +++ b/packages/cli/src/workflows/publication/__tests__/workflow-publication-reconciler.service.test.ts @@ -262,6 +262,30 @@ describe('WorkflowPublicationReconciler', () => { expect(activeWorkflowTriggers.remove).not.toHaveBeenCalled(); }); + it('reports a failing ghost teardown and keeps reconciling', async () => { + // One ghost whose teardown throws must not take down the rest of the + // surplus pass — nor the missing/skew detections that run after it. + activeWorkflowTriggers.getNonWebhookTriggerWorkflowIds.mockReturnValue(['wf-bad', 'wf-good']); + activeWorkflowTriggers.remove.mockImplementation(async (workflowId) => { + if (workflowId === 'wf-bad') throw new Error('closeFunction failed'); + return true; + }); + + await service.reconcile(); + + expect(activeWorkflowTriggers.remove).toHaveBeenCalledWith('wf-good'); + expect(errorReporter.error).toHaveBeenCalledWith(expect.any(Error), { + shouldBeLogged: true, + }); + // The pass carried on into the later detections. + expect(triggerStatusRepository.findActivatedInMemoryTriggers).toHaveBeenCalled(); + expect(outboxRepository.findVersionSkewedWorkflowIds).toHaveBeenCalled(); + expect(eventService.emit).toHaveBeenCalledWith( + 'workflow-publication-reconciliation', + expect.objectContaining({ result: 'success', surplusCount: 1 }), + ); + }); + it('re-checks under the lock and skips a workflow republished since detection', async () => { setGhost('wf-ghost'); // By the time the lock is acquired, a publish has completed: the workflow diff --git a/packages/cli/src/workflows/publication/published-workflow-trigger-deactivator.ts b/packages/cli/src/workflows/publication/published-workflow-trigger-deactivator.ts index 42f3b6ce577..5a54fe2f857 100644 --- a/packages/cli/src/workflows/publication/published-workflow-trigger-deactivator.ts +++ b/packages/cli/src/workflows/publication/published-workflow-trigger-deactivator.ts @@ -1,27 +1,44 @@ import { Logger } from '@n8n/backend-common'; import { WorkflowsConfig } from '@n8n/config'; import { Time } from '@n8n/constants'; -import { OnLeaderStepdown, OnShutdown } from '@n8n/decorators'; +import { OnLeaderStepdown, OnLeaderTakeover, OnShutdown } from '@n8n/decorators'; import { Service } from '@n8n/di'; -import { ActiveWorkflowTriggers, ErrorReporter } from 'n8n-core'; -import { UnexpectedError } from 'n8n-workflow'; +import { ActiveWorkflowTriggers, ErrorReporter, InstanceSettings } from 'n8n-core'; +import { EventService } from '@/events/event.service'; import { WorkflowPublicationLifecycleLock } from '@/workflows/publication/workflow-publication-lifecycle-lock'; import { WorkflowPublicationOutboxConsumer } from '@/workflows/publication/workflow-publication-outbox-consumer'; /** - * How long teardown waits for an in-flight publication record to finish before - * deactivating a workflow's triggers anyway, so a hung trigger `closeFunction` - * cannot block leader demotion. + * How many consecutive teardown passes may find a workflow's lifecycle lock + * held before the held lock itself is reported. One or two locked passes are + * normal (an apply in flight); a longer streak means the holder is stuck and + * the workflow can be neither swept nor torn down until it releases. */ -const STEPDOWN_TEARDOWN_TIMEOUT_MS = 30 * Time.seconds.toMilliseconds; +const STUCK_LOCK_WARN_AFTER_PASSES = 3; /** - * Tears down in-memory triggers on leader stepdown and shutdown. Teardown is - * coordinated with in-flight outbox records via {@link WorkflowPublicationLifecycleLock}. + * Tears down in-memory triggers on leader stepdown and shutdown, and — as a + * safeguard — periodically sweeps the registry while not leader: a trigger + * registration on a non-leader should never exist, so anything found is torn + * down and reported. + * + * All teardown shares one semantic: skip a workflow whose lifecycle lock is + * held and let the periodic sweep retry it after the lock is released — + * nothing ever waits on or bypasses a held lock. Convergence is guaranteed by + * the reconciliation loop, not by any single pass. + * + * The one exception is shutdown: no sweep runs afterwards, so a workflow whose + * lock is held at that instant keeps its triggers until the process exits. + * Acceptable — exit reclaims all in-memory registrations, and broker-side + * trigger state self-heals through its own session timeouts. */ @Service() export class PublishedWorkflowTriggerDeactivator { + private ghostTriggerJanitorInterval: NodeJS.Timeout | undefined; + private isShuttingDown = false; + private consecutiveLockSkipsByWorkflowId: Map = new Map(); + constructor( private readonly logger: Logger, private readonly workflowsConfig: WorkflowsConfig, @@ -29,6 +46,8 @@ export class PublishedWorkflowTriggerDeactivator { private readonly lifecycleLock: WorkflowPublicationLifecycleLock, private readonly activeWorkflowTriggers: ActiveWorkflowTriggers, private readonly outboxConsumer: WorkflowPublicationOutboxConsumer, + private readonly instanceSettings: InstanceSettings, + private readonly eventService: EventService, ) { this.logger = this.logger.scoped('workflow-publication'); } @@ -43,49 +62,100 @@ export class PublishedWorkflowTriggerDeactivator { this.outboxConsumer.stopPolling(); - // Include workflow ids that only exist in the lifecycle lock: a first activation - // can be in-flight before any local triggers or crons are registered. - const workflowIds = new Set([ - ...this.activeWorkflowTriggers.getNonWebhookTriggerWorkflowIds(), - ...this.lifecycleLock.getLockedWorkflowIds(), - ]); - const lockedWorkflowIds: string[] = []; + const workflowIds = this.activeWorkflowTriggers.getNonWebhookTriggerWorkflowIds(); - for (const workflowId of workflowIds) { - if (this.lifecycleLock.isLocked(workflowId)) { - lockedWorkflowIds.push(workflowId); - continue; - } - await this.deactivateWorkflow(workflowId); - } - - for (const workflowId of lockedWorkflowIds) { - await this.deactivateWorkflow(workflowId); - } + await this.deactivateWorkflows(workflowIds); } - /** - * Removes a single workflow's non-webhook triggers under its lifecycle lock. If - * the lock can't be acquired within {@link STEPDOWN_TEARDOWN_TIMEOUT_MS} (e.g. a - * trigger `closeFunction` is stuck), the workflow is deactivated anyway so - * demotion is never blocked, and the timeout is reported. - */ - private async deactivateWorkflow(workflowId: string): Promise { - const { timedOut } = await this.lifecycleLock.runExclusiveOrTimeout( - workflowId, - async () => { - await this.activeWorkflowTriggers.remove(workflowId); - }, - STEPDOWN_TEARDOWN_TIMEOUT_MS, - ); + async sweepGhostTriggers(): Promise { + if (this.instanceSettings.isLeader) return 0; - if (timedOut) { - this.errorReporter.error( - new UnexpectedError( - `Timed out waiting for an in-flight publication record for workflow "${workflowId}" before trigger teardown; tore down anyway`, - ), - { shouldBeLogged: true }, + const ghosts: string[] = []; + + // Nothing may escape this method: the interval callback driving it has + // nobody awaiting it, so an escaped rejection would crash the process. + try { + const workflowIds = this.activeWorkflowTriggers.getNonWebhookTriggerWorkflowIds(); + + ghosts.push(...(await this.deactivateWorkflows(workflowIds))); + + if (ghosts.length > 0) { + this.logger.warn(`Found ${ghosts.length} ghost workflows. Removed them.`, { + workflowIds: ghosts, + }); + this.eventService.emit('workflow-publication-ghost-trigger-sweep', { + removedCount: ghosts.length, + }); + } + } catch (error) { + this.errorReporter.error(error, { shouldBeLogged: true }); + } + + return ghosts.length; + } + + /** Records a locked-skip and reports the lock as stuck once the streak hits the threshold. */ + private trackSkippedWhileLocked(workflowId: string) { + const skips = (this.consecutiveLockSkipsByWorkflowId.get(workflowId) ?? 0) + 1; + + if (skips === STUCK_LOCK_WARN_AFTER_PASSES) { + this.logger.warn( + `Lifecycle lock for workflow "${workflowId}" still held after ${skips} teardown passes; its triggers cannot be torn down until the holder releases the lock`, + { workflowId, passes: skips }, ); } + + this.consecutiveLockSkipsByWorkflowId.set(workflowId, skips); + } + + private async deactivateWorkflows(workflowIds: string[]) { + const deactivatedWorkflows: string[] = []; + + for (const workflowId of workflowIds) { + try { + if (this.lifecycleLock.isLocked(workflowId)) { + this.trackSkippedWhileLocked(workflowId); + continue; + } + this.consecutiveLockSkipsByWorkflowId.delete(workflowId); + const result = await this.lifecycleLock.runExclusive(workflowId, async () => { + if (this.instanceSettings.isLeader && !this.isShuttingDown) return false; + return await this.activeWorkflowTriggers.remove(workflowId); + }); + if (result) { + deactivatedWorkflows.push(workflowId); + } + } catch (error) { + this.errorReporter.error(error, { shouldBeLogged: true }); + } + } + return deactivatedWorkflows; + } + + @OnLeaderStepdown() + startGhostTriggerJanitor(): void { + if (!this.workflowsConfig.useWorkflowPublicationService) return; + if (this.isShuttingDown) return; + if (this.ghostTriggerJanitorInterval) return; + this.consecutiveLockSkipsByWorkflowId = new Map(); + + this.ghostTriggerJanitorInterval = setInterval( + async () => await this.sweepGhostTriggers(), + this.workflowsConfig.publicationReconcileIntervalSeconds * Time.seconds.toMilliseconds, + ); + } + + @OnLeaderTakeover() + stopGhostTriggerJanitor(): void { + if (this.ghostTriggerJanitorInterval) { + clearInterval(this.ghostTriggerJanitorInterval); + this.ghostTriggerJanitorInterval = undefined; + } + } + + @OnShutdown() + shutdown(): void { + this.isShuttingDown = true; + this.stopGhostTriggerJanitor(); } } diff --git a/packages/cli/src/workflows/publication/workflow-publication-lifecycle-lock.ts b/packages/cli/src/workflows/publication/workflow-publication-lifecycle-lock.ts index 5a8a07cfdb7..8207a3a3d93 100644 --- a/packages/cli/src/workflows/publication/workflow-publication-lifecycle-lock.ts +++ b/packages/cli/src/workflows/publication/workflow-publication-lifecycle-lock.ts @@ -13,15 +13,16 @@ interface WorkflowLockState { * leave a trigger running on a demoted instance. * * The lock is keyed by workflow id rather than instance-global so unrelated - * workflows never block each other — teardown can deactivate every workflow with - * no in-flight record immediately and wait only on the ones being processed. + * workflows never block each other. Teardown never queues on a held lock at + * all: it checks {@link isLocked} and skips, relying on the periodic sweep to + * retry after release. * * Synchronization is local — only the leader processes the outbox — so in-process * locks are sufficient; no distributed lock is needed. * - * Hand-rolled rather than using a mutex library: we need per-workflow keyed locks, - * and {@link runExclusiveOrTimeout} runs `fn` anyway (without the lock) on timeout - * instead of rejecting — off-the-shelf mutexes don't offer this. + * Hand-rolled rather than using a mutex library: per-workflow keyed FIFO locks + * with cheap lock-state visibility are the entire requirement — a dependency + * would cost more than these few lines. */ @Service() export class WorkflowPublicationLifecycleLock { @@ -32,11 +33,6 @@ export class WorkflowPublicationLifecycleLock { return this.stateByWorkflowId.has(workflowId); } - /** Workflow ids currently holding or waiting on a lifecycle lock. */ - getLockedWorkflowIds(): string[] { - return Array.from(this.stateByWorkflowId.keys()); - } - /** Runs `fn` under the workflow's lock, waiting indefinitely to acquire it. */ async runExclusive(workflowId: string, fn: () => Promise): Promise { await this.acquire(workflowId); @@ -47,32 +43,6 @@ export class WorkflowPublicationLifecycleLock { } } - /** - * Acquires the workflow's lock within `timeoutMs` and runs `fn` under it. If it - * cannot be acquired in time (e.g. an in-flight record is stuck), `fn` runs - * WITHOUT the lock so the caller still makes progress, and `timedOut` is `true`. - */ - async runExclusiveOrTimeout( - workflowId: string, - fn: () => Promise, - timeoutMs: number, - ): Promise<{ timedOut: boolean }> { - const acquired = await this.acquireWithTimeout(workflowId, timeoutMs); - - if (!acquired) { - await fn(); - return { timedOut: true }; - } - - try { - await fn(); - } finally { - this.release(workflowId); - } - - return { timedOut: false }; - } - private getOrCreateState(workflowId: string): WorkflowLockState { let state = this.stateByWorkflowId.get(workflowId); if (!state) { @@ -104,37 +74,4 @@ export class WorkflowPublicationLifecycleLock { this.stateByWorkflowId.delete(workflowId); } } - - private async acquireWithTimeout(workflowId: string, timeoutMs: number): Promise { - const state = this.getOrCreateState(workflowId); - if (!state.locked) { - state.locked = true; - return true; - } - - return await new Promise((resolve) => { - // `settled` resolves the timeout-vs-handoff race: whichever fires first - // wins, so the lock is never granted twice nor wedged on an abandoned - // waiter. On timeout we splice the waiter out so `release` never hands - // ownership to it. - let settled = false; - - const waiter = () => { - if (settled) return; - settled = true; - clearTimeout(timer); - resolve(true); - }; - - const timer = setTimeout(() => { - if (settled) return; - settled = true; - const index = state.waiters.indexOf(waiter); - if (index !== -1) state.waiters.splice(index, 1); - resolve(false); - }, timeoutMs); - - state.waiters.push(waiter); - }); - } } diff --git a/packages/cli/src/workflows/publication/workflow-publication-outbox-consumer.ts b/packages/cli/src/workflows/publication/workflow-publication-outbox-consumer.ts index 9c9854fd23d..30e48388ffc 100644 --- a/packages/cli/src/workflows/publication/workflow-publication-outbox-consumer.ts +++ b/packages/cli/src/workflows/publication/workflow-publication-outbox-consumer.ts @@ -95,6 +95,10 @@ export class WorkflowPublicationOutboxConsumer { @OnPubSubEvent('workflow-publish-wake-up', { instanceType: 'main', instanceRole: 'leader' }) async wakeUp(): Promise { if (!this.workflowsConfig.useWorkflowPublicationService) return; + // Direct callers (e.g. a short-lived CLI process waking its own local consumer) + // bypass the pubsub role filter, and a non-leader claiming a record it can never + // apply would strand it `in_progress` until its lease expires. + if (!this.instanceSettings.isLeader) return; this.startPolling(); await this.drainPending(); diff --git a/packages/cli/src/workflows/publication/workflow-publication-reconciler.service.ts b/packages/cli/src/workflows/publication/workflow-publication-reconciler.service.ts index 219269bfbcb..0fded3e0f8d 100644 --- a/packages/cli/src/workflows/publication/workflow-publication-reconciler.service.ts +++ b/packages/cli/src/workflows/publication/workflow-publication-reconciler.service.ts @@ -178,15 +178,19 @@ export class WorkflowPublicationReconciler { private async removeGhostTriggers(workflowIds: WorkflowId[]): Promise { let surplusRepairs = 0; for (const workflowId of workflowIds) { - await this.lifecycleLock.runExclusive(workflowId, async () => { - const workflow = await this.workflowRepository.findOneBy({ id: workflowId }); + try { + await this.lifecycleLock.runExclusive(workflowId, async () => { + const workflow = await this.workflowRepository.findOneBy({ id: workflowId }); - if (workflow?.activeVersionId) return; - if (await this.outboxRepository.findInFlightByWorkflowId(workflowId)) return; + if (workflow?.activeVersionId) return; + if (await this.outboxRepository.findInFlightByWorkflowId(workflowId)) return; - await this.activeWorkflowTriggers.remove(workflowId); - surplusRepairs++; - }); + await this.activeWorkflowTriggers.remove(workflowId); + surplusRepairs++; + }); + } catch (error) { + this.errorReporter.error(error, { shouldBeLogged: true }); + } } return surplusRepairs; diff --git a/packages/cli/src/workflows/triggers/__tests__/trigger-activation-retry.test.ts b/packages/cli/src/workflows/triggers/__tests__/trigger-activation-retry.test.ts index 22aeba2981e..2590fc14e59 100644 --- a/packages/cli/src/workflows/triggers/__tests__/trigger-activation-retry.test.ts +++ b/packages/cli/src/workflows/triggers/__tests__/trigger-activation-retry.test.ts @@ -5,8 +5,7 @@ import { retryTriggerActivation, } from '@/workflows/triggers/trigger-activation-retry'; -vi.mock('n8n-workflow', async () => ({ - ...(await vi.importActual('n8n-workflow')), +vi.mock('@n8n/utils/sleep', () => ({ sleep: vi.fn(), })); diff --git a/packages/cli/src/workflows/triggers/__tests__/trigger-execution-context.factory.test.ts b/packages/cli/src/workflows/triggers/__tests__/trigger-execution-context.factory.test.ts index 88d282ba95a..944711c24cf 100644 --- a/packages/cli/src/workflows/triggers/__tests__/trigger-execution-context.factory.test.ts +++ b/packages/cli/src/workflows/triggers/__tests__/trigger-execution-context.factory.test.ts @@ -2,8 +2,9 @@ import type { Logger } from '@n8n/backend-common'; import type { Project, WorkflowEntity } from '@n8n/db'; import { createDeferredPromise } from '@n8n/utils/promise/deferred-promise'; +import { sleep } from '@n8n/utils/sleep'; import type { ErrorReporter, IGetExecutePollFunctions, StorageConfig } from 'n8n-core'; -import { sleep, UnexpectedError, Workflow } from 'n8n-workflow'; +import { UnexpectedError, Workflow } from 'n8n-workflow'; import type { Cron, CronExpression, diff --git a/packages/cli/src/workflows/triggers/__tests__/trigger-test-utils.ts b/packages/cli/src/workflows/triggers/__tests__/trigger-test-utils.ts index 6394d52c42c..923a039f242 100644 --- a/packages/cli/src/workflows/triggers/__tests__/trigger-test-utils.ts +++ b/packages/cli/src/workflows/triggers/__tests__/trigger-test-utils.ts @@ -40,6 +40,16 @@ export function createNodeTypes() { trigger: vi.fn(), } as never; } + // The pseudo triggers under their real type names: they implement `trigger()` + // like any in-memory trigger, but it is a no-op (fired externally by the + // execution engine), so classification must tell them apart by node type. + if ( + type === 'n8n-nodes-base.manualTrigger' || + type === 'n8n-nodes-base.executeWorkflowTrigger' || + type === 'n8n-nodes-base.errorTrigger' + ) { + return { description: { ...description, name: type }, trigger: vi.fn() } as never; + } if (type === 'poll') { return { description: { ...description, name: 'poll' }, poll: vi.fn() } as never; } diff --git a/packages/cli/src/workflows/triggers/__tests__/workflow-trigger-activator.test.ts b/packages/cli/src/workflows/triggers/__tests__/workflow-trigger-activator.test.ts index 7347b0d854c..893e2e20fa4 100644 --- a/packages/cli/src/workflows/triggers/__tests__/workflow-trigger-activator.test.ts +++ b/packages/cli/src/workflows/triggers/__tests__/workflow-trigger-activator.test.ts @@ -23,8 +23,7 @@ import type { WorkflowStaticDataService } from '@/workflows/workflow-static-data import { createNodeTypes, logger, node } from './trigger-test-utils'; -vi.mock('n8n-workflow', async () => ({ - ...(await vi.importActual('n8n-workflow')), +vi.mock('@n8n/utils/sleep', () => ({ sleep: vi.fn(), })); @@ -164,6 +163,26 @@ describe('WorkflowTriggerActivator', () => { expect(kinds.get('tw')).toBe('in-memory'); expect(kinds.size).toBe(5); }); + + test('classifies the no-op pseudo triggers as persisted despite their trigger function', () => { + const activator = buildActivator(); + + const kinds = activator.getTriggerKinds([ + node('manual', 'n8n-nodes-base.manualTrigger'), + node('sub-workflow', 'n8n-nodes-base.executeWorkflowTrigger'), + node('error', 'n8n-nodes-base.errorTrigger'), + node('t', 'trigger'), + ]); + + // Their trigger() is a no-op — manual runs, sub-workflow calls and error + // workflows are fired by the execution engine, never through the trigger + // registry — so the reconciler must not diff them against the registry. + expect(kinds.get('manual')).toBe('persisted'); + expect(kinds.get('sub-workflow')).toBe('persisted'); + expect(kinds.get('error')).toBe('persisted'); + // A genuine trigger with the same capability shape stays in-memory. + expect(kinds.get('t')).toBe('in-memory'); + }); }); describe('getNodesWithUnregisteredWebhooks', () => { diff --git a/packages/cli/src/workflows/triggers/trigger-activation-retry.ts b/packages/cli/src/workflows/triggers/trigger-activation-retry.ts index c76694d38a0..32b1b70d172 100644 --- a/packages/cli/src/workflows/triggers/trigger-activation-retry.ts +++ b/packages/cli/src/workflows/triggers/trigger-activation-retry.ts @@ -1,5 +1,6 @@ import { ensureError } from '@n8n/utils/errors/ensure-error'; -import { WebhookPathTakenError, sleep } from 'n8n-workflow'; +import { sleep } from '@n8n/utils/sleep'; +import { WebhookPathTakenError } from 'n8n-workflow'; import { WORKFLOW_REACTIVATE_INITIAL_TIMEOUT, WORKFLOW_REACTIVATE_MAX_TIMEOUT } from '@/constants'; diff --git a/packages/cli/src/workflows/triggers/workflow-trigger-activator.ts b/packages/cli/src/workflows/triggers/workflow-trigger-activator.ts index 3d9fbd5870a..7bf9357ec05 100644 --- a/packages/cli/src/workflows/triggers/workflow-trigger-activator.ts +++ b/packages/cli/src/workflows/triggers/workflow-trigger-activator.ts @@ -18,7 +18,13 @@ import type { WorkflowExecuteMode, WorkflowId, } from 'n8n-workflow'; -import { Workflow, WorkflowActivationError } from 'n8n-workflow'; +import { + ERROR_TRIGGER_NODE_TYPE, + EXECUTE_WORKFLOW_TRIGGER_NODE_TYPE, + MANUAL_TRIGGER_NODE_TYPE, + Workflow, + WorkflowActivationError, +} from 'n8n-workflow'; import { ActivationErrorsService } from '@/activation-errors.service'; import { TRIGGER_ACTIVATION_MAX_ATTEMPTS } from '@/constants'; @@ -39,6 +45,14 @@ import { WorkflowStaticDataService } from '@/workflows/workflow-static-data.serv export type WorkflowTriggerVersion = { nodes: INode[]; connections: IConnections }; +// Their trigger() is a no-op — fired by the execution engine, never the +// registry — so reconciling them against the registry would re-enqueue forever. +const PSEUDO_TRIGGER_NODE_TYPES = new Set([ + MANUAL_TRIGGER_NODE_TYPE, + EXECUTE_WORKFLOW_TRIGGER_NODE_TYPE, + ERROR_TRIGGER_NODE_TYPE, +]); + /** A single trigger node that failed to (de)register during activation. */ export type TriggerActivationFailure = { nodeId: INode['id']; @@ -113,8 +127,11 @@ export class WorkflowTriggerActivator { * Maps each node to where it lives once activated, decided by which functions * its node type implements: nodes with a `poll` or `trigger` function register * `in-memory`, nodes with only a `webhook` function are `persisted` rows in - * `webhook_entity`. Used by reconciliation to tell which triggers should be in - * the in-memory registry. + * `webhook_entity`. The pseudo triggers (manual, executeWorkflow, error) are + * `persisted` despite their `trigger` function: it is a no-op fired by the + * execution engine, so the registry holds nothing worth reconciling for them. + * Used by reconciliation to tell which triggers should be in the in-memory + * registry. */ getTriggerKinds(nodes: INode[]): Map { const workflow = new Workflow({ @@ -127,7 +144,9 @@ export class WorkflowTriggerActivator { }); const inMemoryNodeIds = new Set( - [...workflow.getPollNodes(), ...workflow.getTriggerNodes()].map((node) => node.id), + [...workflow.getPollNodes(), ...workflow.getTriggerNodes()] + .filter((node) => !PSEUDO_TRIGGER_NODE_TYPES.has(node.type)) + .map((node) => node.id), ); const kinds = new Map(); diff --git a/packages/cli/src/workflows/workflow-finder.service.ts b/packages/cli/src/workflows/workflow-finder.service.ts index 8f4bcb7694e..dfd5a0ed62a 100644 --- a/packages/cli/src/workflows/workflow-finder.service.ts +++ b/packages/cli/src/workflows/workflow-finder.service.ts @@ -165,14 +165,16 @@ export class WorkflowFinderService { workflowIds: string[], user: User, scopes: Scope[], - options: { includeParentFolder?: boolean } = {}, + options: { includeParentFolder?: boolean; includeTags?: boolean } = {}, ): Promise { if (workflowIds.length === 0) return []; const where = await this.findAllWhere(user, scopes); const sharedWorkflows = await this.sharedWorkflowRepository.find({ where: { ...where, workflowId: In(workflowIds) }, - relations: { workflow: { parentFolder: options.includeParentFolder } }, + relations: { + workflow: { parentFolder: options.includeParentFolder, tags: options.includeTags }, + }, }); // A workflow may appear via several share paths (project membership + diff --git a/packages/cli/src/workflows/workflow-publish-guard-proxy.service.ts b/packages/cli/src/workflows/workflow-publish-guard-proxy.service.ts new file mode 100644 index 00000000000..a5124407d45 --- /dev/null +++ b/packages/cli/src/workflows/workflow-publish-guard-proxy.service.ts @@ -0,0 +1,23 @@ +import { Service } from '@n8n/di'; + +/** + * Lets a module veto publishing a workflow without core depending on the module: + * it registers an implementation on {@link WorkflowPublishGuardProxy} and throws + * to block. With no provider registered, publishing is allowed. + */ +export interface WorkflowPublishGuard { + assertCanPublish(workflowId: string): Promise; +} + +@Service() +export class WorkflowPublishGuardProxy implements WorkflowPublishGuard { + private provider: WorkflowPublishGuard | null = null; + + registerProvider(provider: WorkflowPublishGuard): void { + this.provider = provider; + } + + async assertCanPublish(workflowId: string): Promise { + await this.provider?.assertCanPublish(workflowId); + } +} diff --git a/packages/cli/src/workflows/workflow.service.ts b/packages/cli/src/workflows/workflow.service.ts index c395bec0f5f..f047acab584 100644 --- a/packages/cli/src/workflows/workflow.service.ts +++ b/packages/cli/src/workflows/workflow.service.ts @@ -20,10 +20,10 @@ import { hasGlobalScope } from '@n8n/permissions'; import type { EntityManager } from '@n8n/typeorm'; import { In } from '@n8n/typeorm'; import type { QueryDeepPartialEntity } from '@n8n/typeorm/query-builder/QueryPartialEntity'; +import { ensureError } from '@n8n/utils/errors/ensure-error'; import isEqual from 'lodash/isEqual'; import pick from 'lodash/pick'; import type { INode, INodes, IWorkflowSettings, JsonValue, IConnections } from 'n8n-workflow'; -import { ensureError } from '@n8n/utils/errors/ensure-error'; import { PROJECT_ROOT, Workflow, assert, calculateWorkflowChecksum } from 'n8n-workflow'; import { v4 as uuid } from 'uuid'; @@ -52,17 +52,17 @@ import { OwnershipService } from '@/services/ownership.service'; import { ProjectService } from '@/services/project.service.ee'; import { RoleService } from '@/services/role.service'; import { TagService } from '@/services/tag.service'; +import { WebhookService } from '@/webhooks/webhook.service'; import { getBase as getWorkflowExecutionData } from '@/workflow-execute-additional-data'; +import * as WorkflowHelpers from '@/workflow-helpers'; import { WorkflowHookContextService } from '@/workflow-hook-context.service'; -import { WorkflowValidationService } from './workflow-validation.service'; - -import { WebhookService } from '@/webhooks/webhook.service'; -import * as WorkflowHelpers from '@/workflow-helpers'; import { WorkflowPublicationNotifier } from './publication/workflow-publication-notifier'; import { getErrorDescription, getErrorNodeId, getRequiredRedactionScopes } from './utils'; import { WorkflowFinderService } from './workflow-finder.service'; import { WorkflowHistoryService } from './workflow-history/workflow-history.service'; +import { WorkflowPublishGuardProxy } from './workflow-publish-guard-proxy.service'; +import { WorkflowValidationService } from './workflow-validation.service'; @Service() export class WorkflowService { @@ -96,6 +96,7 @@ export class WorkflowService { private readonly pollTriggerJobRegistrar: PollTriggerJobRegistrar, private readonly workflowPublishedVersionRepository: WorkflowPublishedVersionRepository, private readonly workflowHookContextService: WorkflowHookContextService, + private readonly workflowPublishGuard: WorkflowPublishGuardProxy, ) {} async getMany( @@ -564,11 +565,8 @@ export class WorkflowService { ); } - const publishCurrent = workflow.activeVersionId && publishIfActive; - if (publishCurrent) { - updatePayload.active = true; - updatePayload.activeVersionId = workflowUpdateData.versionId; - } + const versionIdToPublish = + workflow.activeVersionId && publishIfActive ? workflowUpdateData.versionId : null; if (parentFolderId) { const project = await this.sharedWorkflowRepository.getWorkflowOwningProject(workflow.id); @@ -632,11 +630,17 @@ export class WorkflowService { source, }); - // Activate workflow if requested, or - // Reactivate workflow if settings changed and workflow has an active version - if (updatedWorkflow.activeVersionId && (publishCurrent || settingsChanged)) { + if (versionIdToPublish) { + const publishedWorkflow = await this.activateWorkflow(user, workflowId, { + versionId: versionIdToPublish, + source, + }); + updatedWorkflow.active = publishedWorkflow.active; + updatedWorkflow.activeVersionId = publishedWorkflow.activeVersionId; + updatedWorkflow.activeVersion = publishedWorkflow.activeVersion; + } else if (settingsChanged && workflow.activeVersionId) { await this.activateWorkflow(user, workflowId, { - versionId: updatedWorkflow.activeVersionId, + versionId: workflow.activeVersionId, source, }); } @@ -853,6 +857,12 @@ export class WorkflowService { }); } + // re-applying the already-published version (e.g. a settings-only update) + // publishes no new version, so the review gate must not block it. + if (versionIdToActivate !== previousActiveVersionId) { + await this.workflowPublishGuard.assertCanPublish(workflowId); + } + if (this.globalConfig.workflows.useWorkflowPublicationService) { await this._publishViaOutbox( user, @@ -1028,25 +1038,7 @@ export class WorkflowService { }); } - if (this.globalConfig.workflows.useWorkflowPublicationService) { - await this._unpublishViaOutbox(user, workflowId, deactivatedVersionId, workflow.updatedAt); - } else { - await this.activeWorkflowManager.remove(workflowId); - - await this.workflowRepository.update(workflowId, { - active: false, - activeVersionId: null, - // workflow content did not change, so we keep updatedAt as is - updatedAt: workflow.updatedAt, - }); - - await this.workflowPublishHistoryRepository.addRecord({ - workflowId, - versionId: deactivatedVersionId, - event: 'deactivated', - userId: user.id, - }); - } + await this._teardownActiveVersion(workflow, deactivatedVersionId, user.id); // Update the workflow object for response workflow.active = false; @@ -1065,6 +1057,79 @@ export class WorkflowService { return workflow; } + /** + * Deactivates a workflow without a user context (system-initiated, e.g. + * crash-loop auto-deactivation). Skips permission and checksum checks and + * does not emit `workflow-deactivated`; publish history records a null user. + */ + async deactivateWorkflowAsSystem(workflowId: string): Promise { + const workflow = await this.workflowRepository.findOne({ + where: { id: workflowId }, + relations: { activeVersion: true }, + }); + if (!workflow) return; + + const deactivatedVersionId = workflow.activeVersionId; + if (deactivatedVersionId === null) return; + + // `active` is still true here: the hook sees the pre-deactivation state. + const deactivatedWorkflow = this.workflowRepository.create({ + ...workflow, + versionId: deactivatedVersionId, + activeVersion: null, + nodes: workflow.activeVersion?.nodes ?? workflow.nodes, + connections: workflow.activeVersion?.connections ?? workflow.connections, + }); + + try { + await this.externalHooks.run('workflow.deactivate', [ + deactivatedWorkflow, + this.workflowHookContextService, + ]); + } catch (error) { + // A failing hook must not leave a crash-looping workflow published + this.logger.warn('workflow.deactivate hook failed during system deactivation, proceeding', { + workflowId, + error: ensureError(error).message, + }); + } + + await this._teardownActiveVersion(workflow, deactivatedVersionId, null); + } + + /** + * Flag-branched teardown shared by user- and system-initiated deactivation. + * Keeping it in one place prevents the two paths from drifting apart, which + * is how system deactivation ended up skipping unpublishing (CAT-3814). + */ + private async _teardownActiveVersion( + workflow: WorkflowEntity, + deactivatedVersionId: string, + userId: string | null, + ): Promise { + const workflowId = workflow.id; + + if (this.globalConfig.workflows.useWorkflowPublicationService) { + await this._unpublishViaOutbox(userId, workflowId, deactivatedVersionId, workflow.updatedAt); + } else { + await this.activeWorkflowManager.remove(workflowId); + + await this.workflowRepository.update(workflowId, { + active: false, + activeVersionId: null, + // workflow content did not change, so we keep updatedAt as is + updatedAt: workflow.updatedAt, + }); + + await this.workflowPublishHistoryRepository.addRecord({ + workflowId, + versionId: deactivatedVersionId, + event: 'deactivated', + userId, + }); + } + } + /** * Deletes a workflow and returns it. * @@ -1159,7 +1224,7 @@ export class WorkflowService { const activeVersionId = workflow.activeVersionId; if (activeVersionId !== null) { if (this.globalConfig.workflows.useWorkflowPublicationService) { - await this._unpublishViaOutbox(user, workflowId, activeVersionId, workflow.updatedAt); + await this._unpublishViaOutbox(user.id, workflowId, activeVersionId, workflow.updatedAt); } else { await this.activeWorkflowManager.remove(workflowId); @@ -1517,7 +1582,7 @@ export class WorkflowService { * the record while the workflow still looks active and handle it as a publish. */ private async _unpublishViaOutbox( - user: User, + userId: string | null, workflowId: string, deactivatedVersionId: string, updatedAt: Date, @@ -1539,7 +1604,7 @@ export class WorkflowService { workflowId, versionId: deactivatedVersionId, event: 'deactivated', - userId: user.id, + userId, }, trx, ); diff --git a/packages/cli/test/integration/api-keys.api.test.ts b/packages/cli/test/integration/api-keys.api.test.ts index 1f5e9c0b8b0..58d563caea9 100644 --- a/packages/cli/test/integration/api-keys.api.test.ts +++ b/packages/cli/test/integration/api-keys.api.test.ts @@ -20,6 +20,7 @@ import { createUser, createUserShell, } from './shared/db/users'; +import { createCustomRoleWithScopeSlugs } from './shared/db/roles'; import type { SuperAgentTest } from './shared/types'; import * as utils from './shared/utils/'; @@ -843,3 +844,89 @@ describe('Cross-user behavior (admin scope)', () => { expect(response.body.data.totals.all).toBe(4); }); }); + +describe('User with a role that grants no apiKey scopes', () => { + let restrictedUser: User; + let authAgent: SuperAgentTest; + + beforeEach(async () => { + const restrictedRole = await createCustomRoleWithScopeSlugs(['role:manageProject'], { + roleType: 'global', + }); + restrictedUser = await createUser({ + password: randomValidPassword(), + role: restrictedRole, + }); + authAgent = testServer.authAgentFor(restrictedUser); + }); + + test('GET /api-keys should return own keys', async () => { + const ownKey = await addApiKey(restrictedUser, { scopes: ['workflow:create'] }); + + const response = await authAgent.get('/api-keys').expect(200); + + expect(response.body.data.counts.all).toBe(1); + expect(response.body.data.items).toHaveLength(1); + expect(response.body.data.items[0].id).toBe(ownKey.id); + }); + + test("GET /api-keys should never include other users' keys", async () => { + await createOwnerWithApiKey(); + await addApiKey(restrictedUser, { scopes: ['workflow:create'] }); + + const response = await authAgent.get('/api-keys?ownership=all').expect(200); + + expect(response.body.data.items).toHaveLength(1); + expect( + response.body.data.items.every( + (apiKey: { userId: string }) => apiKey.userId === restrictedUser.id, + ), + ).toBe(true); + expect(response.body.data.owners).toEqual([]); + }); + + test('DELETE /api-keys/:id should revoke an own key', async () => { + const ownKey = await addApiKey(restrictedUser, { scopes: ['workflow:create'] }); + + const response = await authAgent.delete(`/api-keys/${ownKey.id}`).expect(200); + + expect(response.body.data.success).toBe(true); + + const remaining = await authAgent.get('/api-keys').expect(200); + expect(remaining.body.data.items).toHaveLength(0); + }); + + test("DELETE /api-keys/:id should 404 for another user's key", async () => { + const otherUser = await createOwnerWithApiKey(); + + await authAgent.delete(`/api-keys/${otherUser.apiKeys[0].id}`).expect(404); + }); + + test('POST /api-keys should be forbidden', async () => { + await authAgent + .post('/api-keys') + .send({ label: 'My API Key', expiresAt: null, scopes: ['workflow:create'] }) + .expect(403); + }); + + test('PATCH /api-keys/:id should be forbidden even for an own key', async () => { + const ownKey = await addApiKey(restrictedUser, { scopes: ['workflow:create'] }); + + await authAgent + .patch(`/api-keys/${ownKey.id}`) + .send({ label: 'Updated label', scopes: ['workflow:create'] }) + .expect(403); + }); + + test('POST /api-keys/:id/rotate should be forbidden', async () => { + const ownKey = await addApiKey(restrictedUser, { scopes: ['workflow:create'] }); + + await authAgent.post(`/api-keys/${ownKey.id}/rotate`).expect(403); + }); + + test('GET /api-keys/scopes should return the scopes assignable by the role', async () => { + const response = await authAgent.get('/api-keys/scopes').expect(200); + + expect(Array.isArray(response.body.data)).toBe(true); + }); +}); diff --git a/packages/cli/test/integration/commands/start.cmd.test.ts b/packages/cli/test/integration/commands/start.cmd.test.ts index a6ed53bcc74..9c2b80245c3 100644 --- a/packages/cli/test/integration/commands/start.cmd.test.ts +++ b/packages/cli/test/integration/commands/start.cmd.test.ts @@ -24,7 +24,7 @@ import type { INode } from 'n8n-workflow'; import { ActiveExecutions } from '@/active-executions'; import { ActiveWorkflowManager } from '@/active-workflow-manager'; import { Start } from '@/commands/start'; -import { ExecutionService } from '@/executions/execution.service'; +import { EnqueuedExecutionRecoveryService } from '@/executions/enqueued-execution-recovery.service'; import { ExternalHooks } from '@/external-hooks'; import { N8NCheckpointStorage } from '@/modules/agents/integrations/n8n-checkpoint-storage'; import { Push } from '@/push'; @@ -57,7 +57,10 @@ mockInstance(WorkflowHistoryCompactionService); mockInstance(WorkflowStatisticsRollupService); mockInstance(N8NCheckpointStorage); mockInstance(DurableScheduler); -mockInstance(ExecutionService).findAllEnqueuedExecutions.mockResolvedValue([]); +// Also proves `run()`'s dynamic `.js` import resolves to the same module as this +// static one - otherwise the mock would silently not apply and the real recovery +// would query the test DB. +const enqueuedExecutionRecovery = mockInstance(EnqueuedExecutionRecoveryService); beforeAll(async () => { await testDb.init(); @@ -116,6 +119,7 @@ describe('Start.run() with workflow publication service', () => { // imports — or none at all — leaves the event without a listener). expect(hasWakeUpHandler()).toBe(true); expect(Container.get(PubSubEventBus).listenerCount('workflow-publish-wake-up')).toBe(1); + expect(enqueuedExecutionRecovery.recoverEnqueuedExecutions).toHaveBeenCalled(); // An active, published workflow whose trigger is not yet registered in // memory — the state a wake-up drain must reconcile. diff --git a/packages/cli/test/integration/database/repositories/workflow-publish-history.repository.test.ts b/packages/cli/test/integration/database/repositories/workflow-publish-history.repository.test.ts index 754e279464b..745472b5051 100644 --- a/packages/cli/test/integration/database/repositories/workflow-publish-history.repository.test.ts +++ b/packages/cli/test/integration/database/repositories/workflow-publish-history.repository.test.ts @@ -206,6 +206,122 @@ describe('WorkflowPublishHistoryRepository', () => { }); }); + describe('getVersionPublicationStates', () => { + const repository = () => Container.get(WorkflowPublishHistoryRepository); + + /** Workflow-history createdAt drives the "later version" rule, so space the rows out. */ + const addVersion = async (workflow: Awaited>) => { + const versionId = uuid(); + await createWorkflowHistory({ ...workflow, versionId }); + await new Promise((resolve) => setTimeout(resolve, 10)); + return versionId; + }; + + const activate = async (workflowId: string, versionId: string) => { + await repository().addRecord({ workflowId, versionId, event: 'activated', userId: null }); + }; + + const deactivate = async (workflowId: string, versionId: string) => { + await repository().addRecord({ workflowId, versionId, event: 'deactivated', userId: null }); + }; + + const stateOf = async (workflowId: string, versionId: string) => { + const states = await repository().getVersionPublicationStates(workflowId, [versionId]); + return states.get(versionId); + }; + + it('returns published when the version itself was activated', async () => { + const workflow = await createWorkflow(); + const versionId = await addVersion(workflow); + await activate(workflow.id, versionId); + + await expect(stateOf(workflow.id, versionId)).resolves.toBe('published'); + }); + + it('returns published even after a later deactivation', async () => { + const workflow = await createWorkflow(); + const versionId = await addVersion(workflow); + await activate(workflow.id, versionId); + await deactivate(workflow.id, versionId); + + await expect(stateOf(workflow.id, versionId)).resolves.toBe('published'); + }); + + it('returns not_published when nothing was ever activated', async () => { + const workflow = await createWorkflow(); + const versionId = await addVersion(workflow); + + await expect(stateOf(workflow.id, versionId)).resolves.toBe('not_published'); + }); + + it('returns superseded when a later-created version was activated', async () => { + const workflow = await createWorkflow(); + const versionId = await addVersion(workflow); + const laterVersionId = await addVersion(workflow); + await activate(workflow.id, laterVersionId); + + await expect(stateOf(workflow.id, versionId)).resolves.toBe('superseded'); + }); + + it('returns superseded even after the later version was deactivated', async () => { + const workflow = await createWorkflow(); + const versionId = await addVersion(workflow); + const laterVersionId = await addVersion(workflow); + await activate(workflow.id, laterVersionId); + await deactivate(workflow.id, laterVersionId); + + await expect(stateOf(workflow.id, versionId)).resolves.toBe('superseded'); + }); + + it('returns not_published when only an earlier-created version was activated', async () => { + const workflow = await createWorkflow(); + const earlierVersionId = await addVersion(workflow); + const versionId = await addVersion(workflow); + await activate(workflow.id, earlierVersionId); + + await expect(stateOf(workflow.id, versionId)).resolves.toBe('not_published'); + }); + + it('ignores activations of another workflow', async () => { + const workflow = await createWorkflow(); + const versionId = await addVersion(workflow); + const otherWorkflow = await createWorkflow(); + const otherVersionId = await addVersion(otherWorkflow); + await activate(otherWorkflow.id, otherVersionId); + + await expect(stateOf(workflow.id, versionId)).resolves.toBe('not_published'); + }); + + it('returns unknown when the pinned history row was pruned', async () => { + const workflow = await createWorkflow(); + const versionId = await addVersion(workflow); + await Container.get(WorkflowHistoryRepository).delete({ versionId }); + + await expect(stateOf(workflow.id, versionId)).resolves.toBe('unknown'); + }); + + it('resolves each requested version independently in one call', async () => { + const workflow = await createWorkflow(); + const publishedVersionId = await addVersion(workflow); + const supersededVersionId = await addVersion(workflow); + const activatedVersionId = await addVersion(workflow); + const prunedVersionId = await addVersion(workflow); + await activate(workflow.id, publishedVersionId); + await activate(workflow.id, activatedVersionId); + await Container.get(WorkflowHistoryRepository).delete({ versionId: prunedVersionId }); + + const states = await repository().getVersionPublicationStates(workflow.id, [ + publishedVersionId, + supersededVersionId, + prunedVersionId, + ]); + + expect(states.get(publishedVersionId)).toBe('published'); + expect(states.get(supersededVersionId)).toBe('superseded'); + expect(states.get(prunedVersionId)).toBe('unknown'); + }); + }); + describe('Foreign key constraints', () => { it('should cascade delete when workflow is deleted', async () => { const repository = Container.get(WorkflowPublishHistoryRepository); diff --git a/packages/cli/test/integration/database/services/db-lock.service.test.ts b/packages/cli/test/integration/database/services/db-lock.service.test.ts index c7dd46bd1ca..49e7e2f40b7 100644 --- a/packages/cli/test/integration/database/services/db-lock.service.test.ts +++ b/packages/cli/test/integration/database/services/db-lock.service.test.ts @@ -9,7 +9,8 @@ import { } from '@n8n/db'; import { Container } from '@n8n/di'; import { DataSource } from '@n8n/typeorm'; -import { OperationalError, sleep } from 'n8n-workflow'; +import { sleep } from '@n8n/utils/sleep'; +import { OperationalError } from 'n8n-workflow'; import { randomUUID } from 'node:crypto'; let dbLockService: DbLockService; diff --git a/packages/cli/test/integration/environments/source-control-import.service.test.ts b/packages/cli/test/integration/environments/source-control-import.service.test.ts index fcde5c2c267..4f907f97e41 100644 --- a/packages/cli/test/integration/environments/source-control-import.service.test.ts +++ b/packages/cli/test/integration/environments/source-control-import.service.test.ts @@ -126,6 +126,7 @@ describe('SourceControlImportService', () => { mock(), // dataTableSizeValidator mock(), // activeWorkflowManager mock(), // executionPersistence + mock(), // workflowPublishGuard ); }); diff --git a/packages/cli/test/integration/execution.repository.test.ts b/packages/cli/test/integration/execution.repository.test.ts index 05f36d184a2..f2fd694b7a5 100644 --- a/packages/cli/test/integration/execution.repository.test.ts +++ b/packages/cli/test/integration/execution.repository.test.ts @@ -144,6 +144,19 @@ describe('UserRepository', () => { conditions: undefined, updateExpected: true, }, + // CAT-3862: executions enqueued before a restart are claimed while still `new` + { + statusInDB: 'new' as ExecutionStatus, + statusUpdate: 'running' as ExecutionStatus, + conditions: { requireStatus: 'new' as ExecutionStatus }, + updateExpected: true, + }, + { + statusInDB: 'new' as ExecutionStatus, + statusUpdate: 'running' as ExecutionStatus, + conditions: { requireStatus: 'waiting' as ExecutionStatus }, + updateExpected: false, + }, ])( 'should return $updateExpected with status before: "$statusInDB", status after: "$statusUpdate" and conditions: $conditions', async ({ statusInDB, statusUpdate, conditions, updateExpected }) => { diff --git a/packages/cli/test/integration/oidc/oidc.instance-settings-loader.test.ts b/packages/cli/test/integration/oidc/oidc.instance-settings-loader.test.ts index c7c21183410..b3183097a15 100644 --- a/packages/cli/test/integration/oidc/oidc.instance-settings-loader.test.ts +++ b/packages/cli/test/integration/oidc/oidc.instance-settings-loader.test.ts @@ -48,6 +48,8 @@ describe('SsoInstanceSettingsLoader → OIDC', () => { oidcLoginEnabled: true, oidcPrompt: 'consent', oidcAcrValues: 'mfa, phrh', + oidcAdditionalScopes: 'offline_access', + oidcRpInitiatedLogoutEnabled: true, ssoUserRoleProvisioning: 'instance_and_project_roles', }); @@ -65,5 +67,7 @@ describe('SsoInstanceSettingsLoader → OIDC', () => { expect(config.loginEnabled).toBe(true); expect(config.prompt).toBe('consent'); expect(config.authenticationContextClassReference).toEqual(['mfa', 'phrh']); + expect(config.additionalScopes).toBe('offline_access'); + expect(config.rpInitiatedLogoutEnabled).toBe(true); }); }); diff --git a/packages/cli/test/integration/oidc/oidc.service.ee.test.ts b/packages/cli/test/integration/oidc/oidc.service.ee.test.ts index 03f13ddd377..c0af95bf552 100644 --- a/packages/cli/test/integration/oidc/oidc.service.ee.test.ts +++ b/packages/cli/test/integration/oidc/oidc.service.ee.test.ts @@ -70,6 +70,7 @@ describe('OIDC service', () => { prompt: 'select_account', authenticationContextClassReference: [], additionalScopes: '', + rpInitiatedLogoutEnabled: false, }); }); @@ -83,6 +84,7 @@ describe('OIDC service', () => { prompt: 'select_account', authenticationContextClassReference: [], additionalScopes: '', + rpInitiatedLogoutEnabled: false, }); }); @@ -95,6 +97,7 @@ describe('OIDC service', () => { prompt: 'select_account', authenticationContextClassReference: ['mfa', 'phrh', 'pwd'], additionalScopes: '', + rpInitiatedLogoutEnabled: false, }; await oidcService.updateConfig(newConfig); @@ -119,6 +122,7 @@ describe('OIDC service', () => { prompt: 'select_account', authenticationContextClassReference: ['mfa', 'phrh', 'pwd'], additionalScopes: '', + rpInitiatedLogoutEnabled: false, }; await oidcService.updateConfig(newConfig); @@ -142,6 +146,7 @@ describe('OIDC service', () => { prompt: 'select_account', authenticationContextClassReference: ['mfa', 'phrh', 'pwd'], additionalScopes: '', + rpInitiatedLogoutEnabled: false, }; await expect(oidcService.updateConfig(newConfig)).rejects.toThrowError(UserError); @@ -156,6 +161,7 @@ describe('OIDC service', () => { prompt: 'select_account', authenticationContextClassReference: ['mfa', 'phrh', 'pwd'], additionalScopes: '', + rpInitiatedLogoutEnabled: false, }; await oidcService.updateConfig(newConfig); @@ -180,6 +186,7 @@ describe('OIDC service', () => { prompt: 'select_account', authenticationContextClassReference: ['mfa', 'phrh', 'pwd'], additionalScopes: '', + rpInitiatedLogoutEnabled: false, }; discoveryMock.mockRejectedValueOnce(new Error('Discovery failed')); @@ -206,6 +213,7 @@ describe('OIDC service', () => { prompt: 'select_account', authenticationContextClassReference: ['mfa', 'phrh', 'pwd'], additionalScopes: '', + rpInitiatedLogoutEnabled: false, }; const mockConfiguration = new real_odic_client.Configuration( @@ -238,6 +246,7 @@ describe('OIDC service', () => { prompt: 'select_account', authenticationContextClassReference: ['mfa', 'phrh', 'pwd'], additionalScopes: '', + rpInitiatedLogoutEnabled: false, }; const newMockConfiguration = new real_odic_client.Configuration( @@ -291,6 +300,7 @@ describe('OIDC service', () => { prompt: 'consent', authenticationContextClassReference: ['mfa', 'phrh', 'pwd'], additionalScopes: '', + rpInitiatedLogoutEnabled: false, }; await oidcService.updateConfig(initialConfig); @@ -336,6 +346,7 @@ describe('OIDC service', () => { prompt: 'consent', authenticationContextClassReference: ['mfa', 'phrh', 'pwd'], additionalScopes: '', + rpInitiatedLogoutEnabled: false, }; await oidcService.updateConfig(initialConfig); @@ -437,6 +448,7 @@ describe('OIDC service', () => { prompt: 'select_account', authenticationContextClassReference: [], additionalScopes: '', + rpInitiatedLogoutEnabled: false, }; let provisioningConfig: GlobalConfig['sso']['provisioning']; @@ -511,6 +523,7 @@ describe('OIDC service', () => { await oidcService.updateConfig({ ...baseConfig, additionalScopes: 'groups&redirect_uri=https://evil.com', + rpInitiatedLogoutEnabled: false, }); const authUrl = await oidcService.generateLoginUrl(); @@ -548,6 +561,7 @@ describe('OIDC service', () => { prompt: 'select_account', authenticationContextClassReference: [], additionalScopes: 'groups', + rpInitiatedLogoutEnabled: false, }); const authUrl = await oidcService.generateTestLoginUrl(); @@ -577,6 +591,7 @@ describe('OIDC service', () => { prompt: 'select_account', authenticationContextClassReference: [], additionalScopes: '', + rpInitiatedLogoutEnabled: false, }); const authUrl = await oidcService.generateTestLoginUrl(); @@ -1354,4 +1369,18 @@ describe('OIDC service', () => { expect(() => oidcService.verifyNonce(invalid)).toThrow(BadRequestError); }); }); + + describe('ID token encryption', () => { + it('round-trips an ID token through the real cipher', async () => { + const idToken = 'header.payload.signature'; + const encrypted = await oidcService.encryptIdToken(idToken); + + expect(encrypted).not.toEqual(idToken); + expect(await oidcService.decryptIdToken(encrypted)).toEqual(idToken); + }); + + it('returns undefined for a tampered ciphertext', async () => { + expect(await oidcService.decryptIdToken('not-a-valid-ciphertext')).toBeUndefined(); + }); + }); }); diff --git a/packages/cli/test/integration/public-api/n8n-packages-export.test.ts b/packages/cli/test/integration/public-api/n8n-packages-export.test.ts index 97ddcc4faf9..f8d32268dcb 100644 --- a/packages/cli/test/integration/public-api/n8n-packages-export.test.ts +++ b/packages/cli/test/integration/public-api/n8n-packages-export.test.ts @@ -183,4 +183,17 @@ describe('POST /n8n-packages/export', () => { expect(response.headers['content-type']).toContain('application/gzip'); expect(response.headers['content-disposition']).toContain('export.n8np'); }); + + // The OpenAPI request schema has `additionalProperties: false`, so acceptance + // proves `includeTags` is declared in exportPackageRequest.yml. + test('accepts includeTags=false through the OpenAPI request validator', async () => { + const project = await createTeamProject('Export project', owner); + const folder = await createFolder(project, { name: 'to_production' }); + + const response = await authOwnerAgent + .post('/n8n-packages/export') + .send({ folderIds: [folder.id], includeTags: false }); + + expect(response.statusCode).toBe(200); + }); }); diff --git a/packages/cli/test/integration/public-api/n8n-packages-import.test.ts b/packages/cli/test/integration/public-api/n8n-packages-import.test.ts index bfdb3ef4aa9..0868e1db948 100644 --- a/packages/cli/test/integration/public-api/n8n-packages-import.test.ts +++ b/packages/cli/test/integration/public-api/n8n-packages-import.test.ts @@ -238,6 +238,12 @@ describe('POST /n8n-packages/import', () => { missing: [], stubbed: [], }, + tags: { + matched: [], + created: [], + renamed: [], + skipped: [], + }, }); expect(response.body.workflows[0].localId).not.toBe('wf-http-source'); @@ -261,6 +267,8 @@ describe('POST /n8n-packages/import', () => { .field('dataTableSchemaConflictPolicy', 'fail') .field('variableMissingMode', 'do-nothing') .field('variableParentPolicy', 'project') + .field('tagMissingMode', 'do-nothing') + .field('tagConflictPolicy', 'fail') .attach('package', tarBuffer, 'import.n8np'); expect(response.statusCode).toBe(200); diff --git a/packages/cli/test/integration/public-api/sso-oidc.test.ts b/packages/cli/test/integration/public-api/sso-oidc.test.ts index 800fd574254..cb781382f9b 100644 --- a/packages/cli/test/integration/public-api/sso-oidc.test.ts +++ b/packages/cli/test/integration/public-api/sso-oidc.test.ts @@ -35,6 +35,7 @@ const validConfig = { prompt: 'consent' as const, authenticationContextClassReference: ['mfa'], additionalScopes: 'groups', + rpInitiatedLogoutEnabled: false, }; describe('OIDC SSO configuration in Public API', () => { @@ -84,6 +85,7 @@ describe('OIDC SSO configuration in Public API', () => { prompt: 'select_account', authenticationContextClassReference: [], additionalScopes: '', + rpInitiatedLogoutEnabled: false, }); expect(typeof response.body.discoveryEndpoint).toBe('string'); }); @@ -103,6 +105,7 @@ describe('OIDC SSO configuration in Public API', () => { 'discoveryEndpoint', 'loginEnabled', 'prompt', + 'rpInitiatedLogoutEnabled', ].sort(), ); }); @@ -208,6 +211,20 @@ describe('OIDC SSO configuration in Public API', () => { expect(disabled.body.loginEnabled).toBe(false); }); + it('persists rpInitiatedLogoutEnabled', async () => { + testServer.license.enable('feat:oidc'); + + const response = await testServer + .publicApiAgentFor(owner) + .put('/settings/sso/oidc') + .send({ ...validConfig, rpInitiatedLogoutEnabled: true }); + expect(response.status).toBe(200); + expect(response.body.rpInitiatedLogoutEnabled).toBe(true); + + const read = await testServer.publicApiAgentFor(owner).get('/settings/sso/oidc'); + expect(read.body.rpInitiatedLogoutEnabled).toBe(true); + }); + it('keeps the stored secret when the redacted sentinel is submitted', async () => { testServer.license.enable('feat:oidc'); await testServer.publicApiAgentFor(owner).put('/settings/sso/oidc').send(validConfig); diff --git a/packages/cli/test/integration/scheduling/executor-repository.test.ts b/packages/cli/test/integration/scheduling/executor-repository.test.ts index e359524e91d..6286aaea702 100644 --- a/packages/cli/test/integration/scheduling/executor-repository.test.ts +++ b/packages/cli/test/integration/scheduling/executor-repository.test.ts @@ -126,21 +126,48 @@ describe('ScheduledTaskRepository executor methods', () => { expect(claimed.map((t) => t.id)).toEqual([soon.id]); }); - it('claims every due occurrence of a job (supersede is deferred to the reaper)', async () => { - // Two due occurrences of the same job: both are claimable. Skipping a - // superseded occurrence needs the misfire grace/policy and lands later. - const older = await createTask({ - scheduledFor: new Date('2026-06-01T10:00:00.000Z'), - runAt: new Date(Date.now() - 40_000), - }); - const newer = await createTask({ - scheduledFor: new Date('2026-06-01T11:00:00.000Z'), - runAt: new Date(Date.now() - 20_000), + it('does not claim an occurrence past its deadline', async () => { + const stale = await createTask({ + runAt: past(), + missedAfter: new Date(Date.now() - 30_000), }); const claimed = await taskRepository.claimDueTasks(claimOpts()); - expect(claimed.map((t) => t.id).sort()).toEqual([older.id, newer.id].sort()); + expect(claimed.map((t) => t.id)).not.toContain(stale.id); + expect((await reload(stale.id)).status).toBe('pending'); + }); + + it('claims an occurrence still inside its deadline', async () => { + const late = await createTask({ + runAt: past(), + missedAfter: new Date(Date.now() + 30_000), + }); + + const claimed = await taskRepository.claimDueTasks(claimOpts()); + + expect(claimed.map((t) => t.id)).toContain(late.id); + }); + + it('claims an occurrence that carries no deadline', async () => { + const legacy = await createTask({ runAt: past(), missedAfter: null }); + + const claimed = await taskRepository.claimDueTasks(claimOpts()); + + expect(claimed.map((t) => t.id)).toContain(legacy.id); + }); + + it('still claims a retry whose deadline passed while it waited out its backoff', async () => { + const retry = await createTask({ + runAt: past(), + missedAfter: new Date(Date.now() - 30_000), + attempts: 1, + maxAttempts: 3, + }); + + const claimed = await taskRepository.claimDueTasks(claimOpts()); + + expect(claimed.map((t) => t.id)).toContain(retry.id); }); it('caps the claim at batchSize, taking the earliest by runAt', async () => { @@ -379,6 +406,17 @@ describe('ScheduledTaskRepository executor methods', () => { expect(row.leaseExpiresAt).toBeNull(); }); + it('releaseClaim keeps the deadline, so a released occurrence still expires', async () => { + const { id, epoch } = await claimOne(); + await taskRepository.update(id, { missedAfter: past() }); + + expect(await taskRepository.releaseClaim({ host: HOST_A, id, claimedEpoch: epoch })).toBe(1); + + expect(await taskRepository.claimDueTasks(claimOpts({ host: HOST_A }))).toHaveLength(0); + expect(await taskRepository.retireMissedPending(10)).toBe(1); + expect((await reload(id)).status).toBe('missed'); + }); + it('fences a stale epoch: the owner at a superseded epoch cannot transition', async () => { // The same owner stalls, is reaped and reclaimed (epoch bumped), then its // stale `Executor.fire` tries to write. `claimedBy` still matches, so only @@ -866,5 +904,245 @@ describe('ScheduledTaskRepository executor methods', () => { expect((await reload(task.id)).status).toBe('succeeded'); }); }); + + describe('retireMissedPending', () => { + it('retires a pending occurrence the claim has stopped offering', async () => { + const stale = await createTask({ + runAt: past(), + missedAfter: new Date(Date.now() - 30_000), + }); + + expect(await taskRepository.retireMissedPending(10)).toBe(1); + + const row = await reload(stale.id); + expect(row.status).toBe('missed'); + expect(row.finishedAt).not.toBeNull(); + }); + + it('leaves alone what the claim would still offer', async () => { + // Mirrors the three cases `claimDueTasks` still accepts. + const live = await createTask({ missedAfter: new Date(Date.now() + 30_000) }); + const noDeadline = await createTask({ missedAfter: null }); + const retry = await createTask({ + missedAfter: new Date(Date.now() - 30_000), + attempts: 1, + maxAttempts: 3, + }); + + expect(await taskRepository.retireMissedPending(10)).toBe(0); + + for (const task of [live, noDeadline, retry]) { + expect((await reload(task.id)).status).toBe('pending'); + } + }); + + it('leaves a claimed occurrence alone', async () => { + const running = await createTask({ + status: 'running', + claimedBy: HOST_A, + leaseExpiresAt: new Date(Date.now() + 60_000), + missedAfter: new Date(Date.now() - 30_000), + }); + + expect(await taskRepository.retireMissedPending(10)).toBe(0); + expect((await reload(running.id)).status).toBe('running'); + }); + + it('caps the sweep at the given limit', async () => { + await createTask({ missedAfter: new Date(Date.now() - 30_000) }); + await createTask({ missedAfter: new Date(Date.now() - 60_000) }); + + expect(await taskRepository.retireMissedPending(1)).toBe(1); + }); + }); + }); + + describe('updateToMissed', () => { + it('retires the pending occurrences older than the catch-up run that replaces them', async () => { + const older = await createTask(); + const newer = await createTask(); + const catchUp = await createTask(); + + const retired = await taskRepository.updateToMissed(taskRepository.manager, [ + { jobId: job.id, before: catchUp.scheduledFor }, + ]); + + expect(retired).toBe(2); + for (const task of [older, newer]) { + expect((await reload(task.id)).status).toBe('missed'); + } + expect((await reload(catchUp.id)).status).toBe('pending'); + }); + + it('leaves an occurrence that is already running or finished', async () => { + const running = await createTask({ + status: 'running', + claimedBy: HOST_A, + leaseExpiresAt: new Date(Date.now() + 60_000), + }); + const done = await createTask({ status: 'succeeded', finishedAt: past() }); + const catchUp = await createTask(); + + const retired = await taskRepository.updateToMissed(taskRepository.manager, [ + { jobId: job.id, before: catchUp.scheduledFor }, + ]); + + expect(retired).toBe(0); + expect((await reload(running.id)).status).toBe('running'); + expect((await reload(done.id)).status).toBe('succeeded'); + }); + + it('retires nothing when given nothing', async () => { + await createTask(); + + expect(await taskRepository.updateToMissed(taskRepository.manager, [])).toBe(0); + }); + + it('retires an occurrence whose retry is already scheduled, same as one never attempted', async () => { + // Coalesce means at most one run for the backlog: a failed attempt awaiting + // retry is still a second run alongside the catch-up if left alone. + const retrying = await createTask({ attempts: 1 }); + const catchUp = await createTask(); + + const retired = await taskRepository.updateToMissed(taskRepository.manager, [ + { jobId: job.id, before: catchUp.scheduledFor }, + ]); + + expect(retired).toBe(1); + expect((await reload(retrying.id)).status).toBe('missed'); + }); + + it('applies each job its own cutoff in a single call, without crossing jobs', async () => { + const otherJob = await jobRepository.save( + jobRepository.create({ + name: `job-${Math.random().toString(36).slice(2)}`, + workflowId: null, + nodeId: null, + taskType: TASK_TYPE, + payload: {}, + kind: 'interval', + intervalSeconds: 60, + enabled: true, + nextRunAt: new Date('2026-01-01T00:00:00.000Z'), + maxAttempts: 1, + }), + ); + const superseded = await createTask(); + const catchUp = await createTask(); + const otherSuperseded = await createTask({ jobId: otherJob.id }); + // Older than otherCatchUp, so it would match otherJob's bracket by jobId and + // scheduledFor alone: proves the status filter still applies to every job's + // bracket, not only the first. + const otherRunning = await createTask({ + jobId: otherJob.id, + status: 'running', + claimedBy: HOST_A, + leaseExpiresAt: new Date(Date.now() + 60_000), + }); + const otherCatchUp = await createTask({ jobId: otherJob.id }); + + const retired = await taskRepository.updateToMissed(taskRepository.manager, [ + { jobId: job.id, before: catchUp.scheduledFor }, + { jobId: otherJob.id, before: otherCatchUp.scheduledFor }, + ]); + + expect(retired).toBe(2); + expect((await reload(superseded.id)).status).toBe('missed'); + expect((await reload(catchUp.id)).status).toBe('pending'); + expect((await reload(otherSuperseded.id)).status).toBe('missed'); + expect((await reload(otherRunning.id)).status).toBe('running'); + expect((await reload(otherCatchUp.id)).status).toBe('pending'); + }); + }); + + describe('updateMissedAfterForJobs', () => { + it("recomputes a pending row's deadline from its own future runAt", async () => { + const runAt = new Date(Date.now() + 60_000); + const task = await createTask({ runAt, missedAfter: new Date(runAt.getTime() + 30_000) }); + + await taskRepository.updateMissedAfterForJobs(taskRepository.manager, [job.id], 90); + + const reloaded = await reload(task.id); + expect(reloaded.missedAfter!.getTime()).toBe(runAt.getTime() + 90_000); + }); + + it("clamps an overdue row's recomputed deadline to now", async () => { + const runAt = past(); + const task = await createTask({ runAt, missedAfter: new Date(runAt.getTime() + 30_000) }); + + // DB-clock brackets, not `Date.now()`: a container's clock can drift from + // the test runner's host clock. + const before = await taskRepository.readDbTime(); + await taskRepository.updateMissedAfterForJobs(taskRepository.manager, [job.id], 90); + const after = await taskRepository.readDbTime(); + + const reloaded = await reload(task.id); + const deadline = reloaded.missedAfter!.getTime(); + expect(deadline).toBeGreaterThanOrEqual(before.getTime() + 90_000); + expect(deadline).toBeLessThanOrEqual(after.getTime() + 90_000); + }); + + it('leaves a row with no deadline without one', async () => { + const task = await createTask({ missedAfter: null }); + + await taskRepository.updateMissedAfterForJobs(taskRepository.manager, [job.id], 90); + + expect((await reload(task.id)).missedAfter).toBeNull(); + }); + + it('leaves a running or finished row alone', async () => { + const runAt = past(); + const running = await createTask({ + status: 'running', + claimedBy: HOST_A, + leaseExpiresAt: new Date(Date.now() + 60_000), + runAt, + missedAfter: new Date(runAt.getTime() + 30_000), + }); + const done = await createTask({ + status: 'succeeded', + finishedAt: past(), + runAt, + missedAfter: new Date(runAt.getTime() + 30_000), + }); + + await taskRepository.updateMissedAfterForJobs(taskRepository.manager, [job.id], 90); + + expect((await reload(running.id)).missedAfter!.getTime()).toBe(runAt.getTime() + 30_000); + expect((await reload(done.id)).missedAfter!.getTime()).toBe(runAt.getTime() + 30_000); + }); + + it("leaves another job's rows alone", async () => { + const otherJob = await jobRepository.save( + jobRepository.create({ ...job, id: undefined, name: 'other-job' }), + ); + const runAt = past(); + const otherTask = await taskRepository.save( + taskRepository.create({ + jobId: otherJob.id, + taskType: TASK_TYPE, + payload: {}, + scheduledFor: new Date(), + runAt, + status: 'pending', + attempts: 0, + maxAttempts: 1, + missedAfter: new Date(runAt.getTime() + 30_000), + }), + ); + + await taskRepository.updateMissedAfterForJobs(taskRepository.manager, [job.id], 90); + + expect((await reload(otherTask.id)).missedAfter!.getTime()).toBe(runAt.getTime() + 30_000); + }); + + it('is a no-op when given no jobs', async () => { + const runAt = past(); + const task = await createTask({ runAt, missedAfter: new Date(runAt.getTime() + 30_000) }); + + await taskRepository.updateMissedAfterForJobs(taskRepository.manager, [], 90); + + expect((await reload(task.id)).missedAfter!.getTime()).toBe(runAt.getTime() + 30_000); + }); }); }); diff --git a/packages/cli/test/integration/scheduling/scheduled-repositories.test.ts b/packages/cli/test/integration/scheduling/scheduled-repositories.test.ts index 3d508c56a13..4e7b397a405 100644 --- a/packages/cli/test/integration/scheduling/scheduled-repositories.test.ts +++ b/packages/cli/test/integration/scheduling/scheduled-repositories.test.ts @@ -1,3 +1,4 @@ +import { ScheduledJobMisfirePolicy } from '@n8n/constants'; import { testDb } from '@n8n/backend-test-utils'; import type { NewScheduledJob, @@ -85,6 +86,8 @@ describe('scheduled repositories', () => { /** A minimal interval job row; bookkeeping columns take their defaults. */ const newJobRow = (name: string, overrides: Partial = {}): NewScheduledJob => ({ name, + misfirePolicy: ScheduledJobMisfirePolicy.Coalesce, + misfireGraceSeconds: 60, workflowId: null, nodeId: null, taskType: 'scheduleTrigger', @@ -551,6 +554,67 @@ describe('scheduled repositories', () => { }); }); + describe('ScheduledJobRepository.updateMisfirePolicy', () => { + it('rewrites the policy and grace of the given jobs only', async () => { + const updated = await createJob({ + misfirePolicy: ScheduledJobMisfirePolicy.Coalesce, + misfireGraceSeconds: 60, + }); + const untouched = await createJob({ + misfirePolicy: ScheduledJobMisfirePolicy.Coalesce, + misfireGraceSeconds: 60, + }); + + await dataSource.transaction( + async (trx) => + await jobRepository.updateMisfirePolicy(trx, [updated.id], { + misfirePolicy: ScheduledJobMisfirePolicy.Skip, + misfireGraceSeconds: 120, + }), + ); + + const after = await jobRepository.findOneByOrFail({ id: updated.id }); + expect(after.misfirePolicy).toBe(ScheduledJobMisfirePolicy.Skip); + expect(after.misfireGraceSeconds).toBe(120); + expect(after.intervalSeconds).toBe(updated.intervalSeconds); + expect(after.nextRunAt).toEqual(updated.nextRunAt); + + const other = await jobRepository.findOneByOrFail({ id: untouched.id }); + expect(other.misfirePolicy).toBe(ScheduledJobMisfirePolicy.Coalesce); + expect(other.misfireGraceSeconds).toBe(60); + }); + + it('leaves the queued occurrences of an updated job in place', async () => { + const job = await createJob(); + await createTask(job.id); + + await dataSource.transaction( + async (trx) => + await jobRepository.updateMisfirePolicy(trx, [job.id], { + misfirePolicy: ScheduledJobMisfirePolicy.Skip, + misfireGraceSeconds: 60, + }), + ); + + expect(await taskRepository.countBy({ jobId: job.id })).toBe(1); + }); + + it('does nothing when given no jobs', async () => { + const job = await createJob(); + + await dataSource.transaction( + async (trx) => + await jobRepository.updateMisfirePolicy(trx, [], { + misfirePolicy: ScheduledJobMisfirePolicy.Skip, + misfireGraceSeconds: 120, + }), + ); + + const after = await jobRepository.findOneByOrFail({ id: job.id }); + expect(after.misfirePolicy).toBe(ScheduledJobMisfirePolicy.Coalesce); + }); + }); + describe('ScheduledTaskRepository.insertIgnoringDuplicates', () => { it('inserts occurrences and returns how many were recorded', async () => { const job = await createJob(); diff --git a/packages/cli/test/integration/scheduling/scheduler-benchmarks.test.ts b/packages/cli/test/integration/scheduling/scheduler-benchmarks.test.ts index d77349e8b96..46fd7fa1189 100644 --- a/packages/cli/test/integration/scheduling/scheduler-benchmarks.test.ts +++ b/packages/cli/test/integration/scheduling/scheduler-benchmarks.test.ts @@ -10,6 +10,7 @@ import { import { Container } from '@n8n/di'; import { DataSource } from '@n8n/typeorm'; import type { QueryDeepPartialEntity } from '@n8n/typeorm/query-builder/QueryPartialEntity'; +import { sleep } from '@n8n/utils/sleep'; /** * Durable-scheduler performance benchmarks (CAT-3623), written to answer the @@ -120,8 +121,6 @@ const INSERT_CHUNK = 500; const SEED_TX_CHUNK = 5_000; const TEST_TIMEOUT_MS = 600_000; -const sleep = async (ms: number) => await new Promise((resolve) => setTimeout(resolve, ms)); - const commas = (n: number) => Math.round(n).toLocaleString('en-US'); /** p50/p95/p99 over per-item latency samples (ms). */ diff --git a/packages/cli/test/integration/scheduling/scheduler-cascade-delete.test.ts b/packages/cli/test/integration/scheduling/scheduler-cascade-delete.test.ts index e9d689068a8..01709b408cf 100644 --- a/packages/cli/test/integration/scheduling/scheduler-cascade-delete.test.ts +++ b/packages/cli/test/integration/scheduling/scheduler-cascade-delete.test.ts @@ -97,7 +97,7 @@ describe('scheduler cascade-delete safety', () => { expect(staleComplete).toBe(0); // The now-empty table gives every other pass nothing to do, not an error. - expect(await scheduler.reap()).toEqual({ reclaimed: 0, deadLettered: 0 }); + expect(await scheduler.reap()).toEqual({ reclaimed: 0, deadLettered: 0, missed: 0 }); expect(await scheduler.execute()).toEqual([]); expect(executed).toHaveLength(0); }); @@ -120,7 +120,7 @@ describe('scheduler cascade-delete safety', () => { await jobRepo.delete({ id: job.id }); expect(await taskRepo.countBy({ jobId: job.id })).toBe(0); - expect(await scheduler.reap()).toEqual({ reclaimed: 0, deadLettered: 0 }); + expect(await scheduler.reap()).toEqual({ reclaimed: 0, deadLettered: 0, missed: 0 }); expect(await scheduler.execute()).toEqual([]); }); }); diff --git a/packages/cli/test/integration/scheduling/scheduler-execute.test.ts b/packages/cli/test/integration/scheduling/scheduler-execute.test.ts index 2229496beec..d9111942ab2 100644 --- a/packages/cli/test/integration/scheduling/scheduler-execute.test.ts +++ b/packages/cli/test/integration/scheduling/scheduler-execute.test.ts @@ -130,7 +130,7 @@ describe('scheduler execution over the storage bindings', () => { const result = await scheduler.reap(); - expect(result).toEqual({ reclaimed: 1, deadLettered: 0 }); + expect(result).toEqual({ reclaimed: 1, deadLettered: 0, missed: 0 }); const recovered = await taskRepo.findOneByOrFail({ jobId: job.id }); expect(recovered.status).toBe('pending'); expect(recovered.claimedBy).toBeNull(); @@ -160,7 +160,7 @@ describe('scheduler execution over the storage bindings', () => { const result = await scheduler.reap(); - expect(result).toEqual({ reclaimed: 0, deadLettered: 1 }); + expect(result).toEqual({ reclaimed: 0, deadLettered: 1, missed: 0 }); const failed = await taskRepo.findOneByOrFail({ jobId: job.id }); expect(failed.status).toBe('failed'); expect(failed.claimedBy).toBe('main-dead'); diff --git a/packages/cli/test/integration/scheduling/scheduler-materialize.test.ts b/packages/cli/test/integration/scheduling/scheduler-materialize.test.ts index 70a9f3384c0..bdb86461fed 100644 --- a/packages/cli/test/integration/scheduling/scheduler-materialize.test.ts +++ b/packages/cli/test/integration/scheduling/scheduler-materialize.test.ts @@ -6,7 +6,7 @@ import { ScheduledTaskRepository, } from '@n8n/db'; import { Container } from '@n8n/di'; -import { createScheduler } from '@n8n/scheduler'; +import { createScheduler, totalDiscarded } from '@n8n/scheduler'; import type { SchedulerDeps } from '@n8n/scheduler'; import { buildMaterializerTransaction } from '@/scheduling/durable-scheduler'; @@ -33,6 +33,14 @@ describe('scheduler materialization', () => { let seq = 0; const secondsFromNow = (seconds: number) => new Date(Date.now() + seconds * 1000); + const claimOpts = () => ({ + host: 'materialize-test', + taskTypes: ['test'], + lookaheadMs: 0, + leaseMs: 60_000, + batchSize: 10, + }); + const createJob = async (overrides: Partial = {}) => await jobRepo.save( jobRepo.create({ @@ -91,8 +99,53 @@ describe('scheduler materialization', () => { expect(advanced.nextRunAt!.getTime() - advanced.lastFiredAt!.getTime()).toBe(3600 * 1000); }); - it('drains a backlog in maxPerJob-sized batches across successive passes', async () => { - // A job far behind (interval 10s, ~100s of backlog) so more than maxPerJob fires are due. + it('stops offering a coalesce occurrence once it is past its deadline', async () => { + await createJob({ misfirePolicy: 'coalesce', misfireGraceSeconds: 60 }); + + await runMaterialization(0); + + const [task] = await taskRepo.find(); + await taskRepo.update( + { id: task.id }, + { runAt: secondsFromNow(-86_400), missedAfter: secondsFromNow(-86_340) }, + ); + + expect(await taskRepo.claimDueTasks(claimOpts())).toHaveLength(0); + expect(await taskRepo.retireMissedPending(10)).toBe(1); + expect((await taskRepo.findOneByOrFail({ id: task.id })).status).toBe('missed'); + }); + + it.each(['skip', 'coalesce'] as const)( + 'gives a %s occurrence a deadline the claim can refuse it by', + async (misfirePolicy) => { + await createJob({ misfirePolicy, misfireGraceSeconds: 60 }); + + await runMaterialization(0); + + const [task] = await taskRepo.find(); + expect(task.missedAfter).not.toBeNull(); + expect(task.missedAfter!.getTime()).toBeGreaterThan(task.runAt.getTime()); + }, + ); + + it('retires the queued occurrences a later catch-up run supersedes', async () => { + const job = await createJob({ intervalSeconds: 10, misfireGraceSeconds: 30 }); + + await runMaterialization(0); + const [queued] = await taskRepo.find(); + expect(queued.status).toBe('pending'); + + await jobRepo.update({ id: job.id }, { nextRunAt: secondsFromNow(-300) }); + const summary = await runMaterialization(0); + + expect(summary.retiredOccurrences).toBe(1); + expect((await taskRepo.findOneByOrFail({ id: queued.id })).status).toBe('missed'); + expect(await taskRepo.countBy({ status: 'pending' })).toBe(1); + }); + + it('drops a capped backlog rather than firing a stale run per pass', async () => { + // A job far behind (interval 10s, ~100s of backlog) so more than maxPerJob fires + // are due, forcing the walk to stop at the cap. await createJob({ intervalSeconds: 10, nextRunAt: secondsFromNow(-100) }); const drainScheduler = composeScheduler({ windowSeconds: 0, @@ -102,28 +155,29 @@ describe('scheduler materialization', () => { defaultTimezone: 'UTC', }); - // The first pass records exactly maxPerJob, capping the batch rather than draining it all. + // Records nothing: every fire in this backlog is already stale by the time it's discarded. const first = await drainScheduler.materialize(); - expect(first.occurrences).toBe(5); - expect(await taskRepo.count()).toBe(5); + expect(first.occurrences).toBe(0); + expect(totalDiscarded(first.misfires)).toBe(5); + expect(await taskRepo.count()).toBe(0); - // Successive passes continue draining, each recording at most maxPerJob, until the - // backlog is exhausted. The claim reaches a poll interval ahead of due, so with - // windowSeconds: 0 the job keeps being claimed for its near-future fire even once - // drained; exhaustion shows as a pass that records nothing new, not an unclaimed job. - for (let i = 0; i < 10; i++) { + // Draining stops being a misfire once the remaining instants are inside their + // grace window, so a capped backlog costs at most a grace window of fires. + let passes = 0; + while (passes < 10) { const summary = await drainScheduler.materialize(); - expect(summary.occurrences).toBeLessThanOrEqual(5); - if (summary.occurrences === 0) break; + passes += 1; + if (summary.occurrences === 0 && totalDiscarded(summary.misfires) === 0) break; } - // Drained: the backlog is fully recorded, every occurrence distinct (no duplicate from batching). - const drained = await drainScheduler.materialize(); - expect(drained.occurrences).toBe(0); const tasks = await taskRepo.find(); const distinctInstants = new Set(tasks.map((t) => t.scheduledFor.getTime())); expect(distinctInstants.size).toBe(tasks.length); - expect(tasks.length).toBeGreaterThanOrEqual(10); + // The ~40s beyond the grace window are gone; only the recent tail was recorded. + expect(tasks.length).toBeLessThan(10); + for (const task of tasks) { + expect(task.scheduledFor.getTime()).toBeGreaterThan(Date.now() - 70_000); + } }); it('records the upcoming occurrences within the window, ahead of time', async () => { @@ -136,6 +190,32 @@ describe('scheduler materialization', () => { expect(await taskRepo.count()).toBe(summary.occurrences); }); + it('fires a backlog in full when every occurrence is still inside its grace window', async () => { + // Known residual: the misfire policy only acts once an occurrence is past its + // deadline. A backlog that fits entirely inside the grace window is not a + // misfire at all, so it is recorded and later claimed in full rather than + // coalesced/skipped down to one run: the burst this guards against is bounded by + // grace/interval, not eliminated. + await createJob({ + intervalSeconds: 10, + misfireGraceSeconds: 60, + nextRunAt: secondsFromNow(-45), + }); + + const summary = await runMaterialization(0); + + // -45s, -35s, -25s, -15s, -5s: five due instants, all newer than now-60s. + expect(summary.occurrences).toBe(5); + expect(totalDiscarded(summary.misfires)).toBe(0); + const tasks = await taskRepo.find(); + expect(tasks).toHaveLength(5); + + // Not just recorded: every one of them is still claimable, so this really is a + // five-execution burst on the next executor tick, not a discarded backlog. + const claimed = await taskRepo.claimDueTasks(claimOpts()); + expect(claimed).toHaveLength(5); + }); + it('records the same occurrence only once (idempotent)', async () => { const job = await createJob({ intervalSeconds: 3600, nextRunAt: secondsFromNow(-1) }); diff --git a/packages/cli/test/integration/scheduling/scheduler-multi-main.test.ts b/packages/cli/test/integration/scheduling/scheduler-multi-main.test.ts index e37690f2f51..3d06ffaff25 100644 --- a/packages/cli/test/integration/scheduling/scheduler-multi-main.test.ts +++ b/packages/cli/test/integration/scheduling/scheduler-multi-main.test.ts @@ -132,7 +132,7 @@ describe('scheduler across two mains over one database', () => { // main-b's reaper reclaims the expired lease back to pending, bumping the // fencing epoch so main-a can no longer act on it. - expect(await mainB.reap()).toEqual({ reclaimed: 1, deadLettered: 0 }); + expect(await mainB.reap()).toEqual({ reclaimed: 1, deadLettered: 0, missed: 0 }); // Reclaim pushes `runAt` out by the retry backoff; fast-forward past it so // the recovered occurrence is due for the next claim. @@ -189,7 +189,7 @@ describe('scheduler across two mains over one database', () => { }), ); - expect(await mainB.reap()).toEqual({ reclaimed: 0, deadLettered: 1 }); + expect(await mainB.reap()).toEqual({ reclaimed: 0, deadLettered: 1, missed: 0 }); // Never dispatched: the handler never ran on either main. expect(executedA).toHaveLength(0); @@ -201,7 +201,7 @@ describe('scheduler across two mains over one database', () => { expect(done.errorMessage).toMatch(/lease expired/i); // Terminal: a further sweep has nothing to do, and neither main re-claims it. - expect(await mainB.reap()).toEqual({ reclaimed: 0, deadLettered: 0 }); + expect(await mainB.reap()).toEqual({ reclaimed: 0, deadLettered: 0, missed: 0 }); expect(await mainA.execute()).toEqual([]); expect(await mainB.execute()).toEqual([]); }, 15_000); @@ -230,7 +230,7 @@ describe('scheduler across two mains over one database', () => { ); // A completion is a success, not a dead-letter: neither reclaimed nor dead-lettered. - expect(await mainB.reap()).toEqual({ reclaimed: 0, deadLettered: 0 }); + expect(await mainB.reap()).toEqual({ reclaimed: 0, deadLettered: 0, missed: 0 }); // Not re-run: the effect already happened. expect(executedA).toHaveLength(0); diff --git a/packages/cli/test/integration/scheduling/scheduler-query-benchmarks.test.ts b/packages/cli/test/integration/scheduling/scheduler-query-benchmarks.test.ts index 55f3c875a5f..b515cc908d7 100644 --- a/packages/cli/test/integration/scheduling/scheduler-query-benchmarks.test.ts +++ b/packages/cli/test/integration/scheduling/scheduler-query-benchmarks.test.ts @@ -1,3 +1,4 @@ +import { ScheduledJobMisfirePolicy } from '@n8n/constants'; import { testDb } from '@n8n/backend-test-utils'; import { ScheduledJob, @@ -464,6 +465,8 @@ describe.runIf(runBenchmarks)('durable scheduler query benchmarks', () => { fireAt: null, nextRunAt: secondsFromNow(3600), maxAttempts: 1, + misfirePolicy: ScheduledJobMisfirePolicy.Coalesce, + misfireGraceSeconds: 60, })); const ids = await dataSource.transaction( async (trx) => await jobRepository.insertMany(trx, newJobs), diff --git a/packages/cli/test/integration/shared/utils/index.ts b/packages/cli/test/integration/shared/utils/index.ts index 090ee7afa37..54d5621d417 100644 --- a/packages/cli/test/integration/shared/utils/index.ts +++ b/packages/cli/test/integration/shared/utils/index.ts @@ -21,7 +21,7 @@ import { ManualTrigger } from 'n8n-nodes-base/nodes/ManualTrigger/ManualTrigger. import { ScheduleTrigger } from 'n8n-nodes-base/nodes/Schedule/ScheduleTrigger.node'; import { Set } from 'n8n-nodes-base/nodes/Set/Set.node'; import { Webhook as WebhookNode } from 'n8n-nodes-base/nodes/Webhook/Webhook.node'; -import type { INodeType, INodeTypeData, INode } from 'n8n-workflow'; +import type { INodeProperties, INodeType, INodeTypeData, INode } from 'n8n-workflow'; import type request from 'supertest'; import { v4 as uuid } from 'uuid'; import { mock } from 'vitest-mock-extended'; @@ -83,6 +83,35 @@ export async function initCredentialsTypes(): Promise { }; } +/** + * A node type resolvable by name and version, and safe to publish. The empty arrays matter: + * publishing validates each node — resolving its parameters, scanning for webhooks and checking + * its credentials — and every one of those iterates a field on the description. `properties` must + * declare any parameter a test relies on: parameter resolution drops what the type doesn't declare. + */ +function minimalNodeType(name: string, properties: INodeProperties[] = []): INodeTypeData[string] { + // A plain object, not `mock`: vitest-mock-extended deep-wraps `properties` in + // proxies, and parameter dependency resolution then fails to converge on them. + return { + type: { + description: { + displayName: name, + name, + group: [], + version: 1, + description: '', + defaults: {}, + inputs: [], + outputs: [], + properties, + webhooks: [], + credentials: [], + }, + } as unknown as INodeType, + sourcePath: '', + }; +} + function buildDefaultNodes(): INodeTypeData { ScheduleTrigger.prototype.trigger = async () => ({}); return { @@ -110,20 +139,14 @@ function buildDefaultNodes(): INodeTypeData { type: mock({ description: new WebhookNode().description } as never) as INodeType, sourcePath: '', }, - // Minimal mocks for node types the package-import fixtures reference at - // typeVersion 1; import validation only needs name + version resolution. - 'n8n-nodes-base.httpRequest': { - type: mock({ - description: { name: 'n8n-nodes-base.httpRequest', version: 1 }, - } as never) as INodeType, - sourcePath: '', - }, - 'n8n-nodes-base.dataTable': { - type: mock({ - description: { name: 'n8n-nodes-base.dataTable', version: 1 }, - } as never) as INodeType, - sourcePath: '', - }, + // Minimal mocks for node types the package-import fixtures reference at typeVersion 1. + 'n8n-nodes-base.httpRequest': minimalNodeType('n8n-nodes-base.httpRequest'), + 'n8n-nodes-base.dataTable': minimalNodeType('n8n-nodes-base.dataTable'), + // `workflowId` is declared so the sub-workflow reference survives parameter resolution — + // publishing validates it, and a dropped parameter silently skips that check. + 'n8n-nodes-base.executeWorkflow': minimalNodeType('n8n-nodes-base.executeWorkflow', [ + { displayName: 'Workflow', name: 'workflowId', type: 'workflowSelector', default: '' }, + ]), }; } diff --git a/packages/cli/test/integration/workflow-history-compaction.service.test.ts b/packages/cli/test/integration/workflow-history-compaction.service.test.ts index f8538ae83d2..2ee9d54d703 100644 --- a/packages/cli/test/integration/workflow-history-compaction.service.test.ts +++ b/packages/cli/test/integration/workflow-history-compaction.service.test.ts @@ -3,9 +3,10 @@ import { GlobalConfig } from '@n8n/config'; import { Time } from '@n8n/constants'; import { DbConnection, WorkflowHistoryRepository } from '@n8n/db'; import { Container } from '@n8n/di'; +import { sleep } from '@n8n/utils/sleep'; import repeat from 'lodash/repeat'; import { InstanceSettings } from 'n8n-core'; -import { sleep, type INode } from 'n8n-workflow'; +import type { INode } from 'n8n-workflow'; import { v4 as uuid } from 'uuid'; import { EventService } from '@/events/event.service'; diff --git a/packages/cli/test/integration/workflows/workflow-publication-outbox-consumer.test.ts b/packages/cli/test/integration/workflows/workflow-publication-outbox-consumer.test.ts index f955800ee53..dbc141acd5c 100644 --- a/packages/cli/test/integration/workflows/workflow-publication-outbox-consumer.test.ts +++ b/packages/cli/test/integration/workflows/workflow-publication-outbox-consumer.test.ts @@ -269,7 +269,7 @@ describe('leader stepdown (integration)', () => { deactivator = Container.get(PublishedWorkflowTriggerDeactivator); }); - test('teardown waits for an in-flight record before deactivating triggers', async () => { + test('teardown skips a workflow with an in-flight record; the sweep converges after release', async () => { const owner = await createOwner(); const trigger = scheduleNode('running'); const workflow = await createWorkflowWithHistory({ active: true, nodes: [trigger] }, owner); @@ -288,17 +288,20 @@ describe('leader stepdown (integration)', () => { }), ); - const teardown = deactivator.deactivateAllNonWebhookTriggers(); - await new Promise((resolve) => setImmediate(resolve)); + // The instance was demoted; the stepdown teardown must neither wait on the + // held lock nor tear the workflow down without it — it skips. + Container.get(InstanceSettings).markAsFollower(); + await deactivator.deactivateAllNonWebhookTriggers(); - // Teardown is blocked on the lock, so the trigger is still running. expect(activeWorkflowTriggers.isActive(workflow.id)).toBe(true); releaseHolder(); await holder; - await teardown; - // Once the in-flight record released the lock, teardown deactivated it. + // The follower sweep converges once the lock is released. + const removed = await deactivator.sweepGhostTriggers(); + + expect(removed).toBe(1); expect(activeWorkflowTriggers.isActive(workflow.id)).toBe(false); }); }); diff --git a/packages/cli/test/integration/workflows/workflow-publication-reconciler.test.ts b/packages/cli/test/integration/workflows/workflow-publication-reconciler.test.ts index 10e1c8318b6..9730c6a4ae7 100644 --- a/packages/cli/test/integration/workflows/workflow-publication-reconciler.test.ts +++ b/packages/cli/test/integration/workflows/workflow-publication-reconciler.test.ts @@ -14,6 +14,7 @@ import { } from '@n8n/db'; import { Container } from '@n8n/di'; import { ActiveWorkflowTriggers, ExternalSecretsProxy, InstanceSettings } from 'n8n-core'; +import { ManualTrigger } from 'n8n-nodes-base/nodes/ManualTrigger/ManualTrigger.node'; import { ScheduleTrigger } from 'n8n-nodes-base/nodes/Schedule/ScheduleTrigger.node'; import type { INode, INodeTypeData } from 'n8n-workflow'; @@ -58,11 +59,21 @@ const scheduleNode = (suffix: string): INode => ({ parameters: {}, }); +const manualTriggerNode = (suffix: string): INode => ({ + id: `node-${suffix}`, + name: `Manual ${suffix}`, + type: 'n8n-nodes-base.manualTrigger', + typeVersion: 1, + position: [0, 0], + parameters: {}, +}); + beforeAll(async () => { await testDb.init(); const nodes: INodeTypeData = { 'n8n-nodes-base.scheduleTrigger': { type: new ScheduleTrigger(), sourcePath: '' }, + 'n8n-nodes-base.manualTrigger': { type: new ManualTrigger(), sourcePath: '' }, }; await utils.initNodeTypes(nodes); @@ -340,6 +351,42 @@ describe('WorkflowPublicationReconciler (integration)', () => { expect(await outboxRepository.findVersionSkewedWorkflowIds()).not.toContain(workflow.id); }); + test('records persisted rows for a pseudo-only workflow, keeping it out of leader handoff and reconciliation', async () => { + const owner = await createOwner(); + + const trigger = manualTriggerNode('pseudo'); + const workflow = await createWorkflowWithHistory({ active: true, nodes: [trigger] }, owner); + await setActiveVersion(workflow.id, workflow.versionId); + + await outboxRepository.enqueue(workflow.id, workflow.versionId); + await consumer.processRecord((await outboxRepository.claimNextPendingRecord())!); + + // Activation is untouched: a genuine publish still registers the no-op + // trigger's registry slot, and the status row (whose presence drives the + // publication status API) exists — but classified `persisted`, because the + // node is fired by the execution engine, never through the registry. + expect(activeWorkflowTriggers.get(workflow.id)?.has(trigger.id)).toBe(true); + const rows = await triggerStatusRepository.findByWorkflowId(workflow.id); + expect(rows).toHaveLength(1); + expect(rows[0]).toMatchObject({ + nodeId: trigger.id, + status: 'activated', + triggerKind: 'persisted', + }); + + // Leader handoff on a fresh leader: nothing is registered locally, but a + // pseudo-only workflow has no real trigger work to republish. + await activeWorkflowTriggers.remove(workflow.id); + await outboxRepository.enqueueForLeaderHandoff(); + expect(await outboxRepository.claimNextPendingRecord()).toBeNull(); + + // The reconciler ignores persisted rows even though the node is not + // registered — no re-enqueue loop. + await reconciler.reconcile(); + expect(await outboxRepository.claimNextPendingRecord()).toBeNull(); + expect(activeWorkflowTriggers.get(workflow.id)).toBeUndefined(); + }); + test('a pass with nothing missing enqueues no work', async () => { const owner = await createOwner(); diff --git a/packages/cli/test/integration/workflows/workflow.service.test.ts b/packages/cli/test/integration/workflows/workflow.service.test.ts index 7a89b04caf7..9a3599cad42 100644 --- a/packages/cli/test/integration/workflows/workflow.service.test.ts +++ b/packages/cli/test/integration/workflows/workflow.service.test.ts @@ -32,8 +32,10 @@ import { RoleService } from '@/services/role.service'; import { Telemetry } from '@/telemetry'; import { WebhookService } from '@/webhooks/webhook.service'; import { WorkflowHookContextService } from '@/workflow-hook-context.service'; +import { WorkflowPublishBlockedError } from '@/errors/response-errors/workflow-publish-blocked.error'; import { WorkflowFinderService } from '@/workflows/workflow-finder.service'; import { WorkflowHistoryService } from '@/workflows/workflow-history/workflow-history.service'; +import type { WorkflowPublishGuardProxy } from '@/workflows/workflow-publish-guard-proxy.service'; import { WorkflowValidationService } from '@/workflows/workflow-validation.service'; import { WorkflowService } from '@/workflows/workflow.service'; @@ -53,6 +55,7 @@ const activeWorkflowManager = mockInstance(ActiveWorkflowManager); const workflowValidationService = mockInstance(WorkflowValidationService); const nodeTypes = mockInstance(NodeTypes); const webhookServiceMock = mockInstance(WebhookService); +const workflowPublishGuard = mock(); mockInstance(MessageEventBus); mockInstance(Telemetry); @@ -95,10 +98,12 @@ beforeAll(async () => { mock(), // pollTriggerJobRegistrar workflowPublishedVersionRepository, Container.get(WorkflowHookContextService), // workflowHookContextService + workflowPublishGuard, ); }); beforeEach(() => { + workflowPublishGuard.assertCanPublish.mockResolvedValue(undefined); workflowValidationService.validateForActivation.mockReturnValue({ isValid: true }); workflowValidationService.validateDynamicCredentials.mockResolvedValue({ isValid: true }); workflowValidationService.validateSubWorkflowReferences.mockResolvedValue({ isValid: true }); @@ -124,6 +129,100 @@ afterEach(async () => { }); describe('update()', () => { + test('publishes the newly saved version when an active workflow is updated through the API', async () => { + const owner = await createOwner(); + const workflow = await createActiveWorkflow({}, owner); + const previousActiveVersionId = workflow.activeVersionId; + + const updatedWorkflow = await workflowService.update( + owner, + { + nodes: [ + { + id: 'new-node', + name: 'New Node', + type: 'n8n-nodes-base.manualTrigger', + typeVersion: 1, + position: [250, 300], + parameters: {}, + }, + ], + connections: {}, + } as WorkflowEntity, + workflow.id, + { forceSave: true, publishIfActive: true, publicApi: true, source: 'api' }, + ); + + expect(updatedWorkflow.active).toBe(true); + expect(updatedWorkflow.activeVersionId).toBe(updatedWorkflow.versionId); + expect(updatedWorkflow.activeVersionId).not.toBe(previousActiveVersionId); + expect(updatedWorkflow.activeVersion?.versionId).toBe(updatedWorkflow.versionId); + }); + + test('saves the API update as a draft when an open review blocks re-publication', async () => { + const owner = await createOwner(); + const workflow = await createActiveWorkflow({}, owner); + const previousActiveVersionId = workflow.activeVersionId; + workflowPublishGuard.assertCanPublish.mockRejectedValue( + new WorkflowPublishBlockedError({ + reason: 'review_pending', + workflowReviewRequestId: 'review-1', + }), + ); + + await expect( + workflowService.update( + owner, + { + nodes: [ + { + id: 'new-node', + name: 'New Node', + type: 'n8n-nodes-base.manualTrigger', + typeVersion: 1, + position: [250, 300], + parameters: {}, + }, + ], + connections: {}, + } as WorkflowEntity, + workflow.id, + { forceSave: true, publishIfActive: true, publicApi: true, source: 'api' }, + ), + ).rejects.toMatchObject({ + httpStatusCode: 409, + details: { + reason: 'review_pending', + workflowReviewRequestId: 'review-1', + }, + }); + + const savedWorkflow = await workflowRepository.findOneByOrFail({ id: workflow.id }); + expect(savedWorkflow.versionId).not.toBe(workflow.versionId); + expect(savedWorkflow.activeVersionId).toBe(previousActiveVersionId); + await expect( + workflowHistoryService.findVersion(workflow.id, savedWorkflow.versionId), + ).resolves.not.toBeNull(); + }); + + test('re-applies changed settings to the version that is already published', async () => { + const owner = await createOwner(); + const workflow = await createActiveWorkflow({}, owner); + const activateSpy = vi.spyOn(workflowService, 'activateWorkflow'); + + await workflowService.update( + owner, + { settings: { timezone: 'Europe/Berlin' } } as WorkflowEntity, + workflow.id, + { forceSave: true }, + ); + + expect(activateSpy).toHaveBeenCalledWith(owner, workflow.id, { + versionId: workflow.activeVersionId, + source: 'ui', + }); + }); + test('should save workflow history version with backfilled data when nodes change', async () => { const owner = await createOwner(); const workflow = await createWorkflowWithHistory({}, owner); diff --git a/packages/cli/test/migration/1785247194306-crash-stale-enqueued-executions.test.ts b/packages/cli/test/migration/1785247194306-crash-stale-enqueued-executions.test.ts new file mode 100644 index 00000000000..fa208e5fdd3 --- /dev/null +++ b/packages/cli/test/migration/1785247194306-crash-stale-enqueued-executions.test.ts @@ -0,0 +1,194 @@ +import { + createTestMigrationContext, + initDbUpToMigration, + runSingleMigration, + type TestMigrationContext, +} from '@n8n/backend-test-utils'; +import { GlobalConfig } from '@n8n/config'; +import { DbConnection } from '@n8n/db'; +import { Container } from '@n8n/di'; +import { DataSource } from '@n8n/typeorm'; +import { nanoid } from 'nanoid'; + +const MIGRATION_NAME = 'CrashStaleEnqueuedExecutions1785247194306'; + +const daysAgo = (days: number) => new Date(Date.now() - days * 24 * 60 * 60 * 1000); + +type ExecutionRow = { + status: string; + startedAt: Date | string | null; + stoppedAt: Date | string | null; + waitTill: Date | string | null; +}; + +describe('CrashStaleEnqueuedExecutions migration', () => { + let dataSource: DataSource; + let workflowId: string; + let originalExecutionMode: GlobalConfig['executions']['mode']; + + async function withContext(fn: (context: TestMigrationContext) => Promise): Promise { + const context = createTestMigrationContext(dataSource); + try { + return await fn(context); + } finally { + await context.queryRunner.release(); + } + } + + async function insertWorkflow(context: TestMigrationContext) { + const table = context.escape.tableName('workflow_entity'); + const c = (name: string) => context.escape.columnName(name); + const id = nanoid(16); + + await context.runQuery( + `INSERT INTO ${table} (${c('id')}, ${c('name')}, ${c('active')}, ${c('nodes')}, + ${c('connections')}, ${c('createdAt')}, ${c('updatedAt')}, ${c('triggerCount')}, ${c('versionId')}) + VALUES (:id, :name, :active, :nodes, :connections, :createdAt, :updatedAt, :triggerCount, :versionId)`, + { + id, + name: 'wf', + active: false, + nodes: '[]', + connections: '{}', + createdAt: new Date(), + updatedAt: new Date(), + triggerCount: 0, + versionId: nanoid(), + }, + ); + + return id; + } + + async function insertExecution( + context: TestMigrationContext, + row: { status: string; createdAt: Date; waitTill?: Date }, + ) { + const table = context.escape.tableName('execution_entity'); + const c = (name: string) => context.escape.columnName(name); + + await context.runQuery( + `INSERT INTO ${table} (${c('finished')}, ${c('mode')}, ${c('status')}, + ${c('createdAt')}, ${c('startedAt')}, ${c('waitTill')}, ${c('workflowId')}) + VALUES (:finished, :mode, :status, :createdAt, NULL, :waitTill, :workflowId)`, + { + finished: false, + mode: 'trigger', + status: row.status, + createdAt: row.createdAt, + waitTill: row.waitTill ?? null, + workflowId, + }, + ); + } + + /** Ordered by `createdAt` so rows come back in the order the test seeded them. */ + async function fetchExecutions(context: TestMigrationContext) { + const table = context.escape.tableName('execution_entity'); + const c = (name: string) => context.escape.columnName(name); + + return await context.runQuery( + `SELECT ${c('status')}, ${c('startedAt')}, ${c('stoppedAt')}, ${c('waitTill')} + FROM ${table} ORDER BY ${c('createdAt')}`, + ); + } + + beforeAll(async () => { + const dbConnection = Container.get(DbConnection); + await dbConnection.init(); + dataSource = Container.get(DataSource); + originalExecutionMode = Container.get(GlobalConfig).executions.mode; + }); + + beforeEach(async () => { + Container.get(GlobalConfig).executions.mode = 'regular'; + await withContext(async (context) => { + await context.queryRunner.clearDatabase(); + }); + await initDbUpToMigration(MIGRATION_NAME); + workflowId = await withContext(insertWorkflow); + }); + + afterAll(async () => { + Container.get(GlobalConfig).executions.mode = originalExecutionMode; + const dbConnection = Container.get(DbConnection); + await dbConnection.close(); + }); + + test('crashes an execution enqueued before the cutoff', async () => { + await withContext(async (context) => { + await insertExecution(context, { + status: 'new', + createdAt: daysAgo(8), + waitTill: daysAgo(7), + }); + }); + + await runSingleMigration(MIGRATION_NAME); + + const [execution] = await withContext(fetchExecutions); + expect(execution.status).toBe('crashed'); + expect(execution.stoppedAt).not.toBeNull(); + // it never ran, and nothing should be able to pick it back up + expect(execution.startedAt).toBeNull(); + expect(execution.waitTill).toBeNull(); + }); + + test('leaves an execution enqueued after the cutoff untouched', async () => { + await withContext(async (context) => { + await insertExecution(context, { status: 'new', createdAt: daysAgo(8) }); + await insertExecution(context, { status: 'new', createdAt: daysAgo(6) }); + }); + + await runSingleMigration(MIGRATION_NAME); + + const [stale, recent] = await withContext(fetchExecutions); + expect(stale.status).toBe('crashed'); + expect(recent.status).toBe('new'); + expect(recent.stoppedAt).toBeNull(); + }); + + test('leaves stale enqueued executions untouched in queue mode', async () => { + Container.get(GlobalConfig).executions.mode = 'queue'; + await withContext(async (context) => { + await insertExecution(context, { + status: 'new', + createdAt: daysAgo(8), + waitTill: daysAgo(7), + }); + }); + + await runSingleMigration(MIGRATION_NAME); + + const [execution] = await withContext(fetchExecutions); + expect(execution.status).toBe('new'); + expect(execution.stoppedAt).toBeNull(); + expect(execution.waitTill).not.toBeNull(); + }); + + test.each(['waiting', 'running', 'success', 'error'])( + 'leaves an old %s execution untouched', + async (status) => { + await withContext(async (context) => { + await insertExecution(context, { status, createdAt: daysAgo(8) }); + }); + + await runSingleMigration(MIGRATION_NAME); + + const [execution] = await withContext(fetchExecutions); + expect(execution.status).toBe(status); + expect(execution.stoppedAt).toBeNull(); + }, + ); + + test('is a no-op when there is nothing enqueued', async () => { + await withContext(async (context) => { + await insertExecution(context, { status: 'success', createdAt: daysAgo(8) }); + }); + + await expect(runSingleMigration(MIGRATION_NAME)).resolves.not.toThrow(); + + const executions = await withContext(fetchExecutions); + expect(executions).toHaveLength(1); + }); +}); diff --git a/packages/cli/test/migration/1785247194307-add-misfire-policy-to-scheduler.test.ts b/packages/cli/test/migration/1785247194307-add-misfire-policy-to-scheduler.test.ts new file mode 100644 index 00000000000..29ec430026b --- /dev/null +++ b/packages/cli/test/migration/1785247194307-add-misfire-policy-to-scheduler.test.ts @@ -0,0 +1,177 @@ +import { + createTestMigrationContext, + initDbUpToMigration, + runSingleMigration, + undoLastSingleMigration, + type TestMigrationContext, +} from '@n8n/backend-test-utils'; +import { DbConnection } from '@n8n/db'; +import { Container } from '@n8n/di'; +import { DataSource } from '@n8n/typeorm'; + +const MIGRATION_NAME = 'AddMisfirePolicyToScheduler1785247194307'; + +describe('AddMisfirePolicyToScheduler Migration', () => { + let dataSource: DataSource; + + beforeAll(async () => { + const dbConnection = Container.get(DbConnection); + await dbConnection.init(); + dataSource = Container.get(DataSource); + }); + + beforeEach(async () => { + const context = createTestMigrationContext(dataSource); + await context.queryRunner.clearDatabase(); + await context.queryRunner.release(); + await initDbUpToMigration(MIGRATION_NAME); + }); + + afterAll(async () => { + const dbConnection = Container.get(DbConnection); + await dbConnection.close(); + }); + + async function columnNames(context: TestMigrationContext, table: string): Promise { + if (context.isSqlite) { + const rows = (await context.queryRunner.query( + `PRAGMA table_info(${context.escape.tableName(table)})`, + )) as Array<{ name: string }>; + return rows.map((row) => row.name); + } + const rows = (await context.queryRunner.query( + 'SELECT column_name FROM information_schema.columns WHERE table_name = $1', + [`${context.tablePrefix}${table}`], + )) as Array<{ column_name: string }>; + return rows.map((row) => row.column_name); + } + + async function indexedColumns(context: TestMigrationContext): Promise { + const table = `${context.tablePrefix}scheduled_task`; + if (context.isSqlite) { + const rows = (await context.queryRunner.query( + `SELECT sql FROM sqlite_master WHERE type = 'index' AND tbl_name = '${table}'`, + )) as Array<{ sql: string | null }>; + return rows.map((row) => row.sql ?? ''); + } + const rows = (await context.queryRunner.query( + 'SELECT indexdef FROM pg_indexes WHERE tablename = $1', + [table], + )) as Array<{ indexdef: string }>; + return rows.map((row) => row.indexdef); + } + + describe('up', () => { + it('adds the policy columns to the job and the deadline to the task', async () => { + await runSingleMigration(MIGRATION_NAME); + const context = createTestMigrationContext(dataSource); + + expect(await columnNames(context, 'scheduled_job')).toEqual( + expect.arrayContaining(['misfirePolicy', 'misfireGraceSeconds']), + ); + expect(await columnNames(context, 'scheduled_task')).toContain('missedAfter'); + + await context.queryRunner.release(); + }); + + it('defaults existing jobs to coalesce, so no row needs a backfill', async () => { + const before = createTestMigrationContext(dataSource); + await before.runQuery( + `INSERT INTO ${before.escape.tableName('scheduled_job')} + ("name", "kind", "intervalSeconds", "taskType", "createdAt", "updatedAt") + VALUES ('existing', 'interval', 60, 'test', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)`, + ); + await before.queryRunner.release(); + + await runSingleMigration(MIGRATION_NAME); + + const context = createTestMigrationContext(dataSource); + const [row] = (await context.queryRunner.query( + `SELECT "misfirePolicy", "misfireGraceSeconds" FROM ${context.escape.tableName('scheduled_job')}`, + )) as Array<{ misfirePolicy: string; misfireGraceSeconds: number }>; + + expect(row.misfirePolicy).toBe('coalesce'); + expect(Number(row.misfireGraceSeconds)).toBe(60); + + await context.queryRunner.release(); + }); + + it('is a no-op when replayed under a different recorded migration name', async () => { + await runSingleMigration(MIGRATION_NAME); + const context = createTestMigrationContext(dataSource); + // Deleting the migrations-table row, with the columns left in place, is + // what a rename looks like from the next run's perspective. + await context.runQuery( + `DELETE FROM ${context.escape.tableName('migrations')} WHERE "name" = '${MIGRATION_NAME}'`, + ); + await context.queryRunner.release(); + + await expect(runSingleMigration(MIGRATION_NAME)).resolves.not.toThrow(); + + const after = createTestMigrationContext(dataSource); + expect(await columnNames(after, 'scheduled_job')).toEqual( + expect.arrayContaining(['misfirePolicy', 'misfireGraceSeconds']), + ); + await after.queryRunner.release(); + }); + + it('indexes the deadline over the pending rows the reaper sweeps', async () => { + await runSingleMigration(MIGRATION_NAME); + const context = createTestMigrationContext(dataSource); + + const definitions = await indexedColumns(context); + expect( + definitions.some((sql) => sql.includes('missedAfter') && sql.includes('pending')), + ).toBe(true); + + await context.queryRunner.release(); + }); + }); + + describe('down', () => { + it('removes every column it added', async () => { + await runSingleMigration(MIGRATION_NAME); + await undoLastSingleMigration(); + + const context = createTestMigrationContext(dataSource); + const jobColumns = await columnNames(context, 'scheduled_job'); + + expect(jobColumns).not.toContain('misfirePolicy'); + expect(jobColumns).not.toContain('misfireGraceSeconds'); + expect(await columnNames(context, 'scheduled_task')).not.toContain('missedAfter'); + + await context.queryRunner.release(); + }); + + it('keeps queued occurrences, which the job table rebuild would cascade away', async () => { + await runSingleMigration(MIGRATION_NAME); + + const seed = createTestMigrationContext(dataSource); + await seed.runQuery( + `INSERT INTO ${seed.escape.tableName('scheduled_job')} + ("name", "kind", "intervalSeconds", "taskType", "createdAt", "updatedAt") + VALUES ('queued', 'interval', 60, 'test', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)`, + ); + const [job] = (await seed.queryRunner.query( + `SELECT "id" FROM ${seed.escape.tableName('scheduled_job')}`, + )) as Array<{ id: number }>; + await seed.runQuery( + `INSERT INTO ${seed.escape.tableName('scheduled_task')} + ("jobId", "taskType", "scheduledFor", "runAt", "createdAt") + VALUES (${job.id}, 'test', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)`, + ); + await seed.queryRunner.release(); + + await undoLastSingleMigration(); + + const context = createTestMigrationContext(dataSource); + const [{ count }] = (await context.queryRunner.query( + `SELECT COUNT(*) AS "count" FROM ${context.escape.tableName('scheduled_task')}`, + )) as Array<{ count: number | string }>; + + expect(Number(count)).toBe(1); + + await context.queryRunner.release(); + }); + }); +}); diff --git a/packages/cli/test/migration/1785255306000-create-agent-chat-attachments-table.test.ts b/packages/cli/test/migration/1785255306000-create-agent-chat-attachments-table.test.ts new file mode 100644 index 00000000000..7c9a2e6351b --- /dev/null +++ b/packages/cli/test/migration/1785255306000-create-agent-chat-attachments-table.test.ts @@ -0,0 +1,201 @@ +import { + createTestMigrationContext, + initDbUpToMigration, + runSingleMigration, + type TestMigrationContext, +} from '@n8n/backend-test-utils'; +import { DbConnection } from '@n8n/db'; +import { Container } from '@n8n/di'; +import { DataSource } from '@n8n/typeorm'; +import { randomUUID } from 'node:crypto'; + +const MIGRATION_NAME = 'CreateAgentChatAttachmentsTable1785255306000'; + +describe('CreateAgentChatAttachmentsTable Migration', () => { + let dataSource: DataSource; + + beforeAll(async () => { + const dbConnection = Container.get(DbConnection); + await dbConnection.init(); + dataSource = Container.get(DataSource); + + const context = createTestMigrationContext(dataSource); + await context.queryRunner.clearDatabase(); + await context.queryRunner.release(); + + await initDbUpToMigration(MIGRATION_NAME); + await runSingleMigration(MIGRATION_NAME); + }); + + afterAll(async () => { + await Container.get(DbConnection).close(); + }); + + async function insertProject(context: TestMigrationContext): Promise { + const projectId = randomUUID().slice(0, 36); + const now = new Date(); + await context.runQuery( + `INSERT INTO ${context.escape.tableName('project')} ("id", "name", "type", "createdAt", "updatedAt") + VALUES (:id, :name, :type, :createdAt, :updatedAt)`, + { id: projectId, name: `Project ${projectId}`, type: 'team', createdAt: now, updatedAt: now }, + ); + return projectId; + } + + async function insertAgent(context: TestMigrationContext, projectId: string): Promise { + const agentId = randomUUID().slice(0, 36); + const now = new Date(); + await context.runQuery( + `INSERT INTO ${context.escape.tableName('agents')} ("id", "name", "projectId", "integrations", "tools", "skills", "createdAt", "updatedAt") + VALUES (:id, :name, :projectId, :integrations, :tools, :skills, :createdAt, :updatedAt)`, + { + id: agentId, + name: `Agent ${agentId}`, + projectId, + integrations: '[]', + tools: '{}', + skills: '{}', + createdAt: now, + updatedAt: now, + }, + ); + return agentId; + } + + async function insertAttachment( + context: TestMigrationContext, + overrides: { agentId?: string | null; projectId: string }, + ): Promise { + const id = randomUUID().slice(0, 16); + const now = new Date(); + await context.runQuery( + `INSERT INTO ${context.escape.tableName('agent_chat_attachments')} + ("id", "agentId", "projectId", "threadId", "binaryDataId", "fileName", "mimeType", "fileSizeBytes", "source", "createdAt", "updatedAt") + VALUES (:id, :agentId, :projectId, :threadId, :binaryDataId, :fileName, :mimeType, :fileSizeBytes, :source, :createdAt, :updatedAt)`, + { + id, + agentId: overrides.agentId ?? null, + projectId: overrides.projectId, + threadId: 'thread-1', + binaryDataId: 'filesystem-v2:agent-chat-attachments/x/y', + fileName: 'photo.png', + mimeType: 'image/png', + fileSizeBytes: 33, + source: 'chat', + createdAt: now, + updatedAt: now, + }, + ); + return id; + } + + describe('Up migration', () => { + it('creates the agent_chat_attachments table', async () => { + const context = createTestMigrationContext(dataSource); + const rows = await context.runQuery( + `SELECT * FROM ${context.escape.tableName('agent_chat_attachments')}`, + ); + expect(rows).toEqual([]); + await context.queryRunner.release(); + }); + + it('accepts rows without an agent (inline-agent scope) but requires a project', async () => { + const context = createTestMigrationContext(dataSource); + const projectId = await insertProject(context); + + await expect(insertAttachment(context, { agentId: null, projectId })).resolves.toBeTruthy(); + await expect( + insertAttachment(context, { agentId: null, projectId: randomUUID().slice(0, 36) }), + ).rejects.toThrow(); + await context.queryRunner.release(); + }); + + it('cascades deletes from agents and project', async () => { + const context = createTestMigrationContext(dataSource); + const table = context.escape.tableName('agent_chat_attachments'); + + const projectId = await insertProject(context); + const agentId = await insertAgent(context, projectId); + const agentScoped = await insertAttachment(context, { agentId, projectId }); + const projectScoped = await insertAttachment(context, { agentId: null, projectId }); + + await context.runQuery(`DELETE FROM ${context.escape.tableName('agents')} WHERE "id" = :id`, { + id: agentId, + }); + let remaining = await context.runQuery(`SELECT * FROM ${table} WHERE "id" = :id`, { + id: agentScoped, + }); + expect(remaining).toEqual([]); + + await context.runQuery( + `DELETE FROM ${context.escape.tableName('project')} WHERE "id" = :id`, + { id: projectId }, + ); + remaining = await context.runQuery(`SELECT * FROM ${table} WHERE "id" = :id`, { + id: projectScoped, + }); + expect(remaining).toEqual([]); + await context.queryRunner.release(); + }); + + it('adds a nullable attachments column to agent_execution', async () => { + const context = createTestMigrationContext(dataSource); + try { + const table = await context.queryRunner.getTable(`${context.tablePrefix}agent_execution`); + const column = table?.columns.find((c) => c.name === 'attachments'); + expect(column).toBeDefined(); + expect(column?.isNullable).toBe(true); + } finally { + await context.queryRunner.release(); + } + }); + + it('extends the binary_data sourceType check with agent_chat_attachment', async () => { + const context = createTestMigrationContext(dataSource); + const table = context.escape.tableName('binary_data'); + const now = new Date(); + const insert = async (sourceType: string) => + await context.runQuery( + `INSERT INTO ${table} ("fileId", "sourceType", "sourceId", "data", "mimeType", "fileName", "fileSize", "createdAt", "updatedAt") + VALUES (:fileId, :sourceType, :sourceId, :data, :mimeType, :fileName, :fileSize, :createdAt, :updatedAt)`, + { + fileId: randomUUID(), + sourceType, + sourceId: 'att-1', + data: Buffer.from([1]), + mimeType: 'image/png', + fileName: 'photo.png', + fileSize: 1, + createdAt: now, + updatedAt: now, + }, + ); + + await expect(insert('agent_chat_attachment')).resolves.not.toThrow(); + await expect(insert('bogus_source')).rejects.toThrow(); + await context.queryRunner.release(); + }); + }); + + describe('Down migration', () => { + it('drops the table, restores the check, and can be re-applied', async () => { + await dataSource.undoLastMigration({ transaction: 'each' }); + + const context = createTestMigrationContext(dataSource); + const tableName = `${context.tablePrefix}agent_chat_attachments`; + expect(await context.queryRunner.hasTable(tableName)).toBe(false); + const executionTable = await context.queryRunner.getTable( + `${context.tablePrefix}agent_execution`, + ); + expect(executionTable?.columns.some((c) => c.name === 'attachments')).toBe(false); + + await context.queryRunner.release(); + + // Round-trip: up() must run cleanly again after a full revert. + await runSingleMigration(MIGRATION_NAME); + const context2 = createTestMigrationContext(dataSource); + expect(await context2.queryRunner.hasTable(tableName)).toBe(true); + await context2.queryRunner.release(); + }); + }); +}); diff --git a/packages/core/src/errors/__tests__/error-reporter.test.ts b/packages/core/src/errors/__tests__/error-reporter.test.ts index a0a6e0c8add..aae7dcf9074 100644 --- a/packages/core/src/errors/__tests__/error-reporter.test.ts +++ b/packages/core/src/errors/__tests__/error-reporter.test.ts @@ -389,6 +389,16 @@ describe('ErrorReporter', () => { expect(logger.error).toHaveBeenNthCalledWith(1, `outer\n${outer.stack}\n`, undefined); expect(logger.error).toHaveBeenNthCalledWith(2, `root cause\n${cause.stack}\n`, undefined); }); + + it('should stop walking a cause chain that loops back on itself', () => { + const outer = new Error('outer'); + const inner = new Error('inner', { cause: outer }); + outer.cause = inner; + + errorReporter.error(outer); + + expect(logger.error).toHaveBeenCalledTimes(2); + }); }); describe('error with Sentry initialized', () => { diff --git a/packages/core/src/errors/error-reporter.ts b/packages/core/src/errors/error-reporter.ts index c5201e1201b..fab06d71e4c 100644 --- a/packages/core/src/errors/error-reporter.ts +++ b/packages/core/src/errors/error-reporter.ts @@ -130,7 +130,11 @@ export class ErrorReporter { const { executionId } = options ?? {}; const context = executionId ? ` (execution ${executionId})` : ''; + // Prevent infinite loops in cyclic cause chains. + const seen = new Set(); + do { + seen.add(e); let stack = ''; let meta = undefined; if (e instanceof ApplicationError || e instanceof BaseError) { @@ -147,7 +151,7 @@ export class ErrorReporter { this.logger.error(msg, meta); } e = e.cause as Error; - } while (e); + } while (e && !seen.has(e)); } } diff --git a/packages/core/src/execution-engine/__tests__/active-workflow-triggers.test.ts b/packages/core/src/execution-engine/__tests__/active-workflow-triggers.test.ts index 0922233b16a..e078843abea 100644 --- a/packages/core/src/execution-engine/__tests__/active-workflow-triggers.test.ts +++ b/packages/core/src/execution-engine/__tests__/active-workflow-triggers.test.ts @@ -12,7 +12,12 @@ import type { TriggerTime, CronExpression, } from 'n8n-workflow'; -import { LoggerProxy, TriggerCloseError, WorkflowActivationError } from 'n8n-workflow'; +import { + LoggerProxy, + TriggerCloseError, + WorkflowActivationError, + WorkflowDeactivationError, +} from 'n8n-workflow'; import type { Mock } from 'vitest'; import { mock } from 'vitest-mock-extended'; @@ -783,6 +788,8 @@ describe('ActiveWorkflowTriggers', () => { }); it('should return false when removing non-existent workflow', async () => { + scheduledTaskManager.deregisterGroup.mockReturnValue(false); + const result = await activeWorkflowTriggers.remove('non-existent'); expect(result).toBe(false); @@ -828,6 +835,87 @@ describe('ActiveWorkflowTriggers', () => { expect(triggerResponse.closeFunction).toHaveBeenCalled(); expect(errorReporter.error).not.toHaveBeenCalled(); }); + + describe('best-effort teardown', () => { + const nodeA = mock({ id: 'trigger-a' }); + const nodeB = mock({ id: 'trigger-b' }); + + it('attempts every close and keeps only the failed nodes tracked', async () => { + const failingResponse = mock(); + const healthyResponse = mock(); + (failingResponse.closeFunction as Mock).mockRejectedValue(new Error('close failed')); + triggersAndPollers.runTriggerFunction + .mockResolvedValueOnce(failingResponse) + .mockResolvedValueOnce(healthyResponse); + await addWorkflow({ triggerNodes: [nodeA, nodeB] }); + + await expect(activeWorkflowTriggers.remove(workflowId)).rejects.toThrow( + WorkflowDeactivationError, + ); + + // The failure must not stop the remaining cleanup. + expect(healthyResponse.closeFunction).toHaveBeenCalledTimes(1); + // The failed node stays tracked: dropping it would hide a possibly + // still-live trigger from every later teardown and sweep. + expect(activeWorkflowTriggers.getRegisteredTriggerNodeIds(workflowId)).toEqual( + new Set([nodeA.id]), + ); + expect(activeWorkflowTriggers.isActive(workflowId)).toBe(true); + }); + + it('reports every failure except the last, which is thrown', async () => { + // With several permanent failures, throwing only the last would swallow + // the others forever — each retry pass overwrites them again. + const failingA = mock(); + const failingB = mock(); + (failingA.closeFunction as Mock).mockRejectedValue(new Error('close A failed')); + (failingB.closeFunction as Mock).mockRejectedValue(new Error('close B failed')); + triggersAndPollers.runTriggerFunction + .mockResolvedValueOnce(failingA) + .mockResolvedValueOnce(failingB); + await addWorkflow({ triggerNodes: [nodeA, nodeB] }); + + await expect(activeWorkflowTriggers.remove(workflowId)).rejects.toThrow('close B failed'); + + // The first failure is reported directly; the last is the thrown one, + // reported by the caller — every failure surfaces exactly once. + expect(errorReporter.error).toHaveBeenCalledTimes(1); + expect(errorReporter.error).toHaveBeenCalledWith( + expect.objectContaining({ message: expect.stringContaining('close A failed') }), + expect.anything(), + ); + // Both nodes stay tracked for the next pass. + expect(activeWorkflowTriggers.getRegisteredTriggerNodeIds(workflowId)).toEqual( + new Set([nodeA.id, nodeB.id]), + ); + }); + + it('retries only the still-tracked nodes and converges once the close succeeds', async () => { + const flakyResponse = mock(); + const healthyResponse = mock(); + (flakyResponse.closeFunction as Mock) + .mockRejectedValueOnce(new Error('close failed')) + .mockResolvedValueOnce(undefined); + triggersAndPollers.runTriggerFunction + .mockResolvedValueOnce(flakyResponse) + .mockResolvedValueOnce(healthyResponse); + await addWorkflow({ triggerNodes: [nodeA, nodeB] }); + + await expect(activeWorkflowTriggers.remove(workflowId)).rejects.toThrow( + WorkflowDeactivationError, + ); + // The healthy trigger was already closed by the first, failing attempt … + expect(healthyResponse.closeFunction).toHaveBeenCalledTimes(1); + + const result = await activeWorkflowTriggers.remove(workflowId); + + expect(result).toBe(true); + expect(activeWorkflowTriggers.isActive(workflowId)).toBe(false); + // … and must not be closed a second time by the retry. + expect(healthyResponse.closeFunction).toHaveBeenCalledTimes(1); + expect(flakyResponse.closeFunction).toHaveBeenCalledTimes(2); + }); + }); }); describe('get() and isActive()', () => { @@ -1211,16 +1299,17 @@ describe('ActiveWorkflowTriggers', () => { vi.useRealTimers(); }); - it('should stop a stranded cron on remove even when the workflow is not active in memory', async () => { + it('should stop a stranded cron on remove and report it as removed even when the workflow is not active in memory', async () => { // A cron registered in the ScheduledTaskManager while the workflow is not - // tracked as active in memory must still be stoppable via remove(). + // tracked as active in memory must still be stoppable via remove(), and + // counts as a removal so callers (e.g. the follower ghost sweep) see it. registerStrandedCron(workflowId); expect(realScheduledTaskManager.hasGroup(workflowGroup())).toBe(true); expect(activeWorkflowTriggersReal.isActive(workflowId)).toBe(false); const result = await activeWorkflowTriggersReal.remove(workflowId); - expect(result).toBe(false); + expect(result).toBe(true); expect(realScheduledTaskManager.hasGroup(workflowGroup())).toBe(false); }); diff --git a/packages/core/src/execution-engine/__tests__/eval-mock-fixtures.test.ts b/packages/core/src/execution-engine/__tests__/eval-mock-fixtures.test.ts index 458208b0505..fe2555da044 100644 --- a/packages/core/src/execution-engine/__tests__/eval-mock-fixtures.test.ts +++ b/packages/core/src/execution-engine/__tests__/eval-mock-fixtures.test.ts @@ -1,6 +1,7 @@ import { fileTypeFromBuffer } from 'file-type'; +import { inflateSync } from 'node:zlib'; -import { synthesizeBinaryFixture } from '../eval-mock-fixtures'; +import { buildPdfWithText, evalCanvasPng, synthesizeBinaryFixture } from '../eval-mock-fixtures'; describe('eval-mock-fixtures', () => { describe('synthesizeBinaryFixture', () => { @@ -208,4 +209,56 @@ describe('eval-mock-fixtures', () => { }); }); }); + + describe('evalCanvasPng', () => { + it('produces a sniffable 512×512 PNG with a full pixel raster', async () => { + const png = evalCanvasPng(); + expect((await fileTypeFromBuffer(png))?.mime).toBe('image/png'); + // IHDR width/height sit at fixed offsets after the 8-byte signature. + expect(png.readUInt32BE(16)).toBe(512); + expect(png.readUInt32BE(20)).toBe(512); + // IDAT payload inflates to 512 scanlines of (filter byte + 512 px). + const idatOffset = png.indexOf('IDAT'); + const idatLength = png.readUInt32BE(idatOffset - 4); + const raster = inflateSync(png.subarray(idatOffset + 4, idatOffset + 4 + idatLength)); + expect(raster.length).toBe(512 * 513); + }); + + it('returns the cached buffer on repeat calls', () => { + expect(evalCanvasPng()).toBe(evalCanvasPng()); + }); + }); + + describe('buildPdfWithText', () => { + it('embeds each line as an escaped text run in a %PDF...%%EOF document', () => { + const pdf = buildPdfWithText('Line one\nTotal (net) 50% \\ done').toString('latin1'); + expect(pdf.startsWith('%PDF-1.4')).toBe(true); + expect(pdf.trimEnd().endsWith('%%EOF')).toBe(true); + expect(pdf).toContain('(Line one) Tj'); + expect(pdf).toContain('(Total \\(net\\) 50% \\\\ done) Tj'); + }); + + it('computes xref offsets that point at the object headers', () => { + const pdf = buildPdfWithText('anything').toString('latin1'); + const offsets = [...pdf.matchAll(/^(\d{10}) 00000 n /gm)].map((m) => Number(m[1])); + expect(offsets).toHaveLength(5); + offsets.forEach((offset, index) => { + expect(pdf.slice(offset, offset + `${String(index + 1)} 0 obj`.length)).toBe( + `${String(index + 1)} 0 obj`, + ); + }); + const startxref = Number(/startxref\n(\d+)/.exec(pdf)?.[1]); + expect(pdf.slice(startxref, startxref + 4)).toBe('xref'); + }); + + it('replaces non-ASCII characters and skips blank lines', () => { + const pdf = buildPdfWithText('café £100\n\n \nnext').toString('latin1'); + expect(pdf).toContain('(caf? ?100) Tj'); + expect(pdf).toContain('(next) Tj'); + }); + + it('falls back to a stub line for empty text', () => { + expect(buildPdfWithText(' \n \n').toString('latin1')).toContain('(Mock PDF document) Tj'); + }); + }); }); diff --git a/packages/core/src/execution-engine/__tests__/routing-node.test.ts b/packages/core/src/execution-engine/__tests__/routing-node.test.ts index cd553dc542f..f43ac27be45 100644 --- a/packages/core/src/execution-engine/__tests__/routing-node.test.ts +++ b/packages/core/src/execution-engine/__tests__/routing-node.test.ts @@ -2337,8 +2337,8 @@ describe('RoutingNode', () => { } } - const workflowPackage = await import('n8n-workflow'); - const spy = vi.spyOn(workflowPackage, 'sleep').mockReturnValue( + const sleepModule = await import('@n8n/utils/sleep'); + const spy = vi.spyOn(sleepModule, 'sleep').mockReturnValue( new Promise((resolve) => { resolve(); }), diff --git a/packages/core/src/execution-engine/active-workflow-triggers.ts b/packages/core/src/execution-engine/active-workflow-triggers.ts index f9d418b7601..2c3f73bd6ca 100644 --- a/packages/core/src/execution-engine/active-workflow-triggers.ts +++ b/packages/core/src/execution-engine/active-workflow-triggers.ts @@ -446,7 +446,9 @@ export class ActiveWorkflowTriggers { } /** - * Makes a workflow inactive in memory. + * Makes a workflow inactive in memory. Returns whether anything was + * removed — tracked trigger registrations or stranded orphan crons alike — + * so callers can tell "found and removed something" from "nothing was there". */ async remove(workflowId: string) { // Ensure crons are deregistered to prevent executions on inactive workflows @@ -463,12 +465,33 @@ export class ActiveWorkflowTriggers { ); } - return false; + return hadRegisteredCrons; } const triggers = this.activeTriggersByWorkflowId.get(workflowId); - for (const r of triggers?.triggerResponses() ?? []) { - await this.closeTrigger(r, workflowId); + if (!triggers) return hadRegisteredCrons; + + const errors: Error[] = []; + + // Best-effort: every close is attempted; a failing close keeps its node + // tracked (a possibly still-live trigger must stay visible to later + // teardown passes) while the rest of the cleanup proceeds. + for (const { nodeId, response } of triggers.closableTriggers()) { + try { + await this.closeTrigger(response, workflowId); + triggers.delete(nodeId); + } catch (error) { + errors.push(ensureError(error)); + } + } + + const toThrow = errors.pop(); + for (const error of errors) { + this.errorReporter.error(error, { shouldBeLogged: true }); + } + + if (toThrow) { + throw toThrow; } this.activeTriggersByWorkflowId.delete(workflowId); diff --git a/packages/core/src/execution-engine/eval-mock-fixtures.ts b/packages/core/src/execution-engine/eval-mock-fixtures.ts index fb96cd9bb65..727710ae3b9 100644 --- a/packages/core/src/execution-engine/eval-mock-fixtures.ts +++ b/packages/core/src/execution-engine/eval-mock-fixtures.ts @@ -13,6 +13,8 @@ * supply binary input items to upload nodes that read `$binary.data`. */ +import { deflateSync } from 'node:zlib'; + export type FixtureSizeHint = 'small' | 'medium' | 'large'; export interface SynthesizeBinaryFixtureOptions { @@ -208,6 +210,108 @@ const MP4_FTYP = Buffer.from([ 0x6d, // compat brand "isom" ]); +// --------------------------------------------------------------------------- +// Drawable canvas PNG + text-bearing PDF — for content the workflow PROCESSES +// (Edit Image, Extract PDF Text), where a magic-bytes-only fixture crashes the +// real decoder downstream. +// --------------------------------------------------------------------------- + +/** CRC32 over a PNG chunk's type+data — bitwise, no lookup table needed here. */ +function pngCrc32(buf: Buffer): number { + let crc = 0xffffffff; + for (const byte of buf) { + crc ^= byte; + for (let bit = 0; bit < 8; bit++) crc = (crc >>> 1) ^ (0xedb88320 & -(crc & 1)); + } + return (crc ^ 0xffffffff) >>> 0; +} + +function pngChunk(type: string, data: Buffer): Buffer { + const length = Buffer.alloc(4); + length.writeUInt32BE(data.length); + const typeAndData = Buffer.concat([Buffer.from(type, 'latin1'), data]); + const crc = Buffer.alloc(4); + crc.writeUInt32BE(pngCrc32(typeAndData)); + return Buffer.concat([length, typeAndData, crc]); +} + +const CANVAS_PNG_SIZE = 512; +let canvasPngCache: Buffer | undefined; + +/** + * 512×512 opaque white PNG (8-bit grayscale), built programmatically so it is + * always structurally valid. Unlike `PNG_1X1`, this gives image-PROCESSING + * nodes (Edit Image text overlay, resize, composite via GraphicsMagick) a real + * canvas to operate on — a 1×1 or truncated placeholder makes them fail with + * "Insufficient image data". + */ +export function evalCanvasPng(): Buffer { + if (canvasPngCache) return canvasPngCache; + const ihdr = Buffer.alloc(13); + ihdr.writeUInt32BE(CANVAS_PNG_SIZE, 0); + ihdr.writeUInt32BE(CANVAS_PNG_SIZE, 4); + ihdr[8] = 8; // bit depth + ihdr[9] = 0; // color type: grayscale (bytes 10-12: compression/filter/interlace = 0) + // Each scanline: 1 filter byte (0 = None) + 512 white pixels. + const scanlines = Buffer.alloc(CANVAS_PNG_SIZE * (CANVAS_PNG_SIZE + 1), 0xff); + for (let y = 0; y < CANVAS_PNG_SIZE; y++) scanlines[y * (CANVAS_PNG_SIZE + 1)] = 0; + canvasPngCache = Buffer.concat([ + Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]), + pngChunk('IHDR', ihdr), + pngChunk('IDAT', deflateSync(scanlines)), + pngChunk('IEND', Buffer.alloc(0)), + ]); + return canvasPngCache; +} + +const PDF_TEXT_MAX_LINES = 40; + +/** + * Build a valid one-page PDF whose extractable text is `text` (ASCII-printable + * subset; other characters become `?`). The xref offsets are computed, so + * strict parsers (pdfjs-dist behind Extract From File) accept it — LLM-authored + * "PDF bytes" never parse, so mock layers substitute these and keep the + * scenario's document CONTENT model-authored as plaintext. + */ +export function buildPdfWithText(text: string): Buffer { + const lines = text + .split(/\r?\n/) + .map((line) => line.replace(/[^\x20-\x7e]/g, '?').trimEnd()) + .filter((line) => line.length > 0) + .slice(0, PDF_TEXT_MAX_LINES); + if (lines.length === 0) lines.push('Mock PDF document'); + + const escapePdfString = (value: string) => + value.replace(/\\/g, '\\\\').replace(/\(/g, '\\(').replace(/\)/g, '\\)'); + const contentParts = ['BT', '/F1 12 Tf', '14 TL', '50 780 Td']; + lines.forEach((line, index) => { + if (index > 0) contentParts.push('T*'); + contentParts.push(`(${escapePdfString(line)}) Tj`); + }); + contentParts.push('ET'); + const contentStream = contentParts.join('\n'); + + const objects = [ + '<>', + '<>', + '<>>>/Contents 5 0 R>>', + '<>', + `<>\nstream\n${contentStream}\nendstream`, + ]; + + let pdf = '%PDF-1.4\n'; + const offsets: number[] = []; + objects.forEach((object, index) => { + offsets.push(Buffer.byteLength(pdf, 'latin1')); + pdf += `${String(index + 1)} 0 obj\n${object}\nendobj\n`; + }); + const xrefStart = Buffer.byteLength(pdf, 'latin1'); + pdf += `xref\n0 ${String(objects.length + 1)}\n0000000000 65535 f \n`; + for (const offset of offsets) pdf += `${String(offset).padStart(10, '0')} 00000 n \n`; + pdf += `trailer\n<>\nstartxref\n${String(xrefStart)}\n%%EOF\n`; + return Buffer.from(pdf, 'latin1'); +} + // --------------------------------------------------------------------------- // MIME → fixture map // --------------------------------------------------------------------------- diff --git a/packages/core/src/execution-engine/index.ts b/packages/core/src/execution-engine/index.ts index df46fde496b..eb74c1159d1 100644 --- a/packages/core/src/execution-engine/index.ts +++ b/packages/core/src/execution-engine/index.ts @@ -86,6 +86,8 @@ declare module 'n8n-workflow' { export * from './active-workflow-triggers'; export { synthesizeBinaryFixture, + evalCanvasPng, + buildPdfWithText, type FixtureSizeHint, type SynthesizeBinaryFixtureOptions, } from './eval-mock-fixtures'; diff --git a/packages/core/src/execution-engine/node-execution-context/__tests__/execute-context.test.ts b/packages/core/src/execution-engine/node-execution-context/__tests__/execute-context.test.ts index 0f689254618..d48760d503e 100644 --- a/packages/core/src/execution-engine/node-execution-context/__tests__/execute-context.test.ts +++ b/packages/core/src/execution-engine/node-execution-context/__tests__/execute-context.test.ts @@ -177,6 +177,7 @@ describe('ExecuteContext', () => { expect(() => executeContext.getNodeParameter('testParameter', 0)).toThrow(error); expect(error.context.parameter).toEqual('testParameter'); + expect({ ...error }).toEqual(expect.objectContaining({ cause: 'testValue' })); }); it('should handle expression errors on Set nodes (Ticket #PAY-684)', () => { diff --git a/packages/core/src/execution-engine/node-execution-context/__tests__/shared-tests.ts b/packages/core/src/execution-engine/node-execution-context/__tests__/shared-tests.ts index c905a242c25..51ee62fb952 100644 --- a/packages/core/src/execution-engine/node-execution-context/__tests__/shared-tests.ts +++ b/packages/core/src/execution-engine/node-execution-context/__tests__/shared-tests.ts @@ -282,6 +282,17 @@ export const describeCommonTests = ( context.setMetadata(metadata); expect(context.getExecuteData().metadata?.subExecution).toEqual(metadata.subExecution); }); + + it('deep-merges the nested tracing key instead of clobbering it', () => { + context.setMetadata({ tracing: { 'llm.tokens.total': 42, 'llm.cost.total': 0.01 } }); + context.setMetadata({ tracing: { 'ai.agent.iterations': 3 } }); + + expect(context.getExecuteData().metadata?.tracing).toEqual({ + 'llm.tokens.total': 42, + 'llm.cost.total': 0.01, + 'ai.agent.iterations': 3, + }); + }); }); describe('evaluateExpression', () => { diff --git a/packages/core/src/execution-engine/node-execution-context/base-execute-context.ts b/packages/core/src/execution-engine/node-execution-context/base-execute-context.ts index 523959e62c2..1aea273c222 100644 --- a/packages/core/src/execution-engine/node-execution-context/base-execute-context.ts +++ b/packages/core/src/execution-engine/node-execution-context/base-execute-context.ts @@ -40,6 +40,7 @@ import { } from 'n8n-workflow'; import { PLACEHOLDER_EMPTY_EXECUTION_ID } from '@/constants'; +import { deepMerge } from '@/utils/deep-merge'; import { NodeExecutionContext } from './node-execution-context'; @@ -93,10 +94,7 @@ export class BaseExecuteContext extends NodeExecutionContext { } setMetadata(metadata: ITaskMetadata): void { - this.executeData.metadata = { - ...(this.executeData.metadata ?? {}), - ...metadata, - }; + this.executeData.metadata = deepMerge(this.executeData.metadata ?? {}, metadata); } getContext(type: ContextType): IContextObject { diff --git a/packages/core/src/execution-engine/node-execution-context/node-execution-context.ts b/packages/core/src/execution-engine/node-execution-context/node-execution-context.ts index de02781c108..a5d98e35469 100644 --- a/packages/core/src/execution-engine/node-execution-context/node-execution-context.ts +++ b/packages/core/src/execution-engine/node-execution-context/node-execution-context.ts @@ -544,8 +544,12 @@ export abstract class NodeExecutionContext implements Omit ({ getSchema: vi.fn(), })); -// n8n-workflow is externalized (see vite.config.ts), so its CJS exports are -// getter-only and Vite SSR wraps the namespace separately from `require`. -// Replace the namespace's `sleepWithAbort` so both source code (via Vite SSR -// `import`) and tests see the mock. -const sleepWithAbort = vi.fn(); -Object.defineProperty(n8nWorkflow, 'sleepWithAbort', { +// ESM module namespace exports are read-only, so `vi.spyOn` can't reassign +// `sleep` directly. Replace it via `Object.defineProperty` so both source +// code (via its `import`) and tests see the mock. +const sleepMock = vi.fn(); +Object.defineProperty(n8nUtilsSleep, 'sleep', { configurable: true, - get: () => sleepWithAbort, + get: () => sleepMock, }); describe('getInputConnectionData', () => { @@ -878,7 +877,7 @@ describe('makeHandleToolInvocation', () => { }); it('should respect waitBetweenTries limits (0-5000ms)', async () => { - const sleepWithAbortSpy = (sleepWithAbort as Mock).mockResolvedValue(undefined); + const sleepSpy = (sleepMock as Mock).mockResolvedValue(undefined); const connectedNode = mock({ name: 'Test Tool', @@ -901,8 +900,8 @@ describe('makeHandleToolInvocation', () => { await expect(result).rejects.toThrow('Test error'); - expect(sleepWithAbortSpy).toHaveBeenCalledWith(1500, undefined); - sleepWithAbortSpy.mockRestore(); + expect(sleepSpy).toHaveBeenCalledWith(1500, undefined); + sleepSpy.mockRestore(); }); }); @@ -953,9 +952,7 @@ describe('makeHandleToolInvocation', () => { }); it('should handle abort signal during retry wait', async () => { - const sleepWithAbortSpy = (sleepWithAbort as Mock).mockRejectedValue( - new Error('Execution was cancelled'), - ); + const sleepSpy = (sleepMock as Mock).mockRejectedValue(new Error('Execution was cancelled')); const connectedNode = mock({ name: 'Test Tool', @@ -980,10 +977,10 @@ describe('makeHandleToolInvocation', () => { const result = await handleToolInvocation(toolArgs); expect(result).toBe('Error during node execution: Execution was cancelled'); - expect(sleepWithAbortSpy).toHaveBeenCalledWith(1000, abortController.signal); + expect(sleepSpy).toHaveBeenCalledWith(1000, abortController.signal); expect(connectedNodeType.execute).toHaveBeenCalledTimes(1); // Only first attempt - sleepWithAbortSpy.mockRestore(); + sleepSpy.mockRestore(); }); it('should handle abort signal during execution', async () => { @@ -1028,7 +1025,7 @@ describe('makeHandleToolInvocation', () => { .mockResolvedValueOnce([[{ json: { result: 'success' } }]]), }); - const sleepWithAbortSpy = (sleepWithAbort as Mock).mockResolvedValue(undefined); + const sleepSpy = (sleepMock as Mock).mockResolvedValue(undefined); handleToolInvocation = makeHandleToolInvocation( contextFactory, @@ -1041,9 +1038,9 @@ describe('makeHandleToolInvocation', () => { expect(result).toBe(JSON.stringify([{ result: 'success' }])); expect(connectedNodeType.execute).toHaveBeenCalledTimes(2); - expect(sleepWithAbortSpy).toHaveBeenCalledWith(10, abortController.signal); + expect(sleepSpy).toHaveBeenCalledWith(10, abortController.signal); - sleepWithAbortSpy.mockRestore(); + sleepSpy.mockRestore(); }); it('should work when getExecutionCancelSignal is not available', async () => { @@ -1068,7 +1065,7 @@ describe('makeHandleToolInvocation', () => { .mockResolvedValueOnce([[{ json: { result: 'success' } }]]), }); - const sleepWithAbortSpy = (sleepWithAbort as Mock).mockResolvedValue(undefined); + const sleepSpy = (sleepMock as Mock).mockResolvedValue(undefined); handleToolInvocation = makeHandleToolInvocation( contextFactory, @@ -1080,9 +1077,9 @@ describe('makeHandleToolInvocation', () => { const result = await handleToolInvocation(toolArgs); expect(result).toBe(JSON.stringify([{ result: 'success' }])); - expect(sleepWithAbortSpy).toHaveBeenCalledWith(10, undefined); + expect(sleepSpy).toHaveBeenCalledWith(10, undefined); - sleepWithAbortSpy.mockRestore(); + sleepSpy.mockRestore(); }); }); diff --git a/packages/core/src/execution-engine/node-execution-context/utils/get-input-connection-data.ts b/packages/core/src/execution-engine/node-execution-context/utils/get-input-connection-data.ts index 204bdede837..12b41e69edb 100644 --- a/packages/core/src/execution-engine/node-execution-context/utils/get-input-connection-data.ts +++ b/packages/core/src/execution-engine/node-execution-context/utils/get-input-connection-data.ts @@ -1,5 +1,6 @@ /* eslint-disable @typescript-eslint/no-unsafe-argument */ import { DynamicStructuredTool, StructuredTool, Tool } from '@langchain/core/tools'; +import { sleep } from '@n8n/utils/sleep'; import type { AINodeConnectionType, ChatNodeMessageWithButtons, @@ -28,7 +29,6 @@ import { NodeConnectionTypes, NodeOperationError, UserError, - sleepWithAbort, isHitlToolType, } from 'n8n-workflow'; import z, { ZodType } from 'zod'; @@ -275,7 +275,7 @@ export function makeHandleToolInvocation( lastError = undefined; if (waitBetweenTries !== 0) { try { - await sleepWithAbort(waitBetweenTries, abortSignal); + await sleep(waitBetweenTries, abortSignal); } catch (abortError) { return 'Error during node execution: Execution was cancelled'; } diff --git a/packages/core/src/execution-engine/node-execution-context/utils/request-helpers/__tests__/axios-error-stack.test.ts b/packages/core/src/execution-engine/node-execution-context/utils/request-helpers/__tests__/axios-error-stack.test.ts new file mode 100644 index 00000000000..fbeb5cb967e --- /dev/null +++ b/packages/core/src/execution-engine/node-execution-context/utils/request-helpers/__tests__/axios-error-stack.test.ts @@ -0,0 +1,36 @@ +import type { AxiosStatic } from 'axios'; +import axios from 'axios'; +import { createRequire } from 'node:module'; + +// The `import` above loads axios's lib/ build; production code `require`s +// dist/node/axios.cjs instead. Both are patched, so test both. +// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment -- require() is untyped +const axiosCjs: AxiosStatic = createRequire(__filename)('axios'); + +/** + * Guards `patches/axios.patch`. Without it, axios crashes on a non-string + * `error.stack` (caused by an overridden `Error.prepareStackTrace`) and replaces + * the real request error with its own TypeError. + */ +describe('axios error handling with overridden Error.prepareStackTrace', () => { + // eslint-disable-next-line @typescript-eslint/unbound-method -- saved only to restore, never invoked + const original = Error.prepareStackTrace; + + beforeEach(() => { + Error.prepareStackTrace = () => ({}); + }); + + afterEach(() => { + Error.prepareStackTrace = original; + }); + + it.each([ + ['import (lib build)', axios], + ['require (dist/node build)', axiosCjs], + ])('should surface the original request error via %s', async (_name, axiosInstance) => { + const failure = Object.assign(new Error('boom'), { code: 'ETEST' }); + await expect( + axiosInstance.get('http://unit.test', { adapter: async () => await Promise.reject(failure) }), + ).rejects.toBe(failure); + }); +}); diff --git a/packages/core/src/execution-engine/node-execution-context/utils/request-helpers/pagination.ts b/packages/core/src/execution-engine/node-execution-context/utils/request-helpers/pagination.ts index d00a7d2cc5c..55e62709c9b 100644 --- a/packages/core/src/execution-engine/node-execution-context/utils/request-helpers/pagination.ts +++ b/packages/core/src/execution-engine/node-execution-context/utils/request-helpers/pagination.ts @@ -3,10 +3,11 @@ /* eslint-disable @typescript-eslint/prefer-nullish-coalescing */ import { binaryToString, tryParseUrl } from '@n8n/backend-network'; +import { sleep } from '@n8n/utils/sleep'; import crypto from 'crypto'; import merge from 'lodash/merge'; import pick from 'lodash/pick'; -import { NodeOperationError, jsonParse, sleep } from 'n8n-workflow'; +import { NodeOperationError, jsonParse } from 'n8n-workflow'; import type { IAdditionalCredentialOptions, IDataObject, diff --git a/packages/core/src/execution-engine/routing-node.ts b/packages/core/src/execution-engine/routing-node.ts index 2ab4b746949..1042a9673e9 100644 --- a/packages/core/src/execution-engine/routing-node.ts +++ b/packages/core/src/execution-engine/routing-node.ts @@ -3,6 +3,7 @@ /* eslint-disable @typescript-eslint/prefer-nullish-coalescing */ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ +import { sleep } from '@n8n/utils/sleep'; import get from 'lodash/get'; import merge from 'lodash/merge'; import set from 'lodash/set'; @@ -10,7 +11,6 @@ import { NodeHelpers, NodeApiError, NodeOperationError, - sleep, NodeConnectionTypes, getCredentialAllowedDomains, } from 'n8n-workflow'; diff --git a/packages/core/src/execution-engine/workflow-active-triggers-state.ts b/packages/core/src/execution-engine/workflow-active-triggers-state.ts index 6a68edaf5ca..2926f57bef6 100644 --- a/packages/core/src/execution-engine/workflow-active-triggers-state.ts +++ b/packages/core/src/execution-engine/workflow-active-triggers-state.ts @@ -89,10 +89,14 @@ export class WorkflowActiveTriggersState { return this.registrationsByNodeId.keys(); } - /** All recorded trigger responses, in insertion order. */ - *triggerResponses(): IterableIterator { - for (const registration of this.registrationsByNodeId.values()) { - if (registration.response) yield registration.response; + /** + * Registrations that hold a trigger response to close, with their node ids, + * in insertion order. Response-less registrations (schedule and poll + * markers) are excluded — they have nothing to close. + */ + *closableTriggers() { + for (const [nodeId, registration] of this.registrationsByNodeId.entries()) { + if (registration.response) yield { nodeId, response: registration.response }; } } } diff --git a/packages/core/src/execution-engine/workflow-execute.ts b/packages/core/src/execution-engine/workflow-execute.ts index 9533fe43045..da6dd6ca6b3 100644 --- a/packages/core/src/execution-engine/workflow-execute.ts +++ b/packages/core/src/execution-engine/workflow-execute.ts @@ -5,6 +5,7 @@ import { isAxiosError } from '@n8n/backend-network'; import { TOOL_EXECUTOR_NODE_NAME } from '@n8n/constants'; import { Container } from '@n8n/di'; +import { sleep } from '@n8n/utils/sleep'; import * as assert from 'assert/strict'; import { setMaxListeners } from 'events'; import get from 'lodash/get'; @@ -50,7 +51,6 @@ import { NodeConnectionTypes, ApplicationError, BaseError, - sleep, isNodeClassInstance, UnexpectedError, UserError, diff --git a/packages/frontend/@n8n/composables/src/useToast.test.ts b/packages/frontend/@n8n/composables/src/useToast.test.ts index 712f3f586d3..30b2f4574d2 100644 --- a/packages/frontend/@n8n/composables/src/useToast.test.ts +++ b/packages/frontend/@n8n/composables/src/useToast.test.ts @@ -373,9 +373,6 @@ describe('useToast', () => { }); }); - // The only path that reads `pendingNotificationsForViews` and calls - // `setNotificationsForView`, so it is what proves the injected notification - // state is wired end to end. Previously untested. // The notifier is resolved per call rather than once per `useToast()`. Probing // with a *later registration* is what distinguishes the two: a once-bound // composable captures whatever was registered at creation time and never sees diff --git a/packages/frontend/@n8n/design-system/src/components/N8nAiModelSelectorDropdown/AiModelSelectorDropdown.types.ts b/packages/frontend/@n8n/design-system/src/components/N8nAiModelSelectorDropdown/AiModelSelectorDropdown.types.ts index 3bb073476a9..7774be87d01 100644 --- a/packages/frontend/@n8n/design-system/src/components/N8nAiModelSelectorDropdown/AiModelSelectorDropdown.types.ts +++ b/packages/frontend/@n8n/design-system/src/components/N8nAiModelSelectorDropdown/AiModelSelectorDropdown.types.ts @@ -2,6 +2,8 @@ import type { DropdownMenuItemProps } from '../N8nDropdownMenu/DropdownMenu.type export interface AiModelSelectorMenuItemData { badgeLabel?: string; + /** Right-aligned action pill on the item (e.g. an n8n credits balance). */ + actionPill?: { text: string; type?: 'default' | 'danger' }; description?: string; descriptionTooltipTeleported?: boolean; fullName?: string; diff --git a/packages/frontend/@n8n/design-system/src/components/N8nAiModelSelectorDropdown/AiModelSelectorDropdown.vue b/packages/frontend/@n8n/design-system/src/components/N8nAiModelSelectorDropdown/AiModelSelectorDropdown.vue index 2236a277d54..4440fb56db4 100644 --- a/packages/frontend/@n8n/design-system/src/components/N8nAiModelSelectorDropdown/AiModelSelectorDropdown.vue +++ b/packages/frontend/@n8n/design-system/src/components/N8nAiModelSelectorDropdown/AiModelSelectorDropdown.vue @@ -12,6 +12,7 @@ import type { AiModelSelectorMenuItemData, } from './AiModelSelectorDropdown.types'; import { useI18n } from '../../composables/useI18n'; +import N8nActionPill from '../N8nActionPill/ActionPill.vue'; import N8nBadge from '../N8nBadge'; import N8nDropdownMenu from '../N8nDropdownMenu/DropdownMenu.vue'; import N8nIcon from '../N8nIcon'; @@ -106,6 +107,7 @@ defineExpose({ teleported searchable width="var(--reka-dropdown-menu-trigger-width)" + :sub-menu-max-height="`calc(var(--spacing--5xl) * 2)`" @select="handleSelect" >

- {{ item.label }} + {{ + item.label + }} {{ item.data.badgeLabel }} +
diff --git a/packages/frontend/@n8n/design-system/src/components/N8nCallout/Callout.test.ts b/packages/frontend/@n8n/design-system/src/components/N8nCallout/Callout.test.ts index 871359b3ac4..1103d23be5b 100644 --- a/packages/frontend/@n8n/design-system/src/components/N8nCallout/Callout.test.ts +++ b/packages/frontend/@n8n/design-system/src/components/N8nCallout/Callout.test.ts @@ -89,6 +89,46 @@ describe('components', () => { }); expect(wrapper.html()).toMatchSnapshot(); }); + it('should wrap icon in a tooltip when iconTooltip is provided', () => { + const wrapper = render(N8nCallout, { + props: { + theme: 'custom', + icon: 'info', + iconTooltip: 'Useful explanation', + }, + global: { + stubs: { + N8nIcon: true, + N8nText: true, + N8nTooltip: { + template: '
', + props: ['content'], + }, + }, + }, + slots: { + default: 'This is a callout with an icon tooltip.', + }, + }); + const tooltip = wrapper.container.querySelector('[data-test-id="icon-tooltip"]'); + expect(tooltip).toBeTruthy(); + expect(tooltip?.getAttribute('data-content')).toBe('Useful explanation'); + expect(tooltip?.querySelector('n8n-icon-stub')).toBeTruthy(); + }); + it('should not render a tooltip when iconTooltip is not provided', () => { + const wrapper = render(N8nCallout, { + props: { + theme: 'info', + }, + global: { + stubs: ['N8nIcon', 'N8nText', 'N8nTooltip'], + }, + slots: { + default: 'This is an info callout.', + }, + }); + expect(wrapper.container.querySelector('n8n-tooltip-stub')).toBeFalsy(); + }); it('should render additional slots correctly', () => { const wrapper = render(N8nCallout, { props: { diff --git a/packages/frontend/@n8n/design-system/src/components/N8nCallout/Callout.vue b/packages/frontend/@n8n/design-system/src/components/N8nCallout/Callout.vue index a839b98171a..77a0e328d1b 100644 --- a/packages/frontend/@n8n/design-system/src/components/N8nCallout/Callout.vue +++ b/packages/frontend/@n8n/design-system/src/components/N8nCallout/Callout.vue @@ -5,6 +5,7 @@ import type { IconSize, CalloutTheme } from '../../types'; import N8nIcon from '../N8nIcon'; import { type IconName } from '../N8nIcon/icons'; import N8nText from '../N8nText'; +import N8nTooltip from '../N8nTooltip'; const CALLOUT_DEFAULT_ICONS: Record = { info: 'info', @@ -16,6 +17,7 @@ const CALLOUT_DEFAULT_ICONS: Record = { interface CalloutProps { theme: CalloutTheme; icon?: IconName; + iconTooltip?: string; iconSize?: IconSize; iconless?: boolean; slim?: boolean; @@ -25,6 +27,7 @@ interface CalloutProps { defineOptions({ name: 'N8nCallout' }); const props = withDefaults(defineProps(), { + iconTooltip: undefined, iconSize: 'medium', roundCorners: true, }); @@ -58,7 +61,10 @@ const getIconSize = computed(() => {
- + + + +
diff --git a/packages/frontend/@n8n/design-system/src/components/N8nDropdownMenu/DropdownMenu.test.ts b/packages/frontend/@n8n/design-system/src/components/N8nDropdownMenu/DropdownMenu.test.ts index 4343189610e..80fb3342ad7 100644 --- a/packages/frontend/@n8n/design-system/src/components/N8nDropdownMenu/DropdownMenu.test.ts +++ b/packages/frontend/@n8n/design-system/src/components/N8nDropdownMenu/DropdownMenu.test.ts @@ -323,6 +323,56 @@ describe('N8nDropdownMenu', () => { }); }); + it('should keep the dropdown open after selecting a keepOpen item', async () => { + const items: DropdownMenuItemProps[] = [{ id: 'toggle', label: 'Toggle', keepOpen: true }]; + + const wrapper = render(DropdownMenu, { + props: { + items, + }, + }); + + const trigger = wrapper.container.querySelector('button')!; + await userEvent.click(trigger); + + await waitFor(() => { + expect(document.querySelector('[role="menu"]')).toBeInTheDocument(); + }); + + const menuItem = document.querySelector('[role="menuitem"]')!; + await userEvent.click(menuItem); + + await waitFor(() => { + expect(wrapper.emitted('select')?.[0]).toEqual(['toggle']); + }); + // Still open — the selection did not close the menu. + expect(document.querySelector('[role="menu"]')).toBeInTheDocument(); + }); + + it('should render a header item as a non-interactive label', async () => { + const items: DropdownMenuItemProps[] = [ + { id: 'section', label: 'Section title', header: true }, + { id: 'option-1', label: 'Option 1' }, + ]; + + const wrapper = render(DropdownMenu, { + props: { + items, + }, + }); + + await userEvent.click(wrapper.container.querySelector('button')!); + + await waitFor(() => { + expect(document.querySelector('[role="menu"]')).toBeInTheDocument(); + }); + + // The header text renders... + expect(document.body.textContent).toContain('Section title'); + // ...but it is not a selectable menu item (only the real option is). + expect(document.querySelectorAll('[role="menuitem"]')).toHaveLength(1); + }); + it('should not emit select event for disabled items', async () => { const items: DropdownMenuItemProps[] = [ { id: 'disabled-item', label: 'Disabled', disabled: true }, @@ -662,6 +712,77 @@ describe('N8nDropdownMenu', () => { expect(searchInput).toHaveAttribute('placeholder', 'Search items...'); }); }); + + it('should keep the dropdown open when a keepOpen item is selected with the keyboard', async () => { + const items: DropdownMenuItemProps[] = [{ id: 'toggle', label: 'Toggle', keepOpen: true }]; + + const wrapper = render(DropdownMenu, { + props: { items, modelValue: true, searchable: true }, + }); + + await waitFor(() => { + expect(document.querySelector('input[type="text"]')).toBeInTheDocument(); + }); + + (document.querySelector('input[type="text"]') as HTMLElement).focus(); + await userEvent.keyboard('{ArrowDown}'); // highlight the item + await userEvent.keyboard('{Enter}'); // select via keyboard + + await waitFor(() => { + expect(wrapper.emitted('select')?.[0]).toEqual(['toggle']); + }); + // Still open — keyboard select honors keepOpen, matching click. + expect(document.querySelector('[role="menu"]')).toBeInTheDocument(); + }); + + it('should close the dropdown when a normal item is selected with the keyboard', async () => { + const items: DropdownMenuItemProps[] = [{ id: 'plain', label: 'Plain' }]; + + const wrapper = render(DropdownMenu, { + props: { items, modelValue: true, searchable: true }, + }); + + await waitFor(() => { + expect(document.querySelector('input[type="text"]')).toBeInTheDocument(); + }); + + (document.querySelector('input[type="text"]') as HTMLElement).focus(); + await userEvent.keyboard('{ArrowDown}'); + await userEvent.keyboard('{Enter}'); + + await waitFor(() => { + expect(wrapper.emitted('select')?.[0]).toEqual(['plain']); + }); + await waitFor(() => { + expect(document.querySelector('[role="menu"]')).not.toBeInTheDocument(); + }); + }); + + it('should skip section headers during keyboard navigation and never select them', async () => { + const items: DropdownMenuItemProps[] = [ + { id: 'section', label: 'Section', header: true }, + { id: 'option', label: 'Option' }, + ]; + + const wrapper = render(DropdownMenu, { + props: { items, modelValue: true, searchable: true }, + }); + + await waitFor(() => { + expect(document.querySelector('input[type="text"]')).toBeInTheDocument(); + }); + + (document.querySelector('input[type="text"]') as HTMLElement).focus(); + // First ArrowDown skips the header and lands on the real option. + await userEvent.keyboard('{ArrowDown}'); + await userEvent.keyboard('{Enter}'); + + await waitFor(() => { + expect(wrapper.emitted('select')?.[0]).toEqual(['option']); + }); + // The header id is never emitted as a selection. + expect(wrapper.emitted('select')?.flat()).not.toContain('section'); + }); }); describe('custom content slot', () => { diff --git a/packages/frontend/@n8n/design-system/src/components/N8nDropdownMenu/DropdownMenu.types.ts b/packages/frontend/@n8n/design-system/src/components/N8nDropdownMenu/DropdownMenu.types.ts index 63c3894f7be..616738a6ec8 100644 --- a/packages/frontend/@n8n/design-system/src/components/N8nDropdownMenu/DropdownMenu.types.ts +++ b/packages/frontend/@n8n/design-system/src/components/N8nDropdownMenu/DropdownMenu.types.ts @@ -6,6 +6,14 @@ import type { IconOrEmoji } from '../N8nIconPicker/types'; export const DropdownMenuPortalTargetKey: InjectionKey> = Symbol('DropdownMenuPortalTarget'); +/** + * Injection key overriding the max-height cap of nested sub-menu content, per + * dropdown. Default (unset) keeps the shared cap — only opted-in dropdowns pass it. + */ +export const DropdownMenuSubMaxHeightKey: InjectionKey> = Symbol( + 'DropdownMenuSubMaxHeight', +); + type VueCssClass = undefined | string | Record | Array; export type DropdownMenuPlacement = @@ -42,6 +50,10 @@ export type DropdownMenuItemProps = { divided?: boolean; /** Whether to show a checkmark indicator */ checked?: boolean; + /** Keep the menu open after this item is selected (e.g. toggle rows). */ + keepOpen?: boolean; + /** Render as a non-interactive section header label instead of a selectable item. */ + header?: boolean; /** Whether to expose the item as a menu item checkbox */ checkbox?: boolean; /** Additional CSS classes */ @@ -93,6 +105,8 @@ export interface DropdownMenuProps { maxHeight?: string | number; /** Maximum width of the dropdown menu. */ width?: string; + /** Overrides the max-height cap of nested sub-menu content (CSS length). */ + subMenuMaxHeight?: string | number; /** Whether to show loading state */ loading?: boolean; /** Number of skeleton items to show when loading */ diff --git a/packages/frontend/@n8n/design-system/src/components/N8nDropdownMenu/DropdownMenu.utils.test.ts b/packages/frontend/@n8n/design-system/src/components/N8nDropdownMenu/DropdownMenu.utils.test.ts index c2ebe6e2627..d46c381ecf5 100644 --- a/packages/frontend/@n8n/design-system/src/components/N8nDropdownMenu/DropdownMenu.utils.test.ts +++ b/packages/frontend/@n8n/design-system/src/components/N8nDropdownMenu/DropdownMenu.utils.test.ts @@ -11,6 +11,7 @@ import { hasSubMenu, isInputCursorAtEnd, isInputCursorAtStart, + isNavigableItem, } from './DropdownMenu.utils'; import DropdownMenuSearchableContent from './DropdownMenuSearchableContent.vue'; @@ -59,6 +60,27 @@ describe('DropdownMenu utils', () => { it('should return -1 when all items above are disabled', () => { expect(getNextValidIndex(createItems(3, [0]), 1, -1)).toBe(-1); }); + + it('should skip header items when navigating down', () => { + const items: DropdownMenuItemProps[] = [ + { id: 'a', label: 'A' }, + { id: 'h', label: 'Section', header: true }, + { id: 'b', label: 'B' }, + ]; + expect(getNextValidIndex(items, 0, 1)).toBe(2); + }); + }); + + describe('isNavigableItem', () => { + it('returns true for a plain enabled item', () => { + expect(isNavigableItem({ id: 'a', label: 'A' })).toBe(true); + }); + + it('returns false for disabled, header, or missing items', () => { + expect(isNavigableItem({ id: 'a', label: 'A', disabled: true })).toBe(false); + expect(isNavigableItem({ id: 'h', label: 'Section', header: true })).toBe(false); + expect(isNavigableItem(undefined)).toBe(false); + }); }); describe('hasSubMenu', () => { diff --git a/packages/frontend/@n8n/design-system/src/components/N8nDropdownMenu/DropdownMenu.utils.ts b/packages/frontend/@n8n/design-system/src/components/N8nDropdownMenu/DropdownMenu.utils.ts index 09dd00a0d1f..e983e8faea7 100644 --- a/packages/frontend/@n8n/design-system/src/components/N8nDropdownMenu/DropdownMenu.utils.ts +++ b/packages/frontend/@n8n/design-system/src/components/N8nDropdownMenu/DropdownMenu.utils.ts @@ -7,6 +7,13 @@ export function hasSubMenu(item: DropdownMenuItemProps): boolean { return (item.children && item.children.length > 0) || !!item.loading || !!item.searchable; } +/** Whether a searchable item can be highlighted/selected via keyboard navigation. */ +export function isNavigableItem( + item: DropdownMenuItemProps | undefined, +): item is DropdownMenuItemProps { + return !!item && !item.disabled && !item.header; +} + export function getNextValidIndex( items: Array>, current: number, @@ -14,7 +21,7 @@ export function getNextValidIndex( ): number { let next = current + direction; - while (next >= 0 && next < items.length && items[next].disabled) { + while (next >= 0 && next < items.length && !isNavigableItem(items[next])) { next += direction; } diff --git a/packages/frontend/@n8n/design-system/src/components/N8nDropdownMenu/DropdownMenu.vue b/packages/frontend/@n8n/design-system/src/components/N8nDropdownMenu/DropdownMenu.vue index a8d5cb0fbe8..0f6fa6bf85a 100644 --- a/packages/frontend/@n8n/design-system/src/components/N8nDropdownMenu/DropdownMenu.vue +++ b/packages/frontend/@n8n/design-system/src/components/N8nDropdownMenu/DropdownMenu.vue @@ -13,6 +13,7 @@ import type { IconName } from '@n8n/design-system/components/N8nIcon/icons'; import { isAlign, isSide } from './DropdownMenu.typeguards'; import { DropdownMenuPortalTargetKey, + DropdownMenuSubMaxHeightKey, type DropdownMenuItemProps, type DropdownMenuProps, type DropdownMenuSlots, @@ -54,6 +55,17 @@ provide( computed(() => props.portalTarget), ); +provide( + DropdownMenuSubMaxHeightKey, + computed(() => + props.subMenuMaxHeight === undefined + ? undefined + : typeof props.subMenuMaxHeight === 'number' + ? `${props.subMenuMaxHeight}px` + : props.subMenuMaxHeight, + ), +); + // Handle controlled/uncontrolled state const internalOpen = ref(props.defaultOpen ?? false); @@ -114,9 +126,22 @@ const handleSubMenuOpenChange = (index: number, open: boolean) => { } }; +function findItemById( + list: Array>, + id: T, +): DropdownMenuItemProps | undefined { + for (const item of list) { + if (item.id === id) return item; + const found = item.children && findItemById(item.children, id); + if (found) return found; + } + return undefined; +} + const handleItemSelect = (value: T) => { emit('select', value); - close(); + // Toggle-style rows (e.g. credential selection) keep the menu open. + if (!findItemById(props.items, value)?.keepOpen) close(); }; const handleItemSearch = (term: string, itemId: T) => { @@ -253,8 +278,8 @@ defineExpose({ open, close }); > (null); @@ -98,10 +101,10 @@ const handleSelect = (value: T) => { }; const handleItemSelect = (event: Event) => { - if (!props.disabled && !hasSubMenu.value) { - if (!props.closeOnSelect) event.preventDefault(); - emit('select', props.id); - } + if (props.disabled || hasSubMenu.value) return; + // Keep the menu open for toggle-style rows (keepOpen) or items opting out of close-on-select. + if (props.keepOpen || !props.closeOnSelect) event.preventDefault(); + emit('select', props.id); }; const handlePointerMove = (event: PointerEvent) => { @@ -185,18 +188,22 @@ onBeforeUnmount(() => {
+ + {{ label }} + + { { :id="htmlId" :model-value="checked" :aria-selected="highlighted || undefined" - @pointermove.capture="handlePointerMove" :disabled="disabled" :data-test-id="testId" :class="[$style.item, props.class, { 'is-disabled': !!disabled }]" + @pointermove.capture="handlePointerMove" @select="handleItemSelect" > @@ -373,10 +383,10 @@ onBeforeUnmount(() => { v-else :id="htmlId" :aria-selected="highlighted || undefined" - @pointermove.capture="handlePointerMove" :disabled="disabled" :data-test-id="testId" :class="[$style.item, props.class, { 'is-disabled': !!disabled }]" + @pointermove.capture="handlePointerMove" @select="handleItemSelect" > @@ -437,6 +447,13 @@ onBeforeUnmount(() => { } } +.section-header { + display: flex; + align-items: center; + padding: var(--spacing--2xs) var(--spacing--2xs) var(--spacing--3xs); + user-select: none; +} + .item { font-size: var(--font-size--2xs); line-height: 1; @@ -503,7 +520,10 @@ onBeforeUnmount(() => { width: fit-content; min-width: calc(var(--n8n--dropdown-menu-width) / 4); max-width: var(--n8n--dropdown-menu-width); - max-height: min(var(--reka-dropdown-menu-content-available-height), var(--spacing--5xl)); + max-height: min( + var(--reka-dropdown-menu-content-available-height), + var(--n8n-dropdown-sub-max-height, var(--spacing--5xl)) + ); transform-origin: var(--n8n--dropdown--offset--origin-x) var(--n8n--dropdown--offset--origin-y); overflow: hidden; scrollbar-width: none; diff --git a/packages/frontend/@n8n/design-system/src/components/N8nDropdownMenu/DropdownMenuSearchableContent.vue b/packages/frontend/@n8n/design-system/src/components/N8nDropdownMenu/DropdownMenuSearchableContent.vue index b576e5e774c..34f5437eb11 100644 --- a/packages/frontend/@n8n/design-system/src/components/N8nDropdownMenu/DropdownMenuSearchableContent.vue +++ b/packages/frontend/@n8n/design-system/src/components/N8nDropdownMenu/DropdownMenuSearchableContent.vue @@ -9,6 +9,7 @@ import { hasSubMenu, isInputCursorAtEnd, isInputCursorAtStart, + isNavigableItem, scrollHighlightedItemIntoView, } from './DropdownMenu.utils'; import N8nDropdownMenuSearch from './DropdownMenuSearch.vue'; @@ -105,7 +106,7 @@ const updateHighlightedItem = ( const highlightedItem = oldItems[highlightedIndex.value]; const newIndex = newItems.findIndex((item) => item.id === highlightedItem?.id); - highlightedIndex.value = newItems[newIndex]?.disabled ? -1 : newIndex; + highlightedIndex.value = isNavigableItem(newItems[newIndex]) ? newIndex : -1; }; const debouncedEmitSearch = useDebounceFn((term: string, sequence: number) => { @@ -156,13 +157,14 @@ const selectHighlightedItem = () => { if (highlightedIndex.value < 0) return; const item = props.items[highlightedIndex.value]; - if (!item || item.disabled) return; + if (!isNavigableItem(item)) return; if (hasSubMenu(item)) { handleSubMenuOpenChange(highlightedIndex.value, true); } else { emit('select', item.id); - emit('close'); + // Toggle-style rows (keepOpen) stay open on keyboard select, matching click. + if (!item.keepOpen) emit('close'); } }; @@ -174,7 +176,7 @@ const closeOpenSubMenu = () => { const handleItemHover = (index: number) => { const item = props.items[index]; - if (!item || item.disabled) return; + if (!isNavigableItem(item)) return; highlightedIndex.value = index; diff --git a/packages/frontend/@n8n/design-system/src/components/N8nInput/Input.test.ts b/packages/frontend/@n8n/design-system/src/components/N8nInput/Input.test.ts index 10759b91cea..93d68e1016b 100644 --- a/packages/frontend/@n8n/design-system/src/components/N8nInput/Input.test.ts +++ b/packages/frontend/@n8n/design-system/src/components/N8nInput/Input.test.ts @@ -123,6 +123,32 @@ describe('components/N8nInput', () => { const container = wrapper.container.querySelector('div > div'); expect(container?.className).not.toContain('ph-no-capture'); }); + + it('should mask a textarea and add ph-no-capture when masked', () => { + const wrapper = render(Input, { + props: { + type: 'textarea', + masked: true, + }, + }); + const textarea = wrapper.container.querySelector('textarea'); + expect(textarea).toBeInTheDocument(); + expect(textarea?.className).toContain('masked'); + const container = wrapper.container.querySelector('div > div'); + expect(container?.className).toContain('ph-no-capture'); + }); + + it('should not mask a plain textarea', () => { + const wrapper = render(Input, { + props: { + type: 'textarea', + }, + }); + const textarea = wrapper.container.querySelector('textarea'); + expect(textarea?.className).not.toContain('masked'); + const container = wrapper.container.querySelector('div > div'); + expect(container?.className).not.toContain('ph-no-capture'); + }); }); describe('v-model', () => { diff --git a/packages/frontend/@n8n/design-system/src/components/N8nInput/Input.types.ts b/packages/frontend/@n8n/design-system/src/components/N8nInput/Input.types.ts index 3e5404bff30..17279e48d20 100644 --- a/packages/frontend/@n8n/design-system/src/components/N8nInput/Input.types.ts +++ b/packages/frontend/@n8n/design-system/src/components/N8nInput/Input.types.ts @@ -19,6 +19,9 @@ export interface InputProps { readonly?: boolean; clearable?: boolean; rows?: number; + // Masks a textarea's content (dots) for secrets that span multiple lines, + // where can't be used because it strips newlines. + masked?: boolean; maxlength?: number; autosize?: boolean | { minRows?: number; maxRows?: number }; autofocus?: boolean; diff --git a/packages/frontend/@n8n/design-system/src/components/N8nInput/Input.vue b/packages/frontend/@n8n/design-system/src/components/N8nInput/Input.vue index e077068c105..f4c4d3cd210 100644 --- a/packages/frontend/@n8n/design-system/src/components/N8nInput/Input.vue +++ b/packages/frontend/@n8n/design-system/src/components/N8nInput/Input.vue @@ -20,6 +20,7 @@ const props = withDefaults(defineProps(), { readonly: false, clearable: false, rows: 2, + masked: false, maxlength: undefined, autosize: false, autofocus: false, @@ -90,7 +91,7 @@ const containerClasses = computed(() => [ [$style.hasPrepend]: !!slots.prepend, [$style.hasAppend]: !!slots.append, [$style.isTextarea]: isTextarea.value, - 'ph-no-capture': props.type === 'password', + 'ph-no-capture': props.type === 'password' || props.masked, }, ]); @@ -226,7 +227,7 @@ defineExpose({ focus, blur, select }); v-else ref="inputRef" :value="modelValue ?? ''" - :class="[$style.input, $style.textarea]" + :class="[$style.input, $style.textarea, { [$style.masked]: masked }]" :placeholder="placeholder" :disabled="disabled" :readonly="readonly" @@ -444,6 +445,14 @@ defineExpose({ focus, blur, select }); color: var(--color--text--tint-1); } +/* Masks a multiline secret (e.g. a PEM private key) as dots via + -webkit-text-security (supported in Chromium, Safari, and Firefox 114+). + Display-only: the real value is never re-sent to the client (backend + redaction), and this masks rendering, not copy/paste. */ +.masked { + -webkit-text-security: disc; +} + .prefix, .suffix { display: flex; diff --git a/packages/frontend/@n8n/design-system/src/components/N8nRecycleScroller/RecycleScroller.test.ts b/packages/frontend/@n8n/design-system/src/components/N8nRecycleScroller/RecycleScroller.test.ts index da28ca8d518..6495db453c9 100644 --- a/packages/frontend/@n8n/design-system/src/components/N8nRecycleScroller/RecycleScroller.test.ts +++ b/packages/frontend/@n8n/design-system/src/components/N8nRecycleScroller/RecycleScroller.test.ts @@ -59,5 +59,48 @@ describe('components', () => { } } }); + + it('keeps positions finite when the item keys are all replaced', async () => { + const originalOffsetHeight = Object.getOwnPropertyDescriptor( + HTMLElement.prototype, + 'offsetHeight', + ); + Object.defineProperty(HTMLElement.prototype, 'offsetHeight', { + configurable: true, + value: itemSize, + }); + + try { + const wrapper = mount(N8nRecycleScroller, { + props: { + itemSize, + itemKey, + items, + }, + }); + + await nextTick(); + + const replacedItems = items.map((item) => ({ + id: `replaced-${item.id}`, + name: `Replaced ${item.name}`, + })); + await wrapper.setProps({ items: replacedItems }); + + // Unmeasured keys must fall back to itemSize, otherwise the positions turn + // NaN and every item is rendered instead of the visible window. + expect(wrapper.findAll('.recycle-scroller-item').length).toBeLessThan(replacedItems.length); + expect(wrapper.find('.recycle-scroller').attributes('style')).not.toContain('NaN'); + expect(wrapper.find('.recycle-scroller-items-wrapper').attributes('style')).not.toContain( + 'NaN', + ); + } finally { + if (originalOffsetHeight) { + Object.defineProperty(HTMLElement.prototype, 'offsetHeight', originalOffsetHeight); + } else { + Reflect.deleteProperty(HTMLElement.prototype, 'offsetHeight'); + } + } + }); }); }); diff --git a/packages/frontend/@n8n/design-system/src/components/N8nRecycleScroller/RecycleScroller.vue b/packages/frontend/@n8n/design-system/src/components/N8nRecycleScroller/RecycleScroller.vue index cc3eee31531..3921862114e 100644 --- a/packages/frontend/@n8n/design-system/src/components/N8nRecycleScroller/RecycleScroller.vue +++ b/packages/frontend/@n8n/design-system/src/components/N8nRecycleScroller/RecycleScroller.vue @@ -33,7 +33,9 @@ const itemPositionCache = computed(() => { const key = item[props.itemKey]; const prevItem = props.items[index - 1]; const prevItemPosition = prevItem ? acc[prevItem[props.itemKey]] : 0; - const prevItemSize = prevItem ? itemSizeCache.value[prevItem[props.itemKey]] : 0; + const prevItemSize = prevItem + ? (itemSizeCache.value[prevItem[props.itemKey]] ?? props.itemSize) + : 0; acc[key] = prevItemPosition + prevItemSize; @@ -60,7 +62,7 @@ const startIndex = computed(() => { const endIndex = computed(() => { const foundIndex = props.items.findIndex((item) => { const itemPosition = itemPositionCache.value[item[props.itemKey]]; - const itemSize = itemSizeCache.value[item[props.itemKey]]; + const itemSize = itemSizeCache.value[item[props.itemKey]] ?? props.itemSize; return itemPosition + itemSize >= scrollTop.value + wrapperHeight.value; }); @@ -94,7 +96,9 @@ watch( const scrollerHeight = computed(() => { const lastItem = props.items[props.items.length - 1]; const lastItemPosition = lastItem ? itemPositionCache.value[lastItem[props.itemKey]] : 0; - const lastItemSize = lastItem ? itemSizeCache.value[lastItem[props.itemKey]] : props.itemSize; + const lastItemSize = lastItem + ? (itemSizeCache.value[lastItem[props.itemKey]] ?? props.itemSize) + : props.itemSize; return lastItemPosition + lastItemSize; }); @@ -148,7 +152,10 @@ function onUpdateItemSize(item: { [key: string]: string }) { void nextTick(() => { const itemId = item[props.itemKey]; const itemRef = itemRefs.value[itemId] as HTMLElement; - const previousSize = itemSizeCache.value[itemId]; + // The virtual layout positions an unmeasured row as if it were + // `itemSize` tall, so that is the height to compensate against — using + // the raw cache miss here would scroll by NaN and reset to the top. + const previousSize = itemSizeCache.value[itemId] ?? props.itemSize; const size = itemRef ? itemRef.offsetHeight : props.itemSize; const difference = size - previousSize; @@ -183,21 +190,25 @@ function onScroll() { scrollTop.value = wrapperRef.value.scrollTop; } -function scrollToKey(key: Item[Key]) { +function scrollTo(position: number) { if (!wrapperRef.value) { return; } + wrapperRef.value.scrollTop = position; + scrollTop.value = wrapperRef.value.scrollTop; +} + +function scrollToKey(key: Item[Key]) { const position = itemPositionCache.value[key]; if (position === undefined) { return; } - wrapperRef.value.scrollTop = position; - scrollTop.value = position; + scrollTo(position); } -defineExpose({ scrollToKey }); +defineExpose({ scrollToKey, scrollTo, scrollTop });