Compare commits

...

2193 Commits

Author SHA1 Message Date
n8n-assistant[bot]
936349a9c7
chore: Update e2e impact map (#41317)
Some checks are pending
CI: Master (Build, Test, Lint) / Build & format (push) Waiting to run
CI: Master (Build, Test, Lint) / Unit tests (24.18.1) (push) Waiting to run
CI: Master (Build, Test, Lint) / Unit tests (26.5.1) (push) Waiting to run
CI: Master (Build, Test, Lint) / Lint (push) Waiting to run
CI: Master (Build, Test, Lint) / Verify single-instance deps (npm-install) (push) Waiting to run
CI: Master (Build, Test, Lint) / Notify Slack on failure (push) Blocked by required conditions
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-10-17 08:18:42 +00:00
Olivier
842ec5b62d
fix(core): Turn off expression spans by default (#41211)
Some checks are pending
CI: Master (Build, Test, Lint) / Build & format (push) Waiting to run
CI: Master (Build, Test, Lint) / Unit tests (24.18.1) (push) Waiting to run
CI: Master (Build, Test, Lint) / Unit tests (26.5.1) (push) Waiting to run
CI: Master (Build, Test, Lint) / Lint (push) Waiting to run
CI: Master (Build, Test, Lint) / Verify single-instance deps (npm-install) (push) Waiting to run
CI: Master (Build, Test, Lint) / Notify Slack on failure (push) Blocked by required conditions
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-16 22:23:07 +00:00
n8n-cat-bot[bot]
ec074519d9
feat: Add policy attachment read endpoints (no-changelog) (#41122)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-16 16:13:10 +00:00
Rúben Castro
6f3b397a1e
feat(editor): Mark community packages with blocked node types (no-changelog) (#41105)
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
Co-authored-by: Guillaume Jacquart <jacquart.guillaume@gmail.com>
2026-10-16 15:43:39 +00:00
n8n-cat-bot[bot]
21221adc78
fix: Validate attendee values (#40953)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
2026-10-16 14:56:23 +00:00
Guillaume Jacquart
532e8ef930
fix(core): Save policy rules that name uninstalled packages (no-changelog) (#41210)
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
2026-10-16 14:48:56 +00:00
n8n-cat-bot[bot]
24621ad4ca
chore: Add review/lint rule to discourage open-ended fields in nodes (warn level) (#39959)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
2026-10-16 14:47:15 +00:00
n8n-cat-bot[bot]
8ee5cf1845
chore: Flaky test: frontend.service.test.ts (3 master or merge-queue commits in 72 h) (#41279)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
2026-10-16 14:44:01 +00:00
Stephen Wright
7f16040253
feat(Databricks Node): Add the Lakebase Delete operation (no-changelog) (#40543) 2026-10-16 14:42:03 +00:00
Csaba Tuncsik
ddc97694b5
fix(Microsoft Teams Node): Add the TeamMember.Read.All scope and correct the Trigger scope notice (#39819) 2026-10-16 14:27:44 +00:00
Rúben Castro
eaf47cfe39
fix(editor): Improve destination validation and surface save errors (#40897)
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: Guillaume Jacquart <jacquart.guillaume@gmail.com>
2026-10-16 14:14:55 +00:00
Ricardo Espinoza
986b82fa28
feat: Add telemetry events to the skills hub (no-changelog) (#41234) 2026-10-16 14:12:37 +00:00
Jan
76cce99626
fix(editor): Unify documentation links across settings pages (#40289)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-16 13:57:10 +00:00
Jaakko Husso
f34c093f08
feat(core): Add an optional model for n8n Assistant memory tasks (#41137)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-16 13:55:06 +00:00
Mike Repeć
158152496a
fix(core): Reuse connections in the log streaming webhook destination (#41261)
Co-authored-by: Theo Nastos <theodore.nastos@n8n.io>
2026-10-16 13:54:34 +00:00
Jan
1f223c85f8
fix(editor): Start the empty-state icon cards cycling on load and use them on the MCP and external secrets pages (#40232)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-16 13:53:32 +00:00
Ricardo Espinoza
6a015ecb18
fix(core): Check the delete permission when the agent page moves a skill (no-changelog) (#41222) 2026-10-16 13:52:18 +00:00
n8n-cat-bot[bot]
033ec05bff
chore: Bump handlebars 4.7.9 → 4.7.10 (patch) to clear 6 Aikido findings (#41277)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
2026-10-16 13:46:42 +00:00
Theodore Nastos
dd79a610c5
feat(core): Enable node and credential type policies by default (#41022) 2026-10-16 13:44:56 +00:00
Martin Rechenberger
a63a8edb55
fix(editor): Hide the Community Plus enrollment modal on Cloud (no-changelog) (#41251) 2026-10-16 13:40:37 +00:00
symbiote15
f5dc06f753
fix(Postgres Node): Keep comma-containing values as one query parameter (#40240) 2026-10-16 13:28:55 +00:00
Jaakko Husso
b78913f75d
fix(core): Keep the workflow source file when n8n Assistant sends blank source (#41201)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-16 13:28:54 +00:00
Guillaume Jacquart
dfec6dee81
fix(editor): Allow copy and cut of a selection with a restricted node (no-changelog) (#41276)
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-16 13:25:51 +00:00
Uddish Verma
f92b4c11d6
feat(API): Limit Public API requests for each API key (no-changelog) (#41008)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-16 13:19:34 +00:00
Luigi Gubello
5120d25cbc
fix(editor): Count missing input apart in the QuickJS shadow report (no-changelog) (#41214)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-16 13:15:19 +00:00
Bernhard Wittmann
a6719b8db7
fix(Email Trigger (IMAP) Node): Check the mailbox when the trigger activates (#41013) 2026-10-16 13:10:25 +00:00
Bernhard Wittmann
7001886432
fix(core): Give each new Microsoft Teams sign-in credential a unique name (no-changelog) (#41256) 2026-10-16 13:04:21 +00:00
Sam Wooler
a906d2fdfc
feat(API): Return the parent folder on workflow read endpoints (#40981)
Co-authored-by: Claude Haiku 5.5 <noreply@anthropic.com>
2026-10-16 12:48:25 +00:00
Anne Aguirre
683fd11259
fix(core): Pass reasoning effort on open router model node (#41179) 2026-10-16 12:43:58 +00:00
Mike Repeć
1b02863c00
fix(core): Hand node reference results without an item shape to the engine (no-changelog) (#41083) 2026-10-16 12:25:04 +00:00
n8n-cat-bot[bot]
d0cb582163
chore: Bump fast-uri 3.1.5 → 3.1.8 (patch) to clear 12 Aikido findings (#41255)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
2026-10-16 12:21:08 +00:00
Anne Aguirre
ea98bd0600
fix(AI Agent Node): Replay Anthropic thinking turns with their tool arguments (#41241) 2026-10-16 12:09:01 +00:00
Predrag Ristic
1405d4a351
feat(core): Add self-healing results to Inbox (#40741) 2026-10-16 12:07:09 +00:00
Anne Aguirre
f84413516a
fix(core): Web search misconfigs in agents as UserError (#41257) 2026-10-16 12:00:35 +00:00
Sandra Zollner
333a010112
feat(core): Store promotion exports in n8n directory (#40863)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-16 12:00:28 +00:00
Ali Elkhateeb
b7b3d72737
feat(API): Honor Idempotency-Key on Public API POST requests (#40978)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-16 11:58:36 +00:00
n8n-cat-bot[bot]
5a3fc39972
fix: Skip dependency files and continue after custom node load (#41117)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
2026-10-16 11:53:13 +00:00
Rúben Castro
d460eb28ca
fix(editor): Explain why copy, cut, duplicate and pin refuse a restricted node (no-changelog) (#40997)
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-10-16 11:51:01 +00:00
Francesca Sargent
4a5fdd5b68
fix(editor): Return to the requested page after SSO sign-in (#41151) 2026-10-16 11:42:25 +00:00
MHMD
e7f0607bf1
fix(core): Allow switching a node back to a credential the user cannot use after a save (#41198) 2026-10-16 11:39:13 +00:00
Sandra Zollner
64d8a3ad19
feat(core): Add promotion run and review data model (no-changelog) (#40794)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-16 11:28:06 +00:00
James Martin
c70b5d49b3
fix(core): Redact a parent execution if any sub-workflows have redaction enabled (#41129)
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-16 11:02:43 +00:00
Stephen Wright
c28d130c6c
feat(Databricks Node): Explain Lakebase setup failures (no-changelog) (#40523) 2026-10-16 10:49:36 +00:00
MHMD
ca9661c091
fix(editor): Keep the used-in rows on the credential sharing tab when personal-space sharing is disabled (#41207) 2026-10-16 10:40:31 +00:00
Emre Caglar
a94f5dc8b7
feat(editor): Move the check rule and delete actions in the agent check row (no-changelog) (#41170)
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-16 10:26:51 +00:00
Emre Caglar
fffea4c0c0
fix(editor): Remove the warning badge from the needs-work agent avatar (no-changelog) (#41164)
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-16 10:26:41 +00:00
Emre Caglar
14e633d481
fix(editor): Keep rounded corners on a single row in the agent checks list (no-changelog) (#41166)
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-16 10:26:28 +00:00
Emre Caglar
575eb6fa43
fix(editor): Show the check conversation stripes in dark mode (no-changelog) (#41167)
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-16 10:26:19 +00:00
Emre Caglar
3ea0a3da98
fix(editor): Join the check conversation box to the practice-run strip (no-changelog) (#41168)
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-16 10:26:06 +00:00
Emre Caglar
fed8b1ec69
feat(editor): Use a segment control for the agent checks status filter (no-changelog) (#41169)
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-16 10:09:00 +00:00
Jan Kalkan
eba25caa70
feat(core): Require credential:create scope for create-with-values package imports (no-changelog) (#41131)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-16 09:55:56 +00:00
Sam Wooler
fe3627914a
fix(API): Log errors before returning generic 500 in Public API (#41186)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-16 09:54:41 +00:00
Jaakko Husso
aba5258930
feat(core): Add the scope column to the agents table (no-changelog) (#41165)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-16 09:52:52 +00:00
Bernhard Wittmann
aadbb7c498
fix(editor): Let the Assistant chat finish the Microsoft Teams setup (no-changelog) (#41188) 2026-10-16 09:38:51 +00:00
Predrag Ristic
8bee1eeb20
refactor(core): Prepare the shared Inbox (#40739)
Co-authored-by: kgrhartlage <kai.hartlage@n8n.io>
2026-10-16 09:37:52 +00:00
Eugene
2eab299dfa
fix(core): Pass Agent stop signals to foreground workflow tools (no-changelog) (#40776) 2026-10-16 09:21:44 +00:00
João Almeida
fc9f9575e2
fix(editor): Keep workflow name in edit mode after Rename menu action (#41120)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-16 09:04:26 +00:00
João Almeida
116a81e06e
fix(editor): Keep pending navigation when workflows list query is unchanged (#41125)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-16 09:04:20 +00:00
João Almeida
b36f6c0baf
test: Fix flaky zoom after route changes e2e test (#41130)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-16 09:04:09 +00:00
Riqwan Thamir
fa88c3e251
feat(core): Write finish_setup credentials into node tools (no-changelog) (#41219)
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
2026-10-16 08:49:51 +00:00
Dominik
8120328661
fix(editor): Rename Admin Panel to Cloud console (#41187) 2026-10-16 08:38:56 +00:00
Ricardo Espinoza
1ee29b2875
fix(editor): Lock the skill scope for users who cannot delete the skill (no-changelog) (#41221) 2026-10-16 08:28:41 +00:00
Ricardo Espinoza
f553331100
fix(editor): Keep the builder skill modal open on a taken name (no-changelog) (#41227) 2026-10-16 08:28:19 +00:00
Ricardo Espinoza
1a6b6a65d4
feat(editor): Name the project in the skill "available to" options (no-changelog) (#41229) 2026-10-16 08:27:34 +00:00
Ricardo Espinoza
88a2426947
fix(core): Trim skill names and refuse a name of only spaces (no-changelog) (#41223) 2026-10-16 08:18:55 +00:00
n8n-assistant[bot]
f5febab84d
chore: Update e2e impact map (#41245)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-10-16 08:18:15 +00:00
Ricardo Espinoza
dde9871497
feat(editor): Link to the skills settings page from the agent skill picker (no-changelog) (#41228) 2026-10-16 08:18:13 +00:00
Bernhard Wittmann
cc83b5bd77
feat(core): Use the agent's icon in the Microsoft Teams app package (no-changelog) (#40322) 2026-10-16 08:14:01 +00:00
Guillaume Jacquart
1ea18e9ef9
feat(editor): Show policy-blocked items in the source control pull modal (#40604)
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-16 08:04:25 +00:00
n8n-cat-bot[bot]
1ba56b6579
chore: Bug - Credentials sharing tab placeholder text misleading (#41239)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
Co-authored-by: kgrhartlage <kai.hartlage@n8n.io>
2026-10-16 07:47:39 +00:00
Jaakko Husso
baa38dbdc6
feat(core): Add owned-session queries to the Agents thread repositories (no-changelog) (#40615)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-16 07:45:16 +00:00
Jaakko Husso
601f6ac0da
feat(editor): Let the Agents chat host render host events by placement (no-changelog) (#40720)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-16 07:40:56 +00:00
OrdoAbChao
e29c3a4085
fix(Splunk Node): Preserve errors when continuing after failed items (#40368) 2026-10-16 07:35:28 +00:00
Jaakko Husso
447fc866bb
feat(core): Add hidden turns, turn options and a system kind to the Agents queue (no-changelog) (#40614)
Some checks failed
CI: Master (Build, Test, Lint) / Build & format (push) Has been cancelled
CI: Master (Build, Test, Lint) / Unit tests (24.18.1) (push) Has been cancelled
CI: Master (Build, Test, Lint) / Unit tests (26.5.1) (push) Has been cancelled
CI: Master (Build, Test, Lint) / Lint (push) Has been cancelled
CI: Master (Build, Test, Lint) / Verify single-instance deps (npm-install) (push) Has been cancelled
CI: Master (Build, Test, Lint) / Notify Slack on failure (push) Has been cancelled
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 19:25:36 +00:00
Ricardo Espinoza
860f1273eb
feat(editor): Add the Skills page to Context settings (no-changelog) (#41031) 2026-10-15 15:35:20 +00:00
Jan Kalkan
deed9a2f32
feat(core): Report credentials created with partial package data separately (no-changelog) (#41156)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 15:29:25 +00:00
nvonahsen
a32c652c09
fix(editor): Prevent doubled base path when opening folders with N8N_PATH set (#33065)
Signed-off-by: Niclas von Ahsen <niclas.vonahsen@elementx.ai>
Signed-off-by: James Gee <1285296+geemanjs@users.noreply.github.com>
Co-authored-by: James Gee <1285296+geemanjs@users.noreply.github.com>
2026-10-15 15:25:39 +00:00
Ricardo Espinoza
1903d53bc0
fix(editor): Save only the OpenTelemetry status when enabling or disabling (#41142) 2026-10-15 14:43:53 +00:00
n8n-cat-bot[bot]
131e9c8679
fix: Keep free-floating sticky notes clear of workflow nodes (#41195)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
2026-10-15 14:41:40 +00:00
Bernhard Wittmann
236220b248
fix(Email Trigger (IMAP) Node): Find new mail on servers with a broken IMAP4rev2 mode (#41018) 2026-10-15 14:33:49 +00:00
Toni Devine
af0202d840
feat(editor): Rework the WhatsApp channel setup to match the new design (#40923)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 14:33:41 +00:00
yehorkardash
adc6d35388
feat: Update Slack Bot to n8n rebrand (no-changelog) (#41203) 2026-10-15 14:25:48 +00:00
Svetoslav Dekov
b2b2a8641a
feat(editor): Attach skills hub skills from a picker in the agent builder (no-changelog) (#41104)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 14:14:18 +00:00
James Martin
952b1e73b2
fix(core): Display an error message when failing to connect to oauth credentials (#41058) 2026-10-15 14:03:47 +00:00
Anne Aguirre
f596162dc7
fix(core): Close agent after run when invoked from workflow (#41128) 2026-10-15 13:44:45 +00:00
MHMD
378847ae1e
refactor(core): Share the inbound admission steps between protected-resource surfaces (#41162) 2026-10-15 13:32:58 +00:00
n8n-cat-bot[bot]
9e3b52cbbb
fix: Restore command bar focus and standardize creation icons (#41185)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
2026-10-15 12:58:16 +00:00
Checo
054288e488
fix(GitlabTrigger): use NodeApiError.httpCode and safe description access in webhook checkExists (#35486)
Signed-off-by: sergioperezcheco <checo520@outlook.com>
2026-10-15 12:53:34 +00:00
Mike Repeć
c839d6cd49
fix(core): Remove unused N8N_SCHEDULER_MIN_INTERVAL setting (#41194) 2026-10-15 12:29:35 +00:00
Nour Alhadi Mahmoud
154cd63bbc
feat(editor): Add GitLab promotion connector setup (no-changelog) (#41019) 2026-10-15 11:58:24 +00:00
Ricardo Espinoza
3c447b42a7
fix(core): Key each OAuth authorization request to its own session (#39930)
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-10-15 11:43:45 +00:00
Yen Su
1659fa3737
feat(Databricks Node): Refuse the Vector Store metadata filter on non-standard endpoints (no-changelog) (#41047) 2026-10-15 11:18:08 +00:00
Luigi Gubello
9806798e89
chore: Update @langchain/langgraph to 1.4.21 (#40680)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 11:14:29 +00:00
Charlie Kolb
cdeb405948
refactor(core): Extract Workflow Builder session persistence into a backend module (#41075) 2026-10-15 10:56:13 +00:00
Dimitri Lavrenük
e45a65a9bf
fix(core): Improve base URL check for declarative nodes (#40778) 2026-10-15 10:51:15 +00:00
Charlie Kolb
b68bd3129b
refactor(core): Move auth registries and OAuth proxies into shared packages (#41088) 2026-10-15 10:32:13 +00:00
Lukas
e8ded61fe1
build: Update eslint-plugin-n8n-nodes-base to 2.0.0 for ESLint 10 (#38653)
Co-authored-by: Bernhard Wittmann <bernhard.wittmann@n8n.io>
2026-10-15 10:28:37 +00:00
Bernhard Wittmann
d046047dd0
feat(editor): Update the Microsoft Teams setup copy and give both flows a Done step (no-changelog) (#41010) 2026-10-15 10:10:06 +00:00
Srinjoy
f483ebd9d1
fix(MCP Trigger Node): Pass through MCP content arrays instead of double-wrapping tool results (#39131) 2026-10-15 10:08:42 +00:00
Luigi Gubello
fddef57734
chore: Update toml to 4.2.0 (#40672)
Some checks failed
CI: Master (Build, Test, Lint) / Build & format (push) Waiting to run
CI: Master (Build, Test, Lint) / Unit tests (24.18.1) (push) Waiting to run
CI: Master (Build, Test, Lint) / Unit tests (26.5.1) (push) Waiting to run
CI: Master (Build, Test, Lint) / Lint (push) Waiting to run
CI: Master (Build, Test, Lint) / Verify single-instance deps (npm-install) (push) Waiting to run
CI: Master (Build, Test, Lint) / Notify Slack on failure (push) Blocked by required conditions
Build: Benchmark Image / build (push) Has been cancelled
Util: Publish API Schema / Cloudflare Pages (push) Has been cancelled
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 09:34:39 +00:00
Sebastien Powell
ba97e46afc
fix(editor): Cleanup legacy button variants (#41175) 2026-10-15 09:31:34 +00:00
Anne Aguirre
4cb7a6ee3d
fix(core): Clear agent runtime cache when credential is updated (#40961) 2026-10-15 09:26:22 +00:00
Luigi Gubello
8ed8607a52
chore: Update simple-git to 4.0.2 (#40675)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 09:24:16 +00:00
Yen Su
67c1981f2f
fix(core): Let LLM failed-attempt handling retry and honour Retry-After (#41002) 2026-10-15 09:13:44 +00:00
Luigi Gubello
a8880cb449
chore: Update pdfjs-dist to 6.3.289 (#40677)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 09:13:43 +00:00
Stephen Wright
d411a44be0
feat(Databricks Node): Add the Lakebase Insert operation (no-changelog) (#40520) 2026-10-15 09:12:26 +00:00
Ricardo Espinoza
0c0b38e3cb
feat(core): Turn the skills hub on with an environment variable instead of a PostHog flag (no-changelog) (#41143) 2026-10-15 08:59:25 +00:00
Mayank SIngh
315e0a5377
fix(Convert to File Node): Decode data URIs and validate Base64 input (#38554)
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
2026-10-15 08:57:21 +00:00
Gigi
efd4e21aad
fix(core): Evict idle mcp-browser sessions to prevent connection leak (#36850)
Signed-off-by: Luigi Colluto <luigicolluto2006@gmail.com>
2026-10-15 08:56:27 +00:00
Jaakko Husso
bab83634ce
perf(core): Reuse the prebaked runtime skills bundle in the sandbox (no-changelog) (#41027)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 08:35:34 +00:00
n8n-assistant[bot]
d6c9b9fba3
chore: Update e2e impact map (#41163)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-10-15 08:18:58 +00:00
Luigi Gubello
b36befcb75
chore: Update nodemailer to 10.0.14 (#40674)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 08:09:12 +00:00
Charlie Kolb
ed2500ffb2
refactor(core): Move feature provider proxies into backend services (#41087) 2026-10-15 08:00:25 +00:00
n8n-cat-bot[bot]
9c0b546a30
chore: Bug - Delete project confirmation dialog button misaligned (#41160)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
2026-10-15 07:54:00 +00:00
Charlie Kolb
07595a0007
ci: Respect pnpm workspace exclusions in dependency verifier (#41159) 2026-10-15 07:48:25 +00:00
Nour Alhadi Mahmoud
1e67d0315d
feat(API): Add GitLab promotion repository and branch discovery (no-changelog) (#40788) 2026-10-15 07:15:40 +00:00
Charlie Kolb
338b0ca3fd
refactor(core): Move published workflow data into backend services (#41086) 2026-10-15 07:13:46 +00:00
Jaakko Husso
48edbef643
fix(core): Keep Observer detail lines instead of discarding the response (#41134)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 07:13:19 +00:00
Charlie Kolb
5d79c16f51
test(core): Stabilize transport timeout test (#41150) 2026-10-15 07:03:20 +00:00
Theodore Nastos
40094e363b
chore: Assign policy and OpenTelemetry code to Governance & Observability in OWNERS (no-changelog) (#41050) 2026-10-15 06:30:12 +00:00
Alex Antoniuk
e2529a8494
perf(editor): Load prettier only when the user formats code (#41014)
Some checks are pending
CI: Master (Build, Test, Lint) / Build & format (push) Waiting to run
CI: Master (Build, Test, Lint) / Unit tests (24.18.1) (push) Waiting to run
CI: Master (Build, Test, Lint) / Unit tests (26.5.1) (push) Waiting to run
CI: Master (Build, Test, Lint) / Lint (push) Waiting to run
CI: Master (Build, Test, Lint) / Verify single-instance deps (npm-install) (push) Waiting to run
CI: Master (Build, Test, Lint) / Notify Slack on failure (push) Blocked by required conditions
2026-10-14 21:48:42 +00:00
Raúl Gómez Morales
50c4ced224
fix(core): Keep pending setup for nodes that n8n Assistant added in an earlier build (#41070) 2026-10-14 16:52:18 +00:00
Bernhard Wittmann
b7a5500d16
feat(editor): Align the Microsoft Teams channel settings with the new design (no-changelog) (#41054) 2026-10-14 16:52:09 +00:00
Mayank SIngh
f6b24d0ae9
fix(editor): Display custom evaluation metrics correctly (#40852) 2026-10-14 16:15:34 +00:00
Raúl Gómez Morales
3c7dcf902d
feat(ai-builder): Write LangTracer results from the routing evals (no-changelog) (#40954) 2026-10-14 15:42:42 +00:00
Mike Repeć
fbe743b9df
fix(core): Charge element content for comparing array methods in native evaluation (no-changelog) (#41079) 2026-10-14 15:40:38 +00:00
Andreas Fitzek
f5924004ca
feat(core): Accept OAuth2 bearers on the public API through the trusted-source pipeline (no-changelog) (#41127)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-14 15:34:39 +00:00
Mike Repeć
b270d48686
fix(core): Charge node reference calls against the native evaluation work budget (no-changelog) (#41080) 2026-10-14 15:19:55 +00:00
Mike Repeć
3277f8c3b5
test(core): Give node reference parity cases a slim fixture item (no-changelog) (#41081) 2026-10-14 15:19:46 +00:00
Yuliia Pominchuk
a57c8f3aba
fix(core): Redact the parent execution when a sub-workflow uses a credential the viewer cannot use (#41085)
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-14 15:15:42 +00:00
Ricardo Espinoza
dcd312c5a9
feat(core): Copy inline agent skills into the skills hub at startup (no-changelog) (#41030)
Co-authored-by: Roman Makowiak <roman.makowiak@n8n.io>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 15:08:50 +00:00
Mike Repeć
a6d5b4de3f
test(core): Pin the quickjs nested -0 conversion in native evaluation parity (no-changelog) (#41092) 2026-10-14 15:04:02 +00:00
Temo
3968a60bf7
fix(ERPNext Node): Return each row once and tolerate empty Get Many options (#36860) 2026-10-14 14:51:43 +00:00
Theodore Nastos
17fb3c0e17
docs(API): Label type availability policy endpoints as Preview (no-changelog) (#41101) 2026-10-14 14:26:59 +00:00
Nikhil Kuriakose
b965e90307
test: Move Adore-owned specs to Knowledge and retire the Adore owner (#40933)
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-10-14 14:18:23 +00:00
Bernhard Wittmann
38135e1f83
feat: Read files attached in Microsoft Teams channels and group chats (no-changelog) (#41001) 2026-10-14 13:51:02 +00:00
Anne Aguirre
616062487a
feat(editor): Show agent personalisation icon in subagent modal (#41094) 2026-10-14 13:47:36 +00:00
Mike Repeć
1950434a27
fix(core): Create an XML parser for each webhook request body (#41098) 2026-10-14 13:42:48 +00:00
Anne Aguirre
b3e8b7d9ce
fix(editor): Use the deployment name on the Az Foundry path (#41103) 2026-10-14 13:35:40 +00:00
Jaakko Husso
56eac665c3
fix(core): Make the Assistant check the n8n side before it names a failure cause (no-changelog) (#41097)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 13:26:37 +00:00
Luigi Gubello
8bc7161ab1
feat(editor): Compare the QuickJS expression engine with legacy behind a flag (no-changelog) (#40496)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 13:24:09 +00:00
Uddish Verma
57e26feda4
feat(API): Add the Public API rate limit environment variables (no-changelog) (#41003)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 13:21:34 +00:00
n8n-cat-bot[bot]
9c42a76b1a
test: Stabilize user select email-filtering tests (#41095)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
2026-10-14 13:17:45 +00:00
Irénée
ae374e79ab
feat(core): Add a credential conflict policy to package import (no-changelog) (#40963)
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-14 13:11:32 +00:00
n8n-cat-bot[bot]
ef8d61bda7
fix(API): Check scope version when replacing policy attachments (no-changelog) (#40964)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
2026-10-14 13:06:15 +00:00
Ilfat Mindubaev
caeaff7230
feat(editor): Show the model that answered in the AI log header (#40988)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 12:58:13 +00:00
Johannes Nicolai
a3f47117cf
fix(OpenAI Node): Report token usage for image operations (#38463)
Co-authored-by: Claude <noreply@anthropic.com>
2026-10-14 12:56:44 +00:00
Toni Devine
9a524e7c21
feat(core): Add WhatsApp credential check and webhook handshake status (#40921)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 12:48:31 +00:00
Toni Devine
adedbbf560
feat(core): Add typing indicator, media download and media retention … (#40920)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 12:48:20 +00:00
Bernhard Wittmann
4eb63d6f41
feat: Update Browser Use extension to n8n rebrand (no-changelog) (#41096) 2026-10-14 12:38:28 +00:00
Charlie Kolb
b6d39e2d30
refactor(core): Move JWT support into backend services (#41089) 2026-10-14 12:10:59 +00:00
Sam Wooler
7920e6cf6b
refactor(API): Retire the express-openapi-validator stack from the Public API (no-changelog) (#40801)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 12:07:18 +00:00
Charlie Kolb
76c43094cd
refactor(core): Extract Quick Connect into a backend package (#41076) 2026-10-14 12:04:16 +00:00
Jaakko Husso
898701b05b
fix(core): Skip resource mapper validation when the mapped value is null (#41069)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 12:00:20 +00:00
Kai
f63111202f
fix(core): Confirm project deletions before instance apply (#40941) 2026-10-14 11:50:52 +00:00
Mike Repeć
633eacaae6
fix(editor): Use an outline button for bulk notify in the migration report (no-changelog) (#41084) 2026-10-14 11:29:29 +00:00
José Braulio González Valido
1b0cdd5998
fix(ai-builder): Report eval helper model failures as infrastructure, not as bad JSON (no-changelog) (#40415)
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-14 11:23:25 +00:00
Raúl Gómez Morales
578eea403d
fix(core): Stop asking to set up a node when n8n Assistant only adds a step next to it (#41067) 2026-10-14 11:20:43 +00:00
José Braulio González Valido
7c38974070
fix(ai-builder): Keep eval mock notes to data, not response layouts (no-changelog) (#40818)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 11:20:06 +00:00
Mike Repeć
d68ad7e97b
feat(core): Evaluate pure expression extensions in native expression evaluation (no-changelog) (#40865) 2026-10-14 10:49:37 +00:00
Declan Carroll
0650b9cce0
refactor: Move idle memory baselines to efficiency (#41037) 2026-10-14 10:32:11 +00:00
Mike Repeć
df7b3a19c6
fix(editor): Use outline buttons for secondary actions in the migration report (no-changelog) (#41072) 2026-10-14 10:28:04 +00:00
Mike Repeć
de42c9515d
feat(editor): Let admins notify workflow owners in bulk from the migration rule detail (#41060) 2026-10-14 10:26:05 +00:00
Jaakko Husso
d26f914e3d
fix(editor): Cancel the Assistant thread when the hand-off navigation fails (#41056)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 10:12:32 +00:00
Johannes Nicolai
8b5ea6d9e0
fix(editor): Stop auto-selecting the Evaluation Trigger over other triggers (#38883)
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Bernhard Wittmann <bernhard.wittmann@n8n.io>
2026-10-14 09:57:23 +00:00
Charlie Kolb
bfd4885026
ci: Automate get-n8n fallback version updates (#40973) 2026-10-14 09:53:55 +00:00
Stephen Wright
3800bce812
feat(Text Classifier Node): Add opt-in confidence scores to the output (#40743) 2026-10-14 09:42:05 +00:00
Mike Repeć
aae8eb63d7
docs: Add env-var-docs skill for JSDoc on @Env config properties (#41053) 2026-10-14 09:19:42 +00:00
Mutasem Aldmour
0b4c99f10d
fix(core): Pick output schemas by parameter value for Gmail and Notion (no-changelog) (#40757)
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-14 09:11:49 +00:00
n8n-cat-bot[bot]
066480d8db
fix: Check provisioning license on Users settings page (#41048)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
2026-10-14 08:59:23 +00:00
Florian Gareis
b8780d8fee
fix(HomeAssistant Node): Only sent Home Assistant attributes on upsert if set (#36298) 2026-10-14 08:58:22 +00:00
Anne Aguirre
e47940eecd
feat(editor): Publish agent modal and modal for warning about unpublished workflows (#40987)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-14 08:47:05 +00:00
Jaakko Husso
e3b2603d7b
chore(core): Remove activation-capped AI Assistant trial variant (no-changelog) (#41036)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 08:21:20 +00:00
Jaakko Husso
726a5829c8
fix(core): Use the module registry to decide if the Assistant has the Agents features (#41026)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 08:19:48 +00:00
n8n-assistant[bot]
54f1670791
chore: Update e2e impact map (#41051)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-10-14 08:18:31 +00:00
Jan
b3e8634c83
fix(editor): Apply the standard settings layout to the Context preferences page (#40716)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-14 08:02:30 +00:00
Rick Gale
1643a8598b
chore: Assign evaluation code to the AI Trust team (no-changelog) (#40918)
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
2026-10-14 08:01:17 +00:00
Anne Aguirre
9498088256
feat(core): Apply budget alerts and stops to sub-agent run (#40884)
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
2026-10-14 07:56:27 +00:00
Yuliia Pominchuk
da3491fc87
feat(editor): Link to the credential sharing tab when execution data is redacted (#40976) 2026-10-14 07:48:34 +00:00
Yen Su
d83f95b377
fix(Databricks Node): Mark the MLflow tool span ERROR when the tool returned an error string (no-changelog) (#40990) 2026-10-14 07:27:56 +00:00
n8n-assistant[bot]
46761a60a9
chore: Bundle/2.x (#41035)
Co-authored-by: Matsu <matias.huhta@n8n.io>
Co-authored-by: Bernhard Wittmann <bernhard.wittmann@n8n.io>
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-authored-by: Elias Meire <elias@meire.dev>
Co-authored-by: n8n-assistant[bot] <n8n-assistant[bot]@users.noreply.github.com>
Co-authored-by: João Almeida <jg.almeida56@gmail.com>
Co-authored-by: Savelii <savelii.sychov@n8n.io>
Co-authored-by: Rick Gale <richardgale90@gmail.com>
Co-authored-by: Emilia <100027345+sovietspaceship@users.noreply.github.com>
Co-authored-by: oleg <me@olegivaniv.com>
Co-authored-by: RomanDavydchuk <roman.davydchuk@n8n.io>
Co-authored-by: Stephen Wright <sjw948@gmail.com>
Co-authored-by: Yuliia Pominchuk <31064937+yuliia-pominchuk@users.noreply.github.com>
Co-authored-by: Shireen Missi <94372015+ShireenMissi@users.noreply.github.com>
Co-authored-by: Miguel Ángel Moreno <1411774+miguelsaddress@users.noreply.github.com>
Co-authored-by: Anne Aguirre <anne.aguirre@n8n.io>
Co-authored-by: Dimitri Lavrenük <20122620+dlavrenuek@users.noreply.github.com>
Co-authored-by: Kai <kai.hartlage@n8n.io>
Co-authored-by: Luigi Gubello <27034386+luigigubello@users.noreply.github.com>
Co-authored-by: Danny Martini <danny@n8n.io>
Co-authored-by: MHMD <45968454+dou-mhmd@users.noreply.github.com>
Co-authored-by: Mutasem Aldmour <4711238+mutdmour@users.noreply.github.com>
Co-authored-by: Iván Ovejero <ivov.src@gmail.com>
Co-authored-by: Charlie Kolb <charlie@n8n.io>
2026-10-14 06:33:56 +00:00
Bernhard Wittmann
ccf42e3213
fix(ai-builder): Make browser credential-setup evals grade the right thing (no-changelog) (#40858) 2026-10-14 06:24:28 +00:00
Svetoslav Dekov
94bdf85a8a
feat(core): Switch agent skills to the skills hub (no-changelog) (#40860)
Co-authored-by: Ricardo Espinoza <ricardo@n8n.io>
Co-authored-by: Roman Makowiak <roman.makowiak@n8n.io>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 00:12:36 +00:00
José Braulio González Valido
29de1c63ae
fix(ai-builder): Remove the Context7 docs lookup from the eval mock (no-changelog) (#40021)
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-13 21:30:08 +00:00
José Braulio González Valido
92f8eb51b3
chore(Message an Agent Node): Add output schemas (no-changelog) (#40821)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 21:29:33 +00:00
José Braulio González Valido
146d7804af
fix(ai-builder): Count eval runs that read another build's workflow as framework issues (no-changelog) (#40717)
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-13 21:29:00 +00:00
José Braulio González Valido
88de3cac20
feat(ai-builder): Move eval judges to Sonnet 5.5 and add per-role eval LLM settings (no-changelog) (#40330)
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-13 21:28:25 +00:00
Roman Makowiak
04f4afe3dc
feat(core): Add skills repository, service and REST API (no-changelog) (#40653)
Co-authored-by: Ricardo Espinoza <ricardo@n8n.io>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 20:28:55 +00:00
Nikhil Kuriakose
6924027e93
test: Move Adore-owned specs for Context features to Context (#40932)
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-10-13 19:41:28 +00:00
Nikhil Kuriakose
ef183e632d
test: Move Adore-owned specs for the updates panel to Cloud Growth (#40931)
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-10-13 19:41:27 +00:00
Nikhil Kuriakose
ed1c1e561a
test: Move Adore-owned specs for node features to NODES (#40930)
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-10-13 19:41:27 +00:00
Nikhil Kuriakose
a3a014422f
test: Move Adore-owned specs for Catalysts features to Catalysts (#40929)
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-10-13 19:41:26 +00:00
Ricardo Espinoza
f1fecf97e9
feat(core): Add skills hub tables (no-changelog) (#40464) 2026-10-13 19:03:23 +00:00
Andreas Fitzek
5da4d19e50
feat(core): Add the trusted sources management API (no-changelog) (#40975)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-13 18:24:34 +00:00
Uddish Verma
2019ea5593
feat(API): Expose the API key ID on the token grant (no-changelog) (#40986)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 18:16:50 +00:00
Guillaume Jacquart
4419fc4ba2
feat(core): Add the blocking project and opt-in state to policy violations (#40985)
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-13 17:49:51 +00:00
yehorkardash
52aed7ac97
fix(Execute Command Node): Don't spawn console on Windows (#40909) 2026-10-13 16:07:56 +00:00
n8n-cat-bot[bot]
1d2c879fab
fix: Pass structured inputs to OpenAI Responses tools (#40638)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
2026-10-13 16:07:14 +00:00
Rob Hough
bffe7d8392
feat(editor): Update UI with n8n rebrand (#40342)
Co-authored-by: Bob Hough <15361982+heymynameisrob@users.noreply.github.com>
Co-authored-by: linear-code[bot] <222613912+linear-code[bot]@users.noreply.github.com>
Co-authored-by: Sebastien Powell <sebpowell@users.noreply.github.com>
Co-authored-by: Tuukka Kantola <tuukka@n8n.io>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 16:06:30 +00:00
n8n-cat-bot[bot]
d245b17b89
fix: Allow skipping table initialization (#40437)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
2026-10-13 16:03:37 +00:00
daniel-hai
8977833ee2
feat(Monday.com Node): Add V2 with full V1 parity as the default (#35515)
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Stephen Wright <sjw948@gmail.com>
2026-10-13 15:53:55 +00:00
Alexander Gekov
acf5291d80
feat: Show Gemini models by name and list prioritized models first in model pickers (#40965)
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
2026-10-13 15:41:36 +00:00
Rob Hough
ebf078b64c
fix(editor): Restore the new session shortcut in agent chat (#40907)
Co-authored-by: Bob Hough <15361982+heymynameisrob@users.noreply.github.com>
Co-authored-by: linear-code[bot] <222613912+linear-code[bot]@users.noreply.github.com>
2026-10-13 15:29:20 +00:00
Charlie Kolb
9052f8ff3e
test(editor): Reduce publish timeline paging test work (#40993) 2026-10-13 15:05:18 +00:00
Kai
e0fe04084e
fix(core): Allow project promotion without an instance export (#40939) 2026-10-13 14:31:19 +00:00
Yen Su
e859abaf18
fix(Databricks Node): Bring the AI Agent panel to parity with AI Agent 2.2 (#40989) 2026-10-13 14:10:45 +00:00
Mike Repeć
a972eebe51
feat(editor): Let admins notify workflow owners from the migration rule detail (#40949) 2026-10-13 13:57:26 +00:00
yehorkardash
739c59e991
feat(core): Implement automated migrations for removed nodes (#40877) 2026-10-13 13:45:24 +00:00
Mike Repeć
de090cb2a0
feat(core): Evaluate primitive reads off $now and $today natively (no-changelog) (#40945) 2026-10-13 13:31:32 +00:00
MHMD
b97fe7c7d4
fix(core): Hide personal credentials from agents until they are fully supported (no-changelog) (#40789) 2026-10-13 13:31:17 +00:00
Riqwan Thamir
38e4aebfc8
perf(core): Reduce Agent Builder round trips in an initial build (no-changelog) (#40719)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-13 13:29:11 +00:00
Mike Repeć
d8a06666da
feat(core): Add an endpoint that notifies workflow owners about migration findings (#40952) 2026-10-13 13:07:59 +00:00
Bernhard Wittmann
60e0cb1e7b
feat(core): Publish the Microsoft Teams app to the organisation (no-changelog) (#40697)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-10-13 13:01:48 +00:00
Rup Sarmah
b09efb706c
fix(core): Handle every escaped closing bracket in an expression (#40487) 2026-10-13 12:58:10 +00:00
Tomi Turtiainen
f090f53480
refactor(core): Select CLI start trigger for workflow runs (#40375)
Co-authored-by: Danny Martini <danny@n8n.io>
2026-10-13 12:35:36 +00:00
Mike Repeć
347385b07b
feat(core): Treat notified migration findings as unresolved (#40951) 2026-10-13 12:19:30 +00:00
Guillaume Jacquart
bb45bca5ff
feat(editor): Show the policy reason when an agent tool call is refused (#40673)
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-13 12:19:29 +00:00
Sam Wooler
3075b60576
fix(API): Apply projectId filter to executions for member API keys (#40887)
Co-authored-by: Claude Haiku 5.5 <noreply@anthropic.com>
2026-10-13 12:03:20 +00:00
Sam Wooler
4fcb628fca
refactor(API): Migrate n8n-packages export endpoint to PublicApiController (no-changelog) (#40756)
Some checks failed
Build: Benchmark Image / build (push) Has been cancelled
CI: Master (Build, Test, Lint) / Build & format (push) Has been cancelled
CI: Master (Build, Test, Lint) / Unit tests (24.18.1) (push) Has been cancelled
CI: Master (Build, Test, Lint) / Unit tests (26.5.1) (push) Has been cancelled
CI: Master (Build, Test, Lint) / Lint (push) Has been cancelled
CI: Master (Build, Test, Lint) / Verify single-instance deps (npm-install) (push) Has been cancelled
Util: Publish API Schema / Cloudflare Pages (push) Has been cancelled
CI: Master (Build, Test, Lint) / Notify Slack on failure (push) Has been cancelled
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 11:56:11 +00:00
Bernhard Wittmann
1ee379898b
feat(core): Add the recommended Microsoft Teams channel setup (no-changelog) (#40696)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-10-13 11:55:56 +00:00
Lorent Lempereur
366ed6ed0e
refactor(core): Declare system tasks with a typed schedule and target (no-changelog) (#40703) 2026-10-13 11:42:37 +00:00
Anne Aguirre
248c86355b
fix(core): Describe MCP servers only from user or registry text in Agent Builder (#40974) 2026-10-13 11:19:46 +00:00
Stephen Wright
e5866ac6c1
feat(Kafka Node): Make version 2 the default for new Kafka nodes and triggers (#40744) 2026-10-13 11:07:10 +00:00
Jaakko Husso
2bc3265e63
perf(ai-builder): Trust a matching prebaked bundle manifest in the sandbox (no-changelog) (#40820)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 11:04:18 +00:00
Lorent Lempereur
f6861fe494
feat(core): Enable the durable scheduler by default (#40891)
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
2026-10-13 10:47:35 +00:00
Anne Aguirre
632d8b9eb0
feat(editor): Update budget panel when agent executes (#40971)
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
2026-10-13 10:44:26 +00:00
Declan Carroll
5549374241
chore: Upgrade agent harness to 0.6.0 (no-changelog) (#40967) 2026-10-13 10:28:40 +00:00
Alex Grozav
cba16247b9
feat(editor): Open the migration workflow from its name (no-changelog) (#40895)
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-13 10:24:15 +00:00
Alex Grozav
e528f948ce
fix(editor): Use full width for the migration rule header (no-changelog) (#40890)
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-13 10:24:02 +00:00
James Gee
d3bc2754b4
feat(core): Add create-with-values credential missing mode to package import (#36767) 2026-10-13 10:21:33 +00:00
Jaakko Husso
ffda639b3f
fix(editor): Show follow-up run events in the Assistant after a reconnect (#40883)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 10:12:33 +00:00
Declan Carroll
97e5ae56e4
test: Add Dive checks and reusable Playwright CLI fixtures (#40831) 2026-10-13 10:02:00 +00:00
yehorkardash
bda9118168
feat: Overhaul MCP tool permissions for Agents (#39878)
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
2026-10-13 09:54:46 +00:00
Csaba Tuncsik
96eff1efb9
fix(core): Reflect failed publications in the active workflows endpoints (#40641) 2026-10-13 09:49:33 +00:00
Guillaume Jacquart
eb66e1bde0
docs(API): Mark type availability policy endpoints as beta (#40959)
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-13 09:04:12 +00:00
Tomi Turtiainen
a8a028e2dc
docs: Clarify primary key selection order in migration skill (#40958)
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
2026-10-13 09:03:31 +00:00
Yen Su
44c8a312c3
feat(core): Append node tool attributions to agent replies and add Genie attribution and deep links to the Databricks node (#40765) 2026-10-13 08:42:57 +00:00
Declan Carroll
7bad46ee7e
chore: Upgrade agent harness to 0.5.1 (no-changelog) (#40956) 2026-10-13 08:34:04 +00:00
Veljko Terzic
45eea1bc35
feat(core): Remove the startup pass from workflow history compaction (#40886) 2026-10-13 08:02:51 +00:00
Stephen Wright
c8c8c3a81e
feat(Databricks Node): Add the Lakebase Get Many operation (no-changelog) (#40508) 2026-10-13 07:52:26 +00:00
Florian Gößler
e74f1c19da
fix(core): Log custom data truncation only above the 512-character limit (#40899)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-13 07:49:13 +00:00
Alex Grozav
f16c1375b1
feat(editor): Add bulk migration to the migration rule detail (no-changelog) (#40896)
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-13 07:48:53 +00:00
n8n-release-tag-merge[bot]
5146e3960f Merge tag 'n8n@2.44.0' 2026-10-13 07:44:08 +00:00
Yen Su
14c77900df
fix(Databricks Node): Rename Genie resource labels to Genie Agent and the MCP tile to Databricks Genie One (#40760) 2026-10-13 07:21:37 +00:00
n8n-assistant[bot]
bae368caca
🚀 Release 2.44.0 (#40943)
Co-authored-by: n8n-release-helper[bot] <265227495+n8n-release-helper[bot]@users.noreply.github.com>
2026-10-13 07:12:07 +00:00
Mike Repeć
f3e2bd83b3
feat(core): Suspend in-flight production executions at worker shutdown (#40568) 2026-10-13 07:04:07 +00:00
Jaakko Husso
27fcb968fb
fix(core): Require workflow create scope for single node runs in n8n Assistant (#40908)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 06:40:58 +00:00
n8n-assistant[bot]
2562744611
chore: Update e2e impact map (#40942)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-10-13 06:40:48 +00:00
Pierantonio Cangianiello
8d93e31d1d
chore: Make the create-pr skill write shorter PR descriptions for new reviewers (#40811)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 05:25:35 +00:00
ADITYA KUMAR JHA
2596f3ac1c
fix(core): Match regex case-insensitively when ignore case is enabled (#33589)
Co-authored-by: Michael Kret <michael.k@radency.com>
2026-10-13 04:03:35 +00:00
Svetoslav Dekov
e2c4054cba
feat(editor): Replace Windsurf with Devin in the MCP client dialog (#40912)
Some checks are pending
CI: Master (Build, Test, Lint) / Build & format (push) Waiting to run
CI: Master (Build, Test, Lint) / Unit tests (24.18.1) (push) Waiting to run
CI: Master (Build, Test, Lint) / Unit tests (26.5.1) (push) Waiting to run
CI: Master (Build, Test, Lint) / Lint (push) Waiting to run
CI: Master (Build, Test, Lint) / Verify single-instance deps (npm-install) (push) Waiting to run
CI: Master (Build, Test, Lint) / Notify Slack on failure (push) Blocked by required conditions
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 00:17:07 +00:00
Anne Aguirre
2ca4e55502
fix(core): Stabilise background child approval wait (#40834)
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
2026-10-12 21:18:01 +00:00
n8n-cat-bot[bot]
cea974e770
test: Stabilize MCP workflow select empty-state test (no-changelog) (#40926)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
2026-10-12 18:39:14 +00:00
Luka Zivkovic
ce20d1dea4
fix: Load agent evals by default and share the rollout flag (no-changelog) (#40915) 2026-10-12 16:42:27 +00:00
Svetoslav Dekov
53d1fd5991
feat(editor): Show last updated date on the Preferences page (no-changelog) (#40869)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 15:16:43 +00:00
Veljko Terzic
776f4e1944
feat(core): Run the execution pruning hard delete as a durable system task (no-changelog) (#40661) 2026-10-12 15:01:55 +00:00
Mike Repeć
3ff9c83bda
fix(core): Validate the size argument of the chunk() expression extension (#40880) 2026-10-12 14:54:04 +00:00
Guillaume Jacquart
89733cdb9a
feat(core): Send telemetry when a policy blocks an action (no-changelog) (#40871)
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-12 14:51:40 +00:00
Jaakko Husso
0209caafff
feat(editor): Let the Agents chat host render results of finished tool calls (no-changelog) (#40611)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 14:26:14 +00:00
Andreas Fitzek
0755255c35
feat(core): Add internal API for running the inbound authentication pipeline (no-changelog) (#40894) 2026-10-12 14:05:26 +00:00
James Martin
36ae8f74ce
chore(core): Seeds the default internal trusted source (no-changelog) (#40758)
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-12 13:55:20 +00:00
Nour Alhadi Mahmoud
160c126c6e
feat(core): Add GitLab promotion providers (no-changelog) (#40749) 2026-10-12 13:55:19 +00:00
Mike Repeć
d3deffbfa5
fix(core): Match the Brazilian real symbol in the in-isolate currency regex (#40893) 2026-10-12 13:55:17 +00:00
Anne Aguirre
b542f66119
fix(editor): Remove redundant description for create workflow option in agent config (#40882) 2026-10-12 13:54:15 +00:00
n8n-cat-bot[bot]
f0a38cfe58
feat: Add Antigravity to the MCP client connection dialog (#40879)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
Co-authored-by: Svetoslav Dekov <svetoslav.dekov@n8n.io>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 13:53:19 +00:00
Jan Kalkan
27b43de85f
feat(core): Pair data table columns by id on package import (#40709)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 13:40:47 +00:00
Alex Grozav
7ceb84f06c
fix(editor): Fix the last updated label in the migration report (no-changelog) (#40889)
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-12 13:38:00 +00:00
Alex Grozav
2a51f38d55
fix(editor): Rename the "Executions fail" label to "Executions will fail" (#40888)
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-12 13:37:54 +00:00
Alex Grozav
e0958fe094
feat(editor): Suggest won't fix for workflows that have not run in 12 months (no-changelog) (#40782)
Co-authored-by: Michael Siega <michael.siega@n8n.io>
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-12 13:33:30 +00:00
MHMD
bc8b803471
test: Wait for the Overview list before the sidebar click in a flaky project test (no-changelog) (#40864) 2026-10-12 13:26:08 +00:00
Danny Martini
26d93b601a
docs(engine): Record the resume-token decision (no-changelog) (#39089)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-10-12 13:17:09 +00:00
Pierantonio Cangianiello
c9cb3ee3ab
fix(core): Let the editor download binary data of an Engine v2 execution (#40663) 2026-10-12 13:08:38 +00:00
Svetoslav Dekov
22ff2a0636
feat(editor): Show who created each preference on the Preferences page (#40868)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 13:04:59 +00:00
Alex Grozav
7dae1b64de
feat(editor): Add bulk state actions to the migration rule detail (no-changelog) (#40780)
Co-authored-by: Michael Siega <michael.siega@n8n.io>
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-12 12:51:08 +00:00
Stephen Wright
27aa660a74
fix(Microsoft SharePoint Trigger Node): Name the real cause in the missing-target error (no-changelog) (#40400) 2026-10-12 12:44:08 +00:00
Veljko Terzic
6cd3539b1e
feat(core): Run the pending-authorization cleanup as a durable system task (no-changelog) (#40859) 2026-10-12 12:17:23 +00:00
Jaakko Husso
97ca4c49e0
feat(editor): Let the Agents chat host provide its own interactive cards (no-changelog) (#40609)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 12:08:23 +00:00
Robin Braumann
bd22105646
feat(core): Add Google Vertex AI support for agents (no-changelog) (#40545) 2026-10-12 12:05:53 +00:00
Lorent Lempereur
e16066c322
feat(core): Give long system tasks an explicit timeout (no-changelog) (#40521) 2026-10-12 12:00:59 +00:00
Andreas Fitzek
7cd9fcd3d1
feat(core): Map roles from trusted-source rules on resolve (no-changelog) (#40809)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-12 11:53:47 +00:00
Mutasem Aldmour
2176819865
fix(core): Validate execution timeout against the configured maximum in Instance AI saves (#40769)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 11:48:02 +00:00
Ali Elkhateeb
d038f89e91
feat(API): Delete expired Public API idempotency keys (no-changelog) (#40748)
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Sam Wooler <swooler592@gmail.com>
2026-10-12 11:45:00 +00:00
Lorent Lempereur
3dd805d9ed
feat(core): Bound a poll by the timeout of its occurrence (no-changelog) (#40518)
Some checks are pending
Build: Benchmark Image / build (push) Waiting to run
CI: Master (Build, Test, Lint) / Build & format (push) Waiting to run
CI: Master (Build, Test, Lint) / Unit tests (24.18.1) (push) Waiting to run
CI: Master (Build, Test, Lint) / Unit tests (26.5.1) (push) Waiting to run
CI: Master (Build, Test, Lint) / Lint (push) Waiting to run
CI: Master (Build, Test, Lint) / Verify single-instance deps (npm-install) (push) Waiting to run
CI: Master (Build, Test, Lint) / Notify Slack on failure (push) Blocked by required conditions
Co-authored-by: Veljko Terzic <veljko.terzic@n8n.io>
2026-10-12 11:31:52 +00:00
Michael Drury
16e29da52f
fix(editor): Keep typed characters in Markdown fields (#40805) 2026-10-12 11:25:33 +00:00
n8n-cat-bot[bot]
b8b0387a31
fix: Use ID token claims when UserInfo is unavailable (#40779)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
Co-authored-by: Andreas Fitzek <andreas.fitzek@n8n.io>
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-12 11:14:06 +00:00
Lorent Lempereur
8b6352a6fa
feat(core): Report a timed-out system task run as timed out (no-changelog) (#40516)
Co-authored-by: Veljko Terzic <veljko.terzic@n8n.io>
2026-10-12 10:46:12 +00:00
Lorent Lempereur
839b841dda
feat(core): Stop a scheduled task run at its timeout (no-changelog) (#40515)
Co-authored-by: Veljko Terzic <veljko.terzic@n8n.io>
2026-10-12 10:46:11 +00:00
Lorent Lempereur
53a6019ad5
feat(core): Store and provision the timeout of scheduled jobs (no-changelog) (#40513) 2026-10-12 10:46:10 +00:00
Lorent Lempereur
2a1fde3634
feat(core): Add settings for the timeout of scheduled tasks (no-changelog) (#40512) 2026-10-12 10:46:09 +00:00
Mutasem Aldmour
37799ad2f2
fix(editor): Align nodes panel to the embedded canvas in the n8n Assistant split view (#40795)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 10:29:40 +00:00
n8n-cat-bot[bot]
a1800105ef
fix: Report resource-operation violations as errors (#40830)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
2026-10-12 09:52:15 +00:00
Aryan Bahl
6b77613ee8
fix(Odoo Node): Normalize datetime fields to Odoo format before write (#34186)
Co-authored-by: Toni Devine <tonidevine24@gmail.com>
Co-authored-by: Toni Devine <118546181+devinetoni@users.noreply.github.com>
2026-10-12 09:46:07 +00:00
n8n-cat-bot[bot]
205c1c3ee5
refactor: Share appender for connection changes (#39472)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
2026-10-12 09:11:50 +00:00
Andreas Fitzek
65cc931018
feat(core): Link and provision users from trusted-source identities (no-changelog) (#40755)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-12 09:01:17 +00:00
Andreas Fitzek
3dd3efc42f
feat(core): Resolve trusted-source subjects to users on the read path (no-changelog) (#40698)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-12 09:01:16 +00:00
Svetoslav Dekov
ca9b10054c
refactor(core): Route agent skill refs through one service (#40754)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 08:25:58 +00:00
Jaakko Husso
c9f54fab4d
feat(editor): Add composer and message extensions to the Agents chat panel (no-changelog) (#40613)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 08:17:36 +00:00
Jaakko Husso
82d21934d3
feat(editor): Add a host extension API to the Agents chat panel (no-changelog) (#40608)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 08:17:35 +00:00
Lorent Lempereur
1f27b98366
feat(core): Add a timeout column to the scheduler tables (no-changelog) (#40510) 2026-10-12 08:08:45 +00:00
Nikhil Kuriakose
30d883e866
chore: Assign the CLI skill commands to Context in OWNERS (no-changelog) (#40766)
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-10-12 08:04:32 +00:00
Robin Braumann
3ec89b24dc
feat(core): Add first message time to agent telemetry (no-changelog) (#40857) 2026-10-12 07:29:04 +00:00
Luigi Gubello
2e049bb3de
chore: Remove xregexp from the FTP client (#40676)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: RomanDavydchuk <roman.davydchuk@n8n.io>
2026-10-12 07:21:06 +00:00
Robin Braumann
8297b2e6d2
fix(editor): Load node types before authenticated pages render (#40812) 2026-10-12 07:18:43 +00:00
Elias Meire
601304543d
fix(editor): Keep same-named parameter values apart when their types differ (#40170) 2026-10-12 07:05:03 +00:00
n8n-assistant[bot]
ceaa82cd4f
chore: Update e2e impact map (#40853)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-10-12 06:24:15 +00:00
n8n-assistant[bot]
444e4a9384
chore: Update node popularity data (#40849)
Some checks are pending
CI: Master (Build, Test, Lint) / Build & format (push) Waiting to run
CI: Master (Build, Test, Lint) / Unit tests (24.18.1) (push) Waiting to run
CI: Master (Build, Test, Lint) / Unit tests (26.5.1) (push) Waiting to run
CI: Master (Build, Test, Lint) / Lint (push) Waiting to run
CI: Master (Build, Test, Lint) / Verify single-instance deps (npm-install) (push) Waiting to run
CI: Master (Build, Test, Lint) / Notify Slack on failure (push) Blocked by required conditions
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-10-12 00:49:02 +00:00
n8n-cat-bot[bot]
45938389b8
fix: Wait for MockServer HTTP status without following logs (#40846)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
2026-10-11 22:09:03 +00:00
Jakub Kadlubiec
3301b2cc0f
chore: Move Assistant frontend and AI usage tests ownership to Assistant (no-changelog) (#40804)
Some checks failed
CI: Master (Build, Test, Lint) / Build & format (push) Waiting to run
CI: Master (Build, Test, Lint) / Unit tests (24.18.1) (push) Waiting to run
CI: Master (Build, Test, Lint) / Unit tests (26.5.1) (push) Waiting to run
CI: Master (Build, Test, Lint) / Lint (push) Waiting to run
CI: Master (Build, Test, Lint) / Verify single-instance deps (npm-install) (push) Waiting to run
CI: Master (Build, Test, Lint) / Notify Slack on failure (push) Blocked by required conditions
Util: Update Node Popularity / update-popularity (push) Has been cancelled
Util: Update Credential Setupability / update-setupability (push) Has been cancelled
Util: Update Node Popularity / approve-and-automerge (push) Has been cancelled
Util: Update Credential Setupability / approve-and-automerge (push) Has been cancelled
2026-10-11 11:50:01 +00:00
n8n-assistant[bot]
529b0d1c3b
chore: Update e2e impact map (#40828)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-10-11 06:14:03 +00:00
Bernhard Wittmann
7424f0ca04
feat(editor): Add an upload step to the Microsoft Teams setup (no-changelog) (#40339) 2026-10-09 22:13:36 +00:00
Alex Antoniuk
a33ecf7481
refactor(editor): Move log streaming route and modal to a module descriptor (no-changelog) (#40426) 2026-10-09 20:23:49 +00:00
Alex Antoniuk
3085f45f71
feat(editor): Add placeholder page for inactive frontend modules (no-changelog) (#40553) 2026-10-09 20:23:48 +00:00
José Braulio González Valido
ed4fbf4ed3
fix(ai-builder): Make eval file fixtures match the scenario (no-changelog) (#40725)
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-09 18:55:59 +00:00
Sam Wooler
feed158ba2
feat(API): Support binary response bodies on Public API controller routes (no-changelog) (#40704)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 17:46:14 +00:00
Jaakko Husso
9ff8d4fc73
fix(core): Handle each Chat Hub segment once in queue mode (#40791)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 17:07:01 +00:00
José Braulio González Valido
5bc6021477
fix(ai-builder): Run agents that a workflow calls with mocked tools in eval runs (no-changelog) (#40584)
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-09 15:56:40 +00:00
Toni Devine
50c2ca0a27
feat(core): Handle inbound media handling for whatsapp (#39409)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 15:24:15 +00:00
Jaakko Husso
d6370a0002
fix(editor): Show one attach button in the agent chat composer (no-changelog) (#40810)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 15:09:42 +00:00
Jaakko Husso
44a6370aba
feat(core): Send client context with Agents chat messages (no-changelog) (#40612)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 15:09:17 +00:00
Robin Braumann
ef000908e7
docs: Link Google Vertex AI credential documentation (#40729) 2026-10-09 15:01:53 +00:00
Ali Elkhateeb
cdf3787834
feat(API): Add an idempotency key table for the Public API (no-changelog) (#40381)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-09 14:52:36 +00:00
Anne Aguirre
5e01906a1e
feat(core): Post a budget-stop message on external agent channels (#40767) 2026-10-09 14:41:51 +00:00
Mike Repeć
f05b29abeb
test(core): Skip radix toString parity under quickjs in native evaluation fuzz (no-changelog) (#40800) 2026-10-09 14:33:43 +00:00
Guillaume Jacquart
b33b0db040
feat(core): Add an extends selector to credential type policies (no-changelog) (#40305)
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-09 14:33:37 +00:00
Guillaume Jacquart
d1422fb652
refactor(core): Split type availability policy DTOs by policy kind (no-changelog) (#40602)
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-09 14:33:29 +00:00
Rúben Castro
87c9b1c52a
fix(core): Move the policy scope version by one on each accepted write (no-changelog) (#40802)
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-10-09 14:31:18 +00:00
Mutasem Aldmour
be402be262
fix(editor): Keep the prompt visible while the assistant starts a new thread (#40796)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 14:29:57 +00:00
Robin Braumann
cdc23c666f
feat(editor): Add project selection to Vertex AI credentials (#40667) 2026-10-09 14:23:57 +00:00
Iván Ovejero
bbd35e281b
fix(core): Keep waiting executions unchanged when a resume request fails (#40683)
Co-authored-by: Danny Martini <danny@n8n.io>
2026-10-09 14:23:36 +00:00
Mutasem Aldmour
55eb2ce442
fix(editor): Keep the workflow setup step counter on one line (#40771)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 14:12:42 +00:00
Mutasem Aldmour
4032797790
fix(editor): Show the renamed agent name in the assistant artifact tab (#40770)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 14:12:33 +00:00
ocdejong
c7135451eb
feat(core): Emit opt-in start marker spans for workflow and node executions (#40762)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 14:09:39 +00:00
Charlie Kolb
88a2531558
refactor(core): Move data-table dependency services to shared package (#40526) 2026-10-09 13:54:56 +00:00
Theodore Nastos
75de193d90
feat(editor): Explain a restricted model provider with the restriction popover (#40705) 2026-10-09 13:52:20 +00:00
Jaakko Husso
ee8a608a22
fix(core): Let the streaming heartbeat stop on Chat Hub responses (#40792)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 13:43:59 +00:00
Jaakko Husso
55def8cc2b
feat(core): Add atomic thread metadata patches to Agents memory (no-changelog) (#40610)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 13:41:05 +00:00
Alex Grozav
85f690d3b9
feat(editor): Add filters and sorting to migration rule details (no-changelog) (#40781)
Co-authored-by: Michael Siega <michael.siega@n8n.io>
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-09 13:36:01 +00:00
Yen Su
c2e625bbb0
fix(Databricks Node): Reset dependent Lakebase locators and pin the picker filters (no-changelog) (#40787) 2026-10-09 13:21:15 +00:00
Thanasis G
344dea5c77
feat(core): Report the deployment artifact in telemetry (#40319)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 13:14:32 +00:00
Theodore Nastos
b28dceb462
fix(editor): Place the restricted node popover per host, keep one open, and stack the admin dialog (#40678) 2026-10-09 13:12:49 +00:00
Robin Braumann
a7cba90a85
feat: Add Google Vertex AI credentials (#40665) 2026-10-09 13:06:56 +00:00
Andreas Fitzek
0d7df52a45
feat(core): Translate trusted-source claims into an external identity (no-changelog) (#40681)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-09 13:06:31 +00:00
Robin Braumann
55200605dd
docs: Keep routine pre-commit checks lightweight (#40790) 2026-10-09 13:05:11 +00:00
Theodore Nastos
cde7a9dbd9
feat(editor): Disable restricted model providers in the agent model selector (#40658) 2026-10-09 12:59:23 +00:00
Alex Grozav
ff7ef60841
feat(editor): Redesign the migration rule detail list (no-changelog) (#40654)
Co-authored-by: Michael Siega <michael.siega@n8n.io>
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-09 12:50:38 +00:00
Benjamin Schroth
c92141d141
fix(editor): Fix header on n8n Chat page (no-changelog) (#40737) 2026-10-09 12:47:20 +00:00
Benjamin Schroth
9e1866736c
feat(editor): Always show n8n Chat in agent channels (no-changelog) (#40784) 2026-10-09 12:42:22 +00:00
ocdejong
22ba4e7bc5
feat(core): Add execution identity to every OTel span (#40649)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 12:41:36 +00:00
Yuliia Pominchuk
033a9b512c
fix(core): Allow reverting a workflow version that uses credentials the user cannot use (#40701) 2026-10-09 12:37:11 +00:00
Anne Aguirre
e2b4fee0ec
fix(editor): Keep Agent Builder settings inside a narrow column (#40759) 2026-10-09 12:28:20 +00:00
mfsiega
35078f1d04
feat(core): Let admins assign the owner of a workflow's migration findings (no-changelog) (#40452)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-09 12:15:27 +00:00
Mike Repeć
a2ac242b91
feat(core): Accept array literals and single-parameter callbacks in native expression evaluation (no-changelog) (#40292) 2026-10-09 12:08:12 +00:00
Robin Braumann
e64dc46055
feat(API): Export Agents in n8n packages (no-changelog) (#40428) 2026-10-09 12:05:33 +00:00
Yen Su
fd06e07efa
feat(Databricks Node): Add the Lakebase pickers and endpoint URL assembly (no-changelog) (#40407) 2026-10-09 11:47:21 +00:00
Robin Braumann
21af799450
feat(core): Add source breakdowns to agent telemetry (no-changelog) (#40726) 2026-10-09 11:42:26 +00:00
mfsiega
fd04f01e8e
feat(core): Show the suggested owner of each affected workflow (no-changelog) (#40394)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-09 11:04:19 +00:00
n8n-cat-bot[bot]
af98327a5e
fix: Keep execution details clear of preview canvas (#40176)
Some checks failed
Build: Benchmark Image / build (push) Has been cancelled
CI: Master (Build, Test, Lint) / Build & format (push) Has been cancelled
CI: Master (Build, Test, Lint) / Unit tests (24.18.1) (push) Has been cancelled
CI: Master (Build, Test, Lint) / Unit tests (26.5.1) (push) Has been cancelled
CI: Master (Build, Test, Lint) / Lint (push) Has been cancelled
CI: Master (Build, Test, Lint) / Verify single-instance deps (npm-install) (push) Has been cancelled
Util: Publish API Schema / Cloudflare Pages (push) Has been cancelled
CI: Master (Build, Test, Lint) / Notify Slack on failure (push) Has been cancelled
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
2026-10-09 10:46:58 +00:00
Raúl Gómez Morales
c21ed4f13a
fix(Google Gemini Chat Model Node): Point the AI builder to the model catalog for new Gemini models (#40159)
Co-authored-by: Mutasem Aldmour <mutasem@n8n.io>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 10:43:38 +00:00
Anne Aguirre
8b7065a876
fix(core): Block the assistant when AI credits hit zero (#40659) 2026-10-09 10:39:03 +00:00
Garrit Franke
8f9f673b86
feat(core): Support minor levels in n8nNodesApiVersion (no-changelog) (#39308)
Co-authored-by: Benny Neugebauer <mail@bennycode.com>
2026-10-09 10:35:40 +00:00
Irénée
a3391ba93c
fix(core): Check the credential save policy before package import creates stubs (#40687)
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-09 10:30:47 +00:00
James Martin
56e2802e67
chore(core): Moves the grant authorization method into the TrustedSourceGate (no-changelog) (#40571) 2026-10-09 10:27:04 +00:00
Robin Braumann
aee0b23b8e
feat(core): Add Agent package selection and dependency resolution (no-changelog) (#40418) 2026-10-09 10:22:12 +00:00
mfsiega
d3173d3893
fix(core): Remove the Execute Sub-workflow each-mode breaking-change rule and migration (no-changelog) (#40753)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-09 10:17:18 +00:00
MHMD
19337237ec
fix(editor): Show the unavailable credential at the top of the node picker (#40693) 2026-10-09 10:01:39 +00:00
Raúl Gómez Morales
d900743bce
fix(core): Improve node placement when n8n Assistant edits a workflow (#40139)
Co-authored-by: Mutasem Aldmour <mutasem@n8n.io>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 09:56:40 +00:00
Irénée
978f810bbe
fix(core): Remove credentials of deleted projects on source control pull (#40692)
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-09 09:29:07 +00:00
Rodrigo Santos da Silva
ae15984493
feat(editor): Suggest and apply fixes for failed agent eval checks (no-changelog) (#40711)
Co-authored-by: Claude <noreply@anthropic.com>
2026-10-09 09:27:15 +00:00
Mutasem Aldmour
9f85b8379c
fix(editor): Remove trailing newline from AI Assistant code blocks (no-changelog) (#40731)
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-09 09:24:16 +00:00
Mike Repeć
24b9db41b3
feat(core): Declare what on main awaits a queued run's outcome (no-changelog) (#40567) 2026-10-09 09:05:07 +00:00
Robin Braumann
503092ccca
feat(core): Add the Agent package export adapter (no-changelog) (#40402) 2026-10-09 08:59:58 +00:00
Robin Braumann
16a8db6675
feat(core): Define and validate the Agent package format (no-changelog) (#40391) 2026-10-09 08:59:57 +00:00
Jaakko Husso
bc0a9cbc11
fix(core): Report approval tools with no tool connected in workflow validation (#40561)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 08:56:43 +00:00
mfsiega
c6f1e20d9e
feat(engine): Accept seeded steps on execution start (#40456)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-09 08:56:37 +00:00
Sandra Zollner
a249e20290
feat(core): Promote folder and project renames alongside selected workflows (#40502)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-09 08:51:21 +00:00
Raúl Gómez Morales
1c84af2e96
fix(core): Resume tool calls for deferred tools that a skill loaded (#40126)
Co-authored-by: Mutasem Aldmour <mutasem@n8n.io>
2026-10-09 08:49:16 +00:00
Nikhil Kuriakose
aca9033dca
chore: Split Adore OWNERS entries into Context and Knowledge (no-changelog) (#40355)
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-10-09 08:28:57 +00:00
mfsiega
2efdd3e021
feat(core): Add the migration owner suggestion service (no-changelog) (#40224)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-09 08:28:25 +00:00
Theodore Nastos
01da88958d
feat(editor): Show the restriction callout and lock the agent tool configuration form (#40657) 2026-10-09 08:09:15 +00:00
James Martin
6f5f79321b
chore(core): Adds LocalAuthorizationServer implementation for token authentication (no-changelog) (#40702) 2026-10-09 07:56:57 +00:00
Pierantonio Cangianiello
690fbb525a
test(core): Prove binary data on engine v2 across the planes in the container parity suite (no-changelog) (#40417) 2026-10-09 07:53:35 +00:00
José Braulio González Valido
5480258fb5
feat(ai-builder): Report the eval harness's own model usage per case (no-changelog) (#40718)
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-09 07:52:09 +00:00
José Braulio González Valido
bfebfd26c1
feat(ai-builder): Move eval mocks to Sonnet 5.5 (no-changelog) (#40721)
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-09 07:50:47 +00:00
Robin Braumann
806a2aa94b
refactor(core): Use typed package requirement consumers (#40621) 2026-10-09 07:42:23 +00:00
Dmitrii
13679fcb97
feat(core): Report insights billable executions as the daily instance metric (no-changelog) (#40536) 2026-10-09 06:57:43 +00:00
Svetoslav Dekov
75a9cc7e12
feat(core): Search, create and reopen agents from the command bar (#40504)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 06:29:53 +00:00
n8n-assistant[bot]
03248c11bf
chore: Update e2e impact map (#40733)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-10-09 06:25:02 +00:00
n8n-cat-bot[bot]
23b6de7028
test: Assign Chat Hub E2E specs to Assistant (#40732)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
2026-10-09 06:21:50 +00:00
Predrag Ristic
53d33b0f95
fix(core): Support streamed reasoning from custom AI endpoints (no-changelog) (#37875) 2026-10-09 05:45:07 +00:00
Jakub Kadlubiec
c4f84e2f78
test: Remove flaky Chat Hub conversation flow e2e test (no-changelog) (#40728)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 05:36:24 +00:00
Jan
3ba522910b
fix(editor): Use pulsing status dot for the enabled n8n Assistant setting (#40712)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-09 05:35:12 +00:00
José Braulio González Valido
6fc0aeda35
fix(ai-builder): Read eval pin data when the model explains it after the JSON (no-changelog) (#40682)
Some checks are pending
CI: Master (Build, Test, Lint) / Build & format (push) Waiting to run
CI: Master (Build, Test, Lint) / Unit tests (24.18.1) (push) Waiting to run
CI: Master (Build, Test, Lint) / Unit tests (26.5.1) (push) Waiting to run
CI: Master (Build, Test, Lint) / Lint (push) Waiting to run
CI: Master (Build, Test, Lint) / Verify single-instance deps (npm-install) (push) Waiting to run
CI: Master (Build, Test, Lint) / Notify Slack on failure (push) Blocked by required conditions
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-08 21:57:15 +00:00
José Braulio González Valido
5b78e8b655
fix(ai-builder): Run each eval build in its own empty project (no-changelog) (#40715)
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-08 21:15:11 +00:00
Alex Antoniuk
cef87cfcab
feat(editor): Add order to settings sidebar links (#40505)
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-08 20:48:14 +00:00
Charlie Kolb
a2c8142013
ci: Remove the /test-workflows PR comment trigger (no-changelog) (#40688)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-08 17:54:09 +00:00
Luigi Gubello
cabf0bf3b4
feat(core): Serve the instance CSP on all non-script responses (#40205)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Danny Martini <danny@n8n.io>
2026-10-08 17:33:44 +00:00
n8n-cat-bot[bot]
84d2fd6f3a
fix: Align execution retry menu and clarify actions (#40710)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
2026-10-08 17:16:52 +00:00
Sam Wooler
ce1be847c8
refactor(API): Migrate n8n-packages import-selection endpoint to PublicApiController (no-changelog) (#40554)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 16:12:09 +00:00
Anne Aguirre
fe26ba8d3a
feat(core): Experiment to show assistant composer and starter templates in agent list (#40396)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-08 15:50:16 +00:00
n8n-cat-bot[bot]
03f7c76acd
fix: Update runtime and MCP dependency APIs (no-changelog) (#40204)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
Co-authored-by: Michael Drury <michael.drury@n8n.io>
2026-10-08 15:43:51 +00:00
Raúl Gómez Morales
e67e5a9ef1
test(core): Run routing cases that start with state (no-changelog) (#40378) 2026-10-08 15:33:58 +00:00
Benjamin Schroth
3a37ec1dcc
feat: Show when an n8n Chat agent is no longer available (no-changelog) (#40430) 2026-10-08 15:25:09 +00:00
Uddish Verma
77a692a4bb
fix(API): Give a union of object schemas a root type: object in the generated spec (no-changelog) (#40409)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 15:24:14 +00:00
mfsiega
6a41a5ed39
feat(core): Send telemetry with the migration report overview counts (no-changelog) (#40387)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-08 15:24:12 +00:00
mfsiega
ea8417e29a
feat(core): Block create/update of workflows with deprecated nodes (#31360)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: Danny Martini <danny@n8n.io>
Co-authored-by: Emilia <100027345+sovietspaceship@users.noreply.github.com>
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
2026-10-08 14:39:14 +00:00
Benjamin Schroth
5543023723
feat: Show the attach button and sub-agent names in n8n Chat (no-changelog) (#40429) 2026-10-08 14:38:28 +00:00
Arvin A
05a4abd700
fix(core): Label instance activity entry IDs (no-changelog) (#40691) 2026-10-08 14:32:43 +00:00
Benjamin Schroth
740159411a
feat: Search and delete n8n Chat agent threads (no-changelog) (#40431) 2026-10-08 14:29:26 +00:00
Danny Martini
346cbaa316
docs: Add a README for the ADR directories (no-changelog) (#39006)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-08 14:28:52 +00:00
Lorent Lempereur
a83c438af4
refactor(core): Keep metrics database reads behind a guarded query service (#40221) 2026-10-08 14:20:12 +00:00
Mike Repeć
49a3812bc3
feat(core): Extend native expression evaluation to node references and more data roots (no-changelog) (#40239) 2026-10-08 14:11:19 +00:00
Jan Kalkan
9946cfe901
feat(core): Allow confirming destructive data table changes when continuing an apply (#40491)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 14:06:33 +00:00
Yen Su
4c0902f6bf
fix(Databricks Node): Request float encoding for embeddings from the Unity AI Gateway (#40684) 2026-10-08 13:51:27 +00:00
Arvin A
d7e9b00234
feat(core): Index activity entries by resource (no-changelog) (#38040) 2026-10-08 13:50:52 +00:00
MHMD
63dc285493
fix(editor): Show the owner and credential type for credentials the user cannot use (#40669) 2026-10-08 13:49:45 +00:00
Sam Wooler
5a46327401
refactor(API): Migrate n8n-packages import endpoint to PublicApiController (no-changelog) (#40216)
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-10-08 13:38:52 +00:00
Jaakko Husso
1ad3c54803
feat(editor): Recommend Claude Opus 5.5 and GPT-6 models in AI Assistant setup (#40660)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 13:23:49 +00:00
Jan Kalkan
5d208c85ed
feat(core): Report destructive data table changes in the promotion apply preflight (#40454)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 13:10:08 +00:00
Anne Aguirre
209adf9a7e
feat(core): Show the agent chat attach button on the left of the composer (#40320)
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-08 13:07:33 +00:00
Rob Hough
04e1e7fc47
feat(editor): Animate the n8n Assistant activity indicator (#40497) 2026-10-08 13:05:29 +00:00
Riqwan Thamir
2aa3fbf185
perf(core): Send agent builder config writes as objects with changed fields only (#40666) 2026-10-08 12:55:54 +00:00
Uddish Verma
983f5ecea8
refactor(API): Rename the log streaming destination path parameter to destinationId (no-changelog) (#40533)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 12:42:58 +00:00
Lorent Lempereur
4e217aeac6
feat(core): Serve Prometheus metrics while the database is unavailable (#39908) 2026-10-08 12:41:12 +00:00
Lorent Lempereur
d904e7a247
feat(core): Add Prometheus metrics for database connection recovery (#39890) 2026-10-08 12:41:11 +00:00
Michael Drury
f7e52a851e
fix(editor): Add resend to retryable Agent preview errors (#40592) 2026-10-08 12:37:33 +00:00
Michael Drury
1e6da4e615
fix: Separate agent response steps and show saved reasoning (#40590) 2026-10-08 12:33:01 +00:00
Michael Drury
40c0525bed
fix(core): Use the native Vercel gateway endpoint for agents (no-changelog) (#40524) 2026-10-08 12:32:53 +00:00
Srinjoy
0f02b509be
fix(editor): Preserve Live Data highlights on scroll in expression editor (#33842)
Co-authored-by: Alex Grozav <alex@grozav.com>
Co-authored-by: alex <alex@n8n.io>
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-08 12:28:21 +00:00
n8n-cat-bot[bot]
8ad9b5415e
docs: Document workflow activation with activeVersionId (#40500)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
Co-authored-by: Sam Wooler <swooler592@gmail.com>
2026-10-08 12:16:52 +00:00
Alex Grozav
e11fe8afb0
feat(editor): Show open and won't fix counts on migration report cards (no-changelog) (#40652)
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-08 12:15:04 +00:00
Dimitri Lavrenük
fdc394819c
fix: Prevent node types corruption when multiple loaders run at the same time (#40633) 2026-10-08 11:45:37 +00:00
Robin Braumann
74a8a37134
refactor(core): Consolidate agent delegation tools (no-changelog) (#40650) 2026-10-08 11:45:34 +00:00
Rodrigo Santos da Silva
86877c557a
feat(editor): Preview agent output and review agent checks in the Evals tab (no-changelog) (#39886)
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
2026-10-08 11:28:45 +00:00
Declan Carroll
b464dea23c
build: Move the n8n-pc image onto n8nio/node-pc:26.10.0 (#40642) 2026-10-08 11:18:06 +00:00
Robin Braumann
d008d6cee9
fix(core): Validate canonical package manifest paths (no-changelog) (#40619) 2026-10-08 11:03:31 +00:00
Anne Aguirre
96a8ff4d15
fix(core): Wire Entra OAuth2 into agent Azure OpenAI runtime (#40218)
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
2026-10-08 10:59:14 +00:00
Toni Devine
a195096733
fix(editor): Show why a node on another branch can't be referenced (#40565) 2026-10-08 10:53:30 +00:00
Irénée
aef216cb25
feat(editor): Apply a selection of workflows at the destination (no-changelog) (#39788)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-10-08 10:41:39 +00:00
Rúben Castro
370b30f919
feat(core): Hide credential-only nodes under a credential type policy (#39652)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-08 10:13:44 +00:00
Alexander Gekov
97c54f618c
feat(Google Gemini Node): Show Nano Banana 2.1 and 2 Lite in the image model list (#40596)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-08 09:59:08 +00:00
MHMD
c133d6c893
fix(editor): Shorten the share button label on the credential sharing tab (#40637) 2026-10-08 09:47:49 +00:00
Csaba Tuncsik
a584136b6f
test(Databricks Node): Cover the Vector Store Retrieve search mode and filter (no-changelog) (#39528) 2026-10-08 09:34:26 +00:00
Ilfat Mindubaev
986f6b49e3
feat(Databricks Node): Add the hidden Databricks AI Agent with MLflow trace export (no-changelog) (#39465)
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-08 09:29:23 +00:00
Jaakko Husso
1d63a8f3a1
chore(core): Log npm output when the sandbox workspace link install fails (#40591)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 09:26:41 +00:00
n8n-cat-bot[bot]
f212d5b900
fix: Validate and default public API pagination limits (#40483)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
Co-authored-by: Sam Wooler <swooler592@gmail.com>
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-08 09:02:02 +00:00
mfsiega
f4f7dcf13b
feat(core): Show every user the migration report, scoped to the workflows they can edit (#40449)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-08 08:46:46 +00:00
Eugene
6cdd1ffbbb
test(editor): Stabilize chat session reset assertions (#40587) 2026-10-08 08:43:10 +00:00
Stephen Wright
81f34c2374
fix(S3 Node): Percent-encode the copy source path (#40282) 2026-10-08 08:41:37 +00:00
Predrag Ristic
168a0ac860
feat(core): Persist self-healing results and handoffs (#40115) 2026-10-08 08:11:11 +00:00
Benjamin Schroth
727f2a74ea
refactor(core): Keep each chat channel's setup in one place in agent execution (no-changelog) (#40605) 2026-10-08 08:02:48 +00:00
Robert Squires
3b0337d894
feat(editor): Refine the Assistant preferences card and plus menu (no-changelog) (#40558) 2026-10-08 07:59:07 +00:00
Toni Devine
3c33856997
feat(core): Handle message quality rate limits for whatsapp integration (#39205)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 07:57:33 +00:00
Raúl Gómez Morales
6b8ac76d96
test(core): Add a routing mode to the Assistant discovery evals (no-changelog) (#40288) 2026-10-08 07:55:26 +00:00
Declan Carroll
d138b8ff4b
build: Move the n8n and runners images onto n8nio/base:26.10.0 (#40632) 2026-10-08 07:22:42 +00:00
n8n-assistant[bot]
566a1dafc6
chore: NODE-6095 - Generate Schema Files (no-changelog) (#40026)
Co-authored-by: Jon <jonathan.bennetts@gmail.com>
2026-10-08 07:20:26 +00:00
Bernhard Wittmann
6bc5c93513
fix(Microsoft Outlook Trigger Node): Report failed polls and limit the backlog per poll (#40147) 2026-10-08 07:20:03 +00:00
Nour Alhadi Mahmoud
b28adf9789
fix: Load only the publish history that the editor shows (#40183)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 07:14:26 +00:00
Rodrigo Santos da Silva
7397073d86
refactor: Use AsyncLocalStorageContextManager in OTel test harness (no-changelog) (#40173)
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-10-08 07:11:25 +00:00
Benjamin Schroth
1ea8397d69
feat: Steer and reorder queued messages in n8n Chat (no-changelog) (#40433) 2026-10-08 07:07:46 +00:00
n8n-assistant[bot]
86847d7f16
chore: Update e2e impact map (#40627)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-10-08 06:31:04 +00:00
Veljko Terzic
33cd4d5cc1
feat(core): Run the license renewal as a durable system task (no-changelog) (#39055) 2026-10-08 06:06:38 +00:00
Bernhard Wittmann
60ad29f242
feat(core): Let the Microsoft Teams agent read every message in channels and group chats (no-changelog) (#40334) 2026-10-07 20:50:50 +00:00
Guillaume Jacquart
9191cf3fb0
fix(core): Apply the credential type policy to credential tests and probes (#39927)
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-07 19:16:11 +00:00
Guillaume Jacquart
668e71d54a
feat(core): Report crashed executions from startup log recovery (#40390)
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-07 18:02:40 +00:00
Luigi Gubello
1e919c7c56
chore: Update npm dependencies (#40597)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 17:12:50 +00:00
Uddish Verma
9d3eb0c637
refactor(API): Migrate the log streaming destination test and delete to the decorator pattern (no-changelog) (#40532)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 16:43:03 +00:00
Nour Alhadi Mahmoud
bef5f3f97a
fix(core): Expose 1Password fields for secret preview and autocomplete (#40538) 2026-10-07 16:23:24 +00:00
Eugene
c194d22b5c
test(editor): Isolate the legacy workflow builder smoke test (#40576) 2026-10-07 16:04:19 +00:00
n8n-cat-bot[bot]
df8605e875
feat: Make health check timeout configurable (#39548)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
Co-authored-by: James Gee <1285296+geemanjs@users.noreply.github.com>
2026-10-07 15:37:57 +00:00
Anne Aguirre
fbbbadc292
fix(core): Report MCP server connection failures in verify_mcp_server (#40559) 2026-10-07 15:36:06 +00:00
n8n-cat-bot[bot]
4b5b68ee29
fix: Update matching nodes when selecting imported credentials (#39650)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
2026-10-07 15:33:28 +00:00
Jan
09436b78f9
feat(editor): Rebuild the personal settings page with the design-system settings components (#40298)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-07 15:25:38 +00:00
Anne Aguirre
19e1f70ea6
feat(core): Email the agent owner when a monthly budget alert is crossed (#40544) 2026-10-07 14:53:27 +00:00
Anne Aguirre
0722ddbbd5
docs(core): Document the built-in tools the agent runtime attaches (#40062) 2026-10-07 14:49:18 +00:00
ocdejong
88b22abf15
feat(core): Add a restricted node filter to the workflow list (no-changelog) (#39876)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 14:48:35 +00:00
Svetoslav Dekov
e36093b11d
feat(editor): Rebuild the command bar with type tabs and paged search (#40461)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 14:40:54 +00:00
n8n-cat-bot[bot]
2f4a3868fb
fix: Remove loading flashes from data tables (#39874)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
Co-authored-by: Arvin Ansari <arvin.ansari@n8n.io>
2026-10-07 14:31:09 +00:00
Riqwan Thamir
3816c4afec
feat(ai-builder): Honor reasoning effort override for Anthropic models in evals (no-changelog) (#39809)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-07 14:18:33 +00:00
Yuliia Pominchuk
48f05f92fe
feat(editor): Explain instance-role access in the credential sharing "Used in" row (#40555)
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-07 14:16:00 +00:00
Benjamin Schroth
e2733c5b86
feat: Run background tasks in n8n Chat (no-changelog) (#40434) 2026-10-07 14:09:59 +00:00
Alex Grozav
682d0920ba
feat(editor): Add a state column to the migration rule detail (#40388)
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-07 14:05:20 +00:00
Nour Alhadi Mahmoud
2863939580
feat(core): Allow GitLab promotion providers in the database (no-changelog) (#40412) 2026-10-07 14:00:36 +00:00
Jaakko Husso
35972133b0
fix(core): Require execution delete scope for assistant execution cleanup (no-changelog) (#40548)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 14:00:31 +00:00
Theodore Nastos
0bbea438e1
fix(core): Show the policy block before node issues on execute and publish (#40290) 2026-10-07 13:30:44 +00:00
Riqwan Thamir
6b4da93dcb
perf(core): Let the agent builder chain config writes without re-reading the config (#40462) 2026-10-07 13:30:10 +00:00
Riqwan Thamir
e16566b313
fix(core): Start a new test session when call_agent gets an unknown session ID (#40458) 2026-10-07 13:29:45 +00:00
Riqwan Thamir
1de45a1a9b
perf(core): Let the agent builder edit skill instructions in place (#40447) 2026-10-07 13:29:29 +00:00
Jaakko Husso
1ef77564e3
fix(core): Fix Kimi K3 and other OpenAI-compatible Assistant model providers on self-hosted Assistant (#40439)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 13:26:58 +00:00
n8n-cat-bot[bot]
4c8b76b6f0
fix: Use configured OAuth server rate limits (#40542)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
2026-10-07 13:24:56 +00:00
CodeShakingSheep
00849c4dea
fix(Invoice Ninja Node): Fix retrieving currencies for bank transactions (#12997)
Co-authored-by: Jonathan <jon@n8n.io>
Co-authored-by: Jon <jonathan.bennetts@gmail.com>
2026-10-07 13:23:37 +00:00
Pierantonio Cangianiello
a440967b12
feat(core): Set up binary data storage on the engine v2 data plane host (no-changelog) (#40341) 2026-10-07 13:19:22 +00:00
Anne Aguirre
1c63119756
fix(editor): Show the Agent artifact while the builder creates it (#40535) 2026-10-07 13:12:15 +00:00
Michael Drury
022b23945b
fix(editor): Respect the default voice for chat read aloud (#40440) 2026-10-07 12:55:44 +00:00
MHMD
f31e1a83eb
fix(editor): Add a Remove access tooltip to the remove button in the sharing modal (no-changelog) (#40494) 2026-10-07 12:45:51 +00:00
MHMD
eac5d1d7b3
fix(editor): Rename the Only you label to Available to you in the credential sharing modal (no-changelog) (#40411) 2026-10-07 12:39:05 +00:00
Predrag Ristic
eaf8c25d1c
feat(core): Add workflow suggestion review actions (#39858) 2026-10-07 12:36:55 +00:00
Uddish Verma
741ba22943
refactor(API): Migrate the log streaming destination writes to the decorator pattern (no-changelog) (#40397)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 12:34:47 +00:00
Guillaume Jacquart
e7d8533880
fix(core): Apply node type policies to agent node tools (#39711)
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-07 12:32:29 +00:00
mfsiega
bb76c3c99b
feat(core): Add the migration workflow owner table (no-changelog) (#40223)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-07 11:36:58 +00:00
Emilia
f26747bfc6
fix(core): Return jobs fetched before a worker paused to the queue (#40162)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 11:32:56 +00:00
Emilia
084f3b376a
fix(core): Return jobs dequeued during worker shutdown to the queue (#40086)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 11:32:55 +00:00
Emilia
dc5c80f26a
fix(core): Count jobs as running from the start of job processing (#40050)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 11:32:55 +00:00
Iván Ovejero
5ab2187416
fix(core): Pass storedAt into WorkflowExecute at every callsite (#40495) 2026-10-07 11:21:18 +00:00
Jan Kalkan
dc33c29d1a
feat(core): Add overwrite-non-destructive schema conflict policy to data table package import (#40446)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 11:04:50 +00:00
MHMD
4975ab82fe
fix(editor): Remove the disabled cursor on the group headings in the credential dropdown (no-changelog) (#40499) 2026-10-07 10:51:01 +00:00
n8n-cat-bot[bot]
447e939b73
fix: Allow per-process crash journal paths (#37670)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-10-07 10:47:34 +00:00
Andreas Fitzek
4e802ff11d
feat(core): Verify bearer tokens against trusted sources (no-changelog) (#40279)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-07 10:30:24 +00:00
n8n-cat-bot[bot]
0885af531a
chore: Promotions DB snapshot comparison is order-sensitive on role scopes, failing CLI integration + Postgres unit-coverage job (#40475)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
2026-10-07 10:21:15 +00:00
n8n-cat-bot[bot]
b1ec8e525c
fix: Ignore generated Vitest reports (no-changelog) (#40498)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
2026-10-07 10:20:54 +00:00
Sandra Zollner
a694531a03
fix(editor): Align tooltip trigger box with its content (#40422)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-10-07 09:53:07 +00:00
MHMD
684b181101
fix(editor): Show credentials the user cannot use in the workflow dependency list (no-changelog) (#40481)
Some checks failed
Build: Benchmark Image / build (push) Has been cancelled
CI: Master (Build, Test, Lint) / Build & format (push) Has been cancelled
CI: Master (Build, Test, Lint) / Unit tests (24.18.1) (push) Has been cancelled
CI: Master (Build, Test, Lint) / Unit tests (26.5.1) (push) Has been cancelled
CI: Master (Build, Test, Lint) / Lint (push) Has been cancelled
CI: Master (Build, Test, Lint) / Verify single-instance deps (npm-install) (push) Has been cancelled
Util: Publish API Schema / Cloudflare Pages (push) Has been cancelled
CI: Master (Build, Test, Lint) / Notify Slack on failure (push) Has been cancelled
2026-10-07 09:31:24 +00:00
Ilfat Mindubaev
df4af50125
feat(Databricks Node): Add the LangChain to MLflow span converter (no-changelog) (#39396)
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-07 09:29:36 +00:00
Yuliia Pominchuk
02af7e7659
feat(editor): Sort the credential sharing list by people, then projects (#40478)
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-07 09:22:00 +00:00
Veljko Terzic
b7d7a18980
feat(core): Run the agent interrupted-execution sweep as a durable system task (no-changelog) (#40424) 2026-10-07 08:57:44 +00:00
Charlie Kolb
16dc6fe403
refactor(core): Enforce TypeORM persistence boundary with Code Health (#40384) 2026-10-07 08:39:18 +00:00
mfsiega
779ddcc517
test(engine): Run the EmailSend SMTP spec on engine v2 (#40455)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-10-07 08:33:56 +00:00
Daria Staferova
e1025f35e4
fix(editor): Keep folder creation available globally after Community registration (#40082) 2026-10-07 08:26:02 +00:00
Veljko Terzic
3014c64264
feat(core): Run the external secrets refresh as an instance system task (no-changelog) (#40404) 2026-10-07 08:25:02 +00:00
Charlie Kolb
4ae863717e
feat(core): Add backend module scaffolding (no-changelog) (#40482) 2026-10-07 08:22:23 +00:00
Yuliia Pominchuk
3cff8dc4ec
feat(core): Apply the member credential rule to Owners and admins in team projects (#40448) 2026-10-07 07:52:27 +00:00
Emilia
81cdc278c2
ci: Add jscpd duplication-scan workflow (no-changelog) (#40331)
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-10-07 07:37:01 +00:00
Emilia
e500386a54
build: Add jscpd config for local and CI duplication scans (#39198)
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-10-07 07:37:00 +00:00
James Martin
e882e8a483
chore(core): Begins minting asymmetric signed access tokens (#40286)
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-07 07:23:43 +00:00
Yuliia Pominchuk
66b382de52
fix(editor): Keep the selected credential visible when the picker does not list it (#40450) 2026-10-07 07:20:15 +00:00
Jan Kalkan
3af98170d0
feat(core): Add overwrite schema conflict policy to data table package import (#40345)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 07:19:44 +00:00
Charlie Kolb
6ef70a7303
refactor(core): Move Insights tests into module (#40423) 2026-10-07 07:17:47 +00:00
MHMD
32d472fe76
fix(editor): Name the target on the project Share button in the credential sharing modal (no-changelog) (#40413) 2026-10-07 07:14:49 +00:00
n8n-assistant[bot]
6d1144cea3
chore: Update e2e impact map (#40473)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-10-07 06:24:33 +00:00
José Braulio González Valido
0e1c754999
fix(ai-builder): Take feature flags from env only on Instance AI eval instances (no-changelog) (#40117)
Some checks are pending
CI: Master (Build, Test, Lint) / Build & format (push) Waiting to run
CI: Master (Build, Test, Lint) / Unit tests (24.18.1) (push) Waiting to run
CI: Master (Build, Test, Lint) / Unit tests (26.5.1) (push) Waiting to run
CI: Master (Build, Test, Lint) / Lint (push) Waiting to run
CI: Master (Build, Test, Lint) / Verify single-instance deps (npm-install) (push) Waiting to run
CI: Master (Build, Test, Lint) / Notify Slack on failure (push) Blocked by required conditions
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-06 20:46:09 +00:00
Riqwan Thamir
90b8205b62
feat(core): Load Instance AI skill references progressively (no-changelog) (#40296)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-06 20:03:33 +00:00
Rob Hough
883ade4b9d
feat(editor): Add agent setup checklist (#39919) 2026-10-06 16:30:46 +00:00
Riqwan Thamir
b36c2b506b
perf(core): Prepare Instance AI skill sandbox in the background (#40363) 2026-10-06 16:17:43 +00:00
Riqwan Thamir
ee1db0bc12
perf(core): Keep agent builder system prompt static for prompt caching (#40364) 2026-10-06 16:17:22 +00:00
Toni Devine
b0d1a30f50
feat(core): Add the WhatsApp agent channel (#38750)
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-10-06 16:02:03 +00:00
Theodore Nastos
77370af99a
fix(core): Check the target project's policy on folder, credential and delete-transfer moves (#40301) 2026-10-06 15:52:14 +00:00
Michael Drury
a13705aa8f
fix(editor): Keep Assistant fixes in Agent builder and correct tooltips (#40348) 2026-10-06 15:44:49 +00:00
Tuukka Kantola
76cab1e26d
fix(editor): Polish n8n Assistant and Execute button UI details (#40414)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 15:31:43 +00:00
n8n-cat-bot[bot]
8059b63da8
fix: Return UTC ISO dates without changing other cells (#40156)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
2026-10-06 14:56:43 +00:00
n8n-cat-bot[bot]
e7f8fa38cf
chore: Add .build/ to root .gitignore (#39647)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
2026-10-06 14:41:57 +00:00
MHMD
f72461d56d
fix(editor): Block workflow runs and disable run buttons for credentials the user cannot use (no-changelog) (#40410) 2026-10-06 14:34:13 +00:00
Alex Grozav
24dda0d435
refactor(editor): Extract MCP access into a frontend module package (no-changelog) (#39493)
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
2026-10-06 14:31:21 +00:00
Nikhil Kuriakose
8924a11fda
chore: Reassign shared OWNERS paths away from Adore (no-changelog) (#40353)
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-10-06 14:28:52 +00:00
Stephen Wright
0932e9572e
feat(Databricks Node): Guard the Vector Store insert against the wrong index (#39699) 2026-10-06 13:46:39 +00:00
Stephen Wright
830486a851
feat(Embeddings Azure OpenAI Node): Add Entra ID authentication (#39390)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-10-06 13:46:28 +00:00
Uddish Verma
15d597c4ca
refactor(API): Migrate the log streaming destination reads to the decorator pattern (no-changelog) (#40235)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-06 13:35:35 +00:00
Declan Carroll
2fe28ea0ee
test: Fix credential visibility timeouts on the coverage run (#40386) 2026-10-06 13:20:30 +00:00
Theodore Nastos
c2f6920794
feat(core): Add credentialTransfer policy enforcement point (#40300) 2026-10-06 13:15:19 +00:00
Mike Repeć
2b685c33b6
test(core): Run only expression tests once per engine in the workflow package (no-changelog) (#40242) 2026-10-06 13:07:49 +00:00
Charlie Kolb
62fd028089
refactor(core): Extract Insights backend module package (#40261) 2026-10-06 13:05:38 +00:00
Rob Hough
fb11cc482e
refactor(editor): Improve agent message queue and steering controls (#40287)
Co-authored-by: Bob Hough <15361982+heymynameisrob@users.noreply.github.com>
Co-authored-by: linear-code[bot] <222613912+linear-code[bot]@users.noreply.github.com>
2026-10-06 12:58:33 +00:00
Ricardo Espinoza
d5132d21e0
feat(core): Add SCIM provisioning module foundation (no-changelog) (#40226) 2026-10-06 12:57:50 +00:00
n8n-cat-bot[bot]
0bd0cd2873
chore: Move the deprecated GET /workflows/{id}/{versionId} alias to the decorator controller and delete the legacy workflows handler (#40340)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
Co-authored-by: Sam Wooler <swooler592@gmail.com>
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-06 12:26:56 +00:00
Thanasis G
e5c8a8bb62
feat(Azure OpenAI Chat Model Node): Rename the Foundry nodes and credentials to Microsoft Foundry (#40398)
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-06 12:23:59 +00:00
Jakub Kadlubiec
611a6cccda
chore: Move AI Workflow Builder and AI service ownership to Assistant (no-changelog) (#40312)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 12:23:36 +00:00
Veljko Terzic
38eea65993
feat(core): Run the workflow history trim as a durable daily system task (#40085) 2026-10-06 12:17:32 +00:00
MHMD
c4112794fa
fix(core): Offer credentials shared directly with a user in that user's project workflows (no-changelog) (#40395) 2026-10-06 12:04:28 +00:00
Yen Su
3d8b3b99c6
feat(Databricks Node): Add the Lakebase Data API request helper (no-changelog) (#40401) 2026-10-06 12:03:46 +00:00
Veljko Terzic
fc1c26d867
feat(core): Run the data table upload cleanup as a per-instance system task (no-changelog) (#40389) 2026-10-06 11:55:51 +00:00
Jaakko Husso
5d3866775c
feat(ai-builder): Reopen closed AI Assistant tabs when the agent changes the artifact (#40081)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 11:34:27 +00:00
Yen Su
c134edf473
feat(Azure AI Foundry Chat Model Node): Speak the Anthropic Messages schema for Claude deployments (#40306) 2026-10-06 11:26:07 +00:00
Jaakko Husso
3366d2b9b3
fix(core): Stop marking the Message an Agent node as hidden (#40358)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 11:24:39 +00:00
Stephen Wright
7c126fab32
feat(Microsoft SharePoint Trigger Node): Add the Microsoft SharePoint Trigger node (#40055) 2026-10-06 11:14:11 +00:00
Lorent Lempereur
72960562ce
refactor(core): Run the workflow statistics rollup as a durable system task (#40083) 2026-10-06 11:10:53 +00:00
Lorent Lempereur
de72b7fa0d
refactor(core): Run the workflow statistics rollup as a system task (#40019) 2026-10-06 11:10:52 +00:00
Benjamin Schroth
d767edfacd
feat(editor): Add a Chat with agent picker for n8n Chat variant B (no-changelog) (#40337) 2026-10-06 11:02:56 +00:00
Stephen Wright
cf54433c14
feat(Microsoft SharePoint Node): Add list item events to the trigger (no-changelog) (#40052) 2026-10-06 10:51:32 +00:00
mfsiega
a6f1e79394
fix(core): Serve the migration report on every main in a multi-main setup (#40180)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-06 10:30:44 +00:00
Stephen Wright
3287ef1409
feat(Microsoft SharePoint Node): Implement the trigger poll (no-changelog) (#40051) 2026-10-06 10:25:43 +00:00
Benjamin Schroth
73dad06bea
feat(editor): Show the user's agents on the n8n Assistant page for n8n Chat variant A (no-changelog) (#40336) 2026-10-06 10:02:44 +00:00
Robin Braumann
4cb98aea9f
feat: Configure workflow tool inputs for Agents (#40285) 2026-10-06 10:02:23 +00:00
n8n-cat-bot[bot]
05a992d4f2
chore: Move the shared runtime-credential getter into BaseExecuteContext (#39470)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
2026-10-06 09:37:56 +00:00
Benjamin Schroth
8b68b0fcdd
feat: Add the n8n Chat agent chat page, agents library and merged chat lists (no-changelog) (#40335) 2026-10-06 09:35:35 +00:00
Anne Aguirre
958d23d81c
fix(editor): Small UI fixes to budget guardrail setup components (#40383) 2026-10-06 09:25:08 +00:00
Matsu
3c18f58ee0
ci: Reduce sync noise for sec-sync-bundle-branches (#40385) 2026-10-06 09:16:03 +00:00
Andreas Fitzek
2d09135c6e
feat(core): Persist trusted source metadata and discover it asynchronously (no-changelog) (#40048)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-06 09:14:52 +00:00
Stephen Wright
b7960d7a85
feat(Microsoft SharePoint Node): Add the event selector to the trigger (no-changelog) (#39987) 2026-10-06 09:11:25 +00:00
Stephen Wright
71ffb4c8cc
feat(Microsoft SharePoint Node): Add site and document library selection to the trigger (no-changelog) (#39943) 2026-10-06 09:11:25 +00:00
Stephen Wright
6ea9b1f3bb
refactor(Microsoft SharePoint Node): Move the pickers and helpers out of the v2 action node (no-changelog) (#39902) 2026-10-06 09:11:16 +00:00
Lorent Lempereur
264a931de2
feat(core): Stop a poll trigger task when its claim is lost (no-changelog) (#40273) 2026-10-06 09:07:38 +00:00
Lorent Lempereur
e82087bcc8
feat(core): Stop a durable system task run when its claim is lost (no-changelog) (#40272) 2026-10-06 09:07:37 +00:00
Lorent Lempereur
fe91413e5d
feat(core): Renew the lease of a durable task while its handler runs (no-changelog) (#40271)
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
Co-authored-by: Emilia <100027345+sovietspaceship@users.noreply.github.com>
2026-10-06 09:07:36 +00:00
Lorent Lempereur
f4c734e7e0
feat(core): Add a lease heartbeat to the durable scheduler (no-changelog) (#40270) 2026-10-06 09:07:35 +00:00
Lorent Lempereur
4b69c32541
feat(core): Add a lease renewal query to the scheduled task repository (no-changelog) (#40269) 2026-10-06 09:07:34 +00:00
Lorent Lempereur
39abed9124
refactor(core): Simplify the poll trigger task handler (no-changelog) (#40268) 2026-10-06 09:07:33 +00:00
Lorent Lempereur
f26d7fbfe7
refactor(core): Extract the handler failure path of the scheduler executor (no-changelog) (#40267) 2026-10-06 09:07:32 +00:00
Robin Braumann
9ee5d1ad7a
fix(core): Save agent draft definitions atomically (#40374) 2026-10-06 09:01:38 +00:00
Matsu
7db7fa42c8
fix: Multiple migration timestamps withing a single PR (#40379) 2026-10-06 07:28:47 +00:00
Matsu
3f2144f062
chore: Update turbo.json oxlint configurations (#40377) 2026-10-06 07:27:56 +00:00
n8n-assistant[bot]
a11777054a
🚀 Release 2.43.0 (#40376)
Co-authored-by: n8n-release-helper[bot] <265227495+n8n-release-helper[bot]@users.noreply.github.com>
2026-10-06 07:10:16 +00:00
n8n-assistant[bot]
a448b8d2df
chore: Update e2e impact map (#40370)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-10-06 05:24:45 +00:00
n8n-assistant[bot]
b82ad639e8
chore: Lower boundaries baseline (no-changelog) (#40369)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-10-06 03:30:05 +00:00
Mayank SIngh
439e8ba2e7
fix(core): Reload static data for queued executions (#39668)
Co-authored-by: Iván Ovejero <ivov.src@gmail.com>
2026-10-05 21:31:11 +00:00
Uddish Verma
0280644e77
refactor(API): Migrate GET /settings/log-streaming/event-types to the decorator pattern (no-changelog) (#40234)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-05 15:55:04 +00:00
Eugene
308ec2cc1c
feat(editor): Show Agent plans in preview chat (no-changelog) (#40006) 2026-10-05 15:42:28 +00:00
Michael Drury
5c0aff8cbc
fix(editor): Restore full-width Agent preview (#39986) 2026-10-05 15:41:33 +00:00
Robin Braumann
314c430bfc
fix(core): Limit background task continuation to the latest stop group (#40333) 2026-10-05 15:40:03 +00:00
Sandra Zollner
f7cfb520bf
feat(editor): Submit selected project changes from the promote modal (#39609) 2026-10-05 15:31:23 +00:00
Anne Aguirre
a2fd4189f1
feat(core): Persist agent budget spend in the database (#40138)
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
2026-10-05 15:11:29 +00:00
Sam Wooler
5f73c7cdcb
feat(API): Support multipart request bodies on Public API controller routes (no-changelog) (#40074) 2026-10-05 15:08:34 +00:00
Emilia
02f08035b4
chore(core): Bump task-runner-launcher to 1.5.1 (#40324)
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-10-05 15:00:51 +00:00
Mike Repeć
680f9582e1
feat(core): Add engine suspension at node boundaries for resumable executions (#37649)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-10-05 15:00:01 +00:00
Robin Braumann
f055d9cd11
docs(core): Explain background task continuation selection (#40321) 2026-10-05 14:33:56 +00:00
Charlie Kolb
b5fe251ba5
refactor(core): Move ownership transfer registry to backend services (#40308) 2026-10-05 14:30:21 +00:00
José Braulio González Valido
b7071c91c7
fix(ai-builder): Treat a blank folder path as no folder in the build tool (#40116)
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-05 13:57:14 +00:00
Charlie Kolb
206fc6f317
refactor(core): Move instance write access service (#40307) 2026-10-05 13:54:20 +00:00
Jakub Kadlubiec
01f072b520
chore: Move Chat Hub ownership to Assistant team (no-changelog) (#40243)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 13:38:29 +00:00
Nour Alhadi Mahmoud
548bd57802
fix(core): Load workflow publish history in a separate query (#40171)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 13:30:49 +00:00
Robin Braumann
4939dbc787
feat(core): Stop background workflows in Preview (no-changelog) (#40291) 2026-10-05 13:19:23 +00:00
Thanasis G
022f4a6477
feat(Azure OpenAI Chat Model Node): Add the deployment dropdown (#39643)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 13:15:57 +00:00
Pierantonio Cangianiello
8fa89ee02f
docs(engine): Add ADR on binary data access from a data plane in a separate process (no-changelog) (#40179) 2026-10-05 12:55:40 +00:00
Sebastien Powell
8f13f2830a
feat(core): Add remote search and programmatic focus to N8nCombobox so the MCP selects can leave N8nSelect (#40140) 2026-10-05 12:43:11 +00:00
Benjamin Schroth
9183d0422c
fix(core): Resolve the request target against the dispatcher origin (#39506) 2026-10-05 12:28:43 +00:00
James Martin
073239e6d5
chore(core): Implements JWKS service to provide key pair for signing tokens (no-changelog) (#39718)
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-05 12:22:32 +00:00
Riqwan Thamir
7779f47a7d
fix(core): Retry transient Observer model failures (no-changelog) (#40263) 2026-10-05 12:16:57 +00:00
Andreas Fitzek
79924a5d93
refactor(core): Drop TypeORM imports from event bus, LDAP reset, ownership, evaluation, and tags (no-changelog) (#39353)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-05 12:14:47 +00:00
Andreas Fitzek
1942447e90
feat(core): Add a shared OAuth discovery and JWKS client (no-changelog) (#40012)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-05 12:11:52 +00:00
Savelii
44d9adfc7e
fix(core): Use the new client ID and secret for OAuth2 client credentials (#39891)
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-10-05 12:02:55 +00:00
Ali Elkhateeb
fa8b8b243e
refactor(API): Migrate LDAP sync endpoints to PublicApiController (#40280)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-05 12:00:55 +00:00
Eugene
c4c31a529c
feat(core): Add parent-Agent plan tools (no-changelog) (#39542) 2026-10-05 11:59:50 +00:00
Yuliia Pominchuk
92ae1b336f
feat(editor): Show whose credential a node uses and let others switch from it (#40281) 2026-10-05 11:59:20 +00:00
Bernhard Wittmann
e3aa15d7d7
fix(editor): Check Teams credential again after it is saved (no-changelog) (#40264) 2026-10-05 11:56:23 +00:00
Savelii
360ec09cf2
fix(editor): Enable Save when SSO role provisioning switches back to manual (#39925)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-05 11:23:06 +00:00
Savelii
1cac1e056d
fix(core): Keep concurrent credential tests from sharing one node type registry (#40054)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-05 11:08:25 +00:00
Riqwan Thamir
fe07e84d0f
fix(core): Keep skill activation and memory compaction from breaking the prompt cache (no-changelog) (#40245)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-05 10:48:35 +00:00
Stephen Wright
6610987057
fix(Azure OpenAI Chat Model Node): Support Responses-only deployments (#39403)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-10-05 10:46:42 +00:00
n8n-cat-bot[bot]
94a49d08ce
feat(Crypto Node): Add RSA-OAEP hash option (#39853)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
Co-authored-by: Jonathan <jon@n8n.io>
Co-authored-by: Jon <jonathan.bennetts@gmail.com>
2026-10-05 10:09:39 +00:00
Anne Aguirre
6a5aa7862e
feat(core): Indicate unpublished changes in agent resource list view (#40177) 2026-10-05 10:06:20 +00:00
Charlie Kolb
f47a2fe235
refactor(core): Decouple Insights from CLI and TypeORM (#40214) 2026-10-05 09:59:39 +00:00
Andreas Fitzek
32d7c8ebf4
feat(core): Add inbound-auth contracts and fail-closed defaults (no-changelog) (#40004)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-05 09:30:49 +00:00
José Braulio González Valido
274639fb88
fix(ai-builder): Guide the builder to a working HTTP pagination stop rule (no-changelog) (#40024)
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-05 09:27:30 +00:00
Eugene
a9b0607111
feat(core): Add Agent plan model and validation (no-changelog) (#39530) 2026-10-05 09:21:41 +00:00
Eugene
b9695a7f6e
feat(core): Add Agent plan storage foundation (no-changelog) (#39482) 2026-10-05 09:01:48 +00:00
Yen Su
b8cd29fde4
fix(Databricks Trigger Node): Cap pipeline event pages at 250, the limit Databricks enforces (#40154) 2026-10-05 08:53:30 +00:00
Yen Su
fb69206450
feat(Databricks Node): Enable the Job resource (#40142) 2026-10-05 08:42:30 +00:00
Charlie Kolb
4ea06438db
chore: Remove legacy CODEOWNERS file (#40266) 2026-10-05 08:14:37 +00:00
José Braulio González Valido
674fb57941
fix(ai-builder): Make the eval mock end lists like the real API (no-changelog) (#40017)
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-05 08:09:16 +00:00
Irénée
d931d6415a
feat(API): Apply a selection of workflows on the destination (no-changelog) (#39612)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-10-05 08:07:22 +00:00
Robin Braumann
25831a8ac0
feat(editor): Add Stop all tasks to the Preview task panel (#40120) 2026-10-05 07:59:39 +00:00
n8n-cat-bot[bot]
708e79264b
chore: Hashicorp vault does not handle batch (non-renewable) tokens (#39635)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
Co-authored-by: James Gee <1285296+geemanjs@users.noreply.github.com>
2026-10-05 07:58:49 +00:00
Ricardo Espinoza
5e28ff8c23
fix(core): Block publishing workflows with unconnected required node inputs (#37265)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-10-05 07:56:07 +00:00
Guillaume F.
b0803dc450
chore: Add community engineering ownership to OWNERS (#40184)
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-10-05 07:45:28 +00:00
Matsu
72a4024161
chore: Complete base package migration to Oxlint (#40257) 2026-10-05 10:48:34 +03:00
Matsu
e18afc84cc
chore: Migrate remaining simple base packages to Oxlint (#40255) 2026-10-05 10:04:49 +03:00
Matsu
bec84a1dad
chore: Migrate more base packages to Oxlint (#40254) 2026-10-05 10:04:09 +03:00
Charlie Kolb
e6bb9e0f3b
test(core): Stabilize registry reconciliation integration test (#40172) 2026-10-05 06:26:18 +00:00
Charlie Kolb
d6a364c0c0
build: Load backend test utilities from source in Vitest (#40212) 2026-10-05 06:25:06 +00:00
Charlie Kolb
68ced97f81
refactor(core): Share decorator transform through Vitest config (#40213) 2026-10-05 06:24:53 +00:00
n8n-cat-bot[bot]
a1370863e4
test: Route AI specs to current owners (#40252)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
2026-10-05 06:24:12 +00:00
Matsu
d6ec4c5546
chore: Migrate part of the base packages to oxlint (#40253) 2026-10-05 06:18:31 +00:00
n8n-cat-bot[bot]
be2ee6f356
fix: Replace deprecated APIs in services and tooling (#40199)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
2026-10-05 04:50:04 +00:00
n8n-assistant[bot]
388b4036d4
chore: Update e2e impact map (#40250)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-10-05 04:20:56 +00:00
n8n-assistant[bot]
e77e30c7d9
chore: Update node popularity data (#40248)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-10-05 00:46:59 +00:00
n8n-assistant[bot]
e8b9821f4a
chore: Update e2e impact map (#40237)
Some checks failed
CI: Master (Build, Test, Lint) / Build & format (push) Has been cancelled
CI: Master (Build, Test, Lint) / Unit tests (24.18.1) (push) Has been cancelled
CI: Master (Build, Test, Lint) / Unit tests (26.5.1) (push) Has been cancelled
CI: Master (Build, Test, Lint) / Lint (push) Has been cancelled
CI: Master (Build, Test, Lint) / Verify single-instance deps (npm-install) (push) Has been cancelled
CI: Master (Build, Test, Lint) / Notify Slack on failure (push) Has been cancelled
Util: Update Node Popularity / update-popularity (push) Has been cancelled
Util: Update Credential Setupability / update-setupability (push) Has been cancelled
Util: Update Node Popularity / approve-and-automerge (push) Has been cancelled
Util: Update Credential Setupability / approve-and-automerge (push) Has been cancelled
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-10-04 04:45:47 +00:00
Jan
ea9f451076
fix(editor): Align input heights on the project settings page (#39611)
Some checks are pending
CI: Master (Build, Test, Lint) / Build & format (push) Waiting to run
CI: Master (Build, Test, Lint) / Unit tests (24.18.1) (push) Waiting to run
CI: Master (Build, Test, Lint) / Unit tests (26.5.1) (push) Waiting to run
CI: Master (Build, Test, Lint) / Lint (push) Waiting to run
CI: Master (Build, Test, Lint) / Verify single-instance deps (npm-install) (push) Waiting to run
CI: Master (Build, Test, Lint) / Notify Slack on failure (push) Blocked by required conditions
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-03 11:46:25 +00:00
n8n-assistant[bot]
43fe82b10f
chore: Update e2e impact map (#40229)
Some checks are pending
CI: Master (Build, Test, Lint) / Build & format (push) Waiting to run
CI: Master (Build, Test, Lint) / Unit tests (24.18.1) (push) Waiting to run
CI: Master (Build, Test, Lint) / Unit tests (26.5.1) (push) Waiting to run
CI: Master (Build, Test, Lint) / Lint (push) Waiting to run
CI: Master (Build, Test, Lint) / Verify single-instance deps (npm-install) (push) Waiting to run
CI: Master (Build, Test, Lint) / Notify Slack on failure (push) Blocked by required conditions
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-10-03 04:15:52 +00:00
n8n-assistant[bot]
f0ca6b24f4
chore: Lower boundaries baseline (no-changelog) (#40228)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-10-03 03:34:58 +00:00
Veljko Terzic
944afe5c88
feat(core): Run workflow history pruning as a durable system task (no-changelog) (#40106)
Some checks are pending
CI: Master (Build, Test, Lint) / Build & format (push) Waiting to run
CI: Master (Build, Test, Lint) / Unit tests (24.18.1) (push) Waiting to run
CI: Master (Build, Test, Lint) / Unit tests (26.5.1) (push) Waiting to run
CI: Master (Build, Test, Lint) / Lint (push) Waiting to run
CI: Master (Build, Test, Lint) / Verify single-instance deps (npm-install) (push) Waiting to run
CI: Master (Build, Test, Lint) / Notify Slack on failure (push) Blocked by required conditions
2026-10-02 15:57:08 +00:00
Robin Braumann
bae7656347
feat(core): Coordinate background sub-agent pause and resume in Preview (no-changelog) (#40119)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-02 15:29:57 +00:00
mfsiega
51c4770643
fix(core): Reject an instance rule on the migration rule detail route before the stale check (no-changelog) (#40210)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-02 14:56:07 +00:00
n8n-cat-bot[bot]
df27223a4e
fix: Pass shutdown margin to JavaScript runner in runners image (#40178)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
2026-10-02 14:55:52 +00:00
mfsiega
56d6e9da2c
feat(core): Stop engine v2 executions from the control plane (#39786)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-02 14:53:46 +00:00
Charlie Kolb
afd7cb4851
refactor(core): Move project scope service to backend services (#40209) 2026-10-02 14:45:32 +00:00
mfsiega
b8ff99f2cc
feat(core): Serve the migration rule detail from the finding table (#39974)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-02 13:52:49 +00:00
Bernhard Wittmann
3fe4612d93
fix(editor): Scale up node icons that declare a size smaller than their slot (#40181) 2026-10-02 13:46:54 +00:00
Romeo Balta
dae811ddd9
fix(core): Send instance group facts to PostHog with the owner ID (no-changelog) (#39084)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-02 13:46:50 +00:00
Charlie Kolb
ea38f3a3ee
refactor(core): Load packaged modules from a static manifest (no-changelog) (#40037) 2026-10-02 13:42:01 +00:00
Robin Braumann
628ada76cd
feat(core): Add an instance setting for Agents (#40153) 2026-10-02 13:28:21 +00:00
Robin Braumann
a30bb7d89d
fix(editor): Align Preview and Assistant chat history controls (#40165) 2026-10-02 13:24:33 +00:00
Anne Aguirre
1043504c7a
feat(core): Hide current Agent references and show built Workflow cards in the embedded Assistant (#40149) 2026-10-02 13:13:22 +00:00
Robin Braumann
ea9021ed86
feat(core): Add durable pause checkpoints for background sub-agents (no-changelog) (#40118) 2026-10-02 13:10:50 +00:00
Charlie Kolb
a3ae96e87b
test(core): Stabilize OAuth and background agent tests (#40167) 2026-10-02 12:58:03 +00:00
Charlie Kolb
93c1291f6d
docs: Correct community PR owners script path (#40148) 2026-10-02 12:57:46 +00:00
Matsu
6337b94d3c
chore: Add typescript/no-deprecated and establish a baseline (#40168) 2026-10-02 12:54:37 +00:00
n8n-cat-bot[bot]
9675fc97fd
fix: Align From AI button with parameter inputs (#40152)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
2026-10-02 12:50:51 +00:00
Benjamin Schroth
1adc57b97b
feat(editor): Add n8n Chat as an agent channel (no-changelog) (#40075) 2026-10-02 12:44:22 +00:00
Minho Yoo
407cf23da7
fix(core): Wait for credential deletion to finish (#40103)
Co-authored-by: yoo-minho <13810291+yoo-minho@users.noreply.github.com>
Co-authored-by: Garrit Franke <32395585+garritfra@users.noreply.github.com>
2026-10-02 12:39:52 +00:00
Rohit Gahlawat
1998011d90
fix(core): Pass registry and auth token to npm outdated for community packages (#31515)
Co-authored-by: Michael Kret <michael.k@radency.com>
Co-authored-by: Michael Kret <88898367+michael-radency@users.noreply.github.com>
Co-authored-by: Bernhard Wittmann <bernhard.wittmann@n8n.io>
2026-10-02 12:32:34 +00:00
Charlie Kolb
e0c27dd34f
refactor(core): Move invert helper to shared utils (no-changelog) (#40144) 2026-10-02 12:23:31 +00:00
Lorent Lempereur
03072faadc
feat(core): Emit an overlap skip for a held-back system task occurrence (no-changelog) (#39247) 2026-10-02 12:06:19 +00:00
Lorent Lempereur
fd6c1af1a0
feat(core): Report the occurrences a concurrency limit held back (no-changelog) (#39246) 2026-10-02 12:06:18 +00:00
Lorent Lempereur
01d6a1019c
feat(core): Default a durable system task to one occurrence at a time (no-changelog) (#39245) 2026-10-02 12:06:17 +00:00
Raúl Gómez Morales
387905148a
fix(editor): Leave the sweep time of interrupted tool calls out of the Assistant thinking duration (#40161) 2026-10-02 11:59:44 +00:00
Raúl Gómez Morales
51be26d7e2
fix(editor): Open preferences settings in a new tab from the Assistant preference card (no-changelog) (#40160) 2026-10-02 11:57:10 +00:00
Raúl Gómez Morales
064c3bf979
fix(editor): Refresh the Assistant chat list when the tab becomes visible (#40158) 2026-10-02 11:55:11 +00:00
Charlie Kolb
87ca4e8bf7
refactor(core): Move billable execution helper to backend services (no-changelog) (#40146) 2026-10-02 11:46:11 +00:00
Charlie Kolb
0ae0b6b3c2
ci: Exclude test files from required reviews (no-changelog) (#40164) 2026-10-02 11:35:51 +00:00
Charlie Kolb
69595f5821
refactor(core): Move workflow sharing service to backend services (no-changelog) (#40157) 2026-10-02 11:24:08 +00:00
Matsu
aae8016c84
chore: Repair Oxlint migration verification (no-changelog) (#40087) 2026-10-02 11:08:39 +00:00
Yuliia Pominchuk
deaa9b1054
feat(editor): Stop Execute and Publish when a credential is not yours to use (#39883)
Co-authored-by: MHMD <mohamed.dounnani@n8n.io>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-10-02 11:06:04 +00:00
Anne Aguirre
e79a2ba84b
fix(editor): Remove the new-agent pill from the AI assistant composer (#40014) 2026-10-02 11:01:53 +00:00
Lorent Lempereur
f6f71abef4
feat(core): Cap concurrent occurrences of a durable scheduled job (no-changelog) (#39197) 2026-10-02 10:50:24 +00:00
Stephen Wright
ff78115647
feat(Microsoft SharePoint Node): Accept both v2 credentials on the trigger (no-changelog) (#39863) 2026-10-02 10:49:37 +00:00
Danny Martini
b22b6f4909
feat(core): Add steering pair check to agent message queue (no-changelog) (#39893)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 10:48:39 +00:00
Emilia
28bd124258
fix(core): Cap pending task request timeouts to the shutdown deadline (#40136)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 10:46:33 +00:00
Andreas Fitzek
b4e5fa6eef
feat(core): Add trusted source store with CacheService (no-changelog) (#39976)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-02 10:41:56 +00:00
Predrag Ristic
0352a19654
feat(core): Store isolated workflow suggestions for review (#39505) 2026-10-02 10:20:01 +00:00
Ali Elkhateeb
89e88f87f2
refactor(API): Migrate LDAP configuration endpoints to PublicApiController (#39966)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-02 10:17:50 +00:00
Lucien
900fe0c03a
fix(Google Calendar Trigger Node): Handle all-day event boundaries (#39898) 2026-10-02 10:16:33 +00:00
RomanDavydchuk
8e339d7051
feat(Supabase Node): Add OAuth credentials and reuse them for MCP (#39979) 2026-10-02 10:04:51 +00:00
Stephen Wright
9adaa38f22
refactor(Microsoft SharePoint Node): Move the shared transport out of the v2 action node (no-changelog) (#39860) 2026-10-02 10:04:14 +00:00
n8n-cat-bot[bot]
9e5eb79cc4
fix: Keep workflow sharing open until changes are saved (#40137)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
2026-10-02 10:03:04 +00:00
Yuliia Pominchuk
37ed348665
refactor(core): Bind signed tokens to a declared purpose (#39467)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-10-02 09:59:50 +00:00
Anne Aguirre
c4d31db8d6
feat(editor): Add usage and budget settings for agents (#39909)
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
2026-10-02 09:59:09 +00:00
Raúl Gómez Morales
0a1ed4e09b
fix(editor): Wait for OAuth sign-in before the assistant credential card continues (#39998) 2026-10-02 09:56:20 +00:00
Riqwan Thamir
93c0a5c0ff
fix(core): Apply removed fields when the Agent Builder patches agent config (#40122)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-02 09:51:00 +00:00
Emilia
fc3954bfe2
fix(core): Stop waiting on enqueued jobs in worker shutdown (#40065)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 09:50:53 +00:00
Stephen Wright
d556c318f4
feat(Microsoft SharePoint Node): Add the hidden SharePoint Trigger node skeleton (no-changelog) (#39855) 2026-10-02 09:32:48 +00:00
Benny Neugebauer
7576de5f26
test: Add E2E coverage for the community node API version guard (no-changelog) (#39805) 2026-10-02 09:23:26 +00:00
Uddish Verma
c95e34f97a
fix(API): Let global members grant variable scopes to API keys (#40064)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-02 09:20:55 +00:00
Csaba Tuncsik
01e5ebf726
fix(editor): Use only server-provided hints for the OAuth consent screen icon (#37317) 2026-10-02 09:17:26 +00:00
Tomi Turtiainen
b9368f4db8
feat(engine): Stream webhook responses from engine v2 on the control plane (no-changelog) (#39948) 2026-10-02 08:58:01 +00:00
Declan Carroll
d262baceef
feat(core): Add test-only process internals and heap diagnostics routes (no-changelog) (#39881) 2026-10-02 08:56:30 +00:00
Stephen Wright
f7e82abcba
feat(Microsoft SharePoint Node): Add a document library picker for the trigger (no-changelog) (#39848) 2026-10-02 08:48:47 +00:00
Raúl Gómez Morales
6438ead58d
fix(core): Report empty node output in Instance AI workflow verification (#40047) 2026-10-02 08:45:43 +00:00
Bernhard Wittmann
17fbae497b
feat(editor): Align the Microsoft Teams channel settings with the MVP design (no-changelog) (#39988) 2026-10-02 08:44:51 +00:00
Jaakko Husso
7d5730f688
fix(ai-builder): Pack @n8n/errors when linking the workflow SDK into the sandbox (no-changelog) (#40056)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 08:34:28 +00:00
Sam Wooler
1b294221b5
refactor(API): Migrate remaining data-tables row endpoints to PublicApiController (no-changelog) (#39985)
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-10-02 08:32:19 +00:00
Kai
c5658ba4bf
fix(editor): Extend workflow review divider to full height (#40035) 2026-10-02 08:28:32 +00:00
Rob Hough
d09e6d65fc
fix(editor): Align workflow and agent action menus (#39234)
Co-authored-by: Bob Hough <15361982+heymynameisrob@users.noreply.github.com>
Co-authored-by: linear-code[bot] <222613912+linear-code[bot]@users.noreply.github.com>
2026-10-02 08:24:34 +00:00
yehorkardash
a969387ca9
build: Make check-pnpm as JS script (no-changelog) (#39857) 2026-10-02 08:20:31 +00:00
Charlie Kolb
fb083823bd
ci: Grant permissions to boundaries baseline automerge (#39852) 2026-10-02 08:20:30 +00:00
n8n-assistant[bot]
925f3f54ac
chore: Refresh vendored cubic config schema (no-changelog) (#40027)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-10-02 08:04:34 +00:00
Stephen Wright
5bf4cae3b6
feat(Microsoft SharePoint Node): Add a Graph delta transport helper for the trigger (no-changelog) (#39840) 2026-10-02 07:59:27 +00:00
Charlie Kolb
b48bb1f899
refactor(core): Move finder services to backend services (no-changelog) (#40069) 2026-10-02 07:38:15 +00:00
Mike Repeć
2d3e8140ce
fix(core): Bound native array methods by element content, not count (no-changelog) (#40099)
Some checks are pending
CI: Master (Build, Test, Lint) / Build & format (push) Waiting to run
CI: Master (Build, Test, Lint) / Unit tests (24.18.1) (push) Waiting to run
CI: Master (Build, Test, Lint) / Unit tests (26.5.1) (push) Waiting to run
CI: Master (Build, Test, Lint) / Lint (push) Waiting to run
CI: Master (Build, Test, Lint) / Verify single-instance deps (npm-install) (push) Waiting to run
CI: Master (Build, Test, Lint) / Notify Slack on failure (push) Blocked by required conditions
2026-10-02 07:24:12 +00:00
Mike Repeć
5763326a0a
fix(core): Bound replace() like replaceAll() in native expression evaluation (no-changelog) (#40098) 2026-10-02 07:20:54 +00:00
Ricardo Espinoza
cbaea98521
fix(editor): Remove the one-click connector for the ChatGPT MCP client (#40123) 2026-10-02 06:49:00 +00:00
n8n-assistant[bot]
093ae6f4db
chore: Update e2e impact map (#40125)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-10-02 04:23:16 +00:00
Benjamin Schroth
56aa3d8857
feat(core): Move n8n Chat production messages onto the agent queue (no-changelog) (#40102)
Some checks are pending
CI: Master (Build, Test, Lint) / Build & format (push) Waiting to run
CI: Master (Build, Test, Lint) / Unit tests (24.18.1) (push) Waiting to run
CI: Master (Build, Test, Lint) / Unit tests (26.5.1) (push) Waiting to run
CI: Master (Build, Test, Lint) / Lint (push) Waiting to run
CI: Master (Build, Test, Lint) / Verify single-instance deps (npm-install) (push) Waiting to run
CI: Master (Build, Test, Lint) / Notify Slack on failure (push) Blocked by required conditions
2026-10-01 16:38:31 +00:00
Robin Braumann
53b6a68e12
feat(editor): Add activation menus to Agent capabilities (#40104) 2026-10-01 16:22:23 +00:00
Daria Staferova
7675d2bcbd
fix(core): Improve workflow SDK group layout for AI subtrees and sticky notes (#39981) 2026-10-01 15:57:28 +00:00
n8n-cat-bot[bot]
4a0865bb62
fix: Persist nested node custom span attributes (#40107)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
2026-10-01 15:55:23 +00:00
Toni Devine
e144b337de
fix(Brevo Node): Accept both spellings of the recipients parameter (#40015) 2026-10-01 15:54:04 +00:00
Robin Braumann
33505d7faa
feat(editor): Allow reordering queued messages in Agent Preview (#40101) 2026-10-01 15:44:14 +00:00
Elias Meire
b581d16a82
fix: Update broken external links in node descriptions (no-changelog) (#39710) 2026-10-01 15:42:30 +00:00
mfsiega
6769662ecf
feat(core): Serve the migration report overview from the finding table (#39969)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-01 15:06:08 +00:00
Elias Meire
190d7c0dc0
feat(editor): Show Gateway credits promotions on community nodes (#40010) 2026-10-01 14:59:20 +00:00
Arvin A
88d6579459
fix(editor): Unify MCP access labels (#40090) 2026-10-01 14:57:03 +00:00
Bernhard Wittmann
efec7dc067
feat(editor): Align the Microsoft Teams manual setup with the new design (no-changelog) (#39798)
Co-authored-by: Elias Meire <elias@meire.dev>
2026-10-01 14:54:14 +00:00
oleg
f82fa0c1af
fix(core): Keep node parameters when n8n Assistant cannot see them (#39454) 2026-10-01 13:55:32 +00:00
n8n-cat-bot[bot]
996dec3193
fix: Limit MCP Agent model discovery results (#39973)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
2026-10-01 13:33:45 +00:00
Roman Makowiak
b41019a19a
fix(core): Send user_id with the backend preference telemetry events (no-changelog) (#40088)
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-10-01 13:26:58 +00:00
Charlie Kolb
a70ff86667
refactor(core): Isolate folder finder persistence (no-changelog) (#40068) 2026-10-01 13:15:28 +00:00
Charlie Kolb
f7c1bfa175
refactor(core): Add finder repositories and isolate credential access (no-changelog) (#40066) 2026-10-01 13:15:27 +00:00
mfsiega
f2050832cf
feat(core): Keep migration findings current on workflow save and publish (no-changelog) (#40057)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-01 12:58:57 +00:00
Robin Braumann
3d70eef3a3
feat(editor): Add Remove context menus to Agent configuration (#40089) 2026-10-01 12:55:39 +00:00
Pierantonio Cangianiello
9247a09214
feat(engine): Accept files from webhooks and triggers on engine v2 (no-changelog) (#40005) 2026-10-01 12:50:55 +00:00
Emilia
59c6ab2057
fix(core): Keep the task runner running on an unhandled promise rejection (#39946)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 12:25:26 +00:00
Ricardo Espinoza
d12cab0ed1
fix(core): Generate missing workflow IDs in the import:workflow command (#40011) 2026-10-01 12:14:08 +00:00
Veljko Terzic
fa30358f84
feat(core): Run the execution pruning soft delete as a durable system task (no-changelog) (#40058)
Co-authored-by: Lorent Lempereur <lorent.lempereur@n8n.io>
2026-10-01 12:03:04 +00:00
Anne Aguirre
d5937ed2b6
feat(core): Add an opt-in budget guardrail for agent runs (#39765) 2026-10-01 12:02:21 +00:00
Lorent Lempereur
3faf54fea3
feat(core): Export the stored schedule of durable system tasks as metrics (#40009) 2026-10-01 11:46:12 +00:00
Declan Carroll
a4b87fccbe
refactor: Group quality engineering workspaces by capability (#40007) 2026-10-01 11:20:00 +00:00
Lorent Lempereur
06b27d1461
feat(core): Run instance reporting as a durable system task (no-changelog) (#39924) 2026-10-01 10:45:56 +00:00
Matsu
58ddcd7bbb
chore: Migrate Workflow SDK lint to Oxlint (no-changelog) (#39997) 2026-10-01 13:34:31 +03:00
Anne Aguirre
3745c835b2
feat(core): Add a guardrail runner to the agent loop (#39621)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-01 10:21:42 +00:00
Matsu
5b499d512f
chore: Migrate backend storage lint to Oxlint (no-changelog) (#39991) 2026-10-01 10:16:57 +00:00
n8n-cat-bot[bot]
1ee7233460
chore: Consolidate pending-request cancellation in the task runner (#40061)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
2026-10-01 10:01:05 +00:00
Pierantonio Cangianiello
119e21752b
feat(engine): Add the host callback that deletes the binary files of a pruned execution (no-changelog) (#39701) 2026-10-01 09:50:12 +00:00
Raúl Gómez Morales
0e3f6e996e
feat(editor): Add the AI Assistant test URL card (no-changelog) (#38627) 2026-10-01 09:42:00 +00:00
Robin Braumann
f1d4017883
fix(editor): Align Agent confirmation warning icons (#40040) 2026-10-01 09:18:11 +00:00
Robin Braumann
ee46834915
fix(editor): Correct the expanded Markdown editor close label (#40041) 2026-10-01 09:16:40 +00:00
Robin Braumann
3a445ae2bf
feat(core): Add session approvals for Agent tools (no-changelog) (#39560) 2026-10-01 09:14:23 +00:00
Robin Braumann
86969460f1
fix(core): Preserve document content in agent summaries (no-changelog) (#40044) 2026-10-01 09:14:01 +00:00
Charlie Kolb
577df7a23b
refactor(core): Move role services to backend services (no-changelog) (#39999) 2026-10-01 09:13:58 +00:00
Charlie Kolb
8dc0ac8011
test(editor): Stabilize evaluation runs table tests (#39859) 2026-10-01 09:13:55 +00:00
mfsiega
681768e0bb
feat(engine): Cancel an execution on request (#39785)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-01 08:40:02 +00:00
Jaakko Husso
1700702e52
fix(core): Emit unique keys and conditional requirements in generated node type definitions (#39960)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-01 08:30:56 +00:00
Benjamin Schroth
f6a1224ee2
feat(editor): Add description option to agent overflow menu (no-changelog) (#39793) 2026-10-01 08:13:08 +00:00
Robin Braumann
bdab0b1017
fix(core): Resume Preview agents after waiting workflows finish (#40043) 2026-10-01 08:13:00 +00:00
Ali Elkhateeb
ae99934c21
refactor(API): Serve SAML SSO settings from the public API controller (no-changelog) (#39877)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-01 08:01:17 +00:00
Roman Makowiak
ad6d94e568
fix(editor): Open preferences settings in a new tab from the Instance AI input menu (no-changelog) (#40029)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 07:48:02 +00:00
Jaakko Husso
0817b0e8cc
fix(editor): Close the AI Assistant tab hover card when the pointer leaves the tab (#40003)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 07:20:58 +00:00
Nikhil Kuriakose
fce9570711
fix(editor): Show warning when moving a workflow with data table dependency (#39752)
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-10-01 07:07:55 +00:00
Lorent Lempereur
3123fe20af
feat(core): Run the workflow history optimization as a durable system task (no-changelog) (#40016) 2026-10-01 06:52:16 +00:00
Robin Braumann
d51c9a92ed
test(core): Fix the workflow execution test hang (#40053) 2026-10-01 12:03:05 +03:00
Matsu
e0b4928bc8
ci: Revert bundle branch syncs from rebase to commit-based. Always sync from public (#40031) 2026-10-01 06:45:17 +00:00
n8n-assistant[bot]
eeaf84f756
chore: Bundle/2.x (#40030)
Co-authored-by: Matsu <matias.huhta@n8n.io>
2026-10-01 09:50:06 +03:00
Charlie Kolb
955c99dc26
ci: Show files that require team reviews (#40000) 2026-10-01 06:38:22 +00:00
n8n-assistant[bot]
6590765dc6
chore: Update e2e impact map (#40028)
Some checks are pending
CI: Master (Build, Test, Lint) / Build & format (push) Waiting to run
CI: Master (Build, Test, Lint) / Unit tests (24.18.1) (push) Waiting to run
CI: Master (Build, Test, Lint) / Unit tests (26.5.1) (push) Waiting to run
CI: Master (Build, Test, Lint) / Lint (push) Waiting to run
CI: Master (Build, Test, Lint) / Verify single-instance deps (npm-install) (push) Waiting to run
CI: Master (Build, Test, Lint) / Notify Slack on failure (push) Blocked by required conditions
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-10-01 04:23:31 +00:00
Daria
0976d925b9
refactor(core): Organize workflow SDK group layout logic (#39980) 2026-09-30 23:58:10 +00:00
José Braulio González Valido
1b1bb4698d
fix(ai-builder): Bind a seeded eval Agent's model to the case credential (no-changelog) (#39984)
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-30 22:18:59 +00:00
José Braulio González Valido
e185c36c59
fix(ai-builder): Clear eval dedupe state between runs and add a Sheets mock quirk (no-changelog) (#39990)
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-30 22:18:56 +00:00
José Braulio González Valido
3248b75af8
docs: Source Instance AI eval cases from LangTracer's signals (no-changelog) (#40002)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-30 22:18:54 +00:00
n8n-cat-bot[bot]
5db0c39dde
fix: Report the real cause when a module entrypoint fails to load (#37625)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: Declan Carroll <declan@n8n.io>
Co-authored-by: RomanDavydchuk <roman.davydchuk@n8n.io>
2026-09-30 21:08:03 +00:00
Tomi Turtiainen
3f0c999440
refactor(engine): Split the engine v2 webhook responder into a registry and an outcome mapping (no-changelog) (#39978) 2026-09-30 19:35:23 +00:00
José Braulio González Valido
0c8acfa274
fix(ai-builder): Send requiresMemoryCompaction on eval case push (no-changelog) (#39989)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-30 17:34:50 +00:00
José Braulio González Valido
54f634d5f9
fix(ai-builder): Cancel the thread when an eval turn budget fires after the run (no-changelog) (#39992)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-30 17:34:47 +00:00
n8n-cat-bot[bot]
cd1bfafe72
refactor(API): Migrate POST /settings/otel/test-trace to the decorator pattern (no-changelog) (#39716)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
Co-authored-by: Uddish Verma <uddish.verma@n8n.io>
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Co-authored-by: Declan Carroll <declancarroll@gmail.com>
2026-09-30 15:49:59 +00:00
mfsiega
1ebd6265db
feat(core): Add migration finding query service (no-changelog) (#39957)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-30 15:39:19 +00:00
Veljko Terzic
0ddb89c007
refactor(core): Run the instance-registry heartbeat as a system task (#40008) 2026-09-30 15:19:53 +00:00
Svetoslav Dekov
a931dd8660
fix(core): Return upstream error details from the explore_node_resources MCP tool (#39775)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 14:22:05 +00:00
Elias Meire
5e376b69ed
feat: Hide generated AI tools listed in NODES_EXCLUDE and empty node creator entries (#39577) 2026-09-30 14:02:32 +00:00
Lorent Lempereur
45edf565df
feat(core): Allow a sending status on instance monitoring reports (no-changelog) (#39923) 2026-09-30 13:54:16 +00:00
James Martin
5576c785ce
feat(core): Implements core JWKS registry for extending with new sets (no-changelog) (#39610)
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-30 13:19:26 +00:00
Mike Repeć
6da7f6b010
refactor(core): Use JSDoc for native evaluator helpers and name the size preflight as an assertion (no-changelog) (#40001) 2026-09-30 13:17:30 +00:00
Anne Aguirre
0f11660414
feat(core): Vanish Fix with Assistant banner when AIA fixes error (#39977)
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
2026-09-30 13:10:54 +00:00
n8n-cat-bot[bot]
bd9e1d8947
fix: Center pasted nodes in the canvas viewport (#39995)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
2026-09-30 13:00:49 +00:00
mfsiega
902b039c4d
feat(core): Add migration finding sync service (no-changelog) (#39871)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-30 12:54:22 +00:00
Raúl Gómez Morales
5c1f8f7f6c
fix: Scope the legacy setup credential picker to the target workflow and report unusable credentials on apply (#38197) 2026-09-30 12:52:56 +00:00
Elias Meire
202a52d206
fix: Update broken external links in core packages (no-changelog) (#39704) 2026-09-30 12:41:50 +00:00
Sebastien Powell
b15ffa9542
feat(core): Add 'align' prop to Select component (#39965)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-30 12:29:59 +00:00
Sam Wooler
256788294d
refactor(API): Migrate data-tables row read/insert/upsert endpoints to PublicApiController (no-changelog) (#39962)
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-30 12:21:01 +00:00
Mutasem Aldmour
2df4f3e1d0
docs: Ask for the target LangTracer suite before authoring an Instance AI eval (no-changelog) (#39994)
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-09-30 12:03:00 +00:00
Alex Grozav
9f514bc49d
refactor(editor): Import MCP shared code from platform packages (no-changelog) (#39492)
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-30 12:01:28 +00:00
n8n-cat-bot[bot]
632a1f380e
test: Stabilize canvas zoom checks after route changes (#39970)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
2026-09-30 12:01:27 +00:00
Matsu
63c01bee24
chore: Migrate agent runtime lint to Oxlint (no-changelog) (#39971) 2026-09-30 11:46:43 +00:00
Emilia
cd9d1ac669
fix(core): Keep jobs a worker enqueued out of its shutdown drain (#39800)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 11:46:31 +00:00
Veljko Terzic
320cc2dda3
feat(core): Run the instance registry reconciliation as a durable system task (no-changelog) (#39882) 2026-09-30 11:35:13 +00:00
Mutasem Aldmour
0c449919da
docs: Add sourcing, update shapes, and capability-gap handling to the Agent Builder eval skill (no-changelog) (#39983)
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-09-30 11:22:59 +00:00
Michael Drury
4d931ca77e
fix(editor): Preserve Agent instruction edits during autosave (#39972) 2026-09-30 11:02:32 +00:00
Matsu
849ededd5b
chore: Migrate testing infrastructure lint to Oxlint (no-changelog) (#39942) 2026-09-30 10:55:11 +00:00
Jaakko Husso
10e484f552
feat: Tell the AI Assistant which preview tabs are open (#39768)
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-30 10:53:42 +00:00
n8n-cat-bot[bot]
fa93a7f01f
fix: Clear connection hover promptly (#39958)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
2026-09-30 10:39:50 +00:00
Charlie Kolb
760e4e81d0
refactor(core): Move REST error handling to backend services (no-changelog) (#39907) 2026-09-30 10:31:37 +00:00
Veljko Terzic
ad90c63923
fix(core): Keep floating entitlements attached when a multi-main instance shuts down (#39713) 2026-09-30 10:29:02 +00:00
Alex Grozav
9d11369db7
feat(editor): Show migration progress on report overview (#39861)
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-30 10:18:29 +00:00
Mike Repeć
a19bf32e51
feat(core): Add fast native evaluation for a subset of expressions (no-changelog) (#37613)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-09-30 10:16:42 +00:00
mfsiega
dcda65f7d9
fix(engine): Report the recorded finish time on execution end events (#39963)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-30 10:02:13 +00:00
Tomi Turtiainen
3c1aaae176
feat(core): Send streaming webhook chunks from engine v2 (no-changelog) (#39947) 2026-09-30 09:50:04 +00:00
Uddish Verma
9f0a8463a9
refactor(API): Migrate PUT /settings/otel to the decorator pattern (no-changelog) (#39864)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Co-authored-by: Declan Carroll <declancarroll@gmail.com>
2026-09-30 09:36:02 +00:00
Mutasem Aldmour
52db75973e
docs: Keep Instance AI eval expectations free of internal mechanics (no-changelog) (#39967)
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-09-30 09:35:38 +00:00
yehorkardash
99182c29be
feat(core): Change IAI permissions logic (#39303) 2026-09-30 09:32:20 +00:00
Jaakko Husso
2f4bbfa7d2
feat(editor): Reorder AI Assistant tabs by dragging (#39664)
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-30 09:27:57 +00:00
Declan Carroll
506b99456f
test(editor): Click workflow settings option once after it appears (#39968) 2026-09-30 09:23:07 +00:00
Declan Carroll
0732d03fa1
test: Read recent samples in memory benchmarks (#39964) 2026-09-30 09:13:37 +00:00
Thanasis G
8f3574c48b
fix(core): Skip the preAuthentication retry while the stored token is fresh (no-changelog) (#38927)
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-30 09:02:04 +00:00
Rob Hough
41994bc6c7
feat(editor): Add keyboard controls to n8n Assistant panel (#39386)
Co-authored-by: Bob Hough <15361982+heymynameisrob@users.noreply.github.com>
Co-authored-by: linear-code[bot] <222613912+linear-code[bot]@users.noreply.github.com>
2026-09-30 08:58:04 +00:00
Jaakko Husso
0c4bf38033
feat(editor): Open any project resource as an AI Assistant tab (#39663)
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-30 08:50:48 +00:00
Raúl Gómez Morales
57ef0d138f
fix(editor): Show full reasoning on first expand of assistant thinking block (#39892) 2026-09-30 08:47:04 +00:00
Benjamin Schroth
24178e51d3
feat(core): Filter the global agents list by n8n chat availability (no-changelog) (#39347) 2026-09-30 08:39:51 +00:00
Benjamin Schroth
799b92993a
fix(core): Report a capped agent test run to the builder as an error (no-changelog) (#39513) 2026-09-30 08:39:32 +00:00
mfsiega
52dcb9252c
feat(core): Add migration finding diff (no-changelog) (#39870)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-30 08:26:51 +00:00
Emilia
d8dcc924b7
fix(core): Pass graceful-shutdown timeout to runners in the runners image (#39796)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 07:45:14 +00:00
Raúl Gómez Morales
9a4a1dcf27
feat(editor): Open and collapse the logs panel around runs in the AI Assistant artifact view (#38773) 2026-09-30 07:43:16 +00:00
Raúl Gómez Morales
38f21c01f1
fix(core): Ask before building a picked onboarding suggestion (no-changelog) (#39872) 2026-09-30 07:41:22 +00:00
Raúl Gómez Morales
1694727548
fix(editor): Stop credential icon overlapping the credential name (#39944) 2026-09-30 07:26:24 +00:00
Nour Alhadi Mahmoud
fefb2da2b1
fix(editor): Keep execution filters when navigating away from executions (#39781)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 07:24:19 +00:00
Nour Alhadi Mahmoud
dafc10852f
fix(core): Match variables by name and scope in the promotion change preview (no-changelog) (#39792)
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-30 07:10:49 +00:00
Mike Repeć
c83fee02fa
fix(core): Stop waiting on Bull job.finished() for queued executions (#39723) 2026-09-30 07:01:44 +00:00
n8n-assistant[bot]
191a22e759
chore: Bundle/2.x (#39935)
Co-authored-by: Matsu <matias.huhta@n8n.io>
2026-09-30 07:44:10 +03:00
n8n-assistant[bot]
c17c48043e
chore: Update e2e impact map (#39933)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-09-30 03:55:41 +00:00
Rúben Castro
31b6649d78
fix(core): Check credential types that HTTP Request names by parameter (no-changelog) (#39429)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 17:39:10 +00:00
Robin Braumann
5d40d837f6
feat(core): Escalate background sub-agent approvals to parent chats (#39552) 2026-09-29 15:55:10 +00:00
Sam Wooler
90e3385c15
refactor(API): Migrate remaining data-tables columns endpoints to PublicApiController (no-changelog) (#39790) 2026-09-29 15:39:42 +00:00
Kai
373bf77f3a
feat(editor): Refine the workflow review detail layout (#39866) 2026-09-29 15:30:21 +00:00
Savelii
834202d124
test: Stabilize the project move-resources spec (no-changelog) (#39906)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-29 15:30:11 +00:00
Sam Wooler
6918fda018
fix(API): Emit valid schemas for untyped values in the Public API spec (#39884)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 15:16:40 +00:00
Bernhard Wittmann
f58f0ec1d0
feat(editor): Release Browser Use to all users (#39762)
Co-authored-by: RomanDavydchuk <roman.davydchuk@n8n.io>
2026-09-29 15:13:50 +00:00
n8n-cat-bot[bot]
d69b52e2c7
fix(editor): Keep credentials unchanged on read-only canvases (#39889)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
Co-authored-by: Svetoslav Dekov <svetoslav.dekov@n8n.io>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 15:12:53 +00:00
n8n-cat-bot[bot]
e13c480626
fix(core): Apply MCP availability rules to error workflow settings (#39885)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
Co-authored-by: Svetoslav Dekov <svetoslav.dekov@n8n.io>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 15:11:35 +00:00
mfsiega
a5b00eee2d
feat(engine): Add a service that cancels an execution (#39806)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-29 15:01:08 +00:00
Benny Neugebauer
0cf969a05d
test: Add an offline npm registry service to the E2E harness (no-changelog) (#39813) 2026-09-29 14:56:51 +00:00
Robin Braumann
04abd59fc6
feat(core): Add durable background approval state (no-changelog) (#39558)
Co-authored-by: Danny Martini <danny@n8n.io>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 14:51:18 +00:00
Theodore Nastos
7204df600d
fix(editor): Do not run the workflow when the save before execute fails (#39763) 2026-09-29 14:50:29 +00:00
RomanDavydchuk
f9dbd2c8d6
fix(Split Out Node): Support dot notation in destination fields (#39661) 2026-09-29 14:44:00 +00:00
RomanDavydchuk
ce295b6080
fix(WhatsApp Node): Fetch all templates for the dropdown (#39497) 2026-09-29 14:41:37 +00:00
n8n-cat-bot[bot]
fc99aaf87b
fix(core): Redact MCP tool telemetry properties (#39887)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
Co-authored-by: Svetoslav Dekov <svetoslav.dekov@n8n.io>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 14:39:52 +00:00
n8n-cat-bot[bot]
f0b70134a8
fix(editor): Fix copy button hover state in MCP settings (#39888)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
Co-authored-by: Svetoslav Dekov <svetoslav.dekov@n8n.io>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 14:34:38 +00:00
Dmitrii
1153702d1a
feat(editor): Show n8n logo on canvas in canvas-only mode (#39846) 2026-09-29 14:33:07 +00:00
Oğuz Eroğlu
29c09c6f99
fix(editor): Show remaining credits on Assistant banner for Cloud UBB (#39901) 2026-09-29 14:21:17 +00:00
Jaakko Husso
9ab649ff55
feat(editor): Close AI Assistant tabs and keep the open tabs per thread (#39660)
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-29 14:18:35 +00:00
Pierantonio Cangianiello
933113e375
docs(engine): Add ADR on deletion of binary files written by Engine v2 runs (no-changelog) (#39617) 2026-09-29 14:12:41 +00:00
mfsiega
3c896fcdea
feat(core): Add migration finding repositories (no-changelog) (#39550)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-29 14:10:53 +00:00
MHMD
88d15ed051
feat(editor): Rewrite the credential sharing role and surface where it's used (no-changelog) (#39894) 2026-09-29 14:09:04 +00:00
n8n-cat-bot[bot]
adb42a31ff
chore: Bug - Git hooks create an untracked .build folder in fresh worktrees on macOS (#39648)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
2026-09-29 14:08:46 +00:00
Guillaume Jacquart
55fd9daa08
feat(core): Send a log streaming audit event when a policy blocks an action (#39581)
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-29 14:02:09 +00:00
Rowena Jones
53b84d2baa
fix(Agent Node): Remove outdated tutorial tip callout (#35505)
Co-authored-by: Kartik Balasubramanian <22399046+HumanistSerif@users.noreply.github.com>
2026-09-29 13:59:28 +00:00
Robin Braumann
05401103d2
feat(core): Enable steering for queued Preview messages (no-changelog) (#39556) 2026-09-29 13:57:30 +00:00
José Braulio González Valido
995c101dba
fix(ai-builder): Read the reseeded scenario tables live instead of from pinned rows (no-changelog) (#39814)
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-29 13:53:20 +00:00
Anne Aguirre
114a5737f5
feat(core): Add tool to mark an agent session as failed (#39754) 2026-09-29 13:26:25 +00:00
oleg
55a87877b5
feat(editor): Show the trigger icon on the execute button and run a trigger from its menu (#39580) 2026-09-29 13:25:33 +00:00
Svetoslav Dekov
93b658c8db
fix(core): Connect Slack Agent channels through managed setup in MCP (#39776)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 13:06:12 +00:00
Matsu
fe1bfdfd29
chore(core): Migrate runtime lint to Oxlint (no-changelog) (#39844) 2026-09-29 16:06:29 +03:00
Charlie Kolb
499d64181a
refactor(core): Move shared cache infrastructure to backend services (no-changelog) (#39851) 2026-09-29 12:55:05 +00:00
José Braulio González Valido
bf6ba26a7f
feat(ai-builder): Budget eval builds per user turn and make a timeout its own outcome (no-changelog) (#39540)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-29 12:52:52 +00:00
MHMD
7e3a1037ff
feat(core): Split can-see from can-use in the workflow payload (no-changelog) (#39804) 2026-09-29 12:49:49 +00:00
Robin Braumann
b09fd0dc69
feat(core): Add agent input boundaries and steering storage (no-changelog) (#39529) 2026-09-29 12:41:34 +00:00
n8n-cat-bot[bot]
7b12ec165d
refactor(API): Migrate GET /settings/otel to the decorator pattern (no-changelog) (#39720)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
Co-authored-by: Uddish Verma <uddish.verma@n8n.io>
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Co-authored-by: Declan Carroll <declancarroll@gmail.com>
2026-09-29 12:41:18 +00:00
Arvin A
41a0ccaf16
fix(Execute Workflow Node): Return declared sub-workflow outputs (#39755) 2026-09-29 12:40:18 +00:00
Savelii
6974abaea3
test: Stabilize the workflow sharing UI spec (no-changelog) (#39862)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-29 12:25:49 +00:00
Matsu
f3f660b745
chore: Migrate database and engine lint to Oxlint (no-changelog) (#39841) 2026-09-29 12:24:22 +00:00
Declan Carroll
d2b6e718d1
chore: Add local test inventory and profiling helpers (no-changelog) (#39388)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
2026-09-29 12:16:24 +00:00
Matsu
62cadddf28
chore: Migrate AI tooling lint to Oxlint (no-changelog) (#39834) 2026-09-29 12:02:19 +00:00
Michael Drury
1882cff76b
fix(core): Correct Agent session pagination on SQLite (#39802) 2026-09-29 11:57:20 +00:00
Rob Hough
8d4111dff8
fix(editor): Correct dark theme overlay shadows in design-system (no-changelog) (#39602) 2026-09-29 11:44:21 +00:00
Matsu
0e5c9aaed6
chore: Migrate backend tooling lint to Oxlint (no-changelog) (#39825) 2026-09-29 11:42:22 +00:00
Tomi Turtiainen
a45162c5fa
refactor(core): Extract trigger credential gate (no-changelog) (#39453) 2026-09-29 11:31:18 +00:00
Robin Braumann
0811443909
fix(core): Deduplicate agent message deliveries (#39794) 2026-09-29 11:24:49 +00:00
Veljko Terzic
54c351b92f
feat(core): Run the trusted key refresh as a durable system task (no-changelog) (#39789)
Co-authored-by: Lorent Lempereur <lorent.lempereur@n8n.io>
2026-09-29 11:06:01 +00:00
Andreas Fitzek
0f63032916
feat(core): Add inbound-auth-core module with trusted source entities and migration (#39642) 2026-09-29 10:47:24 +00:00
Theodore Nastos
188b03c486
feat(editor): Show restricted node types in the agent and Chat Hub tool pickers (no-changelog) (#39461)
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-29 10:35:52 +00:00
Raúl Gómez Morales
df3945af8a
fix(editor): Correct the n8n Assistant onboarding greeting animations and actions (no-changelog) (#39829) 2026-09-29 10:25:33 +00:00
Charlie Kolb
212fbfe455
refactor(core): Move EventService to backend-services (#39743) 2026-09-29 10:22:19 +00:00
Tomi Turtiainen
84da588c36
feat(engine): Let the caller tell engine v2 which response it expects (#39780) 2026-09-29 10:19:05 +00:00
Joco-95
af2695407a
feat: Implements n8n Assistant nudge on workflow errors (#39774) 2026-09-29 10:16:39 +00:00
Benjamin Schroth
ff98d9900f
feat(core): Filter the agent session list by chat origin and owner (no-changelog) (#39773) 2026-09-29 10:09:26 +00:00
Anne Aguirre
6cc8c0d448
feat(ai-builder): Change knowledge subtitle (#39838) 2026-09-29 09:41:25 +00:00
Guillaume Jacquart
c575f18c99
fix(core): Apply policy checks to CLI import and execute commands (#39714)
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-29 09:40:36 +00:00
Anne Aguirre
f931ea81d5
feat(core): New agent styled differently on agent list view (#39856) 2026-09-29 09:39:59 +00:00
Anne Aguirre
8c9733b5c6
feat(core): Capture sidecall generation usd cost (#39627) 2026-09-29 09:39:58 +00:00
n8n-cat-bot[bot]
f487613bfc
chore: Bug - The Execute workflow button overlaps with other canvas buttons when the AI assistant is open on a smaller screen (#39797)
Some checks failed
Build: Benchmark Image / build (push) Has been cancelled
CI: Master (Build, Test, Lint) / Build & format (push) Has been cancelled
CI: Master (Build, Test, Lint) / Unit tests (24.18.1) (push) Has been cancelled
CI: Master (Build, Test, Lint) / Unit tests (26.5.1) (push) Has been cancelled
CI: Master (Build, Test, Lint) / Lint (push) Has been cancelled
CI: Master (Build, Test, Lint) / Verify single-instance deps (npm-install) (push) Has been cancelled
Util: Publish API Schema / Cloudflare Pages (push) Has been cancelled
CI: Master (Build, Test, Lint) / Notify Slack on failure (push) Has been cancelled
Util: Refresh cubic schema / Refresh vendored cubic schema (push) Has been cancelled
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
2026-09-29 09:26:36 +00:00
Arvin A
d2eae41dd6
fix(DataTable Node): Continue paginated reads when row counts change (#39764) 2026-09-29 09:25:02 +00:00
Savelii
d92bc4d3ab
perf(core): Load project relations without joining every role scope (#39756)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-29 08:45:29 +00:00
Robin Braumann
84efbac6f1
refactor(core): Reference accepted messages from agent queue (#39778) 2026-09-29 08:39:25 +00:00
Andreas Fitzek
599df45795
feat(core): Add trusted source config schema in @n8n/inbound-auth (no-changelog) (#39533) 2026-09-29 08:33:06 +00:00
mfsiega
0f77ba5e7e
feat(editor): Replace severity with impact categories in the migration report (#39551)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Co-authored-by: Claude Fable 5.1 <noreply@antheropic.com>
2026-09-29 08:30:09 +00:00
Jaakko Husso
682afd5aa9
feat(core): Add endpoints to read and save AI Assistant thread tabs (no-changelog) (#39658)
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-29 08:24:30 +00:00
Svetoslav Dekov
529761ff03
chore: Connect the Claude Code desktop app to Codespaces (no-changelog) (#39524)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 08:04:54 +00:00
Charlie Kolb
3220953fb4
test(core): Stabilize Sentry event bus integration test (#39830) 2026-09-29 08:04:44 +00:00
Charlie Kolb
0cd66905e5
ci: Run Windows build for CLI build script changes (#39824) 2026-09-29 08:01:08 +00:00
Lorent Lempereur
bab2978ccf
feat(core): Run legacy Cron node schedules on the durable scheduler (#39411) 2026-09-29 07:58:52 +00:00
oleg
a66221d24a
feat(editor): Announce credentials while the n8n assistant builds workflows (no-changelog) (#39464) 2026-09-29 07:43:03 +00:00
Mike Repeć
c92f86ef71
ci: Copy the full typescript-config package into the benchmark image (no-changelog) (#39833) 2026-09-29 07:38:48 +00:00
Elias Meire
0afda58804
fix: Hide node types that the instance does not load (#39571) 2026-09-29 07:34:41 +00:00
n8n-cat-bot[bot]
46645c6d73
test: Keep WebSocket test connections offline (#39832)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
2026-09-29 07:26:46 +00:00
Charlie Kolb
2bf9b94936
refactor(core): Move shared errors to @n8n/errors (no-changelog) (#39753) 2026-09-29 07:19:05 +00:00
n8n-release-tag-merge[bot]
702bb42cbb Merge tag 'n8n@2.42.0' 2026-09-29 07:23:13 +00:00
n8n-assistant[bot]
86c23326c9
🚀 Release 2.42.0 (#39826)
Co-authored-by: n8n-release-helper[bot] <265227495+n8n-release-helper[bot]@users.noreply.github.com>
2026-09-29 07:11:00 +00:00
Konstantin Tieber
b613184d64
feat(core): Backfill historic daily data when instance-reporting is first enabled (no-changelog) (#39567)
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-29 06:59:12 +00:00
Ali Elkhateeb
dfd7a52224
fix(API): Return an execution when its stored trace context is incomplete (#39715)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-29 06:53:58 +00:00
Charlie Kolb
918bcddb4f
fix(core): Build email templates on Windows (no-changelog) (#39823) 2026-09-29 06:47:57 +00:00
Veljko Terzic
5fe92a568a
feat(core): Run activity pruning as a durable system task (no-changelog) (#39769) 2026-09-29 06:37:55 +00:00
Michael Kret
2b0719dc70
feat: Suggest Gateway credit services during node discovery (#39631) 2026-09-29 06:36:43 +00:00
Tomi Turtiainen
1630d4c894
fix: Require full ADR references in prose (no-changelog) (#39783) 2026-09-29 06:01:59 +00:00
Charlie Kolb
8f29a36f16
ci: Allow trusted team to skip required reviews (no-changelog) (#39759) 2026-09-29 05:37:23 +00:00
Ricardo Espinoza
8cd8cf04ac
fix(core): Withhold MCP preference scopes from users outside the experiment arm (no-changelog) (#39812) 2026-09-29 05:04:36 +00:00
n8n-assistant[bot]
cdbb60e0fe
chore: Update e2e impact map (#39818)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-09-29 03:41:31 +00:00
n8n-cat-bot[bot]
9f5c752cd1
fix(core): Link MCP workflow access errors to workflow settings (#39738)
Some checks are pending
CI: Master (Build, Test, Lint) / Build & format (push) Waiting to run
CI: Master (Build, Test, Lint) / Unit tests (24.18.1) (push) Waiting to run
CI: Master (Build, Test, Lint) / Unit tests (26.5.1) (push) Waiting to run
CI: Master (Build, Test, Lint) / Lint (push) Waiting to run
CI: Master (Build, Test, Lint) / Verify single-instance deps (npm-install) (push) Waiting to run
CI: Master (Build, Test, Lint) / Notify Slack on failure (push) Blocked by required conditions
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
Co-authored-by: Svetoslav Dekov <svetoslav.dekov@n8n.io>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 21:24:32 +00:00
mfsiega
310635e7d5
feat(core): Add migration finding tables (no-changelog) (#39760)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-28 19:13:06 +00:00
Mike Repeć
a1360f42d3
fix(benchmark): Wait for the readiness endpoint before the owner setup (no-changelog) (#39799) 2026-09-28 16:26:02 +00:00
Michael Drury
3daa34dba0
fix(editor): Improve Agent layout and sub-agent guidance (#39791) 2026-09-28 16:11:31 +00:00
Rúben Castro
0b92e12d9c
feat(core): Widen the content-import policy check to cover credentials (no-changelog) (#39379)
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-28 16:05:10 +00:00
Robin Braumann
af36326844
refactor(core): Separate agent messages from execution state (#39742) 2026-09-28 16:00:39 +00:00
n8n-cat-bot[bot]
a41c8c46e7
fix: Update stale docs.n8n.io links in product code (no-changelog) (#39638)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
Co-authored-by: Elias Meire <elias@meire.dev>
2026-09-28 15:53:59 +00:00
n8n-cat-bot[bot]
6c1edac204
fix(API): Accept workflow descriptions on creation (#39705)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
2026-09-28 15:48:16 +00:00
José Braulio González Valido
5676882e64
fix(core): Scope the Agent Builder credential list to the eval thread allowlist (no-changelog) (#39092)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-28 15:41:16 +00:00
José Braulio González Valido
918512f62a
fix(ai-builder): Keep scenario tables for Agent builds and reuse same-named seed tables (no-changelog) (#39767)
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-28 15:40:32 +00:00
Uddish Verma
3a91c09727
refactor(API): Migrate PUT /settings/sso/oidc to the decorator pattern (no-changelog) (#39729)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-28 15:39:01 +00:00
Riqwan Thamir
d0255c71ec
fix(core): Point post-build guidance at the active skill (no-changelog) (#39203)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-28 15:23:22 +00:00
Riqwan Thamir
d3e846eb53
perf(core): Move instance URLs out of the Instance AI system prompt (no-changelog) (#39555)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-28 15:12:23 +00:00
Riqwan Thamir
c1d666301f
feat(editor): Make Instance AI LLM step debug data readable (no-changelog) (#39575)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-28 15:11:01 +00:00
Sam Wooler
bd2d361afc
refactor(API): Migrate GET data-tables columns endpoint to PublicApiController (no-changelog) (#39772) 2026-09-28 14:45:03 +00:00
Bernhard Wittmann
80a1596a3d
feat(core): Render Microsoft Teams agent replies progressively (no-changelog) (#39514) 2026-09-28 14:41:26 +00:00
Jaakko Husso
ae2b8535a9
fix(core): Include the project id in the AI Assistant project context (no-changelog) (#39770)
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-28 14:39:36 +00:00
Tomi Turtiainen
c59441323d
refactor(core): Separate webhook no-response callback (#39684) 2026-09-28 14:36:12 +00:00
Raúl Gómez Morales
37572b6c1a
feat(core): Update the n8n Assistant onboarding copy (no-changelog) (#39777) 2026-09-28 14:30:49 +00:00
Yuliia Pominchuk
4a01756ea4
feat(core): Check credentials against the person running the workflow (#39685)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-28 14:09:16 +00:00
Jaakko Husso
106f9824b0
feat(core): Add a concise reply style prompt profile (no-changelog) (#39408)
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-28 14:00:26 +00:00
Tomi Turtiainen
697e0bb8a6
test(engine): Run responseNode webhook E2E tests on engine v2 (#39761) 2026-09-28 13:59:41 +00:00
Dmitrii
20807d2d3d
fix(core): Enforce one instance report per day with a unique index (#39536)
Co-authored-by: konstantintieber <konstantin.tieber@n8n.io>
2026-09-28 13:51:25 +00:00
Milorad FIlipović
41021e5e19
fix(editor): Address Assistant mentions feedback (no-changelog) (#39596) 2026-09-28 13:46:22 +00:00
Alexandre Vaz
b3d4cfe311
fix(googleFirebaseCloudFirestore Node): Preserve field types for numeric strings and falsy values (#27399)
Co-authored-by: Garrit Franke <32395585+garritfra@users.noreply.github.com>
2026-09-28 13:43:23 +00:00
Raúl Gómez Morales
d765bbd68e
feat: Add an n8n Assistant onboarding thread for new Cloud signups (#39730) 2026-09-28 13:34:11 +00:00
Uddish Verma
63ae22b42b
refactor(API): Migrate GET /settings/sso/oidc to the decorator pattern (no-changelog) (#39728)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-28 13:30:29 +00:00
Declan Carroll
41dbfd987b
ci: Speed up the PR build job and move the E2E matrix off its path (no-changelog) (#39736) 2026-09-28 13:16:13 +00:00
Daria
06c61d9437
fix(editor): Empty Group Last Refinements (no-changelog) (#39632) 2026-09-28 12:57:32 +00:00
Daria
b3ce351cde
feat(core): Add empty group telemetry (no-changelog) (#39519) 2026-09-28 12:57:31 +00:00
Daria
8336da3482
feat(editor): Gate empty canvas groups behind a feature flag (no-changelog) (#39389) 2026-09-28 12:57:30 +00:00
Daria
a392f4e18e
feat(editor): Improve empty-group style & behavior (no-changelog) (#38705) 2026-09-28 12:57:29 +00:00
Daria
acaa281c54
feat(editor): Manage the empty-group placeholder lifecycle (no-changelog) (#38704) 2026-09-28 12:57:28 +00:00
Daria
37c160eec0
feat(editor): Connect empty groups to the node graph (no-changelog) (#38703) 2026-09-28 12:57:27 +00:00
Daria
ad68bfefd1
feat(editor): Add manual empty-group entry points (no-changelog) (#38702) 2026-09-28 12:57:26 +00:00
Tomi Turtiainen
98a1fc1b94
chore: Instruct create pr skill to not include no-changelog when not needed (#39693) 2026-09-28 12:49:36 +00:00
Mayank SIngh
a2a2638461
fix(API): Validate OpenAPI path parameter names (#39067) 2026-09-28 12:37:34 +00:00
Sam Wooler
9e7bc6691c
fix(API): Allow extendsCredential in workflow create/update (#39243)
Co-authored-by: Uddish Verma <uddish.verma@n8n.io>
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-28 12:35:46 +00:00
n8n-cat-bot[bot]
9bda02122d
chore: Schema-First migration — audit (#39207)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
Co-authored-by: Sam Wooler <swooler592@gmail.com>
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-28 12:35:38 +00:00
Daria
8ff280ed3d
feat(core): Define empty-group contract (no-changelog) (#38701) 2026-09-28 12:22:29 +00:00
Alex Grozav
63ee800cf2
refactor(editor): Cut the MCP settings dependency on experiments and shell modals (no-changelog) (#39491)
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-28 12:20:49 +00:00
Tomi Turtiainen
1a2958294d
test(engine): Run lastNode webhook E2E tests on engine 2.0 (no-changelog) (#39694) 2026-09-28 12:12:12 +00:00
Jaakko Husso
356eb8f093
feat(core): Add storage for AI Assistant thread tabs (no-changelog) (#39657)
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-28 12:06:00 +00:00
Lorent Lempereur
bda9453953
fix(core): Count a database ping as successful when its reply arrives during event-loop lag (#39645) 2026-09-28 12:04:44 +00:00
Savelii
1852295d9a
test: Stabilize the form submit gate client spec (no-changelog) (#39750)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-28 11:49:02 +00:00
Michael Drury
68f856fb9d
feat(core): Add production n8n Chat channel infrastructure (no-changelog) (#39476)
Co-authored-by: Benjamin Schroth <benjamin@n8n.io>
2026-09-28 11:47:35 +00:00
Charlie Kolb
256e581aa7
refactor(core): Move UrlService to backend services (no-changelog) (#39751) 2026-09-28 11:43:39 +00:00
n8n-cat-bot[bot]
b8cda55e57
fix(editor): Sorting data table rows by unknown column returns raw database error (#39733)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
2026-09-28 11:43:27 +00:00
Lorent Lempereur
194f13d30f
refactor: Move first occurrence seeding into the scheduler's provision (no-changelog) (#39605) 2026-09-28 11:42:20 +00:00
Veljko Terzic
e3b4ecb808
feat(core): Run the MCP registry refresh as a durable system task (no-changelog) (#39520)
Co-authored-by: Lorent Lempereur <lorent.lempereur@n8n.io>
2026-09-28 11:34:28 +00:00
mfsiega
6272d465df
perf(core): Avoid terminal outbox scans during publication reconciliation (#39267) 2026-09-28 10:57:32 +00:00
Alex Grozav
7439d56c5c
perf(editor): Load the editor with fewer startup requests (no-changelog) (#39616)
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-28 10:46:02 +00:00
Tomi Turtiainen
ce3d50b8f9
feat(engine): Answer lastNode and responseNode webhooks with a remote engine v2 data plane (#39636) 2026-09-28 10:44:31 +00:00
Robin Braumann
3584d15ddd
refactor(core): Simplify agent runtime structure (no-changelog) (#39507) 2026-09-28 10:40:51 +00:00
Tomi Turtiainen
a51434ff7d
test(engine): Run onReceived webhook E2E tests on engine 2.0 (#39181) 2026-09-28 10:12:20 +00:00
Guillaume Jacquart
2157b35e24
test: Add E2E tests for credential type policy flows (no-changelog) (#39584)
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-28 09:30:12 +00:00
Guillaume Jacquart
e13178ac40
test: Add E2E tests for node type policy flows (no-changelog) (#39579)
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-28 09:30:11 +00:00
Jaakko Husso
88d8390222
feat(editor): Restyle AI Assistant tab bar and add a tab hover card (#39625)
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-28 09:29:55 +00:00
Mike Repeć
5d508ec1e1
fix(core): Store queue job results only for executions this process enqueued (#39712) 2026-09-28 09:19:34 +00:00
Declan Carroll
ad2c9829fd
feat(editor): Add dev server reverse proxy for backend routes (no-changelog) (#39562) 2026-09-28 09:18:49 +00:00
Robin Braumann
8a4fd13423
feat(editor): Add queued message controls to agent Preview chat (#39487) 2026-09-28 09:07:54 +00:00
James Martin
6437496faf
chore(core): Implements Principal and SecurityContext types (no-chagelog) (#39496)
Some checks are pending
Build: Benchmark Image / build (push) Waiting to run
CI: Master (Build, Test, Lint) / Build & format (push) Waiting to run
CI: Master (Build, Test, Lint) / Unit tests (24.18.1) (push) Waiting to run
CI: Master (Build, Test, Lint) / Unit tests (26.5.1) (push) Waiting to run
CI: Master (Build, Test, Lint) / Lint (push) Waiting to run
CI: Master (Build, Test, Lint) / Verify single-instance deps (npm-install) (push) Waiting to run
CI: Master (Build, Test, Lint) / Notify Slack on failure (push) Blocked by required conditions
Util: Publish API Schema / Cloudflare Pages (push) Waiting to run
2026-09-28 08:58:05 +00:00
ocdejong
2ec6ac7c9c
feat(editor): Show each policy violation with its scope when a save, publish or execution is refused (#39263)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 08:56:57 +00:00
Declan Carroll
6752f1b5f7
ci: Measure PR coverage only for changed source files (no-changelog) (#39675) 2026-09-28 08:49:46 +00:00
Declan Carroll
de4fb8d6a3
test: Remove redundant E2E page loads and the fixed reset delay (no-changelog) (#39681) 2026-09-28 08:41:09 +00:00
Michael Drury
1fe19db437
fix(editor): Show project icons in Agent breadcrumbs (#39649) 2026-09-28 08:35:04 +00:00
n8n-cat-bot[bot]
52365781a6
refactor(API): Migrate community package POST, PATCH, and DELETE routes (no-changelog) (#39346)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
Co-authored-by: Ali Elkhateeb <ali.elkhateeb@n8n.io>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Declan Carroll <declan@n8n.io>
2026-09-28 08:33:59 +00:00
Yuliia Pominchuk
9af0f02a74
feat(editor): Group the node credential picker by where each credential comes from (#39646) 2026-09-28 08:26:21 +00:00
n8n-cat-bot[bot]
d899e8b846
chore: Improve settings modal responsiveness (#39651)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
2026-09-28 08:22:43 +00:00
Declan Carroll
3f0c7d5bda
build: Upgrade Vitest to v5 (no-changelog) (#39674) 2026-09-28 07:54:06 +00:00
Bernhard Wittmann
4f64f8c4a6
test(core): Cover Microsoft Teams channel and group chat conversations (no-changelog) (#38801) 2026-09-28 07:39:22 +00:00
Tomi Turtiainen
eb684d2256
chore: Update create pr skill instructions for engine scope (#39692) 2026-09-28 07:37:43 +00:00
Tomi Turtiainen
3ec310db40
refactor(core): Extract webhook additional data setup (#39673) 2026-09-28 07:35:51 +00:00
Elias Meire
53dc251508
feat(core): Set OS process titles for n8n processes (#39362) 2026-09-28 07:23:48 +00:00
Emilia
34f77c059b
fix(core): Keep one failed binary-data dir from aborting the delete batch (#39194)
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-28 07:20:45 +00:00
Emilia
7bebbe4033
fix(core): Retry each file removal in the byte store's delete (#39193)
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-28 07:20:44 +00:00
Emilia
3a31c38751
fix(core): Retry the recursive removal of an execution's binary-data directory (#39185)
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-28 07:20:43 +00:00
n8n-cat-bot[bot]
7ed93eadae
chore: Replace repeated response-mode child scans with one predicate (#39076)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
2026-09-28 07:18:38 +00:00
Stephen Wright
ee9dfd1a37
feat(Azure OpenAI Chat Model Node): Rename to Azure AI Foundry Chat Model (#39603)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-28 07:17:19 +00:00
Elias Meire
4c2f220971
fix(editor): Keep quick-connect OAuth credentials out of lists until the user authorizes (#39297) 2026-09-28 07:15:53 +00:00
Charlie Kolb
2dbe4c265b
ci: Add nightly workflow that lowers the boundaries baseline (no-changelog) (#39690)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-28 07:08:01 +00:00
Charlie Kolb
5c6c581b35
build(editor): Reduce per-file module cost in the vitest suite (#38370)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-28 07:02:43 +00:00
MHMD
c5825bcd81
feat(core): Redact execution data from users who cannot use a referenced credential (no-changelog) (#39614) 2026-09-28 06:21:18 +00:00
Declan Carroll
c32827ccaf
ci: Align GitHub Actions versions across workflows (no-changelog) (#39634) 2026-09-28 05:23:04 +00:00
n8n-assistant[bot]
62f08a8b2e
chore: Update e2e impact map (#39688)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-09-28 03:35:53 +00:00
n8n-assistant[bot]
c408bace90
chore: Update node popularity data (#39687)
Some checks are pending
CI: Master (Build, Test, Lint) / Build & format (push) Waiting to run
CI: Master (Build, Test, Lint) / Unit tests (24.18.1) (push) Waiting to run
CI: Master (Build, Test, Lint) / Unit tests (26.5.1) (push) Waiting to run
CI: Master (Build, Test, Lint) / Lint (push) Waiting to run
CI: Master (Build, Test, Lint) / Performance (push) Waiting to run
CI: Master (Build, Test, Lint) / Verify single-instance deps (npm-install) (push) Waiting to run
CI: Master (Build, Test, Lint) / Notify Slack on failure (push) Blocked by required conditions
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-09-28 00:46:57 +00:00
n8n-assistant[bot]
2f2bac4645
chore: Update e2e impact map (#39676)
Some checks failed
CI: Master (Build, Test, Lint) / Build & format (push) Waiting to run
CI: Master (Build, Test, Lint) / Unit tests (24.18.1) (push) Waiting to run
CI: Master (Build, Test, Lint) / Unit tests (26.5.1) (push) Waiting to run
CI: Master (Build, Test, Lint) / Lint (push) Waiting to run
CI: Master (Build, Test, Lint) / Performance (push) Waiting to run
CI: Master (Build, Test, Lint) / Verify single-instance deps (npm-install) (push) Waiting to run
CI: Master (Build, Test, Lint) / Notify Slack on failure (push) Blocked by required conditions
Util: Update Node Popularity / update-popularity (push) Has been cancelled
Util: Update Credential Setupability / update-setupability (push) Has been cancelled
Util: Update Node Popularity / approve-and-automerge (push) Has been cancelled
Util: Update Credential Setupability / approve-and-automerge (push) Has been cancelled
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-09-27 03:36:25 +00:00
Tomi Turtiainen
7cc0450ae4
fix(engine): Handle Redis failure modes in engine v2 execution responses (#39486)
Some checks are pending
CI: Master (Build, Test, Lint) / Build & format (push) Waiting to run
CI: Master (Build, Test, Lint) / Unit tests (24.18.1) (push) Waiting to run
CI: Master (Build, Test, Lint) / Unit tests (26.5.1) (push) Waiting to run
CI: Master (Build, Test, Lint) / Lint (push) Waiting to run
CI: Master (Build, Test, Lint) / Performance (push) Waiting to run
CI: Master (Build, Test, Lint) / Verify single-instance deps (npm-install) (push) Waiting to run
CI: Master (Build, Test, Lint) / Notify Slack on failure (push) Blocked by required conditions
2026-09-26 18:44:11 +00:00
Tomi Turtiainen
f21d644e70
refactor(core): Encapsulate webhook response state in WebhookResponder (#39626) 2026-09-26 18:42:14 +00:00
Alex Grozav
c1463d0e76
refactor(editor): Decouple MCP access store from shell workflow stores (no-changelog) (#39398)
Some checks are pending
CI: Master (Build, Test, Lint) / Build & format (push) Waiting to run
CI: Master (Build, Test, Lint) / Unit tests (24.18.1) (push) Waiting to run
CI: Master (Build, Test, Lint) / Unit tests (26.5.1) (push) Waiting to run
CI: Master (Build, Test, Lint) / Lint (push) Waiting to run
CI: Master (Build, Test, Lint) / Performance (push) Waiting to run
CI: Master (Build, Test, Lint) / Verify single-instance deps (npm-install) (push) Waiting to run
CI: Master (Build, Test, Lint) / Notify Slack on failure (push) Blocked by required conditions
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-26 10:32:15 +00:00
n8n-assistant[bot]
80d5be2814
chore: Update e2e impact map (#39665)
Some checks are pending
CI: Master (Build, Test, Lint) / Build & format (push) Waiting to run
CI: Master (Build, Test, Lint) / Unit tests (24.18.1) (push) Waiting to run
CI: Master (Build, Test, Lint) / Unit tests (26.5.1) (push) Waiting to run
CI: Master (Build, Test, Lint) / Lint (push) Waiting to run
CI: Master (Build, Test, Lint) / Performance (push) Waiting to run
CI: Master (Build, Test, Lint) / Verify single-instance deps (npm-install) (push) Waiting to run
CI: Master (Build, Test, Lint) / Notify Slack on failure (push) Blocked by required conditions
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-09-26 03:30:32 +00:00
Jaakko Husso
a96980bcc2
feat(editor): Group canvas toolbar buttons in a single container (#39587)
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-25 22:00:53 +00:00
Bernhard Wittmann
a7f8f99084
feat(editor): Add telemetry for agent channel setup (no-changelog) (#39604) 2026-09-25 15:50:36 +00:00
Michael Drury
2f5f267eda
fix(core): Repair invalid tool call IDs in agent history (no-changelog) (#39640) 2026-09-25 15:18:00 +00:00
Robin Braumann
4263117b48
feat(editor): Revamp the Agent skills modal (#39545)
Co-authored-by: Michael Drury <michael.drury@n8n.io>
2026-09-25 14:56:17 +00:00
Yuliia Pominchuk
6c4135ddfb
feat(core): Attach the publishing user to triggered executions (#39466) 2026-09-25 14:41:51 +00:00
Michael Drury
8a0ca48bb7
feat(core): Add shared Agent context access (#38930) 2026-09-25 14:26:57 +00:00
Sam Wooler
8ad818dc70
fix(API): Validate Public API path parameters before the scope middlewares (no-changelog) (#39512)
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-25 14:22:21 +00:00
Uddish Verma
2742c2342f
refactor(API): Migrate POST /workflows/{workflowId}/test-runs/{runId}/cancel to the decorator pattern (no-changelog) (#39259)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-25 14:21:08 +00:00
Rushikesh Patil
bc9faf230a
fix(Google Cloud Storage Node): Preserve request headers on object create (#35218)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: Elias Meire <elias@meire.dev>
2026-09-25 14:20:32 +00:00
n8n-cat-bot[bot]
d658d182e4
fix(editor): Fetch publication status when a new workflow becomes saved (#38382)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: Declan Carroll <declan@n8n.io>
Co-authored-by: Elias Meire <elias@meire.dev>
2026-09-25 14:03:09 +00:00
Ayyub Bhoraniya
6b9c48fe4d
feat(Anthropic Node): Add prompt caching to the Message operation (#35804)
Co-authored-by: Ayyub Bhoraniya <314098225+ayyubbhoraniya@users.noreply.github.com>
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-25 14:02:56 +00:00
RomanDavydchuk
070d9fea4e
fix(editor): Show when update available for community packages if only verified packages are enabled (#39424) 2026-09-25 14:02:07 +00:00
Irénée
f459f3c400
feat: Add package import-selection command (no-changelog) (#39566)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-09-25 13:34:08 +00:00
Milorad FIlipović
ebbf129719
feat(editor): Roll out Assistant mentions (no-changelog) (#39370) 2026-09-25 13:27:43 +00:00
Danny Martini
c53a5e4dd8
feat(engine): Store the execution's waiting status (no-changelog) (#39093)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-25 13:13:59 +00:00
Csaba Tuncsik
a9247247af
fix(Microsoft Teams Node): Correct the v2 resource-locator copy (#39443) 2026-09-25 13:03:58 +00:00
Irénée
7edf74bb15
fix(core): Accept nested folders when applying a promotion (no-changelog) (#39630)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-09-25 12:47:47 +00:00
Garrit Franke
be121813f0
fix(Zoho CRM Node): Update logo to current Zoho branding (#39305) 2026-09-25 12:45:56 +00:00
Nour Alhadi Mahmoud
6c57533958
feat: Add pinned Apply and Apply Continue to the promotion CLI commands (#39597)
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-25 12:32:10 +00:00
n8n-cat-bot[bot]
88c3962423
chore: Migrate data-tables table-level endpoints to PublicApiController (#38639)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: Sam Wooler <swooler592@gmail.com>
2026-09-25 12:18:43 +00:00
Declan Carroll
3f4d63ca8a
chore: Add compact agent check commands (no-changelog) (#39624) 2026-09-25 12:18:22 +00:00
Declan Carroll
212f27505a
ci: Remove unused toolchain pilot infrastructure (no-changelog) (#39633) 2026-09-25 13:34:53 +01:00
n8n-cat-bot[bot]
8ccb6cd0a0
chore: Extract the repeated CodeMirror match-before-cursor guard in the code editor completions (#39600)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
2026-09-25 11:59:35 +00:00
n8n-cat-bot[bot]
4d6ce95563
chore: Remove the duplicate credential check from activity expansion (#39547)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
2026-09-25 11:57:31 +00:00
n8n-cat-bot[bot]
ea66d19c5e
chore: Centralize conversation-history message ordering (#39599)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
2026-09-25 11:51:21 +00:00
n8n-cat-bot[bot]
29b9421518
chore: Use one credential-scope check for activity categories (#39598)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
2026-09-25 11:49:19 +00:00
Kai
dda1166326
feat(editor): Add adaptive comment input to the review activity feed (#39583) 2026-09-25 11:48:56 +00:00
n8n-cat-bot[bot]
2a205c294f
chore: Reuse the version-name detail helper for activity events (#39595)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
2026-09-25 11:47:06 +00:00
Declan Carroll
1333a2c130
fix: Scope CLI mutation diffs to changed tests (no-changelog) (#39285) 2026-09-25 11:42:39 +00:00
Milorad FIlipović
94b501b657
feat(editor): Connect mentions to thread artifacts (no-changelog) (#39356) 2026-09-25 11:38:10 +00:00
Milorad FIlipović
95aa27ae60
feat(editor): Integrate Assistant mentions with composer (no-changelog) (#39314) 2026-09-25 11:38:09 +00:00
mfsiega
da1e9070f2
test(engine): Run parity in a separate engine container (no-changelog) (#39441)
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-25 11:31:14 +00:00
Irénée
893e97c18e
feat(API): Add partial import of a selected workflow subset (no-changelog) (#39535)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-09-25 11:17:09 +00:00
Lorent Lempereur
8e065f9a78
refactor(core): Move the event bus unsent message retry to a system task (no-changelog) (#39525) 2026-09-25 11:15:22 +00:00
Rob Hough
b09676e34a
feat(editor): Improve Agent Builder intro interactions (#39590)
Co-authored-by: Bob Hough <15361982+heymynameisrob@users.noreply.github.com>
Co-authored-by: linear-code[bot] <222613912+linear-code[bot]@users.noreply.github.com>
2026-09-25 11:15:11 +00:00
Declan Carroll
679b5bda19
ci: Seed pilot pnpm disk on Blacksmith host (no-changelog) (#39622) 2026-09-25 12:16:02 +01:00
Declan Carroll
1b33016019
docs: Focus agent quality guidance (no-changelog) (#39620) 2026-09-25 11:07:20 +00:00
n8n-cat-bot[bot]
9b5ed1da6e
chore: Bug - When a workflow has no executions, the "No executions found" and "Nothing here yet" empty states should be vertically centered within the available space (#39608)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
2026-09-25 11:06:40 +00:00
Declan Carroll
160ca30590
ci: Add manual pnpm sticky disk seed (no-changelog) (#39619) 2026-09-25 12:01:58 +01:00
Stephen Wright
2f98090707
fix(Azure OpenAI Chat Model Node): Make the Azure Entra credential sign in as the app (#39377)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-25 10:39:53 +00:00
Milorad FIlipović
4601565dbd
feat(editor): Add Assistant mention providers and artifact index (no-changelog) (#39294) 2026-09-25 10:34:47 +00:00
Bernhard Wittmann
e64291dc6d
fix(core): Make a Microsoft Teams approval card private and self-clearing (no-changelog) (#39515) 2026-09-25 10:13:14 +00:00
Dmitrii
c88df6f9c7
docs(core): Document the lost response limitation of instance report retries (no-changelog) (#39607) 2026-09-25 10:05:51 +00:00
RomanDavydchuk
11acaa3f2b
fix(MCP Client Node): Terminate session before closing the client (#39526) 2026-09-25 10:03:01 +00:00
Tomi Turtiainen
f593c2eab4
refactor(core): Extract immediate webhook response handling (no-changelog) (#39455)
Co-authored-by: Danny Martini <danny@n8n.io>
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-25 09:55:38 +00:00
Lorent Lempereur
10826925c9
feat(core): Let instance system tasks run on a sub-second interval (no-changelog) (#39522) 2026-09-25 09:55:32 +00:00
n8n-cat-bot[bot]
7c54cc6a10
chore: Extract the repeated MCP tool error telemetry and response into one helper (#39601)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
2026-09-25 09:35:17 +00:00
n8n-cat-bot[bot]
56d0b8fc63
chore: Bug - If node branches are flipped and crossing (#39480)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
2026-09-25 09:18:33 +00:00
n8n-cat-bot[bot]
0fa8d99934
chore: Use the shared error-message helper in ephemeral node execution (#39591)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
2026-09-25 09:10:11 +00:00
Jaakko Husso
42dc3991c1
fix(editor): Keep typed text in the AI Assistant chat rename input (#39570)
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-25 08:52:27 +00:00
Tomi Turtiainen
44fef497f2
feat(core): Carry execution responses over Redis (no-changelog) (#39475) 2026-09-25 08:52:18 +00:00
n8n-cat-bot[bot]
f364b38b74
fix: Enable OpenCode code mode by default (no-changelog) (#39589)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
2026-09-25 08:43:50 +00:00
Andreas Fitzek
38c92f4011
feat(core): Add browser OAuth2 flow for n8n User Auth webhooks (#39332) 2026-09-25 08:37:06 +00:00
n8n-cat-bot[bot]
9f5436cd6d
chore: Remove redundant async wrappers from OAuth flow proxy callbacks (#39077)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
2026-09-25 08:36:58 +00:00
Danny Martini
d5777232ad
feat(engine): Declare a deadline wait when a v1 node waits for a date (no-changelog) (#39164)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-25 08:23:46 +00:00
Pierantonio Cangianiello
94d803f595
feat(engine): Support binary reference bodies in Engine v2 sendResponse (no-changelog) (#39534) 2026-09-25 08:22:17 +00:00
Veljko Terzic
40fbf7c155
feat(core): Run the publication outbox cleanup as a durable system task (no-changelog) (#39511) 2026-09-25 08:01:38 +00:00
Tomi Turtiainen
c6fb5737c1
chore(engine): Use engine v2 naming (#39582) 2026-09-25 07:54:48 +00:00
José Braulio González Valido
e3fe95d228
fix(ai-builder): Honor neverError in eval mocks, seed agent tables, filter pinned reads and report start refusals (no-changelog) (#39337)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-25 07:47:28 +00:00
Kai
1a4a91256d
fix(API): Save credential descriptions through the public credentials API (no-changelog) (#39565) 2026-09-25 07:36:09 +00:00
Anne Aguirre
80e4084420
feat(ai-builder): Confirm template selections before finishing the build (#39517) 2026-09-25 07:29:09 +00:00
Elias Meire
58e55b5898
fix(Xero Node): Send tenant header on every page when returning all items (#39431) 2026-09-25 06:56:53 +00:00
n8n-cat-bot[bot]
a3aa6c0074
fix: Validate node codex categories during linting (#39543)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
2026-09-25 06:04:30 +00:00
n8n-assistant[bot]
4d5c66375d
chore: Update e2e impact map (#39564)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-09-25 03:32:30 +00:00
Roman Makowiak
a634154a22
feat(editor): Let the user choose the scope of a preference from the chat card (no-changelog) (#39451)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Co-authored-by: Ricardo Espinoza <ricardo@n8n.io>
2026-09-25 00:38:20 +00:00
Jan
8d48d90d72
feat(editor): Animate height changes in the MCP callback URLs dialog (#39490)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-25 00:27:31 +00:00
José Braulio González Valido
aac074ab06
fix(core): Let the eval user proxy complete a templated-auth credential card (no-changelog) (#39115)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-25 00:19:15 +00:00
Jan
fdc6b9d892
feat(editor): Preview own connected clients on the MCP settings overview (#39489)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-25 00:11:46 +00:00
José Braulio González Valido
44471d1c94
fix(ai-builder): Validate only the selected case files in the eval push CLI (no-changelog) (#39248)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-24 20:11:04 +00:00
mfsiega
de238f3dde
feat(core): Run engine 2.0 as a standalone data plane (no-changelog) (#39440)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-24 19:51:43 +00:00
Miguel Ángel Moreno
e838224264
feat(editor): Build the new group shapes on the canvas (no-changelog) (#39319)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-24 19:41:52 +00:00
Miguel Ángel Moreno
3aa9179424
feat(core): Accept the new group shapes when a workflow is saved (no-changelog) (#39236)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-24 19:41:51 +00:00
Miguel Ángel Moreno
0fedb30182
feat(core): Add a feature flag for each relaxed node group rule (no-changelog) (#39171)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-24 19:41:50 +00:00
n8n-cat-bot[bot]
f63195656a
chore: Bug — Sharing drop-down looks like it's broken (#39481)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
Co-authored-by: James Gee <1285296+geemanjs@users.noreply.github.com>
2026-09-24 19:00:57 +00:00
n8n-cat-bot[bot]
8bd4563087
chore: Bug - Misaligned input box in workflow settings (#39478)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
2026-09-24 18:13:47 +00:00
n8n-cat-bot[bot]
f9b2987716
chore: Variable scope shows as global until refresh (#39479)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
2026-09-24 17:42:06 +00:00
Declan Carroll
0347de1f22
ci: Publish and smoke-test pinned toolchain image (no-changelog) (#39538) 2026-09-24 17:51:27 +01:00
Michael Drury
9329d1f752
fix(editor): Keep agent capability chips within card width (#39532) 2026-09-24 16:26:05 +00:00
Veljko Terzic
6c6123fb29
feat(core): Run the Instance AI checkpoint pruning as a durable system task (no-changelog) (#39508) 2026-09-24 15:58:27 +00:00
Dmitrii
d9c9d6e616
fix(core): Stop retrying instance reports the receiver rejects with 400 or 413 (#39502) 2026-09-24 15:23:14 +00:00
Mayank SIngh
a9bc9c57e7
fix(Mistral Cloud Node): Support citation reference chunks (#38952) 2026-09-24 15:13:31 +00:00
Ricardo Espinoza
d4999a2b2f
fix(editor): Prefill the server URL in the Mistral Vibe connector link (#39518) 2026-09-24 15:08:09 +00:00
Michael Drury
8a0d833080
fix(editor): Show Agent Builder activity on prompt submission (#39501) 2026-09-24 14:45:56 +00:00
Kai
2b891f17a5
feat(editor): Create credentials and variables from promotion bindings (#39342) 2026-09-24 14:36:58 +00:00
Robin Braumann
5ca15b3793
feat(core): Connect Preview and integrations to the agent queue (#39468) 2026-09-24 14:24:38 +00:00
Sandra Zollner
3ea51f9944
feat(core): Add project-scoped selective promote endpoint (no-changelog) (#38434)
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Irénée <irenee.ajeneza@n8n.io>
2026-09-24 14:07:29 +00:00
n8n-cat-bot[bot]
7edb12df2d
chore: Share the binary-file node helper between execution contexts (#39473)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
2026-09-24 13:49:29 +00:00
Bernhard Wittmann
edf112cd92
feat: Guide the Microsoft Teams channel setup from n8n (no-changelog) (#38804)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-24 13:48:06 +00:00
José Braulio González Valido
f2a10ff3ed
docs(ai-builder): Describe dispatcher calibration and branch validation in the eval skills (no-changelog) (#39251)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-24 13:38:15 +00:00
n8n-cat-bot[bot]
fbb27a989c
chore: Extract the duplicated workflowPublish policy-enforcement (#37674)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-24 13:16:55 +00:00
n8n-cat-bot[bot]
64d20004c7
test: Add structural main-landmark check with regression tests (#37755)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-24 12:37:51 +00:00
Robin Braumann
6c592bdb33
feat(core): Add the durable agent message queue foundation (#39435) 2026-09-24 12:27:30 +00:00
Jaakko Husso
482ffb5306
fix(core): Run one node of a tool through the node that runs it (no-changelog) (#38759)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-24 12:26:45 +00:00
n8n-cat-bot[bot]
25eeb74fe9
chore: Migrate GET /api/v1/community-packages to PublicApiController (#39352)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
Co-authored-by: Ali Elkhateeb <ali.elkhateeb@n8n.io>
2026-09-24 12:04:37 +00:00
MHMD
5e65197df6
feat(core): Block publishing a workflow the publisher cannot run (no-changelog) (#39425) 2026-09-24 12:01:43 +00:00
Svetoslav Dekov
772d420dc4
fix: Tweaks on codespace opencode setup (no-changelog) (#39488) 2026-09-24 11:55:54 +00:00
Riqwan Thamir
d13b63054b
ci: Create ephemeral sandboxes in Instance AI eval runs (no-changelog) (#39459)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-24 11:53:58 +00:00
Dawid Myslak
7af6741bf2
feat(editor): Suggest Gateway credits after credential errors (#38818)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-24 11:49:34 +00:00
Oğuz Eroğlu
dd4e7f62b2
feat(editor): Route assistant credit CTAs to top-up for UBB accounts (#39416) 2026-09-24 11:48:53 +00:00
Roman Makowiak
099e832028
feat(core): Return the limit on a refused preference write and show the refusal in the chat (no-changelog) (#39324)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-24 11:42:02 +00:00
Benjamin Schroth
3628dc1f04
fix(core): Close Chat integration runtime on shutdown and let followers send (#38958)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-24 11:06:12 +00:00
Robin Braumann
c829374f48
fix(core): Show content filtering errors in agent chat (no-changelog) (#39477) 2026-09-24 11:05:24 +00:00
Pierantonio Cangianiello
e5996eeaff
feat(engine): Support Buffer bodies in Engine 2.0 sendResponse (no-changelog) (#39483)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-24 11:03:12 +00:00
Lorent Lempereur
b94a3e5d16
fix(core): Run the telemetry pulse packet on the durable scheduler (no-changelog) (#39321) 2026-09-24 10:23:46 +00:00
Lorent Lempereur
f49b611321
refactor(core): Move the telemetry buffer flush to a system task (no-changelog) (#39309) 2026-09-24 10:23:45 +00:00
Lorent Lempereur
40b99348e8
refactor(core): Move the telemetry pulse packet to a system task (no-changelog) (#39304) 2026-09-24 10:23:44 +00:00
Rob Hough
9f89ecab7e
feat(editor): Add expanded N8nMarkdownEditor view (#39296)
Co-authored-by: Bob Hough <15361982+heymynameisrob@users.noreply.github.com>
Co-authored-by: linear-code[bot] <222613912+linear-code[bot]@users.noreply.github.com>
2026-09-24 10:18:23 +00:00
Jan
e648610eae
fix(editor): Show a cached role at once and align the personal space block in the instance role editor (#39394)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-24 10:07:01 +00:00
n8n-cat-bot[bot]
6aa0dda7ee
chore: Secret providers - Save alerts in modal are easy to miss (#39258)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
2026-09-24 09:46:28 +00:00
Ricardo Espinoza
b4106dcf63
feat(core): Save, update and undo a preference over MCP (no-changelog) (#39436)
Co-authored-by: Roman Makowiak <roman.makowiak@n8n.io>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-24 09:35:56 +00:00
n8n-cat-bot[bot]
a2370042f4
build: Limit Storybook typecheck to design-system stories (#39474)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
2026-09-24 09:33:58 +00:00
Svetoslav Dekov
dfeb93cdc7
fix: Connect cloud OpenCode sessions to the latest conversation (no-changelog) (#39449) 2026-09-24 09:28:34 +00:00
Anne Aguirre
3b3ac393ba
feat(editor): Add a write lock to the agent builder (#38758)
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-24 09:22:54 +00:00
James Martin
96735b20d9
feat(editor): Displays instance admins (and owner) in all project user lists (#39419)
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-24 09:13:23 +00:00
mfsiega
fdd6f127c2
refactor(core): Prepare engine data plane for standalone execution (no-changelog) (#39439)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Co-authored-by: Tomi Turtiainen <10324676+tomi@users.noreply.github.com>
2026-09-24 09:11:16 +00:00
Michael Kret
4dbe8de82d
feat: Add Gateway credits option to Agent web search (#39169) 2026-09-24 09:06:10 +00:00
Tomi Turtiainen
d1e5637929
refactor(core): Extract webhook invocation (no-changelog) (#39452) 2026-09-24 08:53:14 +00:00
n8n-cat-bot[bot]
b096147894
fix: Align MCP workflow headings and remove outdated notice (#39463)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
2026-09-24 08:49:22 +00:00
Tomi Turtiainen
95c4070090
feat(core): Support the responseNode webhook response mode on engine 2.0 (no-changelog) (#39003) 2026-09-24 08:24:43 +00:00
oleg
67b70ea814
fix(Notion Node): Clarify simplified output for AI workflow generation (#39051) 2026-09-24 08:17:45 +00:00
Yuliia Pominchuk
7d1bae51fc
fix(core): Validate token payload before resolving the user (#39078)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-24 07:54:13 +00:00
Danny Martini
aa89efd1e2
test(engine): Mark the manual wait spec as passing on engine 2.0 (no-changelog) (#39156)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-24 07:48:10 +00:00
Theodore Nastos
ea1e4ef249
feat(core): Propagate project span attributes to node spans (#39420) 2026-09-24 07:42:06 +00:00
Bernhard Wittmann
f9982ef182
feat(core): Add the Microsoft Teams agent channel (no-changelog) (#38686)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-24 07:06:58 +00:00
n8n-assistant[bot]
adf1eb245e
chore: Update e2e impact map (#39442)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-09-24 03:35:53 +00:00
n8n-cat-bot[bot]
bd2efe1c9e
chore: Skip E2E shard matrix generation in merge queue (#39437)
Some checks failed
CI: Master (Build, Test, Lint) / Build & format (push) Has been cancelled
CI: Master (Build, Test, Lint) / Unit tests (24.18.1) (push) Has been cancelled
CI: Master (Build, Test, Lint) / Unit tests (26.5.1) (push) Has been cancelled
CI: Master (Build, Test, Lint) / Lint (push) Has been cancelled
CI: Master (Build, Test, Lint) / Performance (push) Has been cancelled
CI: Master (Build, Test, Lint) / Verify single-instance deps (npm-install) (push) Has been cancelled
CI: Master (Build, Test, Lint) / Notify Slack on failure (push) Has been cancelled
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
2026-09-23 23:44:15 +00:00
Benny Neugebauer
d23f949801
fix: Make n8n-node release work with release-it 21 (#39378) 2026-09-23 22:37:00 +00:00
Dmitrii
f9147709a6
fix(core): Retry instance reports that cross the UTC midnight boundary (#39290) 2026-09-23 20:49:41 +00:00
Samyak Jain
82cd04c9a6
fix(core): Release the expression isolate when the webhook response ends (#39138)
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-23 20:12:46 +00:00
Theodore Nastos
dc98f6483a
fix(core): Apply a node type policy rule to the node's synthetic tool variants (no-changelog) (#39289) 2026-09-23 18:30:50 +00:00
Milorad FIlipović
0efb22a1dd
feat(editor): Support bounded workflow search and recent state (no-changelog) (#39283) 2026-09-23 18:20:56 +00:00
Tomi Turtiainen
972db68d6b
feat(engine): Report execution response failures (#39001) 2026-09-23 16:54:39 +00:00
Rúben Castro
49c61475de
feat(core): Add credential type availability endpoint for a project (no-changelog) (#39317)
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-23 15:52:26 +00:00
Ricardo Espinoza
46be542125
chore(core): Revert deprecate the "Any workflow" caller policy (#39400)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-09-23 15:48:53 +00:00
Anne Aguirre
9ee6a8f88b
feat(ai-builder): Prompt suggestions and intro on empty agent builder aia (#39098) 2026-09-23 15:10:10 +00:00
Thanasis G
4e3d5c10f1
feat(Azure OpenAI Chat Model Node): Add the project input and the deployment list call (#39375)
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-23 14:59:15 +00:00
Danny Martini
691bf44151
feat(engine): Suspend and resume waiting steps (no-changelog) (#37853)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-23 14:59:15 +00:00
Tomi Turtiainen
e0ee51f442
refactor(core): Normalize webhook helper errors (no-changelog) (#39374) 2026-09-23 14:39:10 +00:00
Rúben Castro
9dfcef628b
feat(API): Add credential type policy public API endpoints (no-changelog) (#39168)
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-23 14:30:41 +00:00
Tuukka Kantola
5cc6640ae8
fix(editor): Shorten the Assistant name in navigation and settings (#39288) 2026-09-23 14:12:09 +00:00
Emilia
73bdae55c6
fix(core): Return test webhook response when the call lands on another main (#39382)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-23 13:59:19 +00:00
n8n-cat-bot[bot]
853f99919d
chore: Schema-First migration — security-policy (#39165)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
Co-authored-by: Sam Wooler <swooler592@gmail.com>
2026-09-23 13:34:23 +00:00
Robin Braumann
487f4af04c
fix(core): Keep agent chat session pointer across restarts (no-changelog) (#39365)
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-23 13:33:58 +00:00
Declan Carroll
18b174f9ab
ci: Warm format cache on master with Blacksmith (no-changelog) (#39413) 2026-09-23 16:05:22 +01:00
Anne Aguirre
85963cfa5f
fix(editor): Keep long titles from overlapping the Assistant chat history button (#39402) 2026-09-23 13:29:00 +00:00
mfsiega
8c720987cb
fix(core): Persist workflow publication retry state (no-changelog) (#39266) 2026-09-23 13:13:46 +00:00
Uddish Verma
84886e33b9
refactor(API): Migrate GET /workflows/{workflowId}/test-runs/{runId} and its test-cases route to the decorator pattern (no-changelog) (#39260)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-23 13:08:57 +00:00
Jon
2e9aacfd9e
feat(Zendesk Node): Add hidden fields for global auth support (no-changelog) (#39272)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-23 13:07:47 +00:00
n8n-cat-bot[bot]
098d1d365e
chore: Migrate POST /api/v1/folders to @PublicApiController + Zod (#38619)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: Ali Elkhateeb <ali.elkhateeb@n8n.io>
2026-09-23 13:06:46 +00:00
Roman Makowiak
a423a6b203
feat(editor): Show which AI preferences a turn applied (no-changelog) (#39397)
Co-authored-by: Ricardo Espinoza <ricardo@n8n.io>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-23 13:06:09 +00:00
Kai
af838bc8ea
feat(editor): Add destination binding dialog (#39214) 2026-09-23 13:04:53 +00:00
Ricardo Espinoza
183d769841
feat(editor): Add Mistral Vibe to the MCP connect-client picker (#39183) 2026-09-23 13:02:25 +00:00
MHMD
2a710313ff
feat(core): Let a personal credential be usable in any project its owner works in (no-changelog) (#39316) 2026-09-23 12:57:49 +00:00
Declan Carroll
507a1eb419
ci: Run format check alongside build (no-changelog) (#39387) 2026-09-23 12:51:51 +00:00
Roman Makowiak
1f873774ab
feat(core): Save a preference from the n8n Assistant chat (no-changelog) (#39237)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Co-authored-by: Ricardo Espinoza <ricardo@n8n.io>
2026-09-23 12:44:08 +00:00
Csaba Tuncsik
eb5eef341f
feat(Microsoft Teams Node): Add online meeting attendees on Create, Create or Get and Update (#38850) 2026-09-23 12:40:29 +00:00
Matsu
3b4da4f39b
fix: Local pnpm nathan command usage (no-changelog) (#39385) 2026-09-23 12:25:21 +00:00
Michael Drury
042d16718e
feat(editor): Standardize Agent Builder modals (#39220) 2026-09-23 12:25:19 +00:00
Robin Braumann
80e29bf57c
refactor(core): Simplify agent service functions (no-changelog) (#39331) 2026-09-23 11:52:01 +00:00
Lorent Lempereur
da49251bf3
feat(core): Start the system tasks on the worker and webhook instances (no-changelog) (#39280) 2026-09-23 11:50:47 +00:00
Lorent Lempereur
25db401c9f
feat(core): Run instance-scoped system tasks in every eligible instance (no-changelog) (#39279) 2026-09-23 11:50:46 +00:00
Lorent Lempereur
c88d7a3820
refactor(core): Move the system task placement onto its own type (no-changelog) (#39278) 2026-09-23 11:50:45 +00:00
Lorent Lempereur
2d7816a227
refactor(core): Name the system task metrics mode after what drives the run (no-changelog) (#39277) 2026-09-23 11:50:44 +00:00
Lorent Lempereur
1e0519ad53
feat(core): Collect system task metrics on every instance type (no-changelog) (#39276) 2026-09-23 11:50:43 +00:00
Lorent Lempereur
9a39b0ddba
refactor(core): Pick the system task run trace by the durable mode (no-changelog) (#39275) 2026-09-23 11:50:42 +00:00
oleg
1e1f3cb711
fix(editor): Improve n8n Assistant async credential setup (no-changelog) (#39229) 2026-09-23 11:37:03 +00:00
Danny Martini
dbc822ba8b
fix: Exempt ADR Links fields from the paragraph length rule (no-changelog) (#39384)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-23 11:33:35 +00:00
Robin Braumann
98f012e268
fix: Recover running preview chat after reload (#39311) 2026-09-23 11:07:22 +00:00
Jaakko Husso
fb357977b5
fix(core): Record live n8n Assistant test runs as verification evidence (#39361)
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-23 11:03:38 +00:00
Rob Hough
afa5fc3b1a
fix(editor): Move agent AI entry point below header (#39318) 2026-09-23 10:49:24 +00:00
Rob Hough
1c82574f86
fix(editor): Make agent configuration header more compact (#39172) 2026-09-23 10:45:00 +00:00
Konstantin Tieber
a182724136
feat(core): Authenticate instance reports with the license certificate (#39217)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
2026-09-23 10:42:13 +00:00
Csaba Tuncsik
a7d5ee01cd
refactor(Microsoft Teams Node): Unify the v2 resource-locator definitions behind a builder (no-changelog) (#39088) 2026-09-23 10:38:33 +00:00
Csaba Tuncsik
4eddcc43d4
refactor(Microsoft Teams Node): Centralize the Service Principal guard and share the chat member binding (no-changelog) (#38928) 2026-09-23 10:38:21 +00:00
oleg
0254343de3
feat(core): Track n8n Assistant workflow setup (no-changelog) (#39190) 2026-09-23 10:37:44 +00:00
n8n-cat-bot[bot]
34bec9c119
chore: Remove stale response mode complexity suppression (#39371)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
2026-09-23 10:33:49 +00:00
Danny Martini
f3fea3b8b8
refactor(core): Let the engine decide how a wait is implemented (no-changelog) (#39364)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-23 10:18:48 +00:00
Savelii
0827eb8eda
feat(editor): Make SSO the primary option on the sign-in page when SSO is enabled (#39268)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-23 10:16:51 +00:00
Matsu
27627bc793
chore: Migrate phase-one backend packages to Oxlint (no-changelog) (#39360) 2026-09-23 10:00:59 +00:00
Emilia
1c48187ca1
test(editor): Repeat the inline expression editor clear until it empties (no-changelog) (#39363)
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-23 09:54:20 +00:00
Matsu
28d1918c26
ci: Update cla-check version and manually input pr number (#39357) 2026-09-23 09:52:15 +00:00
João Almeida
24d54e4db5
test: De-flake NDV run selector linking across input and output (#39087)
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-23 09:47:46 +00:00
João Almeida
346989eda1
test(editor): Stabilize workflow history open and close in canvas page object (no-changelog) (#39086)
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-23 09:47:41 +00:00
Arvin A
57d654f972
feat: Add credential descriptions behind an instance flag (#39177) 2026-09-23 09:31:29 +00:00
ocdejong
dbefd920b5
fix(core): Keep OpenTelemetry export working after a restart when Sentry is enabled (#38480)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-23 09:05:06 +00:00
Lorent Lempereur
60bfb02db9
feat(core): Add a concurrency limit option to durable scheduled jobs (no-changelog) (#39166) 2026-09-23 09:04:27 +00:00
Tomi Turtiainen
dc3283da19
feat(core): Answer lastNode webhooks from execution responses (#38959) 2026-09-23 08:59:11 +00:00
Nour Alhadi Mahmoud
bc779f2216
feat(editor): Add a last refreshed timestamp to the promotion changes view (no-changelog) (#39320)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-23 08:52:42 +00:00
Svetoslav Dekov
ed4e1ce391
fix: Add terminal fallback for cloud sessions (no-changelog) (#38285)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-23 08:51:19 +00:00
Guillaume Jacquart
46b77abeb2
feat(core): Enforce credential type availability policies (no-changelog) (#39250)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-23 08:41:48 +00:00
Tomi Turtiainen
aa4bd2cc11
ci: Add ADR template (#39358)
Some checks failed
CI: Master (Build, Test, Lint) / Build for Github Cache (push) Waiting to run
CI: Master (Build, Test, Lint) / Unit tests (24.18.1) (push) Waiting to run
CI: Master (Build, Test, Lint) / Unit tests (26.5.1) (push) Waiting to run
CI: Master (Build, Test, Lint) / Lint (push) Waiting to run
CI: Master (Build, Test, Lint) / Performance (push) Waiting to run
CI: Master (Build, Test, Lint) / Verify single-instance deps (npm-install) (push) Waiting to run
CI: Master (Build, Test, Lint) / Notify Slack on failure (push) Blocked by required conditions
Build: Benchmark Image / build (push) Has been cancelled
2026-09-23 08:30:55 +00:00
Rob Hough
db1e08fa66
refactor(editor): Update N8nBadge component (#37010) 2026-09-23 08:17:02 +00:00
Declan Carroll
50f89666fc
ci: Reuse build outputs across test shards (no-changelog) (#39355) 2026-09-23 07:45:43 +00:00
James Martin
72a14b078c
feat(core): Allows custom roles to have view/use/manage access to instance credentials (#39306)
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-23 07:36:06 +00:00
Tomi Turtiainen
d01ec72338
ci: Enforce ADR conventions (#39286) 2026-09-23 06:50:50 +00:00
Tomi Turtiainen
3f5aab992c
fix(engine): Preserve host execution mode (#39253) 2026-09-23 06:28:26 +00:00
Svetoslav Dekov
522d8795b0
chore: Connect local OpenCode clients to Codespaces (#38374)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-23 05:51:41 +00:00
Svetoslav Dekov
4dc6c80cac
build: Use pnpm's default package import method (no-changelog) (#39019)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-23 05:49:33 +00:00
n8n-assistant[bot]
67fadbc64b
chore: Update e2e impact map (#39341)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-09-23 03:25:19 +00:00
n8n-cat-bot[bot]
fe0fad5743
fix: Preserve tool arguments in reconstructed history (#39153)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
2026-09-22 19:15:02 +00:00
José Braulio González Valido
b9b527af9d
ci: Run the Instance AI eval PR gate on the pr tier only (no-changelog) (#39315)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-22 18:42:31 +00:00
Michael Drury
94b9627f75
feat(core): Enable Agents by default (#39312) 2026-09-22 18:33:27 +00:00
Arvin A
724f1f7f90
feat(core): Show the injected instance context in the trace (no-changelog) (#38240) 2026-09-22 17:15:29 +00:00
Kai
8012f017bc
feat(API): Create projects with source IDs and package metadata (#39218) 2026-09-22 16:41:52 +00:00
ocdejong
5131af5ab7
feat(core): Report crashed executions to OTel through an execution-crashed event (#38853)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-22 15:36:32 +00:00
Alex Grozav
25c730223f
feat(editor): Add N8nTimeAgo and N8nSelectedItemsInfo to @n8n/design-system (no-changelog) (#39230)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-22 15:31:49 +00:00
Robin Braumann
5ed678ae54
fix(core): Make agent session admission atomic (no-changelog) (#39307) 2026-09-22 15:22:16 +00:00
Uddish Verma
86a8ffe96a
refactor(API): Migrate GET and POST /workflows/{workflowId}/test-runs to the decorator pattern (no-changelog) (#39163)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-22 15:14:03 +00:00
Pierantonio Cangianiello
4f680fdfcb
refactor(core): Extract the webhook Buffer body codec into n8n-core (no-changelog) (#39301)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-22 15:09:39 +00:00
Anne Aguirre
d9f65684f8
feat(ai-builder): Add approval settings for agent channel actions (#39059) 2026-09-22 15:06:36 +00:00
Stephen Wright
27bb095b45
feat(Databricks Node): Show the Databricks Trigger in the node picker (#39176) 2026-09-22 15:02:48 +00:00
Jaakko Husso
dec7c712c7
fix(core): Gate the AI Assistant project list on the team-projects license (#39235)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-22 14:51:46 +00:00
Sebastien Powell
22d18a7bc9
feat(editor): Implement Reka UI based context menu for design system (#37925) 2026-09-22 14:50:28 +00:00
Guillaume Jacquart
36688eface
feat(core): Seal credential type writes behind the credentials repository (no-changelog) (#39106) 2026-09-22 14:42:34 +00:00
Sam Wooler
428bab3201
feat(API): Migrate users write endpoints to the decorator pattern (#39120)
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-22 14:38:03 +00:00
RomanDavydchuk
3c48cefad4
fix(Wise Node): Use current sandbox API (#39255) 2026-09-22 14:35:53 +00:00
Benjamin Schroth
60d0598a69
fix(AI Agent Node): Let an AI Agent Tool use its own tools under a pre-v3 parent agent (#39160) 2026-09-22 14:29:41 +00:00
Michael Drury
da06957d53
fix: Increase default Agent iteration limits (no-changelog) (#39299) 2026-09-22 14:14:10 +00:00
Thanasis G
230631c466
feat(Databricks Node): Add search mode and metadata filter to Vector Store Get Many (no-changelog) (#39287)
Co-authored-by: Yen Su <yen.su@n8n.io>
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-22 14:11:09 +00:00
José Braulio González Valido
8325f12c33
fix(ai-builder): Read the eval credential allowlist on every list call (no-changelog) (#39249)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-22 14:10:36 +00:00
n8n-cat-bot[bot]
f86d227dc5
chore: Migrate DELETE /api/v1/projects/:projectId/folders/:folderId to @PublicApiController + Zod (#38617)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: Ali Elkhateeb <ali.elkhateeb@n8n.io>
2026-09-22 14:07:41 +00:00
n8n-cat-bot[bot]
3df5aa3f99
chore: Increase frontend unit test matrix to four shards (#39302)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
2026-09-22 14:01:12 +00:00
Rúben Castro
fb0c332006
fix(core): Summarize saved credential type policies against known credential types (no-changelog) (#39191)
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-22 13:58:58 +00:00
Veljko Terzic
f89687fd87
feat(core): Run the insights pruning as a durable system task (no-changelog) (#39173)
Co-authored-by: Lorent Lempereur <lorent.lempereur@n8n.io>
2026-09-22 13:51:40 +00:00
Rob Hough
b6b4e54251
feat(editor): Use test label and icon for agent preview (#38939) 2026-09-22 13:51:01 +00:00
Yen Su
b16b705f5d
feat(Databricks Node): Add the hidden Databricks Vector Store sub-node (no-changelog) (#38990) 2026-09-22 13:24:40 +00:00
n8n-cat-bot[bot]
cae5062e3a
refactor(API): Migrate GET /discover to the decorator pattern (no-changelog) (#39162)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
Co-authored-by: Uddish Verma <uddish.verma@n8n.io>
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-22 13:23:51 +00:00
MHMD
fc68bb6a8f
feat(core): Add feature flag for granular credential sharing work (#39293) 2026-09-22 13:22:12 +00:00
Veljko Terzic
4d8c29adbe
feat(core): Run the agent checkpoint pruning as a durable system task (no-changelog) (#39269) 2026-09-22 13:10:30 +00:00
Riqwan Thamir
40f6c9d253
fix(core): Keep skill activation from invalidating the prompt cache (no-changelog) (#39231)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-22 13:04:44 +00:00
Sam Wooler
6433934db7
feat(API): Migrate GET /users endpoints to the decorator pattern (#38599)
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-22 12:49:05 +00:00
Matsu
1bb166dbaa
chore: Enforce extraneous dependencies in Oxlint (no-changelog) (#39281) 2026-09-22 12:48:49 +00:00
Stephen Wright
dfe014a2ba
feat(Microsoft Teams Node): Send activity feed notifications to a user (#38954)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-22 12:29:49 +00:00
Ricardo Espinoza
b42f1dc56f
feat(core): Apply AI preferences on every Instance AI turn (no-changelog) (#39004)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Roman Makowiak <roman.makowiak@n8n.io>
2026-09-22 12:28:35 +00:00
Nour Alhadi Mahmoud
02cd44e3aa
feat(editor): Add the incoming changes banner and apply entry point on the destination (no-changelog) (#39150)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-22 12:11:14 +00:00
Robin Braumann
dda9a56cb7
fix(core): Resume only private sessions in Preview (#39151) 2026-09-22 11:46:51 +00:00
Tomi Turtiainen
518a24dd64
refactor(engine): Split execution response endpoints (#39261) 2026-09-22 11:44:49 +00:00
Anne Aguirre
43d7cc755e
fix(core): Use Sessions tab terminology in n8n Assistant (#39265) 2026-09-22 11:41:11 +00:00
Milorad FIlipović
1a370b965c
feat(editor): Support external dropdown menu input (no-changelog) (#39188) 2026-09-22 11:24:10 +00:00
José Braulio González Valido
5b149495c5
feat(ai-builder): Grade token consumption and memory compaction in Instance AI evals (no-changelog) (#38974)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-22 11:12:10 +00:00
Alex Grozav
340d36c860
refactor(editor): Relocate MCP-shared constants and test helper (no-changelog) (#39196)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-22 11:09:34 +00:00
Stephen Wright
870880963d
feat(Databricks Node): Add the Pipeline resource to the hidden Databricks Trigger (no-changelog) (#38827) 2026-09-22 10:39:11 +00:00
Stephen Wright
1308aae1b7
feat(Databricks Node): Poll pipeline update events for the Databricks Trigger (no-changelog) (#38842) 2026-09-22 10:39:11 +00:00
Stephen Wright
2945c2d8d0
feat(Databricks Node): Add the pipeline picker for the Databricks Trigger (no-changelog) (#38841) 2026-09-22 10:39:10 +00:00
Stephen Wright
4b14fe447b
refactor(Databricks Node): Move the helpers the trigger resources share into trigger/shared.ts (no-changelog) (#38840) 2026-09-22 10:39:09 +00:00
Konstantin Tieber
7f3be81d6c
chore(core): Add integration tests for instance-reporting retries (#39158) 2026-09-22 10:06:26 +00:00
ocdejong
d42b7621de
feat(editor): Show a restricted node panel in the NDV with contact-admin and replace paths (#38869)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-22 09:52:51 +00:00
Matsu
196ed7f450
refactor(core): Enforce persistence boundaries in code (no-changelog) (#39170) 2026-09-22 13:14:00 +03:00
Robin Braumann
9142b47bdf
fix(editor): Keep agent builder chat messages within the panel (#39233) 2026-09-22 09:47:16 +00:00
Rúben Castro
cff3a1c9db
feat(core): Add credential type policy instance and project endpoints (no-changelog) (#39111)
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-22 09:33:06 +00:00
Robin Braumann
5ba266db25
feat(core): Scope agent preview sessions to their owner (#39100) 2026-09-22 09:22:44 +00:00
Savelii
c114a18c87
fix(editor): Remove half-checked permissions from system instance roles and restore the Member preset (#38652)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-22 09:16:23 +00:00
Mike Repeć
3f76dccc32
fix(core): Restore pubsub subscriptions after Redis reconnect and detect half-open subscriber connections (#39174) 2026-09-22 08:28:01 +00:00
Veljko Terzic
48cea71beb
feat(core): Run the insights compaction as a durable system task (no-changelog) (#39096)
Some checks failed
Build: Benchmark Image / build (push) Waiting to run
CI: Master (Build, Test, Lint) / Build for Github Cache (push) Waiting to run
CI: Master (Build, Test, Lint) / Unit tests (24.18.1) (push) Waiting to run
CI: Master (Build, Test, Lint) / Unit tests (26.5.1) (push) Waiting to run
CI: Master (Build, Test, Lint) / Lint (push) Waiting to run
CI: Master (Build, Test, Lint) / Performance (push) Waiting to run
CI: Master (Build, Test, Lint) / Verify single-instance deps (npm-install) (push) Waiting to run
CI: Master (Build, Test, Lint) / Notify Slack on failure (push) Blocked by required conditions
Util: Publish API Schema / Cloudflare Pages (push) Has been cancelled
2026-09-22 08:05:17 +00:00
Robin Braumann
6a678f02a0
fix(core): Remove checkpoints when deleting agent sessions (#39175) 2026-09-22 07:47:55 +00:00
n8n-cat-bot[bot]
f840c58934
chore: Migrate PATCH /api/v1/projects/:projectId/folders/:folderId to @PublicApiController + Zod (#38618)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: Ali Elkhateeb <ali.elkhateeb@n8n.io>
2026-09-22 07:45:49 +00:00
Mayank SIngh
07a741b595
fix: Preserve tables in AI workflow builder web fetch (#38879)
Co-authored-by: oleg <me@olegivaniv.com>
Co-authored-by: Matsu <matias.huhta@n8n.io>
2026-09-22 07:33:07 +00:00
Stephen Wright
620b549486
feat(Databricks Node): Add the Job resource and run polling to the hidden Databricks Trigger (no-changelog) (#38794) 2026-09-22 07:28:39 +00:00
RomanDavydchuk
d1cd34b375
fix(LinkedIn Node): Validate post text (#39201) 2026-09-22 07:18:49 +00:00
n8n-release-tag-merge[bot]
61ab76b6f5 Merge tag 'n8n@2.41.0' 2026-09-22 07:32:43 +00:00
n8n-assistant[bot]
669a65ade5
🚀 Release 2.41.0 (#39228)
Co-authored-by: n8n-release-helper[bot] <265227495+n8n-release-helper[bot]@users.noreply.github.com>
2026-09-22 07:18:44 +00:00
RomanDavydchuk
ecfec194b4
fix(core): Remove feature flag for MCP Registry in AI Assistant (#39018)
Co-authored-by: Elias Meire <elias@meire.dev>
2026-09-22 07:18:27 +00:00
Yen Su
bba3475deb
feat(Databricks Node): Add the DatabricksVectorStore LangChain class (no-changelog) (#39036) 2026-09-22 06:55:51 +00:00
Andreas Fitzek
32aa4629de
refactor: Add DB error predicates and drop TypeORM imports from execution and credential services (#38940) 2026-09-22 05:30:05 +00:00
Matsu
872b145ea8
refactor(core): Internalize cipher primitives (no-changelog) (#39148) 2026-09-22 08:08:02 +03:00
n8n-assistant[bot]
8298d3c195
chore: Update e2e impact map (#39225)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-09-22 03:24:50 +00:00
n8n-cat-bot[bot]
af83516f0b
chore: Deduplicate chained Playwright scope-lockdown findings (#39209)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
Co-authored-by: Declan Carroll <declan@n8n.io>
2026-09-21 23:44:03 +00:00
Ricardo Espinoza
4e3a7720cf
feat(core): Let MCP discover and install verified community nodes (no-changelog) (#37374)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-09-21 21:44:05 +00:00
n8n-cat-bot[bot]
3be2a04383
chore: Wire Currents record key in PR caller to activate shard upload skip (#39213)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
2026-09-21 20:26:37 +00:00
Guillaume Jacquart
b35d946929
feat(core): Add the credentialSave policy enforcement point (no-changelog) (#39099)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-21 19:44:58 +00:00
Rob Hough
263eda0106
fix(editor): Improve Assistant chat controls and history menu layout (#39212) 2026-09-21 18:29:32 +00:00
Svetoslav Dekov
3b248ebcca
feat(editor): Show a Preview label on the Context settings item (no-changelog) (#39103)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Co-authored-by: Nikhil Kuriakose <nikhilkuria@gmail.com>
Co-authored-by: Nikhil Kuriakose <nikhil.kuriakose@n8n.io>
2026-09-21 18:03:44 +00:00
José Braulio González Valido
2d51078ce1
fix(core): Let a mocked trigger emit zero items in Instance AI evals (no-changelog) (#39113)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-21 17:35:41 +00:00
n8n-cat-bot[bot]
52e6546c69
chore: Add currents-record-key input and shard upload conditional to reusable E2E workflow (#38566)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-21 17:24:08 +00:00
Michael Drury
add77db7a0
feat(editor): Explain agent task schedules (#39117) 2026-09-21 17:16:58 +00:00
Konstantin Tieber
4faeb001ba
feat(core): Support disabling certain permission scopes in personal space for canvas only (no-changelog) (#38858)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-21 16:07:42 +00:00
Rúben Castro
c4527242c9
refactor(core): Rename policy DTO folder off node-type-policies (no-changelog) (#39192)
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-21 15:51:10 +00:00
n8n-cat-bot[bot]
eacbb46994
chore: Make Playwright janitor ignore Playwright route forwarding (#39180)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
2026-09-21 15:30:04 +00:00
Dimitri Lavrenük
048f0a7b71
fix(core): Stop pages hanging while the Browser Use extension is attached (#39159)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-21 15:28:02 +00:00
Alex Grozav
6a36102939
refactor(editor): Add a capability registry to the frontend module SDK (no-changelog) (#39161)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
2026-09-21 14:46:07 +00:00
Tomi Turtiainen
6c6ef51066
docs(engine): Refine execution response comments (#39167)
Co-authored-by: mfsiega <93014743+mfsiega@users.noreply.github.com>
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
2026-09-21 14:46:00 +00:00
oleg
3f7c984b49
fix(core): Preserve unchanged nodes during n8n Assistant edits (#38967) 2026-09-21 14:26:02 +00:00
Veljko Terzic
40ec259ccd
feat(core): Lock insights compaction batches across instances (no-changelog) (#39186) 2026-09-21 14:16:38 +00:00
Mayank SIngh
c6e3db8609
fix(core): Raise MiniMax output token limit (no-changelog) (#39044) 2026-09-21 14:16:10 +00:00
Arvin A
4198ba9915
feat(core): Gate the activity record on a rollout flag (no-changelog) (#38822) 2026-09-21 14:15:43 +00:00
Arvin A
bd454d403d
fix(core): Correct the instance-context read surface (no-changelog) (#38815) 2026-09-21 14:15:43 +00:00
Robin Braumann
495d1a9f2b
fix(core): Handle empty Observer results safely (no-changelog) (#39049) 2026-09-21 14:15:38 +00:00
Robin Braumann
40ed55b37f
fix(core): Shorten checkpoint migration index name (no-changelog) (#39184) 2026-09-21 13:51:14 +00:00
mfsiega
56334b72ba
feat(core): Add a suggested migration for Execute Sub-workflow each mode (no-changelog) (#38102)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-21 13:29:28 +00:00
Savelii
f0325776c1
feat(core): Add view and delete permissions for executions to project roles (#38698)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-21 13:21:59 +00:00
Dmitrii
6a9bbd93de
fix(editor): Keep NDV input and output panel content visible at narrow widths (#38615) 2026-09-21 13:06:16 +00:00
Thanasis G
48b1274616
feat(Databricks Node): Add hidden Databricks Embeddings sub-node (#38989)
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-21 13:04:57 +00:00
Emilia
f4f3011630
fix(core): Guard worker execution save against overwriting a canceled status (#38957)
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-21 12:51:16 +00:00
José Braulio González Valido
ecda0d4680
fix(ai-builder): Report un-intercepted vendor sub-nodes and stop blaming the mock layer for a root's own crash (no-changelog) (#38155)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-21 12:18:30 +00:00
Eugene
9043a74e2d
fix(core): Remove rejected agent attachments from history (no-changelog) (#39094) 2026-09-21 12:17:44 +00:00
Rúben Castro
e76cf87d44
feat(core): Add the credential-types policy kind and fix package rule matching (no-changelog) (#38854)
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-21 11:57:02 +00:00
Theodore Nastos
546aa598a0
feat(editor): Show restricted node types in the nodes panel and search (no-changelog) (#38865) 2026-09-21 11:35:12 +00:00
MHMD
c3432699aa
test: Wait for the move-resource search response instead of a fixed delay (#39155) 2026-09-21 11:00:18 +00:00
Robin Braumann
82b40c3218
refactor(core): Centralize agent conversation state checks (no-changelog) (#39038) 2026-09-21 10:55:17 +00:00
Tomi Turtiainen
c37c4d7b3f
feat(engine): Publish execution response events on the data plane (no-changelog) (#39080) 2026-09-21 10:44:58 +00:00
Ricardo Espinoza
7d8d58f39a
fix(core): Reserve canvas space for collapsed node groups during workflow layout (#38149) 2026-09-21 09:58:03 +00:00
Arvin A
6f74038eea
feat(core): Return credential descriptions in assistant and MCP tools (#38984) 2026-09-21 09:53:17 +00:00
Michael Drury
3f06dd868b
fix(core): Expose Telegram photo context to agents (#38979) 2026-09-21 09:48:48 +00:00
Rob Hough
d7d8c03193
feat(editor): Refactor ResizeWrapper and add to Agents Preview Dock (#38250) 2026-09-21 09:45:29 +00:00
Arvin A
d9dc457d40
feat(core): Track credential description usage (no-changelog) (#38986) 2026-09-21 09:09:43 +00:00
n8n-cat-bot[bot]
989b67ee95
test: Wait for MCP workflow publication (no-changelog) (#39154)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
2026-09-21 09:07:30 +00:00
Sam Wooler
d624ac9a4b
feat(API): Add a user quota gate for Public API controller routes (no-changelog) (#38587)
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-21 08:58:36 +00:00
Michael Drury
d605e8ca80
test(core): Support Agent attachments in Instance AI evaluations (#38874) 2026-09-21 08:57:39 +00:00
Guillaume Jacquart
fc0c3ca23a
perf(core): Coalesce policy scope reads and demote the cache TTL (no-changelog) (#39085) 2026-09-21 08:38:29 +00:00
Robin Braumann
09cf3eb81d
refactor(core): Require agent execution recording (no-changelog) (#39075) 2026-09-21 08:31:57 +00:00
Savelii
51a05126fd
feat(editor): Show the personal space permissions in the instance role editor (#39108)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-21 08:13:44 +00:00
oleg
2ac57b13a8
feat(core): Add model catalog discovery and refresh AI model guidance (#38792)
Some checks are pending
CI: Master (Build, Test, Lint) / Build for Github Cache (push) Waiting to run
CI: Master (Build, Test, Lint) / Unit tests (24.18.1) (push) Waiting to run
CI: Master (Build, Test, Lint) / Unit tests (26.5.1) (push) Waiting to run
CI: Master (Build, Test, Lint) / Lint (push) Waiting to run
CI: Master (Build, Test, Lint) / Performance (push) Waiting to run
CI: Master (Build, Test, Lint) / Verify single-instance deps (npm-install) (push) Waiting to run
CI: Master (Build, Test, Lint) / Notify Slack on failure (push) Blocked by required conditions
2026-09-21 07:47:25 +00:00
Michael Kret
67c304b387
fix(editor): Apply Gateway credits eligibility to Agent tool resource and operation selectors (#38828) 2026-09-21 07:26:42 +00:00
Yen Su
fb66e3f37f
feat(core): Add a searchByText opt-in to createVectorStoreNode (no-changelog) (#39027) 2026-09-21 07:25:39 +00:00
Robin Braumann
a378ba34cd
fix(core): Follow user language changes in Assistant and Builder (#39119) 2026-09-21 07:14:15 +00:00
n8n-assistant[bot]
71329a839f
chore: Update e2e impact map (#39141)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-09-21 03:30:06 +00:00
n8n-assistant[bot]
5729f343e7
chore: Update node popularity data (#39139)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-09-21 00:44:52 +00:00
kaluli123123
0b2ff221c1
fix(core): Keep collecting insights after one un-attributable event (#38518)
Some checks failed
CI: Master (Build, Test, Lint) / Build for Github Cache (push) Waiting to run
CI: Master (Build, Test, Lint) / Unit tests (24.18.1) (push) Waiting to run
CI: Master (Build, Test, Lint) / Unit tests (26.5.1) (push) Waiting to run
CI: Master (Build, Test, Lint) / Lint (push) Waiting to run
CI: Master (Build, Test, Lint) / Performance (push) Waiting to run
CI: Master (Build, Test, Lint) / Verify single-instance deps (npm-install) (push) Waiting to run
CI: Master (Build, Test, Lint) / Notify Slack on failure (push) Blocked by required conditions
Util: Update Node Popularity / update-popularity (push) Has been cancelled
Util: Update Credential Setupability / update-setupability (push) Has been cancelled
Util: Update Node Popularity / approve-and-automerge (push) Has been cancelled
Util: Update Credential Setupability / approve-and-automerge (push) Has been cancelled
Co-authored-by: Kai <kai.hartlage@n8n.io>
2026-09-20 10:52:38 +00:00
n8n-assistant[bot]
02dc131bc9
chore: Update e2e impact map (#39128)
Some checks are pending
CI: Master (Build, Test, Lint) / Build for Github Cache (push) Waiting to run
CI: Master (Build, Test, Lint) / Unit tests (24.18.1) (push) Waiting to run
CI: Master (Build, Test, Lint) / Unit tests (26.5.1) (push) Waiting to run
CI: Master (Build, Test, Lint) / Lint (push) Waiting to run
CI: Master (Build, Test, Lint) / Performance (push) Waiting to run
CI: Master (Build, Test, Lint) / Verify single-instance deps (npm-install) (push) Waiting to run
CI: Master (Build, Test, Lint) / Notify Slack on failure (push) Blocked by required conditions
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-09-20 03:13:25 +00:00
n8n-assistant[bot]
69b0c527fa
chore: Update e2e impact map (#39121)
Some checks are pending
CI: Master (Build, Test, Lint) / Build for Github Cache (push) Waiting to run
CI: Master (Build, Test, Lint) / Unit tests (24.18.1) (push) Waiting to run
CI: Master (Build, Test, Lint) / Unit tests (26.5.1) (push) Waiting to run
CI: Master (Build, Test, Lint) / Lint (push) Waiting to run
CI: Master (Build, Test, Lint) / Performance (push) Waiting to run
CI: Master (Build, Test, Lint) / Verify single-instance deps (npm-install) (push) Waiting to run
CI: Master (Build, Test, Lint) / Notify Slack on failure (push) Blocked by required conditions
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-09-19 03:22:02 +00:00
José Braulio González Valido
fd2f25a2cb
fix(core): Give id-shaped builder placeholders a numeric value in Instance AI evals (no-changelog) (#39082)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-19 01:51:34 +00:00
Garrit Franke
c16bf24b04
feat(core): Send maxN8nNodesApiVersion in community-nodes catalog requests (#38968)
Some checks failed
CI: Master (Build, Test, Lint) / Build for Github Cache (push) Waiting to run
CI: Master (Build, Test, Lint) / Unit tests (24.18.1) (push) Waiting to run
CI: Master (Build, Test, Lint) / Unit tests (26.5.1) (push) Waiting to run
CI: Master (Build, Test, Lint) / Lint (push) Waiting to run
CI: Master (Build, Test, Lint) / Performance (push) Waiting to run
CI: Master (Build, Test, Lint) / Verify single-instance deps (npm-install) (push) Waiting to run
CI: Master (Build, Test, Lint) / Notify Slack on failure (push) Blocked by required conditions
Util: Publish API Schema / Cloudflare Pages (push) Has been cancelled
2026-09-18 22:04:05 +00:00
Milorad FIlipović
898480070f
fix(core): Isolate instance settings writes (no-changelog) (#39118)
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
2026-09-18 21:19:44 +00:00
Michael Drury
30b3226101
fix(editor): Keep agent icon background visible (#39112) 2026-09-18 16:13:10 +00:00
Michael Drury
e94dff42e1
fix(editor): Refine agent instructions and trace view (#39104) 2026-09-18 15:27:58 +00:00
n8n-cat-bot[bot]
7c6425e74e
refactor(API): Migrate GET /insights/summary to @PublicApiController (#38600)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: Uddish Verma <uddish.verma@n8n.io>
2026-09-18 15:05:54 +00:00
Robin Braumann
07813780b9
fix(core): Keep Assistant and Agent Builder replies in the user's language (#39101) 2026-09-18 14:56:56 +00:00
Nour Alhadi Mahmoud
f3c5a5751e
fix(core): Keep workflow version ids out of the package content file and the promotion diff (#39091)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
2026-09-18 14:49:17 +00:00
Dimitri Lavrenük
2f65a6b066
feat: Add admin permission for node execution in n8n assistant (#39102) 2026-09-18 14:44:51 +00:00
ocdejong
f03ffd7fed
feat(editor): Render a restricted node type on the canvas with a replace path (#38859)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-18 14:20:23 +00:00
Savelii
343ce793eb
refactor(core): Align the OAuth JWE private-key storage format with data-encryption keys (no-changelog) (#38920)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-09-18 14:18:52 +00:00
Anne Aguirre
233afff41c
fix(ai-builder): Reactive subject name on chip (#39097) 2026-09-18 14:14:58 +00:00
Michael Drury
7683e1c7f6
feat(editor): Move agent skills to a separate card (#38993) 2026-09-18 14:10:25 +00:00
Yen Su
247e44194b
fix(core): Test the posted OAuth2 credential values instead of the stored credential (#38877) 2026-09-18 13:40:01 +00:00
Charlie Kolb
21008ce758
build(core): Add the @n8n/backend-services package (no-changelog) (#39034)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Co-authored-by: Lorent Lempereur <lorent.lempereur@n8n.io>
2026-09-18 13:30:13 +00:00
Rob Hough
b68164528e
fix(editor): Prevent dropdown menu overlap (#38835) 2026-09-18 13:11:52 +00:00
Michael Kret
19128a10f9
feat(core): Send agent attribution to the AI Gateway proxy (no-changelog) (#38590) 2026-09-18 13:10:00 +00:00
Irénée
d8882f776e
feat(editor): Add instance promote action to Environments settings (#38985)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-09-18 12:58:49 +00:00
Yen Su
e82a373b50
fix(core): Resolve hidden OAuth endpoint fields from the credential type for all credentials (#38697) 2026-09-18 12:56:22 +00:00
Theodore Nastos
964e4b5da5
chore(editor): Bootstrap the type availability policies frontend module (no-changelog) (#39090)
Co-authored-by: Olivier de Jong <olivier.dejong@n8n.io>
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-18 12:54:05 +00:00
oleg
c2534c5dc8
fix(core): Report publication state after n8n assistant workflow restores (#39037) 2026-09-18 12:50:59 +00:00
Dimitri Lavrenük
2d56eee224
feat: Add standalone node execution as a Workflow Builder and AI Assistant tool (#37512)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Jaakko Husso <jaakko@n8n.io>
2026-09-18 12:46:14 +00:00
Yen Su
d42a753176
feat(Databricks Node): Show the Chat Model node in the node picker (#38918) 2026-09-18 12:43:37 +00:00
Predrag Ristic
288e5e31e3
feat(core): Track AI Assistant response start latency (#37003) 2026-09-18 12:30:12 +00:00
Andreas Fitzek
b3b5939ef9
perf(core): Stop loading project members when listing credentials (#39068) 2026-09-18 12:30:06 +00:00
Matsu
a625a183f0
chore: Make eslint config changes require qa-dx review (#39083) 2026-09-18 11:52:33 +00:00
Savelii
4bec688648
refactor(core): Remove the unused apiKey:list and apiKey:delete scopes (#38994)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-18 11:48:05 +00:00
José Braulio González Valido
326030418d
fix(core): Attribute a draft-Agent eval scenario to the harness, not the builder (no-changelog) (#39007)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-18 11:44:52 +00:00
Rúben Castro
1b6b20c29e
feat(core): Add credentialTypePolicy:manage as a separate permission (no-changelog) (#38855)
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-18 11:43:09 +00:00
Robin Braumann
6f2bc13984
refactor(core): Share preview agent execution across callers (no-changelog) (#39064) 2026-09-18 11:32:54 +00:00
n8n-cat-bot[bot]
3d00ed98b0
fix: Limit declarative routing during base URL ownership checks (#39058)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
2026-09-18 11:31:53 +00:00
Tomi Turtiainen
d75fb91d1d
refactor(core): Simplify chat response mode detection (no-changelog) (#39050) 2026-09-18 11:31:47 +00:00
Sam Wooler
a2ab0d5f60
refactor(API): Migrate POST /credentials/{credentialId}/test and GET /credentials/schema/{credentialTypeName} to the decorator pattern (no-changelog) (#38638)
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-18 11:27:11 +00:00
Srinjoy
45a3833f0b
fix(core): RFC 9068 compliance for MCP OAuth access tokens (#38310) 2026-09-18 11:16:51 +00:00
Nour Alhadi Mahmoud
c87a6e5219
feat(API): Let promotion Apply pin the reviewed commit (no-changelog) (#39052)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-18 11:13:30 +00:00
Kai
85b3be3526
feat(API): Allow credential creation with source IDs (#38791) 2026-09-18 11:08:47 +00:00
Robin Braumann
87fe3c02f6
refactor(core): Bind integration context to agent turns (no-changelog) (#39043) 2026-09-18 10:56:47 +00:00
Benjamin Schroth
a70938f228
feat(editor): Show timestamp dividers in the agent preview chat (#38962)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-18 10:54:17 +00:00
Garrit Franke
3fe11518dd
fix(core): Remove the nodes ledger entry when a community package is uninstalled (no-changelog) (#38917) 2026-09-18 10:52:05 +00:00
Milorad FIlipović
fb0917c35b
fix(core): Fix workflow publishing silently failing in multi-main mode (#38431) 2026-09-18 10:29:27 +00:00
Robin Braumann
35445613b3
fix(editor): Align AI chat history menus (#38983) 2026-09-18 10:25:57 +00:00
Tomi Turtiainen
0c8aa72378
refactor(core): Extract enabled form-page node check (no-changelog) (#39045) 2026-09-18 10:22:47 +00:00
Tomi Turtiainen
95d69d91a2
refactor(core): Name supported webhook response modes (no-changelog) (#39041) 2026-09-18 10:22:46 +00:00
Tomi Turtiainen
cc093f53dd
refactor(core): Reuse webhook user projection (no-changelog) (#39033) 2026-09-18 10:18:55 +00:00
Robin Braumann
4ced93d549
refactor(core): Consolidate agent turn lifecycle (no-changelog) (#39032) 2026-09-18 10:04:47 +00:00
oleg
92b1aad608
fix(OpenAI Node): Accept object values for JSON-typed parameters (#33886) 2026-09-18 10:03:46 +00:00
Mutasem Aldmour
d5393f2a12
fix(editor): Render bare URLs in Instance AI chat as clickable links (#39060)
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-18 09:58:32 +00:00
Matsu
5158944ad3
ci: Refactor codespace previews script-base (#39042) 2026-09-18 09:57:03 +00:00
oleg
b37fdc2d3a
feat(editor): Add inline credential setup to n8n Assistant (no-changelog) (#38684) 2026-09-18 09:34:46 +00:00
Savelii
8e0c08dff2
ci: Run the encryption rollout test only in the public repository (no-changelog) (#39048)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-09-18 09:23:24 +00:00
Declan Carroll
3fdf46e530
test: Keep local sandbox TLS files removable (no-changelog) (#39047) 2026-09-18 09:18:52 +00:00
RomanDavydchuk
873948b838
feat(core): Filter out MCP servers from the registry based on instance capabilities (#38867) 2026-09-18 09:17:50 +00:00
James Martin
ec54145304
fix(core): Apply the same mandatory roles when creating role from API and frontend (#38860)
Co-authored-by: Savelii <savelii.sychov@n8n.io>
2026-09-18 09:10:55 +00:00
Charlie Kolb
399da28fda
ci: Enforce OWNERS required reviews on every base branch and fork PR (no-changelog) (#38713)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-18 09:07:10 +00:00
Ricardo Espinoza
1b39c1d57d
feat(core): Let the preferences MCP tool read one project (no-changelog) (#39000) 2026-09-18 08:56:41 +00:00
James Martin
7b059d64f1
fix(editor): Split global variable scopes to be added to custom roles separately (#38724) 2026-09-18 08:50:32 +00:00
Declan Carroll
0e7ccf6f74
test(engine): Add v2 infrastructure benchmarks (#38998) 2026-09-18 08:33:32 +00:00
Tomi Turtiainen
24e9125a7e
refactor(core): Simplify webhook request body parsing (no-changelog) (#39031) 2026-09-18 07:47:34 +00:00
Tomi Turtiainen
296d1ff6c1
refactor(core): Move webhook auth reason translator (no-changelog) (#39023) 2026-09-18 07:47:34 +00:00
Tomi Turtiainen
ccfa3e3be9
refactor(core): Extract MCP queue webhook setup (no-changelog) (#39022) 2026-09-18 07:45:13 +00:00
Guillaume Jacquart
e530b79b5a
feat(core): Lock credential decryption to available node types (no-changelog) (#38988)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-18 07:42:00 +00:00
oleg
0f3c8f0841
fix(core): Report saved and executed workflow revisions to n8n Assistant accurately (#38902) 2026-09-18 07:40:47 +00:00
Alexander Gekov
0d7aac283c
fix(core): Exclude Gemini embedding models from chat model lists (no-changelog) (#38997)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-18 07:29:00 +00:00
Benny Neugebauer
87ac2ef5b0
test: Add text families for property-based tests (#38511) 2026-09-18 07:17:54 +00:00
n8n-cat-bot[bot]
82f3a0154b
docs: Standardise on "engine v2" naming in AGENTS.md (#38966)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
2026-09-18 06:37:41 +00:00
n8n-assistant[bot]
39ef24a169
chore: Update e2e impact map (#39012)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-09-18 03:26:19 +00:00
Benny Neugebauer
1525f3a142
fix(Slack Node): Point OAuth2 scopes notice to an existing docs anchor (#38992) 2026-09-17 23:02:31 +00:00
Guillaume Jacquart
4dfec51b8b
test(core): Cover policy fan-out atomicity and cross-main reads (no-changelog) (#38950)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-17 19:27:42 +00:00
Garrit Franke
f4b857f6ec
feat(core): Reject install and update of community packages requiring an unsupported node API version (no-changelog) (#37744) 2026-09-17 18:52:40 +00:00
mfsiega
7374cbdab0
test(engine): Add a Playwright parity project for engine 2.0 (no-changelog) (#38708)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Co-authored-by: Danny Martini <danny@n8n.io>
2026-09-17 18:15:35 +00:00
Charlie Kolb
9ae5600999
ci: Enable the unlisted and catalog-reference knip rules (no-changelog) (#38795)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-17 17:15:32 +00:00
Riqwan Thamir
0a4976e62c
feat(editor): Open Edit with AI without a greeting turn (#38745)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-17 16:45:07 +00:00
Robin Braumann
77cd72ca05
perf(core): Improve observational memory retention (no-changelog) (#38961) 2026-09-17 16:23:19 +00:00
Declan Carroll
0024474b95
test(benchmark): Stabilize Playwright infrastructure baselines (#38900) 2026-09-17 15:58:16 +00:00
Nour Alhadi Mahmoud
0ee4d1f812
feat(core): Preview promotion changes in both directions and on the public API (no-changelog) (#38847)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-17 15:43:36 +00:00
Robin Braumann
9f57c9194b
fix(editor): Correct Agent Builder chat layout and update notices (#38987) 2026-09-17 15:39:47 +00:00
Lorent Lempereur
cebe908d3c
feat(core): Add a tracing span for each system task run (no-changelog) (#38913) 2026-09-17 15:22:02 +00:00
Ricardo Espinoza
768e74a5e7
fix(core): Always cast resource mapper string fields when type conversion is enabled (#37851) 2026-09-17 15:19:44 +00:00
Declan Carroll
9e18c4a8aa
fix: Unify startup deadlines and cancellation (#37927) 2026-09-17 15:14:42 +00:00
Rúben Castro
477481fec3
feat(core): Add kind property to node type policy telemetry events (no-changelog) (#38749)
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-17 14:53:20 +00:00
João Almeida
b26bb7f452
fix(editor): Convert a pasted expression into expression mode in the resource locator input (#38872)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-17 14:32:08 +00:00
Veljko Terzic
514c499896
feat(core): Run the instance-registry stale-member cleanup as a durable system task (no-changelog) (#38621)
Co-authored-by: Lorent Lempereur <lorent.lempereur@n8n.io>
2026-09-17 13:57:24 +00:00
Robin Braumann
512ac3be49
fix(core): Allow agents to finish without a reply (no-changelog) (#38973) 2026-09-17 13:56:31 +00:00
Tomi Turtiainen
3dd22cfd60
fix(engine): Use shared UUID v7 pattern for execution id check (#38963) 2026-09-17 13:54:54 +00:00
Tomi Turtiainen
cb1577367c
fix(engine): Report the host's v1 execution mode to a step (#38960) 2026-09-17 13:54:47 +00:00
José Braulio González Valido
e15b3fc922
fix(core): Restore the system prompt in Instance AI LLM step debug (no-changelog) (#38887)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-17 13:26:23 +00:00
Arvin A
33def0e800
feat(core): Add a description field to credentials (no-changelog) (#38839) 2026-09-17 13:16:34 +00:00
Arvin A
ba16e4f08d
feat(core): Give MCP clients the opening instance context (no-changelog) (#38292) 2026-09-17 13:16:10 +00:00
Predrag Ristic
cd623fd6ca
fix(core): Simulate Agent write tools during workflow verification (#38553) 2026-09-17 13:07:18 +00:00
Veljko Terzic
9059a89078
feat(core): Run the JTI cleanup as a durable system task (no-changelog) (#38596) 2026-09-17 12:55:14 +00:00
Robin Braumann
3ea03971d3
fix(editor): Add tooltips to assistant header actions (#38946) 2026-09-17 12:31:36 +00:00
Anne Aguirre
d1c805a595
fix(core): Observe agent execution tail when observation cursor moved in the middle of the run (#38907) 2026-09-17 12:18:00 +00:00
Kai
74334d2d85
feat(API): Pause promotion Apply until bindings are resolved (#38741) 2026-09-17 11:56:35 +00:00
Robin Braumann
536ae3ea14
fix(editor): Prevent agent builder shift during assistant transition (#38948) 2026-09-17 11:54:11 +00:00
Roman Makowiak
fe1012d75f
feat(core): Add the foundations for assistant-written preferences (no-changelog) (#38829)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-17 11:53:09 +00:00
Robin Braumann
0f0fca177a
fix(editor): Match n8n Assistant header background (#38944) 2026-09-17 11:52:33 +00:00
Declan Carroll
f6226f7252
ci: Run v3 sync automation from master (no-changelog) (#38951) 2026-09-17 11:34:54 +00:00
Robin Braumann
76fd3a0f67
fix(editor): Show step indicators in dark mode (#38937) 2026-09-17 11:27:35 +00:00
Declan Carroll
d027f2c286
ci: Defer sync lockfile conflicts (no-changelog) (#38942) 2026-09-17 11:13:50 +00:00
Declan Carroll
a0d084bbc3
ci: Isolate sync lockfile validation (no-changelog) (#38938) 2026-09-17 10:51:55 +00:00
n8n-cat-bot[bot]
3b3a773dc2
chore: Move the duplicated deserialize into AbstractEventMessage (#38486)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 10:36:23 +00:00
Declan Carroll
a9ae58ba76
ci: Use exact CI sync validation (no-changelog) (#38934) 2026-09-17 10:30:00 +00:00
Declan Carroll
615158d7c2
ci: Mirror sync lock validation with CI (no-changelog) (#38932) 2026-09-17 10:09:33 +00:00
Declan Carroll
ecc822d588
ci: Force fresh sync patch validation (no-changelog) (#38929) 2026-09-17 09:47:55 +00:00
Jan Kalkan
43fa826342
fix(core): Bound concurrent HashiCorp Vault external secrets reads (#38866)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-17 09:31:32 +00:00
Charlie Kolb
c915881537
build: Bump pnpm to 12.4.2 so Ctrl+C stops the dev processes (no-changelog) (#38919)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-17 09:24:18 +00:00
Declan Carroll
c05a02b60a
ci: Apply patches during sync validation (no-changelog) (#38924) 2026-09-17 09:21:06 +00:00
Rob Hough
77e10b05f8
feat(editor): Distinguish skills from tools in session timelines (#38481)
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
2026-09-17 09:12:56 +00:00
Miguel Ángel Moreno
087d134036
feat(core): Remove the N8N_MCP_CANVAS_GROUPS_ENABLED feature flag (no-changelog) (#38645)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-17 09:11:18 +00:00
Declan Carroll
39834a75d0
ci: Validate regenerated sync lockfiles (no-changelog) (#38921) 2026-09-17 09:08:26 +00:00
Savelii
692a4f69cc
test(core): Add repeatable upgrade and rotation tests for the encryption key rollout (no-changelog) (#37885)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-09-17 08:45:47 +00:00
Charlie Kolb
7fb840ec1d
ci: Skip owners required-reviews run for comment-only reviews (no-changelog) (#38906)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-17 08:40:49 +00:00
Matsu
0c815bbd73
ci: Improve Preview Codespace QoL (#38916) 2026-09-17 08:38:46 +00:00
Romeo Balta
062e99e829
fix(editor): Fix blank workflow previews in AI Assistant (#38608)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-17 08:15:53 +00:00
Anne Aguirre
8fcb236ecf
fix(editor): Agent preview thread wiring update and error markers (#38826) 2026-09-17 08:03:00 +00:00
Andreas Fitzek
d7e2dcd44d
fix(core): Repair data-encryption keys stored as the raw instance key (#38707) 2026-09-17 07:55:15 +00:00
Benjamin Schroth
d3b844554c
feat(editor): Open new agents in the builder with the n8n Assistant panel (#38727) 2026-09-17 07:32:10 +00:00
Matsu
5933694096
ci: Let a preview instance take environment from a webhook (#38894)
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 10:33:25 +03:00
Ilfat Mindubaev
f5090fc3c2
feat(Databricks Node): Add the Get Run operation to the hidden Job resource (no-changelog) (#38787)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-17 07:17:55 +00:00
Charlie Kolb
914eabb226
feat: Pin the sandbox service version in get-n8n.sh and update it on --upgrade (#38799)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-17 07:06:11 +00:00
Raúl Gómez Morales
24951fa2f4
feat(core): Count injected trigger output as simulated in AI Assistant verification (no-changelog) (#38626) 2026-09-17 07:05:33 +00:00
Svetoslav Dekov
82b38382b7
feat(editor): Show each AI sub-node as its own workflow setup step (no-changelog) (#38814)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-17 07:01:53 +00:00
Matsu
5227face30
ci: Remove unnecessary license override from SBOM (#38893) 2026-09-17 06:51:30 +00:00
n8n-cat-bot[bot]
386fedf257
fix: Keep the logs panel header height fixed (#38675)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 06:01:32 +00:00
Csaba Tuncsik
0b559b5ad0
feat(Microsoft Teams Node): Add a Chat resource with create, get and get many (#38035) 2026-09-17 04:43:56 +00:00
n8n-assistant[bot]
cfc1915f56
chore: Update e2e impact map (#38891)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-09-17 03:31:56 +00:00
n8n-cat-bot[bot]
cf49003c27
chore: Bump @types/json-diff from 1.0.0 to 1.0.3 (#38884)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
2026-09-16 22:32:54 +00:00
yehorkardash
efc9c85e9b
fix(editor): Send number values in credential inputs (#38715)
Co-authored-by: Sebastien Powell <sebpowell@users.noreply.github.com>
2026-09-16 17:41:21 +00:00
Jan Kalkan
f960330ecb
fix(core): Stop an unreachable external secrets provider from blocking startup (#38257)
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-16 16:11:49 +00:00
Andreas Fitzek
ccaeeb7eb6
feat: Wire up the email change confirmation flow (#38730) 2026-09-16 15:49:37 +00:00
Arvin A
1d029046f8
feat(core): Add the instance-context read tools to the MCP server (no-changelog) (#38241)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-16 15:15:20 +00:00
mfsiega
378e926584
fix(core): Tear down workflow triggers when publication meets a node type the instance cannot load (#38754)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-16 15:05:21 +00:00
n8n-cat-bot[bot]
2e588363e0
refactor(API): Migrate DELETE /tags/{id} to the decorator pattern (no-changelog) (#38469)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: Uddish Verma <uddish.verma@n8n.io>
2026-09-16 15:02:58 +00:00
Predrag Ristic
e36572e582
fix(core): Return available build errors in one tool result (#38231) 2026-09-16 14:56:51 +00:00
Theodore Nastos
5afb7e8102
feat(editor): Add available-types store to the type-availability-policies module (no-changelog) (#38784) 2026-09-16 14:42:18 +00:00
n8n-cat-bot[bot]
721d808eed
chore: Migrate GET /api/v1/folders/:folderId to @PublicApiController + Zod (#38616)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-16 14:33:14 +00:00
James Martin
c247cff20a
fix(editor): Disable 'pinning' redacted data if user does not have permission to un-redact it (#38334) 2026-09-16 14:19:55 +00:00
Jaakko Husso
ac752e7026
feat(core): Add an AI Assistant tool that runs one node of a workflow (#38601)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-16 14:16:44 +00:00
Declan Carroll
07fcc835cd
test: Make test resource acquisition and cleanup safe (#37923) 2026-09-16 13:58:30 +00:00
n8n-cat-bot[bot]
a24f8ae999
refactor(API): Migrate PUT /tags/{id} to the decorator pattern (no-changelog) (#38473)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: Uddish Verma <uddish.verma@n8n.io>
2026-09-16 13:54:35 +00:00
Benjamin Schroth
9f0e653c5a
feat(editor): Add the n8n Assistant panel to the agent builder (#38726) 2026-09-16 13:49:17 +00:00
n8n-cat-bot[bot]
7867844fbb
chore: Migrate GET /api/v1/folders to @PublicApiController + Zod (#38620)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: Ali Elkhateeb <ali.elkhateeb@n8n.io>
2026-09-16 13:42:04 +00:00
Michael Kret
94c12a6e7f
fix: Show only chat models in the Gemini and Qwen model dropdowns (#38800) 2026-09-16 13:35:07 +00:00
Charlie Kolb
8a93e66d2c
refactor(editor): Remove the AI Assistant template examples experiment (#38353)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-16 13:11:56 +00:00
Lorent Lempereur
5b4fc8c27d
feat(core): Add Prometheus metrics for system tasks (no-changelog) (#38743) 2026-09-16 13:09:55 +00:00
Benjamin Schroth
1268199bc4
refactor(editor): Extract the n8n Assistant conversation from the thread view (no-changelog) (#38719) 2026-09-16 13:00:42 +00:00
Luigi Gubello
41e17802f1
fix(core): Update undici to 7.29.1 (#38844)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-16 12:58:35 +00:00
Anne Aguirre
e149dfa1ae
fix(ai-builder): Pass parent ask-user answers into Agent Builder (#38693) 2026-09-16 12:24:54 +00:00
Michael Drury
b7b4722e4f
feat(editor): Improve agent builder interactions (#38728) 2026-09-16 12:22:02 +00:00
Luigi Gubello
74ab9c2ba2
fix(core): Update vm2 to 3.12.2 (#38823)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-16 12:15:41 +00:00
Stephen Wright
7c6cd34dbe
feat(Databricks Node): Add the Get Job operation to the hidden Job resource (no-changelog) (#38793) 2026-09-16 11:52:32 +00:00
Yen Su
d05cf2beb8
fix(core): Hide the Custom Scopes toggle when an MCP registry row pins the credential scope (#38635)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-09-16 11:48:11 +00:00
Yen Su
bf5d488bfe
fix(Databricks Node): Name Databricks in the Chat Model rate limit error and keep its message (#38642) 2026-09-16 11:47:57 +00:00
Yen Su
2f31b65b3c
fix(Databricks Node): Enforce the credential domain allowlist on chat model requests (#38694) 2026-09-16 11:47:32 +00:00
Precious Amesinlola
039212362b
feat(MiniMax Node): Fetch available models from the API instead of a hardcoded list (#37646)
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-16 11:24:35 +00:00
Eugene
1ad183a086
fix(editor): Prevent agent credential warnings during tab switches (#38816) 2026-09-16 11:16:08 +00:00
yehorkardash
7ff87c4321
fix(S3 Node): Handle files with + in names (#38688) 2026-09-16 11:14:16 +00:00
Robin Braumann
ee9177d1d5
feat(editor): Show recent agent changes (#38796) 2026-09-16 11:12:51 +00:00
Matsu
d7475392dd
build: Cli package oxlint migration (#38714) 2026-09-16 11:11:07 +00:00
Gauthier POGAM--LE MONTAGNER
0b9c174d1a
fix(cli): allow importing workflows and credentials with --userId for existing owner (#36278)
Co-authored-by: Junie <junie@jetbrains.com>
Co-authored-by: Emilia <100027345+sovietspaceship@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-16 10:59:51 +00:00
Tomi Turtiainen
9b66a2bbfa
feat(engine): Read executions from the data plane and merge with the DB (#38110) 2026-09-16 10:45:58 +00:00
Luka Zivkovic
c5935b9cb5
fix(core): Show an explicit error for unsupported end-user credential triggers (#37332) 2026-09-16 10:18:25 +00:00
Mutasem Aldmour
e009b9aaa0
feat(editor): Track Instance AI pre-fills with telemetry (no-changelog) (#38691)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-16 10:14:48 +00:00
yehorkardash
1d0c65856b
fix(Form Node): Validate content type (#38720) 2026-09-16 10:12:50 +00:00
Tomi Turtiainen
abdfb0a7fb
chore(benchmark): Upgrade Terraform and the Azure provider to latest versions (#38797) 2026-09-16 10:09:20 +00:00
Tomi Turtiainen
b3d15a64cc
refactor(core): Extract the execution list into its own service (#38326) 2026-09-16 10:02:14 +00:00
Stephen Wright
db55515f7a
feat(Databricks Node): Add polling transport for job runs and pipeline events (no-changelog) (#38629) 2026-09-16 10:00:42 +00:00
Luigi Gubello
533442071f
fix(Snowflake Node): Update the Snowflake SDK to 3.3.0 (#38798)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-16 09:59:06 +00:00
Rob Hough
a6ca37cbbf
feat(editor): Move web search to agent capabilities (#38646) 2026-09-16 09:51:44 +00:00
Kai
62ff356566
feat(core): Add promotion binding preflight with creation context (#38657) 2026-09-16 09:42:20 +00:00
Rob Hough
da55339548
fix(editor): Remove empty agent button hover background (#38788) 2026-09-16 09:22:13 +00:00
Tomi Turtiainen
dac4f63f1e
feat(engine): Add endpoints to list and count executions in the data plane (#38109)
Co-authored-by: Stephen Wright <sjw948@gmail.com>
Co-authored-by: Svetoslav Dekov <svetoslav.dekov@n8n.io>
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Co-authored-by: n8n-cat-bot[bot] <283985454+n8n-cat-bot[bot]@users.noreply.github.com>
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
Co-authored-by: Andreas Fitzek <andreas.fitzek@n8n.io>
Co-authored-by: Elias Meire <elias@meire.dev>
Co-authored-by: Mutasem Aldmour <4711238+mutdmour@users.noreply.github.com>
Co-authored-by: Riqwan Thamir <rmthamir@gmail.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Guillaume Jacquart <jacquart.guillaume@gmail.com>
Co-authored-by: n8n-assistant[bot] <100856346+n8n-assistant[bot]@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Matsu <matias.huhta@n8n.io>
Co-authored-by: Raúl Gómez Morales <raul00gm@gmail.com>
Co-authored-by: subaru <79771445+subaru-hello@users.noreply.github.com>
Co-authored-by: Declan Carroll <declan@n8n.io>
Co-authored-by: Charlie Kolb <charlie@n8n.io>
Co-authored-by: Roman Makowiak <roman.makowiak@n8n.io>
Co-authored-by: Tuukka Kantola <Tuukkaa@users.noreply.github.com>
2026-09-16 09:06:07 +00:00
Stephen Wright
aa8cc414bb
feat(Databricks Node): Add the hidden Databricks Trigger node skeleton (no-changelog) (#38722) 2026-09-16 09:03:20 +00:00
Emilia
a4c977dd8a
fix(core): Recheck the execution status before failing a stalled queue job (#38138)
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-16 09:01:48 +00:00
Emilia
a5995d757b
fix(core): Finalize the active execution when a stalled job turns out to have succeeded (#38220)
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-16 09:01:47 +00:00
Csaba Tuncsik
2627912062
feat(Microsoft Teams Node): Add @mentions on Reply and team tag mentions (#38023) 2026-09-16 08:52:58 +00:00
Declan Carroll
bda91bf1e3
test: Avoid duplicate project setup in data table tests (no-changelog) (#38782) 2026-09-16 08:49:18 +00:00
Mike Repeć
c2a2733915
fix(core): Keep breaking change detection running when a rule throws (#38738)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-16 08:22:40 +00:00
Roman Makowiak
780139c880
feat(core): Serve the saved AI preferences from an MCP tool instead of the server instructions (no-changelog) (#38582)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-16 08:09:45 +00:00
Mutasem Aldmour
c4c7365c7f
fix(editor): Keep the Instance AI chat input live during plan review (#38086)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-16 08:07:32 +00:00
Tuukka Kantola
25dc45d086
feat(editor): Improve AI Assistant sidebar and history dropdown (#38419)
Co-authored-by: r00gm <raul00gm@gmail.com>
2026-09-16 08:03:59 +00:00
Roman Makowiak
5b199f5cdf
feat(core): Add folder seeding to the Instance AI eval harness (no-changelog) (#38550)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-16 07:47:46 +00:00
n8n-cat-bot[bot]
d0da7cc332
chore: Benchmark load harness mis-accounts Playwright timeout across load stages (Kafka benchmarks always fail) (#38783)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
2026-09-16 07:42:02 +00:00
Charlie Kolb
d3c8eee07e
fix(core): Include sub-workflow tool calls in the workflow dependency index (#38062)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-09-16 07:34:49 +00:00
Declan Carroll
c09e00884c
fix: Remove agent harness checksum validation (no-changelog) (#38776) 2026-09-16 07:17:17 +00:00
subaru
706183b79e
fix(core): Read property keys by the name they stand for (no-changelog) (#34972)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-16 07:13:39 +00:00
Tomi Turtiainen
415fbdef9d
refactor(core): Connect cursor pagination end-to-end and drop unused range fields (#38070) 2026-09-16 07:13:29 +00:00
n8n-cat-bot[bot]
cf0a4911d0
chore: Extract the repeated detection-report construction in the breaking-changes rules (#38729)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-16 06:54:36 +00:00
Matsu
8968c6b19d
ci: Allow sync workflow to write synced workflow files (#38779) 2026-09-16 09:52:02 +03:00
Raúl Gómez Morales
92a3a28355
fix(editor): Add top padding to the n8n Assistant permission dropdowns (#38718) 2026-09-16 06:13:23 +00:00
n8n-assistant[bot]
776ae83c06
chore: Bundle/2.x (#38765)
Co-authored-by: Matsu <matias.huhta@n8n.io>
2026-09-16 07:52:44 +03:00
n8n-assistant[bot]
ec09d65da7
chore: Update e2e impact map (#38763)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-09-16 03:22:38 +00:00
Guillaume Jacquart
3d0c201496
refactor(core): Scope policy document lookups by kind (no-changelog) (#38739)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-15 17:04:54 +00:00
n8n-cat-bot[bot]
d31fe11a44
chore: Upgrade the n8n repository to the latest OpenCode harness (#38744)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
2026-09-15 16:10:14 +00:00
n8n-cat-bot[bot]
5c1dfef8d6
fix: Open the EULA modal when a license is activated from a link (#38712)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-15 16:06:00 +00:00
Riqwan Thamir
5c1c2a9936
feat(core): Pass Instance AI model id to LangSmith traces and run telemetry (no-changelog) (#38731)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-15 15:44:52 +00:00
Mutasem Aldmour
2f352bde2b
fix(core): Offer Computer Use only where the client shows its entry points (#38108)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-15 15:35:01 +00:00
Elias Meire
5f6627a71e
fix(Email Trigger (IMAP) Node): Recover from silently dead connections by moving to imapflow (#36716) 2026-09-15 15:18:17 +00:00
n8n-cat-bot[bot]
34f7327b4b
fix: Show a reveal prompt for expressions over redacted data (#38577)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: Andreas Fitzek <andreas.fitzek@n8n.io>
2026-09-15 15:18:08 +00:00
Svetoslav Dekov
9f709d76f0
fix(editor): Apply server-side feature flags when diagnostics are disabled (no-changelog) (#38732)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-15 15:13:05 +00:00
Stephen Wright
13c8a3bbd4
feat(Databricks Node): Add the Get Run Output operation to the hidden Job resource (no-changelog) (#38709) 2026-09-15 14:20:37 +00:00
Veljko Terzic
e56b2a7654
feat(core): Skip an in-memory system task run when its durable job is provisioned (no-changelog) (#38459)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-15 14:03:34 +00:00
Pierantonio Cangianiello
f8038a640f
feat(engine): Bind the data plane credentials helper in the Engine 2.0 runtime (no-changelog) (#38580)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-15 13:55:45 +00:00
Veljko Terzic
37176addee
feat(core): Deprovision stale durable system task jobs at boot (no-changelog) (#38445) 2026-09-15 13:29:51 +00:00
Danny Martini
c5534b54a7
feat(editor): Run expressions through the QuickJS engine in the browser (no-changelog) (#37448)
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Co-authored-by: Luigi Gubello <luigi.gubello@n8n.io>
Co-authored-by: Luigi Gubello <27034386+luigigubello@users.noreply.github.com>
Co-authored-by: Emilia <100027345+sovietspaceship@users.noreply.github.com>
2026-09-15 13:27:20 +00:00
Charlie Kolb
e3c3cb8f5a
test(core): Stop agents unit tests from fetching the model catalog (no-changelog) (#38592)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-15 12:50:41 +00:00
Anne Aguirre
48445f5b9e
fix(core): Sweep parents parked on a finished sub-execution (#37553)
Co-authored-by: Emilia <100027345+sovietspaceship@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-15 12:17:14 +00:00
Veljko Terzic
2fb19eb06a
feat(core): Provision durable jobs for system tasks (no-changelog) (#37759) 2026-09-15 12:11:25 +00:00
Csaba Tuncsik
9c8361377a
feat(Microsoft Teams Node): Add @mention support to message Create (#37624) 2026-09-15 12:00:08 +00:00
Rob Hough
506b7686e9
fix(editor): Align agent knowledge sections with panels (#38696) 2026-09-15 11:54:09 +00:00
Garrit Franke
8bafdfa3c7
feat(core): Add push-based reload endpoint for custom node development (#36837) 2026-09-15 11:46:25 +00:00
c2nprds
75a2fe5985
fix(Monday.com Node): Migrate to API version 2026-07 (#26262)
Co-authored-by: Dimitri Lavrenük <20122620+dlavrenuek@users.noreply.github.com>
2026-09-15 11:31:21 +00:00
Theodore Nastos
f7f51fbcd3
feat(editor): Add the type-availability-policies frontend module (no-changelog) (#38634) 2026-09-15 11:17:15 +00:00
Thanasis G
4514a35d1d
fix(core): Bump Strapi listing version for streamable-http-templated (#38595)
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-15 11:11:13 +00:00
Swifty
8dd2b78935
fix(editor): Clarify n8n Assistant approval details (#38422) 2026-09-15 11:10:42 +00:00
Matsu
446f7876cd
ci: Use GH app token for all calls in ci-owners-required-reviews (#38716) 2026-09-15 10:59:26 +00:00
Srinjoy
4f26727ecf
fix(core): Eliminate false-negative 500s in workflow builder tools (#34975) 2026-09-15 10:53:23 +00:00
Rúben Castro
d61003c395
feat(API): Add node type policy document and attachment endpoints (no-changelog) (#38229)
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-15 10:44:03 +00:00
James Martin
f9fcf37b67
feat(editor): Make view related tag permissions available by default (#38640) 2026-09-15 10:12:58 +00:00
Guillaume Jacquart
715907ea21
feat(core): Enforce node type availability policies (no-changelog) (#38476)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-15 10:09:01 +00:00
Charlie Kolb
2b346002db
test(editor): Stub the lazy credentials button in NodeView test and report signal-killed vitest runs (no-changelog) (#38695)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-15 10:08:06 +00:00
Predrag Ristic
4d9cd43168
fix(editor): Restore MCP connection popup retries (#37956) 2026-09-15 10:05:51 +00:00
Raúl Gómez Morales
dba75ffb25
fix(editor): Align AI Assistant permission dropdowns with the group control (#38196) 2026-09-15 10:01:32 +00:00
n8n-release-tag-merge[bot]
66a8f2046b Merge tag 'n8n@2.40.0' 2026-09-15 10:15:08 +00:00
n8n-assistant[bot]
caff5b7013
🚀 Release 2.40.0 (#38711)
Co-authored-by: Matsuuu <16068444+Matsuuu@users.noreply.github.com>
2026-09-15 13:02:15 +03:00
Matsu
80731ca347
ci: Bypass safe-chain proxy on pnpm publish (#38700) 2026-09-15 12:59:22 +03:00
Charlie Kolb
6876a86f42
test(editor): Test source control repo URL regexes without mounting the view (#38368)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-15 09:48:42 +00:00
n8n-cat-bot[bot]
40dfde5b4c
chore: Replace corepack with npm i -g pnpm in benchmark Dockerfile (#38226)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: Charlie Kolb <charlie@n8n.io>
2026-09-15 09:32:33 +00:00
Raúl Gómez Morales
954dd35a91
feat(core): Add a timeout option to test webhook registrations (no-changelog) (#38625) 2026-09-15 09:32:27 +00:00
Vikas Rathee
da910f5eaf
feat: Add Microsoft Dataverse core node (#34286)
Co-authored-by: Stephen Wright <sjw948@gmail.com>
2026-09-15 09:13:13 +00:00
Ricardo Espinoza
d2af313653
fix(editor): Keep header permissions after template insert (#38387) 2026-09-15 08:52:46 +00:00
Rob Hough
d282dec072
feat(editor): Mark required agent fields (#38624) 2026-09-15 08:39:17 +00:00
Raúl Gómez Morales
4e1fcf6eac
fix(core): Prefer native nodes over Code nodes in n8n Assistant workflow builds (#38598) 2026-09-15 08:27:22 +00:00
Declan Carroll
c3f9dedef6
feat: Use shared OpenCode harness (no-changelog) (#38602) 2026-09-15 08:27:08 +00:00
Matsu
8a7e3951ca
ci: Add a probe to verify npm token exchange (#38699) 2026-09-15 12:00:04 +03:00
n8n-cat-bot[bot]
d057d8693b
fix: Guard the getWebhookInfo response shape (#38680)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-15 08:22:25 +00:00
Charlie Kolb
ca550a95b6
ci: Diff the PR head in ci-filter on pull request events (no-changelog) (#38689)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-15 08:20:28 +00:00
Declan Carroll
bbd30299d8
ci: Shard E2E performance tests (no-changelog) (#38500) 2026-09-15 08:06:56 +00:00
Declan Carroll
9ba62160c0
ci: Run database test suites in parallel (no-changelog) (#38641) 2026-09-15 08:03:39 +00:00
Rob Hough
f81cbcd60d
fix(editor): Reserve space for list and table scrollbars (#38603) 2026-09-15 07:59:28 +00:00
Eugene
7efefc7347
feat(editor): Show background tasks in the agent preview (#38360) 2026-09-15 07:35:56 +00:00
Riqwan Thamir
cb55ecce7f
feat(core): Add option to disable mid-run observation for Instance AI (no-changelog) (#38664)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-15 07:32:34 +00:00
Charlie Kolb
7e1100d73f
ci: Run knip in the PR quality checks (no-changelog) (#38610)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-15 07:20:10 +00:00
Paul Fitzpatrick
137c64f5b7
feat(Grist Node): Add OAuth2 as an authentication option (#34798)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-09-15 07:18:48 +00:00
Irénée
8671d833d7
fix(editor): Use the push scope for the source control owner filter (#38682)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-09-15 07:15:57 +00:00
Ali Elkhateeb
89fe108b41
feat(core): Add a v3 migration report rule for preExecute execution creation (#38681)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-15 07:07:40 +00:00
Declan Carroll
df54872bc7
ci: Shard scoped CLI integration tests across runners (no-changelog) (#38395) 2026-09-15 07:06:48 +00:00
yehorkardash
02ee221e2d
fix(core): Accept null headers from mcp registry api (#38614) 2026-09-15 07:02:33 +00:00
Ilfat Mindubaev
d04d3a4516
fix(Microsoft Excel Node): Accept personal OneDrive workbook IDs in the By ID field (#38471)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-15 06:26:59 +00:00
n8n-assistant[bot]
d609c47344
chore: Update e2e impact map (#38669)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-09-15 03:33:46 +00:00
Arvin A
8bff5da5a2
feat: Add the AI preferences API and the Context settings UI (no-changelog) (#38213)
Co-authored-by: Svetoslav Dekov <svetoslav.dekov@n8n.io>
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-14 18:56:45 +00:00
Ricardo Espinoza
c7c179b818
fix(core): Correct the expression prefix warnings on fields that cannot hold an expression (#37791) 2026-09-14 18:49:35 +00:00
Rob Hough
8123c2ca46
feat(editor): Split workflows into a separate capability row (#38588) 2026-09-14 16:18:59 +00:00
Stephen Wright
e4ca242154
feat(Microsoft Teams Node): Add the channel message undo delete operation (#38429) 2026-09-14 15:50:38 +00:00
Mutasem Aldmour
b835bef402
fix(editor): Keep the typed AI Assistant prompt when no project is selected (#37813)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-14 15:50:27 +00:00
Joco-95
c6ab37993a
feat: Implements AIA inspiration from taxonomy experiment (#38609) 2026-09-14 15:45:45 +00:00
Jan
4180591c5a
refactor(editor): Align security settings with shared layout (#37620)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-14 15:20:44 +00:00
Pierantonio Cangianiello
bb9556b9d5
feat(engine): Add the data plane credentials helper that resolves credentials through the control plane (no-changelog) (#38443)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-14 15:13:43 +00:00
Stephen Wright
100983eb98
feat(Microsoft Teams Node): Add the Service Principal path for Online Meeting Update (#38324) 2026-09-14 14:49:06 +00:00
Mike Repeć
1cc5708f4d
feat(core): Add v3 breaking change rule for the storage directory rename (#38423)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-14 14:36:37 +00:00
Konstantin Tieber
c70e754d4c
feat(core): Include reportTime and lastSuccessfulReport of instance-reporting in module-settings endpoint (no-changelog) (#38458)
Co-authored-by: Sam Wooler <swooler592@gmail.com>
2026-09-14 14:29:30 +00:00
Sam Wooler
6307a5aaf1
refactor(API): Migrate DELETE /credentials/{credentialId} and PUT /credentials/{credentialId}/transfer to the decorator pattern (#38454)
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-14 14:28:25 +00:00
Anne Aguirre
eb4eebaa98
feat(ai-builder): Better validation errors tooltips for agent preview and publish (#38246) 2026-09-14 14:24:21 +00:00
Jaakko Husso
3392e3da66
feat(core): Warn in the migration report that chat hub goes away (#38613)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-14 14:10:14 +00:00
Robin Braumann
52132465ae
fix(editor): Allow creating web search credentials from the agent builder settings (#38574)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-14 14:08:41 +00:00
Tuukka Kantola
c979296d6c
feat(editor): Add paginated AI Assistant chat history (#38467)
Co-authored-by: r00gm <raul00gm@gmail.com>
2026-09-14 14:01:07 +00:00
n8n-cat-bot[bot]
f886fff9fd
refactor(API): Migrate GET /tags/{id} to the decorator pattern (no-changelog) (#38468)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: Uddish Verma <uddish.verma@n8n.io>
2026-09-14 13:52:37 +00:00
Mike Repeć
e05924fe7f
chore(core): Warn about internal task runner mode by default and drop the v3 migration rule (no-changelog) (#38581)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-14 13:50:40 +00:00
Stephen Wright
ad04d48572
feat(Databricks Node): Add a hidden Job resource with a Run operation (no-changelog) (#38579) 2026-09-14 13:49:11 +00:00
Jaakko Husso
7b10a16ca4
feat(core): Tell Instance AI when a verified draft is not the live workflow (#38289)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-14 13:47:19 +00:00
Charlie Kolb
737d66eeb3
build: Remove unused dependencies reported by knip (no-changelog) (#38591)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-14 13:22:42 +00:00
Sandra Zollner
7ec481ceaa
feat(core): Add selective promote for Git-based promotions (#38105)
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: kgrhartlage <kai.hartlage@n8n.io>
2026-09-14 13:14:28 +00:00
Michael Drury
684b930fa6
feat(core): Improve Agent Builder evaluation and intent routing (#38329) 2026-09-14 13:13:51 +00:00
Yen Su
8f9faa7b58
feat(Microsoft Teams Node): Add the chat member remove operation (#37747) 2026-09-14 13:05:31 +00:00
Svetoslav Dekov
28592bc7a9
feat(core): Inject AI preferences into the Instance AI opening turn (no-changelog) (#38206)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-14 12:49:40 +00:00
Roman Makowiak
36f82a31d2
fix(editor): Read the MCP nudge flag as a multivariate experiment (#38586)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-14 12:35:39 +00:00
Yen Su
7db4dcf130
feat(core): Append the MCP registry attribution to standalone agent replies (#38584)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-09-14 12:33:40 +00:00
n8n-cat-bot[bot]
d58c7db803
refactor(API): Migrate POST /tags to the decorator pattern (no-changelog) (#38472)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: Uddish Verma <uddish.verma@n8n.io>
2026-09-14 12:30:14 +00:00
Stephen Wright
aa84837945
feat(Microsoft Teams Node): Add the Service Principal path for Online Meeting Delete (#38323) 2026-09-14 12:27:37 +00:00
Stephen Wright
05f35278c5
feat(Microsoft Teams Node): Add the Service Principal path for Online Meeting Create or Get (#38322) 2026-09-14 12:27:23 +00:00
Matsu
6ab84e8d7b
build: Migrate base eslint rules to oxlint (#38605) 2026-09-14 12:19:07 +00:00
Veljko Terzic
53cbe9700d
fix(core): Clear the scheduled task error message when a retried occurrence succeeds (no-changelog) (#38604) 2026-09-14 12:15:10 +00:00
Charlie Kolb
ccb935478c
ci: Run owners and PR quality scripts without a Node.js setup (no-changelog) (#38349)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-14 11:40:39 +00:00
Stephen Wright
ba935fcb3c
feat(Microsoft Teams Node): Add the channel message delete operation (#38428) 2026-09-14 11:02:27 +00:00
Stephen Wright
59c7d15a60
feat(Azure Storage Node): Add Azure cloud selection and custom endpoints to the Shared Key credential (#38181) 2026-09-14 10:45:55 +00:00
Robin Braumann
2f55165c11
fix(core): Send a JSON Accept header in the Brave Search credential test (#38594)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-14 10:25:54 +00:00
James Gee
82d284f547
feat(core): Add project promotion change preview (no-changelog) (#38361)
Co-authored-by: Jan K. <jan.kalkan@n8n.io>
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-14 10:13:36 +00:00
Stephen Wright
7c7a130949
feat(Microsoft Teams Node): Add the chat message undo delete operation (#38427) 2026-09-14 10:12:40 +00:00
Stephen Wright
fa7553fca1
feat(Microsoft Teams Node): Add the chat message delete operation (#38426) 2026-09-14 10:12:05 +00:00
Andreas Fitzek
016ce8ba1b
feat(core): Add email change confirmation flow (no-changelog) (#37950)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-09-14 10:10:00 +00:00
Svetoslav Dekov
fe5de38a61
fix(editor): Show friendly labels for the activity and conversation-history tools in the n8n Assistant chat (#38447)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-14 10:08:13 +00:00
Andreas Fitzek
11cc12043b
fix: Scope deployment key lookups by type (#38410)
Some checks failed
Build: Benchmark Image / build (push) Has been cancelled
CI: Master (Build, Test, Lint) / Build for Github Cache (push) Has been cancelled
CI: Master (Build, Test, Lint) / Unit tests (24.18.1) (push) Has been cancelled
CI: Master (Build, Test, Lint) / Unit tests (26.5.1) (push) Has been cancelled
CI: Master (Build, Test, Lint) / Lint (push) Has been cancelled
CI: Master (Build, Test, Lint) / Performance (push) Has been cancelled
CI: Master (Build, Test, Lint) / Verify single-instance deps (npm-install) (push) Has been cancelled
Util: Publish API Schema / Cloudflare Pages (push) Has been cancelled
CI: Master (Build, Test, Lint) / Notify Slack on failure (push) Has been cancelled
2026-09-14 09:52:36 +00:00
Ricardo Espinoza
d0f94dabb6
fix(editor): Stop the Publish modal from hanging after a workflow is published (#38151)
Co-authored-by: Svetoslav Dekov <svetoslav.dekov@n8n.io>
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-14 09:45:07 +00:00
Anne Aguirre
5c42568468
feat(ai-builder): Wrap vecto store settings in advanced section (#38437) 2026-09-14 09:36:02 +00:00
Sebastien Powell
bbb76244c2
feat(editor): Cleanup Select component (#35523)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-14 09:35:31 +00:00
Stephen Wright
59715e8a3c
fix: Stop requiring Organization.Read.All for the Microsoft Entra Service Principal credential test (#38568) 2026-09-14 09:18:28 +00:00
n8n-cat-bot[bot]
c220447682
fix: Hide the deprecated node and show a deprecation notice (#38571)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 09:11:17 +00:00
Charlie Kolb
b67870cdbc
chore: Add knip for unused dependency detection (no-changelog) (#38456)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-14 09:06:03 +00:00
Markus Corazzione
0396ef8894
fix(Guardrails Node): Align threshold hint with behavior (#29018)
Co-authored-by: yehorkardash <yehor.kardash@n8n.io>
2026-09-14 09:01:39 +00:00
Raúl Gómez Morales
4890d04457
fix(core): Settle answered confirmation cards in the AI Assistant run-sync frame (#38328) 2026-09-14 08:50:42 +00:00
Sandra Zollner
29cea0b017
feat(core): Apply a selective package export to a Git working copy (no-changelog) (#37997)
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: kgrhartlage <kai.hartlage@n8n.io>
2026-09-14 08:46:21 +00:00
Robin Braumann
1175b1c1df
fix(editor): Match workflow save and publish keyboard shortcuts in agent builder (#38572)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-14 08:42:23 +00:00
Sam Wooler
e9860b34e1
refactor(API): Migrate POST /credentials and PATCH /credentials/{id} to the decorator pattern (no-changelog) (#37985) 2026-09-14 08:42:00 +00:00
Danny Martini
3d23aaddcd
feat(core): Generalize QuickJS Intl host delegation (no-changelog) (#37213)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: Luigi Gubello <luigi.gubello@n8n.io>
Co-authored-by: Luigi Gubello <27034386+luigigubello@users.noreply.github.com>
2026-09-14 08:08:17 +00:00
Matsu
4cda10ebc6
build: Initialize n8n-oxlint config (#38565)
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 07:55:26 +00:00
Pierantonio Cangianiello
68d567c063
feat(engine): Add the control plane route that resolves credentials for the data plane (no-changelog) (#38218)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-14 07:48:41 +00:00
Miguel Ángel Moreno
762b9a64de
feat(core): Warn MCP builders when the canvas is over the top-level ceiling (no-changelog) (#38305)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-14 07:33:56 +00:00
Miguel Ángel Moreno
8b6885433e
feat(core): Refuse an ungrouped Instance AI build over the top-level ceiling (no-changelog) (#38235)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-14 07:33:55 +00:00
Mike Repeć
f031805b5c
feat(core): Warn on deprecated N8N_DB_PING_TIMEOUT env var (#38555)
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-14 07:29:45 +00:00
Guillaume Jacquart
1020dfd188
feat(core): Add node type policy telemetry events (no-changelog) (#38331)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-14 07:26:52 +00:00
Danny Martini
ac3e312fa0
refactor(core): Extract shared host functions from the expression bridges (no-changelog) (#37158)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: Luigi Gubello <luigi.gubello@n8n.io>
2026-09-14 06:58:45 +00:00
Stephen Wright
763e0cf2e0
fix(Microsoft Excel Node): Show only workbook files in the Workbook picker (#38244) 2026-09-14 06:56:51 +00:00
n8n-assistant[bot]
aba174c9f3
chore: Update e2e impact map (#38563)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-09-14 03:03:01 +00:00
n8n-assistant[bot]
8db30e46f4
chore: Update node popularity data (#38560)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-09-14 00:46:32 +00:00
n8n-assistant[bot]
4169b55bf3
chore: Update e2e impact map (#38533)
Some checks failed
CI: Master (Build, Test, Lint) / Build for Github Cache (push) Waiting to run
CI: Master (Build, Test, Lint) / Unit tests (24.18.1) (push) Waiting to run
CI: Master (Build, Test, Lint) / Unit tests (26.5.1) (push) Waiting to run
CI: Master (Build, Test, Lint) / Lint (push) Waiting to run
CI: Master (Build, Test, Lint) / Performance (push) Waiting to run
CI: Master (Build, Test, Lint) / Verify single-instance deps (npm-install) (push) Waiting to run
CI: Master (Build, Test, Lint) / Notify Slack on failure (push) Blocked by required conditions
Util: Update Node Popularity / update-popularity (push) Has been cancelled
Util: Update Credential Setupability / update-setupability (push) Has been cancelled
Util: Update Node Popularity / approve-and-automerge (push) Has been cancelled
Util: Update Credential Setupability / approve-and-automerge (push) Has been cancelled
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-09-13 03:01:20 +00:00
Alex Grozav
9da3b7479f
refactor(editor): Add defineFrontendModule() and make it the canonical descriptor form (no-changelog) (#38464)
Some checks are pending
CI: Master (Build, Test, Lint) / Build for Github Cache (push) Waiting to run
CI: Master (Build, Test, Lint) / Unit tests (24.18.1) (push) Waiting to run
CI: Master (Build, Test, Lint) / Unit tests (26.5.1) (push) Waiting to run
CI: Master (Build, Test, Lint) / Lint (push) Waiting to run
CI: Master (Build, Test, Lint) / Performance (push) Waiting to run
CI: Master (Build, Test, Lint) / Verify single-instance deps (npm-install) (push) Waiting to run
CI: Master (Build, Test, Lint) / Notify Slack on failure (push) Blocked by required conditions
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: multica-agent <github@multica.ai>
2026-09-12 16:34:08 +00:00
Robin Braumann
072f4960f0
feat(core): Share integration threads across participants (#38461)
Some checks are pending
CI: Master (Build, Test, Lint) / Build for Github Cache (push) Waiting to run
CI: Master (Build, Test, Lint) / Unit tests (24.18.1) (push) Waiting to run
CI: Master (Build, Test, Lint) / Unit tests (26.5.1) (push) Waiting to run
CI: Master (Build, Test, Lint) / Lint (push) Waiting to run
CI: Master (Build, Test, Lint) / Performance (push) Waiting to run
CI: Master (Build, Test, Lint) / Verify single-instance deps (npm-install) (push) Waiting to run
CI: Master (Build, Test, Lint) / Notify Slack on failure (push) Blocked by required conditions
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-12 05:14:33 +00:00
n8n-assistant[bot]
4603205230
chore: Update e2e impact map (#38507)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-09-12 03:00:51 +00:00
Rúben Castro
f7f92c7107
feat(API): Add node type policy instance and project endpoints (no-changelog) (#38239)
Some checks failed
CI: Master (Build, Test, Lint) / Build for Github Cache (push) Waiting to run
CI: Master (Build, Test, Lint) / Unit tests (24.18.1) (push) Waiting to run
CI: Master (Build, Test, Lint) / Unit tests (26.5.1) (push) Waiting to run
CI: Master (Build, Test, Lint) / Lint (push) Waiting to run
CI: Master (Build, Test, Lint) / Performance (push) Waiting to run
CI: Master (Build, Test, Lint) / Verify single-instance deps (npm-install) (push) Waiting to run
CI: Master (Build, Test, Lint) / Notify Slack on failure (push) Blocked by required conditions
Util: Publish API Schema / Cloudflare Pages (push) Has been cancelled
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-11 16:57:33 +00:00
Sam Wooler
b9a7f0341a
fix(API): Return null variable values as empty string & fix newly imported source control variable values (#38488)
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-11 16:26:32 +00:00
Roman Makowiak
4579e78071
fix(core): Read the Instance AI folder-exploration flag as a variant, not a boolean (#38479)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-11 16:23:35 +00:00
Kai
98f4febbf8
feat(core): Push to a timestamped branch on promote for promotions (#37573) 2026-09-11 15:44:02 +00:00
n8n-cat-bot[bot]
d1ea14cea6
chore: Move the triplicated defineField into the extensions utils.ts (#38485)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-11 15:11:18 +00:00
Luka Zivkovic
70e5a2bf16
feat: Export structured agent artifacts in eval results (#38394) 2026-09-11 15:11:02 +00:00
Yen Su
13b8f22fd0
feat(Databricks Node): Append a per-row MCP registry attribution to the agent reply (#38306)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-11 14:46:48 +00:00
n8n-cat-bot[bot]
ce58a153c8
refactor(API): Migrate the variables write routes to @PublicApiController (#38313)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
Co-authored-by: Uddish Verma <uddish.verma@n8n.io>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-11 14:26:49 +00:00
Dmitrii
3fc78031f6
feat(core): Add log streaming events for instance reports (#38442) 2026-09-11 14:15:54 +00:00
Declan Carroll
d11d185f5c
test: Balance E2E shards by fixture pool (#38301) 2026-09-11 13:17:07 +00:00
Mutasem Aldmour
3bd0e699bf
fix(core): Redact opaque field values in confirmed secret containers (#38067)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-11 13:15:19 +00:00
Uddish Verma
697b538c95
refactor(API): Migrate the project member routes to the decorator pattern (no-changelog) (#37979)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-11 13:01:51 +00:00
Anne Aguirre
999b541764
fix(ai-builder): Show reusable credentials in the Instance AI agent credential card (#38276) 2026-09-11 12:48:24 +00:00
Eugene
374224622d
fix(editor): Improve agent preview update reliability (no-changelog) (#38359) 2026-09-11 12:45:32 +00:00
Stephen Wright
404ab49c50
fix: Hint the Microsoft OAuth2 scope format and link the Service Principal credential docs (#38451) 2026-09-11 12:25:03 +00:00
Robin Braumann
40b6d84525
fix(core): Run tool nodes of an AI Agent upstream of the destination node (#38348)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-11 12:23:27 +00:00
Mike Repeć
357b2a03d9
feat(core): Acquire expression isolates lazily on first engine evaluation (no-changelog) (#37614)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-09-11 12:02:19 +00:00
Miguel Ángel Moreno
f8876ae2ff
feat(core): Improve node grouping instructions for AI Assistant and MCP (no-changelog) (#37729)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-11 11:53:43 +00:00
Raúl Gómez Morales
e44fbd358a
fix(core): Return Instance AI node output grouped per output branch (#38414) 2026-09-11 11:51:35 +00:00
Milorad FIlipović
064e90964d
fix(editor): Restore legacy sticky note markdown spacing (#38333) 2026-09-11 11:50:23 +00:00
James Martin
fc8a02113f
fix(editor): Fixes issue that prevented some users from revoking their end-user credentials (#38311) 2026-09-11 11:48:22 +00:00
Andreas Fitzek
840aef623e
fix(core): Redact execution data on partial private-credential runs (#38364) 2026-09-11 11:24:54 +00:00
Kai
c11f0312ea
feat(editor): Add provider and connection settings for Environments v2 (#38402) 2026-09-11 11:02:23 +00:00
Chisa Kotegawa
a48373d9c0
fix(core): Select the OpenAI API for custom endpoints (no-changelog) (#38012) 2026-09-11 11:02:03 +00:00
Alex Grozav
783cdef629
build(editor): Enable incremental typecheck for frontend packages (#38317) 2026-09-11 10:59:30 +00:00
Charlie Kolb
c1284a0302
chore: Drop the dead ElTable guard from the element-plus pnpm patch (#38412)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-11 10:53:48 +00:00
n8n-cat-bot[bot]
35fb4f1324
chore: Rename the role path parameter from slug to roleSlug (#38118)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: Sam Wooler <swooler592@gmail.com>
2026-09-11 10:53:21 +00:00
n8n-cat-bot[bot]
b0e8476766
chore: Rename the workflow version path parameter from versionId to workflowVersionId (#38119)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: Sam Wooler <swooler592@gmail.com>
2026-09-11 10:49:41 +00:00
Savelii
37e50b56fd
fix(core): Scope end-user credential checks to reachable nodes (#36970)
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-11 10:42:45 +00:00
Ilfat Mindubaev
6424ca6bcb
feat(Confluence Node): Unhide the node and enable AI tool usage (#38416)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-11 10:39:02 +00:00
Matsu
3454df1c1b
chore: Remove dead ESLint config and add lint parity tooling (no-changelog) (#38343)
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-11 13:33:07 +03:00
Ilfat Mindubaev
b0f47ebc1d
fix(Confluence Node): Return the page sub-tree in tree order and skip drafts (#38403)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-11 10:12:13 +00:00
Matsu
aca9b189b9
ci: Grant contents read to the bundle PR retarget job (no-changelog) (#38425)
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-11 10:09:56 +00:00
Charlie Kolb
89b0ea157c
chore: Remove unused workspace dependencies (no-changelog) (#38377)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-11 09:53:19 +00:00
n8n-cat-bot[bot]
7bac547878
fix: Serve binary data stored in the temp execution dir (#38270)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: Emilia <100027345+sovietspaceship@users.noreply.github.com>
2026-09-11 09:51:30 +00:00
Benjamin Schroth
56d18bdddc
feat(editor): Hand agent change requests in the preview chat to the AI Assistant (#38283)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-11 09:44:31 +00:00
n8n-cat-bot[bot]
ea0ff45c2c
refactor: Extract the shared detected/resolved flow of the instance-registry cluster checks (#38005)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-11 09:44:19 +00:00
Matsu
23074f4773
ci: Create attestations on v3 branch nightlies (#38417) 2026-09-11 09:26:16 +00:00
n8n-cat-bot[bot]
dcb1d173aa
fix: Scope workflow sharing candidates to personal projects (#38222)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: James Martin <james.martin@n8n.io>
Co-authored-by: James Martin <393605+ymhr@users.noreply.github.com>
2026-09-11 09:15:51 +00:00
Yen Su
c9a674261b
feat(Databricks Node): Query the chat model through Unity AI Gateway model services (no-changelog) (#37951)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-09-11 09:10:53 +00:00
Stephen Wright
65cd935b00
feat(Microsoft Teams Node): Add the Service Principal path for Online Meeting Get (#38321) 2026-09-11 08:57:51 +00:00
Stephen Wright
d6fe24d465
fix(Microsoft SharePoint Node): Keep the resource dropdowns working when the API omits a label (#38411)
Some checks are pending
CI: Master (Build, Test, Lint) / Build for Github Cache (push) Waiting to run
CI: Master (Build, Test, Lint) / Unit tests (24.18.1) (push) Waiting to run
CI: Master (Build, Test, Lint) / Unit tests (26.5.1) (push) Waiting to run
CI: Master (Build, Test, Lint) / Lint (push) Waiting to run
CI: Master (Build, Test, Lint) / Performance (push) Waiting to run
CI: Master (Build, Test, Lint) / Verify single-instance deps (npm-install) (push) Waiting to run
CI: Master (Build, Test, Lint) / Notify Slack on failure (push) Blocked by required conditions
Co-authored-by: Ilfat Mindubaev <ilfat.mindubaev@n8n.io>
2026-09-11 08:26:38 +00:00
Daria
d1e6d6fd5c
fix(editor): Prevent group frames from overlapping during auto-layout (#37509) 2026-09-11 08:24:55 +00:00
Anne Aguirre
8800c7765d
feat(ai-builder): Add ability to duplicate agents (#38214) 2026-09-11 08:23:39 +00:00
Nguyen Thanh Dat
8c8bd23364
fix: Keep GitHub Actions expressions intact in scaffolded workflow files (#37973)
Co-authored-by: Guillaume F. <guillaume.feliciano@n8n.io>
2026-09-11 08:08:47 +00:00
oleg
f4df71c263
fix(core): Document the webhook input envelope and check simulated-node parameters in AI Assistant verification (no-changelog) (#37962) 2026-09-11 08:05:32 +00:00
Matsu
bcad21f9b6
ci: Add some triggerable primitives for codespace workflows (#38404) 2026-09-11 08:00:58 +00:00
Romeo Balta
d7e50cda01
fix(core): Make profile update telemetry consistent with account creation (no-changelog) (#38290)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-11 07:51:35 +00:00
Declan Carroll
dd86cab539
ci: Shard lint by package group (no-changelog) (#38384) 2026-09-11 07:18:47 +00:00
Declan Carroll
20a1c7a8db
ci: Shard scoped CLI unit tests across runners (no-changelog) (#38385) 2026-09-11 07:18:37 +00:00
Declan Carroll
6271b56cf0
test: Reduce E2E stack readiness latency (#37934) 2026-09-11 07:12:49 +00:00
Charlie Kolb
2fd8f68989
test(Databricks Node): Test the async polling path without real delays (#38367)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-11 07:08:49 +00:00
Robin Braumann
5de6a4071a
feat(core): Add agent-directed episodic memory capture (#38134)
Co-authored-by: Michael Drury <michael.drury@n8n.io>
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-11 05:03:28 +00:00
n8n-assistant[bot]
6137e60fe5
chore: Update e2e impact map (#38400)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-09-11 03:15:30 +00:00
Robin Braumann
79f0e040c7
fix(editor): Hide Custom API Call option in node tool configuration (#38365)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-10 20:59:54 +00:00
Rúben Castro
9131e162fc
chore: Remove unnecessary comment in DataTableAggregateService (no-changelog) (#38315)
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-10 18:38:49 +00:00
Ricardo Espinoza
7ab15826d8
fix(core): Accept numeric pagination tokens from resource locator list methods (#38147) 2026-09-10 17:20:57 +00:00
Ricardo Espinoza
a1f84052f3
fix(core): Stop MCP workflow updates from dropping unknown operation fields (#38148) 2026-09-10 17:20:01 +00:00
n8n-cat-bot[bot]
ec267acdbc
test: Wait for backend readiness before the Playwright database reset (#38366)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: Declan Carroll <declan@n8n.io>
2026-09-10 16:48:50 +00:00
Stephen Wright
1abdc9222b
feat(Microsoft Teams Node): Add the Service Principal path for Online Meeting Create (#38320) 2026-09-10 16:26:42 +00:00
Marc Littlemore
604ef9a4b5
fix(editor): Update release notes URL to the new changelog page (no-changelog) (#37710)
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-10 16:08:10 +00:00
Charlie Kolb
302cf288a8
ci: Let a package ignore asset-only dependencies in test scoping (#38352)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-10 16:05:22 +00:00
Charlie Kolb
9648611b47
test(core): Assert the Postgres teardown timeout path without a long wait (#38369)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-10 16:03:01 +00:00
Raúl Gómez Morales
39dfe59d36
fix(editor): Show renamed credentials in the AI Assistant setup picker (#38295) 2026-09-10 16:01:24 +00:00
Anne Aguirre
93ca2d1fc0
fix(ai-builder): Guard against 429 errors on channel integrations (#38106)
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
2026-09-10 15:43:28 +00:00
Robin Braumann
bea96e54c3
feat(AI Agent Node): Add option to force a tool call on the first iteration (#38342)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-10 15:31:55 +00:00
Pierantonio Cangianiello
25d5fd974c
refactor(core): Expose the credential access check by id and narrow the global credential query (no-changelog) (#38251) 2026-09-10 15:27:28 +00:00
Jaakko Husso
ab101d53f8
fix(core): Select sandbox npm install flags by provider (#38358)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-10 15:16:21 +00:00
Suzu
b4fbcc5f6c
fix(editor): Clear tag loading state after failed requests (#37901) 2026-09-10 15:15:49 +00:00
Nour Alhadi Mahmoud
9f3f2be215
refactor(core): Keep isArchived in workflow.json and rename the sidecar to workflow-metadata.json (#38356)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-10 15:13:17 +00:00
Irénée
e62c16dc09
feat(core): Add v3 migration report rule for external secrets project roles (no-changelog) (#38215)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-09-10 15:03:28 +00:00
Sam Wooler
85cfc26799
docs(API): State the {resource}Id path param convention in Public API skill (#38312)
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-10 14:45:20 +00:00
Yash Rajeshbhai Darji
ef16421e42
fix(Microsoft Outlook Trigger Node): Fetch the latest message when executing manually (#34486)
Co-authored-by: yehorkardash <yehor.kardash@n8n.io>
2026-09-10 14:19:15 +00:00
n8n-cat-bot[bot]
ade15ea42d
chore: Rename the promotion connection path parameter from id to promotionConnectionId (#38288)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: Sam Wooler <swooler592@gmail.com>
2026-09-10 14:16:37 +00:00
Eugene
c7c731ac3b
fix(core): Clarify agent delegation and status checks (no-changelog) (#38339) 2026-09-10 13:53:56 +00:00
Predrag Ristic
429116f985
fix(core): Require verification coverage for every workflow trigger (#37961) 2026-09-10 13:43:01 +00:00
oleg
1bee3bcac7
feat(core): Add progressive workflow building (no-changelog) (#37996) 2026-09-10 13:42:43 +00:00
mfsiega
0c3c9d7beb
fix(core): Reconnect the Postgres Trigger on every publish through the publication service (#38211)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-10 13:37:25 +00:00
mfsiega
0a6709fac9
feat(core): Enable the workflow publication service by default (#37835)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-10 13:23:38 +00:00
Robin Braumann
7ac975c37a
fix(AI Agent Node): Preserve Anthropic thinking blocks with empty text across tool calls (#38302)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-10 13:00:09 +00:00
Robin Braumann
2beccada02
fix(core): Preserve link URLs in inbound agent chat messages (#38294)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-10 12:56:05 +00:00
n8n-cat-bot[bot]
30d44a5cd9
fix: Show credentials shared with all users and projects in the (#38287)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-10 12:40:58 +00:00
Charlie Kolb
763aeaaaaa
feat(editor): Add workflow dependencies submenu to the workflow menu (#37981)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-09-10 12:34:33 +00:00
Suzu
6a1ad2657f
fix(editor): Keep CSV imports tied to the selected file (#37906) 2026-09-10 12:20:55 +00:00
Uddish Verma
0ae8240e03
refactor(API): Migrate PUT and DELETE /projects/{projectId} to the decorator pattern (no-changelog) (#37977)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-10 12:08:10 +00:00
Rúben Castro
ef77996df1
refactor(core): Move node type policy DTOs to @n8n/api-types (#38238)
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-10 11:52:25 +00:00
oleg
0380ffccfd
feat(core): Trace AI Assistant sandbox operations (no-changelog) (#38170) 2026-09-10 11:48:08 +00:00
Rob Hough
a58b24f2c4
refactor(editor): Re-organize Agent sections (#38055) 2026-09-10 11:44:41 +00:00
Lorent Lempereur
3c7223a7c8
refactor(core): Run license auto-renewal as a system task (no-changelog) (#37549)
Co-authored-by: Veljko Terzic <veljko.terzic@n8n.io>
2026-09-10 11:33:48 +00:00
Matsu
7be94a1184
chore: Update eslint configuration (#38307)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-10 14:40:35 +03:00
mfsiega
4d0604e519
fix(core): Release the workflow publication lock after a bounded wait for abandoned trigger operations (no-changelog) (#38020)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-10 11:19:09 +00:00
Uddish Verma
0bff510085
refactor(API): Migrate GET and POST /projects to the decorator pattern (no-changelog) (#37971)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-10 11:17:35 +00:00
Mutasem Aldmour
219402cf4f
fix(core): Route "trigger my workflow" to the existing workflow, not the builder (#38073)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-10 11:12:44 +00:00
Mutasem Aldmour
2afb5b6d0d
fix(core): Ground provider OAuth scope guidance in n8n docs (#38066)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-10 11:03:02 +00:00
Declan Carroll
9135888f00
fix: Bump task-runner-launcher to 1.5.0 (#38309) 2026-09-10 12:20:48 +01:00
Andreas Fitzek
82229f3b7e
feat(core): Add observability for the decryption path (#38236) 2026-09-10 10:59:40 +00:00
n8n-cat-bot[bot]
78ea57db75
fix: Show custom role scope tooltips next to the hovered scope (#38284)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-10 10:48:27 +00:00
n8n-cat-bot[bot]
90176902f3
chore: Extract the repeated resource-URL to webhook-path preamble in the oauth-server resolvers (#38299)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-10 10:29:38 +00:00
n8n-cat-bot[bot]
481c494df0
chore: Move the duplicated CLI command error banner into BaseCommand (#38298)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-10 10:27:32 +00:00
Mike Repeć
75e44b65cb
fix(core): Point the Docker-only deprecation notices at the hosting docs (no-changelog) (#38279)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-10 10:03:55 +00:00
Svetoslav Dekov
1cfd0959e3
feat(core): Add the shared AI preference repository and resolver service (no-changelog) (#38174)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Co-authored-by: Roman Makowiak <roman.makowiak@n8n.io>
2026-09-10 09:59:30 +00:00
Pierantonio Cangianiello
23a64770f7
feat(engine): Add the control plane client that resolves credentials for the data plane (no-changelog) (#38123) 2026-09-10 09:57:11 +00:00
Emilia
d7c54142ed
test(editor): Add an e2e guard for the $env expression preview (#38042)
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-10 09:52:00 +00:00
n8n-cat-bot[bot]
cf6cb771e4
fix: Center the registration badge in the Usage and Plan heading (#38275)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-10 09:50:56 +00:00
Jan Kalkan
e803d4ba14
fix(core): Trust the CA from GIT_SSL_CAINFO for source control HTTPS remotes (#38258)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-10 09:42:39 +00:00
n8n-cat-bot[bot]
45a7f74c8e
fix: Close the variable modal on a backdrop click (#38286)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-10 09:41:42 +00:00
Sam Wooler
a33a23bb10
refactor(API): Migrate POST /source-control/pull to the decorator pattern (#38186) 2026-09-10 09:34:53 +00:00
n8n-cat-bot[bot]
21238959c7
refactor: Migrate GET /api/v1/variables to the decorator pattern (#38091)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-10 09:33:27 +00:00
Ilfat Mindubaev
eb4ea7f9c7
chore(Confluence Node): Polish error messages and validation (#38099)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-10 09:26:24 +00:00
Rúben Castro
a47f19b50d
fix(core): Scope data table Overview access by project role, not a global gate (#37676)
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-10 09:17:31 +00:00
Rick Gale
c410581c26
fix(editor): Strip Evaluation Trigger metadata fields from test case inputs (#37572)
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-10 09:08:19 +00:00
Tuukka Kantola
5807183432
feat: Rename AI Assistant to n8n Assistant (#38065)
Co-authored-by: Claude <noreply@anthropic.com>
2026-09-10 08:56:53 +00:00
Rob Hough
39a7baec89
feat(editor): Add preview chat utilities to the more options menu (#38210) 2026-09-10 08:51:28 +00:00
Thanasis G
4baf766e9f
feat(Databricks Node): Send partner User-Agent on Genie MCP traffic (#38068)
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
Co-authored-by: Yen Su <yen.su@n8n.io>
Co-authored-by: Yen Su <49794855+yens1@users.noreply.github.com>
2026-09-10 08:45:56 +00:00
n8n-cat-bot[bot]
092e53b46d
fix: Resolve node source path before the translation lookup (#38281)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-10 08:43:22 +00:00
José Braulio González Valido
44fae2aac9
fix(ai-builder): Give agent narration a larger judge budget than tool payloads (no-changelog) (#38192)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-10 08:37:07 +00:00
Sam Wooler
cbfc2576a3
feat(API): Let @Body require a body independently of DTO field optionality (#38237)
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-10 08:33:05 +00:00
James Martin
3527d059bd
fix(core): Fix flakey e2e test by awaiting debounce (no-changelog) (#38082) 2026-09-10 08:28:12 +00:00
n8n-cat-bot[bot]
b1943ff11f
chore: Remove corepack references from CONTRIBUTING.md (#38225)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-10 08:27:56 +00:00
Raúl Gómez Morales
564646cfac
feat(core): Add published flag to Instance AI eval seed workflows (no-changelog) (#38209) 2026-09-10 08:15:02 +00:00
Roman Makowiak
8a463dab3a
feat(editor): Add MCP nudge modal on JSON export/import and copy/paste (#38144)
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-10 08:09:09 +00:00
Jaakko Husso
c95756df7b
fix(core): Keep node settings, positions and error routes through an assistant workflow edit (#38254)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-10 08:08:54 +00:00
Stephen Wright
fdb31d2966
fix(Confluence Node): Match hyphenated and multi-word titles in the page picker search (#38175) 2026-09-10 07:48:54 +00:00
oleg
dd0e1f61bc
feat(core): Add setup panel verification and agent test execution (no-changelog) (#38031) 2026-09-10 07:46:46 +00:00
Pierantonio Cangianiello
a63e88e7c1
refactor(engine): Make the control plane token scope a parameter on both sides (no-changelog) (#37978) 2026-09-10 07:39:19 +00:00
Matsu
33960b0684
ci: Bump safe-chain to 1.5.20 (#38274) 2026-09-10 07:31:31 +00:00
n8n-cat-bot[bot]
76ac97faa9
chore: Migrate n8n provenance job to actions/attest (#38169)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-10 07:22:25 +00:00
Declan Carroll
42b9da7f9b
test: Add correlated startup telemetry (#37920) 2026-09-10 07:06:39 +00:00
Kai
a8933cec25
feat: Replace Git connection CLI commands with Promotions (#38027) 2026-09-10 06:43:42 +00:00
Jaakko Husso
4a6e625326
fix(core): Recognize always-loaded tools in agent tool discovery (#38204)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-10 06:38:34 +00:00
Veljko Terzic
3195750127
refactor(core): Round system task intervals in the runner only (no-changelog) (#38242) 2026-09-10 06:32:59 +00:00
Kai
d328bbf369
fix(core): Add model boundaries to external tool results (#37316)
Co-authored-by: r00gm <raul00gm@gmail.com>
2026-09-10 06:21:50 +00:00
n8n-cat-bot[bot]
47c59aa790
ci: Alert on E2E infrastructure workflow failure (#37595)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-10 06:18:48 +00:00
Svetoslav Dekov
cf8df4fa6c
feat(core): Add ai_preference table for AI preferences (#38131)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-10 05:22:31 +00:00
n8n-assistant[bot]
fdaac929fd
chore: Update e2e impact map (#38267)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-09-10 03:08:54 +00:00
Guillaume Jacquart
b4fee56db3
fix(core): Apply the SSO expression mapping env var in the provisioning loader (#37170)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-09 18:59:54 +00:00
Guillaume Jacquart
a88ffc3932
feat(core): Add node type availability endpoint for a project (no-changelog) (#38228)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-09 18:59:45 +00:00
Ricardo Espinoza
663bb3e6f8
fix(Postgres Node): Stop reporting parameter-loading errors to Sentry (#28865) 2026-09-09 18:31:09 +00:00
James Gee
cadfc58396
feat(core): Add a hashing package writer (no-changelog) (#38200) 2026-09-09 17:58:51 +00:00
James Gee
52141b2129
feat(core): List promote base branch files and hashes (no-changelog) (#38115) 2026-09-09 17:40:50 +00:00
Anne Aguirre
1ed3a24af2
fix(ai-builder): Enhance guidance UX for agents with no sessions on builder session tab (#38243) 2026-09-09 16:26:02 +00:00
n8n-cat-bot[bot]
d2d2f7b7da
refactor: Extract the duplicated N8N_ENV_FEAT_ flag check into a (#38183)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-09 16:08:40 +00:00
n8n-cat-bot[bot]
b93c6bc96e
chore: Pull the identical run-less getCredentials into the node execution context base (#38048)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-09 15:47:23 +00:00
n8n-cat-bot[bot]
e88301b65d
chore: Extract the duplicated mockNodeTypes stub shared by CredentialsHelper and CredentialsTester (#38168)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-09 15:46:51 +00:00
Robin Braumann
c5653f6668
fix(core): Consolidate secret redaction in @n8n/utils and redact more credential shapes (#38054)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-09 15:39:44 +00:00
Dhanunjay Gupta
92f702da1b
fix(core): Enforce execution timeout for stuck jobs in queue mode (#36258)
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
Co-authored-by: Emilia <100027345+sovietspaceship@users.noreply.github.com>
2026-09-09 15:31:39 +00:00
MHMD
3b6faa2d74
fix(core): Reuse an existing consent grant after the visitor authenticates mid-flow (#38084) 2026-09-09 15:27:58 +00:00
Emilia
0c6c9e3176
fix(core): Return luxon values from expressions as instances (#38034)
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-09 15:24:31 +00:00
Precious Amesinlola
9823c2ebdc
fix(core): Check Gateway credits eligibility against the correct sub-node (#38193)
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-09 15:04:17 +00:00
Robin Braumann
e6dea03f48
fix(editor): Prevent stale agent autosaves (#38087)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-09 14:21:53 +00:00
Eugene
99e9730a01
feat(core): Notify parent agents when background jobs settle (#37617)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-09 14:15:38 +00:00
Robin Braumann
fcf21f5efc
refactor(core): Share LLM output JSON extraction and tracing header stripping (no-changelog) (#38050)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-09 13:53:35 +00:00
Guillaume Jacquart
0b58c67de8
feat(core): Enable the policy infrastructure module by default (no-changelog) (#38224)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-09 13:42:18 +00:00
Robin Braumann
4b4d670206
fix(core): Keep nodes clear of the agent card in SDK-generated workflow layouts (#38093)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-09 13:16:03 +00:00
Mutasem Aldmour
4a3fc82d85
docs: Teach the Instance AI eval skill to reproduce a gap before reclassifying it (#38217)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-09 12:34:36 +00:00
Ilfat Mindubaev
0b8fd1e321
feat(Databricks Node): Refresh user-login tokens in the chat model during long executions (#37854)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-09 12:29:54 +00:00
Eugene
404d5870dd
feat(core): Schedule agent tasks via the durable scheduler behind a flag (no-changelog) (#37849)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Co-authored-by: Robin Braumann <robin.braumann@n8n.io>
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-09 12:29:02 +00:00
Jaakko Husso
efb5547368
feat(core): Stop Instance AI reporting a simulated run as fully verified (#38060)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-09 12:25:10 +00:00
Rúben Castro
eac5f9eccf
feat(core): Add node type policy project scope (no-changelog) (#38061)
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-09 12:00:39 +00:00
Benjamin Schroth
26afe95616
fix(core): Prevent false agent model stream stalls and show run errors in chat history (#37558)
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
2026-09-09 11:43:48 +00:00
Luigi Gubello
718b7f6fe4
chore: Update isolated-vm to 7.0.1 (#38207)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-09 11:38:03 +00:00
Mutasem Aldmour
4f05c24c7e
fix(editor): Hide the AI Assistant settings page when it has nothing to show (#38063)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-09 11:35:56 +00:00
mfsiega
db60f99a7a
fix(core): Reconnect the Email Trigger (IMAP) on every publish through the publication service (#38190)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-09 11:32:47 +00:00
Mike Repeć
f3db19df76
fix(core): Memoize pairedItem ancestry traversal (#38085)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Emilia <100027345+sovietspaceship@users.noreply.github.com>
2026-09-09 11:30:38 +00:00
Raúl Gómez Morales
80da0c8c21
fix(editor): Keep the artifact tab the user picks during an AI Assistant run (#38178) 2026-09-09 11:20:12 +00:00
Kai
179dbb35b6
feat(core): Move promotions services and API to the new model (#37965) 2026-09-09 11:19:56 +00:00
Raúl Gómez Morales
b3e1b7f46d
fix(editor): Show loading skeletons in the AI Assistant artifacts sidebar (#38180) 2026-09-09 11:01:17 +00:00
Raúl Gómez Morales
90f6eae720
feat(editor): Add setup panel shell above the Instance AI composer (no-changelog) (#37941)
Co-authored-by: Oleg Ivaniv <me@olegivaniv.com>
2026-09-09 10:42:06 +00:00
Matsu
dd3064db0c
ci: Point the workflow-scripts lockfile dir at .github/scripts (no-changelog) (#38198)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-09 13:57:21 +03:00
Jaakko Husso
b1f06033ba
perf(core): Stop copying user workflows into the Instance AI sandbox (#38135)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-09 10:38:32 +00:00
Jaakko Husso
ee8a77e663
perf(core): Install sandbox dependencies from the npm cache again (#38136)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-09 10:38:19 +00:00
Danny Martini
b3306b2206
fix(core): Forward the receiver in the lazy-proxy set trap (#37321)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-09-09 10:18:00 +00:00
Thanasis G
3d293b56ae
fix: Refresh the Confluence/Jira OAuth2 token automatically when it has expired (#37776)
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: Ilfat Mindubaev <ilfat.mindubaev@n8n.io>
2026-09-09 10:14:24 +00:00
Frank Prößdorf
41a5575411
feat(core): Support ephemeral sandboxes for the n8n sandbox service (#38185) 2026-09-09 10:05:05 +00:00
Rob Hough
06fc43366b
fix(editor): Rename the first agent builder tab to Build (#38184) 2026-09-09 10:04:42 +00:00
n8n-cat-bot[bot]
f60cb87e0e
chore: Extract the sixfold-duplicated isUnknownArray type guard into @n8n/utils (#38187)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-09 09:58:21 +00:00
Charlie Kolb
83997c7a7d
ci: Run the Postgres compatibility legs in the merge queue only (#37520)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-09-09 09:47:23 +00:00
Elias Meire
1776a515f2
fix(Email Trigger (IMAP) Node): Restore the connection after the server drops it (#36801) 2026-09-09 09:42:59 +00:00
Declan Carroll
6363d25a1b
test: Add harness contract baseline (#37913) 2026-09-09 09:33:53 +00:00
José Braulio González Valido
8ace2a3184
fix(core): Accept an object in the schema fields of the LangChain schema-driven nodes (no-changelog) (#38156)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-09 09:26:03 +00:00
Matt Kay
3d18cca043
fix(API): Report the full route path in public API usage telemetry (no-changelog) (#38152)
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-09 09:14:37 +00:00
Michael Drury
4d5d6112b7
fix(editor): Open AIA built agent previews in the chat panel and remove fullscreen mode (#37994) 2026-09-09 09:09:38 +00:00
oleg
0cfc3f5f28
feat(core): End the turn after post-build setup when the setup panel is on (no-changelog) (#37963) 2026-09-09 08:46:20 +00:00
mfsiega
cbda01aa6d
fix(core): Re-register the n8n Trigger on every publish through the publication service (#38153)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-09 08:36:47 +00:00
Rob Hough
a234034ca1
fix(editor): Align Instance AI input controls (#37162) 2026-09-09 08:35:39 +00:00
Rodrigo Santos da Silva
852c765e54
fix(editor): Refresh evaluations side panel when a run finishes (#38075) 2026-09-09 08:32:41 +00:00
Stephen Wright
dfbe574295
feat(Microsoft Teams Node): Add Online Meeting Create or Get operation (#37708) 2026-09-09 08:30:39 +00:00
Nour Alhadi Mahmoud
bd13ab4a6d
refactor(core): Move workflow publish and archive state out of workflow.json (#38116)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-09 08:27:22 +00:00
Milorad FIlipović
2f62e5f0a5
fix(editor): Fix Sticky node edit mode custom colors (no-changelog) (#37852) 2026-09-09 08:26:32 +00:00
Rob Hough
9ca366420c
feat(editor): Add N8nChatActions to @n8n/design-system (#36588) 2026-09-09 08:22:59 +00:00
Rob Hough
1f2e2a7695
chore:Update design owners to design engineering (no-changelog) (#38092) 2026-09-09 08:22:58 +00:00
Kay
19651defd9
fix(core): Merge system messages for strict providers (#37908)
Co-authored-by: Mutasem Aldmour <4711238+mutdmour@users.noreply.github.com>
2026-09-09 08:10:45 +00:00
Ali Elkhateeb
cb12be7151
feat(API): Add billable to Insights table (#36856)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-09 08:02:18 +00:00
James Gee
7e3b6b651b
feat(core): Add a shared entity file formatter for package exports (no-changelog) (#38112) 2026-09-09 08:01:36 +00:00
Lorent Lempereur
1d576f6d42
refactor(core): Run workflow maintenance jobs as system tasks (no-changelog) (#37548)
Co-authored-by: Veljko Terzic <veljko.terzic@n8n.io>
2026-09-09 07:58:57 +00:00
Milorad FIlipović
e28f5b5012
fix(core): Handle nodes without parameters in mcp tools (no-changelog) (#37742) 2026-09-09 07:48:47 +00:00
Alex Grozav
e510f0f33c
ci: Split the packages/modules test filters into frontend and backend (no-changelog) (#38121)
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-09 07:43:57 +00:00
n8n-cat-bot[bot]
9095e9a986
fix: Allow one icon file for the light and dark icon slots (#37711)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-09 07:17:11 +00:00
Kai
e77b7bb12f
feat(core): Add promotion schema entities and DTOs (#37939) 2026-09-09 07:17:09 +00:00
Nikhil Kuriakose
a0535781a7
fix(editor): Allow switching a toggle parameter from expression to fixed (#32762)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Ricardo Espinoza <ricardo@n8n.io>
2026-09-09 07:12:13 +00:00
Nikhil Kuriakose
0c82743a77
fix(core): Treat empty strings as empty in Data Table filters (#32887)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-09-09 06:55:58 +00:00
Matsu
0aefea55bf
build: Dedupe langchain deps and catch future cases (#38164) 2026-09-09 10:01:23 +03:00
Matsu
8e8daecba2
build(editor): Declare SCSS @use explicitly instead of injecting a prelude (#38071)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
2026-09-09 08:22:06 +03:00
Matsu
adae0b76b1
build(editor): Improve frontend builds (#37869)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
2026-09-09 08:21:17 +03:00
n8n-assistant[bot]
3afdf4a0f8
chore: Update e2e impact map (#38159)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-09-09 03:04:43 +00:00
n8n-cat-bot[bot]
4f874fc24b
test: Polyfill the jsdom dialog methods in the editor-ui test setup (#38146)
Some checks failed
Build: Benchmark Image / build (push) Has been cancelled
CI: Master (Build, Test, Lint) / Build for Github Cache (push) Has been cancelled
CI: Master (Build, Test, Lint) / Unit tests (24.18.1) (push) Has been cancelled
CI: Master (Build, Test, Lint) / Unit tests (26.5.1) (push) Has been cancelled
CI: Master (Build, Test, Lint) / Lint (push) Has been cancelled
CI: Master (Build, Test, Lint) / Performance (push) Has been cancelled
CI: Master (Build, Test, Lint) / Verify single-instance deps (npm-install) (push) Has been cancelled
Util: Publish API Schema / Cloudflare Pages (push) Has been cancelled
CI: Master (Build, Test, Lint) / Notify Slack on failure (push) Has been cancelled
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-08 20:02:56 +00:00
mfsiega
ecc4ba3fb8
fix(core): Unpublish workflows before a source control pull deletes them (#37967)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-08 18:09:32 +00:00
Andreas Fitzek
4b36c51940
fix(core): Repair legacy-format data-encryption keys during bootstrap (#38100) 2026-09-08 16:44:49 +00:00
Mike Repeć
3570091474
chore(core): Deprecate internal task runner mode and warn about SSRF default block list changes (no-changelog) (#38103)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-08 16:30:45 +00:00
Jasper V.
6f5269acff
feat(HTTP Request Node): Add support for PROPFIND, MKCOL, MOVE, COPY and REPORT methods (#24151) 2026-09-08 15:44:19 +00:00
Alex Grozav
0a286c0530
refactor(editor): Extract insights into a frontend module package (no-changelog) (#37030)
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: multica-agent <github@multica.ai>
2026-09-08 15:41:15 +00:00
Uddish Verma
b482979c14
refactor(API): Give @Param a Zod schema to validate and document path parameters (#37952)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 15:35:57 +00:00
Raúl Gómez Morales
899d52d795
fix(core): Ask which MCP connector to set up before the AI Assistant gives a URL (#37992) 2026-09-08 15:35:54 +00:00
Mike Repeć
7e152a48bd
fix(editor): Restore tag text hidden by zero line-height (#38107)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-08 15:08:32 +00:00
Rob Hough
41621ca0bd
chore(editor): Update design-system skills and guidelines (no-changelog) (#37109) 2026-09-08 15:00:56 +00:00
Arvin A
31b08b75b8
feat(core): Add the instance-context block and activity tool (no-changelog) (#37867) 2026-09-08 14:44:47 +00:00
Declan Carroll
28061c08e3
ci: Add nightly SBOM license validation (no-changelog) (#38077) 2026-09-08 14:30:36 +00:00
Declan Carroll
1ad0b44a68
build: Move the monorepo and Docker deploys to pnpm 12 (no-changelog) (#37899)
Co-authored-by: Matsu <matias.huhta@n8n.io>
2026-09-08 15:15:57 +01:00
Robin Braumann
b7a58c25c1
fix(core): Treat Instance AI model stream stalls as dropped provider connections (#38079)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-08 13:43:03 +00:00
Robin Braumann
f3196f1100
fix(editor): Center modal dialogs vertically in the viewport (#38081) 2026-09-08 13:42:14 +00:00
Theodore Nastos
541440391e
fix(core): Keep a resumed execution in the trace of the parked execution (#38033) 2026-09-08 13:22:33 +00:00
Charlie Kolb
a3f53d66f5
fix: Keep dev-metrics shims off pnpm-managed binaries (no-changelog) (#37982)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-09-08 13:16:20 +00:00
Nour Alhadi Mahmoud
793136b7e1
fix: Enforce protected instance execution restrictions (#37947)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-08 12:56:27 +00:00
Mike Repeć
7e3d5e04b9
feat: Enable the v3 migration report and fix findings table layout (no-changelog) (#38058) 2026-09-08 12:52:46 +00:00
Yen Su
8c623260fc
feat(Microsoft Teams Node): Add the chat member add operation (#37746) 2026-09-08 12:48:42 +00:00
Raúl Gómez Morales
229405b1cc
fix(editor): Remove legacy AI Workflow Builder references from AI usage settings (#38056) 2026-09-08 12:48:07 +00:00
Eugene
588543cba8
feat(core): Store parent identity on background jobs (#37752)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-08 12:47:30 +00:00
Mutasem Aldmour
8cb5159fa0
fix(editor): Stop rendering task and sub-agent labels with no text (#38059)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 12:46:03 +00:00
Robin Braumann
b6b5ecdc4b
fix(core): Retry Instance AI model turns when the provider stream closes without output (#38080)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-08 12:45:55 +00:00
Robin Braumann
57f84d05d2
refactor(core): Share Zod to JSON Schema conversion through @n8n/ai-utilities (no-changelog) (#38051)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-08 12:42:56 +00:00
Eugene
c0f6471afa
fix(core): Execute MCP toolkit members on workers (#38069) 2026-09-08 12:36:40 +00:00
Sandra Zollner
c3f1981823
feat(core): Add workflow selection to project package export (#37611)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-08 12:21:20 +00:00
Raúl Gómez Morales
d4f7672f45
feat(core): Add structured output parser validator to workflow SDK (#37983) 2026-09-08 12:18:21 +00:00
Rob Hough
af2bd808be
fix(editor): Truncate long AIA context chips (#37878) 2026-09-08 12:16:13 +00:00
Romeo Balta
8aa6e9810f
fix(editor): Attribute PostHog events to the logged-in user (no-changelog) (#35977)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 12:06:02 +00:00
Robin Braumann
4015329654
refactor(core): Import agents types and helpers from @n8n/agents instead of the instance-ai barrel (no-changelog) (#38053)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-08 12:05:50 +00:00
Robin Braumann
cf999248f9
refactor(core): Replace local isRecord and model-id helper copies with shared ones (no-changelog) (#38052)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-08 12:05:27 +00:00
Guillaume Jacquart
56383fc004
fix(core): Register redaction context hook on all instance types (#37298)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 12:03:53 +00:00
Tomi Turtiainen
9b464baf76
feat(engine): Route poll trigger runs to the engine 2.0 data plane (#37803) 2026-09-08 11:51:36 +00:00
Emilia
3521e54b81
fix(core): Count executions that crash recovery marks as crashed (#37733)
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-08 11:29:15 +00:00
Tuukka Kantola
6acdf815ea
chore(editor): Allow narrower Assistant chat panels (#37861)
Co-authored-by: r00gm <raul00gm@gmail.com>
2026-09-08 11:26:55 +00:00
Stephen Wright
ab38a17bd4
feat(Microsoft Teams Node): Add Online Meeting Update operation (#37707) 2026-09-08 11:15:04 +00:00
Lorent Lempereur
dde72eed75
refactor(core): Run AI checkpoint pruning as system tasks (no-changelog) (#37544)
Co-authored-by: Veljko Terzic <veljko.terzic@n8n.io>
2026-09-08 11:08:15 +00:00
Michael Drury
8ace5381ec
fix(editor): Merge native agent tools into n8n nodes (#38057) 2026-09-08 10:55:00 +00:00
Tomi Turtiainen
18c386b071
feat(engine): Route active trigger runs to the engine 2.0 data plane (#37726) 2026-09-08 10:52:58 +00:00
Guillaume Jacquart
aa91e71893
fix(API): Let global members grant folder scopes to API keys (#37654)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 10:50:16 +00:00
Predrag Ristic
418f85e9eb
refactor(core): Remove legacy Instance AI eval agent (no-changelog) (#36740) 2026-09-08 10:14:32 +00:00
Mutasem Aldmour
2550501caa
fix(core): Allow a fallback model alongside the primary on AI nodes (#38007)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 09:53:58 +00:00
Raúl Gómez Morales
37efe11502
fix(core): Stop the workflow SDK collapsing inline branch chains to their branching node (#37717) 2026-09-08 09:46:47 +00:00
Svetoslav Dekov
5cfae1ce62
chore: Fix seed assistant threads so the assistant page can list them (no-changelog) (#37863)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-08 09:24:24 +00:00
n8n-cat-bot[bot]
bbc8e83e5d
chore: Share the workflow-node task payload type and guard between the schedule- and poll-trigger tasks (#38038)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-08 09:24:23 +00:00
n8n-cat-bot[bot]
a51e6a0455
chore: Reuse one helper for the triplicated scheduler task-identity span attributes (#38037)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-08 09:23:16 +00:00
Robin Braumann
79d936fa5e
fix(core): Batch LangSmith agent session exports (#38030)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-08 09:04:15 +00:00
Matsu
04c82c0586
ci: Retarget PRs to bundle branches on sync (#38022) 2026-09-08 09:04:07 +00:00
Charlie Kolb
31f24f0f45
ci: Report a missing required review as pending, not failure (#38036)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-09-08 09:01:48 +00:00
Stephen Wright
ea73209a1d
feat(Microsoft Teams Node): Add Online Meeting Delete operation (#37502) 2026-09-08 08:38:04 +00:00
mfsiega
52127777e1
fix(core): Bound the wait for the workflow publication lock (#38019)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-08 08:35:30 +00:00
Declan Carroll
362311942d
ci: Improve multi-architecture manifest publishing (no-changelog) (#37894) 2026-09-08 08:24:10 +00:00
Robin Braumann
7383fd7e89
fix(Azure OpenAI Chat Model Node): Resolve the proxy against the Azure endpoint so NO_PROXY applies (#38026)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-08 08:13:29 +00:00
Alex Grozav
7718f45b00
docs(editor): Add the frontend module guide next to the module CLI (no-changelog) (#37004)
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-08 08:12:00 +00:00
n8n-release-tag-merge[bot]
2661f4de38 Merge tag 'n8n@2.39.0' 2026-09-08 08:25:47 +00:00
n8n-assistant[bot]
1620ec42ca
🚀 Release 2.39.0 (#38029)
Co-authored-by: Matsuuu <16068444+Matsuuu@users.noreply.github.com>
2026-09-08 11:04:45 +03:00
Ali Elkhateeb
53ecf6b6be
fix(core): Do not persist an execution when preExecute throws (#37701)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-08 08:00:49 +00:00
Benjamin Schroth
68d5deba65
feat(editor): Add manual agent creation entry point on the agents pages (#37702) 2026-09-08 07:53:11 +00:00
Robin Braumann
80577ac3b0
feat(core): Share the parent workspace sandbox with background sub-agents (#38025)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-08 07:34:37 +00:00
n8n-release-tag-merge[bot]
4223e6172a Merge tag 'n8n@2.38.0' 2026-09-08 07:40:20 +00:00
Mike Repeć
7ce17be560
fix(core): Keep reporting the original job error when the job key is gone (#37993)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-09-08 07:26:07 +00:00
Lorent Lempereur
52befceb66
refactor(core): Run insights compaction and pruning as system tasks (no-changelog) (#37543)
Co-authored-by: Veljko Terzic <veljko.terzic@n8n.io>
2026-09-08 07:15:14 +00:00
n8n-cat-bot[bot]
cbaa0be44d
fix: Use consistent personal space icon in the Instance AI artifacts (#38011)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: Jeroen van der Poll <74372991+jvanderpoll@users.noreply.github.com>
2026-09-08 07:03:26 +00:00
n8n-assistant[bot]
9c8a0f7dc1
chore: Update e2e impact map (#38017)
Some checks are pending
CI: Master (Build, Test, Lint) / Build for Github Cache (push) Waiting to run
CI: Master (Build, Test, Lint) / Unit tests (24.18.1) (push) Waiting to run
CI: Master (Build, Test, Lint) / Unit tests (26.5.1) (push) Waiting to run
CI: Master (Build, Test, Lint) / Lint (push) Waiting to run
CI: Master (Build, Test, Lint) / Performance (push) Waiting to run
CI: Master (Build, Test, Lint) / Verify single-instance deps (npm-install) (push) Waiting to run
CI: Master (Build, Test, Lint) / Notify Slack on failure (push) Blocked by required conditions
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-09-08 02:58:41 +00:00
Roman Makowiak
9769dacbe3
feat(core): Add folder exploration to Instance AI (#37865)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-07 20:52:53 +00:00
n8n-cat-bot[bot]
b5072af998
chore: Share the data-table row file prefix between the export and import services (#38006)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-07 19:43:54 +00:00
Savelii
e12dc06a44
feat(core): Store signing deployment keys in the same wrapped form as data-encryption keys (#37953)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-09-07 18:21:34 +00:00
Predrag Ristic
082008363b
fix(editor): Restore Instance AI draft when thread creation fails (#37874) 2026-09-07 17:56:50 +00:00
Robin Braumann
19eba4305b
feat(core): Index agent workflow tool dependencies and invalidate dependent runtimes (#37857)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-07 17:14:03 +00:00
Andreas Fitzek
93945cce5d
fix(core): Register the encryption key provider before any encrypt or decrypt (no-changelog) (#37972)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-09-07 17:04:39 +00:00
Dimitri Lavrenük
a716209294
feat: Add node output schemas harvested from test files (no-changelog) (#37846) 2026-09-07 16:36:41 +00:00
Thanasis G
6fa5b3d292
fix: Sign Microsoft Dynamics OAuth2 requests with the access token (#37855)
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-07 16:24:47 +00:00
n8n-cat-bot[bot]
9a41c66852
chore: Extract the duplicated stringifyQuery helper in the public API data-table handlers (#38001)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-07 16:11:02 +00:00
n8n-cat-bot[bot]
5e3a74f8b2
chore: Small upgrade for js-yaml - license-server (#37999)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-07 16:04:59 +00:00
Rúben Castro
f2109fbb83
fix(core): Make node type policy writes safe under concurrent requests (no-changelog) (#37955)
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-07 15:57:58 +00:00
Tomi Turtiainen
07d1cad441
chore: Add publication module ownership to catalysts (no-changelog) (#37998) 2026-09-07 15:43:45 +00:00
Sam Wooler
21755d2bf7
feat(API): Add push endpoint to the public API for source control (#37873) 2026-09-07 15:32:31 +00:00
Eugene
9418c23b81
feat(core): Declare agents as a scheduled job owner (no-changelog) (#37848)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Co-authored-by: Emilia <100027345+sovietspaceship@users.noreply.github.com>
2026-09-07 15:28:55 +00:00
n8n-cat-bot[bot]
fc2bea75db
chore: Extract the triplicated capability-count telemetry properties in the agents module (#37986)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-07 15:06:02 +00:00
Tomi Turtiainen
10bc6ca351
feat(engine): Store the workflow that ran with each engine 2.0 execution (#37858) 2026-09-07 15:05:31 +00:00
Michael Drury
5f13016d06
fix(core): Attribute agent session metrics to users (no-changelog) (#37988) 2026-09-07 15:03:45 +00:00
Konstantin Tieber
82d348002b
feat(core): Add Instance reporting module (#36796) 2026-09-07 14:56:32 +00:00
Benjamin Schroth
085a7a980d
fix(core): Make pending agent creation and thread binding idempotent (#37074) 2026-09-07 14:56:21 +00:00
Alex Grozav
e974e5e22e
docs(editor): Document @n8n/design-system consumption in its README (no-changelog) (#37017)
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-07 14:37:42 +00:00
Robin Braumann
60db997f5b
feat(core): Reject the agent publish with the names of unpublished workflow tools (#37889)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-07 14:24:18 +00:00
MHMD
188e285069
feat(core): Make chat trigger OAuth2 identity pipeline unconditional (#37942) 2026-09-07 13:19:34 +00:00
ocdejong
1ab9382789
perf(core): Bound peak memory during source control push (#37862)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-07 13:15:41 +00:00
n8n-cat-bot[bot]
f5a8ac260c
chore: Share the Slack setup constants and the managed-app cache key between the two Slack setup services (#37968)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-07 13:06:01 +00:00
Yiğit ERDOĞAN
5ae18613fd
fix(Freshservice Node): Send change filters as dedicated query params (#34380)
Co-authored-by: Elias Meire <elias@meire.dev>
2026-09-07 12:57:36 +00:00
Robin Braumann
7533aaaa2a
fix(Mistral Cloud Chat Model Node): Handle Request objects in undici-backed proxyFetch (#37946)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-07 12:55:23 +00:00
Robin Braumann
49e7821ff4
fix(core): Report the provider response when a chat model returns no choices (#37959)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-07 12:49:41 +00:00
Lorent Lempereur
9a56e9bd79
refactor(core): Run instance registry maintenance as system tasks (no-changelog) (#37545)
Co-authored-by: Veljko Terzic <veljko.terzic@n8n.io>
2026-09-07 12:17:35 +00:00
Alex Grozav
27d6420502
refactor(editor): Move keyless single-consumer modal children into features/workflows (no-changelog) (#37937)
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-07 12:11:53 +00:00
Raúl Gómez Morales
8119ea357b
fix(editor): Refresh workflow checksum after publishing from an embedded editor (#37949) 2026-09-07 12:04:51 +00:00
Robin Braumann
309fa8d9c5
fix(Anthropic Chat Model Node, AI Agent Node): Send disabled thinking to the provider and parse content-block output (#37954)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-07 11:58:43 +00:00
Alex Grozav
259e575b48
refactor(editor): Remove unread locale keys (no-changelog) (#36345)
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-07 11:42:17 +00:00
Alex Grozav
26ea74bc39
build(editor): Give the frontend module template the design-system plugins and stylelint (no-changelog) (#37101)
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: multica-agent <github@multica.ai>
2026-09-07 11:29:55 +00:00
Raúl Gómez Morales
36426a8eb8
fix(core): Return 503 when the AI assistant service is unreachable (#37957) 2026-09-07 11:10:32 +00:00
Lorent Lempereur
821b102ce7
refactor(core): Run token exchange maintenance as system tasks (no-changelog) (#37547)
Co-authored-by: Veljko Terzic <veljko.terzic@n8n.io>
2026-09-07 10:37:06 +00:00
Pierantonio Cangianiello
0f86167947
feat(engine): Prepare engine v2 for credential resolution (no-changelog) (#37876)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
2026-09-07 10:34:43 +00:00
Yen Su
42dcc709f7
feat(Microsoft Teams Node): Add the chat member get many operation (#37745) 2026-09-07 10:24:43 +00:00
Sam Wooler
6f64dc6ae3
refactor(API): Migrate GET /credentials to the decorator pattern (no-changelog) (#37556) 2026-09-07 10:19:53 +00:00
Michael Kret
c29247614e
fix(editor): Hide Gateway credits for HTTP Request and GraphQL nodes (#37810) 2026-09-07 10:08:22 +00:00
Nicolas Santiago Moreno Monroy
9f2ebe5650
fix(Jira Software Node): Send expand as comma-separated string for Jira Cloud searches (#37527) 2026-09-07 10:03:34 +00:00
Guillaume Jacquart
d18e719e29
chore(core): Make the workflow seal lint rule type-aware (no-changelog) (#37785)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-07 09:36:55 +00:00
Michael Drury
a0df5b34aa
fix: Refresh Agent MCP OAuth credentials (no-changelog) (#37828) 2026-09-07 09:27:56 +00:00
Raúl Gómez Morales
bb5b910ea1
feat(editor): Add setup panel apply paths for AI-built workflows (no-changelog) (#37638) 2026-09-07 09:27:44 +00:00
Rúben Castro
9491b328ab
feat(core): Add node-type-policy service, instance controller, and audit wiring (no-changelog) (#37648)
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-07 09:13:32 +00:00
Stephen Wright
d5e05b2cbe
fix(Microsoft Teams Node): Send Online Meeting times in UTC and share the Graph helpers (#37723) 2026-09-07 09:12:20 +00:00
MHMD
369c9e2cd3
fix(Chat Trigger Node): Fix singular copy on the connect bar (#37945) 2026-09-07 09:10:10 +00:00
Sebastien Powell
c9c06d60f7
chore(editor): Add Motion playground to Storybook docs (no-changelog) (#37845) 2026-09-07 09:09:00 +00:00
Charlie Kolb
eef87db07b
ci: Run DB migration tests only when a migration-relevant path changes (#37518)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-09-07 08:51:01 +00:00
n8n-cat-bot[bot]
2e78aacef3
refactor(core): Use the shared getErrorMessage util across the Instance AI code (no-changelog) (#37566)
Co-authored-by: Emilia <100027345+sovietspaceship@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-07 08:12:34 +00:00
Robin Braumann
03e1793368
fix(core): Make scoped workspace path errors actionable for agents (#37887)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-07 08:09:20 +00:00
Shaurav Vora
feb8c57d30
feat(Edit Image Node): Add horizontal and vertical text alignment options (#34505)
Co-authored-by: Elias Meire <elias@meire.dev>
2026-09-07 07:38:26 +00:00
RomanDavydchuk
5ddfb6086a
fix(Microsoft SQL Node): Fix TLS error when the IP is used to connect to the server (#37870) 2026-09-07 07:36:30 +00:00
Theo Gravity
c9456333f5
feat(Airtop Node): Support running an agent with a specific browser profile (#34933)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Elias Meire <elias@meire.dev>
2026-09-07 07:34:37 +00:00
Declan Carroll
73ad071adf
build: Deploy production closures without pnpm deploy --legacy (no-changelog) (#37890) 2026-09-07 07:01:04 +00:00
Charlie Kolb
30be927af8
ci: Narrow the DB test trigger to the files the DB legs run (#37517)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-09-07 06:12:21 +00:00
Declan Carroll
fb7590db16
ci: Update Blacksmith checkout to v1.5.0 (#37898)
Some checks are pending
CI: Master (Build, Test, Lint) / Build for Github Cache (push) Waiting to run
CI: Master (Build, Test, Lint) / Unit tests (24.18.1) (push) Waiting to run
CI: Master (Build, Test, Lint) / Unit tests (26.5.1) (push) Waiting to run
CI: Master (Build, Test, Lint) / Lint (push) Waiting to run
CI: Master (Build, Test, Lint) / Performance (push) Waiting to run
CI: Master (Build, Test, Lint) / Verify single-instance deps (npm-install) (push) Waiting to run
CI: Master (Build, Test, Lint) / Notify Slack on failure (push) Blocked by required conditions
2026-09-07 05:13:25 +00:00
n8n-assistant[bot]
d348ec1945
chore: Update e2e impact map (#37929)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-09-07 03:05:38 +00:00
n8n-assistant[bot]
552881a8a7
chore: Update node popularity data (#37928)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-09-07 01:03:01 +00:00
Tomi Turtiainen
e8b8694a50
feat(engine): Route webhook runs to the engine 2.0 data plane (#37657)
Some checks failed
CI: Master (Build, Test, Lint) / Build for Github Cache (push) Waiting to run
CI: Master (Build, Test, Lint) / Unit tests (24.18.1) (push) Waiting to run
CI: Master (Build, Test, Lint) / Unit tests (26.5.1) (push) Waiting to run
CI: Master (Build, Test, Lint) / Lint (push) Waiting to run
CI: Master (Build, Test, Lint) / Performance (push) Waiting to run
CI: Master (Build, Test, Lint) / Verify single-instance deps (npm-install) (push) Waiting to run
CI: Master (Build, Test, Lint) / Notify Slack on failure (push) Blocked by required conditions
Build: Benchmark Image / build (push) Has been cancelled
Util: Update Node Popularity / update-popularity (push) Has been cancelled
Util: Update Credential Setupability / update-setupability (push) Has been cancelled
Util: Update Node Popularity / approve-and-automerge (push) Has been cancelled
Util: Update Credential Setupability / approve-and-automerge (push) Has been cancelled
2026-09-06 11:54:27 +00:00
Alex Grozav
95ee3f4d91
feat(editor): Let frontend modules register parameter input types (no-changelog) (#37100)
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: multica-agent <github@multica.ai>
2026-09-06 10:24:24 +00:00
n8n-assistant[bot]
7cb77fbed6
chore: Update e2e impact map (#37918)
Some checks are pending
CI: Master (Build, Test, Lint) / Build for Github Cache (push) Waiting to run
CI: Master (Build, Test, Lint) / Unit tests (24.18.1) (push) Waiting to run
CI: Master (Build, Test, Lint) / Unit tests (26.5.1) (push) Waiting to run
CI: Master (Build, Test, Lint) / Lint (push) Waiting to run
CI: Master (Build, Test, Lint) / Performance (push) Waiting to run
CI: Master (Build, Test, Lint) / Verify single-instance deps (npm-install) (push) Waiting to run
CI: Master (Build, Test, Lint) / Notify Slack on failure (push) Blocked by required conditions
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-09-06 02:57:56 +00:00
n8n-assistant[bot]
33eb5c196e
chore: Update e2e impact map (#37902)
Some checks are pending
CI: Master (Build, Test, Lint) / Build for Github Cache (push) Waiting to run
CI: Master (Build, Test, Lint) / Unit tests (24.18.1) (push) Waiting to run
CI: Master (Build, Test, Lint) / Unit tests (26.5.1) (push) Waiting to run
CI: Master (Build, Test, Lint) / Lint (push) Waiting to run
CI: Master (Build, Test, Lint) / Performance (push) Waiting to run
CI: Master (Build, Test, Lint) / Verify single-instance deps (npm-install) (push) Waiting to run
CI: Master (Build, Test, Lint) / Notify Slack on failure (push) Blocked by required conditions
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-09-05 03:05:34 +00:00
Alex Grozav
909a6bc60a
refactor(editor): Make the otel module descriptor import-light (no-changelog) (#37768)
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-04 19:48:22 +00:00
Savelii
44d9d908b2
feat(core): Add encryption usage guardrails (no-changelog) (#37728)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-09-04 19:24:29 +00:00
Guillaume Jacquart
441970b211
feat(core): Log a decision audit line when a policy blocks an action (no-changelog) (#37880)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-04 16:58:49 +00:00
Garrit Franke
4b0de25a08
fix(core): Always redact OTel header values in settings responses (#37283) 2026-09-04 15:43:57 +00:00
Emilia
afc4a54798
fix(core): Wait for detached sub-workflows before the worker stops its task runner (#37737)
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-04 15:41:31 +00:00
Emilia
6828320862
fix(core): Allow webhook nodes to set Set-Cookie response headers (#37883) 2026-09-04 15:41:11 +00:00
MHMD
64ba49d96c
fix(Chat Trigger Node): Re-authenticate hosted chat on session change (#37868) 2026-09-04 15:19:22 +00:00
James Gee
fed5d70460
fix(core): Allow HashiCorp Vault external secrets to read a KV v2 sub-path (#37748) 2026-09-04 15:19:00 +00:00
Bernhard Wittmann
5d355a657f
perf(Supabase Node): Fetch the API schema once for all column dropdowns (#37756) 2026-09-04 15:12:48 +00:00
Jan Kalkan
f56d87d786
fix(editor): Show restart notice after license activation (#37841) 2026-09-04 15:10:52 +00:00
Mike Repeć
2ad8d013f2
fix(core): Stop task broker before task runner processes on shutdown (#37833)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-09-04 15:06:26 +00:00
Guillaume Jacquart
8f3617e9c3
docs(core): Correct and extend the policy infrastructure README (#37879)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-04 14:55:09 +00:00
Svetoslav Dekov
9a80b14fff
feat(core): Allow Instance AI to search past conversations (#37478)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-09-04 14:48:24 +00:00
n8n-cat-bot[bot]
8109f22098
ci: Cache the pnpm executable in setup-nodejs (no-changelog) (#37843)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: Declan Carroll <declan@n8n.io>
2026-09-04 14:29:04 +00:00
Predrag Ristic
500ea3942b
fix(core): Prefer service-specific credentials for HTTP requests (#37844) 2026-09-04 14:22:13 +00:00
ocdejong
f748613f9a
feat(core): Add OTLP gRPC protocol support for OpenTelemetry tracing (#37491)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-09-04 14:19:55 +00:00
n8n-cat-bot[bot]
e4e78da0d6
chore: DEVP-1038 · c2: Add --test-files forwarding and CLI Stryker scope config (#37679)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-04 14:17:04 +00:00
oleg
7ac335d94b
fix(core): Stop npm audit from stalling Instance AI sandbox setup (#37864) 2026-09-04 14:11:49 +00:00
Luka Zivkovic
fe3d57e9da
docs: Add optional eval source observations (#37798) 2026-09-04 14:10:41 +00:00
n8n-cat-bot[bot]
ec006a4448
refactor: Extract the shared trigger-node lookup in the scheduler (#37703)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-04 14:05:47 +00:00
Lorent Lempereur
7b08fef9c6
refactor(core): Run MCP registry refresh as a system task (no-changelog) (#37546)
Co-authored-by: Veljko Terzic <veljko.terzic@n8n.io>
2026-09-04 14:03:50 +00:00
Yen Su
6c55d73d6d
fix(Databricks Node): Drop the version from the partner User-Agent (no-changelog) (#37829)
Co-authored-by: Thanasis Gkliatis <thanasis.gkliatis@n8n.io>
Co-authored-by: Thanasis G <96360514+gthanasis@users.noreply.github.com>
2026-09-04 13:54:16 +00:00
Sandra Zollner
c5f06cf51c
feat(editor): Remove preview tag for reviews in nav bar (#37816) 2026-09-04 13:38:40 +00:00
Jan Kalkan
217c7b3aca
fix(core): Keep a serving external secrets provider active when its replacement fails (#37781)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-04 13:36:18 +00:00
Matt Kay
e6d8eddf9d
feat(API): Return data table size on data table read endpoints (#37078)
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: Sam Wooler <swooler592@gmail.com>
2026-09-04 13:29:24 +00:00
Matsu
eeec778a88
ci: Add an opt in label that automatically provisions a codespace (#37800)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-04 13:19:24 +00:00
Sandra Zollner
90d176b969
feat(editor): Add UI to select workflow changes to promote (no-change… (#37530)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-04 13:06:06 +00:00
Predrag Ristic
be678fcb69
feat(core): Guide Instance AI credential selection (#37447) 2026-09-04 13:04:42 +00:00
Robin Braumann
199433204f
fix(core): Harden agent workflow tools against unpublished, missing, or incompatible workflows (#37772)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-04 13:03:53 +00:00
oleg
5951b009e0
feat(core): Emit setup items early instead of suspending mid-build (no-changelog) (#37700) 2026-09-04 12:50:50 +00:00
Michael Drury
a9d5fb743b
fix(editor): Show skill size limits as a character count (#37847)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-04 12:28:17 +00:00
Uddish Verma
c8b9fed853
refactor(API): Migrate the executions retry endpoint to the decorator pattern (no-changelog) (#37667)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-04 12:26:19 +00:00
yehorkardash
5583ba4e2c
feat(core): Support existing credentials in MCP registry (#37235)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-04 12:25:00 +00:00
Kai
98dfb30509
fix(core): Take archived workflows into account during package import (#37668) 2026-09-04 12:20:21 +00:00
Rob Hough
20cf19f453
fix(editor): Update Agent builder assistant placeholder (#37529) 2026-09-04 12:19:38 +00:00
Arvin A
16992f4d2c
feat(core): Record instance activity behind the write flag (no-changelog) (#37750) 2026-09-04 12:16:46 +00:00
sandbender3000
e9f366dfc1
feat(G Suite Admin Node): Add "orgUnitPath" option to User create and update operations (#20108)
Co-authored-by: Dimitri Lavrenük <20122620+dlavrenuek@users.noreply.github.com>
Co-authored-by: Dimitri Lavrenük <dimitri.lavrenuek@n8n.io>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-04 12:16:06 +00:00
Sam Wooler
daab1959ca
feat(API): Add source control status endpoint to the public API (#37001) 2026-09-04 12:15:26 +00:00
n8n-cat-bot[bot]
52499b929e
refactor: Use one shell escape helper across the agents sandbox (#37806)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-04 12:12:51 +00:00
Csaba Tuncsik
187d504ab7
chore: Rewrite the cubic Frontend review agent rules (#37458) 2026-09-04 12:08:24 +00:00
Guillaume Jacquart
2b69e99e9e
feat(core): Enforce content-import policy on package import (no-changelog) (#37762)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-04 12:06:48 +00:00
Rob Hough
185bf3e2ca
feat(editor): Add agent card chat preview action (#37812) 2026-09-04 12:00:43 +00:00
n8n-cat-bot[bot]
56ed714e0c
chore: Use the shared truncate util instead of per-file copies in instance-ai and ai-workflow-builder (#37807)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-04 11:33:00 +00:00
Nour Alhadi Mahmoud
285b730cfe
feat(API): Name exported package entities by slug and id (#37656)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-04 11:24:33 +00:00
Mike Repeć
325feb3ba6
fix(core): Update Qdrant client and remove undici v6 dependencies so AI requests work on Node 26 (#37758)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-09-04 11:19:03 +00:00
Eugene
cc203b1333
feat(core): Track waiting workflow tools as background jobs (#37284)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-09-04 11:17:23 +00:00
Bernhard Wittmann
3c48b53392
fix(Read/Write Files from Disk Node): Allow glob character classes in File(s) Selector (#36901) 2026-09-04 11:16:12 +00:00
Savelii
003fcf3190
feat(core): Cache the encryption keys in memory (#37753)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-09-04 10:57:01 +00:00
Tomi Turtiainen
853344a078
docs: Adjust ADR title and filename (#37842) 2026-09-04 10:53:56 +00:00
Mike Repeć
8272a8bc33
feat(core): Add project-level worker pools (no-changelog) (#32975)
Co-authored-by: mfsiega <93014743+mfsiega@users.noreply.github.com>
Co-authored-by: Tomi Turtiainen <10324676+tomi@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Michael Siega <michael.siega@n8n.io>
2026-09-04 10:19:28 +00:00
Savelii
8399317ccb
fix(editor): Scope end-user credential trigger warning to reachable nodes (#37081)
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-04 10:15:44 +00:00
Bernhard Wittmann
ddf6534fcc
fix(AMQP Trigger Node): Reattach the receiver link when the broker detaches it (#37431) 2026-09-04 09:50:07 +00:00
Arvin A
ac432dd66a
chore: Add dev tooling to seed an instance and inspect its activity feed (#37645) 2026-09-04 09:49:31 +00:00
Dimitri Lavrenük
a3a8863626
feat(core): Add output-schema coverage and harvest tooling (no-changelog) (#37268)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-09-04 09:27:38 +00:00
Guillaume Jacquart
76a881c0f3
docs(core): Add policy infrastructure module README (#37761)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-04 09:23:00 +00:00
Thanasis G
e0daef096a
feat(Databricks Node): Add partner User-Agent to chat model requests (#37774)
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-04 08:59:56 +00:00
Declan Carroll
b45f4b00dc
test: Remove unstable staged TCR test (#37836) 2026-09-04 11:30:23 +01:00
Declan Carroll
fcabdcae3c
feat: Add Flaky MCP to headless OpenCode sessions (no-changelog) (#37809) 2026-09-04 07:58:32 +00:00
Declan Carroll
a9b713be58
ci: Improve pnpm install performance (no-changelog) (#37789) 2026-09-04 08:46:18 +01:00
Jaakko Husso
ed541bc1e7
feat(core): Add opt-in concurrency limits for Instance AI runs (#37435)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-04 06:57:07 +00:00
Declan Carroll
5adfcaa37a
perf: Back off idle Codespaces polling (no-changelog) (#37751) 2026-09-04 06:31:31 +00:00
Declan Carroll
f8bea81da6
ci: Build Docker images from one application artifact (no-changelog) (#37738) 2026-09-04 05:57:48 +00:00
Michael Kret
9c131d6bd5
fix(editor): Align credit balance copy to "left" across views (#37423) 2026-09-04 05:56:28 +00:00
Matsu
fd105a6dc2
feat(core): Add PR preview instances on Codespaces (no-changelog) (#37695)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-04 09:01:33 +03:00
n8n-assistant[bot]
64de0e71db
chore: Update e2e impact map (#37793)
Some checks are pending
CI: Master (Build, Test, Lint) / Build for Github Cache (push) Waiting to run
CI: Master (Build, Test, Lint) / Unit tests (24.18.1) (push) Waiting to run
CI: Master (Build, Test, Lint) / Unit tests (26.5.1) (push) Waiting to run
CI: Master (Build, Test, Lint) / Lint (push) Waiting to run
CI: Master (Build, Test, Lint) / Performance (push) Waiting to run
CI: Master (Build, Test, Lint) / Verify single-instance deps (npm-install) (push) Waiting to run
CI: Master (Build, Test, Lint) / Notify Slack on failure (push) Blocked by required conditions
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-09-04 03:06:30 +00:00
Milorad FIlipović
0e8fbb0d4e
fix(editor): Fix visual glitch with canvas selection in Safari (no-changelog) (#37534)
Co-authored-by: Ricardo Espinoza <ricardo@n8n.io>
2026-09-03 20:41:49 +00:00
Declan Carroll
63312d1733
feat: Add OpenCode Codespace sessions (no-changelog) (#37782) 2026-09-03 21:55:09 +01:00
Guillaume Jacquart
0a48e727bd
feat(core): Seal the source-control import path with a clearance token (no-changelog) (#37481)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-03 19:44:56 +00:00
Elias Meire
9768c1befd
fix(core): Hide instance MCP OAuth discovery when MCP access is disabled (#36726) 2026-09-03 19:12:46 +00:00
Toni Devine
b6b2a7db17
feat(GraphQL Node): Add support for predefined credential types (#37671) 2026-09-03 17:47:06 +00:00
Declan Carroll
30d00cc61a
fix: Load Codespaces worker secrets before startup (no-changelog) (#37767) 2026-09-03 17:14:13 +01:00
Tomi Turtiainen
ae26d9599d
feat(engine): Return run data for engine 2.0 executions (#37486)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-03 15:41:04 +00:00
Uddish Verma
4a53faff09
refactor(API): Migrate the executions stop endpoints to the decorator pattern (no-changelog) (#37531)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-03 15:40:03 +00:00
RomanDavydchuk
bc8e5ea79c
feat(editor): Update node creator empty state and add a link to suggest an MCP service (#37339) 2026-09-03 15:17:19 +00:00
Thanasis G
44ddbbd28c
feat(Databricks Node): Support templated MCP registry server URLs (#37250)
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-03 14:40:34 +00:00
Mike Repeć
9a64820a2b
fix(core): Cap task runner task timeouts to the graceful shutdown window (#37651)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-09-03 14:32:20 +00:00
Arvin A
95f7e6a8a8
feat(core): Run seeded workflows before the graded turn in Instance AI evals (no-changelog) (#37516) 2026-09-03 14:09:18 +00:00
Milorad FIlipović
d667485f4c
fix(core): Inline reused schema instances in MCP tool schemas (no-changelog) (#37734) 2026-09-03 14:04:38 +00:00
RomanDavydchuk
a1c1f6fff4
fix(Google Gemini Node): Validate domain before downloading (#37459) 2026-09-03 13:34:34 +00:00
Sebastien Powell
50ea6bdaef
chore(editor): Storybook tweaks (no-changelog) (#37718) 2026-09-03 13:05:59 +00:00
Declan Carroll
55dc07be70
feat: Stream OpenCode session progress to Slack (no-changelog) (#37741) 2026-09-03 12:47:35 +00:00
n8n-cat-bot[bot]
2c0e22f8ec
test: Emit per-bucket axe scores to CI output (#37626)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-03 12:47:21 +00:00
n8n-cat-bot[bot]
9bccbd793f
chore: Use the existing removeTrailingSlash util instead of the per-node tolerateTrailingSlash copies (#37725)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-03 12:44:49 +00:00
Rob Hough
5e3774afcd
fix(editor): Expand AI question text input automatically (#37627)
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
2026-09-03 12:37:29 +00:00
Lorent Lempereur
0ed18c2a1b
refactor(core): Make scheduled job ownerKey non-nullable (no-changelog) (#37719) 2026-09-03 12:12:54 +00:00
Stephen Wright
e07e717c9a
feat(Confluence Node): Add Atlassian Service Account (2LO) authentication (#37536) 2026-09-03 12:11:56 +00:00
Danny Martini
5a03db41ef
feat(Gmail Trigger Node): Finish each poll within its time budget (#37319)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-09-03 12:02:46 +00:00
Guillaume Jacquart
91f87947b0
fix(Ldap Node): Escape expression output in the Custom Filter field (#37301)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-03 12:01:39 +00:00
Anne Aguirre
d657bfe66b
feat(ai-builder): Make AI Assistant capability-aware for agent changes (#37479) 2026-09-03 11:42:15 +00:00
Arvin A
47c9644392
feat(core): Let Instance AI read node-type usage from the dependency index (#37539) 2026-09-03 11:23:21 +00:00
James Martin
69190dddde
feat(editor): Add a new option to output the user details with a chat message trigger (no-changelog) (#37660)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-03 10:42:59 +00:00
n8n-cat-bot[bot]
60de10030b
chore: Extract the duplicated getErrorMessage helper in the instance-ai module into @n8n/utils (#37724)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-03 10:23:13 +00:00
Benjamin Schroth
47ece307e5
feat(core): Let chat channels start a new agent session (#37174) 2026-09-03 10:15:50 +00:00
Savelii
9b10838d9e
feat(core): Make the encryption write path pluggable and move the rotation decision into the module (#37603)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-09-03 10:12:18 +00:00
Lorent Lempereur
29c7d9f33f
fix(core): Complete a scheduled occurrence as not dispatched when its workflow is unpublished (#37721) 2026-09-03 10:11:39 +00:00
Andreas Fitzek
3e0929981e
fix(core): Redirect failed OIDC logins to the sign-in page (#37428) 2026-09-03 10:11:02 +00:00
Milorad FIlipović
1441a4eea2
fix(core): Clean up multipart webhook files (no-changelog) (#37318) 2026-09-03 10:10:18 +00:00
oleg
8ef5ba691d
fix(core): Stop the AI Assistant from pulling the whole workflow through the model for small edits (#37644) 2026-09-03 10:04:08 +00:00
Lorent Lempereur
c09ee10f1e
docs: Document the scheduled job owner contract and pin the job lifecycle (no-changelog) (#37685) 2026-09-03 09:58:59 +00:00
Jaakko Husso
918067af8b
fix(core): Give every simulated node at least one plausible item (#37557)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-03 09:54:51 +00:00
Uddish Verma
cea717b49e
refactor(API): Migrate GET /executions to the decorator pattern (no-changelog) (#37169)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-03 09:45:22 +00:00
Stephen Wright
40a957ec6a
feat(Microsoft Teams Node): Add Online Meeting Get operation (#37501) 2026-09-03 09:43:29 +00:00
Arvin A
7b9e09cbbe
feat(core): Add the activity event table and its migration (no-changelog) (#37429) 2026-09-03 09:38:37 +00:00
Robin Braumann
dc5f420745
fix(core): Prevent Anthropic agent threads from breaking permanently and keep run errors visible (#37640)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-03 09:26:33 +00:00
Declan Carroll
84855b2e53
build(editor): Delete frontend source maps after the Sentry upload (#37532)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-03 09:24:09 +00:00
MHMD
6fa7538922
feat(Chat Trigger Node): Handle unconnected end-user accounts in the hosted chat (no-changelog) (#37659) 2026-09-03 09:16:11 +00:00
n8n-cat-bot[bot]
dc27051834
chore: Extract the duplicated desired-job naming in the scheduler job registrars (#37704)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-03 09:06:31 +00:00
Jaakko Husso
cf0ef472ad
fix(core): Mark retired node types as deprecated in their workflow-sdk type definitions (#37628)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-03 08:58:58 +00:00
Rodrigo Santos da Silva
fd6db75b15
fix: Guarantee package.json restore after the FE trim step (#37446) 2026-09-03 08:27:10 +00:00
Csaba Tuncsik
8b7068515e
chore: Trim the shared cubic review instructions (#37492) 2026-09-03 08:17:24 +00:00
Mike Repeć
903fe572af
fix(core): Execute multi-input nodes re-fired through a loop-back edge in legacy order (#37561)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-09-03 07:48:00 +00:00
enzopiccolino-commits
7c21f4b47c
fix(core): Forward all output branches when a waiting webhook node resumes (#31714)
Co-authored-by: angtho <angtho@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Mat Leszkiewicz <137778020+mleszkiewicz@users.noreply.github.com>
Co-authored-by: Elias Meire <elias@meire.dev>
2026-09-03 07:42:17 +00:00
mfsiega
496b81b750
feat(editor): Show partial/failed publication status on workflow list cards (no-changelog) (#34069)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-09-03 07:27:43 +00:00
João Almeida
a3aadec2a2
feat(editor): Add node-context chat telemetry (no-changelog) (#36912)
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-03 07:26:23 +00:00
Raúl Gómez Morales
9711b60199
feat(editor): Add setup checklist state for AI-built workflows (no-changelog) (#37607) 2026-09-03 07:22:42 +00:00
Thanasis G
1c22d8473a
feat(Microsoft Teams Node): Add get and reply operations for channel messages (#37542)
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-03 07:19:15 +00:00
Bernhard Wittmann
b436bfa584
feat(editor): Deprecate and hide the Ask AI tab in the Code node (#37641) 2026-09-03 07:09:43 +00:00
n8n-cat-bot[bot]
ffe97a82e7
chore: Extract the duplicated per-node failure message formatter in the workflow publication service (#37673)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-03 07:02:52 +00:00
Guillaume Jacquart
11289ced77
feat(core): Seal the AI-builder workflow create with a clearance token (no-changelog) (#37493)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-03 07:02:10 +00:00
Paul Bauriegel
a69155a976
fix(Jira Node): Strip trailing slashes from domain URL (#32163)
Co-authored-by: RomanDavydchuk <roman.d@radency.com>
Co-authored-by: RomanDavydchuk <roman.davydchuk@n8n.io>
2026-09-03 05:33:06 +00:00
n8n-assistant[bot]
9244609f73
chore: Update e2e impact map (#37691)
Some checks are pending
CI: Master (Build, Test, Lint) / Build for Github Cache (push) Waiting to run
CI: Master (Build, Test, Lint) / Unit tests (24.18.1) (push) Waiting to run
CI: Master (Build, Test, Lint) / Unit tests (26.5.1) (push) Waiting to run
CI: Master (Build, Test, Lint) / Lint (push) Waiting to run
CI: Master (Build, Test, Lint) / Performance (push) Waiting to run
CI: Master (Build, Test, Lint) / Verify single-instance deps (npm-install) (push) Waiting to run
CI: Master (Build, Test, Lint) / Notify Slack on failure (push) Blocked by required conditions
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-09-03 03:13:18 +00:00
Benny Neugebauer
090a3c728c
feat(core): Add no-unsafe-connection-type-cast community node lint rule (no-changelog) (#37551) 2026-09-02 21:03:42 +00:00
Rúben Castro
5a45b0b282
feat(core): Add policy shadow-lint validator and request DTOs (no-changelog) (#37647)
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-02 20:54:41 +00:00
Lorent Lempereur
2a24001fac
feat(core): Deprovision a workflow's schedules when it stops owning them (#37135) 2026-09-02 20:10:44 +00:00
Lorent Lempereur
7675c614bf
refactor(core): Own scheduled jobs by owner data rather than by workflow node (no-changelog) (#37134) 2026-09-02 20:10:43 +00:00
Lorent Lempereur
bf28c44747
feat(core): Reconcile scheduled jobs whose owner is gone (no-changelog) (#37133) 2026-09-02 20:10:42 +00:00
Lorent Lempereur
8f046e2a3f
feat(core): Introduce scheduled job owners as data (no-changelog) (#37132) 2026-09-02 20:10:42 +00:00
n8n-cat-bot[bot]
efd3ec9e26
chore: DEVP-1038 · c1: Fix working-tree restore and stryker-setup file cleanup (#37672)
Some checks failed
CI: Master (Build, Test, Lint) / Build for Github Cache (push) Waiting to run
CI: Master (Build, Test, Lint) / Unit tests (24.18.1) (push) Waiting to run
CI: Master (Build, Test, Lint) / Unit tests (26.5.1) (push) Waiting to run
CI: Master (Build, Test, Lint) / Lint (push) Waiting to run
CI: Master (Build, Test, Lint) / Performance (push) Waiting to run
CI: Master (Build, Test, Lint) / Verify single-instance deps (npm-install) (push) Waiting to run
CI: Master (Build, Test, Lint) / Notify Slack on failure (push) Blocked by required conditions
Build: Benchmark Image / build (push) Has been cancelled
Util: Publish API Schema / Cloudflare Pages (push) Has been cancelled
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-02 17:55:08 +00:00
n8n-cat-bot[bot]
6cd984fb83
chore: Disable CodSpeed job on pull requests, keep it on post-merge only (#37666)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-02 16:46:04 +00:00
n8n-cat-bot[bot]
cc92e15a79
chore: Replace hand-rolled unique-constraint checks in packages/cli with the shared isUniqueConstraintError from @n8n/db (#37570)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: Emilia <100027345+sovietspaceship@users.noreply.github.com>
2026-09-02 16:33:18 +00:00
n8n-cat-bot[bot]
3e9a6336ba
chore: Use the shared isRecord helper in packages/cli/src/modules/agents instead of local copies (#37563)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-02 16:21:28 +00:00
Anne Aguirre
10ea0c8b64
fix(core): Workflow tools should wait and not throw if parent WF is suspended (#37087) 2026-09-02 15:13:41 +00:00
Anne Aguirre
731ea42c5c
fix(core): Fix Azure OpenAI classic and Foundry credential setup and execution for agents (#37251)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-02 15:07:49 +00:00
oleg
df4d30b3f5
fix(core): Keep Instance AI data-table queries targeted while diagnosing (#37489) 2026-09-02 15:04:47 +00:00
Garrit Franke
a8c9c1d3fc
feat(core): Skip community packages requiring an unsupported node API at startup (no-changelog) (#37449) 2026-09-02 14:47:52 +00:00
Garrit Franke
fec7a227c8
feat(core): Add node API version constant and package compatibility helper (no-changelog) (#37444) 2026-09-02 14:47:51 +00:00
Yen Su
233f1ca0e7
feat(Databricks Node): Add hidden Databricks Chat Model sub-node (#37299) 2026-09-02 14:42:00 +00:00
n8n-cat-bot[bot]
ab48cfc2ad
chore: Replace hand-rolled escapeRegExp helpers with lodash/escapeRegExp (#37567)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: Emilia <100027345+sovietspaceship@users.noreply.github.com>
2026-09-02 14:01:52 +00:00
Stephen Wright
f069c841d3
feat(Jira Node): Add Atlassian Service Account (2LO) authentication (#37513) 2026-09-02 13:49:15 +00:00
Uddish Verma
1020742d6e
refactor(API): Migrate the executions tags endpoints to the decorator pattern (no-changelog) (#37369)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-02 13:28:08 +00:00
Guillaume Jacquart
0a7412e3e3
feat(core): Seal the workflow import path with a clearance token (no-changelog) (#37461)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-02 13:27:24 +00:00
Rúben Castro
344e8b1d27
feat(core): Add nodeTypePolicy RBAC scope and license flag (no-changelog) (#37538)
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-02 13:12:26 +00:00
MHMD
4608fd236c
feat(Chat Trigger Node): Add end-user credential connect bar to the hosted chat shell (no-changelog) (#37550) 2026-09-02 12:37:31 +00:00
Guillaume Jacquart
91dff038f9
feat(core): Seal the eval thread-restore workflow write (no-changelog) (#37578)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-02 12:36:39 +00:00
Jaakko Husso
d8aafc634c
fix(core): Report a model id in Instance AI traces and run metrics (no-changelog) (#37415)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-02 11:52:50 +00:00
Ricardo Espinoza
53b1ad7e5b
fix(core): List the valid techniques when get_workflow_best_practices rejects input (#37580) 2026-09-02 11:50:46 +00:00
Benjamin Schroth
c2637daedc
feat(editor): Add progress feedback to the artifact panel while the AI Assistant builds (#37562) 2026-09-02 11:38:28 +00:00
Jaakko Husso
2f5a13c359
fix(core): Cap the resolve_llm model list and check the free-credits model (no-changelog) (#37510)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-02 11:35:57 +00:00
Raúl Gómez Morales
4b3acc85dd
refactor(core): Drop the Instance AI run snapshot store (no-changelog) (#37204) 2026-09-02 11:29:04 +00:00
James Martin
d997348352
feat: Refresh chat OAuth2 access tokens periodically (no-changelog) (#37541)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: MHMD <45968454+dou-mhmd@users.noreply.github.com>
2026-09-02 11:27:29 +00:00
Robin Braumann
6912024af8
fix(core): Gate observation on the model-facing window budget (no-changelog) (#37438)
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Matsu <matias.huhta@n8n.io>
2026-09-02 11:13:37 +00:00
Matsu
d5652d8829
ci: Verify a sandbox can be created in the hosted preflight (no-changelog) (#37631)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-02 13:57:25 +03:00
Savelii
430bbe53c3
feat(core): Always load the encryption module and seed keys safely on startup (#37416)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-09-02 10:28:28 +00:00
Luigi Gubello
e3a2c9dda5
ci: Rework the cubic security review rules (no-changelog) (#37417)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-02 10:11:31 +00:00
Mike Repeć
51ac147cd0
fix(core): Ensure running job cleanup when a workflow run rejects (#37621)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-09-02 09:49:33 +00:00
Ayush Sharma
5aeb96cdb9
fix(Wordpress Node): Send OAuth2 client credentials in request body (#36082)
Co-authored-by: yehorkardash <yehor.kardash@n8n.io>
2026-09-02 09:44:01 +00:00
João Almeida
42adcc0e7b
feat(editor): Add canvas nodes as context for the AI Assistant (no-changelog) (#36225)
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Miguel Angel Moreno <1411774+miguelsaddress@users.noreply.github.com>
2026-09-02 09:38:40 +00:00
n8n-cat-bot[bot]
e08a4fdbfe
fix: Fix attribution footer link on the Form Ending page (#37288)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: Matsu <matias.huhta@n8n.io>
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
2026-09-02 09:18:45 +00:00
n8n-cat-bot[bot]
3130b2ff7b
test: Wire accessibility checks into a journey with an opt-in HTML (#37349)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-02 09:18:38 +00:00
n8n-cat-bot[bot]
0a28f5365c
chore: Import writeGithubOutput from github-helpers.mjs in sync-master-to-3x.mjs (#37564)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-02 09:16:00 +00:00
Guillaume Jacquart
823dee4eed
feat(core): Seal the chat-hub workflow creates with a clearance token (no-changelog) (#37494)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-02 08:47:14 +00:00
Anne Aguirre
c2e8d02921
fix(ai-builder): UI tweak to prevent overflowing text on err callout when on Agent Preview (#37615) 2026-09-02 08:23:09 +00:00
Sebastien Powell
5fa620e771
docs(editor): Refresh colour, shadow, and type style guide pages (no-changelog) (#37504)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-02 08:22:26 +00:00
Matsu
6586dbe05e
ci: Serve the local sandbox runner over TLS (no-changelog) (#37599)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-02 08:09:13 +00:00
Yen Su
5a88f06bb9
feat(Databricks Node): Send a partner User-Agent on every API call (#37279) 2026-09-02 08:07:01 +00:00
n8n-cat-bot[bot]
ea29906b48
chore: Minor upgrade for multer - license-server (#37602)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-02 07:35:03 +00:00
n8n-cat-bot[bot]
e23ee05901
fix: Check constructor-assigned and type-asserted node descriptions (#37523)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-02 07:21:56 +00:00
Tomi Turtiainen
6c28987d64
ci: Pin sandbox service API and runner images to 1.2.0 (no-changelog) (#37601) 2026-09-02 09:58:55 +03:00
Ricardo Espinoza
485e370416
fix(API): Check publish permission when a workflow save republishes (#37264) 2026-09-02 06:30:04 +00:00
Matsu
9a94c3da13
ci: Pin sandbox service test images (no-changelog) (#37596)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-02 09:34:36 +03:00
Matsu
f02944960e
ci: Run the backport workflow on a runner with a fixed git version (no-changelog) (#37594) 2026-09-02 09:34:05 +03:00
n8n-cat-bot[bot]
31c9317d45
chore: Conveyor test: Small refactoring (#37552)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-02 06:10:11 +00:00
n8n-assistant[bot]
cae5e093d3
chore: Bundle 2.x (#37586)
Co-authored-by: Matsu <matias.huhta@n8n.io>
2026-09-02 08:16:39 +03:00
n8n-assistant[bot]
d8194d4e66
chore: Update e2e impact map (#37583)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-09-02 03:18:13 +00:00
n8n-cat-bot[bot]
7dd722bfb6
chore: Bump sanitize-html 2.17.5 → 2.17.7 (patch) to clear 1 Aikido finding (#37582)
Some checks are pending
Build: Benchmark Image / build (push) Waiting to run
CI: Master (Build, Test, Lint) / Build for Github Cache (push) Waiting to run
CI: Master (Build, Test, Lint) / Unit tests (24.18.1) (push) Waiting to run
CI: Master (Build, Test, Lint) / Unit tests (26.5.1) (push) Waiting to run
CI: Master (Build, Test, Lint) / Lint (push) Waiting to run
CI: Master (Build, Test, Lint) / Performance (push) Waiting to run
CI: Master (Build, Test, Lint) / Verify single-instance deps (npm-install) (push) Waiting to run
CI: Master (Build, Test, Lint) / Notify Slack on failure (push) Blocked by required conditions
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-02 02:20:13 +00:00
Stephen Wright
336fda8cf7
feat(Microsoft Teams Node): Add Online Meeting resource with Create operation (#37500) 2026-09-01 18:22:39 +00:00
n8n-cat-bot[bot]
1b87f4f003
chore: Use sleep from @n8n/utils/sleep instead of inline setTimeout promises in packages/cli (#37565)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-01 17:16:21 +00:00
Mike Repeć
b0550cb3cb
fix(Code Node): Keep identity and reporting fields when wrapping task runner errors (#37490)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-09-01 16:27:59 +00:00
Uddish Verma
51c9ae7230
refactor(API): Migrate GET and DELETE /executions/{id} to the decorator pattern (no-changelog) (#37086)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-01 15:12:16 +00:00
Lorent Lempereur
e852ec4906
chore: Add lint rule to require system tasks for leader-only timers (#37540) 2026-09-01 14:56:00 +00:00
Guillaume Jacquart
40bd432867
feat(core): Seal the workflow create path with a clearance token (no-changelog) (#37442)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-01 14:55:33 +00:00
Sebastien Powell
8c37452ff3
chore(editor): Tidy Storybook for Figma import (no-changelog) (#35734)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-01 14:33:33 +00:00
Raúl Gómez Morales
242858ee8b
feat(core): Add setup-items event contract and setup panel flag (no-changelog) (#37146) 2026-09-01 14:27:34 +00:00
Charlie Kolb
6fb87836a1
chore(core): Skip workflow history compaction telemetry for no-op runs (#37537)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-09-01 14:17:24 +00:00
Uddish Verma
0b3ab5216a
feat(API): Add a workflow version endpoint and deprecate the old two-variable path (#37114)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: Matsu <matias.huhta@n8n.io>
2026-09-01 13:47:07 +00:00
Dimitri Lavrenük
1490129c22
feat(core): Resolve node output schemas into generated type definitions (#37246)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-09-01 13:24:19 +00:00
Jaakko Husso
02464a1ca1
test: Use the hosted sandbox service when it is configured (#37297)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-01 13:12:23 +00:00
Uddish Verma
de09565056
docs(API): Document how to verify a public API migration (#37048)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-01 13:03:37 +00:00
Lorent Lempereur
d88eee888c
feat(core): Register system tasks through the module contract (no-changelog) (#37519) 2026-09-01 13:02:46 +00:00
n8n-cat-bot[bot]
d8a9f6caaf
chore: DEVP-932 · c1: Move release compliance SBOM job to Blacksmith runner (#37525)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-01 12:45:01 +00:00
n8n-cat-bot[bot]
7efef5bc24
chore: DEVP-932 · c2: Move image SBOM attestation job to Blacksmith runner (#37526)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-01 12:44:54 +00:00
Declan Carroll
923173eecc
ci: Scope workflow token permissions to job level and harden test image builds (no-changelog) (#36529)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-09-01 12:26:27 +00:00
Declan Carroll
7236abaf40
ci: Delete the e2e image tarball from the cache after e2e passes (no-changelog) (#37346)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-01 11:59:19 +00:00
Alexander Gekov
8946168423
fix(editor): Preserve n8n credits when copying workflows (#37253)
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Michael Kret <michael.k@radency.com>
2026-09-01 11:53:10 +00:00
Lorent Lempereur
bcfdfa4252
feat(core): Run system tasks from the registry (no-changelog) (#37255) 2026-09-01 11:37:16 +00:00
yehorkardash
a0b2fccc81
refactor: Centralize MCP registry control surface (#37219)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-01 10:55:57 +00:00
Matsu
ebbcac7425
ci: Only enforce PR size limit in the main monorepo (#37505) 2026-09-01 10:39:56 +00:00
Kai
ad4b8995db
feat(editor): Allow users to resize the review inbox sidebar (#37196) 2026-09-01 10:39:06 +00:00
RomanDavydchuk
b639fe5dfb
fix: Update MCP server status in AI Assistant when tool call failed or credentials updated (#37197)
Co-authored-by: Elias Meire <elias@meire.dev>
2026-09-01 10:20:46 +00:00
mfsiega
d04c90d15d
feat(core): Add publication status to the workflow list API (no-changelog) (#34068)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-09-01 10:09:46 +00:00
Charlie Kolb
6de8eacbc6
ci: Replace CODEOWNERS with required-review enforcement in OWNERS (#37120)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-09-01 10:09:39 +00:00
Uddish Verma
086739d160
ci: Minimize outdated cubic review comments (#37495) 2026-09-01 10:08:46 +00:00
Declan Carroll
ca16909e0f
ci: Refresh E2E shard metrics and add a per-shard minimum test time (no-changelog) (#37451)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-01 09:55:51 +00:00
Irénée
614cfe2f7c
feat(API): Push and pull projects to a Git connection (#36998)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Nour Alhadi Mahmoud <nour.mahmoud@n8n.io>
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-01 09:37:05 +00:00
13568 changed files with 1073490 additions and 491241 deletions

View File

@ -13,11 +13,11 @@ clear, when to stay silent, what never to say — lives once in
| Directory | Agent | Scope |
|-------------|----------|-----------------------------------------------------|
| `security/` | Security | backend packages + nodes |
| `security/` | Security | backend packages, nodes, and the frontend render surface |
| `backend/` | Backend | `cli`, `@n8n/db`, `core`, `workflow`, node packages |
| `db-migrations/` | DB migrations | `@n8n/db` migrations + their tests in `cli` |
| `frontend/` | Frontend | `packages/frontend` |
| `qa-dx/` | QA & DX | `.github`, `docker`, `scripts`, `patches`, `packages/testing`, the lint/test/TS config packages, baselines |
| `qa-dx/` | QA & DX | `.github`, `docker`, `scripts`, `patches`, `packages/quality`, the lint/test/TS config packages, baselines |
| `testing/` | Backend + Frontend | any package with a test suite |
A directory maps to one agent unless, like `testing/`, the policy is identical
@ -27,7 +27,7 @@ never a copy per directory. Security and QA & DX deliberately don't link
agents shouldn't be able to produce.
All five slots are used, so a new domain now merges into an existing agent. QA &
DX covers the build, test, and CI surface — the same paths `.github/OWNERS`
DX covers the build, test, and CI surface — the same paths `OWNERS`
assigns to `@n8n-io/qa-dx`. Code-quality rules that happen to apply broadly
(error classes, `any`, lazy imports) are backend rules, not QA & DX ones. DB
migrations is split out of Backend because a migration is permanent and runs
@ -59,6 +59,9 @@ enforces them in CI:
files — a backend PR still loads the node rules, since include globs are
per-agent.
3. Add its path to every `file_paths` that should load it in `cubic.yaml`.
Keep each agent's list ordered by how often the class it covers actually
shows up — the ceiling truncates from the end, so the last file listed is the
one that silently disappears first.
4. Run `pnpm check:cubic-config`. It validates `cubic.yaml` against cubic's
published JSON schema, then fails on a missing path, an over-budget agent, or
a rule file nobody links, and warns at 80% of the ceiling.

View File

@ -0,0 +1,14 @@
# License enforcement
Applies to: `packages/cli`, `*.ee.ts` files, licensed modules.
Nothing fails the build when a paid feature ships ungated, so this is review-only.
- Endpoints in `*.ee.ts` reaching licensed features without `@Licensed` — a
scope decorator is a permission check, not a license check
- `@Licensed` not matching the feature (`LICENSE_FEATURES` in `@n8n/constants`)
- Bypassed quota checks, or missing `FeatureNotLicensedError` on an unlicensed
path
- Custom licensing middleware where the `@Licensed` decorator fits
- Decorator order: route decorator → `@Licensed` → scope decorators
- Enterprise code reachable outside `*.ee.ts` files or licensed modules

View File

@ -0,0 +1,21 @@
# Wrapping a third-party component
Applies to: `packages/frontend`.
`@n8n/design-system` wraps reka-ui, Element Plus, TipTap and CodeMirror. The
recurring defect is assuming the wrapper passes something through, or that a
library default is what you want.
- **Attributes are forwarded only if the wrapper forwards them.** `N8nSelect`
binds just `$props` and `on*` listeners to `ElSelect`, so an `aria-label`,
`data-test-id` or native attribute set on it lands on the outer `<div>` and
never reaches the control. Check the wrapper's `v-bind`, and prefer its own
prop where one exists.
- **Library defaults are behaviour.** A dialog focus trap focusing the first
tabbable child opens a filterable select on mount. Flag a handler
suppressing a library behaviour unconditionally when one state needs it,
e.g. a blanket `@interact-outside` `preventDefault`.
- **Vendor class names are contracts.** A `:global()` selector against one
stops matching silently when the library renames it.
- An accessible name computing to empty for an icon-only control, or a
`<button>` with no explicit `type`, which is `submit` inside a form.

View File

@ -1,16 +1,42 @@
# Design system enforcement
# Design system
Applies to: `packages/frontend`.
The design system skill linked alongside this file is the source of truth for
which token to reach for. This file sets the enforcement level.
Stylelint checks the *grammar* of a custom-property name, at warning severity.
Nothing checks that a token exists or is the right one, and `var(--gone)`
resolves to nothing rather than failing. Wrong tokens are the most common
shipped frontend regression in this repo.
Stylelint validates CSS custom-property *names* but never their values, so
nothing catches a hard-coded one. Never comment on the naming itself — that
half already fails the build.
Three layers, narrowest first; reaching past the narrowest that fits is the
defect:
- Strong warning: hard-coded visual values (px, rem, hex colours, durations)
where a token exists; legacy token usage; deprecated style or component
surfaces.
- Soft warning: token-to-token substitutions. Ask for intent rather than
asserting a regression — a deliberate change looks identical to a mistake.
1. component-scoped: `--button--*`, `--callout--*`, `--input--*`, `--tag--*`,
`--node--*`, `--canvas-node--*`, `--sticky--*`
2. semantic: `--background--*`, `--text-color--*`, `--border-color`
3. primitives, in px: `--spacing--` 5xs 2, 4xs 4, 3xs 6, 2xs 8, xs 12, sm 16,
md 20, lg 24, xl 32, 2xl 48, 3xl 64, 4xl 128, 5xl 256; `--radius--` 4xs 2,
3xs 4, 2xs 6, xs 8, sm 12, md 16, lg 20, xl 24, 2xl 32, `--radius--full`
Flag, strongest first:
- A `var()` naming a token that no longer exists, e.g. a legacy `--color--*`
kept through a refactor. It renders as nothing, silently.
- A hard-coded px, rem, hex, `rgb()`, ms or `z-index` literal where a token
covers it. Name the token.
- A generic token where a component-scoped one exists, e.g. a `--color--*`
background on an input that has `--input--color--background`.
- A px target stated in the diff or a comment that the chosen token misses.
The scales share suffixes at different values: `--radius--xs` is 8px,
`--spacing--xs` is 12px. "Design calls for 8px" beside `--radius--lg` (20px)
is a real defect.
Soft warning on a plain token swap: ask the intent, since a deliberate change
and a mistake look identical.
Flag a hand-rolled control duplicating an `N8n*` component (icon button,
tooltip, dialog, select), which arrives missing hover, focus and disabled
states, and a NEW direct `element-plus` or `reka-ui` import in `editor-ui`.
No lint rule covers those and many files predate the convention, so judge
only the import being added.
Do NOT comment on Storybook story titles, args, or doc cross-references.

View File

@ -0,0 +1,18 @@
# i18n
Applies to: `packages/frontend`.
`baseText(key, { interpolate })` takes an untyped record and vue-i18n leaves
an unmatched placeholder in the output. Nothing checks those names against
`en.json`.
- An `interpolate` key not matching the `{placeholder}` in that string,
including a case difference such as `docUrl` against `{docURL}`. The user
sees the raw placeholder or nothing.
- `i18n.baseText` passed as a bare function reference. It is a class method
reading `this`, so detaching it returns the key instead of the translation.
Pass the `i18n` object.
- Punctuation, separators or brackets hardcoded around a translated string in
a template. Composition differs per locale; put them in the string.
Do NOT flag a missing non-`en` translation, or wording itself unless it
contradicts copy the same PR adds.

View File

@ -0,0 +1,43 @@
# Vue correctness
Applies to: `packages/frontend`.
These fail silently: `vue-tsc` and ESLint pass, the app runs, the value or the
DOM is wrong.
## List identity
The editor patches long-lived lists in place, so `:key` decides whether Vue
reuses the wrong row. Flag an index `:key` on a list that can be reordered,
filtered or shortened, where revealed fields, inputs and refs then follow the
position instead of the item; a key omitting part of what identifies the
instance, e.g. a session key without the execution id, so switching
executions reuses the old component's state; and a key regenerated every
render. Index keys are fine on an append-only list.
## Reactivity
- A `ref` or `computed` read without `.value` outside a template. Templates
unwrap, script does not, so you hold the wrapper: `activeNode?.type` reads
`undefined` instead of the type, without throwing.
- A reactive object spread, cloned or deep-merged. `{ ...store }`,
`structuredClone` and recursive merge helpers detach it, and a merge into a
shared defaults object leaks one call into the next.
- An `injectStrict` helper such as `injectNDVStore` called outside `setup()`.
## State written after an await
Navigation swaps the active workflow document, and dialogs, threads and NDV
panels unmount mid-request. Flag a continuation writing to a store,
`localStorage` or the route without re-checking what it captured before the
`await`: navigating first and persisting afterwards what the destination was
meant to read, clearing a selection the user has since changed, toasting for a
closed dialog. Ask for a revalidated reference or a request sequence number,
never a lock.
## Payload shape
Backend responses and `/rest/settings` omit optional sections entirely. Flag a
new getter or computed walking more than one level into a response without
optional chaining, e.g. `settings.value.license.planName`. It throws during
boot and takes the view with it.

View File

@ -1,37 +0,0 @@
# Migrated workflow fields must use workflowDocumentStore
Applies to: `packages/frontend`.
Workflow state is actively migrating from `workflowsStore`
(`packages/frontend/editor-ui/src/app/stores/workflows.store.ts`) to
`workflowDocumentStore`
(`packages/frontend/editor-ui/src/app/stores/workflowDocument.store.ts`).
`workflowDocumentStore` is the single source of truth for migrated fields. New
code that reads or writes one of them must go through it, not through
`workflowsStore.workflow.*` or an equivalent accessor.
Already-migrated fields — do NOT access these via `workflowsStore`:
`active`, `activeVersion`, `activeVersionId`, `checksum`, `createdAt`,
`homeProject`, `meta`, `pinData`, `settings`, `tags`, `updatedAt`
Flag when NEW code does any of:
1. Reads a migrated field from `workflowsStore.workflow.<field>`
2. Reads one from a destructured `workflow` ref originating in `workflowsStore`
3. Writes one through `workflowsStore`, e.g. `workflowsStore.workflow.active = true`
4. Calls a `workflowsStore` setter/action that only mutates a migrated field,
when `workflowDocumentStore` already exposes an equivalent method
Do NOT flag:
- Non-migrated fields, e.g. `workflowsStore.workflow.nodes`, `.connections`
- Code inside `workflowDocument.store.ts` or its sub-modules — those *are* the
source of truth
- Code inside `workflows.store.ts` itself — the migration is still in progress there
Use `useWorkflowDocumentStore(createWorkflowDocumentId(workflowId))` from
`@/app/stores/workflowDocument.store.ts`.
This is an active migration; the field list will grow.

View File

@ -13,8 +13,10 @@ Flag a diff that **adds** entries to any of these, and ask for the fix instead:
|------|---------|
| `.code-health-baseline.json` | `@n8n/code-health` violations |
| `.boundaries-baseline.json` | `turbo boundaries` issue count |
| `packages/testing/playwright/.janitor-baseline.json` | Playwright janitor findings |
| `packages/cli/eslint.config.mjs` | the `misplaced-n8n-typeorm-import` and public-API allowlists, each captioned "NEVER add to this list" |
| `packages/quality/testing/playwright/.janitor-baseline.json` | Playwright janitor findings |
| `.code-health-baseline.json` | the `typeorm-persistence-boundary` baseline, which only shrinks as business logic moves queries into repositories |
| `packages/cli/eslint.config.mjs` | the public-API allowlists captioned "NEVER add to this list" |
| `.code-health-baseline.json` (`lint-config-layering`) | package-wide rule downgrades left in package ESLint configs |
Removals are the healthy direction and need no comment.
@ -33,3 +35,11 @@ Flag a lint rule moved from `'error'` to `'warn'` or `'off'`, and a new
`eslint-disable` for one of the guarded rules. Most packages run
`eslint . --quiet`, so a warning never fails CI — downgrading to `'warn'` is
functionally deleting the rule while appearing to keep it.
In a package `eslint.config.mjs` the `lint-config-layering` code-health rule
catches this for you: a package-wide downgrade fails Static Analysis unless it
is already in the baseline. What it cannot judge is a downgrade dressed as a
scoped block. Check that a new `files` glob names the paths that actually need
the exception, and that a rule retired in `@n8n/eslint-config/src/configs/base.ts`
comes with the count of packages that had already stopped enforcing it
(`node scripts/lint-parity/majority.mjs`).

View File

@ -35,7 +35,7 @@ fed from a `needs:` list. Flag:
Jobs gated on a `ci-filter` filter only run when a matching file changed, so a
filter that is too narrow means the test quietly stops covering new code. When
a PR adds a directory that an existing filter was meant to cover — a new package
under `packages/testing/`, a new script under `.github/scripts/` — check the
under `packages/quality/`, a new script under `.github/scripts/` — check the
filter still matches it.
## Least privilege

View File

@ -1,36 +1,18 @@
# Authentication, authorization, and licensing
# Authentication and access control
Applies to: `packages/cli`, `packages/@n8n/db`.
## Authentication and sessions
- JWT handling that weakens validation or expiration
- Missing or disabled cookie security flags (HttpOnly, Secure, SameSite)
- Changes that bypass MFA enforcement
- SSO/SAML/OIDC flows with validation gaps
- OAuth 2.0 missing PKCE, or with weak redirect URI validation
- JWT validation or expiration weakened; cookie flags (HttpOnly, Secure, SameSite) missing or disabled; MFA bypassed; SSO/SAML/OIDC validation gaps; OAuth 2.0 without PKCE or a weak redirect check
## Authorization
No static check covers this: the `endpoint-scope-coverage` rule in
`@n8n/code-health` is disabled, so a new route without a scope decorator reaches
production unnoticed unless a reviewer catches it.
`@n8n/code-health`'s `endpoint-scope-coverage` rule is disabled, so an undecorated route ships unless a reviewer catches it.
- Every authenticated route needs `@GlobalScope` or `@ProjectScope`, unless it
declares `skipAuth`, `allowUnauthenticated`, or `apiKeyAuth`
- Missing credential permission validation before workflow execution
- Subworkflow execution bypassing caller policies or ownership validation
- Missing project-level or resource-level access controls
## License enforcement
- Missing `FeatureNotLicensedError` for unlicensed feature access
- Bypassed license quota checks
- New licensed features without middleware or module-level enforcement
- New endpoints in `*.ee.ts` that reach licensed features without `@Licensed`
- `@Licensed` not matching the feature (check `LICENSE_FEATURES` in `@n8n/constants`)
- Endpoints in `*.ee.ts` carrying a scope decorator but missing `@Licensed` — the
license check is separate from the permission check
- Prefer the `@Licensed` decorator over custom licensing middleware
- Decorator order: route decorator → `@Licensed` → scope decorators
- Enterprise code reachable outside `*.ee.ts` files or licensed modules
- Every authenticated route needs `@GlobalScope` or `@ProjectScope`, unless it declares `skipAuth`, `allowUnauthenticated`, or `apiKeyAuth`
- A decorator proves the caller may do this somewhere, not that the row is theirs. Ownership belongs in the query — a `find…ForUser` finder, or the project filter alongside the id — never a comparison afterwards
- An endpoint querying a repository directly while its siblings use a project-aware service — the public API reaching past it is the usual shape
- Middleware treating an authenticated session as sufficient (`if (req.user) return next()`), skipping the ownership check behind it
- A scope parameter defaulting to the weakest value, or a role map gaining write or execute: mutating call sites must ask for the stronger scope
- Subworkflow execution bypassing caller policies

View File

@ -1,15 +1,17 @@
# Code execution and sandboxing
Applies to: `packages/cli`, `packages/core`, `packages/workflow`.
Applies to: `packages/cli`, `packages/core`, `packages/workflow`, Code and Merge nodes.
Every sandbox n8n has shipped was eventually escaped, so the bar is reachability: if sandboxed code can reach it, assume it can climb out.
Flag changes that:
- Weaken expression evaluation sandbox protections
- Reduce isolation in Code node sandboxes (JavaScript/Python)
- Grant sandboxed contexts new access to Node.js builtins or external modules
- Introduce prototype pollution or constructor access bypasses
- Weaken prototype sanitizers or function context validators
- Expose `process.env` access in Code nodes
- Give a sandboxed context new access to Node.js builtins, `process.env`, dynamic `require`/`import`, the filesystem, or the network
- Add a global, helper, or bridge callable from inside the sandbox that hands back a host-realm value — one returned object, thrown error, or callback is enough to walk `constructor` back out
- Weaken a prototype sanitizer, a blocklist, or the allowed globals
- Weaken file access restriction enforcement, or let unsanitized input reach a file path or an n8n internal directory
Higher scrutiny applies to the expression engine and the code execution nodes —
a regression there is reachable by any workflow author.
Two guards that did not hold before:
- `typeof value === 'string'` before a blocklist check: `['__proto__']` reports `'object'` yet coerces to `__proto__` as a key. Compare `String(value)`
- An AST check covering computed member expressions only — the same name also arrives as a call argument or a template literal, neither a string literal

View File

@ -4,15 +4,10 @@ Applies to: all backend packages.
Flag:
- Credentials logged or exposed in error messages
- Credential values reaching logs, error messages, response headers, or a REST response — a decrypted field returned "because the UI needs it" is the shape
- Hardcoded secrets, API keys, or tokens
- Changes to credential encryption/decryption that weaken security
- OAuth state/CSRF token handling that bypasses validation
- Webhook requests that don't sanitize auth cookies
- External secrets provider integrations with insecure configurations
- Credential access not respecting scope boundaries (instance/project/user)
- Data fetched via credentials being exposed to unauthorized user groups
- Credential encryption weakened, or `N8N_ENCRYPTION_KEY` reused for token signing, OAuth state, or anything but credential encryption
- OAuth state/CSRF validation bypassed, or webhook requests keeping auth cookies
- Credential access ignoring scope boundaries (instance/project/user), or a credential resolved by name and type with no ownership filter
An error message that interpolates a caught error from an integration is a
common way credential material reaches a persisted status field or an API
response. Sanitize before recording.
A caught integration error interpolated into a message is a common way credential material reaches a status field or an API response.

View File

@ -1,32 +0,0 @@
# Filesystem, database, network, and audit trails
Applies to: all backend packages.
## Filesystem
- Weakened file access restriction enforcement
- File operations bypassing allowlist/blocklist patterns
- Access to n8n internal directories
- Unsanitized user input flowing into a file path
## Database
- Missing indexes on frequently queried columns in migrations
- Resource-intensive queries without pagination or limits
- Missing encryption for sensitive fields beyond credentials
- Raw SQL that bypasses TypeORM protections
## HTTP, webhooks, and network
- SSRF risk in user-controlled URLs
- CORS allowing all origins
- CSP or iframe sandbox changes that weaken protections
- Rate limiting changes that reduce protection
- Disabled TLS certificate validation (`rejectUnauthorized: false`)
- Bearer tokens forwarded across a redirect to a different host
## Audit logging
- Missing logging for security-relevant events
- Sensitive data in logs or error messages
- Incomplete audit trails for authentication and authorization events

View File

@ -3,75 +3,32 @@
Applies to: `packages/nodes-base`, `packages/@n8n/nodes-langchain`. Skip this
file for other packages.
Node code receives workflow-author-controlled input, mainly via
`this.getNodeParameter(...)` and incoming `item.json` keys.
## Prototype pollution via node parameters
A value derived from user-controlled input must never be used as a computed
object key in an **assignment** to a plain object. A workflow author can set it
to `__proto__`, `constructor`, or `prototype`, polluting the prototype chain.
ESLint does not cover this: `no-prototype-builtins` is only a warning here and `pnpm lint` runs with `--quiet`.
ESLint does not cover this — `no-prototype-builtins` is only a warning in these
packages, and `pnpm lint` runs with `--quiet`.
Flag NEW code where a value tracing back to `this.getNodeParameter(...)`
(directly, via a variable, via a `.map`/`.reduce`/`.forEach` callback param, or
via a `getNodeParam` function passed into a helper such as `createTableStruct`)
is used as a computed key to BUILD A NESTED OR CONTAINER STRUCTURE on a plain
object (an `IDataObject` or object literal — not a `Map`, not
`Object.create(null)`):
Flag NEW code where a value from `this.getNodeParameter(...)` — directly or via a variable, a callback param, or a helper — is a computed key BUILDING A NESTED OR CONTAINER STRUCTURE on a plain object (`IDataObject` or object literal, not a `Map` or `Object.create(null)`):
1. Container creation: `obj[key] = {}` or `obj[key] = []`
2. Nested write where the untrusted value is a key: `obj[k1][k2] = value`
3. The same via `obj[key] ??= {}` / `obj[key] ||= []`
This nested/container shape is what actually pollutes `Object.prototype` — the
dangerous pattern is typically a shared accumulator.
The carrier is usually a shared accumulator; a deep merge of user options into a config object is the same defect.
Do NOT flag:
- Reads: `const x = obj[key]`, `if (obj[key] === undefined)` — reads don't pollute
- Single-level writes of a concrete (non-object) value, e.g.
`item.json[field] = value`, `item.binary[prop] = data`, `body[target] = value`.
Assigning a primitive to `__proto__` is a no-op, and setting one property on a
fresh per-item object is not prototype pollution. This is the overwhelmingly
common, benign case in nodes — flagging it is noise.
- Keys that are string/number literals, or validated by `isSafeObjectProperty(key)`
- Single-level writes of a concrete (non-object) value, e.g. `item.json[field] = value` — a primitive assigned to `__proto__` is a no-op. This is the common, benign case in nodes; flagging it is noise
- Keys that are literals, or validated by `isSafeObjectProperty(key)`
- Writes routed through `setSafeObjectProperty(...)`
- Targets that are a `Map` or created with `Object.create(null)`
Violation:
```typescript
const table = this.getNodeParameter('table', i) as string;
const key = this.getNodeParameter('deleteKey', i) as string;
if (acc[table] === undefined) acc[table] = {}; // acc['__proto__'] = {}
if (acc[table][key] === undefined) acc[table][key] = [];
```
Allowed:
```typescript
import { setSafeObjectProperty, isSafeObjectProperty } from 'n8n-workflow';
if (isSafeObjectProperty(table) && acc[table] === undefined) {
setSafeObjectProperty(acc, table, {});
}
// or: const acc = new Map<string, ...>();
```
Prefer `setSafeObjectProperty` / `isSafeObjectProperty` from `n8n-workflow`. See
`nodes/Google/GSuiteAdmin/GSuiteAdmin.node.ts` and
`nodes/HttpRequest/V3/HttpRequestV3.node.ts` for reference usage.
Use `setSafeObjectProperty` / `isSafeObjectProperty` from `n8n-workflow`, or a `Map`, and coerce with `String(key)` before any check.
## Injection through node parameters
Flag NEW code where a node parameter reaches a sink without escaping or
parameterisation:
Flag NEW code where a parameter reaches a sink without escaping:
- SQL query fields built by string concatenation or expression interpolation
instead of bound parameters
- SQL values concatenated or expression-interpolated instead of bound. An expression is interpolation, not parameterisation
- SQL identifiers — table, column, `ORDER BY`, a cast suffix after `name:`. They cannot be bound, so they need an allowlist; numeric fields a numeric cast
- A SQL dialect reaching JavaScript (AlaSQL in the Merge node) gaining a new construct that can call a function
- Command execution with unsanitized shell arguments
- File operations where a parameter reaches a path without traversal checks
- Community package names accepted without validation

View File

@ -0,0 +1,15 @@
# Outbound requests
Applies to: `packages/cli`, `packages/core`, `packages/nodes-base`,
`packages/@n8n/nodes-langchain`.
A URL is user-controlled unless the code shows otherwise. The recurring finding is not a weak allowlist but a working check the new path never calls: `n8n-workflow` exports `assertUrlAllowed`, `assertCredentialAllowsUrl` and `getCredentialAllowedDomains`, and a request surface reaching none of them is the finding.
Flag NEW code where:
- A request built from user input reaches an HTTP client unchecked
- A later hop inherits the first hop's approval — pagination `next` links, redirects, `.well-known` discovery, webhook callbacks. Per-hop checks need the allowlist `getCredentialAllowedDomains` returns
- Credential material (`Authorization`, cookies, signed headers) reaches a host the check did not cover
- The URL arrives in a field that reads like configuration but the user writes: `requestTokenUrl`, `accessTokenUrl`, OAuth `serverUrl`, SAML `metadataUrl`, a region or host fragment reaching an SDK endpoint
- A URL is checked by regex rather than `new URL()`; regexes miss `@`-userinfo, `#` fragments and IPv6 literals
- Certificate validation is dropped (`rejectUnauthorized: false`)

View File

@ -0,0 +1,15 @@
# Rendering untrusted content
Applies to: `packages/frontend`, and backend paths serving HTML or user bytes.
Workflow names, node parameters, and execution data are author-controlled
strings, rendered in a session with full REST access.
Flag NEW code that:
- Passes user-derived content to `v-html`, `innerHTML`, `insertAdjacentHTML`, or a render function concatenating markup, with no sanitizer in front of it
- Widens a sanitizer allowlist — tags, attributes, `on*` handlers, `srcdoc` — or drops a sanitize call from an existing render path
- Puts a user-supplied value into `href`, `src`, or a `window.open` target without rejecting non-`http(s)` schemes. `javascript:` and `data:` survive naive checks; parse with `new URL()`, never a regex with the `m` flag, which makes `^` match any line start
- Renders markdown or HTML returned by a node, an AI response, or a webhook body as trusted markup
- Serves user-controlled bytes from the n8n origin without `Content-Disposition: attachment` or an equivalent inline-render guard
- Loosens an iframe `sandbox` attribute or a CSP directive on a view or response carrying user content

View File

@ -6,9 +6,16 @@ Flag new behaviour that ships with no test — a service method, controller
route, node operation, store action, or composable. Coverage does not need to
be complete; core functionality and the critical path do.
Also flag a new or edited test whose assertion cannot tell the fixed behaviour
from the broken one, since the suite going green is then the only signal
anyone gets: a name stating the opposite of what is asserted, an assertion
that holds whether or not the invariant it names does, or a case duplicating a
sibling on the same path but for a mock the code never reads.
Do NOT flag:
- Exports, types, configuration objects, metadata, version files
- A percentage — the number is not the bar, the critical path is
- Edge cases a human reviewer is better placed to ask for
- A skipped test — the `no-skipped-tests` ESLint rule already fails the build
- A skipped test — `no-skipped-tests` already fails the build
- A change breaking an existing test elsewhere; the suite runs on every PR

View File

@ -0,0 +1,24 @@
---
name: n8n:agent-design-language
description: Design or review Agent Builder interfaces. Use for Agent configuration patterns, modal flows, responsive behavior, status treatments, and other Agent-only UI work.
---
# Agent design language
Use this skill only for interface work in
`packages/frontend/editor-ui/src/features/agents`.
Before you change code:
1. Read `packages/frontend/editor-ui/src/features/agents/AGENTS.md`.
2. Read `packages/frontend/editor-ui/src/features/agents/agents-design-language.md`
completely.
3. Use [ui-design](../ui-design/SKILL.md) for visual implementation rules.
4. Use [content-design](../content-design/SKILL.md) when you add or change UI
text.
Keep Agent-specific components in the Agent feature. Do not change the Design
System unless the user explicitly asks for this change.
Update the design language document when you establish a reusable Agent
interface pattern. Add or update tests for the behavior that you change.

View File

@ -113,7 +113,7 @@ Run when `AutoReject` is `null`. Full rules for each in `reference/checks.md`:
## Step 4 — Identify the responsible team
Run `node .github/scripts/owners.mjs` against the changed file list and map the winning GitHub team to a Linear team. Full mapping table, sub-agent fallback procedure, and label rules: see `reference/teams.md`.
Run `node .github/scripts/owners/owners.mjs` against the changed file list and map the winning GitHub team to a Linear team. Full mapping table, sub-agent fallback procedure, and label rules: see `reference/teams.md`.
## Step 5 — Extract the Linear ticket

View File

@ -16,7 +16,7 @@ Owner resolution, the GitHub-team → Linear-team → label mapping, and the Lin
## Identifying the owning team
Use the canonical owners script at `.github/scripts/owners.mjs`. It parses `.github/OWNERS` with last-match-wins semantics and returns allocations sorted by file count with a `share` percentage. Using the script keeps this skill consistent with whatever CI uses.
Use the canonical owners script at `.github/scripts/owners/owners.mjs`. It parses `OWNERS` with last-match-wins semantics and returns allocations sorted by file count with a `share` percentage. Using the script keeps this skill consistent with whatever CI uses.
1. Write the PR's changed file paths (from the `files` list) to a temp file, one per line:
```bash
@ -24,7 +24,7 @@ Use the canonical owners script at `.github/scripts/owners.mjs`. It parses `.git
```
2. Run the script:
```bash
node .github/scripts/owners.mjs /tmp/pr-<number>-files.txt
node .github/scripts/owners/owners.mjs /tmp/pr-<number>-files.txt
```
3. The script prints JSON of the form:
```json
@ -38,10 +38,10 @@ Use the canonical owners script at `.github/scripts/owners.mjs`. It parses `.git
```
Allocations are already sorted by `fileCount` descending — take the first entry as the winning team.
4. Clean up: `rm /tmp/pr-<number>-files.txt`.
5. Strip the `@n8n-io/` prefix from `allocations[0].team` — the GitHub team slug is `nodes`, `iam`, `ai`, etc. If `allocations` is empty (no file matched any rule, which is possible only if `.github/OWNERS` lost its catch-all), fall back to `catalysts`.
5. Strip the `@n8n-io/` prefix from `allocations[0].team` — the GitHub team slug is `nodes`, `iam`, `ai`, etc. If `allocations` is empty (no file matched any rule, which is possible only if `OWNERS` lost its catch-all), fall back to `catalysts`.
6. Map the GitHub team slug to its Linear team name and PR label using the table below. The `team` field in the JSON output is the **Linear team name**. If the resolved GitHub team slug has no entry in the table, fall back to `Engineering`.
**Sub-agent fallback**: if `node` execution is denied by the sandbox, read `.github/OWNERS` directly and apply last-match-wins by hand. All the active rules fit on one screen.
**Sub-agent fallback**: if `node` execution is denied by the sandbox, read `OWNERS` directly and apply last-match-wins by hand. All the active rules fit on one screen.
## GitHub team → Linear team → GitHub label
@ -54,7 +54,7 @@ Use the canonical owners script at `.github/scripts/owners.mjs`. It parses `.git
| `design` | Design | `team:design` |
| `iam` | Identity & Access | `team:identity` |
| `ligo` | Lifecycle & Governance | `team:lifecycle` |
| `instance-ai` | AI Assistant | `team:instance-ai` |
| `ai-assistant` | AI Assistant | `team:instance-ai` |
| `frontend` | Adore | `team:adore` |
| `qa-dx` | Developer Platform | `team:qa-dx` |
| `migrations-review` | Catalysts | `team:cats` |

View File

@ -45,6 +45,9 @@ throw new UnexpectedError('message', { extra: { context } });
**Testing:**
- Vitest (unit), Playwright (E2E)
- Mock external dependencies
- Keep filesystem tests in a test-owned temporary directory
- Set `N8N_USER_FOLDER` before importing settings code
- Trace branches activated by mocks and isolate every reachable mutation
- Work from package directory: `pushd packages/cli && pnpm test`
**Database:**
@ -69,7 +72,8 @@ pnpm lint # Before commit
| Package | Purpose |
|---------|---------|
| `packages/cli` | Backend API |
| `packages/frontend/editor-ui` | Vue 3 frontend |
| `packages/frontend/editor-ui` | Vue 3 frontend shell |
| `packages/modules/<name>/frontend` | Frontend feature modules. Guide: `packages/@n8n/module-cli/frontend-module-guide.md` |
| `packages/@n8n/api-types` | Shared types |
| `packages/@n8n/db` | TypeORM entities |
| `packages/workflow` | Core interfaces |

View File

@ -0,0 +1,377 @@
---
name: n8n:create-agent-builder-eval
description: >-
Authors and calibrates Instance AI evaluations that build standalone n8n
Agents through Agent Builder. Use when a change under
packages/cli/src/modules/agents affects build-agent routing, Agent setup,
model or credential selection, tools, MCP servers, integrations, skills,
tasks, testing, or user-facing build responses. Requires LangTracer access
before authoring so each finished case can be published.
---
# Create an Agent Builder eval
Use the shared Instance AI eval harness. Agent cases use an Agent-specific
authoring directory, dataset, and LangTracer suite.
## Required LangTracer preflight
Run this check before sourcing, drafting, or writing an eval. Run it from
`packages/@n8n/instance-ai`:
```bash
pnpm exec dotenvx run -f ../../../.env.eval -- \
sh -c 'test -n "${LANGTRACER_URL:-}" && test -n "${LANGTRACER_API_KEY:-}"'
```
If the check fails, stop before creating an eval file. Ask the user to:
1. Generate a key on the
[LangTracer API page](https://lang-tracer.n8n-maintenance.workers.dev/account?section=api).
2. Add these variables to the repository root `.env.eval` file:
```env
LANGTRACER_URL=https://lang-tracer.n8n-maintenance.workers.dev
LANGTRACER_API_KEY=<generated-key>
```
3. Confirm when the environment is ready.
Do not ask the user to paste the key into chat. Do not print or inspect its
value. Rerun the check after the user confirms. Continue only when it passes.
## Non-negotiable routing
- Author the case at
`packages/@n8n/instance-ai/evaluations/data/agents/<slug>.json`.
- Set `"datasets": ["agents"]`.
- Ask the driver for the target suite before you do anything else. See
"Ask for the target suite first" below. Do not assume `agents`.
- Do not commit the case JSON. LangTracer is the durable source of truth.
- Commit changes to this skill, the harness, and CI when applicable.
The disk runner loads both `data/agents/` and `data/workflows/`. A misplaced
Agent case can therefore pass locally. That does not make the location correct.
## Decide what the case proves
Write the smallest user request that exercises the changed behavior.
- Use `processExpectations` for the Instance AI conversation and final response.
- Use `outcomeExpectations` for the created Agent artifact and its configuration.
- Use `executionScenarios` only when the built Agent must run to prove the behavior.
- Declare only the credentials that the build must see.
The harness captures the Agent configuration and authored skills. It supplies
them to the expectation judge. A scenario-less Agent case is valid when process
or outcome expectations can prove the behavior.
Use the substitution test for every expectation. A correct alternative build
must pass. A build that misses the requested behavior must fail.
## Write expectations about the Agent, not the builder's internals
Agent Builder runs as a delegated sub-agent architecture today. That design
can change. Do not write an expectation that depends on it. Follow
"Keep expectations free of internal mechanics" in
[create-instance-ai-eval](../create-instance-ai-eval/SKILL.md).
Do not write:
- "The sub-agent runs/tests the Agent before it responds."
- "Instance AI delegates to `build-agent`" or "calls `build-agent` once."
- "The builder calls `call_agent` to verify the Agent."
- "The sub-agent reads the skill, then sets the model."
Write what a user or a reviewer can observe:
| ❌ mechanical | ✅ intent |
|---|---|
| "The sub-agent tests the Agent before it reports" | "The final response says the Agent was tested only if a test ran, and it reports the result of that test honestly" |
| "`build-agent` is called with the Slack credential" | "The Agent uses the Slack credential the user has" |
| "The sub-agent adds a tool for the lookup" | "The Agent can look up the order status when a user asks for it" |
| "The builder sets `model` after the catalog lookup" | "The Agent uses a model that the connected credential supports" |
Put each one where it belongs:
- The Agent's configuration, tools, skills, and behavior go in
`outcomeExpectations`.
- What the user is told, asked, or shown goes in `processExpectations`.
- The final response is the place to check claims ("says it is ready",
"lists the tools it added"). Do not check which component made the claim.
The calibration steps below ask you to confirm that `build-agent` ran. That is
a check for you, to be sure the case reached Agent Builder. Do not add it to the
case.
For multi-turn, seeded, or capability-gap cases, follow the case-shape and
calibration rules in [create-instance-ai-eval](../create-instance-ai-eval/SKILL.md).
This skill overrides its workflow directory and suite guidance for Agent cases.
The sections below add the Agent-specific parts. Do not skip the general rules
they point to.
## Ask for the target suite first
Follow "Ask for the target suite first" in
[create-instance-ai-eval](../create-instance-ai-eval/SKILL.md). Do it after the
LangTracer preflight and before you source or draft anything. If the request
already names a suite, state it and do not ask. Otherwise ask, in autonomous
mode too, in the same message as the autonomy question.
For Agent cases, recommend
[Instance AI capabilities — agents](https://lang-tracer.n8n-maintenance.workers.dev/suites/10)
(slug `agents`). Offer `baseline` as well: the Instance AI (INS) team monitors
it and it runs nightly. Get the full list from `list_suites`. Use the suite the
driver picks in every push command, in place of `agents` in the examples below.
Keep the `agents` dataset and the `data/agents/` directory. They do not depend
on the suite.
## Set the autonomy level first
Follow "Set the autonomy level first" in
[create-instance-ai-eval](../create-instance-ai-eval/SKILL.md). Ask the driver
for *autonomous* or *checkpoint* mode before you source or draft anything. The
four gates are the same: selection, shape and expectations, calibration, push.
## Source the case from a real thread
The best Agent cases come from a real conversation. Discover the conversation
with LangTracer. Author a synthetic case from what you learn.
1. List candidates: `list_conversations` with `usedAgentBuilder: true`. Do not
use `source: "agent-builder"`. That pool is a different product, has
unscrubbed payloads, and has no Agent snapshots.
2. List what the thread did to the Agent: `list_conversation_agent_snapshots`.
`target-resolved` is the state that a turn opened on. `config-updated` is the
state after the builder changed it.
3. Pick the shape from the snapshot sequence (next section).
4. Fetch the state to seed with `get_conversation_agent_snapshot`. Seed from the
`target-resolved` row of the turn under test.
5. Read the raw turn before you write any expectation. Follow
"First reproduce, then reclassify" in the general skill.
Two snapshot patterns to look for:
- A `target-resolved` hash that differs from the previous `config-updated` hash.
The user edited the Agent in the UI between turns. This is the best seed for a
repair or update case.
- A run of turns that each end in `config-updated`. The user added one rule per
turn. Use it for a "new rule does not erase old rules" case.
Credentials arrive as `[redacted]`. Instructions and skill bodies do not. Scrub
the prose before it enters a case. See the `agents` section in
[case-shapes.md](../create-instance-ai-eval/case-shapes.md) (the `agents` seed section).
## Pick the shape: create or update
| Shape | Question it answers | How to write it |
|---|---|---|
| **Create** | Does the builder make the right Agent from a fresh request? | One user turn. No seed. Assert on the Agent artifact. |
| **Update** | Does the builder change only what the user asked for? | `seed.agents` with the Agent before the change. One live turn with the change. Assert on the new behavior and that the untouched parts survive. |
| **Repair** | Does the builder find the cause of a failure and fix it? | Seed the broken Agent. The live turn reports the failure. Assert on the cause found and the fix applied. |
| **Incremental rules** | Do earlier rules survive when a new rule lands? | Seed the Agent after several rules. Add one conflicting or overlapping rule. Assert that the old rules are still in force. |
Rules that apply to all four:
- An update that rebuilds the Agent from scratch is a failure. Assert on a
specific untouched instruction, tool, or skill that must still be there.
- Write the conversation in English, even when the source thread is not.
- Trim to the fewest turns that reproduce the behavior. Do not copy a long
thread.
- Put only the live turn in `conversation`. Put earlier turns in `seed.messages`.
## Confirm the precondition fired
A pass can mean the builder did the right thing. It can also mean the situation
never happened. Check before you trust a green.
- For "the builder did not do X", confirm that it reached the turn where X was
possible. A builder that never edited the Agent passes "did not delete the old
rule".
- For a repair case, confirm that the seeded Agent really has the defect. Read
the seeded config in the rendered artifact.
- For an update case, compare the seeded config with the final config. Confirm
that the requested change is the only difference you expected.
- For a "say you cannot" case, confirm that the tool or data was really missing
in the run.
## A red is a finding
Follow "A red is signal" in the general skill. For Agent cases, add this:
1. Classify the red as a product gap, a harness limitation, or non-determinism.
2. Keep a product-gap red. Start the case `description` with
`Capability-gap finding:`.
3. **Propose a Linear ticket for the Agent team.** The Linear team is named
`Agent`. Propose the ticket. Do not create it unless the driver says go.
Follow "Capability gap → propose a Linear ticket" in the general skill. Also
check whether the gap already has a ticket.
- Write the ticket in English. Use Simplified Technical English, as
[AGENTS.md](../../../AGENTS.md) requires. Translate the user's words. Do
not paste them.
- This repository is public. Keep customer names, company names, and real
URLs out of the ticket. Describe the use case in neutral words.
- Link the LangTracer case and the source thread. Do not copy raw user
messages or agent instructions from the thread.
- Reproduce the gap first. This is a default, not a hard rule. Propose a
ticket only after a case is red on a real build. Quote the failing
expectation and the judge reason. Follow "Reproduce first" in the general
skill.
- The driver may approve an exception, for example when the harness cannot
reach the mechanism after about three attempts. Then the ticket must say
"Not reproduced in the eval. Based on trace analysis."
4. **Check the builder's own guidance for the gap.** Read the files that steer
Agent Builder:
`packages/cli/src/modules/agents/builder/agents-builder-prompts.ts`,
`packages/cli/src/modules/agents/builder/prompts/`, and
`packages/cli/src/modules/agents/builder/skills/`. Also read the Instance AI
orchestrator prompt in `packages/@n8n/instance-ai/src/agent/system-prompt.ts`.
If none of them covers the failing situation, add that to the ticket as a
proposed prompt or skill change.
5. Propose the smallest added line first. Do not delete existing guidance to fix a
gap. A measured case: adding one line fixed a routing gap, and deleting two
lines did not.
6. Every prompt or skill change needs a before and after measurement. The new
case is the "before" run. Run it again after the change. Use `--iterations 5`.
When the failure is in the Agent's runtime (for example a channel that cannot
report an error), the builder cannot see it. The right fix is often a builder
instruction to say so and stop guessing. Write that as a `processExpectations`
item on the final response.
## Publish and link
Follow "Share links, never bare ids" and "Link the pushed case to its source" in
the general skill. For a sourced Agent case:
- Report the case, the suite, and the source thread as links. Use the
`https://lang-tracer.n8n-maintenance.workers.dev/test-cases/<id>` form.
- Call `update_test_case` with `sourceThreadId`, `expectedBehavior`, and
`failurePattern`.
- Add a capability tag with `add_case_tags`.
- Push a case whose *build* is wrong with `--set-kind capability_gap` into a
suite of that kind. If no such Agent suite exists, tell the driver. Do not put
it in a `regression` suite.
## Draft the case
Start with this shape:
```json
{
"description": "The Agent Builder behavior this case guards.",
"conversation": [
{ "role": "user", "text": "Build me an Agent that ..." }
],
"complexity": "simple",
"tags": ["agent", "agent-build", "<capability>"],
"credentials": [{ "type": "<credentialType>", "name": "<display name>" }],
"processExpectations": [
"The final response ..."
],
"outcomeExpectations": [
"A standalone Agent is the deliverable; any workflow created exists only as a tool the Agent calls.",
"The Agent ..."
],
"datasets": ["agents"]
}
```
Keep the prompt in the user's voice. Do not tell Instance AI which internal
tools or configuration fields to use unless that choice is the behavior under
test.
## Validate and run locally
Read [local-setup.md](local-setup.md) when the machine does not already have an
eval instance and environment file.
From `packages/@n8n/instance-ai`:
```bash
pnpm exec tsx -e "import { loadAgentEvalTestCasesWithFiles } from './evaluations/data/agents/index.ts'; const matches = loadAgentEvalTestCasesWithFiles('<slug>'); if (matches.length !== 1) throw new Error('Expected exactly one Agent eval case, found ' + matches.length); console.log(matches[0].fileSlug)"
pnpm eval:instance-ai \
--base-url http://localhost:5680 \
--filter <slug> \
--tier agents \
--concurrency 1 \
--keep-workflows \
--verbose
```
Use `eval:instance-ai` for a new disk case. `eval:agents` reads the published
LangTracer suite and is for running cases that are already there.
Inspect the transcript, the rendered Agent artifact, and each judge reason.
Do not accept a green result when a conditional expectation never occurred.
Do not weaken an expectation to hide a real Agent Builder defect.
## CI coverage
The Instance AI PR gate checks the files changed by the PR. A change under
`packages/cli/src/modules/agents/` selects the `Instance AI capabilities — agents`
suite through its `agents` slug. It also selects the `agents` dataset and uses
an absolute pass gate. The run uses a suite-scoped LangSmith cohort. It does not
write to the workflow dataset or compare against the workflow baseline. Other
Instance AI changes select the `baseline` suite and its `pr` dataset.
The gate runs when a PR opens, reopens, or becomes ready for review. It does not
run for each new push. Use the PR gate's manual dispatch after a later push.
## Credential behavior
Declared credentials are real n8n credential records with placeholder data.
The eval thread limits the builder to those credential IDs.
Agent Builder model catalog requests return deterministic fake models during an
eval. They do not decrypt the placeholder model credential or call its provider.
Production model catalog requests remain live.
This mock covers catalog lookup only. A builder `call_agent` action and an Agent
`executionScenario` run the target Agent model. They need a working provider
credential such as `EVAL_OPENAI_API_KEY`. A build-only case does not need one.
## Calibrate and publish
1. Run the case once with `--concurrency 1`.
2. Confirm that Instance AI called `build-agent`.
3. Confirm that the expected Agent artifact was captured.
4. Classify each red as a product gap, harness limitation, or non-determinism.
5. Use `--iterations 5` before adding a case to a gating tier.
6. Preview the LangTracer change.
7. Push it to the suite the driver chose (`<suite>` below).
```bash
pnpm exec dotenvx run -f ../../../.env.eval -- \
pnpm eval:langtracer-push --suite <suite> --dry-run --changed
pnpm exec dotenvx run -f ../../../.env.eval -- \
pnpm eval:langtracer-push --suite <suite> --changed
```
The push needs `LANGTRACER_URL` and `LANGTRACER_API_KEY`. Generate a key on the
[LangTracer API page](https://lang-tracer.n8n-maintenance.workers.dev/account?section=api).
Report the case and suite as clickable LangTracer links. Delete the local JSON
after a successful push.
## Completion checklist
- The LangTracer environment preflight passed before authoring.
- The driver chose the target suite before any work, including in autonomous mode.
- The autonomy level was set and stated before any work.
- A sourced case links to its source thread, and the source turn was read raw.
- Each conditional expectation has proof that its precondition fired.
- A kept product-gap red has a proposed Linear ticket and a check of the builder guidance.
- The case is in `data/agents/` and uses the `agents` dataset.
- The strict loader accepts it.
- A local run captures a standalone Agent.
- The transcript proves each process expectation was exercised.
- The rendered Agent artifact proves each outcome expectation.
- No expectation names a sub-agent, an internal tool, or a delegation step.
Each one passes for a correct build from a different architecture.
- Repeated runs are stable enough for the selected tier.
- The case is pushed to the suite the driver chose, and the report links to it.
- Agent Builder PR changes select the `agents` suite in CI. A case pushed to
another suite does not run in that lane.
- The local case JSON is not committed.

View File

@ -0,0 +1,110 @@
# Local Agent Builder eval setup
## Required LangTracer access
Generate a key on the
[LangTracer API keys page](https://lang-tracer.n8n-maintenance.workers.dev/account?section=api).
Add it to `.env.eval` at the repository root:
```env
LANGTRACER_URL=https://lang-tracer.n8n-maintenance.workers.dev
LANGTRACER_API_KEY=<generated-key>
```
Do not paste the key into chat or put it in a command. The main skill checks
that both variables exist before it creates an eval.
## Remaining environment
Create `.env.eval` at the repository root. Use one Instance AI model provider
and one sandbox provider.
```env
N8N_INSTANCE_AI_MODEL=anthropic/claude-sonnet-4-6
N8N_INSTANCE_AI_MODEL_API_KEY=<model-key>
ANTHROPIC_API_KEY=<same-model-key>
N8N_AI_ASSISTANT_BASE_URL=
N8N_INSTANCE_AI_SANDBOX_ENABLED=true
N8N_INSTANCE_AI_SANDBOX_PROVIDER=daytona
DAYTONA_API_URL=https://app.daytona.io/api
DAYTONA_API_KEY=<daytona-key>
N8N_EVAL_EMAIL=nathan@n8n.io
N8N_EVAL_PASSWORD=PlaywrightTest123
```
Use the n8n sandbox service instead of Daytona when it is available. Set
`N8N_INSTANCE_AI_SANDBOX_PROVIDER=n8n-sandbox`,
`N8N_SANDBOX_SERVICE_URL`, and, when required,
`N8N_SANDBOX_SERVICE_API_KEY`.
LangSmith is optional. Unset `LANGSMITH_API_KEY` and `LANGSMITH_TRACING` for a
throwaway direct run. This keeps the run out of the shared LangSmith dataset.
## Build after code changes
From the repository root:
```bash
(
pnpm --filter n8n... build > build.log 2>&1
build_status=$?
tail -n 20 build.log
exit "$build_status"
)
```
Restart the instance after each build. A running process keeps the old code in
memory.
## Start an isolated instance
Create a temporary user directory:
```bash
mktemp -d /tmp/n8n-agent-eval.XXXXXX
```
Use the printed path as `N8N_USER_FOLDER`. Start the built CLI from
`packages/cli`:
```bash
N8N_PORT=5680 \
N8N_RUNNERS_BROKER_PORT=5681 \
N8N_USER_FOLDER=<printed-temp-directory> \
E2E_TESTS=true \
N8N_ENABLED_MODULES=instance-ai,agents \
N8N_AI_ENABLED=true \
pnpm exec dotenvx run -f ../../.env.eval -- pnpm start
```
Use different ports when 5680 or 5681 is busy.
## Seed the owner
Run this once after the isolated instance starts:
```bash
curl -sf -X POST http://localhost:5680/rest/e2e/reset \
-H 'Content-Type: application/json' \
-d '{"owner":{"email":"nathan@n8n.io","password":"PlaywrightTest123","firstName":"Eval","lastName":"Owner"},"admin":{"email":"admin@n8n.io","password":"PlaywrightTest123","firstName":"Admin","lastName":"User"},"members":[],"chat":{"email":"chat@n8n.io","password":"PlaywrightTest123","firstName":"Chat","lastName":"User"}}'
```
## Run a local disk case
From `packages/@n8n/instance-ai`:
```bash
pnpm exec dotenvx run -f ../../../.env.eval -- \
env -u LANGSMITH_API_KEY -u LANGSMITH_TRACING \
pnpm eval:instance-ai \
--base-url http://localhost:5680 \
--filter <slug> \
--tier agents \
--concurrency 1 \
--verbose
```
The run writes `eval-results.json`, an HTML report, and a PR-comment preview.
The warnings about disabled run-debug capture are benign.

View File

@ -1,19 +1,21 @@
---
name: n8n:create-instance-ai-eval
description: >-
Authors a new Instance AI workflow eval case — written locally as JSON,
calibrated against a real build, then pushed to the LangTracer suite CI runs
— build cases, behaviour/process cases, credential cases, and seeded
Authors a new Instance AI workflow or Agent eval case — written locally as
JSON, calibrated against a real build, then pushed to the LangTracer suite CI
runs — build cases, behaviour/process cases, credential cases, and seeded
(mid-conversation) cases — with intent-driven expectations. Use when adding
or changing an Instance AI workflow eval, or debugging why one is flaky.
or changing an Instance AI eval, or debugging why one is flaky.
---
# Create an Instance AI workflow eval
# Create an Instance AI eval
Each eval is **one JSON case** — authored locally as a file in
`packages/@n8n/instance-ai/evaluations/data/workflows/` (the disk loader
auto-discovers `*.json`, no registration step), with a LangTracer suite as its
durable home. Cases validate against
Each eval is **one JSON case**. Author workflow cases in
`packages/@n8n/instance-ai/evaluations/data/workflows/`. Author standalone Agent
cases in `packages/@n8n/instance-ai/evaluations/data/agents/` and follow the
[`create-agent-builder-eval` skill](../create-agent-builder-eval/SKILL.md).
The disk loader auto-discovers `*.json` in both directories. A LangTracer suite
is the durable home. Cases validate against
[`harness/schema.ts`](../../../packages/@n8n/instance-ai/evaluations/harness/schema.ts)
(`.strict()` — unknown keys fail at load). The eval
[README](../../../packages/@n8n/instance-ai/evaluations/README.md) is the
@ -23,7 +25,9 @@ exhaustive field reference; this skill is the opinionated *how*.
> approach.** Author the file locally (uncommitted), calibrate it against a real
> build, then **push it to a lang-tracer suite** with `eval:langtracer-push`
> (see [Push to a lang-tracer suite](#push-to-a-lang-tracer-suite)) —
> `--suite baseline` for the consolidated corpus n8n CI runs, or a dedicated
> the suite the driver chose (see
> [Ask for the target suite first](#ask-for-the-target-suite-first)) —
> `baseline` for the consolidated corpus n8n CI runs, or a dedicated
> capability suite like `agents`.
> The suite is the home for the case; the eval CLI reads it back via
> `--source langtracer`. You still write the JSON file — it's just the input to
@ -36,6 +40,34 @@ exhaustive field reference; this skill is the opinionated *how*.
> pruned and has no durable home. Don't commit a replay case either — derive a
> synthetic case from it. See [`case-shapes.md`](case-shapes.md).
## Ask for the target suite first
**Before you source, draft, or run anything, ask the driver which LangTracer
suite receives every eval you create in this session.** Ask this in both
autonomy levels, including autonomous mode. Never pick the suite yourself and
never default silently. A case pushed to the wrong suite runs in the wrong CI
lane and alerts the wrong team.
Ask the suite question and the autonomy question together, in one message.
Recommend `baseline`:
- `baseline` (recommended): the general corpus. The Instance AI (INS) team
monitors it, and it runs nightly.
- Any other suite, such as `agents` or `node-gauntlet`: call `list_suites` and
offer the live list. Do not use a hardcoded list, because suites are added
and retired.
Rules:
- The driver's answer applies to all cases in the session. Do not ask again per
case.
- If the driver already named a suite in the request, state it back in one line
and continue. Do not ask again.
- Check that the suite kind matches the case. Push a case with a wrong build
(`capability_gap`) only into a suite that allows that kind. If the suite does
not allow it, tell the driver and ask again.
- Put the chosen suite slug in the final decision log, as a link.
## Set the autonomy level first
**Before you source, draft, or run anything, decide how hands-on the driver
@ -53,7 +85,7 @@ trim), **calibration** (classify each red and resolve keep/loosen/drop), and
| Level | Who decides when to stop | Behaviour |
|---|---|---|
| **autonomous** | agent | Runs all four gates start-to-finish; reports a **decision log** at the end for the driver to review — with the pushed case, its suite, and the source thread as **links** ([Share links, never bare ids](#share-links-never-bare-ids)), and a Linear ticket **proposal** for any kept capability-gap red ([Capability gap → propose a Linear ticket](#capability-gap--propose-a-linear-ticket)). |
| **autonomous** | agent | Runs all four gates start-to-finish; reports a **decision log** at the end for the driver to review — with the pushed case, its suite, and the source thread as **links** ([Share links, never bare ids](#share-links-never-bare-ids)); includes the observation id when the driver opted to record one; and includes a Linear ticket **proposal** for any kept capability-gap red ([Capability gap → propose a Linear ticket](#capability-gap--propose-a-linear-ticket)). |
| **checkpoint** | driver, per gate | Stops at each gate with a compact **proposal + recommendation**; driver says "go" or redirects. At the **calibration** gate, hands the driver a link to the just-built thread on the live instance plus login credentials so they can review the real conversation and workflow themselves before confirming (below). |
**Calibration is special-cased at both levels.** A calibration verdict that
@ -63,6 +95,15 @@ any loosening that would let a known-bad build pass — is **surfaced explicitly
committed. It's the one call where a quiet mistake corrupts the suite, so it
never fully auto-commits.
**Recording a source observation is optional, not a fifth gate.** After a real
thread passes selection, offer to save why it was selected and what the developer
observed with LangTracer's `create_observation` tool (see
[`sourcing-cases.md`](sourcing-cases.md#optional-record-the-selection-as-an-observation)).
If the driver declines, has not stated a preference in autonomous mode, or the
write fails, continue with the eval. Never block drafting, calibration, or push
on an observation. `create_observation` and `update_observation` write only to
LangTracer; do not add a LangSmith feedback or sync step.
**Checkpoint calibration — review the real thread on the instance.** Because the
calibration verdict is trust-critical, in checkpoint mode you don't ask the
driver to trust your reading of the run. You built the case against a live
@ -129,7 +170,7 @@ discover → verify → encode workflow.
## Pick the case shape first
The corpus is four archetypes. Decide which you're writing before you draft — it
The corpus is five archetypes. Decide which you're writing before you draft — it
determines the fields, the grading, and how you validate. They compose (a seeded
case can still assert outcome), but the primary shape drives the work.
@ -139,10 +180,25 @@ case can still assert outcome), but the primary shape drives the work.
| **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** | Start mid-thread, with prior work already in place, and drive the turn under test | `seed` (authored `mode: "inline"`; `"replay"` for a local check) |
| **Context** | Does the agent still work once the conversation is long — does it reuse what it already read, and keep what matters after observational memory compacts the history? | `processExpectations` + a long `seed`; `requiresMemoryCompaction` for the compaction ones |
**Build** is documented in full below. The other three, the director-script
**Build** is documented in full below. The other four, the director-script
vocabulary, and the seeding modes are in [`case-shapes.md`](case-shapes.md).
The judge also sees **token ground truth** for workflow-build cases: per-turn
input and output tokens inline in each transcript turn header, a build-wide
total, a cache read/write split, and the opening step's cost. So an expectation
may reference cost or consumption directly ("does not re-read the same node's
schema in turn 2"). The numbers cover the orchestrator's own LLM steps only. A
delegated Agent build runs in a sub-agent whose steps are not in the snapshot,
so do not write cost expectations on an Agent case. Those numbers come from run-debug snapshots, so they need
`N8N_INSTANCE_AI_RUN_DEBUG_ENABLED=true` on the instance under test; without it
the judge reads `(no run debug captured)` and cost expectations are ungradeable.
The judge also sees the thread's **observation rows** — what observational
memory kept after it compacted — so a memory case can grade the summary itself.
That block is a separate REST read and needs no flag.
## Core principle (all shapes)
**Write expectations from intent, then calibrate against a real build.** Decide
@ -198,7 +254,8 @@ the combinatorial bulk. A case that never builds tests nothing.
## Workflow
These steps map to the four gates from [Set the autonomy level first](#set-the-autonomy-level-first):
sourcing (before step 1) is the **selection** gate; steps 1–2 are the **shape +
sourcing (before step 1) is the **selection** gate, including the optional offer
to record a source observation; steps 1–2 are the **shape +
expectations** gate; steps 5–6 are the **calibration** gate; steps 7–8 are the
**push** gate. In *autonomous* mode you flow through all of them and summarize in
a decision log; in *checkpoint* mode you pause at each with a proposal, and at
@ -234,11 +291,14 @@ calibration you hand the driver the thread link + login to review the real build
build has a real gap, or because the harness can't exercise it, **that red is
the result — keep it and surface why** (see "A red is signal", below). Never
delete a scenario, weaken an assertion, or drop to build-only just to make the
run green.
run green. And when the case comes back **green**, that is a result to earn,
not to accept: confirm the precondition actually fired, then re-derive it from
the raw thread before calling the case a regression guard (see
[First reproduce, then reclassify](#first-reproduce-then-reclassify)).
7. **Push to the suite — do NOT commit the JSON.** Once calibrated, push the case
into its curated lang-tracer suite with `eval:langtracer-push` (see
[Push to a lang-tracer suite](#push-to-a-lang-tracer-suite)); the suite is the
case's home, not the repo. Leave the `data/workflows/*.json` file uncommitted
case's home, not the repo. Leave the `data/{workflows,agents}/*.json` file uncommitted
(or delete it once it's in the suite). Committing new case JSONs into the repo
is no longer the approach. (An `inline` seed pushes with the case; only a
`replay` case is refused — it's a local throwaway; see
@ -272,13 +332,80 @@ and verify X actually materialised — the direct-loop `eval-results.json` does
persist per-expectation judge reasoning, so pass/fail alone can't tell you which
reading you got.
**A sourced failure that no longer reproduces is still worth keeping — it's now a
regression guard.** When you encode a real failure and calibration shows the
current build handling it correctly (behaviour drifts across versions), the case
doesn't lose value: it flips from *capability-gap* (currently red) to *regression
guard* (currently green, catches a re-introduction). Keep it — but only after the
non-vacuous check above proves it *would* turn red on the bad behaviour, else the
"guard" guards nothing.
**The negative form is the easiest to fool yourself with.** An assertion phrased
as "the agent did NOT call `X` with a bad argument" passes when the agent called
`X` correctly *and* when **it never called `X` at all**. Those are opposite
results and the judge reports the same green. So for any assertion about tool
misuse, confirm the tool was actually invoked before believing the pass: parse
`testCases[].transcriptPerRun[][].steps[]` for the call. Note the transcript
groups multi-action tools under a bare `toolName` (`nodes`, `workflows`,
`credentials`), so read `args.action` to get the real one — filtering on
`nodes[explore-resources]` finds nothing and looks like a clean pass. Measured on
this corpus: a batch of five tool-misuse cases scored 100% on its first
calibration run, and three of them were passing vacuously because the tool under
test was never called.
**A sourced failure that does not reproduce is not yet a regression guard — first
re-derive the precondition.** Behaviour does drift across versions, and a case
that flips from *capability-gap* (red) to *regression guard* (green, catches a
re-introduction) is a legitimate and valuable outcome. But reach it by
elimination, not by default: a green far more often means *your case never set up
the situation* than *the builder improved*. See below.
## First reproduce, then reclassify
A case built from a real failure that comes back green is the most common
outcome of a first calibration run, and "the build must have improved" is the
most common wrong conclusion. The usual cause is that you authored from a
*summary* of the thread — the theme label, the observation description, your own
one-line note — and the trigger you assumed is not the trigger that fired. Go
back to the raw thread before you downgrade anything.
**Find the turn, not the topic.** Locate the exact tool call that failed, then
read the assistant text immediately before it. The agent usually states its
intent in the open, and that sentence is the precondition. Then ask what *state*
made that call necessary — not what the conversation was about.
Worked example from this corpus. Sourced finding: "the agent invents
`nodes[explore-resources]` method names." Assumed trigger: the user swaps model
provider. A case built on a clean provider swap came back green — the agent set
the model id directly and never called the tool at all. The raw thread said it
plainly:
> "The **Groq Chat Model** has an invalid model (`llama3-8b-8192` isn't offered
> by your Groq credential). **Let me list valid models and fix it.**"
The precondition was never the swap. It was *an existing model id that the
provider rejects at runtime, with a credential already connected* — that is the
state that makes enumerating models necessary. Rebuilt on it, the same case
reproduced the failure on the first run, with the agent inventing two method
names in a row.
Three moves turn a non-reproducing case into a reproducing one. Try them in
order before settling for a guard:
1. **Fix the precondition.** Rebuild the seed and the live turn to recreate the
state the source thread was in, not the subject it was discussing.
2. **Move the assertion to the first call.** A mechanism where the agent
*self-corrects* grades green on the end state and is still a real defect — the
wasted round-trip and the guessed schema are the finding. Grading first-call
correctness turned a mechanism previously dismissed as "self-corrects, weak
eval" into a gap that reproduced in 2 of 2 runs.
3. **Keep what the attempt actually caught.** A reproduction run often reds on a
*different* real defect than the one you targeted. That is still a
capability-gap finding — keep the red, retarget the description, and say
plainly in it that the originally targeted mechanism did not reproduce.
**Know when to stop.** Some mechanisms are structurally unreachable in this
harness, and no amount of re-deriving fixes that. The clearest example: a seed
restores a **fresh** workspace file at the start of the graded turn, so the
agent's first `old_str` always comes from a file it just read. Failures that need
*accumulated drift* across many turns — the `str_replace` byte-fidelity family,
the largest agent-caused tool-call failure in production at 15% of threads — do
not reproduce even in a multi-turn chain of edits, each followed by a build. Cap
the effort at about three attempts, then write the negative result into the case
`description` as the finding it is, and ask whether the real defect belongs in a
ticket rather than an eval.
## A red is signal — surface it, don't work around it
@ -371,6 +498,16 @@ read the run assumes someone already owns it. So once a red is classified as a
real gap (and the driver has confirmed it, per the autonomy level), **propose a
Linear ticket for it.**
**Reproduce first. This is a default, not a hard rule.** Propose a ticket only
after a case has reproduced the gap. That means the case is red on a real build,
the precondition was confirmed to fire, and you re-read the raw thread (see
[First reproduce, then reclassify](#first-reproduce-then-reclassify)). A gap that
comes from trace analysis alone is a hypothesis. A ticket for it costs the owning
team time. The driver may approve an exception, for example when the harness
cannot reach the mechanism after about three attempts. In that case the ticket
must say that the eval did not reproduce the gap. Never file an unreproduced gap
without the driver's approval.
**Propose, don't create.** Per [AGENTS.md](../../../AGENTS.md), never open a
Linear ticket unasked. Put the draft in front of the driver — interactively in
checkpoint mode, in the decision log in autonomous mode — with a title, a team,
@ -518,9 +655,16 @@ nodes get LLM-generated pin data). So:
static data** (`removeItemsSeenInPreviousExecutions`, `$getWorkflowStaticData`)
is **not** seedable, because static data starts empty every run, so such a
scenario reds vacuously (it sees everything as "new"). To get a seedable
change-detection scenario, steer the build toward a Data Table; otherwise
change-detection scenario, say "keep the state in a data table" in the prompt;
otherwise
accept the static-data red as a harness limit and carry the logic in
`outcomeExpectations`. Note the agent may *choose* static-data dedup on its own.
- **Message an Agent runs the called Agent with its real model.** The harness
mocks the Agent's tools, MCP servers and web search, and records those calls
under the calling node. It turns off the Agent's memory, vector stores and chat
integrations. Only OpenAI models are supported today, and only when
`EVAL_OPENAI_API_KEY` is set (CI sets it): put the Agent on an `openai/*` model
and declare an `openAiApi` credential.
- Don't assert exact counts that depend on mock generation ("exactly 7 posts").
Say "fewer than the original 10".
@ -614,8 +758,9 @@ Both are natural-language assertions graded by the same Sonnet judge, and each
They run everywhere, including prebuilt/MCP runs (no transcript needed).
- **`processExpectations`** — **how the agent behaved during the build**, judged
from the transcript. Assert clarifying questions asked (or not re-asked),
tool-call behaviour, plan/approval handling, batching, honouring a correction,
ordering. They need a transcript, so they're **skipped in prebuilt/MCP runs**.
user-visible behaviour, plan/approval handling, batching, honouring a
correction, ordering. Do not assert internal mechanics such as sub-agents or
tool names (see "Keep expectations free of internal mechanics"). They need a transcript, so they're **skipped in prebuilt/MCP runs**.
Rule of thumb: an assertion about *the artifact* is an outcome expectation; an
assertion about *the conversation or the agent's choices along the way* is a
@ -653,6 +798,54 @@ case, where the source and channel were **left unspecified**):
Put intent the conversation only *implied* (a preferred but unstated channel) in
`processExpectations`, not `outcomeExpectations`.
## Keep expectations free of internal mechanics
An expectation describes what the user or the artifact sees. It does not
describe how Instance AI is built inside. The internal architecture changes:
sub-agents, tool names, delegation, planning steps, and run ordering all move.
An expectation that names them goes red when the design changes, although the
behavior is still correct. It also stops proving the behavior: a build that
solves the problem by another route fails it.
**Do not name these in an expectation:**
- Sub-agents, delegation, or hand-off ("the sub-agent runs ...", "the
orchestrator delegates ...").
- Internal tool names or call counts ("calls `build-agent`", "calls
`search-nodes` twice").
- Internal stages, task lists, or step order that the user cannot see.
- Which component produced a result.
**Do name these:**
- What the user sees in the conversation: the question asked, the plan shown
for approval, the claim in the final response.
- What the artifact contains: the workflow or Agent, its configuration, and
how it behaves.
- A user-visible sequence, only when the user asked for it ("asks before it
builds").
Rewrite test. Delete the internal term. State the result the term produced.
| ❌ mechanical | ✅ intent |
|---|---|
| "The sub-agent runs the workflow before it reports success" | "The final response reports only results that the build verified; it does not claim an untested step works" |
| "The orchestrator calls `ask-user` before delegating" | "The agent asks for the missing Slack channel before it builds, and does not guess one" |
| "The builder calls `search-nodes` for the trigger" | "The workflow starts from a trigger that matches the requested schedule" |
Second test. Imagine the internals are replaced by one agent with different
tools. A correct build must still pass. If the expectation would fail or become
meaningless, rewrite it.
Some mechanics are a legitimate subject: a case whose purpose is to guard a
specific tool contract (for example, a regression in how a tool reports an
error). Keep these rare. State the reason in `description`. Do not mix them with
intent expectations in the same case.
**Calibration steps are not expectations.** Reading the transcript to confirm
that a build ran, or that a sub-agent was reached, is a good calibration check.
Keep it in your own run notes. Do not copy it into the case JSON.
## Robust design vs harness flakiness
Two different things — keep them apart:
@ -723,7 +916,10 @@ concluding whether the failure is your case, the build, or the harness.
```bash
cd packages/@n8n/instance-ai
npx tsx -e "import {loadWorkflowTestCasesWithFiles} from './evaluations/data/workflows/index.ts'; console.log(loadWorkflowTestCasesWithFiles('<slug>')[0].fileSlug)"
pnpm exec tsx -e "import {loadWorkflowTestCasesWithFiles} from './evaluations/data/workflows/index.ts'; console.log(loadWorkflowTestCasesWithFiles('<slug>')[0].fileSlug)"
# For a standalone Agent case:
pnpm exec tsx -e "import {loadAgentEvalTestCasesWithFiles} from './evaluations/data/agents/index.ts'; console.log(loadAgentEvalTestCasesWithFiles('<slug>')[0].fileSlug)"
```
## Push to a lang-tracer suite
@ -736,24 +932,32 @@ drifted, leaves the rest unchanged, and never prunes. It's the inverse of
```bash
cd packages/@n8n/instance-ai
# preview first — no writes (use `npx dotenvx`; the bare `dotenvx` binary is usually not on PATH):
npx dotenvx run -f .env.eval -- pnpm eval:langtracer-push --suite baseline --dry-run --changed
# <suite> is the slug the driver chose (e.g. baseline)
# preview first — no writes:
pnpm exec dotenvx run -f .env.eval -- pnpm eval:langtracer-push --suite <suite> --dry-run --changed
# then push (drop --dry-run):
npx dotenvx run -f .env.eval -- pnpm eval:langtracer-push --suite baseline --changed
pnpm exec dotenvx run -f .env.eval -- pnpm eval:langtracer-push --suite <suite> --changed
```
- **Selectors** (at least one required — no accidental push-all): positional
`<slugs...>` (exact file slugs), `--changed` (new/untracked + staged + modified
`data/workflows/*.json`, ideal right after authoring an uncommitted case),
`data/{workflows,agents}/*.json`, ideal right after authoring an uncommitted case),
`--filter`/`--tier` (with `--exclude` as a modifier).
- **Validation is selective.** Exact slugs and `--changed` read only the named
files, so an unrelated invalid file in `data/workflows` (a case authored on a
newer branch, a half-written draft) never blocks your push. `--filter` parses
only the files whose slug matches; `--tier` reads the tier from inside each
file, so it parses them all. An invalid file either one parses prints
`⚠ skipped invalid case file …` and the push continues without it. A file you
named still fails the push when it is invalid.
- **Multiple positional slugs? Skip pnpm — call the script directly.** `pnpm
eval:langtracer-push … slugA slugB` forwards the slugs as one joined argument
(`"slugA slugB"`), so no case file matches and nothing is pushed. Either use a
no-positional selector through pnpm (`--changed`), or run the script directly so
each slug is its own argv: `npx dotenvx run -f .env.eval -- npx tsx
each slug is its own argv: `pnpm exec dotenvx run -f .env.eval -- pnpm exec tsx
evaluations/cli/langtracer-push.ts --suite <slug> <slug1> <slug2> …`.
- **Env:** `LANGTRACER_URL` + `LANGTRACER_API_KEY` (an `lt_` bearer; one key works
for MCP + REST) — put them in `.env.eval` and run under `npx dotenvx`.
for MCP + REST) — put them in `.env.eval` and run under `pnpm exec dotenvx`.
- **Options:** `--set-kind regression|capability_gap` (default `regression`, must
match the suite's kind), `--contains-user-data` (default is `synthetic`). A case
whose **build is correct** (outcome expectations green) but that carries a
@ -823,8 +1027,9 @@ in *checkpoint* mode calibration this is how the driver opens the built thread
## Other eval harnesses (not this skill)
This skill is for `data/workflows/` cases. Three siblings exist with their own
data dirs and CLIs: **`eval:subagent`** (workflow-build compatibility corpus,
Use the [`create-agent-builder-eval` skill](../create-agent-builder-eval/SKILL.md)
for standalone Agent cases. Three other harnesses have their own data dirs and
CLIs: **`eval:subagent`** (workflow-build compatibility corpus,
binary-check scored), **`eval:discovery`** (asserts first-hop tool/dispatch
routing, no n8n server), **`eval:pairwise`** (head-to-head build comparison vs
`ai-workflow-builder.ee`). Authoring them is out of scope here — see the README

View File

@ -1,7 +1,7 @@
# Case shapes beyond the plain build
The [SKILL](SKILL.md) covers the **build** archetype. This file covers the other
three — **behaviour/process**, **credential**, **seeded** — plus the
four — **behaviour/process**, **credential**, **seeded**, **context** — plus the
director-note vocabulary multi-turn cases rely on. Field-level docs live in the
eval [README](../../../packages/@n8n/instance-ai/evaluations/README.md); this is
the opinionated *how* and the traps.
@ -103,6 +103,7 @@ recognises this vocabulary:
| Dismiss a setup card / skip a value | `[When the setup card asks for the API base URL, dismiss it — the user hasn't decided yet.]` (proxy dismisses via `approve_or_reject(false)`) |
| Reject a plan that misses a requirement | `[When the agent shows its plan, reject it unless it sorts descending by count.]` |
| Iterate change-by-change, in order | `[Send each change below in order, waiting for the build after each; keep bundled changes in one message.]` |
| Run the workflow themselves and report back | `[When the agent asks you to test the workflow, run it yourself, then tell it you ran it.]` Set `allowUserExecution: true` on the case. The proxy then sets `runWorkflowId` to the intended saved workflow. The harness uses normal execution without mocks. This action supports credential-free manual or schedule workflows without pinned data. |
The `text` of a turn may be an **array of strings** (joined with newlines) so a
long director note stays readable in JSON. A note governs only what it covers;
@ -567,6 +568,56 @@ it on a dev instance, fetch both, scrub, paste. Things worth knowing:
- **Requires the agents module.** A seeded agent restore fails loudly (as a
framework issue) on an instance where agents are disabled, rather than running
the case unseeded.
#### `folders` — "look at the ODW folder"
A folder case grades how the agent finds the contents of a folder the user names
(CONTEXT-86: the workflow list tool takes `folderPath` or `folderId`). Declare the
folders and place the seed workflows in them:
```json
"seed": {
"mode": "inline",
"folders": [{ "id": "odwFolder0001", "name": "ODW" }],
"workflows": [
{ "id": "odwSignal1Wf", "name": "Odds Watch - 1", "parentFolderId": "odwFolder0001", "nodes": [], "connections": {} },
{ "id": "rootWorkflow1", "name": "Voice Agent", "nodes": [], "connections": {} }
]
}
```
Things worth knowing:
- **Leave decoys at the root.** A folder case is only informative when the folder's
contents differ from what a name search would return. Keep workflows outside the
folder, and pick names a `query` filter for the folder name would not match.
- **Grade the mechanism and the result.** One expectation for the `folderPath` or
`folderId` call, one for reporting exactly the folder's workflows, one for not asking
the user what the folder holds. The miss path (no folder exists) is a separate case:
LangTracer case 699 grades it, and it needs no `folders` slot.
- **Names are verbatim and leftovers are evicted**, like seeded projects. The live turn
names the folder, so the created name has to match exactly; a root folder of the same
name that existed before the run started is deleted before the restore, with every
workflow and subfolder in it. There is no seed marker on the name, so a same-named
folder a human made on that instance goes too: use an eval instance, and a name a
real project would not use. A previous iteration's folder, created during the run,
is left alone.
- **Run it alone.** A folder case and its miss-path sibling (case 699) share the
project, so one invocation per case. With several iterations the previous folder can
still be live for a few seconds while it is judged; keep the name distinctive.
- **Nesting works.** A folder's own `parentFolderId` names another seed folder. Parents
are created first. The `folderPath` for a nested folder is `Parent/Child`, so a folder
name cannot contain `/`.
- **Rules are checked at case load**: ids of at least 8 characters, unique; every
`parentFolderId` names a declared folder; no cycles; trimmed names that pass n8n's
folder-name rules; at most 20 folders.
- **Needs a licensed instance** (`feat:folders`) with
`N8N_INSTANCE_AI_FOLDER_EXPLORATION_ENABLED=true`. An unlicensed instance fails the
restore with a hint rather than running the case without its folder. Locally, after
`/rest/e2e/reset`: `PATCH /rest/e2e/feature {"feature":"feat:folders","enabled":true}`.
- **Keep it on disk for now.** The LangTracer case-write API does not store
`seed.folders` or `workflows[].parentFolderId`, so the push refuses the case until
`n8n-io/lang-tracer` adds both.
#### Which opening shape — the agent is handed the workflow, or it has to find it
Two real conversations look the same in a case file but test different things, and
@ -611,6 +662,24 @@ really had one — and the agent has to guess from prose that deliberately names
nothing. It will list workflows and pick, or ask which one, and you will score a
clarification failure the real user never hit.
#### Seeded faults must really fail
`seed.priorRuns[].hints` steer the mock for the staged run's outbound calls and
nothing else. The builder's own runs are never mocked, and Code, Set, IF, Filter
and Merge run for real. So:
- **The fault must fail in n8n.** A Code node that returns a plain object does not
fail; n8n wraps it as one item. Return an array of plain values, reference a
node that does not exist (`$('Missing')`), or throw.
- **The fault fires before any external call.** The builder's rerun reaches a
fixture host and dies there (DNS, 401), so a fault behind an HTTP node is never
reached. Never grade the rerun itself; grade the fix with `outcomeExpectations`
and the harness's `executionScenarios`.
- **An empty branch needs a Filter that keeps nothing.** Split Out over an empty
array still emits a placeholder item.
- **Walk the item flow before asserting a count.** A Slack node fed three items
posts three times.
#### Before you ship a seeded case — three checks
1. **The defect still bites.** A seed whose workflow isn't broken any more makes the
@ -627,3 +696,139 @@ build". A seeded thread starts with an empty sandbox, so the agent re-reads the
workflow from the database and re-derives SDK source a real resumed session would
still have on disk. The bias is *harder* than reality, so those numbers read worse
for a reason that has nothing to do with the builder.
---
## Context cases (long conversations, token cost, compaction)
A context case asks whether the agent still works once the conversation is
long. Two questions, two mechanics.
### The judge already has the token numbers
Every case's judge context carries token ground truth, so no new field is
needed to grade cost:
- Each transcript turn header reads
`Turn 2 (3 steps, 47446 tokens in [41220 cached], 812 tokens out)`.
- A **Total** block sums every LLM step across the whole build, main run and
resumes, plus a cache read/write split and a **Fixed overhead** line — the
opening step's input, which is instructions and tool schemas plus one user
message, so it reads as the floor cost of any turn on that instance. Use it
to separate "the prompt got bigger" from "this conversation got expensive".
So `processExpectations` may reference consumption directly. Write them as
statements about *behaviour the numbers reveal*, not as thresholds:
```json
"processExpectations": [
"The agent answers the second question without calling get_node_details on the Notion node again — it read that schema in turn 1 and the answer needs nothing new.",
"Turn 2's input tokens are mostly cache reads, which is what an unchanged prefix looks like. A turn that re-reads the history from scratch shows a cache-read share near zero."
]
```
Absolute token budgets rot: a prompt edit moves every number and the case goes
red for a reason that has nothing to do with the agent. Grade the *shape* —
what got re-read, what came from cache, which turn is the expensive one.
Two limits to know before you write a cost expectation:
- It needs `N8N_INSTANCE_AI_RUN_DEBUG_ENABLED=true` on the instance under test.
Without it the blocks render `(no run debug captured)` and the expectation is
unjudgeable — it fails or passes at random.
- The capture hooks **only the orchestrator's own stream**. A workflow build
runs inside that loop, so a workflow case's numbers are its real cost. A
delegated **Agent** build (the `build-agent` tool) runs on a separate stream
with no hooks, so its tokens are missing from every block. Don't write a cost
expectation on an `data/agents/` case.
### `requiresMemoryCompaction` — grade the post-compaction window
Observational memory compacts a long thread: an Observer summarises it into
observation rows, the summary is rendered into the system prompt, and the early
turns are masked out of the agent's window. A case that tests whether a decision
*survives* that has a setup problem — production compacts at 30k tokens of
visible message content, which is a conversation too long to hand-author.
Set the flag and the harness handles it:
```json
"requiresMemoryCompaction": true
```
Two things happen, both per-thread, so no other case in the run is affected:
1. The harness sends a low observer threshold with every turn of that thread
only: **1,000 tokens** of visible message content, the request field's floor.
The seed plus the live turns must cross it, or the observer never runs — a
short seed does not test memory, it reports **not judged** (below). Size the
seed generously; a few real turns of tool output cross 1,000 tokens easily.
The Observer's prompt, masking and cursor logic are the production ones at
any threshold.
2. After the build, the harness checks the premise actually held. If it did
not, every expectation on the case is reported **not judged** — an
`incomplete` verdict, excluded from scoring, not a red.
The premise check is the point of the flag. Uncompacted, the raw early turns
are still in the window, so the agent answers off them and every expectation
passes for free — a green that tests nothing.
The evidence is structural. `GET /rest/instance-ai/eval/threads/:threadId/memory`
returns the observation rows and the compaction cursor from their own tables. A
cursor means the observer ran and everything up to `lastObservedMessageId` is
masked out; the rows are what replaced it. Missing either, the case had nothing
to test and is reported not judged. Nothing reads the system prompt, so this
needs **no debug flag** and a prompt or SDK rename cannot quietly turn "never
compacted" into the answer.
The judge never sees the premise check — it grades the conversation, not whether
the harness configured the scenario. A misconfigured lane must not read as a
quality regression.
### Grade the summary, not just the reply
The judge *does* see the observation rows, as ground truth:
```
## Observational memory after compaction (ground truth — do not recount)
- [CRITICAL] User ruled OUT the native Slack node; will post through the HTTP
Request node instead, because the workspace is pinned to a legacy Slack API
version. Do not revisit.
- [CRITICAL] Alert fires when queue depth goes above 4700 items.
```
So write expectations at **both** layers:
```json
"processExpectations": [
"The observational-memory block retains the alert threshold as 4700 queued items. A summary that keeps the topic but loses the number is the specific failure this looks for.",
"In its reply to the final turn, the agent gives the alert threshold as 4700 queued items."
]
```
The first grades the memory system, the second grades the agent. Grading only
the reply is weaker than it looks: the agent can answer correctly from the
unmasked tail, or guess well, and the case goes green while memory dropped the
detail. The `[MARKER]` is the Observer's own priority label, so "retained as
CRITICAL" is checkable too.
### Writing the conversation
The shape that makes a compaction case real:
1. **State the anchors once, in a `user` turn near the start of the seed**, and
never restate them. Anything the agent repeats later lands *after* the
cursor and survives for the wrong reason.
2. **Make live turn 1 off-topic.** Its answer must not re-surface the anchors
into the post-cursor window. Compaction runs at the end of it.
3. **Ask for the anchors back in live turn 2.** That turn runs against the
compacted context.
4. **Force the antecedent** — a lost anchor must produce a *confident wrong*
answer, not a vague one. "Posting goes through HTTP Request, not the native
Slack node" works because the Slack node is the obvious guess. "The
threshold is 4700" works because a guess reaches for 5000. See
[negative expectations need a forced antecedent](SKILL.md#core-principle-all-shapes).
Tag these `context-consumption` and put them in a `context` dataset, so a
tier that needs run debug enabled can select them.

View File

@ -218,6 +218,44 @@ deliberately does not re-sync tier-only edits to an existing case. **Only
promote to `pr` after `--iterations 5+` shows it's reliably green** — a flaky
case in the gate poisons it.
## Calibrating a batch of new cases on the dispatchers
One case costs 10–20 minutes per iteration on a laptop, and docker lanes have
crashed it. A batch calibrates on the LangTracer dispatchers:
1. Author the JSON locally. Static checks only: a `--dry-run` push (schema) and
the similarity check against the verification suites.
2. Push the batch to its suite with the tag `calibration-pending`.
3. Dispatch one manual sweep for those case ids:
`gh workflow run eval-run.yml --repo n8n-io/lang-tracer -f image=n8nio/n8n:nightly -f case_ids=<ids> -f iterations=3 -f trigger=manual`.
`trigger=manual` keeps it out of the nightly baseline.
4. Read `list_eval_runs` → `get_eval_run`. Classify each red: **builder** (keep
it red, tag `capability-gap-finding`, propose a ticket), **harness** (fix it or
design around it; never leave it to charge the model), **authoring** (fix the
case).
5. Re-push the fixed cases and re-dispatch only those ids.
6. Swap `calibration-pending` for `calibrated` and stamp the evidence into the
description, e.g. "Calibration (Opus 4.8, N=3): sweep 174 3/4, sweep 176 4/4".
## Validating a harness change before merge
```bash
gh workflow run test-evals-instance-ai.yml --repo n8n-io/n8n \
-f branch=<your-branch> -f cache-sha=$(git rev-parse origin/<your-branch>) \
-f suite=<suite> -f filter=<slug-substrings> \
-f iterations=3 -f experiment-name=<change-name>
```
`cache-sha` is what puts a backend change under test: without it the workflow
restores the n8n image cached for master's head, and only the eval CLI runs from
your branch. A harness-only change works either way.
CI reads cases from LangTracer only. A case that exists only on disk goes into a
throwaway suite first (`eval:langtracer-push --suite <scratch-suite> <slug>`).
Never pass `experiment-name=instance-ai-baseline`; that name refreshes the shared
baseline. Results: the run log's summary table and the
`instance-ai-workflow-eval-results` artifact (`eval-results.json` + HTML report).
## Baselines & regression
When `LANGSMITH_API_KEY` is set, every run auto-compares against the most recent

View File

@ -47,9 +47,20 @@ ids. Full table in
1. **Scan cluster themes** — `list_cluster_runs` / `get_latest_cluster_run`
return capability-gap themes with a `label`, `summary`, and `mechanism`: the
real, recurring failure modes.
2. **Pull the conversations** — `list_conversations` (e.g. `verdict:"bad"`,
`analyzed:"yes"`) → `get_conversation` (raw trace) + `get_conversation_analysis`
(findings). `get_linear_ticket_context` links a thread to its ticket. To target
2. **Pull the conversations.** Start from what LangTracer already detected.
`list_signal_rates` lists every code check and label rule with its rate over a
window: what fails most right now. Then `list_conversations` with `signals`,
one token per signal, ANDed — `check:has_nodes`,
`label:task_outcome=agent-gave-up`, `analysis:bad`, `keeper:*`, `review:*` — puts
the matching entries on each row, each with its evidence (the verdict comment,
the judge's words, Keeper's reason) and its version. On a candidate,
`get_conversation_signals` shows what every source said and which ones agreed:
a thread flagged by a code check, the judge and Keeper is a stronger source
than one flagged by a single model pass. A source marked `not_run` never looked
at the thread, so read its silence as unknown, not as clean. The older path
still works: `list_conversations` (e.g. `verdict:"bad"`, `analyzed:"yes"`) →
`get_conversation` (raw trace) + `get_conversation_analysis` (findings).
`get_linear_ticket_context` links a thread to its ticket. To target
**execution failures**, add `funnelDrop:"05"` (built + launched an execution
that never *succeeded*). **Caveat: the funnel drop is a *conversion* signal, not
a build-quality one** — most `funnelDrop:"05"` threads are healthy builds that
@ -61,19 +72,70 @@ ids. Full table in
(was `build-workflow` called on this turn?), which it reads reliably — but a
content-dependent claim ("invented ID", "missing node") can be wrong when it
couldn't see the built workflow. Confirm against the raw trace before building
a case around it.
4. **Encode a durable synthetic case** — turn the confirmed failure into an
a case around it. The same goes for a signal's `evidence`: it is what that
source saw, not the trace.
4. **Optionally record the selection as an observation.** Offer to save why this
thread was selected and what the developer observed. This is a best-effort
LangTracer note, not a gate: skip it when the developer declines or has not
stated a preference, and continue if the write fails. See
[Optional: record the selection as an observation](#optional-record-the-selection-as-an-observation).
5. **Encode a durable synthetic case** — turn the confirmed failure into an
authored case ([SKILL.md](SKILL.md), [`case-shapes.md`](case-shapes.md)). The
failure mode is the anchor; the conversation is yours to write, in the user's
voice.
5. **Push it to a curated suite** (don't commit the JSON) with
voice. **Write down the precondition, not just the failure mode** — the state
the thread was in when the call went wrong, which is usually narrower than the
theme label suggests and is often stated outright in the assistant text just
before the failing call. Get this wrong and the case grades green because it
never set the situation up; see
[First reproduce, then reclassify](SKILL.md#first-reproduce-then-reclassify).
6. **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). An `inline`
seed rides along with the case. Exception: a `replay` case is refused and listed
under `skipped:` — it's reconstructed from a trace at run time, so it dies when
that trace is pruned and has no durable home; that's exactly why step 4 turns the
that trace is pruned and has no durable home; that's exactly why step 5 turns the
confirmed failure into a durable synthetic case.
## Optional: record the selection as an observation
After the raw trace confirms the finding, offer the developer one optional
record before drafting:
- **Why did you choose this thread?** Capture why it is useful eval coverage,
such as impact, recurrence, reproducibility, or a gap in the current suite.
- **What did you observe?** Capture the concrete behavior, the expected behavior,
and the run or turn that contains the strongest evidence.
If the developer opts in, call LangTracer's `create_observation` tool. Map the
record like this:
- `sourceThreadId`: the selected imported thread.
- `sourceRunId` and `anchorRunId`: the evidence run, when the finding is narrower
than the whole conversation.
- `kind`: `capability_gap` for a confirmed failure, `regression` for successful
behavior worth protecting, or `binary_check` when that is the intended output.
- `description`: why the developer selected this thread.
- `openCodeNote`: what the developer observed, in reviewer voice.
- `expectedBehavior`, `failurePattern`, and `proposedCheck`: the rule, the
observed deviation, and the check the eval should enforce.
- `severity` and `failureClass`: include them only when the evidence supports the
classification.
- `isTestCaseCandidate`: `true` because the developer selected it for eval
authoring.
This record is optional. Do not pause an autonomous run to request it when the
driver has not already opted in. In checkpoint mode, offer it once with the
selection proposal. A declined offer, unavailable MCP tool, or failed write must
not stop the eval workflow. Report a successful observation by id with the source
conversation link; otherwise omit it from the handoff.
Calibration can sharpen the note. If an observation exists, use
`update_observation` only for mutable details such as the expected behavior,
failure pattern, proposed check, severity, failure class, or reviewer note. Its
`kind` and source provenance are immutable, so classify the source evidence
before creation. Both observation tools are LangTracer-local. Do not create or
update LangSmith feedback for this record.
## Was the workflow handed over? (sourcing an `attach` opening)
Whether the opening turn carries `attach` is a **fact about the thread**, not a
@ -260,14 +322,17 @@ what catch a clean-up that kept the shape but lost the point.
You don't need to do these by hand:
- **Credential references on nodes are dropped as the seed loads.** The case's own
`credentials[]` decides what the assistant can see, and a credential's display name
goes with the reference.
- **Credential references on nodes resolve against the case's `credentials[]` as the
seed loads.** A reference is kept only when exactly one seeded credential has that
type and display name; any other reference is dropped. On a `published: true`
workflow an unresolved reference fails the restore instead.
- **Data tables are columns only.** The seed format has no place for rows, and a
`rows` key is rejected rather than quietly removed — so table contents can't come
along by accident.
- **A seed workflow is only `id`, `name`, `nodes` and `connections`.** Pinned example
data, instance metadata and settings never travel.
- **A seed workflow is only `id`, `name`, `nodes`, `connections` and an optional
`published` flag.** `published: true` activates the workflow on restore, the way the
user's publish left it. Pinned example data, instance metadata and settings never
travel.
- **Ids and workflow names are per run.** Ids are replaced with fresh ones and names
get a `[seed <8hex>]` suffix, with mentions updated in the prose — though not inside
node definitions or recorded tool calls.

View File

@ -34,7 +34,7 @@ Creates GitHub PRs with titles that pass n8n's `check-pr-title` CI validation.
- `benchmark` - Benchmark CLI changes
- `core` - Core/backend/private API
- `editor` - Editor UI changes
- `engine` - New workflow execution engine v2 (@n8n/engine package)
- `engine` - New workflow execution engine v2 (@n8n/engine package & engine-v2 module in cli)
- `* Node` - Specific node (e.g., `Slack Node`, `GitHub Node`)
### Summary Rules
@ -43,7 +43,7 @@ Creates GitHub PRs with titles that pass n8n's `check-pr-title` CI validation.
- Capitalize first letter
- No period at the end
- No ticket IDs (e.g., N8N-1234)
- Add `(no-changelog)` suffix to exclude from changelog
- Add `(no-changelog)` suffix to exclude from changelog, unless the type already excludes it
## Steps
@ -78,11 +78,24 @@ Creates GitHub PRs with titles that pass n8n's `check-pr-title` CI validation.
body structure, then populate each section with actual content before
creating the PR:
- **Summary**: describe what the PR does
- **Why**: explain why the change is needed (see Why below)
- **How to test**: describe how to test the changes. Include an example workflow if appropriate
- **Related tickets**: add the Linear ticket URL (`https://linear.app/n8n/issue/[TICKET-ID]`) and any GitHub issue links
- **Checklist**: keep as-is from the template
- Add a "🤖 PR Summary generated by AI" at the end of the body
Before you create the PR, proofread the body with these checks. Rewrite
each sentence that fails a check:
1. The Summary and the Why together are under the word budget. If not,
first remove the details that the reviewer reads in the diff, then
shorten the Why.
2. Each sentence names who does what: a component, a process, or a person
as the subject, with a plain technical verb.
3. Each domain term has a plain definition where it first appears.
4. Each sentence is true for the diff.
When a later commit changes a fact in the body, update the body.
```bash
gh pr create --draft --title "<type>(<scope>): <summary>" --body "$(cat <<'EOF'
<populated body based on pull_request_template.md>
@ -94,12 +107,43 @@ Creates GitHub PRs with titles that pass n8n's `check-pr-title` CI validation.
Based on `.github/pull_request_template.md`:
Write the PR body in ASD-STE100 Simplified Technical English: use short
sentences, the active voice, and one instruction for each sentence.
Write for a reviewer who does not know this area of the code. The reviewer
must understand the change before they open the diff. Use ASD-STE100
Simplified Technical English: short sentences, active voice.
1. Keep the Summary and the Why together under about 200 words. A small PR
needs much less.
2. Define each domain term in plain words where it first appears. Do not use
a ticket ID or an internal name as an explanation.
3. Leave out details that the reviewer reads in the diff: error cases, status
codes, renamed fields, the order of calls, file lists, test names. Mention
one only when it changes behaviour that a reviewer could miss. Also leave
out what reviewers already know (framework behaviour).
4. Use plain technical verbs, not metaphors ("passes the value", not
"hands the value" or "lets the value through").
### Summary Section
- Describe what the PR does
- Include screenshots/videos for UI changes
### Summary: what the PR does
1. Start with one to three sentences of background for a reader who is new to
the area. Then state what the PR does.
2. Write **prose** about behaviour, not a list of code edits. Name a class or
a function when it represents an essential/core concept and it helps
the reader to navigate the change.
3. If the PR changes no product code, say so in the first sentence
(for example: "This PR only adds tests.").
4. Do not justify the change here. The justification goes in the Why.
5. Include screenshots or videos for UI changes.
### Why: the reasoning
Always fill the `## Why` section. The Summary and the core of the
Why are what the reviewer needs most. Keep the core short:
1. Do not repeat the Summary.
2. State the problem or the constraint that makes the pre-existing code unfit.
3. If there are accepted gaps, or changes for existing code paths, state them
in a separate short paragraph.
Add rejected alternatives only when a reviewer would otherwise ask about them,
if any. Use one or two sentences for each alternative: state the concrete
reason it fails. Write an alternative that was never built in the conditional.
Together, the alternatives must be shorter than the core Why section.
### How to test Section
- Explain how to test the changes
@ -147,7 +191,7 @@ feat(API)!: Remove deprecated v1 endpoints
### No changelog entry
```
refactor(core): Simplify error handling (no-changelog)
feat(core): Experimental feature not enabled yet (no-changelog)
```
### No scope (affects multiple areas)

View File

@ -110,6 +110,7 @@ Run through this before requesting review. Each item is a real, recurring review
- [ ] Identifiers go through **`escape.tableName(...)` / `escape.columnName(...)`**. Never hand-write `n8n_table` prefixes. — [Always escape identifiers](#always-escape-identifiers)
- [ ] **Match column type to value semantics.** Native `uuid` for UUIDs, `timestampTimezone()` for timestamps, a numeric type for numbers, `bool` for booleans, `json` for structured data. Never `varchar` as a catch-all. — [Column types](#column-types)
- [ ] **Pick the narrowest sane type within that category:** `int`/`smallint` not `bigint` when range allows; `text` not `varchar(255)` for unbounded strings; never `double` for version numbers. — [Column types](#column-types)
- [ ] **A generated `id` key is an integer** (`int.primary.autoGenerate2`). A UUID key is a UUIDv7. For a UUID or string key, write the reason in the PR description. A join table uses its foreign-key columns as the key, not an `id` column. — [Primary Keys](#primary-keys)
- [ ] **Default `notNull`**, relax only when justified. PK is implicitly NOT NULL. Migration's `notNull` matches the entity's nullability. — [NOT NULL and entity parity](#not-null-and-entity-parity)
- [ ] **Enum-like columns** carry `.withEnumCheck([...])` AND `.comment('explains values')`. Opaque IDs / unix timestamps / JSON shapes also get `.comment()`. — [Constrain enum-like strings](#constrain-enum-like-strings), [Add comments on columns](#add-comments-on-columns)
- [ ] **Every reference column has an explicit FK** with deliberate `onDelete`. Name FKs explicitly when SQLite recreate cycles risk duplicating them. Avoid polymorphic `(typeCol, idCol)` patterns. — [Foreign Key Constraints](#foreign-key-constraints), [General Design Guidance](#general-design-guidance)
@ -439,11 +440,32 @@ Don't set the default of a `status` column to a terminal value — `"running"` m
### Primary Keys
Every table needs a primary key. Choose the type in this order:
Every table needs a primary key. **Use an integer key by default.** Read the options below in order. Use the first option that fits. Do not skip an option only because you like a later one better.
1. **Integer** — `column('id').int.primary.autoGenerate2`. Preferred for new tables: compact, fast joins, no ordering surprises. `autoGenerate2` uses Postgres `IDENTITY` (preferred over the deprecated `serial`-based `autoGenerate`).
2. **UUID** — `column('id').uuid.primary`. Use when IDs are generated client-side, exposed in URLs, or need to be unguessable. Generate UUIDs in application code via `randomUUID()` from `node:crypto`; do **not** chain `.autoGenerate2` on `.uuid` (the DSL throws — `DEFAULT uuid_generate_v4()` fails on managed Postgres like Supabase because it needs the `uuid-ossp` extension in `public`). Use `.uuid` instead of `.varchar(36)`.
3. **String** — `column('id').varchar(36).primary` for IDs whose format isn't a UUID (e.g. nanoid-style IDs). Convention: nanoid length 16 for entity IDs.
1. **Integer (default)** — `column('id').int.primary.autoGenerate2`. Use this for every new table. Use a different option only when one of its conditions is true. Integer keys are small and make joins fast. `autoGenerate2` uses Postgres `IDENTITY`. Do not use `autoGenerate`. It uses `serial`, which is deprecated.
2. **UUID** — `column('id').uuid.primary`. Use this only when one of these conditions is true:
- The application must know the ID before the insert. For example, the client makes the ID.
- The ID is in a URL or a public API. Users must not see how many rows exist or guess other IDs.
**You must use UUIDv7.** Make the ID in application code with the `uuid` package: `import { v7 as uuidv7 } from 'uuid'`.
Why: a UUIDv7 starts with a timestamp. Thus, new rows go near the end of the index. A UUIDv4 is random, for example `randomUUID()` from `node:crypto`. New rows go to random positions in the index. This splits index pages and makes the index larger.
A UUIDv7 does not give an exact creation order. Two IDs made in the same millisecond can sort in the wrong order. Clock differences between hosts can also cause this. If you need the exact order, sort by a timestamp column.
Do **not** chain `.autoGenerate2` on `.uuid`. The DSL throws an error. The database default makes a UUIDv4. Also, `uuid_generate_v4()` needs the `uuid-ossp` extension. Some managed Postgres services, such as Supabase, do not have it. Use `.uuid`, not `.varchar(36)`.
3. **String** — `column('id').varchar(36).primary`. Use this only when one of these conditions is true:
- The entity already uses IDs in a format that is not a UUID. For example, the public API or the UI already shows nanoid IDs for this entity.
- The UI shows the ID to users. A UUID is too long to read, copy, or type.
Entity IDs are usually nanoids with 16 characters.
**Give a reason for every key that is not an integer.** Write the reason in the PR description. Reviewers will ask for it. These reasons are not sufficient:
- "Other tables use string IDs."
- "This module already does it this way."
- "We might need it later."
**These options apply to a generated `id` column only.** A join table that only links other tables does not need an `id` column. Use its foreign-key columns as a composite key (see below). A foreign-key column always has the same type as the key that it refers to.
**Keep ID-column types consistent across related tables.** Mixing `uuid` and `varchar(36)` for what is "the same kind of ID" creates JOIN footguns.
@ -696,7 +718,7 @@ If only Postgres needs the change, just put the file in `postgresdb/`; don't wri
### Postgres-version-aware UUID generation
`gen_random_uuid()` requires Postgres ≥ 13. n8n dropped Postgres 12 — prefer it over `uuid_generate_v4()` (which needs the `uuid-ossp` extension and breaks on managed services like Supabase). For UUID PKs, generate at the application level with `randomUUID()` — see [Primary Keys](#primary-keys).
`gen_random_uuid()` requires Postgres ≥ 13. n8n dropped Postgres 12 — prefer it over `uuid_generate_v4()` (which needs the `uuid-ossp` extension and breaks on managed services like Supabase). For UUID PKs, make a UUIDv7 in application code — see [Primary Keys](#primary-keys).
### SQLite doesn't enforce `varchar(N)` length

View File

@ -1,43 +0,0 @@
---
name: n8n:design-system
description: Guidelines on using Design System styles and components. Use when working on .vue files in packages/frontend. Triggers for tasks that include component architecture, styling, UI changes, or feature work.
---
# Design System
Comprehensive guide for building, styling, and using components in the frontend.
## When to Apply
Reference these guidelines when:
- Working on `.{vue|css|scss}` files in `packages/frontend`
- Adding new components to `packages/frontend/@n8n/design-system`
- Refactoring styles for Vue components
- Implementing new UI components or features
- Reviewing changes to UI
## Rules
- Follow guidelines in `packages/frontend/@n8n/design-system/src/styleguide/*.mdx`
- ALWAYS use CSS variables for styles from `packages/frontend/@n8n/design-system/src/css/_tokens.scss` or `packages/frontend/@n8n/design-system/src/css/_primitives.scss`. Use hard-coded values only when no suitable tokens.
- ALWAYS prefer using existing components from `packages/frontend/@n8n/design-system/src/components`. Prefer components that aren't marked `@deprecated`.
- Use `light-dark()` when alternating colors for ligh/dark mode
- If you need to add hover/active alpha behavior to solid components, prefer `color-mix()` with explicit percentages.
- When working with animations or transitions, ALWAYS prefer using mixins from `packages/frontend/@n8n/design-system/src/css/mixins/motion.scss`
- When reviewing animations, follow the guides in `rules/web-animation-guidelines.md`
- When reviewing UI changes or adding new components, follow `rules/web-interface-guidelines.md`
## Storybook Stories
- ALWAYS add new stories to `packages/frontend/@n8n/design-system`.
- Every story must use one of these title categories:
- `Style guide`: styles, tokens, and utilities
- `Core`: components used across the app
- `Areas/<Product area>`: patterns and components for a specific product area, for example `Areas/Settings` or `Areas/Assistant`
- `Experimental`: beta components that require caution
## Examples
- "Add a modal dialog for confirming workflow deletion" → Use `N8nDialog`
- "Add a dropdown to select workflow status" → Use `N8nDropdown` or `N8nSelect`
- "Add button with + icon to add new tiem" → Wrap `N8nButton` with `iconOnly` prop with `N8nTooltip` and wrap in `N8nTooltip`. Use `N8nIcon` and proper aria-label.
- "Add a destructive action button" → use `N8nButton` with `variant="destructive"`
- "Make background color white/black" → Use `var(--background--surface)` for white on light mode and "black" on dark mode
- "Animate the title in gracefully" -> Use `fade-in-up` mixin from `motion.scss` with `var(--duration--base)`

View File

@ -0,0 +1,158 @@
---
name: n8n:env-var-docs
description: >-
Writes and reviews the JSDoc on `@Env(...)` config properties. The docs team
extracts these comments from source into the public environment variable
reference, so they are user documentation, not code comments. Use when adding
or changing an `@Env` property, a config class, or reviewing a PR that touches
one.
---
# Environment variable docs
The JSDoc above every `@Env('...')` property is exported almost verbatim into
the [environment variables reference](https://docs.n8n.io/deploy/host-n8n/configure-n8n/basic-configuration/use-environment-variables)
on docs.n8n.io. The generated table has four columns: variable name,
description, data type, default. Your JSDoc is the description column. The
reader is a self-hosting operator, not an n8n developer.
Follow the [n8n docs style guide](https://docs.n8n.io/contribute/contribution-guide-for-n8n-docs/style-guide-for-n8n-docs)
for these comments. It overrides the ASD-STE100 rule in `AGENTS.md` for this
one case. The rules below are the parts of that guide that matter for a
one-paragraph description.
## Rules
- **Every `@Env` property has a JSDoc.** No exceptions. A missing comment
ships as an empty description cell.
- **Write for the operator.** Say what the setting does, when to change it,
and what happens when it's unset or wrong. Don't explain why the code is
written the way it is.
- **No code references.** No file names, class names, function names,
constants, license keys, or other internals the reader can't open. Values
the operator types into the variable are fine and often needed, for example
a credential type ID like `googleOAuth2Api` or a mode like `postgresdb`.
The test: would the operator paste this into a config file, or would they
need the source to know what it means? Details for developers go in a
regular `//` comment on the implementation, not in the JSDoc.
- **No attack scenarios.** Say what the setting protects, not how an
attacker would exploit it. This is a public page.
- **Don't repeat the default.** The default renders in its own column. Don't
write "Defaults to true" or "Default: 1 hour". Do say what the unset state
means when it's not obvious, for example "When unset, n8n uses the Redis
host".
- **State units and formats.** Say "in milliseconds", "in bytes", "a
comma-separated list", "a JSON object". The data type column only says
`Number` or `String`.
- **Use allowed values when the set is small.** "One of `sqlite`,
`postgresdb`." For a list, show one inline example in the sentence.
- **No JSDoc tags.** No `@example`, `@default`, `@see`, `@deprecated`. The
exporter copies the tag name into the docs. Put the example or the
deprecation notice in a sentence instead.
- **Product names:** "Gateway credits", not "n8n Connect" or "AI Gateway".
"Publish a workflow", not "activate". `n8n` is always lowercase.
- **Style:** present tense, active voice, second person. Use contractions.
No em dashes, use a comma or a new sentence. No "e.g." or "i.e.", write
"for example" and "that is". Spell out zero to nine, numerals from 10.
Values the operator types stay numerals, for example "Set to 0".
Sentence case. One idea per sentence. End every sentence with a period.
- **Plain words:** "use" not "utilize", "lets you" not "allows you to", cut
"powerful", "seamless", "simply", "it's important to note".
- **Length:** one to three sentences. If the setting needs more, the extra
belongs in a docs page the comment can't link to yet; keep the comment to
what fits a table cell.
## Examples
Bad, as found in the generated docs:
```ts
/**
* Instance-level enablement for n8n Connect (AI Gateway). Licensed instances
* are enabled by default; false opts out. Budget / free allowance still comes
* from the license (`quota:aiGatewayBudget`).
*/
```
Wrong product name, a restated default, and an internal license key. Good:
```ts
/**
* Whether Gateway credits are available on this instance. Set to false to
* turn them off on a licensed instance. The credit allowance comes from your
* license.
*/
```
Bad:
```ts
/**
* Time to live in milliseconds for Redis cache entries. Set to 0 to disable
* expiry. Default: 1 hour.
*/
```
The default is already in the table. Good:
```ts
/**
* Time to live in milliseconds for Redis cache entries. Set to 0 to keep
* entries until Redis evicts them.
*/
```
Bad:
```ts
/**
* Comma-separated list of credential types for which overwrites are skipped
* when the credential has been customized.
* @example `N8N_SKIP_CREDENTIAL_OVERWRITE=zohoOAuth2Api,salesforceOAuth2Api`
*/
```
The `@example` tag lands in the description as literal text. Good:
```ts
/**
* Comma-separated list of credential types to skip when applying credential
* overwrites, for example `zohoOAuth2Api,salesforceOAuth2Api`. n8n skips an
* overwrite only when a user has already changed one of its fields.
*/
```
Bad:
```ts
/**
* Bind OAuth credential callbacks to the browser that initiated the flow.
* When enabled, a session-scoped `n8n-oauth-binding` cookie is set on flow
* initiation and verified on callback. Defeats phishing attacks where an
* attacker injects an OAuth authorize URL into a victim's browser to land
* tokens on the attacker's credential.
*/
```
Attack narrative and an internal cookie name on a public page. Good:
```ts
/**
* Whether an OAuth credential connection must finish in the same browser
* that started it. When enabled, n8n rejects the callback if it arrives
* from a different browser.
*/
```
## Review checklist
When you review a PR that adds or changes an `@Env` property, check each
comment against this list and quote the fix in the review:
1. JSDoc present.
2. No code identifiers, file names, or internal keys.
3. No default restated.
4. Units, format, or allowed values stated.
5. No JSDoc tags.
6. No em dashes, Latin abbreviations, or filler words.
7. Reads correctly as a sentence in a table cell with no surrounding context.

View File

@ -20,4 +20,4 @@ Start with the relevant mode in [reference.md](reference.md):
- `Review` for auditing experiment changes.
- `Retire` for cleaning up completed or abandoned experiments.
When experiment work touches Vue components or user-facing copy, also follow `n8n:design-system` and `n8n:content-design`.
When experiment work touches Vue components or user-facing copy, also follow `n8n:ui-design` and `n8n:content-design`.

View File

@ -66,7 +66,7 @@ Use this guide for code under `packages/frontend/editor-ui/src/experiments/` and
- Wire experiments at the narrowest host surface that owns the decision.
- Keep experiment components inside the experiment folder unless they become generally reusable.
- All user-facing text in Vue code must use i18n. Follow `n8n:content-design` when writing or revising copy.
- Follow `n8n:design-system` for Vue component structure, n8n design-system components, and CSS variables.
- Follow `n8n:ui-design` for Vue component structure, n8n design-system components, and CSS variables.
- At the end of scaffold or wiring work, tell the user what remains manual, such as PostHog configuration, route guards, modal registration, or extra workflow payload files.
## Test

View File

@ -13,11 +13,11 @@ the same commands from the repo via `pnpm nathan`.
## Prerequisites
- **No tunnel setup needed.** Nathan replies asynchronously through a short-lived
public tunnel that the script opens for you (`npx localtunnel`). A `deploy` to a
**new** `test-<name>` also polls that instance URL directly, so it still reports
success even if the tunnel drops. (Redeploying a name that's *already up* skips
the poll — it can't tell the old instance from the new — so it falls back to the
tunnel; prefer a fresh name when you need the reliable report.)
Cloudflare Quick Tunnel that the script opens for you (`npx cloudflared`). A
`deploy` to a **new** `test-<name>` also polls that instance URL directly, so it
still reports success even if the tunnel drops. (Redeploying a name that's
*already up* skips the poll — it can't tell the old instance from the new — so
it falls back to the tunnel; prefer a fresh name when you need the reliable report.)
- **A token in `~/.n8n/dev/nathan-token`.** If a command reports no token, **ask the
user for one** — point them at the form
(`https://internal.users.n8n.cloud/form/d6d34a2f-4899-4ee8-afc8-f8c41a8a243d`),

View File

@ -21,8 +21,7 @@ check the code, not license to drop a team default.
## Non-negotiable rules
- New endpoints are `@PublicApiController` classes under `v1/controllers/`, one
`*.public.controller.ts` per feature. A controller is a class — never
`export =` (the legacy tuple style; `require-public-api-controller` flags it).
`*.public.controller.ts` per feature.
- Public API and internal REST are separate HTTP surfaces. A public controller
never calls an internal controller/endpoint; both reuse the same service.
- Controllers and handlers delegate to a service — never import a repository or
@ -31,7 +30,6 @@ check the code, not license to drop a team default.
`@ApiResponse(Dto)`.
- Register each controller via a side-effect import in `v1/controllers/index.ts`
(`public-api-controllers.test.ts` fails otherwise).
- Don't add business logic to legacy `express-openapi-validator` (EOV) handlers.
- Migrating a legacy endpoint must not change its public contract.
These are `n8n-local-rules` ESLint rules (see `packages/cli/eslint.config.mjs`)
@ -41,6 +39,7 @@ add to it.
## Team defaults
- Write code that acts as its own documentation. The schema, the decorator, and the test should make the rule clear on their own without a comment.
- List endpoints: cursor-based pagination (internal API uses both cursor- and
page-based — don't copy an internal endpoint's model).
- Pagination args are always `offset` and `limit` — on service methods, handler
@ -108,19 +107,25 @@ model; reuse only what applies. Decorators, all from `@n8n/decorators`:
| `@Get/@Post/@Put/@Patch/@Delete('/path')` | Route method. |
| `@ApiKeyScope('res:action')` | API-key grant check. |
| `@ProjectScope/@GlobalScope('res:action')` | User RBAC check. |
| `@ApiResponse(status)` / `@ApiResponse(status, Dto)` | Success status + (optional) output DTO; registry `.parse()`s + strips the return value. Exactly one per route — a second `@ApiResponse` throws. `204` can't carry a DTO — throws. |
| `@ApiResponse(status)` / `@ApiResponse(status, Dto)` | Success status + (optional) output DTO; registry `.parse()`s + strips the return value. Exactly one per route — a second `@ApiResponse` throws. `204` can't carry a DTO or a binary body — throws. For a binary response body (returned as `{ body, headers? }`), use `@ApiResponse(status, { mediaType, description?, headers? })` — see [Binary response bodies](reference.md#binary-response-bodies). |
| `@ApiErrorResponse(status)` | Declares an additional documented non-2xx status (e.g. `404`, `409`). Stack multiple for more than one. `400`/`401`/`403` are added automatically (body/query present, always, and `@ApiKeyScope` present, respectively) — don't declare those yourself. |
| `@ApiSummary(text)` / `@ApiDescription(text)` / `@ApiTags([...])` | OpenAPI summary/description/tags. `@ApiTags` sorts alphabetically regardless of the order you pass. All optional but expected on every real route. |
| `@Query` / `@Body` / `@Param('name')` | Bind + validate via a `Z.class` DTO / path param. |
| `@Query` / `@Body` / `@Param('name')` | Bind + validate via a `Z.class` DTO / path param. `@Body` is JSON by default; `@Body({ mediaType: 'multipart/form-data', uploadLimits })` takes a `multipart/form-data` body instead — see [Request body media types](reference.md#request-body-media-types). |
| `@Licensed('feat')` | Gates the route on a single `BooleanLicenseFeature`; `PublicApiControllerRegistry` runs its own license middleware (after auth/`@ApiKeyScope`/`@ProjectScope`|`@GlobalScope`, before the handler) and 403s unlicensed requests. Only takes one feature — if the gate is an any-of/all-of combination (e.g. `LicenseState.isProvisioningLicensed()`, which is `feat:saml` OR `feat:oidc`), `@Licensed` can't express that; check manually in the handler instead, same as the internal `provisioning.controller.ee.ts`/`role-mapping-rule.controller.ee.ts` do today (throwing `ForbiddenError` on failure). |
## Authorization (easy to get wrong)
- `@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.
- Name every path param `{resource}Id` (e.g. `workflowId`, `credentialId`,
`projectId`, …) — never a generic `:id` / `{id}`. This is the Public API's
naming convention: it keeps the API self-documenting and gives typed SDK
codegen a real argument name instead of `id`. `@ProjectScope` also reads
`req.params` as-is and does not remap `id` — it resolves authorization by
exact key name (`workflowId`, `credentialId`, `projectId`, `dataTableId`,
…), so a generic `id` on a `@ProjectScope` route often fails outright; a
`@GlobalScope` or unscoped route won't fail the same way, but still follow
the convention.
- `@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
@ -132,7 +137,15 @@ model; reuse only what applies. Decorators, all from `@n8n/decorators`:
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.
- An output DTO restricts which fields you return, not which values they may hold.
The registry parses the handler's return value against it, so a value the schema
rejects becomes a `500`. Keep the schema loose enough for anything an existing
row may contain.
- Build the response from the relations the route loaded, not from the entity type.
TypeORM relations are opt-in, so two routes over the same entity can return
different shapes.
- Make input DTOs strict so unknown/partial fields aren't silently accepted:
`Z.class(shape, { strict: true })`.
- 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).
@ -185,4 +198,5 @@ existing tests.
- [Test-before-save endpoints](reference.md#test-before-save-endpoints)
- [Errors](reference.md#errors)
- [Testing matrix](reference.md#testing-matrix)
- [Migrating legacy EOV endpoints](reference.md#migrating-legacy-eov-endpoints)
- [Verifying a migration](reference.md#verifying-a-migration)
- [CI and merging](reference.md#ci-and-merging)

View File

@ -100,50 +100,105 @@ scope, RBAC denial. Add whichever apply, matching the nearest existing tests:
`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.
unchanged. Tests alone cannot show this — see
[Verifying a migration](#verifying-a-migration).
## Migrating legacy EOV endpoints
## Request body media types
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.
`@Body` defaults to `application/json`; the body is already parsed by the
app-wide `bodyParser` before the registry sees it. Declaring
`@Body({ mediaType: 'multipart/form-data', uploadLimits })` instead takes a
`multipart/form-data` body:
- Prefer migrating to `@PublicApiController` over extending the handler.
- Preserve the public contract: path, method, scopes, status codes, response
shape, and pagination.
- Move HTTP concerns into the controller and business orchestration into the
shared service; keep the controller a thin HTTP boundary. If the legacy
handler was itself already just a thin wrapper around an internal
`@RestController` (calling its methods directly, e.g.
`Container.get(SomeController).createThing(req, res, payload)`), the new
public controller can call that same internal controller directly — no need
to duplicate its validation/business logic.
- A route must be served by either the EOV handler or a controller, not both —
the build's `mergeDecoratorDocument` (`v1/openapi-gen/generate.ts`) throws on
a path+method declared by both sides. Remove the legacy wiring (its path's
`$ref` entry in `openapi.yml`, the `x-eov-operation-*` handler, and its
`handler.ts`) only after the new controller is registered, `pnpm build`
regenerates the spec cleanly, and tests are updated.
- Fully delete the migrated legacy files: the handler's `.ts`, its
`spec/paths/*.yml` and `spec/schemas/*.yml`, and any now-dead request type
in `packages/cli/src/public-api/types.ts`. Then check
`v1/shared/spec/schemas/_index.yml` and `v1/shared/spec/parameters/_index.yml`
for entries that `$ref` one of the deleted schema/parameter files — those are
separate from the path's own `$ref` in `openapi.yml` and are easy to miss;
left dangling, the next bundle fails on a broken `$ref`.
- If the legacy handler gated on a license (`isLicensed('feat:x')` middleware),
`@Licensed('feat:x')` now replicates that for a controller route (see the
decorator table in [SKILL.md](SKILL.md#declaring-a-controller)) — but only for
a single feature. If the legacy check was an any-of/all-of over several flags
(e.g. `LicenseState.isProvisioningLicensed()`), `@Licensed` can't express
that; replicate it manually in the controller instead, don't drop it - this
is exactly what the internal `provisioning.controller.ee.ts` and
`role-mapping-rule.controller.ee.ts` already do, since neither uses
`@Licensed` for that reason.
- As a legacy file drops repository access / the `export =` tuple, remove its
entry from the `off` allowlists for `no-repository-in-public-api-handler` and
`require-public-api-controller` in `packages/cli/eslint.config.mjs` (shrink-only
— never extend them).
- For complex legacy-only, multipart, or non-standard endpoints, study the
nearest existing handler first.
- `uploadLimits: () => MultipartUploadLimits` is a thunk, read once the route
handles its first request (not at startup) — read live config inside it
(e.g. `Container.get(GlobalConfig)`), don't inline a literal.
- The route's `@Body` DTO validates text fields merged with uploaded files,
not `req.body` alone. A text field and a file share the same flat object;
a file field uses `publicApiUploadedFileSchema` (`@n8n/api-types`) — a
multer-file-shaped schema that documents itself as `{ type: 'string',
format: 'binary' }`. Several files under the same field name become an
array.
- An unknown field (one the DTO doesn't declare, on a `{ strict: true }` DTO)
fails with `Unexpected form field "<name>"` — not the default "unrecognized
keys" wording.
- Multer parsing errors map to these statuses: `413` for a size or count limit,
`500` for `LIMIT_UNEXPECTED_FILE`, `400` for any other multer error, and `500`
for anything else (a malformed body). The `500` is not masked, because it is a
`ResponseError`. A missing boundary gives `400` with `multipart file(s) required`.
- The body is parsed **after** every auth/scope/license/quota gate and
**before** controller/route middlewares — a caller those gates would reject
never has their (possibly huge) body read off the socket, and a middleware
that reads `req.body` sees it already parsed.
- `/discover` shows no request schema for a multipart route (same as a legacy
multipart route today) — a client can't assume a JSON schema it never gets.
- The generator documents the body under the `multipart/form-data` content
key (not `application/json`) and adds `413` alongside `415` to the route's
documented responses automatically.
Everything above lives in `packages/cli/src/public-api/media-types/`, one
handler per media type (`REQUEST_BODY_HANDLERS` in
`media-types/request-body/index.ts`). Adding a further media type (e.g.
`application/octet-stream`) means: add it to `RequestBodyMediaOptions` in
`@n8n/decorators`'s `controller/types.ts`, write a handler implementing
`RequestBodyHandler` in `media-types/request-body/`, and register it in
`REQUEST_BODY_HANDLERS` — the registry, resolver, generator and `/discover`
need no change, since they all read the handler, not the media type.
## Binary response bodies
`@ApiResponse(status, { mediaType, description?, headers? })` declares a
success body that the controller method returns as `{ body, headers? }`, for
example an `application/gzip` stream. The framework treats options whose
`mediaType` is in `BINARY_RESPONSE_MEDIA_TYPES` as a binary response.
`mediaType` accepts only those types.
- `body` is a `Buffer` or a `Readable`. The method returns the result and never
writes to `res`.
- `headers` holds the response headers. Every header declared in `headers` must
be in the result. A missing header fails the request with a `500`. Matching is
case-insensitive.
- The registry checks the headers, then sets the declared status, the headers,
and `Content-Type: <mediaType>`. A header in the result cannot replace the
`Content-Type`.
- A stream's first chunk is read before any header is set. So a stream that
fails before its first chunk gives the normal JSON error response, with no
binary `Content-Type`. A method that throws before it returns does the same.
- After the first chunk is sent, an error ends the response. A client that
disconnects is not an error, and the framework destroys the stream.
- A method that returns no binary body fails with a `500`.
- The generator documents the body as `{ type: string, format: binary }`
under the `mediaType` content key, with the description and headers.
The description defaults to `Operation successful.`.
The runtime part is `sendBinaryResponse` in
`packages/cli/src/public-api/media-types/binary-response.ts`.
## Verifying a migration
Tests are written against the new code, so they can't show that the old behavior
survived. These checks can:
- Diff live responses against master. Run the route on an instance per branch and
compare status, body, and error message for the same requests. Reading the old
YAML beside the new Zod schema doesn't find the differences.
- Check whether a field is absent or `null`. `activeVersion` omitted is not the
same response as `activeVersion: null`; use a conditional spread to omit it.
- Check query-param coercion at the edges (`""`, `"0"`, `"false"`, absent). A Zod
DTO and the old validator don't coerce identically.
- Request a route the PR didn't migrate. A shared DTO change can stop the spec
bundle compiling at startup, which turns every legacy route into a `500` without
failing a test.
## CI and merging
Two failures that a migration hits outside the code itself:
- Merge master in before merging. A generator change on master leaves every
branch's committed `*.generated.yml` stale, and `generated-spec-drift.test.ts`
then fails only on the merge commit, so the PR itself stays green and
`MERGEABLE`. `gh pr checks` hides `merge_group` runs; look for the run on
`gh-readonly-queue/master/pr-<number>-<sha>`.
- Ask a maintainer for a `/size-limit-override` early. Generated YAML counts
toward the 1,000-line PR size limit, so a single-route migration can exceed it
on generator output alone.

View File

@ -32,7 +32,7 @@ Based on the affected area, pick the test layer and pattern:
| CLI / API | Vitest integration | setupTestServer + supertest | `packages/cli/test/integration/` |
| Config | Vitest unit | GlobalConfig + Container | `packages/@n8n/config/src/configs/__tests__/` |
| Editor UI | Vitest | Vue Test Utils + Pinia | `packages/frontend/editor-ui/src/**/__tests__/` |
| E2E / Canvas | Playwright | Test containers + composables | `packages/testing/playwright/` |
| E2E / Canvas | Playwright | Test containers + composables | `packages/quality/testing/playwright/` |
## Step 3: Locate Source Files
@ -73,6 +73,12 @@ Write a regression test that:
- Asserts the CORRECT behavior (test will fail on current code)
- Also includes a "happy path" test to prove the setup works
Complete the test safety gate in `/AGENTS.md` before you run the test:
- Trace side effects from imports, constructors, hooks, and mocked branches
- Keep filesystem access in a test-owned temporary directory
- Set `N8N_USER_FOLDER` before importing modules that resolve it
- When a mock changes a state check, inspect and isolate the branch that it activates
## Step 8: Run and Score
Run the test from the package directory (e.g., `cd packages/nodes-base && pnpm test <file>`).

View File

@ -0,0 +1,39 @@
---
name: n8n:ui-design
description: Guidelines on designing and building UI. Use when working in editor-ui or design-system packages. Triggers for tasks that include refactoring components, styling changes, or feature work.
---
# UI Design
Comprehensive guide for building, styling, and using components in the frontend.
## References
- When styling components, use `packages/frontend/@n8n/design-system/src/styleguide/*.mdx`
- For animations, use `rules/web-animation-guidelines.md`
- When reviewing UI changes, use `rules/web-interface-guidelines.md`
## Best practices
- ALWAYS use CSS variables from `_tokens.scss` or `_primitives.scss` in
`packages/frontend/@n8n/design-system/src/css/`. Look up every length, color,
font-size and duration before you write it as a literal. Each spacing token
carries its px value in a comment: `--spacing--5xs: 0.125rem; /** 2px **/`.
Grep that comment to find the token for a px value. The grep gives one hit for
each token family, so pick the family that matches the property: `--spacing--*`
for margin and padding, `--radius--*` for border-radius, `--height--*` for
height. The styleguide
`packages/frontend/@n8n/design-system/src/styleguide/spacing-size.mdx` lists the
same scale as a table. The spacing scale starts at 2px, so a small value is not
a reason to hardcode. Hardcode only if the lookup finds no token. Give the
reason in a comment.
- ALWAYS prefer using existing components from `packages/frontend/@n8n/design-system/src/components`. Prefer components that aren't marked `@deprecated`.
- If you need to add hover/active alpha behavior to solid components, prefer `color-mix()` with explicit percentages.
- When working with animations or transitions, ALWAYS prefer using mixins from `packages/frontend/@n8n/design-system/src/css/mixins/motion.scss`
## Components
Use existing `design-system` components over creating custom implementations:
- "Add a modal dialog for confirming workflow deletion" → Use `N8nDialog`
- "Add a dropdown to select workflow status" → Use `N8nDropdown` or `N8nSelect`
- "Add button with + icon to add new tiem" → Wrap `N8nButton` with `iconOnly` prop with `N8nTooltip` and wrap in `N8nTooltip`. Use `N8nIcon` and proper aria-label.
- "Add a destructive action button" → use `N8nButton` with `variant="destructive"`
- "Make background color white/black" → Use `var(--background--surface)` for white on light mode and "black" on dark mode
- "Animate the title in gracefully" -> Use `fade-in-up` mixin from `motion.scss` with `var(--duration--base)`

View File

@ -8,7 +8,7 @@ exclude:
# Machine-generated instance-AI expectation traces. Contain ephemeral
# credential reference IDs (nanoids) that re-trip the secrets scanner
# on every regeneration. No secret material.
- packages/testing/playwright/expectations
- packages/quality/testing/playwright/expectations
# Test code repo-wide. Fixtures routinely contain synthetic PEM blocks,
# basic-auth strings, and deliberately fake keys (including the secret

View File

@ -1,3 +1,3 @@
{
"issues": 169
"issues": 110
}

View File

@ -0,0 +1 @@
../../../../.agents/skills/agent-design-language

View File

@ -0,0 +1 @@
../../../../.agents/skills/create-agent-builder-eval

View File

@ -1 +0,0 @@
../../../../.agents/skills/design-system

View File

@ -0,0 +1 @@
../../../../.agents/skills/env-var-docs

View File

@ -0,0 +1 @@
../../../../.agents/skills/ui-design

File diff suppressed because it is too large Load Diff

View File

@ -4,6 +4,7 @@ ARG NODE_VERSION=24
FROM node:${NODE_VERSION}-bookworm
ARG PLAYWRIGHT_VERSION=1.62.1
ARG OPENCODE_VERSION=1.18.25
RUN apt-get update && apt-get install -y --no-install-recommends \
tmux sudo less jq ripgrep postgresql-client \
@ -14,7 +15,7 @@ RUN npx -y playwright@${PLAYWRIGHT_VERSION} install-deps chromium
# Agent harnesses. Self-update is off: the image is the update mechanism
ENV DISABLE_AUTOUPDATER=1
RUN npm install -g @anthropic-ai/claude-code opencode-ai
RUN npm install -g @anthropic-ai/claude-code opencode-ai@${OPENCODE_VERSION}
COPY codespaces-env.sh /usr/local/lib/codespaces-env.sh
COPY codespaces-secrets.sh /etc/profile.d/codespaces-secrets.sh
@ -24,8 +25,8 @@ COPY gitcredential-refresh.sh /usr/local/bin/gitcredential-refresh.sh
COPY gh-shim.sh /usr/local/bin/gh
RUN chmod +x /usr/local/bin/gitcredential-refresh.sh /usr/local/bin/gh
# VS Code terminals are non-login shells: they read bashrc, not profile.d
RUN echo '. /usr/local/lib/codespaces-env.sh' >> /etc/bash.bashrc
# VS Code terminals load secrets here because they do not read profile.d.
RUN echo '[ "${N8N_SKIP_CODESPACE_SECRETS:-}" = "1" ] || . /usr/local/lib/codespaces-env.sh' >> /etc/bash.bashrc
COPY tmux.conf /etc/tmux.conf
@ -37,4 +38,7 @@ ENV PNPM_HOME=/home/node/.local/share/pnpm
ENV PATH=$PNPM_HOME/bin:$PATH
USER node
RUN mkdir -p $PNPM_HOME ~/.pnpm-store && pnpm config set store-dir ~/.pnpm-store --global
# The store must sit on the same filesystem as the worktrees (/workspaces is its own
# mount), or pnpm copies node_modules into every worktree instead of hardlinking.
# The directory is created on the first install: Codespaces mounts /workspaces at runtime.
RUN mkdir -p $PNPM_HOME && pnpm config set store-dir /workspaces/.pnpm-store --global

View File

@ -2,7 +2,7 @@
Run long-lived, human-steered agent sessions (Claude Code / OpenCode) on a
GitHub Codespace instead of your laptop: start a task, close the lid, steer it
from anywhere with a terminal, resume tomorrow.
from anywhere, resume tomorrow.
This is a separate devcontainer config from the laptop one in
`.devcontainer/` — it ships both agent CLIs, `tmux` for session persistence,
@ -11,42 +11,269 @@ Playwright system deps, and Docker-in-Docker (for testcontainers and
## One-time setup (~5 min)
1. Add your key at [github.com/settings/codespaces](https://github.com/settings/codespaces)
→ **New secret** → name `ANTHROPIC_API_KEY`, repository access `n8n-io/n8n`.
(Alternative for Max subscriptions: `CLAUDE_CODE_OAUTH_TOKEN` from `claude setup-token`.)
2. Give the GitHub CLI the codespace scope:
1. Give the GitHub CLI the codespace scope:
```bash
gh auth refresh -h github.com -s codespace
```
## Daily flow
2. Add provider keys at [github.com/settings/codespaces](https://github.com/settings/codespaces)
if your client needs them. Give each secret access to `n8n-io/n8n`.
- `ANTHROPIC_API_KEY` for Claude Code in the terminal (`pnpm session`).
Alternative for Max subscriptions: `CLAUDE_CODE_OAUTH_TOKEN` from
`claude setup-token`. The desktop app does not need either secret.
- `OPENROUTER_API_KEY` for OpenCode.
## Choose a client
All clients run the agent, its tools, and builds in the Codespace. Your laptop
shows the conversation only.
| Client | Command | Use it when |
|---|---|---|
| Claude Code desktop app | `pnpm session ssh-config` once | You want the full desktop experience. |
| Claude Code in VS Code | Open the codespace in VS Code or the browser | You already work in VS Code. |
| Claude Code in a terminal | `pnpm session` | You want a plain terminal, or you are on a remote machine. |
| OpenCode | `pnpm session:opencode` | You use OpenCode. See [Local OpenCode clients](#local-opencode-clients). |
### Claude Code desktop app
1. From a local checkout, add an SSH host for your codespace:
```bash
pnpm session ssh-config
```
The command creates or starts the codespace. It writes the host
`n8n-codespace` to `~/.ssh/n8n-codespace.conf` and includes that file from
`~/.ssh/config`.
2. In the desktop app, add an SSH connection to the host `n8n-codespace`.
3. Open the folder `/workspaces/n8n`, or a worktree under `/workspaces`.
The desktop app installs its own Claude Code on the codespace and signs in
with your desktop account. Sessions continue when you close the app. They stop
when the codespace stops.
- **Run `pnpm session ssh-config` again after you recreate the codespace.**
- **If the connection times out**, the codespace is probably starting. Run
`pnpm session ssh-config`, then connect again.
- **Test the connection** with `ssh n8n-codespace`.
### Claude Code in VS Code
The dev container installs the Claude Code extension. Open the codespace in
VS Code or the browser, then open the Claude Code panel. Sign in from the
extension if you did not add `ANTHROPIC_API_KEY` or `CLAUDE_CODE_OAUTH_TOKEN`.
### Claude Code in a terminal
```bash
pnpm session # attach the default agent session (creates everything on first run)
pnpm session fix-flaky # a second, parallel agent in its own git worktree
pnpm session ls # what's running
pnpm session tunnel # forward n8n ports (default 5678, 8080) to localhost; Ctrl-C to stop
pnpm session stop # end of day: billing stops, disk survives
pnpm session rm # delete the codespace
pnpm session # attach Claude Code (creates everything on first run)
pnpm session:shell # open a shell in the default checkout
pnpm session:opencode # connect the local OpenCode TUI
pnpm session:opencode fix-flaky # use a separate remote worktree
pnpm session:opencode --web # open OpenCode in the local browser
pnpm session:opencode --legacy # use the remote TUI in tmux
pnpm session fix-flaky # Claude Code in a separate worktree
pnpm session ls # what's running
pnpm session tunnel # forward n8n ports (default 5678, 8080); Ctrl-C to stop
pnpm session ssh-config # add the n8n-codespace SSH host for the desktop app
pnpm session stop # end of day: billing stops, disk survives
pnpm session rm # delete the codespace
```
- **Detach** with `Ctrl-b d` — the agent keeps working without you.
- **Scroll** with the mouse wheel (tmux mouse mode is on). To use the
The dev container supports Codespaces with 2, 4, or 8 cores. The session commands
create an 8-core Codespace by default.
- **Detach from tmux** with `Ctrl-b d` — the agent keeps working without you.
Local OpenCode connections use the exit controls described below.
- **Scroll in tmux** with the mouse wheel (tmux mouse mode is on). To use the
terminal's own text selection, hold **Shift** and drag.
- **Reattach** by running the same `pnpm session <name>` from any machine.
- **Reattach** by running the same session command from any machine.
- Each named session gets its own worktree (`/workspaces/wt-<name>`, branch
`session/<name>`), so parallel agents never touch each other's tree. Builds
`session/<name>`), so parallel agents never touch each other's tree. A new
branch starts from the latest `origin/master`. Builds
in fresh worktrees are cache-hits via a shared turbo cache.
- First codespace creation takes ~20 min uncached (image + full build). After
that, sessions attach instantly; new worktrees cost a `pnpm install` (~1–2 min).
that, sessions attach instantly; new worktrees cost a `pnpm install` (~10 s: the
store lives on `/workspaces`, so `node_modules` is hardlinked, not copied).
## Configure the local OpenCode harness
Install the pinned shared harness:
```bash
pnpm agent:harness
```
The command verifies the release checksum. It caches the bundle under
`~/.cache/n8n-agent-harness`. It then links the bundle plugin into
`~/.config/opencode/plugins`. Run the command again after the lock changes.
Restart OpenCode after the command completes.
## Local OpenCode clients
Run these commands from a local checkout. The TUI runs on your laptop. The
OpenCode server, repository, tools, and builds run in the Codespace. Browser
mode opens the remote web interface through a local connection.
```bash
pnpm session:opencode fix-flaky # resume that workspace's latest conversation
pnpm session:opencode fix-flaky --web # open it in a browser
pnpm session:opencode fix-flaky --new # start a new conversation in that worktree
pnpm session:opencode --web --port 4100 # override the default browser port
pnpm session:opencode --help
```
For the TUI, install the same OpenCode version as the remote server. The
`OPENCODE_VERSION` argument in the [Dockerfile](Dockerfile) pins the version
for new images:
```bash
npm install -g opencode-ai@<version>
```
Use npm, not pnpm. The package gets its binary from a `postinstall` script.
pnpm does not run that script by default.
The launcher checks both versions. It reports a mismatch with both version
numbers before it opens the TUI. An existing Codespace can have a different
version. Install that version locally, or use `--web`. Browser mode does not
need a local OpenCode install.
The launcher sends its bootstrap code over SSH, so an existing Codespace does
not need a rebuild to use this connection method.
The remote server loads the shared OpenCode harness that the Codespace
post-start command installs. The local harness setup above is not required for
these commands.
The command prepares the worktree, starts or reuses one server, opens an SSH
tunnel, and connects the client. Each workspace opens its own conversation: the
most recently updated one in that worktree. A conversation moves to the front
when it receives a message. Switching to a conversation without sending a
message does not move it. Switching to another workspace's conversation does
not change what this workspace opens next. `--new` starts a new conversation
instead. It preserves the worktree and the old conversation.
- **Exit the TUI** with `/exit` or its quit shortcut. The launcher closes its
tunnel. The remote server stays running.
- **Disconnect browser mode** with `Ctrl-C` in the launcher terminal. Closing
the browser tab does not close the tunnel. Keep the launcher running while
you use the browser.
- **Reconnect** with the same command after a network interruption. After a
Codespace stop, the command restarts the server and opens the workspace's
most recently updated conversation.
A stop terminates running tools. It does not resume interrupted commands.
- **Browser mode uses local port 4096 by default.** Use `--port` to override it.
TUI mode selects an available port unless you specify one.
A fixed browser port preserves the browser origin across runs.
An occupied fixed port causes an error. It does not stop the existing listener.
- **Use model and permission controls in the client.** The new connection uses
the remote OpenCode configuration. It does not force the legacy `--auto` mode.
For the old CLI flags, use `pnpm session:opencode fix-flaky --legacy --model <model>`.
Both server and tunnel bind to `127.0.0.1`. The server uses a generated password.
The TUI receives it through its environment. Browser mode uses a local proxy
that adds authentication and rejects requests from other browser origins. The
proxy supports streamed responses, attachments, and terminal WebSockets. The
password does not appear in the URL or terminal output. Other processes on
your laptop can access the local browser proxy while it runs. Do not forward
this proxy or the OpenCode server port to other machines.
The server enables only OpenRouter. It reads `OPENROUTER_API_KEY` when it starts.
It enables OpenCode code mode by default to reduce the initial tool context.
Browser mode opens the workspace's most recently updated conversation directly.
The web UI stores opened projects in browser storage. If a new-session page
shows **New project**, open `/workspaces/n8n` there once. Keep the same browser
port when you reconnect to preserve this selection.
The server runs in the detached tmux session `n8n-opencode-server`. Its log is
`/workspaces/.n8n-opencode/server.log`. That directory also holds the server
credentials. It is readable only by its owner.
An unhealthy server produces an error without stopping active work. Inspect
the log through `pnpm session:shell`. To restart it after checking active work,
run `tmux kill-session -t '=n8n-opencode-server'` in that shell. Then reconnect.
Restart the server after changing provider secrets, server configuration, or
the pinned harness release.
### Limits
- The launcher supports macOS and Linux. On Windows, run it in WSL. If the
browser does not open automatically, open the printed URL yourself.
- Local clipboard and attachment controls depend on the client, terminal,
file type, and model. A laptop file path does not copy a file to the Codespace.
Use a supported attachment control or copy the file with `gh codespace cp`.
- Tools and local MCP processes run on the Codespace. Laptop configuration,
browser sessions, and files do not sync automatically.
- Workspace names identify worktrees. Two agents that use the same name share
files. Separate conversations alone do not isolate edits.
- The server survives a client disconnect while the Codespace stays running.
Codespaces idle timeouts still apply. An open tunnel is not a guarantee that
the Codespace will stay awake. Use `pnpm session stop` to stop compute billing.
- Existing tmux OpenCode conversations are not migrated automatically. Use
`--legacy` to return to them.
For upstream behavior, see the [OpenCode CLI](https://opencode.ai/docs/cli/),
[server](https://opencode.ai/docs/server/), and [web](https://opencode.ai/docs/web/)
documentation.
## PR previews (a running instance of someone else's PR)
A preview is a codespace that serves one pull request, so a reviewer can use the
PR instead of reading it. It is a different box from a `pnpm session`: it uses
`.devcontainer/preview/devcontainer.json`, and its display name is
`preview/pr-<number>`.
**From GitHub:** for a PR targeting master, add the `codespace-preview` label to the PR.
[util-codespace-preview.yml](../../.github/workflows/util-codespace-preview.yml)
creates the box, serves the PR head, shares port 5678 with the org, and comments
the URL on the PR. A later push serves the new head in the same box. Remove the
label, or close the PR, to delete the box. The same workflow runs by hand from
the Actions tab: give it a PR number and `up`, `refresh` or `down`.
**From your laptop:** `pnpm preview up <pr>` does the same thing, plus
`pnpm preview refresh <pr>`, `pnpm preview down <pr>` and `pnpm preview ls`. It
needs `gh` with the codespace scope, the same as `pnpm session`.
- **Watch it come up on the PR.** The comment appears before the box work starts
and updates about once a minute with a checklist of the phases, so you can see
which step a slow preview is on. `pnpm preview up <pr>` prints the same phases as
plain progress — the markers the comment reads are `--json` only.
- **Sign in with one click** at `<url>/preview-signin`. It logs you in as the
seeded owner and sends you to the editor. The credentials are
`preview@n8n.io` / `PreviewInstance1`. They are not secrets: the boundary is
the org-visible forwarded port, which needs a GitHub sign-in and n8n org
membership.
- **Configure the instance with `preview:*` labels.** `preview:enterprise` serves
it with a licence, so enterprise features such as SSO and source control are
present. `preview:debug` sets `N8N_LOG_LEVEL=debug`. Adding or removing one
re-serves the box; it never creates or deletes one. From a laptop the labels
apply the same way — `pnpm preview refresh <pr>` reads them from the PR. The
toggles are defined in `scripts/codespace-preview/preview-labels.mjs`; add new ones there.
- **Configure the instance from a webhook.** A preview also reads extra
environment from an n8n webhook, so a value can change without a commit. It
needs the `CODESPACE_ENV_URL`, `CODESPACE_ENV_USER` and `CODESPACE_ENV_PASSWORD`
codespace secrets. Every key the webhook returns becomes an environment
variable, so editing that workflow runs code in the box. Without the secrets
the preview serves as usual. See [WORKFLOWS.md](../../.github/WORKFLOWS.md).
- **A preview sleeps after 2 hours** of no use and GitHub deletes it after 24
hours. A box that slept serves nothing and its port is private again, so wake
it with `pnpm preview up <pr>` or a manual run of
[util-codespace-preview.yml](../../.github/workflows/util-codespace-preview.yml)
with `up`. Removing and adding the label works too, but it deletes the box and
builds a new one.
- **A PR from a fork gets no preview.** A codespace's token is scoped to
`n8n-io/n8n`, so it cannot check out a fork head.
- **A PR that predates this tooling has no `scripts/codespace-preview/preview-serve.mjs`.** The
serve step says so and stops; rebase the PR on master and retry.
## Agent worker (drive a session from n8n)
`agent-worker.mjs` lets an n8n workflow drive a Claude Code session on the
`agent-worker.mjs` lets an n8n workflow drive an OpenCode session on the
codespace. This is how Slack (the Flaky bot) steers a session that runs here.
Each turn is one headless `claude -p`. The session state is on disk. So
`--resume` continues a conversation across turns, and across a stop/start.
Each turn runs `openrouter/openai/gpt-5.6-sol` through
`opencode run --format json --auto`. The session state remains on disk. The
returned session ID continues a conversation across turns and a Codespace
restart.
**The worker polls outward. Nothing inbound is exposed.** GitHub sets every
forwarded port to private on start. It gives no API to make a port public. So
@ -55,32 +282,59 @@ instead. It asks n8n for a turn addressed to this box's owner (`$GITHUB_USER`).
It runs the turn. It sends the result to the turn's resume URL. It uses no
tunnel, no open port, and no domain.
The worker starts on each container start (`postStartCommand`). It needs two
secrets. Add them at
The post-start command installs the pinned OpenCode harness before it starts the
worker. It verifies a valid cached bundle before reuse. The worker does not start
if the harness is unavailable. `/tmp/post-start-status.json` contains the harness
and worker status.
The worker needs three
secrets and uses three optional secrets. Add them at
[github.com/settings/codespaces](https://github.com/settings/codespaces), the
same way as `ANTHROPIC_API_KEY`:
- `AGENT_WORKER_TOKEN` — the shared bearer token. The worker sends it on each poll.
- `N8N_DEQUEUE_URL` — the n8n webhook that returns a pending turn.
- `OPENROUTER_API_KEY` — the model provider key for Sol.
- `SLACK_BOT_TOKEN` — optional bot token for progress messages. It needs `chat:write` only.
- `FLAKY_MCP_URL` and `FLAKY_MCP_TOKEN` — optional Flaky MCP connection for OpenCode.
The worker explicitly reloads `/usr/local/lib/codespaces-env.sh` when its tmux
session starts. An existing tmux server can otherwise retain an environment
that did not load the Codespaces secrets.
The dequeue payload can include `slack.channel` and `slack.thread_ts`. The
worker posts one placeholder in that thread. It coalesces completed tool calls.
It updates the message at most once every 1.5 seconds. It does not send reasoning
text. The worker replaces the placeholder with the final answer.
If the Slack API fails, the turn still completes through the n8n resume URL.
The worker does not export its dequeue or Slack credentials to OpenCode. It uses
the harness `sandbox` runtime and `slack` profile. The profile supplies the
atomic-turn instruction.
Interactive sessions remove all worker-only credentials before they start.
Interactive OpenCode uses the global harness plugin with the `sandbox` runtime.
It does not set a profile. It keeps the dynamic OpenRouter configuration.
An idle worker starts with a 3-second poll interval. After each empty dequeue,
it doubles the interval and limits it to 30 seconds. Work resets the interval
to 3 seconds. Queued turns run without a delay between them.
The log is at `/tmp/agent-worker.log`. The tmux session is `agent-worker`. To
watch it, run `tmux attach -t agent-worker`. The worker does not start if a
secret or `$GITHUB_USER` is missing.
required secret or `$GITHUB_USER` is missing.
A turn stops after about 25 minutes (`TURN_TIMEOUT_MS`). This limit is below the
n8n Wait limit. So the worker reports a clear message before n8n reports a
generic timeout. Keep the worker limit below the n8n limit if you change either.
**A turn is atomic, and the worker tells the session so.** The turn ends on the
**A turn is atomic, and the harness tells the session so.** The turn ends on the
session's final message, and its children end with it: a background `Bash` task
is killed, `Monitor` events never arrive, `PushNotification` has nowhere to go,
and `ScheduleWakeup` never fires. The session also gets no turn of its own to
report back in — the turn's resume URL continues one waiting n8n execution and is
then spent, so nothing on the box can post to the thread unprompted. A session
that backgrounds a build and signs off with "I'll verify once it finishes" is
therefore describing something that cannot happen. The worker states this in
`--append-system-prompt` on every turn (`turnContract`), together with a pointer
to this file for the box-specific parts. This is only the n8n/Slack path: an
therefore describing something that cannot happen. The harness `slack` profile
states this contract. This is only the n8n/Slack path: an
interactive session (`pnpm session`, tmux) is long-lived, so background work,
monitors and notifications behave normally there.
@ -120,13 +374,18 @@ session rarely needs a cold `pnpm install` or a full `pnpm build`. Both are slow
## Flaky tools (MCP)
Claude sessions get the `flaky` MCP server automatically: Currents
Claude sessions and the headless OpenCode worker get the `flaky` MCP server automatically: Currents
flaky/quarantine data, the `qa_*` BigQuery dataset, Sentry RCA, live Linear,
and repo investigation. Login registers it from the repo-level
`FLAKY_MCP_TOKEN` / `FLAKY_MCP_URL` secrets. Forks have no secrets and skip
it. Tell the agent to call `get_flaky_context` first — it returns the rules
and repo investigation. The worker keeps the token in its environment and puts
only an environment reference in the OpenCode config. Forks have no secrets and
skip it. Tell the agent to call `get_flaky_context` first — it returns the rules
the tools assume.
`post-start.mjs` registers the server for Claude Code on each container start.
Its `headersHelper` reads the token from the secrets file on each connect, so
the server works in every client. The token is not copied into the Claude Code
MCP configuration.
## Quality and security skills (Claude plugins)
Claude sessions can also load the private skills from the
@ -188,7 +447,8 @@ to pull the skills into context.
## Viewing the dev UI locally
Two terminal windows:
`pnpm session tunnel` works with every client. With the terminal client, use
two terminal windows:
```bash
pnpm session # window 1: attach the agent session
@ -218,7 +478,7 @@ partial mapping breaks it.
| Event | Running processes | Disk (checkout, worktrees, chat history) |
|---|---|---|
| Detach / close laptop / network drop | ✅ keep running | ✅ |
| Stop, or idle timeout (default 30 min, max 4 h) | ❌ killed | ✅ |
| Stop, or idle timeout (2 h for a codespace that `pnpm session` creates, max 4 h) | ❌ killed | ✅ |
| Delete (`pnpm session rm`) | ❌ | ❌ (push your branches first) |
After a stop, `pnpm session <name>` restarts the codespace (~30–60 s); run
@ -238,10 +498,10 @@ After a stop, `pnpm session <name>` restarts the codespace (~30–60 s); run
agent injects them into VS Code sessions only; they're delivered
base64-encoded to `/workspaces/.codespaces/shared/.env-secrets`. The image
sources `/usr/local/lib/codespaces-env.sh` in login shells (profile.d), in
interactive shells (bashrc), and in the `pnpm session` prelude. If Claude
Code shows `Missing environment variables: FLAKY_MCP_TOKEN`, the shell that
started Claude did not source the file. Run
`. /usr/local/lib/codespaces-env.sh` and start Claude again.
interactive shells (bashrc), and in the `pnpm session` prelude. Desktop app
sessions do not source it. `git`, `gh`, `scripts/codespace-env.mjs`, and the
`flaky` MCP entry read the file when they run, so they work everywhere. New
code that needs a secret must do the same.
- **Do not read `CODESPACE_NAME` or `GITHUB_USER` from the process env** — use
`scripts/codespace-env.mjs`. Codespaces gives these variables to VS Code
sessions only. Other processes read them from `codespaces-env.sh`, and a
@ -250,7 +510,7 @@ After a stop, `pnpm session <name>` restarts the codespace (~30–60 s); run
polled correctly as its owner while `dev:up` in the same session saw an empty
box name, printed the localhost URL, and did not share the port. The helper
reads `/workspaces/.codespaces/shared`, which is always correct.
- **You cannot paste images into a remote Claude session.** Image paste reads
- **You cannot paste images into a terminal Claude session.** Image paste reads
the clipboard of the machine where `claude` runs — the codespace, not your
laptop. Drag the file into the VS Code explorer (or
`gh codespace cp shot.png remote:/workspaces/n8n/`) and give Claude the
@ -276,3 +536,7 @@ Compute bills only while the codespace runs: ~$0.72/hr for the 8-core box,
~nothing stopped. Usage draws from a shared monthly org budget, so
`pnpm session stop` when you leave; the idle timeout is the backstop, not the
plan.
Previews draw from the same budget on a 2-core box. Remove the
`codespace-preview` label when the review is done instead of waiting for the
24-hour retention.

View File

@ -1,35 +1,43 @@
#!/usr/bin/env node
// Poll worker for per-turn Claude Code conversations on a codespace.
// You cannot reach a codespace from outside. GitHub keeps forwarded ports
// private. So this worker calls out. It polls n8n for a turn addressed to this
// box's owner. It runs one `claude -p`. It sends the result to the turn's resume
// URL. Every call is outbound HTTPS to n8n. It opens no inbound port.
//
// AGENT_WORKER_TOKEN=… N8N_DEQUEUE_URL=… node agent-worker.mjs
//
// Env:
// N8N_DEQUEUE_URL n8n webhook that hands back one pending turn (required)
// AGENT_WORKER_TOKEN shared bearer sent on every dequeue (required)
// GITHUB_USER box owner's login; the bootstrap route for a new thread (see codespace-env.mjs)
// CODESPACE_NAME stable box id; routes a thread back to the box holding its session (same source)
// TURN_TIMEOUT_MS per-turn limit; keep below the n8n Wait limit (default 25 min)
import { execFile } from 'node:child_process';
// GitHub keeps Codespaces ports private, so inbound delivery is not available.
import { spawn } from 'node:child_process';
import { resolve as resolvePath, sep } from 'node:path';
import { setTimeout as sleep } from 'node:timers/promises';
import { pathToFileURL } from 'node:url';
import { codespaceEnv } from '../../scripts/codespace-env.mjs';
const DEQUEUE_URL = process.env.N8N_DEQUEUE_URL;
const TOKEN = process.env.AGENT_WORKER_TOKEN;
// Read both identities from the codespace. tmux can give an empty copy of either.
const SLACK_TOKEN = process.env.SLACK_BOT_TOKEN;
// tmux can retain empty identity values, but the Codespaces files stay current.
const GITHUB_USER = codespaceEnv('GITHUB_USER');
const BOX_ID = codespaceEnv('CODESPACE_NAME');
const ROOT = '/workspaces';
const POLL_INTERVAL_MS = 3000;
const INITIAL_POLL_INTERVAL_MS = 3000;
const MAX_POLL_INTERVAL_MS = 30_000;
const SLACK_UPDATE_INTERVAL_MS = 1500;
const SLACK_TEXT_LIMIT = 3900;
export function openCodeConfig(environment) {
const config = {
provider: { openrouter: { options: { apiKey: '{env:OPENROUTER_API_KEY}' } } },
};
if (environment.FLAKY_MCP_URL && environment.FLAKY_MCP_TOKEN) {
config.mcp = {
flaky: {
type: 'remote',
url: environment.FLAKY_MCP_URL,
enabled: true,
oauth: false,
headers: { Authorization: 'Bearer {env:FLAKY_MCP_TOKEN}' },
},
};
}
return config;
}
// Warn and use the default on a bad value, so a config mistake cannot silently
// disable the turn limit.
function posNum(name, fallback) {
const raw = process.env[name];
if (raw === undefined) return fallback;
@ -39,102 +47,169 @@ function posNum(name, fallback) {
return fallback;
}
// Keep this below the n8n Wait-node limit. Then the worker reports a slow turn
// before n8n's Wait ends with a generic message.
const TURN_TIMEOUT_MS = posNum('TURN_TIMEOUT_MS', 25 * 60_000);
function nextIdlePollInterval(interval) {
return Math.min(interval * 2, MAX_POLL_INTERVAL_MS);
}
for (const [k, v] of Object.entries({
N8N_DEQUEUE_URL: DEQUEUE_URL,
AGENT_WORKER_TOKEN: TOKEN,
GITHUB_USER,
})) {
if (!v) {
console.error(`Refusing to start: ${k} is not set.`);
process.exit(1);
export async function pollOnce(
interval,
{ dequeueTurn = dequeue, handleTurn = handle, wait = sleep, logError = console.error } = {},
) {
let turn;
try {
turn = await dequeueTurn();
} catch (error) {
logError(`poll error: ${error.message}`);
await wait(interval);
return nextIdlePollInterval(interval);
}
if (!turn) {
await wait(interval);
return nextIdlePollInterval(interval);
}
try {
await handleTurn(turn);
return INITIAL_POLL_INTERVAL_MS;
} catch (error) {
logError(`poll error: ${error.message}`);
await wait(INITIAL_POLL_INTERVAL_MS);
return nextIdlePollInterval(INITIAL_POLL_INTERVAL_MS);
}
}
// Not fatal, but box pinning needs it: without a box id every turn routes by
// owner, so a thread cannot follow the box holding its session.
if (!BOX_ID)
console.error(
'CODESPACE_NAME did not resolve — box pinning disabled; turns route by githubUser only.',
);
// This limit expires before n8n's Wait node so that the user receives a specific error.
const TURN_TIMEOUT_MS = posNum('TURN_TIMEOUT_MS', 25 * 60_000);
function turnTimeoutMessage(timeout) {
const duration = timeout % 60_000 === 0 ? `${timeout / 60_000}-minute` : `${timeout}-millisecond`;
return `The turn passed the ${duration} limit and stopped. It may have been in a build. Do a smaller step, or run a long build in its own turn.`;
}
// A turn gets a copy of this environment. Put the correct values in it, because
// `pnpm dev:up` and `gh -c $CODESPACE_NAME` in the session need them.
const TURN_ENV = { ...process.env };
export function openCodeEnvironment(environment) {
const childEnvironment = { ...environment };
delete childEnvironment.AGENT_WORKER_TOKEN;
delete childEnvironment.N8N_DEQUEUE_URL;
delete childEnvironment.SLACK_BOT_TOKEN;
childEnvironment.OPENCODE_CONFIG_CONTENT = JSON.stringify(openCodeConfig(childEnvironment));
childEnvironment.N8N_AGENT_RUNTIME = 'sandbox';
childEnvironment.N8N_AGENT_PROFILE = 'slack';
return childEnvironment;
}
// OpenCode does not need the credentials that control the broker.
const TURN_ENV = openCodeEnvironment(process.env);
if (BOX_ID) TURN_ENV.CODESPACE_NAME = BOX_ID;
if (GITHUB_USER) TURN_ENV.GITHUB_USER = GITHUB_USER;
const CODESPACE_DOCS = '.devcontainer/codespaces/README.md';
// A session cannot be told any of this after its final message, and a system
// prompt is not part of the resumed transcript, so send it on every turn. State
// the turn's hard limits inline: a session that has to read a file to learn them
// can reply before it gets there. Point at the box docs for the rest — they
// already cover dev:up, ports, and build cost, and AGENTS.md does not.
function turnContract(author) {
return [
'# Your runtime',
'You are one turn of a Slack thread, driven by an n8n workflow that runs you as a headless',
'`claude -p` on a GitHub codespace. Your final message is the reply that reaches Slack, so keep',
'it short and skip heavy markdown.',
author ? `You are replying to ${author}.` : '',
'',
'# A turn is atomic',
'The turn ends when you emit your final message, and everything you started ends with it:',
'background Bash tasks are killed, Monitor events never arrive, PushNotification has nowhere to',
'go, and ScheduleWakeup never fires. You get no turn of your own afterwards — you cannot speak',
'again until a human writes again. So run long work (builds, test suites, restarts) in the',
'foreground of this turn and wait for it, or do not start it at all. Never end a turn promising',
`to verify, check back, or follow up. Work that will not fit the turn limit of ~${Math.round(
TURN_TIMEOUT_MS / 60_000,
)} minutes`,
'should be split: do the part that fits, then say what to ask for next.',
'',
'# This box',
`You are on codespace ${BOX_ID ?? '(unknown)'}, not a laptop. Before you build, start, or expose`,
`the app, read ${CODESPACE_DOCS} ("Build and run the app in a session"). It is box-specific and`,
'the repo AGENTS.md does not cover it.',
]
.filter(Boolean)
.join('\n');
}
function runClaude({ message, sessionId, cwd, author }) {
function safeCwd(cwd) {
const safeCwd = resolvePath(typeof cwd === 'string' && cwd ? cwd : `${ROOT}/n8n`);
if (safeCwd !== ROOT && !safeCwd.startsWith(ROOT + sep))
throw new Error(`cwd must be under ${ROOT}`);
const args = [
'-p',
'--output-format',
'json',
'--dangerously-skip-permissions',
'--append-system-prompt',
turnContract(typeof author === 'string' ? author : ''),
];
if (sessionId) args.push('--resume', sessionId);
args.push(message);
return new Promise((res, rej) => {
execFile(
'claude',
args,
{ cwd: safeCwd, env: TURN_ENV, timeout: TURN_TIMEOUT_MS, maxBuffer: 64 * 1024 * 1024 },
(err, stdout, stderr) => {
try {
res(JSON.parse(stdout));
} catch {
if (err?.killed)
rej(
new Error(
`The turn passed the ${Math.round(TURN_TIMEOUT_MS / 60_000)}-minute limit and stopped. It may have been in a build. Do a smaller step, or run a long build in its own turn.`,
),
);
else rej(new Error(stderr?.trim() || err?.message || 'claude produced no output'));
}
},
);
return safeCwd;
}
function stopProcessTree(child, signal) {
if (!child.pid) return;
try {
process.kill(-child.pid, signal);
} catch {
child.kill(signal);
}
}
function eventError(event) {
return event.error?.data?.message ?? event.error?.message ?? event.error?.name;
}
export function runOpenCode(
{ message, sessionId, cwd },
onEvent,
onSession,
{
spawnProcess = spawn,
stopProcess = stopProcessTree,
timeout = TURN_TIMEOUT_MS,
killDelay = 5000,
} = {},
) {
const directory = safeCwd(cwd);
const args = ['run', '--model', 'openrouter/openai/gpt-5.6-sol', '--format', 'json', '--auto'];
if (sessionId) args.push('--session', sessionId);
return new Promise((resolve, reject) => {
const child = spawnProcess('opencode', args, {
cwd: directory,
detached: true,
env: TURN_ENV,
stdio: ['pipe', 'pipe', 'pipe'],
});
let activeSessionId = sessionId ?? '';
let buffer = '';
let stderr = '';
let error = '';
const text = [];
let timedOut = false;
let forceTimer;
const timer = setTimeout(() => {
timedOut = true;
stopProcess(child, 'SIGTERM');
forceTimer = setTimeout(() => stopProcess(child, 'SIGKILL'), killDelay);
forceTimer.unref?.();
}, timeout);
const consume = (line) => {
if (!line.trim()) return;
let event;
try {
event = JSON.parse(line);
} catch {
console.error(`OpenCode output ignored: ${line.slice(0, 200)}`);
return;
}
if (!activeSessionId && typeof event.sessionID === 'string') {
activeSessionId = event.sessionID;
onSession(activeSessionId);
}
if (event.sessionID !== activeSessionId) return;
onEvent(event);
if (event.type === 'text' && typeof event.part?.text === 'string') text.push(event.part.text);
if (event.type === 'error') error = eventError(event) || 'OpenCode failed';
};
child.stdout.setEncoding('utf8');
child.stdout.on('data', (chunk) => {
buffer += chunk;
const lines = buffer.split(/\r?\n/);
buffer = lines.pop() ?? '';
for (const line of lines) consume(line);
});
child.stderr.setEncoding('utf8');
child.stderr.on('data', (chunk) => (stderr = `${stderr}${chunk}`.slice(-4000)));
child.once('error', (processError) => {
clearTimeout(timer);
if (forceTimer) clearTimeout(forceTimer);
reject(processError);
});
child.once('close', (code) => {
clearTimeout(timer);
if (forceTimer) clearTimeout(forceTimer);
consume(buffer);
if (timedOut) {
reject(new Error(turnTimeoutMessage(timeout)));
return;
}
if (code !== 0 || error) {
const failureMessage = error || stderr.trim() || `OpenCode exited with ${code}`;
reject(new Error(failureMessage));
return;
}
if (!activeSessionId) {
reject(new Error('OpenCode did not return a session id'));
return;
}
resolve({ result: text.join('\n').trim(), session_id: activeSessionId });
});
child.stdin.end(message);
});
}
@ -151,20 +226,136 @@ async function dequeue() {
const res = await post(DEQUEUE_URL, { githubUser: GITHUB_USER, boxId: BOX_ID, token: TOKEN });
if (!res.ok) throw new Error(`dequeue HTTP ${res.status}`);
const text = await res.text();
if (!text.trim()) return null; // no pending turn
if (!text.trim()) return null;
const turn = JSON.parse(text);
return turn?.turnId ? turn : null;
}
async function handle(turn) {
let result;
async function slackApi(method, body) {
const res = await fetch(`https://slack.com/api/${method}`, {
method: 'POST',
headers: { authorization: `Bearer ${SLACK_TOKEN}`, 'content-type': 'application/json' },
body: JSON.stringify(body),
signal: AbortSignal.timeout(20_000),
});
const result = await res.json();
if (!res.ok || !result.ok) throw new Error(result.error || `HTTP ${res.status}`);
return result;
}
function progressText(tools) {
const progress = [...tools.values()].slice(-6).map(({ status, title }) => {
if (status === 'error') return `Failed: ${title}`;
return `Done: ${title}`;
});
return (progress.length ? progress.join('\n') : 'Flaky is working…').slice(0, SLACK_TEXT_LIMIT);
}
function finalSlackText(text, sessionId, boxId) {
const metadata = [sessionId && `⟳session:${sessionId}`, boxId && `⟳box:${boxId}`]
.filter(Boolean)
.join(' ');
const suffix = metadata ? `\n\n${metadata}` : '';
const body = text || 'Flaky completed the turn';
return `${body.slice(0, SLACK_TEXT_LIMIT - suffix.length)}${suffix}`;
}
const NO_SLACK_PROGRESS = { event() {}, async finish() {} };
export async function startSlackProgress(
turn,
{
callSlack = SLACK_TOKEN ? slackApi : undefined,
updateInterval = SLACK_UPDATE_INTERVAL_MS,
} = {},
) {
const channel = turn.slack?.channel;
const threadTs = turn.slack?.thread_ts;
if (!callSlack || typeof channel !== 'string' || typeof threadTs !== 'string')
return NO_SLACK_PROGRESS;
let message;
try {
const r = await runClaude(turn);
message = await callSlack('chat.postMessage', {
channel,
thread_ts: threadTs,
text: 'Flaky is working…',
});
} catch (error) {
console.error(`turn ${turn.turnId}: Slack placeholder failed: ${error.message}`);
return NO_SLACK_PROGRESS;
}
const tools = new Map();
let timer;
let inFlight;
let pending;
let lastUpdate = 0;
let stopped = false;
const update = (text) =>
callSlack('chat.update', { channel, ts: message.ts, text }).catch((error) =>
console.error(`turn ${turn.turnId}: Slack update failed: ${error.message}`),
);
const flush = () => {
if (stopped || timer || inFlight || pending === undefined) return;
const delay = Math.max(0, lastUpdate + updateInterval - Date.now());
timer = setTimeout(() => {
timer = undefined;
const text = pending;
pending = undefined;
lastUpdate = Date.now();
inFlight = update(text).finally(() => {
inFlight = undefined;
flush();
});
}, delay);
};
const schedule = () => {
pending = progressText(tools);
flush();
};
return {
event(event) {
if (!event.part?.id) return;
if (event.type === 'tool_use') {
tools.set(event.part.id, {
status: event.part.state?.status,
title: event.part.state?.title || event.part.tool,
});
schedule();
}
},
async finish(text, sessionId, boxId) {
stopped = true;
pending = undefined;
if (timer) {
clearTimeout(timer);
timer = undefined;
}
if (inFlight) await inFlight;
const delay = Math.max(0, lastUpdate + updateInterval - Date.now());
if (delay) await sleep(delay);
await update(finalSlackText(text, sessionId, boxId));
},
};
}
async function handle(turn) {
console.log(
`${new Date().toISOString()} turn ${turn.turnId} by ${turn.author ?? 'unknown'}: ${turn.sessionId ? 'resume' : 'new'}`,
);
let result;
let activeSessionId = turn.sessionId ?? '';
const progress = await startSlackProgress(turn);
try {
const r = await runOpenCode(turn, progress.event, (sessionId) => (activeSessionId = sessionId));
result = {
turnId: turn.turnId,
status: 'done',
output: r.result ?? '',
sessionId: r.session_id ?? turn.sessionId ?? '',
output: r.result,
sessionId: r.session_id,
boxId: BOX_ID,
};
} catch (error) {
@ -172,13 +363,12 @@ async function handle(turn) {
turnId: turn.turnId,
status: 'error',
output: error.message,
sessionId: turn.sessionId ?? '',
sessionId: activeSessionId,
boxId: BOX_ID,
};
}
// Send the result to the turn's resume URL. This continues the waiting n8n
// execution. Retry on a failed status or a network error, so a transient
// failure does not drop the result and leave the turn to time out.
await progress.finish(result.output, result.sessionId, result.boxId);
// The resume POST is the delivery contract. Retry transient failures.
for (let attempt = 1; attempt <= 3; attempt++) {
try {
const res = await post(turn.resumeUrl, result);
@ -194,19 +384,33 @@ async function handle(turn) {
console.error(`turn ${turn.turnId}: result not delivered after 3 attempts`);
}
console.log(`agent-worker polling as ${GITHUB_USER} every ${POLL_INTERVAL_MS}ms`);
for (;;) {
try {
const turn = await dequeue();
if (turn) {
console.log(
`${new Date().toISOString()} turn ${turn.turnId} by ${turn.author ?? 'unknown'}: ${turn.sessionId ? 'resume' : 'new'}`,
);
await handle(turn);
continue; // get the next turn now, with no delay
async function main() {
for (const [key, value] of Object.entries({
N8N_DEQUEUE_URL: DEQUEUE_URL,
AGENT_WORKER_TOKEN: TOKEN,
OPENROUTER_API_KEY: process.env.OPENROUTER_API_KEY,
GITHUB_USER,
})) {
if (!value) {
console.error(`Refusing to start: ${key} is not set.`);
process.exit(1);
}
} catch (error) {
console.error(`poll error: ${error.message}`);
}
await sleep(POLL_INTERVAL_MS);
if (!BOX_ID)
console.error(
'CODESPACE_NAME did not resolve — box pinning disabled; turns route by githubUser only.',
);
if (!SLACK_TOKEN)
console.error(
'SLACK_BOT_TOKEN is not set. Turns will complete without Slack progress updates.',
);
console.log(
`agent-worker polling as ${GITHUB_USER} every ${INITIAL_POLL_INTERVAL_MS}-${MAX_POLL_INTERVAL_MS}ms`,
);
let pollInterval = INITIAL_POLL_INTERVAL_MS;
for (;;) pollInterval = await pollOnce(pollInterval);
}
if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) await main();

View File

@ -0,0 +1,383 @@
import assert from 'node:assert/strict';
import { EventEmitter } from 'node:events';
import { readFileSync } from 'node:fs';
import { PassThrough } from 'node:stream';
import { test } from 'node:test';
import { setTimeout as sleep } from 'node:timers/promises';
import {
openCodeConfig,
openCodeEnvironment,
pollOnce,
runOpenCode,
startSlackProgress,
} from './agent-worker.mjs';
const turn = {
turnId: 'turn-1',
slack: { channel: 'C123', thread_ts: '123.456' },
};
test('reloads Codespaces secrets before the worker starts', () => {
const postStart = readFileSync(new URL('./post-start.mjs', import.meta.url), 'utf8');
assert.match(
postStart,
/bash -lc "\. \/usr\/local\/lib\/codespaces-env\.sh; .*node \/workspaces\/n8n\/\.devcontainer\/codespaces\/agent-worker\.mjs/,
);
assert.match(postStart, /installAgentHarness\(\)/);
assert.match(postStart, /harness\.status === 'active' &&\s+tryRun\('worker start'/);
assert.match(postStart, /JSON\.stringify\(\{ installed, failed, harness, workerStarted \}/);
});
function slackRecorder() {
const calls = [];
return {
calls,
async call(method, body) {
calls.push({ method, body });
return method === 'chat.postMessage' ? { ok: true, ts: '456.789' } : { ok: true };
},
};
}
async function waitFor(check) {
for (let attempt = 0; attempt < 100; attempt++) {
if (check()) return;
await sleep(5);
}
throw new Error('Condition was not met');
}
function childProcess() {
const child = new EventEmitter();
child.pid = 123;
child.stdin = new PassThrough();
child.stdout = new PassThrough();
child.stderr = new PassThrough();
child.kill = () => {};
return child;
}
function completedChild(lines) {
const child = childProcess();
setImmediate(() => {
for (const line of lines) child.stdout.write(`${line}\n`);
child.stdout.end();
child.stderr.end();
child.emit('close', 0);
});
return child;
}
test('backs off idle polls and resets when work arrives', async () => {
let interval = 3000;
const waits = [];
const turns = [null, null, null, null, null, { turnId: '1' }, { turnId: '2' }];
const handled = [];
for (let attempt = 0; attempt < 5; attempt++) {
interval = await pollOnce(interval, {
dequeueTurn: async () => turns.shift(),
handleTurn: async (turn) => handled.push(turn.turnId),
wait: async (delay) => waits.push(delay),
});
}
assert.deepEqual(waits, [3000, 6000, 12_000, 24_000, 30_000]);
assert.equal(interval, 30_000);
interval = await pollOnce(interval, {
dequeueTurn: async () => turns.shift(),
handleTurn: async (turn) => handled.push(turn.turnId),
wait: async (delay) => waits.push(delay),
});
interval = await pollOnce(interval, {
dequeueTurn: async () => turns.shift(),
handleTurn: async (turn) => handled.push(turn.turnId),
wait: async (delay) => waits.push(delay),
});
assert.equal(interval, 3000);
assert.deepEqual(handled, ['1', '2']);
assert.equal(waits.length, 5);
});
test('backs off after a dequeue error', async () => {
const waits = [];
const errors = [];
const interval = await pollOnce(3000, {
dequeueTurn: async () => {
throw new Error('unavailable');
},
wait: async (delay) => waits.push(delay),
logError: (error) => errors.push(error),
});
assert.equal(interval, 6000);
assert.deepEqual(waits, [3000]);
assert.deepEqual(errors, ['poll error: unavailable']);
});
test('resets the interval before handling work', async () => {
const waits = [];
const errors = [];
const interval = await pollOnce(30_000, {
dequeueTurn: async () => ({ turnId: '1' }),
handleTurn: async () => {
throw new Error('failed');
},
wait: async (delay) => waits.push(delay),
logError: (error) => errors.push(error),
});
assert.equal(interval, 6000);
assert.deepEqual(waits, [3000]);
assert.deepEqual(errors, ['poll error: failed']);
});
test('streams tool progress but excludes reasoning', async () => {
const slack = slackRecorder();
const progress = await startSlackProgress(turn, {
callSlack: slack.call,
updateInterval: 50,
});
progress.event({
type: 'reasoning',
sessionID: 'ses_1',
part: { id: 'prt_reason', type: 'reasoning', text: 'private reasoning' },
});
progress.event({
type: 'tool_use',
sessionID: 'ses_1',
part: {
id: 'prt_tool',
type: 'tool',
tool: 'read',
state: { status: 'completed', title: 'Read worker source' },
},
});
await sleep(100);
const streamed = slack.calls.find((call) => call.method === 'chat.update')?.body.text;
assert.match(streamed, /Done: Read worker source/);
assert.doesNotMatch(streamed, /private reasoning/);
});
test('coalesces Slack updates within the configured interval', async () => {
const slack = slackRecorder();
const progress = await startSlackProgress(turn, {
callSlack: slack.call,
updateInterval: 50,
});
for (const [id, title] of ['One', 'Two', 'Three'].entries()) {
progress.event({
type: 'tool_use',
sessionID: 'ses_1',
part: {
id: `prt_${id}`,
type: 'tool',
tool: 'read',
state: { status: 'completed', title },
},
});
}
await sleep(100);
assert.equal(slack.calls.filter((call) => call.method === 'chat.update').length, 1);
progress.event({
type: 'tool_use',
sessionID: 'ses_1',
part: {
id: 'prt_4',
type: 'tool',
tool: 'read',
state: { status: 'completed', title: 'Four' },
},
});
assert.equal(slack.calls.filter((call) => call.method === 'chat.update').length, 1);
await sleep(100);
assert.equal(slack.calls.filter((call) => call.method === 'chat.update').length, 2);
});
test('replaces progress with the final answer', async () => {
const calls = [];
let releaseProgress;
const callSlack = async (method, body) => {
calls.push({ method, body });
if (method === 'chat.postMessage') return { ok: true, ts: '456.789' };
if (calls.filter((call) => call.method === 'chat.update').length === 1) {
return await new Promise((resolve) => (releaseProgress = () => resolve({ ok: true })));
}
return { ok: true };
};
const progress = await startSlackProgress(turn, {
callSlack,
updateInterval: 0,
});
progress.event({
type: 'tool_use',
sessionID: 'ses_1',
part: {
id: 'prt_tool',
type: 'tool',
tool: 'read',
state: { status: 'completed', title: 'Read worker source' },
},
});
await waitFor(() => calls.filter((call) => call.method === 'chat.update').length === 1);
for (const title of ['Second tool', 'Third tool', 'Fourth tool']) {
progress.event({
type: 'tool_use',
sessionID: 'ses_1',
part: {
id: title,
type: 'tool',
tool: 'read',
state: { status: 'completed', title },
},
});
}
const finish = progress.finish('Final answer', 'ses_1', 'box-1');
await sleep(10);
assert.equal(calls.filter((call) => call.method === 'chat.update').length, 1);
releaseProgress();
await finish;
assert.deepEqual(calls.at(-1), {
method: 'chat.update',
body: {
channel: 'C123',
ts: '456.789',
text: 'Final answer\n\n⟳session:ses_1 ⟳box:box-1',
},
});
});
test('streams OpenCode CLI events and resumes an OpenCode session', async () => {
let invocation;
let prompt = '';
const events = [];
const result = await runOpenCode(
{
message: 'Test message',
sessionId: 'ses_existing',
cwd: '/workspaces/n8n',
author: 'Tester',
},
(event) => events.push(event),
() => {},
{
spawnProcess(command, args, options) {
invocation = { command, args, options };
const child = completedChild([
JSON.stringify({ type: 'step_start', sessionID: 'ses_existing', part: {} }),
JSON.stringify({
type: 'tool_use',
sessionID: 'ses_existing',
part: { id: 'prt_1', type: 'tool', tool: 'read', state: { status: 'completed' } },
}),
JSON.stringify({
type: 'text',
sessionID: 'ses_existing',
part: { id: 'prt_2', type: 'text', text: 'Completed response' },
}),
]);
child.stdin.on('data', (chunk) => (prompt += chunk));
return child;
},
},
);
assert.equal(result.result, 'Completed response');
assert.equal(result.session_id, 'ses_existing');
assert.deepEqual(invocation.args, [
'run',
'--model',
'openrouter/openai/gpt-5.6-sol',
'--format',
'json',
'--auto',
'--session',
'ses_existing',
]);
assert.equal(invocation.options.detached, true);
assert.equal(prompt, 'Test message');
assert.deepEqual(
events.map((event) => event.type),
['step_start', 'tool_use', 'text'],
);
});
test('stops the OpenCode process group at the turn limit', async () => {
const signals = [];
let child;
const run = runOpenCode(
{ message: 'Test message', cwd: '/workspaces/n8n', author: 'Tester' },
() => {},
() => {},
{
spawnProcess() {
child = childProcess();
return child;
},
stopProcess(_child, signal) {
signals.push(signal);
if (signal === 'SIGTERM') setImmediate(() => child.emit('close', null));
},
killDelay: 5,
timeout: 1,
},
);
await assert.rejects(run, /The turn passed the 1-millisecond limit/);
assert.deepEqual(signals, ['SIGTERM']);
});
test('keeps broker credentials out of the OpenCode process', () => {
const environment = openCodeEnvironment({
AGENT_WORKER_TOKEN: 'worker',
N8N_DEQUEUE_URL: 'https://example.com',
SLACK_BOT_TOKEN: 'slack',
ANTHROPIC_API_KEY: 'model',
OPENROUTER_API_KEY: 'openrouter',
GITHUB_TOKEN: 'github',
});
assert.deepEqual(JSON.parse(environment.OPENCODE_CONFIG_CONTENT), {
provider: { openrouter: { options: { apiKey: '{env:OPENROUTER_API_KEY}' } } },
});
delete environment.OPENCODE_CONFIG_CONTENT;
assert.deepEqual(environment, {
ANTHROPIC_API_KEY: 'model',
OPENROUTER_API_KEY: 'openrouter',
GITHUB_TOKEN: 'github',
N8N_AGENT_RUNTIME: 'sandbox',
N8N_AGENT_PROFILE: 'slack',
});
});
test('configures Flaky MCP without serializing its token', () => {
const token = 'flaky-secret';
const environment = openCodeEnvironment({
FLAKY_MCP_URL: 'https://example.com/mcp',
FLAKY_MCP_TOKEN: token,
OPENROUTER_API_KEY: 'openrouter',
});
assert.deepEqual(JSON.parse(environment.OPENCODE_CONFIG_CONTENT), {
provider: { openrouter: { options: { apiKey: '{env:OPENROUTER_API_KEY}' } } },
mcp: {
flaky: {
type: 'remote',
url: 'https://example.com/mcp',
enabled: true,
oauth: false,
headers: { Authorization: 'Bearer {env:FLAKY_MCP_TOKEN}' },
},
},
});
assert.equal(environment.FLAKY_MCP_TOKEN, token);
assert.equal(environment.OPENCODE_CONFIG_CONTENT.includes(token), false);
});
test('omits Flaky MCP when either setting is missing', () => {
assert.equal(openCodeConfig({ FLAKY_MCP_URL: 'https://example.com/mcp' }).mcp, undefined);
assert.equal(openCodeConfig({ FLAKY_MCP_TOKEN: 'flaky-secret' }).mcp, undefined);
});

View File

@ -0,0 +1,150 @@
import assert from 'node:assert/strict';
import { spawnSync } from 'node:child_process';
import { chmodSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { after, before, test } from 'node:test';
import { fileURLToPath } from 'node:url';
const repoRoot = fileURLToPath(new URL('../../', import.meta.url));
const sessionScript = join(repoRoot, 'scripts/cloud-session.mjs');
let fixtureDir;
let invocation = 0;
before(() => {
fixtureDir = mkdtempSync(join(tmpdir(), 'cloud-session-'));
const mockGh = join(fixtureDir, 'gh');
writeFileSync(
mockGh,
`#!/usr/bin/env node
const { writeFileSync } = require('node:fs');
const args = process.argv.slice(2);
if (args[0] === 'codespace' && args[1] === 'list') {
process.stdout.write('[{"name":"test-codespace","state":"Available"}]\\n');
} else {
writeFileSync(process.env.GH_CAPTURE, JSON.stringify(args));
}
`,
);
chmodSync(mockGh, 0o755);
for (const [name, script] of Object.entries({
infocmp: '[ "$1" = "$SUPPORTED_TERM" ]',
tmux: 'printf "%s" "$TERM"',
})) {
const executable = join(fixtureDir, name);
writeFileSync(executable, `#!/bin/sh\n${script}\n`);
chmodSync(executable, 0o755);
}
});
after(() => rmSync(fixtureDir, { recursive: true, force: true }));
function runSession(args) {
const capture = join(fixtureDir, `invocation-${invocation++}.json`);
const result = spawnSync(process.execPath, [sessionScript, ...args], {
encoding: 'utf8',
env: {
...process.env,
GH_CAPTURE: capture,
PATH: `${fixtureDir}:${process.env.PATH}`,
},
});
assert.equal(result.status, 0, result.stderr || result.stdout);
return JSON.parse(readFileSync(capture, 'utf8'));
}
function remoteCommand(args) {
const invocationArgs = runSession(args);
assert.deepEqual(invocationArgs.slice(0, 6), [
'codespace',
'ssh',
'-c',
'test-codespace',
'--',
'-t',
]);
return invocationArgs[6];
}
for (const { name, supportedTerm, expectedTerm } of [
{
name: 'uses xterm-256color when the remote terminal definition is missing',
supportedTerm: 'xterm-256color',
expectedTerm: 'xterm-256color',
},
{
name: 'keeps the terminal type when the remote definition is available',
supportedTerm: 'xterm-ghostty',
expectedTerm: 'xterm-ghostty',
},
]) {
test(name, () => {
const result = spawnSync('/bin/sh', ['-c', remoteCommand(['--opencode', '--legacy'])], {
encoding: 'utf8',
env: {
...process.env,
PATH: `${fixtureDir}:${process.env.PATH}`,
TERM: 'xterm-ghostty',
SUPPORTED_TERM: supportedTerm,
},
});
assert.equal(result.status, 0, result.stderr);
assert.equal(result.stdout, expectedTerm);
});
}
test('starts a named OpenCode session in a worktree', () => {
const command = remoteCommand(['--opencode', 'fix-flaky', '--legacy', '--model', 'test']);
assert.match(command, /tmux new -As fix-flaky-opencode/);
assert.match(command, /unset AGENT_WORKER_TOKEN N8N_DEQUEUE_URL SLACK_BOT_TOKEN/);
assert.match(command, /OPENCODE_CONFIG_CONTENT/);
assert.match(command, /export N8N_AGENT_RUNTIME=sandbox; unset N8N_AGENT_PROFILE/);
assert.match(
command,
/git -C \/workspaces\/n8n fetch origin master; git -C \/workspaces\/n8n worktree add --no-track -b "session\/fix-flaky" "\/workspaces\/wt-fix-flaky" origin\/master/,
);
assert.match(command, /cd "\/workspaces\/wt-fix-flaky" && opencode --auto --model test/);
assert.doesNotMatch(command, /unset OPENROUTER_API_KEY/);
assert.doesNotMatch(command, /claude plugin/);
});
test('forwards legacy flags without a workspace name to the main checkout', () => {
for (const flags of [['--help'], ['--model', 'test']]) {
const command = remoteCommand(['--opencode', '--legacy', ...flags]);
assert.match(command, /tmux new -As agent-opencode/);
assert.ok(command.includes(`cd /workspaces/n8n && opencode --auto ${flags.join(' ')}`));
assert.doesNotMatch(command, /git .*worktree add/);
}
});
test('keeps removed credentials out of the login shell', () => {
const command = remoteCommand(['--shell']);
const profile = readFileSync(new URL('./codespaces-secrets.sh', import.meta.url), 'utf8');
const dockerfile = readFileSync(new URL('./Dockerfile', import.meta.url), 'utf8');
assert.match(command, /tmux new -As agent-shell/);
assert.match(
command,
/unset AGENT_WORKER_TOKEN N8N_DEQUEUE_URL SLACK_BOT_TOKEN; unset OPENROUTER_API_KEY/,
);
assert.match(command, /export N8N_SKIP_CODESPACE_SECRETS=1; exec "\$\{SHELL:-\/bin\/bash\}" -l/);
assert.ok(profile.includes('[ "${N8N_SKIP_CODESPACE_SECRETS:-}" = "1" ] && return'));
assert.ok(
dockerfile.includes(
'RUN echo \'[ "${N8N_SKIP_CODESPACE_SECRETS:-}" = "1" ] || . /usr/local/lib/codespaces-env.sh\'',
),
);
assert.doesNotMatch(command, /claude plugin|OPENCODE_CONFIG_CONTENT/);
});
test('keeps the existing Claude session behavior', () => {
const command = remoteCommand(['agent', '--model', 'test']);
assert.match(command, /tmux new -As agent/);
assert.match(command, /unset OPENROUTER_API_KEY/);
assert.match(command, /claude plugin marketplace add/);
assert.match(command, /cd \/workspaces\/n8n && claude --model test/);
assert.doesNotMatch(command, /OPENCODE_CONFIG_CONTENT|N8N_SKIP_CODESPACE_SECRETS/);
});

View File

@ -1,3 +1,6 @@
# Interactive shell sessions set this guard after they remove worker credentials.
[ "${N8N_SKIP_CODESPACE_SECRETS:-}" = "1" ] && return
# Login-shell setup for ssh/tmux sessions: export secrets, then do the
# one-time registrations.
. /usr/local/lib/codespaces-env.sh
@ -9,14 +12,6 @@
'{"hasCompletedOnboarding":true,"theme":"dark","projects":{"/workspaces/n8n":{"hasTrustDialogAccepted":true}}}' \
>"$HOME/.claude.json"
# Register the Flaky MCP server for Claude Code. The config keeps a literal
# ${FLAKY_MCP_TOKEN}; Claude Code expands it at connect time, so the token is
# not written to disk. Forks have no repo secrets and skip this.
if [ -n "$FLAKY_MCP_URL" ] && ! grep -qs '"flaky"' "$HOME/.claude.json" && command -v claude >/dev/null 2>&1; then
claude mcp add --scope user --transport http flaky "$FLAKY_MCP_URL" \
--header 'Authorization: Bearer ${FLAKY_MCP_TOKEN}' >/dev/null 2>&1 || true
fi
# Register the credential helper in the user config, because Codespaces
# regenerates the managed /etc/gitconfig. When the env token is missing, the
# system helper exits 0 without output, and git falls through to ours.

View File

@ -5,9 +5,9 @@
// Codespaces clones the repo into /workspaces/<repo>, unlike the laptop config
"workspaceFolder": "/workspaces/n8n",
"hostRequirements": {
"cpus": 8,
"memory": "32gb",
"storage": "64gb"
"cpus": 2,
"memory": "8gb",
"storage": "32gb"
},
"features": {
"ghcr.io/devcontainers/features/github-cli:1": {},
@ -18,13 +18,17 @@
"ghcr.io/devcontainers/features/docker-in-docker:2": {}
},
"forwardPorts": [8080, 5678],
// Grant the codespace's own GitHub token read access to the private skills
// marketplace (same org), so Claude can install it with the codespace's normal
// git auth — no extra PAT. Each user authorizes this once at codespace creation.
"customizations": {
// Claude Code in VS Code or the browser editor. It runs in the codespace.
"vscode": {
"extensions": ["anthropic.claude-code"]
},
// Grant the Codespace token read access to the private skills and harness repositories.
// Each user authorizes this access once when they create the Codespace.
"codespaces": {
"repositories": {
"n8n-io/n8n-agent-skills": { "permissions": { "contents": "read" } }
"n8n-io/n8n-agent-skills": { "permissions": { "contents": "read" } },
"n8n-io/cat-bot": { "permissions": { "contents": "read" } }
}
}
},
@ -38,12 +42,18 @@
"ANTHROPIC_API_KEY": {
"description": "Anthropic API key for Claude Code / OpenCode sessions"
},
"OPENROUTER_API_KEY": {
"description": "OpenRouter API key for Flaky's OpenCode sessions"
},
"AGENT_WORKER_TOKEN": {
"description": "The shared bearer token. The poll worker sends it to n8n on each dequeue. See .devcontainer/codespaces/agent-worker.mjs."
},
"N8N_DEQUEUE_URL": {
"description": "n8n webhook the poll worker calls to claim a pending turn"
},
"SLACK_BOT_TOKEN": {
"description": "Slack bot token for agent progress messages. It needs the chat:write scope."
},
"CLAUDE_CODE_OAUTH_TOKEN": {
"description": "Alternative to an API key: token from `claude setup-token` (Max subscription)"
},

View File

@ -27,7 +27,7 @@ services:
DB_TYPE: postgresdb
DB_POSTGRESDB_PASSWORD: password
# Prompt-cache TTL in seconds. 1 hour keeps the cache warm between turns.
ANTHROPIC_PROMPT_CACHE_TTL: "3600"
ANTHROPIC_PROMPT_CACHE_TTL: '3600'
# Do not add CODESPACE_NAME here. Codespaces does not give it to compose, so
# this file can set an empty value only. An empty value is worse than no
# value, because tmux keeps it for each new pane. The consumers read the name

View File

@ -0,0 +1,36 @@
// Fake executables for tests. Each shim is a Node script. It receives `args`, `root`,
// `file(name)` under the root, and `log(event)`, which appends to calls.jsonl in the root.
import { mkdirSync, mkdtempSync, readFileSync, writeFileSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
const COMMON = `
const fs = require('node:fs');
const path = require('node:path');
const args = process.argv.slice(2);
const root = process.env.TEST_ROOT;
const file = (name) => path.join(root, name);
const log = (event) => fs.appendFileSync(file('calls.jsonl'), JSON.stringify(event) + '\\n');
`;
export function fakeBinaries(prefix) {
const root = mkdtempSync(join(tmpdir(), prefix));
const binDir = join(root, 'bin');
mkdirSync(binDir);
const logFile = join(root, 'calls.jsonl');
writeFileSync(logFile, '');
return {
root,
env: { PATH: `${binDir}:${process.env.PATH}`, TEST_ROOT: root },
bin: (name, body) =>
writeFileSync(join(binDir, name), `#!${process.execPath}\n${COMMON}\n${body}`, {
mode: 0o755,
}),
calls: () =>
readFileSync(logFile, 'utf8')
.trim()
.split('\n')
.filter(Boolean)
.map((line) => JSON.parse(line)),
};
}

View File

@ -0,0 +1,224 @@
import assert from 'node:assert/strict';
import { spawn } from 'node:child_process';
import { rmSync, readFileSync } from 'node:fs';
import { test } from 'node:test';
import { setTimeout as delay } from 'node:timers/promises';
import { fileURLToPath } from 'node:url';
import { fakeBinaries } from './fake-bin.mjs';
import { parseOpenCodeArgs } from '../../scripts/cloud-session-opencode.mjs';
const script = fileURLToPath(new URL('../../scripts/cloud-session.mjs', import.meta.url));
const secret = 'a'.repeat(64);
function fixture(t, env = {}) {
const { root, bin, calls, env: binEnv } = fakeBinaries('opencode-client-');
t.after(() => rmSync(root, { recursive: true, force: true }));
bin(
'gh',
`
log({ command: 'gh', args });
if (args[0] === 'codespace' && args[1] === 'list') {
console.log(JSON.stringify([
{ name: 'dev-box', state: 'Available' },
]));
} else if (args.includes('-N')) {
if (process.env.TEST_TUNNEL_FAIL) process.exit(1);
const mapping = args[args.indexOf('-L') + 1];
const port = +mapping.split(':')[1];
const ssh = require('node:child_process').spawn(process.execPath, ['-e', 'setInterval(() => {}, 1000)'], { stdio: 'ignore' });
log({ event: 'ssh-child', pid: ssh.pid });
const server = require('node:http').createServer((req, res) => {
const expected = 'Basic ' + Buffer.from('opencode:' + '${secret}').toString('base64');
if (process.env.TEST_UNAUTHORIZED || req.headers.authorization !== expected) { res.writeHead(401).end(); return; }
res.setHeader('content-type', 'application/json');
res.end(JSON.stringify({ healthy: true, version: process.env.TEST_SERVER_VERSION || '1.2.3' }));
});
server.listen(port, '127.0.0.1');
if (process.env.TEST_TUNNEL_DROP) setTimeout(() => process.exit(1), 500);
process.on('SIGTERM', () => { log({ event: 'tunnel-stopped' }); server.closeAllConnections(); server.close(() => process.exit(0)); });
} else if (args.at(-1).includes('node --input-type=module')) {
let source = '';
process.stdin.on('data', chunk => source += chunk);
process.stdin.on('end', () => {
log({ event: 'bootstrap', hasSource: source.includes('prepareOpenCode') });
if (process.env.TEST_BOOTSTRAP_FAIL) process.exit(1);
const parts = args.at(-1).split(' ');
const name = parts.at(-3);
const fresh = parts.at(-2) === 'true';
const web = parts.at(-1) === 'true';
console.log(JSON.stringify({ port: 4242, password: '${secret}',
...(fresh && !web ? {} : { sessionID: 'ses_saved' }),
directory: name === 'agent' ? '/workspaces/n8n' : '/workspaces/wt-' + name }));
});
}
`,
);
bin(
'opencode',
`
if (args[0] === '--version') {
if (process.env.TEST_NO_CLIENT) process.exit(1);
console.log(process.env.TEST_CLIENT_VERSION || '1.2.3');
} else {
log({ command: 'opencode', args, password: process.env.OPENCODE_SERVER_PASSWORD });
if (process.env.TEST_CLIENT_WAIT) setInterval(() => {}, 1000);
}
`,
);
for (const opener of ['open', 'xdg-open']) bin(opener, `log({ command: 'browser', args });`);
return {
calls,
start(args) {
const child = spawn(process.execPath, [script, '--opencode', ...args], {
env: { ...process.env, ...binEnv, ...env },
stdio: ['ignore', 'pipe', 'pipe'],
});
let output = '';
child.stdout.on('data', (chunk) => {
output += chunk;
});
child.stderr.on('data', (chunk) => {
output += chunk;
});
const done = new Promise((resolve) => child.on('exit', (code) => resolve({ code, output })));
t.after(() => child.kill('SIGKILL'));
return { child, done };
},
};
}
async function waitFor(check) {
for (let attempt = 0; attempt < 100; attempt++) {
const result = check();
if (result) return result;
await delay(50);
}
throw new Error('Fixture did not become ready.');
}
// An orphaned fixture process can linger as a zombie where PID 1 does not reap
// it, and kill(pid, 0) still succeeds for one. Read the process state where the
// kernel exposes it; without /proc only the signal error can tell.
function stopped(pid) {
try {
process.kill(pid, 0);
} catch {
return true;
}
try {
return readFileSync(`/proc/${pid}/stat`, 'utf8').split(' ')[2] === 'Z';
} catch {
return false;
}
}
test('parses options before or after the workspace and rejects unsupported flags', () => {
assert.equal(parseOpenCodeArgs(['--web']).port, 4096);
assert.equal(parseOpenCodeArgs([]).port, 0);
assert.equal(parseOpenCodeArgs(['--port', '4100', '--web']).port, 4100);
assert.deepEqual(parseOpenCodeArgs(['--web', 'fix-flaky', '--new', '--port', '4100']), {
name: 'fix-flaky',
web: true,
fresh: true,
port: 4100,
});
for (const args of [
['--port'],
['--port', '0'],
['--port', '65536'],
['--model', 'test'],
['a/b'],
['one', 'two'],
]) {
assert.throws(() => parseOpenCodeArgs(args));
}
});
test(
'attaches the local TUI to the selected workspace and closes the whole tunnel',
{ timeout: 15000 },
async (t) => {
const f = fixture(t);
const result = await f.start(['fix-flaky']).done;
assert.equal(result.code, 0, result.output);
const calls = f.calls();
assert.ok(calls.find((call) => call.event === 'bootstrap').hasSource);
assert.ok(
calls
.filter((call) => call.command === 'gh' && call.args[1] === 'ssh')
.every((call) => call.args[3] === 'dev-box'),
);
const tunnel = calls.find((call) => call.command === 'gh' && call.args.includes('-N'));
assert.match(tunnel.args.at(-1), /^127\.0\.0\.1:\d+:127\.0\.0\.1:4242$/);
const client = calls.find((call) => call.command === 'opencode');
assert.deepEqual(client.args.slice(2), [
'--dir',
'/workspaces/wt-fix-flaky',
'--session',
'ses_saved',
]);
assert.equal(client.password, secret);
assert.ok(!result.output.includes(secret));
assert.ok(calls.some((call) => call.event === 'tunnel-stopped'));
const pid = calls.find((call) => call.event === 'ssh-child').pid;
await waitFor(() => stopped(pid));
},
);
test('starts a new conversation for --new instead of continuing', { timeout: 15000 }, async (t) => {
const f = fixture(t);
const result = await f.start(['fix-flaky', '--new']).done;
assert.equal(result.code, 0, result.output);
const client = f.calls().find((call) => call.command === 'opencode');
assert.deepEqual(client.args.slice(2), ['--dir', '/workspaces/wt-fix-flaky']);
});
for (const signal of ['SIGINT', 'SIGHUP']) {
test(
`opens the newest web conversation and cleans up on ${signal}`,
{ timeout: 15000 },
async (t) => {
const f = fixture(t, { TEST_NO_CLIENT: '1' });
const run = f.start(['--web', 'fix-flaky']);
const browser = await waitFor(() => f.calls().find((call) => call.command === 'browser'));
const url = new URL(browser.args[0]);
assert.equal(
url.pathname,
`/${Buffer.from('/workspaces/wt-fix-flaky').toString('base64url')}/session/ses_saved`,
);
assert.equal(url.password, '');
const response = await fetch(`${url.origin}/global/health`);
assert.equal(response.status, 200);
await response.text();
run.child.kill(signal);
const result = await run.done;
assert.equal(result.code, 0, result.output);
assert.ok(f.calls().some((call) => call.event === 'tunnel-stopped'));
await assert.rejects(fetch(url.origin));
},
);
}
for (const [label, env, message] of [
[
'a version mismatch',
{ TEST_SERVER_VERSION: '1.14.22' },
/versions differ.*npm install -g opencode-ai@1\.14\.22/,
],
['an authentication failure', { TEST_UNAUTHORIZED: '1' }, /health check failed \(401\)/],
['a tunnel startup failure', { TEST_TUNNEL_FAIL: '1' }, /SSH tunnel closed/],
['a remote preparation failure', { TEST_BOOTSTRAP_FAIL: '1' }, /Could not prepare/],
['a missing local client', { TEST_NO_CLIENT: '1' }, /Install OpenCode locally/],
['a connection loss', { TEST_CLIENT_WAIT: '1', TEST_TUNNEL_DROP: '1' }, /SSH connection closed/],
]) {
test(`reports ${label} without printing credentials`, { timeout: 15000 }, async (t) => {
const f = fixture(t, env);
const result = await f.start([]).done;
assert.equal(result.code, 1, result.output);
assert.match(result.output, message);
assert.ok(!result.output.includes(secret));
if (label !== 'a connection loss')
assert.ok(!f.calls().some((call) => call.command === 'opencode'));
});
}

View File

@ -0,0 +1,147 @@
import assert from 'node:assert/strict';
import { createServer, request } from 'node:http';
import { connect } from 'node:net';
import { test } from 'node:test';
import { openCodeProxy } from '../../scripts/cloud-session-opencode-proxy.mjs';
test('proxies uploads and event streams with authentication and rejects other origins', async (t) => {
const requests = [];
const backend = createServer(async (req, res) => {
requests.push(req.headers);
res.writeHead(200, { 'content-type': 'text/event-stream' });
for await (const chunk of req) res.write(chunk);
res.end();
});
await new Promise((resolve) => backend.listen(0, '127.0.0.1', resolve));
t.after(() => {
backend.closeAllConnections();
backend.close();
});
const proxy = await openCodeProxy({ password: 'test-secret' });
proxy.targetPort = backend.address().port;
t.after(proxy.close);
const response = await fetch(`${proxy.origin}/session`, {
method: 'POST',
body: 'data: attached image\n\n',
headers: { origin: proxy.origin },
});
assert.equal(response.status, 200);
assert.equal(await response.text(), 'data: attached image\n\n');
assert.equal(
requests[0].authorization,
`Basic ${Buffer.from('opencode:test-secret').toString('base64')}`,
);
assert.ok(!proxy.origin.includes('test-secret'));
for (const headers of [
{ origin: 'https://example.com' },
{ host: 'example.com' },
{ 'sec-fetch-site': 'cross-site' },
]) {
const status = await new Promise((resolve, reject) => {
const req = request(proxy.origin, { headers }, (response) => {
response.resume();
resolve(response.statusCode);
});
req.on('error', reject);
req.end();
});
assert.equal(status, 403);
}
assert.equal(requests.length, 1);
});
test('forwards WebSocket upgrades and closes their sockets', async (t) => {
const peers = new Set();
const backend = createServer();
backend.on('upgrade', (req, socket) => {
assert.equal(
req.headers.authorization,
`Basic ${Buffer.from('opencode:test-secret').toString('base64')}`,
);
peers.add(socket);
socket.on('close', () => peers.delete(socket));
socket.write(
'HTTP/1.1 101 Switching Protocols\r\nConnection: Upgrade\r\nUpgrade: websocket\r\n\r\n',
);
socket.pipe(socket);
});
await new Promise((resolve) => backend.listen(0, '127.0.0.1', resolve));
t.after(() => {
for (const peer of peers) peer.destroy();
backend.close();
});
const proxy = await openCodeProxy({ password: 'test-secret' });
proxy.targetPort = backend.address().port;
t.after(proxy.close);
const socket = await new Promise((resolve, reject) => {
const req = request(`${proxy.origin}/pty/test/connect`, {
headers: { origin: proxy.origin, connection: 'Upgrade', upgrade: 'websocket' },
});
req.on('upgrade', (_response, peer) => resolve(peer));
req.on('error', reject);
req.end();
});
t.after(() => socket.destroy());
const echoed = new Promise((resolve) => socket.once('data', (data) => resolve(data.toString())));
socket.write('terminal input');
assert.equal(await echoed, 'terminal input');
const closed = new Promise((resolve) => socket.once('close', resolve));
proxy.close();
await closed;
});
test(
'closes the browser response when the upstream stream disconnects',
{ timeout: 5000 },
async (t) => {
const backend = createServer((_req, res) => {
res.writeHead(200, { 'content-type': 'text/event-stream' });
res.write('data: partial\n\n');
setTimeout(() => res.destroy(), 50);
});
await new Promise((resolve) => backend.listen(0, '127.0.0.1', resolve));
t.after(() => {
backend.closeAllConnections();
backend.close();
});
const proxy = await openCodeProxy({ password: 'test' });
proxy.targetPort = backend.address().port;
t.after(proxy.close);
const signal = AbortSignal.timeout(3000);
const response = await fetch(proxy.origin, { signal });
await assert.rejects(response.text());
assert.equal(
signal.aborted,
false,
'The proxy must close the response before the client times out.',
);
},
);
test('rejects an occupied browser port and reports a missing or closed upstream', async (t) => {
const proxy = await openCodeProxy({ password: 'test' });
t.after(proxy.close);
await assert.rejects(openCodeProxy({ password: 'test', port: +new URL(proxy.origin).port }), {
code: 'EADDRINUSE',
});
const waiting = await fetch(proxy.origin);
assert.equal(waiting.status, 503);
await waiting.text();
proxy.targetPort = 1;
const response = await fetch(proxy.origin);
assert.equal(response.status, 502);
await response.text();
const rejection = await new Promise((resolve) => {
const socket = connect(+new URL(proxy.origin).port, '127.0.0.1', () => {
socket.write(
'GET /pty/test/connect HTTP/1.1\r\nHost: example.com\r\nConnection: Upgrade\r\nUpgrade: websocket\r\n\r\n',
);
});
socket.once('data', (data) => {
socket.destroy();
resolve(data.toString());
});
});
assert.match(rejection, /403 Forbidden/);
});

View File

@ -0,0 +1,228 @@
#!/usr/bin/env node
// The laptop sends this file over SSH as one standalone module. Do not import sibling files.
// It also works in an older Codespace checkout.
import { execFileSync, spawnSync } from 'node:child_process';
import { randomBytes } from 'node:crypto';
import { existsSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from 'node:fs';
import { createServer } from 'node:net';
import { join } from 'node:path';
import { setTimeout as delay } from 'node:timers/promises';
const quote = (value) => `'${value.replaceAll("'", "'\\''")}'`;
function readJson(file) {
try {
return JSON.parse(readFileSync(file, 'utf8'));
} catch (error) {
if (error.code === 'ENOENT') return undefined;
throw new Error(`Cannot read ${file}. Restore or remove this state file, then retry.`);
}
}
function saveJson(file, value) {
writeFileSync(`${file}.tmp`, JSON.stringify(value), { mode: 0o600 });
renameSync(`${file}.tmp`, file);
}
function run(command, args, options = {}) {
const result = spawnSync(command, args, {
stdio: ['ignore', 'inherit', 'inherit'],
...options,
});
if (result.error || result.status !== 0) {
throw new Error(`${command} failed. Check the output above, then retry.`);
}
}
async function freePort() {
const server = createServer();
await new Promise((resolve, reject) => {
server.once('error', reject);
server.listen(0, '127.0.0.1', resolve);
});
const { port } = server.address();
await new Promise((resolve) => server.close(resolve));
return port;
}
async function request(server, path, directory, options = {}, timeout = 60_000) {
return await fetch(`http://127.0.0.1:${server.port}${path}`, {
...options,
headers: {
authorization: `Basic ${Buffer.from(`opencode:${server.password}`).toString('base64')}`,
'x-opencode-directory': encodeURIComponent(directory),
'content-type': 'application/json',
},
signal: AbortSignal.timeout(timeout),
});
}
async function health(server, directory) {
try {
const response = await request(server, '/global/health', directory, {}, 3000);
if (!response.ok) return undefined;
const result = await response.json();
return result.healthy === true && typeof result.version === 'string' ? result : undefined;
} catch {
return undefined;
}
}
const serverFile = (stateDir) => join(stateDir, 'server.json');
async function runningServer({ stateDir, mainDirectory }) {
const previous = readJson(serverFile(stateDir));
return previous && (await health(previous, mainDirectory)) ? previous : undefined;
}
async function startServer({ stateDir, mainDirectory, workspaces }) {
const tmuxSession = 'n8n-opencode-server';
if (spawnSync('tmux', ['has-session', '-t', `=${tmuxSession}`]).status === 0) {
throw new Error(
`OpenCode is still starting or is unhealthy. Check ${stateDir}/server.log, then retry. The running server was not stopped.`,
);
}
const server = { port: await freePort(), password: randomBytes(32).toString('hex') };
const launcher = join(stateDir, 'serve.sh');
// Read secrets when the server starts. Do not store provider keys in the launcher.
writeFileSync(
launcher,
[
'#!/bin/bash',
'. /usr/local/lib/codespaces-env.sh 2>/dev/null || true',
'unset AGENT_WORKER_TOKEN N8N_DEQUEUE_URL SLACK_BOT_TOKEN',
`export TURBO_CACHE_DIR=${quote(join(workspaces, '.turbo-cache'))}`,
`[ -d "$TURBO_CACHE_DIR" ] || cp -r ${quote(join(mainDirectory, '.turbo/cache'))} "$TURBO_CACHE_DIR" 2>/dev/null || mkdir -p "$TURBO_CACHE_DIR"`,
`export OPENCODE_SERVER_USERNAME=opencode OPENCODE_SERVER_PASSWORD=${quote(server.password)}`,
'export OPENCODE_EXPERIMENTAL_CODE_MODE=true',
`export OPENCODE_CONFIG_CONTENT=${quote(
JSON.stringify({
enabled_providers: ['openrouter'],
provider: { openrouter: { options: { apiKey: '{env:OPENROUTER_API_KEY}' } } },
}),
)}`,
// Interactive sessions use the harness sandbox runtime without a profile.
'export N8N_AGENT_RUNTIME=sandbox; unset N8N_AGENT_PROFILE',
`cd ${quote(mainDirectory)} || exit 1`,
`exec opencode serve --hostname 127.0.0.1 --port ${server.port} >> ${quote(join(stateDir, 'server.log'))} 2>&1`,
'',
].join('\n'),
{ mode: 0o600 },
);
saveJson(serverFile(stateDir), server);
run('tmux', ['new-session', '-d', '-s', tmuxSession, `bash ${quote(launcher)}`]);
console.error('Starting the OpenCode server…');
for (let attempt = 0; attempt < 60; attempt++) {
if (await health(server, mainDirectory)) return server;
await delay(500);
}
throw new Error(`OpenCode did not become ready. Check ${stateDir}/server.log.`);
}
function prepareWorkspace({ name, directory, mainDirectory, stateDir }) {
if (!existsSync(directory)) {
console.error(`Creating worktree ${directory}…`);
const branch = `session/${name}`;
const exists =
spawnSync('git', [
'-C',
mainDirectory,
'show-ref',
'--verify',
'--quiet',
`refs/heads/${branch}`,
]).status === 0;
if (!exists)
spawnSync('git', ['-C', mainDirectory, 'fetch', 'origin', 'master'], {
stdio: ['ignore', 2, 2],
});
run(
'git',
[
'-C',
mainDirectory,
'worktree',
'add',
...(exists ? [] : ['--no-track', '-b', branch]),
directory,
exists ? branch : 'origin/master',
],
{ stdio: ['ignore', 2, 2] },
);
}
// A failed install must be retried even if it left a node_modules directory.
const installed = join(stateDir, `${name}.installed`);
const installing = join(stateDir, `${name}.installing`);
if (
!existsSync(join(directory, 'node_modules')) ||
existsSync(installing) ||
(name !== 'agent' && !existsSync(installed))
) {
console.error(`Installing dependencies in ${directory}…`);
writeFileSync(installing, '', { mode: 0o600 });
run('pnpm', ['install'], { cwd: directory, stdio: ['ignore', 2, 2] });
writeFileSync(installed, '', { mode: 0o600 });
rmSync(installing);
}
}
// OpenCode's own resume is scoped to the repository project, so all worktrees
// share one pool. Scope the selection to the workspace directory instead, so
// each worktree resumes its own most recently updated root conversation.
async function resolveSession({ name, fresh, directory, server }) {
if (!fresh) {
const query = new URLSearchParams({ directory, roots: 'true' });
const response = await request(server, `/session?${query}`, directory);
if (!response.ok) throw new Error(`Cannot list OpenCode sessions (${response.status}).`);
const sessions = await response.json();
const latest = Array.isArray(sessions)
? sessions.find((entry) => typeof entry?.id === 'string' && entry.id.length > 0)
: undefined;
if (latest) return latest.id;
}
const response = await request(server, '/session', directory, {
method: 'POST',
body: JSON.stringify({ title: `n8n: ${name}` }),
});
if (!response.ok) throw new Error(`Cannot create OpenCode session (${response.status}).`);
const session = await response.json();
if (typeof session.id !== 'string' || session.directory !== directory)
throw new Error('OpenCode returned an invalid session.');
return session.id;
}
export async function prepareOpenCode({
name = 'agent',
fresh = false,
web = false,
workspaces = '/workspaces',
} = {}) {
const stateDir = join(workspaces, '.n8n-opencode');
const mainDirectory = join(workspaces, 'n8n');
if (!/^\w[\w-]*$/.test(name)) throw new Error('Invalid OpenCode workspace name.');
mkdirSync(stateDir, { recursive: true, mode: 0o700 });
const directory = name === 'agent' ? mainDirectory : join(workspaces, `wt-${name}`);
let server = await runningServer({ stateDir, mainDirectory });
// On a cold start, fail before the worktree install when OpenCode is missing on the Codespace.
if (!server) execFileSync('opencode', ['--version'], { stdio: ['ignore', 'pipe', 'inherit'] });
prepareWorkspace({ name, directory, mainDirectory, stateDir });
server ??= await startServer({ stateDir, mainDirectory, workspaces });
const state = { ...server, directory };
// A fresh TUI conversation needs no record. The TUI creates it on the first message.
if (fresh && !web) return state;
// Only the parent process reads stdout. Never send this record to terminal output.
return { ...state, sessionID: await resolveSession({ name, fresh, directory, server }) };
}
if (process.argv[1] === '-') {
try {
const [name, fresh, web] = process.argv.slice(2);
console.log(
JSON.stringify(await prepareOpenCode({ name, fresh: fresh === 'true', web: web === 'true' })),
);
} catch (error) {
console.error(error.message);
process.exitCode = 1;
}
}

View File

@ -0,0 +1,275 @@
import assert from 'node:assert/strict';
import { mkdirSync, readFileSync, rmSync, statSync, writeFileSync } from 'node:fs';
import { join } from 'node:path';
import { test } from 'node:test';
import { setTimeout as delay } from 'node:timers/promises';
import { fakeBinaries } from './fake-bin.mjs';
import { prepareOpenCode } from './opencode-server.mjs';
// An orphaned fixture process can linger as a zombie where PID 1 does not reap
// it, and kill(pid, 0) still succeeds for one. Read the process state where the
// kernel exposes it; without /proc only the signal error can tell.
function stopped(pid) {
try {
process.kill(pid, 0);
} catch {
return true;
}
try {
return readFileSync(`/proc/${pid}/stat`, 'utf8').split(' ')[2] === 'Z';
} catch {
return false;
}
}
function fixture(t) {
const fake = fakeBinaries('opencode-server-');
const dir = fake.root;
mkdirSync(join(dir, 'n8n', 'node_modules'), { recursive: true });
const savedEnv = { ...process.env };
// prepareOpenCode spawns the shims in this process. Log every call before the shim body runs.
Object.assign(process.env, fake.env, {
AGENT_WORKER_TOKEN: 'test-worker',
N8N_DEQUEUE_URL: 'test-queue',
SLACK_BOT_TOKEN: 'test-slack',
});
const bin = (name, body) =>
fake.bin(name, `log({ command: ${JSON.stringify(name)}, args });\n${body}`);
bin(
'git',
`
if (args.includes('show-ref')) process.exit(fs.existsSync(file('branch-exists')) ? 0 : 1);
if (args.includes('fetch')) process.exit(0);
fs.mkdirSync(args.at(-2), { recursive: true });
`,
);
bin(
'pnpm',
`
fs.mkdirSync(path.join(process.cwd(), 'node_modules'), {recursive:true});
if (process.env.TEST_INSTALL_FAIL) process.exit(1);
`,
);
bin(
'tmux',
`
if (args[0] === 'has-session') {
const pid = +fs.readFileSync(file('pid'), 'utf8');
let gone = false;
try { process.kill(pid, 0); } catch { gone = true; }
if (!gone) {
try { gone = fs.readFileSync('/proc/' + pid + '/stat', 'utf8').split(' ')[2] === 'Z'; } catch {}
}
if (gone) process.exit(1);
} else {
const child = require('node:child_process').spawn('bash', ['-c', args.at(-1)], { detached:true, stdio:'ignore' });
fs.writeFileSync(file('pid'), String(child.pid)); child.unref();
}
`,
);
bin(
'opencode',
`
if (args[0] === '--version') { console.log('1.2.3'); process.exit(0); }
fs.writeFileSync(file('server-env.json'), JSON.stringify({
worker: !!process.env.AGENT_WORKER_TOKEN, queue: !!process.env.N8N_DEQUEUE_URL, slack: !!process.env.SLACK_BOT_TOKEN,
cache: process.env.TURBO_CACHE_DIR, config: JSON.parse(process.env.OPENCODE_CONFIG_CONTENT),
runtime: process.env.N8N_AGENT_RUNTIME, profile: process.env.N8N_AGENT_PROFILE ?? null,
}));
let sessions = fs.existsSync(file('sessions.json')) ? JSON.parse(fs.readFileSync(file('sessions.json'), 'utf8')) : {};
const server = require('node:http').createServer(async (req, res) => {
res.setHeader('content-type', 'application/json');
const expected = 'Basic ' + Buffer.from('opencode:' + process.env.OPENCODE_SERVER_PASSWORD).toString('base64');
if (req.headers.authorization !== expected) { res.writeHead(401).end('{}'); return; }
const directory = decodeURIComponent(req.headers['x-opencode-directory']);
if (req.url === '/global/health') { res.end(JSON.stringify({ healthy:true, version:'1.2.3' })); return; }
if (req.method === 'POST' && req.url === '/session') {
if (fs.existsSync(file('reject-create'))) { res.writeHead(503).end('{}'); return; }
let body = '';
for await (const chunk of req) body += chunk;
const id = 'ses_' + (Object.keys(sessions).length + 1);
sessions[id] = { id, directory, title: JSON.parse(body).title };
fs.writeFileSync(file('sessions.json'), JSON.stringify(sessions));
res.end(JSON.stringify(sessions[id])); return;
}
if (fs.existsSync(file('reject-session'))) { res.writeHead(503).end('{}'); return; }
const url = new URL(req.url, 'http://localhost');
if (url.pathname === '/session') {
// Newest first, like the real server. Filter on the query, not the header,
// so the test fails when the launcher omits either parameter. The
// child-session file models a subagent conversation that the roots query
// flag must exclude.
const directory = url.searchParams.get('directory');
const roots = url.searchParams.get('roots') === 'true';
const stored = Object.values(sessions);
if (fs.existsSync(file('child-session')))
stored.push({ id: 'ses_child', directory, title: 'child', parentID: 'ses_1' });
const list = stored
.filter(s => s.directory === directory && (!roots || !s.parentID))
.reverse();
if (fs.existsSync(file('extra-session'))) list.unshift({ id: 'ses_extra', directory, title: 'TUI session' });
res.end(JSON.stringify(list)); return;
}
const session = sessions[req.url.split('/').at(-1)];
if (!session) { res.writeHead(404).end('{}'); return; }
res.end(JSON.stringify(session));
});
server.listen(+args[args.indexOf('--port') + 1], '127.0.0.1');
`,
);
const stop = async () => {
try {
const pid = +readFileSync(join(dir, 'pid'), 'utf8');
process.kill(-pid, 'SIGTERM');
for (let count = 0; count < 100; count++) {
if (stopped(pid)) return;
await delay(20);
}
throw new Error('Fixture server did not stop.');
} catch (error) {
if (!['ENOENT', 'ESRCH'].includes(error.code)) throw error;
}
};
t.after(async () => {
await stop();
for (const key of Object.keys(process.env)) if (!(key in savedEnv)) delete process.env[key];
Object.assign(process.env, savedEnv);
rmSync(dir, { recursive: true, force: true });
});
return {
dir,
stop,
prepare: (options = {}) => prepareOpenCode({ workspaces: dir, ...options }),
commands: fake.calls,
};
}
test(
'reuses the server, serves each workspace its newest conversation, and resumes after restart',
{ timeout: 15000 },
async (t) => {
const f = fixture(t);
const first = await f.prepare({ name: 'fix-flaky' });
assert.equal(first.directory, join(f.dir, 'wt-fix-flaky'));
const git = f.commands().filter((entry) => entry.command === 'git');
assert.deepEqual(
git.map((entry) => entry.args.slice(2)),
[
['show-ref', '--verify', '--quiet', 'refs/heads/session/fix-flaky'],
['fetch', 'origin', 'master'],
[
'worktree',
'add',
'--no-track',
'-b',
'session/fix-flaky',
first.directory,
'origin/master',
],
],
);
assert.equal(first.sessionID, 'ses_1');
assert.deepEqual(await f.prepare({ name: 'fix-flaky' }), first);
const second = await f.prepare({ name: 'another-task' });
assert.equal(second.port, first.port);
assert.equal(second.sessionID, 'ses_2');
const webbed = await f.prepare({ name: 'fix-flaky', web: true });
assert.equal(webbed.sessionID, 'ses_1');
const created = JSON.parse(readFileSync(join(f.dir, 'sessions.json'), 'utf8'));
assert.equal(created.ses_1.title, 'n8n: fix-flaky');
assert.equal(created.ses_1.directory, join(f.dir, 'wt-fix-flaky'));
assert.equal((await f.prepare({ name: 'fix-flaky', web: true })).sessionID, 'ses_1');
assert.equal((await f.prepare({ name: 'another-task', web: true })).sessionID, 'ses_2');
writeFileSync(join(f.dir, 'extra-session'), '');
assert.equal((await f.prepare({ name: 'fix-flaky', web: true })).sessionID, 'ses_extra');
rmSync(join(f.dir, 'extra-session'));
writeFileSync(join(f.dir, 'child-session'), '');
assert.equal((await f.prepare({ name: 'fix-flaky', web: true })).sessionID, 'ses_1');
rmSync(join(f.dir, 'child-session'));
const fresh = await f.prepare({ name: 'fix-flaky', web: true, fresh: true });
assert.notEqual(fresh.sessionID, 'ses_1');
assert.equal((await f.prepare({ name: 'fix-flaky', web: true })).sessionID, fresh.sessionID);
const env = JSON.parse(readFileSync(join(f.dir, 'server-env.json'), 'utf8'));
assert.deepEqual([env.worker, env.queue, env.slack], [false, false, false]);
assert.equal(env.cache, join(f.dir, '.turbo-cache'));
assert.equal(env.config.provider.openrouter.options.apiKey, '{env:OPENROUTER_API_KEY}');
assert.deepEqual(env.config.enabled_providers, ['openrouter']);
assert.deepEqual([env.runtime, env.profile], ['sandbox', null]);
assert.equal(statSync(join(f.dir, '.n8n-opencode')).mode & 0o777, 0o700);
for (const file of ['serve.sh', 'server.json']) {
assert.equal(statSync(join(f.dir, '.n8n-opencode', file)).mode & 0o777, 0o600);
}
assert.ok(!f.commands().some((entry) => JSON.stringify(entry.args).includes(first.password)));
assert.equal(f.commands().filter((entry) => entry.command === 'pnpm').length, 2);
await f.stop();
const restarted = await f.prepare({ name: 'fix-flaky', web: true });
assert.equal(restarted.sessionID, fresh.sessionID);
assert.notEqual(restarted.password, first.password);
rmSync(first.directory, { recursive: true });
writeFileSync(join(f.dir, 'branch-exists'), '');
assert.equal((await f.prepare({ name: 'fix-flaky' })).directory, join(f.dir, 'wt-fix-flaky'));
const worktree = f
.commands()
.filter((entry) => entry.command === 'git' && entry.args.includes('add'))
.at(-1);
assert.deepEqual(worktree.args, [
'-C',
join(f.dir, 'n8n'),
'worktree',
'add',
first.directory,
'session/fix-flaky',
]);
},
);
test(
'retries a failed worktree install before starting the server',
{ timeout: 10000 },
async (t) => {
const f = fixture(t);
process.env.TEST_INSTALL_FAIL = '1';
await assert.rejects(f.prepare({ name: 'retry' }), /pnpm failed/);
assert.ok(!f.commands().some((entry) => entry.command === 'tmux'));
delete process.env.TEST_INSTALL_FAIL;
assert.equal((await f.prepare({ name: 'retry' })).directory, join(f.dir, 'wt-retry'));
assert.equal(f.commands().filter((entry) => entry.command === 'pnpm').length, 2);
},
);
test(
'retries a failed main checkout install even when node_modules exists',
{ timeout: 10000 },
async (t) => {
const f = fixture(t);
rmSync(join(f.dir, 'n8n', 'node_modules'), { recursive: true });
process.env.TEST_INSTALL_FAIL = '1';
await assert.rejects(f.prepare(), /pnpm failed/);
delete process.env.TEST_INSTALL_FAIL;
assert.equal((await f.prepare()).directory, join(f.dir, 'n8n'));
assert.equal(f.commands().filter((entry) => entry.command === 'pnpm').length, 2);
},
);
test(
'reports session API errors and creates conversations only when none exist',
{ timeout: 10000 },
async (t) => {
const f = fixture(t);
const first = await f.prepare({ web: true });
assert.equal(first.sessionID, 'ses_1');
writeFileSync(join(f.dir, 'reject-session'), '');
await assert.rejects(f.prepare({ web: true }), /Cannot list OpenCode sessions \(503\)/);
assert.ok(JSON.parse(readFileSync(join(f.dir, 'sessions.json'), 'utf8')).ses_1);
rmSync(join(f.dir, 'reject-session'));
assert.equal((await f.prepare({ web: true })).sessionID, first.sessionID);
writeFileSync(join(f.dir, 'reject-create'), '');
await assert.rejects(
f.prepare({ name: 'fresh-task', web: true }),
/Cannot create OpenCode session \(503\)/,
);
rmSync(join(f.dir, 'reject-create'));
assert.equal((await f.prepare({ name: 'fresh-task', web: true })).sessionID, 'ses_2');
},
);

View File

@ -1,9 +1,11 @@
#!/usr/bin/env node
// Runs on each codespace start. Installs the skills marketplace, starts the worker.
// Runs on each Codespace start. Installs the skills and harness, then starts the worker.
import { execFileSync } from 'node:child_process';
import { rmSync, writeFileSync } from 'node:fs';
import { readFileSync, rmSync, writeFileSync } from 'node:fs';
import { homedir } from 'node:os';
import { join } from 'node:path';
import { installAgentHarness } from '../../scripts/agent-harness.mjs';
import { codespaceSecret } from '../../scripts/codespace-env.mjs';
import { MARKETPLACE, PLUGINS } from './plugins.mjs';
const STATUS_FILE = '/tmp/post-start-status.json';
@ -48,6 +50,42 @@ function addMarketplace() {
return add('marketplace add (retry)');
}
// Claude Code reads the Flaky token when it connects. An env reference such as
// ${FLAKY_MCP_TOKEN} fails in processes that did not load the secrets file, for
// example the desktop app's SSH server. The helper reads the file on each connect,
// so it also gets a rotated token. The token is not copied into ~/.claude.json.
const FLAKY_HEADERS_HELPER =
'. /usr/local/lib/codespaces-env.sh; printf %s "{\\"Authorization\\":\\"Bearer $FLAKY_MCP_TOKEN\\"}"';
function registerFlakyMcp() {
// Forks have no repository secrets.
const url = codespaceSecret('FLAKY_MCP_URL');
if (!url || !codespaceSecret('FLAKY_MCP_TOKEN')) return;
let current;
try {
current = JSON.parse(readFileSync(join(homedir(), '.claude.json'), 'utf8')).mcpServers?.flaky;
} catch {
// No readable config yet: register the server below.
}
if (current?.url === url && current.headersHelper === FLAKY_HEADERS_HELPER && !current.headers) {
return;
}
if (current) tryRun('flaky mcp remove', 'claude', ['mcp', 'remove', '--scope', 'user', 'flaky']);
const config = { type: 'http', url, headersHelper: FLAKY_HEADERS_HELPER };
tryRun('flaky mcp add', 'claude', [
'mcp',
'add-json',
'--scope',
'user',
'flaky',
JSON.stringify(config),
]);
}
registerFlakyMcp();
tryRun('skills repo reachable', 'git', ['ls-remote', `https://github.com/${MARKETPLACE}`, 'HEAD']);
const installed = [];
@ -61,19 +99,33 @@ if (addMarketplace()) {
failed.push(...PLUGINS);
}
writeFileSync(STATUS_FILE, JSON.stringify({ installed, failed }, null, 2));
// A skipped install is otherwise invisible until someone misses a skill mid-session.
if (failed.length > 0) {
console.error(`\n!! SKILLS NOT INSTALLED: ${failed.join(', ')}`);
console.error('!! Sessions start without them. Retry with:');
console.error('!! node /workspaces/n8n/.devcontainer/codespaces/post-start.mjs\n');
}
let harness;
try {
const result = installAgentHarness();
harness = { status: 'active', version: result.version, cacheHit: result.cacheHit };
} catch (error) {
harness = { status: 'unavailable', error: error.message };
console.error(`agent harness: ${error.message}`);
}
tryRun('worker start', 'tmux', [
'new-session',
'-d',
'-s',
'agent-worker',
'bash -lc "export CLAUDE_CODE_PLUGIN_PREFER_HTTPS=1 CLAUDE_CODE_PLUGIN_KEEP_MARKETPLACE_ON_FAILURE=1; node /workspaces/n8n/.devcontainer/codespaces/agent-worker.mjs >> /tmp/agent-worker.log 2>&1"',
]);
const workerStarted =
harness.status === 'active' &&
tryRun('worker start', 'tmux', [
'new-session',
'-d',
'-s',
'agent-worker',
'bash -lc ". /usr/local/lib/codespaces-env.sh; export CLAUDE_CODE_PLUGIN_PREFER_HTTPS=1 CLAUDE_CODE_PLUGIN_KEEP_MARKETPLACE_ON_FAILURE=1; node /workspaces/n8n/.devcontainer/codespaces/agent-worker.mjs >> /tmp/agent-worker.log 2>&1"',
]);
if (!workerStarted && harness.status !== 'active') {
console.error('worker start: skipped because the pinned agent harness is unavailable');
}
writeFileSync(STATUS_FILE, JSON.stringify({ installed, failed, harness, workerStarted }, null, 2));

View File

@ -0,0 +1,32 @@
import assert from 'node:assert/strict';
import { test } from 'node:test';
import { aliasHostConfig, withInclude } from '../../scripts/cloud-session-ssh-config.mjs';
const GH_CONFIG = `Host cs.ominous-doodle.master
User node
ProxyCommand gh cs ssh -c ominous-doodle --stdio
UserKnownHostsFile=/dev/null
`;
test('replaces the gh host name with the fixed alias', () => {
assert.equal(
aliasHostConfig(GH_CONFIG),
GH_CONFIG.replace('cs.ominous-doodle.master', 'n8n-codespace'),
);
});
test('rejects gh output without exactly one proxied host', () => {
assert.throws(() => aliasHostConfig(''));
assert.throws(() => aliasHostConfig(`${GH_CONFIG}Host other\n`));
assert.throws(() => aliasHostConfig('Host cs.x\n\tUser node\n'));
});
test('adds the Include line at the top once', () => {
const include = 'Include ~/.ssh/n8n-codespace.conf';
assert.equal(withInclude(''), `${include}\n`);
const updated = withInclude('Host github.com\n\tUser git\n');
assert.equal(updated, `${include}\n\nHost github.com\n\tUser git\n`);
assert.equal(withInclude(updated), undefined);
});

View File

@ -0,0 +1,32 @@
ARG NODE_VERSION=24
# Debian to match the agent image's toolchain. No Playwright, no agent CLIs and no
# Docker here: a preview only builds the repo and serves it.
FROM node:${NODE_VERSION}-bookworm
# tmux holds the backend between `preview refresh` runs. The rest is for triage.
RUN apt-get update && apt-get install -y --no-install-recommends \
tmux sudo less jq \
&& rm -rf /var/lib/apt/lists/*
# Shared with the agent config, so there is one copy of each shim. The build context
# is .devcontainer/, set in devcontainer.json, which is why these paths start with
# codespaces/. git and gh must survive Codespaces' token rotation; the stock helpers
# read a snapshot of $GITHUB_TOKEN and go stale in ssh and tmux shells.
COPY codespaces/codespaces-env.sh /usr/local/lib/codespaces-env.sh
COPY codespaces/gitcredential-refresh.sh /usr/local/bin/gitcredential-refresh.sh
COPY codespaces/gh-shim.sh /usr/local/bin/gh
RUN chmod +x /usr/local/bin/gitcredential-refresh.sh /usr/local/bin/gh
# Interactive shells for a human debugging the box.
RUN echo '. /usr/local/lib/codespaces-env.sh' >> /etc/bash.bashrc
RUN echo node ALL=\(root\) NOPASSWD:ALL > /etc/sudoers.d/node && chmod 0440 /etc/sudoers.d/node
RUN mkdir -p /workspaces && chown node:node /workspaces
RUN corepack enable
ENV PNPM_HOME=/home/node/.local/share/pnpm
ENV PATH=$PNPM_HOME/bin:$PATH
USER node
RUN mkdir -p $PNPM_HOME ~/.pnpm-store && pnpm config set store-dir ~/.pnpm-store --global

View File

@ -0,0 +1,54 @@
{
"name": "n8n (PR preview)",
// No compose: a preview needs no Postgres sidecar. n8n defaults to sqlite
// (packages/@n8n/config/src/configs/database.config.ts), which is what a
// throwaway instance wants. Context is .devcontainer/ so the Dockerfile can
// reuse the token-rotation shims in codespaces/ instead of copying them.
"build": {
"dockerfile": "Dockerfile",
"context": ".."
},
// Codespaces clones the repo into /workspaces/<repo>
"workspaceFolder": "/workspaces/n8n",
// The floor, so the 2-core machine stays available. This config exists mainly to
// ask for less than the agent one: the measured footprint is ~15gb, the
// editor-ui build peaks near 3gb, and scripts/agent-setup.mjs already builds the
// whole repo inside 6gb.
"hostRequirements": {
"cpus": 2,
"memory": "8gb",
"storage": "32gb"
},
"features": {
"ghcr.io/devcontainers/features/github-cli:1": {},
// gh codespace ssh needs an SSH server inside the container
"ghcr.io/devcontainers/features/sshd:1": {}
},
"forwardPorts": [5678],
// Runs in prebuilds, so a fresh preview starts with deps installed and the build
// warm. Concurrency and heap are capped for the 2-core box, matching the
// reasoning in scripts/agent-setup.mjs. A failing onCreateCommand is fatal —
// Codespaces discards the container — so the build only ever tails its log.
"onCreateCommand": "corepack install && pnpm install && (NODE_OPTIONS=--max-old-space-size=6144 pnpm exec turbo run build --concurrency=2 > /tmp/build.log 2>&1 || tail -n 50 /tmp/build.log)",
// Codespaces regenerates the managed /etc/gitconfig on each start, and the ssh
// command shells `pnpm preview` uses are neither login nor interactive, so
// profile.d and bashrc never fire for them. Register the helper here instead.
"postStartCommand": "git config --global --get-all credential.helper 2>/dev/null | grep -qs gitcredential-refresh || git config --global --add credential.helper /usr/local/bin/gitcredential-refresh.sh",
// Declarations only. All of these are set at the repository level, so Codespaces
// injects them into every preview box whether or not they are listed here.
// scripts/codespace-preview/preview-serve.mjs reads them with codespaceSecret().
"secrets": {
"N8N_LICENSE_ACTIVATION_KEY": {
"description": "Sandbox licence key. The preview:enterprise label decides whether it reaches n8n."
},
"CODESPACE_ENV_URL": {
"description": "n8n webhook that returns extra environment for a preview, as a flat JSON object"
},
"CODESPACE_ENV_USER": {
"description": "Basic auth user for CODESPACE_ENV_URL. Defaults to `preview`."
},
"CODESPACE_ENV_PASSWORD": {
"description": "Basic auth password for CODESPACE_ENV_URL"
}
}
}

View File

@ -0,0 +1,81 @@
/**
* One-click sign-in for preview instances. Loaded through EXTERNAL_HOOK_FILES by
* scripts/codespace-preview/preview-serve.mjs, so it is never part of a packaged n8n.
*
* The route performs an ordinary login on the visitor's behalf: it calls
* /rest/login with the preview instance's own seeded credentials and forwards the
* session cookie that endpoint returns. The visitor gets exactly the session a
* normal sign-in would give them, so this is a convenience over the login form
* rather than a different way in.
*
* Only enable this on a throwaway instance whose port is org-visible. It refuses to
* register unless N8N_PREVIEW_SIGNIN is exactly "1", so loading the file by
* accident does nothing.
*
* Calling /rest/login rather than AuthService keeps this working across changes to
* n8n's internals, and means it can never grant more than a real login would.
*
* Two things make the route reachable:
* - `n8n.ready` fires after configure(), so the SPA catch-all is already
* registered. N8N_ADDITIONAL_NON_UI_ROUTES must list this path, or
* historyApiHandler answers with index.html before the route is reached.
* - the handler is registered on server.app, which AbstractServer exposes.
*/
const ROUTE = '/preview-signin';
module.exports = {
n8n: {
ready: [
async function previewSignin(server) {
if (process.env.N8N_PREVIEW_SIGNIN !== '1') return;
const email = process.env.PREVIEW_OWNER_EMAIL;
const password = process.env.PREVIEW_OWNER_PASSWORD;
if (!email || !password) {
console.warn(
`[preview-signin] PREVIEW_OWNER_EMAIL or PREVIEW_OWNER_PASSWORD is unset — ${ROUTE} not registered`,
);
return;
}
const port = process.env.N8N_PORT ?? '5678';
server.app.get(ROUTE, async (_req, res) => {
const fallback = `Sign in as ${email} instead.`;
try {
const login = await fetch(`http://127.0.0.1:${port}/rest/login`, {
method: 'POST',
headers: { 'content-type': 'application/json' },
body: JSON.stringify({ emailOrLdapLoginId: email, password }),
signal: AbortSignal.timeout(15_000),
});
if (!login.ok) {
res
.status(502)
.type('text')
.send(`Preview sign-in failed: /rest/login returned ${login.status}. ${fallback}`);
return;
}
// Hand the browser exactly the cookies the login endpoint issued.
const cookies = login.headers.getSetCookie();
if (cookies.length === 0) {
res
.status(502)
.type('text')
.send(`Preview sign-in failed: no session cookie was issued. ${fallback}`);
return;
}
res.setHeader('set-cookie', cookies);
res.redirect('/');
} catch (error) {
res.status(502).type('text').send(`Preview sign-in failed: ${error.message}. ${fallback}`);
}
});
console.log(`[preview-signin] ${ROUTE} is live — it signs the visitor in as ${email}`);
},
],
},
};

View File

@ -26,7 +26,7 @@
!docker/images/runners
!docker/images/runners/n8n-task-runners.json
# === benchmark image (packages/@n8n/benchmark/Dockerfile) ===
# === benchmark image (packages/quality/efficiency/scale/benchmark/Dockerfile) ===
!package.json
!pnpm-lock.yaml
!pnpm-workspace.yaml
@ -34,8 +34,10 @@
!patches/**
!scripts
!scripts/**
!packages/@n8n/benchmark
!packages/@n8n/benchmark/**
!packages/quality
!packages/quality/efficiency
!packages/quality/efficiency/scale
!packages/quality/efficiency/scale/benchmark
!packages/quality/efficiency/scale/benchmark/**
!packages/@n8n/typescript-config
!packages/@n8n/typescript-config/**

View File

@ -15,11 +15,6 @@ N8N_LOG_LEVEL=debug
N8N_AI_ENABLED=true
N8N_ENABLED_MODULES=instance-ai
# Uncomment when calibrating MCP grouping cases (--build-via-mcp): canvas-group
# support in the MCP tools is PostHog-gated and PostHog is unreachable locally
# (fails closed), so the env var is the only lever. Mirrors test-evals-mcp.yml.
# N8N_MCP_CANVAS_GROUPS_ENABLED=true
# Model key for the builder and the eval helper (mock generation, verifier,
# user-proxy, expectation judge). When the instance runs without the proxy, the
# model client reads ANTHROPIC_API_KEY directly, so export that too if you go

1
.gitattributes vendored
View File

@ -1,2 +1,3 @@
*.sh text eol=lf
*.patch text eol=lf
docs/generated/**/* linguist-generated

36
.githooks/check-pnpm.mjs Normal file
View File

@ -0,0 +1,36 @@
#!/usr/bin/env node
// Git hook helper (post-merge, post-rewrite, post-checkout): warn when the
// installed pnpm major differs from the one pinned in package.json.
// Usage: node .githooks/check-pnpm.mjs [checkout-type]
// For post-checkout, pass lefthook's {3}: "0" is a file checkout, which we skip.
import { spawnSync } from 'node:child_process';
import { readFileSync } from 'node:fs';
import { homedir } from 'node:os';
import { fileURLToPath } from 'node:url';
if (process.argv[2] === '0') process.exit(0);
const packageJsonPath = fileURLToPath(new URL('../package.json', import.meta.url));
const { packageManager = '' } = JSON.parse(readFileSync(packageJsonPath, 'utf8'));
const wanted = /^pnpm@(\d+)/.exec(packageManager)?.[1];
if (!wanted) process.exit(0);
// Run from the home dir so pnpm reports the installed version,
// not the version pinned by this repo.
// `shell` lets Windows resolve pnpm.cmd. The command has no user input.
const result = spawnSync('pnpm --version', { cwd: homedir(), shell: true, encoding: 'utf8' });
const current = result.status === 0 ? result.stdout.trim().split('.')[0] : '';
if (wanted === current) process.exit(0);
const installCommand =
process.platform === 'win32'
? 'powershell -c "irm https://get.pnpm.io/install.ps1 | iex"'
: 'curl -fsSL https://get.pnpm.io/install.sh | sh -';
console.log(`
⚠ This repo now pins pnpm ${wanted} (you have ${current || 'none'}).
pnpm should auto-download it on the next install command.
If that fails (some pnpm 10 versions can't install v12), reinstall:
${installCommand}
`);

View File

@ -19,6 +19,39 @@ All telemetry uses the same format:
}
```
Container startup payloads also include:
```json
{
"attempt_id": "uuid",
"correlation": {
"profile": "sqlite",
"shard": "3",
"worker": "2",
"retry": 1,
"restartReason": null
},
"stages": [
{
"name": "n8n-startup",
"source": "local",
"startedAt": "2026-03-16T12:00:00.000Z",
"elapsedMs": 4200,
"outcome": "failure",
"errorMessage": "readiness failed"
}
],
"failure_phase": "n8n-startup"
}
```
`attempt_id` identifies one stack startup. Join all stage records and attached
Playwright startup evidence with this value. `outcome` is `success`, `failure`,
or `cancelled`. Stage durations are also sent as `stack-startup-stage` metrics
with `attempt_id`, `stage`, `source`, and `outcome` dimensions. `profile` is
resolved from the configured profile or stack shape. Other unavailable
correlation values stay `null`.
## Standard Context Fields
```typescript
@ -48,10 +81,12 @@ return 'blacksmith';
| Telemetry | Source | Metrics |
|-----------|--------|---------|
| Playwright perf/benchmark | `packages/testing/playwright/reporters/metrics-reporter.ts` | Any metric attached via `attachMetric()` |
| Playwright perf/benchmark | `packages/quality/testing/playwright/reporters/metrics-reporter.ts` | Any metric attached via `attachMetric()` |
| Accessibility buckets | `packages/quality/testing/playwright/reporters/a11y-reporter.ts` | Per-bucket axe score, violated rules, violating elements |
| Build stats | `.github/scripts/send-build-stats.mjs` | Per-package build duration, cache hit/miss, run total |
| Docker stats | `.github/scripts/send-docker-stats.mjs` | Image size per platform, docker build duration |
| Container stack | `packages/testing/containers/telemetry.ts` | E2E stack startup times per service |
| Dive image efficiency | `packages/quality/efficiency/images/image-efficiency.spec.ts` | Layer content size, estimated waste, efficiency score, and layer count through the shared Playwright reporter |
| Container stack | `packages/quality/environments/containers/telemetry.ts` | E2E stack startup times per service |
## Secrets

2
.github/CLAUDE.md vendored
View File

@ -13,7 +13,7 @@ This folder contains n8n's GitHub Actions infrastructure.
| `workflows/` | GitHub Actions workflows |
| `actions/` | Reusable composite actions |
| `scripts/` | Release & Docker automation |
| `CODEOWNERS` | Team review ownership |
| `scripts/owners/` | Team review ownership scripts; the OWNERS file is at the repo root (`required` entries gate merges) |
### Workflow Naming

7
.github/CODEOWNERS vendored
View File

@ -1,7 +0,0 @@
packages/@n8n/db/src/migrations/ @n8n-io/migrations-review
.github/workflows @n8n-io/qa-dx
.github/scripts @n8n-io/qa-dx
.github/actions @n8n-io/qa-dx
.github/poutine-rules @n8n-io/qa-dx
docker/get-n8n.sh @n8n-io/qa-dx
.code-health-baseline.json @n8n-io/qa-dx

View File

@ -124,6 +124,9 @@ of the breaking removal on `3.x`.
## How the daily sync works
The sync runs automation code from the triggering `master` SHA while its working checkout
stays on `3.x`.
[`util-sync-master-to-3x.yml`](./workflows/util-sync-master-to-3x.yml) runs daily and
**replays the `3.x`-only commits on top of `master`** (a rebase), then force-pushes `3.x`:
@ -133,19 +136,19 @@ of the breaking removal on `3.x`.
original message and author. Nothing is ever squashed. Merge commits in the range (breaking
PRs merged into `3.x`) are flattened away, and a breaking commit that also landed on
`master` is dropped as empty.
3. Conflicts confined to **mechanical files** — tool-generated content with a deterministic
resolution (`pnpm-lock.yaml`, `packages/frontend/editor-ui/data/node-popularity.json`,
3. Conflicts confined to **non-lockfile mechanical files** — bot-maintained content with a
deterministic resolution (`packages/frontend/editor-ui/data/node-popularity.json`,
`packages/@n8n/instance-ai/src/tools/nodes/credential-setupability.json`, and
`.github/test-metrics/e2e-impact-map.json`) — are **auto-resolved during the replay**,
exactly as a human resolver would: the lockfile is regenerated with
`pnpm install --lockfile-only` (pnpm merges its own conflict markers), bot-maintained data
files take `master`'s side. The resolution is folded into the stalled commit, so this
still adds **no commit and no PR**. The list lives in `MECHANICAL_PATHS` in
by taking `master`'s side. The resolution is folded into the stalled commit, so this still
adds **no commit and no PR**. The list lives in `MECHANICAL_PATHS` in
[`sync-master-to-3x.mjs`](./scripts/sync-master-to-3x.mjs).
4. On a **real code conflict**, `3.x` is left **untouched** and a **draft conflict PR**
(labeled `automation:v3-sync`) carrying the conflict markers is opened on
`sync/master-to-3x` — with the mechanical files already pre-resolved — plus a post to the
**`#alerts-v3-sync`** Slack channel. **Syncs pause until that PR is merged** — so
conflicts never pile up silently.
4. On a **real code conflict or any `pnpm-lock.yaml` conflict**, `3.x` is left **untouched**
and a **draft conflict PR** (labeled `automation:v3-sync`) carrying the conflict markers is
opened on `sync/master-to-3x`. Other mechanical files are already pre-resolved. The
lockfile stays unresolved because pnpm validation is not reliable while the merge index is
unresolved. The workflow also posts to the **`#alerts-v3-sync`** Slack channel. **Syncs
pause until that PR is merged**, so conflicts never pile up silently.
Whatever route it takes, the sync verifies that the content it is about to push is **exactly
the tree a merge of `3.x` and `master` produces** (`git merge-tree`), and that no conflict
@ -164,8 +167,8 @@ rewriting `3.x`.
The conflict branch is `master` merged into `3.x` with the **conflict markers committed**, so
you see exactly what clashed — and the required checks stay red until they're gone, so the PR
can't be merged half-resolved. Mechanical files arrive **pre-resolved** (listed in the PR
under "Auto-resolved for you"), so only the real code conflicts need you:
can't be merged half-resolved. Non-lockfile mechanical files arrive **pre-resolved** (listed
in the PR under "Auto-resolved for you"), so only the code and lockfile conflicts need you:
```bash
git fetch origin sync/master-to-3x && git switch sync/master-to-3x
@ -173,9 +176,8 @@ git fetch origin sync/master-to-3x && git switch sync/master-to-3x
git push origin sync/master-to-3x
```
If the PR says the lockfile was **deferred** (a `package.json` / `pnpm-workspace.yaml` is
conflicted too), resolve the manifests first, then regenerate it with
`pnpm install --lockfile-only` and include the result in your fix commit.
If the PR says the lockfile was **deferred**, resolve the other conflicts first. Then
regenerate it with `pnpm install --lockfile-only` and include the result in your fix commit.
Watch for the **"Deleted on one side, changed on the other"** section. Git leaves no markers
for a delete/modify, so the branch looks clean where it is not: the merge keeps `3.x`'s side

234
.github/OWNERS vendored
View File

@ -1,234 +0,0 @@
# n8n CODEOWNERS
#
# Last-match-wins: specific rules MUST come AFTER general rules.
# Default catch-all (ensures every file gets at least one reviewer)
* @n8n-io/catalysts
# Catalysts
packages/core/ @n8n-io/catalysts
packages/workflow/ @n8n-io/catalysts
packages/workflow/styker.config.mjs @n8n-io/qa-dx
packages/@n8n/config/ @n8n-io/catalysts
packages/@n8n/backend-common/ @n8n-io/catalysts
packages/@n8n/backend-network/ @n8n-io/catalysts
packages/@n8n/backend-test-utils/ @n8n-io/catalysts
packages/@n8n/di/ @n8n-io/catalysts
packages/@n8n/errors/ @n8n-io/catalysts
packages/@n8n/constants/ @n8n-io/catalysts
packages/@n8n/utils/ @n8n-io/catalysts
packages/@n8n/api-types/ @n8n-io/catalysts
packages/@n8n/workflow-sdk/ @n8n-io/ai-assistant
packages/@n8n/task-runner/ @n8n-io/catalysts
packages/@n8n/task-runner-python/ @n8n-io/catalysts
packages/@n8n/expression-runtime/ @n8n-io/catalysts
packages/@n8n/db/ @n8n-io/catalysts
packages/@n8n/json-schema-to-zod/ @n8n-io/catalysts
packages/@n8n/crdt/ @n8n-io/catalysts
packages/@n8n/extension-sdk/ @n8n-io/catalysts
packages/@n8n/eslint-config/ @n8n-io/qa-dx
packages/@n8n/typescript-config/ @n8n-io/qa-dx
packages/@n8n/db/src/migrations/ @n8n-io/migrations-review
# Top-level paths
scripts/ @n8n-io/qa-dx
patches/ @n8n-io/qa-dx
assets/ @n8n-io/adore
security/ @n8n-io/qa-dx
# @n8n/cli
packages/@n8n/cli/ @n8n-io/adore
packages/@n8n/cli/src/commands/credential/ @n8n-io/iam
packages/@n8n/cli/src/commands/user/ @n8n-io/iam
packages/@n8n/cli/src/commands/data-table/ @n8n-io/adore
packages/@n8n/cli/src/commands/tag/ @n8n-io/adore
packages/@n8n/cli/src/commands/project/ @n8n-io/ligo
packages/@n8n/cli/src/commands/source-control/ @n8n-io/ligo
packages/@n8n/cli/src/commands/variable/ @n8n-io/ligo
packages/@n8n/cli/src/commands/skill/ @n8n-io/ai
# packages/cli
packages/cli/ @n8n-io/catalysts
packages/cli/src/scaling/ @n8n-io/catalysts
packages/cli/src/concurrency/ @n8n-io/catalysts
packages/cli/src/execution-lifecycle/ @n8n-io/catalysts
packages/cli/src/executions/ @n8n-io/catalysts
packages/cli/src/task-runners/ @n8n-io/catalysts
packages/cli/src/webhooks/ @n8n-io/catalysts
packages/cli/src/push/ @n8n-io/catalysts
packages/cli/src/commands/ @n8n-io/catalysts
packages/cli/src/config/ @n8n-io/catalysts
packages/cli/src/eventbus/ @n8n-io/catalysts
packages/cli/src/events/ @n8n-io/catalysts
packages/cli/src/security-audit/ @n8n-io/catalysts
packages/cli/src/modules/workflow-index/ @n8n-io/catalysts
packages/cli/src/modules/breaking-changes/ @n8n-io/catalysts
packages/cli/src/modules/otel/ @n8n-io/ligo
packages/cli/src/auth/ @n8n-io/iam
packages/cli/src/credentials/ @n8n-io/iam
packages/cli/src/mfa/ @n8n-io/iam
packages/cli/src/oauth/ @n8n-io/iam
packages/cli/src/permissions.ee/ @n8n-io/iam
packages/cli/src/sso.ee/ @n8n-io/iam
packages/cli/src/user-management/ @n8n-io/iam
packages/cli/src/license/ @n8n-io/iam
packages/cli/src/modules/ldap.ee/ @n8n-io/iam
packages/cli/src/modules/log-streaming.ee/ @n8n-io/iam
packages/cli/src/modules/sso-oidc/ @n8n-io/iam
packages/cli/src/modules/sso-saml/ @n8n-io/iam
packages/cli/src/modules/provisioning.ee/ @n8n-io/iam
packages/cli/src/modules/dynamic-credentials.ee/ @n8n-io/iam
packages/cli/src/modules/redaction/ @n8n-io/iam
packages/cli/src/modules/instance-registry/ @n8n-io/iam
packages/cli/src/modules/token-exchange/ @n8n-io/iam
packages/cli/src/environments.ee/ @n8n-io/ligo
packages/cli/src/public-api/ @n8n-io/ligo
packages/cli/src/modules/source-control.ee/ @n8n-io/ligo
packages/cli/src/modules/external-secrets.ee/ @n8n-io/ligo
packages/cli/src/modules/insights/ @n8n-io/ligo
packages/cli/src/collaboration/ @n8n-io/catalysts
packages/cli/src/binary-data/ @n8n-io/catalysts
packages/cli/src/posthog/ @n8n-io/adore
packages/cli/src/modules/data-table/ @n8n-io/adore
packages/cli/src/evaluation.ee/ @n8n-io/ai
packages/cli/src/chat/ @n8n-io/ai
packages/cli/src/tool-generation/ @n8n-io/ai
packages/cli/src/modules/workflow-builder/ @n8n-io/ai
packages/cli/src/modules/mcp/ @n8n-io/adore
packages/cli/src/modules/quick-connect/ @n8n-io/ai
packages/cli/src/modules/chat-hub/ @n8n-io/ai
packages/cli/src/modules/instance-ai/ @n8n-io/ai-assistant
packages/cli/src/modules/community-packages/ @n8n-io/nodes
# CLI controllers
packages/cli/src/controllers/auth.controller.ts @n8n-io/iam
packages/cli/src/controllers/invitation.controller.ts @n8n-io/iam
packages/cli/src/controllers/me.controller.ts @n8n-io/iam
packages/cli/src/controllers/mfa.controller.ts @n8n-io/iam
packages/cli/src/controllers/owner.controller.ts @n8n-io/iam
packages/cli/src/controllers/password-reset.controller.ts @n8n-io/iam
packages/cli/src/controllers/role.controller.ts @n8n-io/iam
packages/cli/src/controllers/users.controller.ts @n8n-io/iam
packages/cli/src/controllers/user-settings.controller.ts @n8n-io/iam
packages/cli/src/controllers/api-keys.controller.ts @n8n-io/iam
packages/cli/src/controllers/security-settings.controller.ts @n8n-io/iam
packages/cli/src/controllers/oauth/ @n8n-io/iam
packages/cli/src/controllers/ai.controller.ts @n8n-io/ai
packages/cli/src/controllers/annotation-tags.controller.ee.ts @n8n-io/ai
packages/cli/src/controllers/cta.controller.ts @n8n-io/adore
packages/cli/src/controllers/folder.controller.ts @n8n-io/adore
packages/cli/src/controllers/tags.controller.ts @n8n-io/adore
packages/cli/src/controllers/binary-data.controller.ts @n8n-io/adore
packages/cli/src/controllers/dynamic-templates.controller.ts @n8n-io/adore
packages/cli/src/controllers/posthog.controller.ts @n8n-io/adore
packages/cli/src/controllers/translation.controller.ts @n8n-io/adore
packages/cli/src/controllers/project.controller.ts @n8n-io/ligo
packages/cli/src/controllers/workflow-statistics.controller.ts @n8n-io/ligo
packages/cli/src/controllers/node-types.controller.ts @n8n-io/nodes
packages/cli/src/controllers/dynamic-node-parameters.controller.ts @n8n-io/nodes
packages/cli/src/controllers/e2e.controller.ts @n8n-io/qa-dx
# CLI services
packages/cli/src/services/jwt.service.ts @n8n-io/iam
packages/cli/src/services/user.service.ts @n8n-io/iam
packages/cli/src/services/role.service.ts @n8n-io/iam
packages/cli/src/services/role-cache.service.ts @n8n-io/iam
packages/cli/src/services/password.utility.ts @n8n-io/iam
packages/cli/src/services/public-api-key.service.ts @n8n-io/iam
packages/cli/src/services/security-settings.service.ts @n8n-io/iam
packages/cli/src/services/ssrf/ @n8n-io/catalysts
packages/cli/src/services/static-auth-service.ts @n8n-io/iam
packages/cli/src/services/access.service.ts @n8n-io/iam
packages/cli/src/services/ai.service.ts @n8n-io/ai
packages/cli/src/services/ai-usage.service.ts @n8n-io/ai
packages/cli/src/services/ai-workflow-builder.service.ts @n8n-io/ai
packages/cli/src/services/annotation-tag.service.ee.ts @n8n-io/ai
packages/cli/src/services/folder.service.ts @n8n-io/adore
packages/cli/src/services/tag.service.ts @n8n-io/adore
packages/cli/src/services/cta.service.ts @n8n-io/adore
packages/cli/src/services/dynamic-templates.service.ts @n8n-io/adore
packages/cli/src/services/frontend.service.ts @n8n-io/adore
packages/cli/src/services/banner.service.ts @n8n-io/adore
packages/cli/src/services/project.service.ee.ts @n8n-io/ligo
packages/cli/src/services/workflow-statistics.service.ts @n8n-io/ligo
packages/cli/src/services/export.service.ts @n8n-io/ligo
packages/cli/src/services/import.service.ts @n8n-io/ligo
packages/cli/src/services/ownership.service.ts @n8n-io/ligo
packages/cli/src/services/dynamic-node-parameters.service.ts @n8n-io/nodes
# Adore
packages/frontend/editor-ui/ @n8n-io/frontend
packages/frontend/editor-ui/src/features/ai/ @n8n-io/ai
packages/frontend/editor-ui/src/features/credentials/ @n8n-io/iam
packages/frontend/editor-ui/src/features/execution/ @n8n-io/ligo
packages/frontend/editor-ui/src/features/project-roles/ @n8n-io/iam
packages/frontend/editor-ui/src/features/integrations/ @n8n-io/ligo
packages/frontend/@n8n/design-system/ @n8n-io/design
packages/frontend/@n8n/stores/ @n8n-io/frontend
packages/frontend/@n8n/composables/ @n8n-io/frontend
packages/frontend/@n8n/rest-api-client/ @n8n-io/frontend
packages/frontend/@n8n/storybook/ @n8n-io/design
packages/frontend/@n8n/i18n/ @n8n-io/frontend
packages/@n8n/stylelint-config/ @n8n-io/qa-dx
# AI
packages/@n8n/instance-ai/ @n8n-io/ai-assistant
packages/@n8n/nodes-langchain/ @n8n-io/ai
packages/@n8n/ai-utilities/ @n8n-io/ai
packages/@n8n/ai-node-sdk/ @n8n-io/ai
packages/@n8n/ai-workflow-builder.ee/ @n8n-io/ai
packages/@n8n/agents/ @n8n-io/ai
packages/frontend/@n8n/chat/ @n8n-io/ai
# Chat
packages/@n8n/chat-hub/ @n8n-io/ai
# Nodes
packages/@n8n/codemirror-lang/ @n8n-io/nodes
packages/@n8n/codemirror-lang-html/ @n8n-io/nodes
packages/@n8n/codemirror-lang-sql/ @n8n-io/nodes
packages/nodes-base/ @n8n-io/nodes
packages/@n8n/decorators/ @n8n-io/catalysts
packages/node-dev/ @n8n-io/nodes
packages/@n8n/create-node/ @n8n-io/nodes
packages/@n8n/node-cli/ @n8n-io/nodes
packages/@n8n/imap/ @n8n-io/iam
packages/@n8n/syslog-client/ @n8n-io/iam
packages/@n8n/scan-community-package/ @n8n-io/nodes
packages/@n8n/eslint-plugin-community-nodes/ @n8n-io/nodes
packages/@n8n/computer-use/ @n8n-io/nodes
packages/@n8n/local-gateway/ @n8n-io/nodes
packages/@n8n/mcp-browser/ @n8n-io/nodes
packages/@n8n/mcp-browser-extension/ @n8n-io/nodes
# IAM
packages/@n8n/permissions/ @n8n-io/iam
packages/@n8n/client-oauth2/ @n8n-io/iam
# LiGo
packages/extensions/insights/ @n8n-io/ligo
# CI/CD
.github/ @n8n-io/qa-dx
docker/ @n8n-io/qa-dx
# QA
packages/testing/ @n8n-io/qa-dx
packages/@n8n/benchmark/ @n8n-io/qa-dx
packages/@n8n/vitest-config/ @n8n-io/qa-dx

448
.github/WORKFLOWS.md vendored
View File

@ -10,7 +10,6 @@ Complete reference for n8n's `.github/` folder.
.github/
├── WORKFLOWS.md # This document
├── CI-TELEMETRY.md # Telemetry & metrics guide
├── CODEOWNERS # Team ownership for PR reviews
├── pull_request_template.md # PR description template
├── pull_request_title_conventions.md # Title format rules (Angular)
├── actionlint.yml # Workflow linter config
@ -21,6 +20,7 @@ Complete reference for n8n's `.github/` folder.
│ ├── config.yml # Routes to community/security
│ └── 01-bug.yml # Structured bug report form
├── scripts/ # Automation scripts
│ ├── owners/ # Owners scripts (the OWNERS file lives at the repo root)
│ ├── bump-versions.mjs # Calculate next version
│ ├── update-changelog.mjs # Generate CHANGELOG
│ ├── trim-fe-packageJson.js # Strip frontend devDeps
@ -177,7 +177,7 @@ These only run if specific files changed:
| `docker/images/n8n-base/Dockerfile` | `build-base-image.yml` | any |
| `**/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/quality/efficiency/scale/benchmark/**` | `build-benchmark-image.yml` | master |
| `packages/cli/src/public-api/**/*.yml`, `packages/cli/src/public-api/**/*.yaml`, `packages/cli/src/public-api/**/*.css`, `packages/cli/src/public-api/v1/openapi-gen/**/*.ts`, `packages/cli/scripts/build.mjs`, `packages/cli/package.json` | `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`, `packages/@n8n/agents/src/**` | `ci-instance-ai-evals.yml` | on PR `opened` / `reopened` / `ready_for_review` |
| `docker/get-n8n.sh`, `docker/get-n8n-compose.yml`, `docker/test-get-n8n.sh` | `test-get-n8n.yml` | any |
@ -230,25 +230,190 @@ parallelism). See the `--build-via-mcp` section in
### On PR Close/Merge
| Event | Workflow |
|----------------------------|-----------------------------|
| PR closed (any) | `util-notify-pr-status.yml` |
| PR merged to `release/*` | `release-publish.yml` |
| Event | Workflow |
|------------------------------------|--------------------------------|
| PR closed (any) | `util-notify-pr-status.yml` |
| PR merged to `release/*` | `release-publish.yml` |
| PR closed with `codespace-preview` | `util-codespace-preview.yml` |
### Manual Triggers (PR Comments)
### Label Triggers
| Command | Workflow | Permissions |
|--------------------|------------------------------|---------------------|
| `/test-workflows` | `test-workflows-callable.yml`| admin/write/maintain|
| Label | Workflow | Effect |
|-----------------------|------------------------------|----------------------------------------------------|
| `codespace-preview` | `util-codespace-preview.yml` | Runs the PR in a Codespace, comments the URL |
| `preview:enterprise` | `util-codespace-preview.yml` | Re-serves the instance with an enterprise licence |
| `preview:debug` | `util-codespace-preview.yml` | Re-serves the instance with `N8N_LOG_LEVEL=debug` |
**Why:** Re-run tests without pushing commits. Useful for flaky test investigation.
**Why:** A reviewer gets a running instance of the PR without a Docker build or a
cloud deploy. The workflow calls `scripts/codespace-preview/preview.mjs`, which keeps one codespace
for each PR (display name `preview/pr-<number>`) and shares port 5678 with the
organization. A later push serves the new head in the same box. Removing the
label, or closing the PR, deletes the box.
Only a PR from a branch in this repository is eligible: a codespace token is
scoped to `n8n-io/n8n` and cannot check out a fork head.
#### Live progress on the PR
`up` and `refresh` take minutes, and an absent comment looks the same as a broken
preview. So the comment goes up before the box work starts, as a checklist of the
phases in `scripts/codespace-preview/preview-phases.mjs`, and is edited for each phase and once a
minute after that. The final URL replaces it in place.
A comment **edit sends no notification** — only a create does. That is what makes a
heartbeat on a sticky comment acceptable at all.
A `refresh` keeps the URL of the previous run in the checklist. The URL does not
change between runs, and taking a working link off the PR for several minutes is
worse than saying it is briefly down.
A cancelled job — including the 45-minute `timeout-minutes` — kills the script while
its checklist is up, so a last `if: cancelled()` step writes the outcome instead. It
writes **only over a checklist**: a run cancelled while it was queued never started
work, and must leave the previous run's URL alone.
#### Running it by hand
A box sleeps after 2 hours of no use, and GitHub makes every forwarded port
private again at each start. So a preview that slept reaches nobody until
something shares port 5678 again, and its backend is gone with the container.
**Actions → Util: Codespace Preview → Run workflow** does both without a commit
and without a label toggle:
| Input | Meaning |
|---|---|
| `pr_number` | The pull request to act on. |
| `operation` | `up` (default) creates the box if it is gone, starts it if it sleeps, serves the head and shares the port again. `refresh` re-serves the head in a box that already exists. `down` deletes the box. |
Prefer `up` unless you mean to delete: it covers create, wake and re-share.
The button needs write access, and it appears only once the trigger is on
`master` — GitHub lists dispatchable workflows from the default branch. A manual
run takes the branch picked in the dropdown, which is the branch GitHub read the
workflow from, so a branch can test a change to the preview scripts. A fork head
is still refused, by `preview.mjs` rather than by the job's `if`. There is no
`ls` operation: it needs no PR and posts no comment — run `pnpm preview ls`
locally.
#### Preview toggles
A `preview:*` label configures an instance that already exists, so adding or
removing one re-serves the box instead of creating or deleting it. It does
nothing on a PR without `codespace-preview`.
The vocabulary lives in `scripts/codespace-preview/preview-labels.mjs`, which both ends import:
`preview.mjs` turns the PR's labels into slugs, and `preview-serve.mjs` turns
those slugs into environment inside the box. Add a toggle there, in one place.
Two things cross the gap, and both are shape-checked rather than trusted: a
`preview:*` slug, and a phase key from `scripts/codespace-preview/preview-phases.mjs`. The `gh
codespace ssh` command is a shell string that appears in the box's process list, so
a value is never passed through it — `preview:enterprise` resolves to a licence key
inside the box, not on the runner.
`preview:enterprise` needs a **Codespaces** secret named
`N8N_LICENSE_ACTIVATION_KEY`, scoped to `n8n-io/n8n`. That is a Codespaces
secret, not an Actions secret, and it is unrelated to `CODESPACE_PREVIEW_TOKEN`.
Use the sandbox key: the preview sets tenant `1001` to match, and the default
tenant (`1`) rejects it. Without the secret the preview still serves, unlicensed,
and says so in the log.
Note what the label does and does not control. Codespaces injects the secret into
**every** preview box, so the label decides whether the licence reaches n8n, not
whether the key reaches the box. Anyone who can run PR-head code can read
`/workspaces/.codespaces/shared/.env-secrets`. Previews are limited to branches
in this repository, so that is the set of people who already have write access.
#### Preview environment from a webhook
A preview can also take environment from an n8n webhook we control, so a value
can change without a commit and a merge. `scripts/codespace-preview/preview-remote-env.mjs` fetches
it, and `preview-serve.mjs` hands the result to the backend.
It needs three **Codespaces** secrets on `n8n-io/n8n`, again not Actions secrets:
| Secret | Purpose |
| ------------------------ | ------------------------------------------------- |
| `CODESPACE_ENV_URL` | The webhook URL |
| `CODESPACE_ENV_USER` | Basic auth user. Optional, defaults to `preview`. |
| `CODESPACE_ENV_PASSWORD` | Basic auth password |
The webhook answers with a flat JSON object. Its keys become environment
variables and its values are used as-is, so a number or a boolean is stringified
and a nested object is dropped. The request carries the PR number and the head
SHA as query parameters, so one endpoint can answer per PR.
The fetch runs in the box, not on the runner. Codespaces secrets are unreadable
from Actions, so neither the password nor a returned value can reach a CI log.
The log prints key names only.
**Every key is passed through.** A response containing `NODE_OPTIONS`,
`EXTERNAL_HOOK_FILES` or `PATH` runs code inside the preview box, so whoever can
edit that workflow can run code there. The one exception is the preview's own
wiring — the sign-in hook and the owner credentials — which is applied last and
wins. The `.env-secrets` note above applies to these secrets too.
Nothing here is required. Without the secrets, an unreachable webhook or a
rejected password, the preview serves as usual and says so in the log. A wrong
password is not retried: it cannot fix itself.
#### The `CODESPACE_PREVIEW_TOKEN` secret
The job needs `CODESPACE_PREVIEW_TOKEN`, a **fine-grained** personal access token,
held in the `codespaces` environment. Set the resource owner to `n8n-io` and limit
repository access to `n8n-io/n8n`. Grant these repository permissions:
| Permission | Level | What it unlocks |
|---|---|---|
| Metadata | Read | Mandatory, selected for you |
| Codespaces | Read and write | Create, list and delete a box |
| Codespaces metadata | Read | `GET .../codespaces/machines`, which resolves the machine type |
| Codespaces lifecycle admin | Read and write | Start a stopped box, which is what `gh codespace ssh` does |
| Contents | Read | Read the repository |
| Pull requests | Read | `gh pr view`, to resolve the head ref and SHA |
`Codespaces metadata` is a **different permission** from `Codespaces`. Without it
the run fails with `HTTP 403: Resource not accessible by personal access token` on
the `machines` endpoint, after the org billing check has already printed a tick —
so the failure looks unrelated to permissions.
An organization owner may have to approve the token. It stays pending until then.
No other credential can do this:
- `GITHUB_TOKEN` has no Codespaces access.
- A GitHub App **installation** token cannot create a codespace at all. The
Codespaces API belongs to a user, not to an installation.
- A **classic** token is refused by org policy:
`` `n8n-io` forbids access via a personal access token (classic) ``. So no
combination of classic scopes works, whatever the API reference says about the
`codespace` scope.
The environment keeps the token away from every other workflow: only a job that
names `codespaces` can read it. **The environment must allow every branch.** A
`pull_request` run has the ref `refs/pull/<n>/merge`, which no deployment branch
policy matches, so a branch rule would block every preview. Add required
reviewers only if a click for each preview is acceptable.
The codespace belongs to whoever owns the token, and shows up in that account's
codespace list. Billing still goes to the organization, because the repository is
organization-owned and has a Codespaces budget. A service account is therefore
better than a person's account for quota attribution, though the token is scoped
to one repository either way.
The job checks out the base branch, never the PR head, so a PR cannot supply the
script that reads that token. A manual run checks out the branch chosen in the
Run-workflow dropdown, which only a user with write access can pick.
### Other Manual Workflows
| Workflow | Purpose |
|-----------------------------|---------------------------------------------------------|
| `util-codespace-preview.yml`| Wake, re-serve or delete a PR preview instance by hand |
| `util-data-tooling.yml` | SQLite/PostgreSQL export/import validation (manual) |
| `test-image-efficiency.yml` | Measure n8n and runners image size and layer waste with Dive |
| `util-probe-registry.yml` | Diagnose slow npm metadata fetches (temporary) |
| `sec-sync-bundle-branches.yml` | Sync both bundle branches on demand, including before a bundle cut |
---
@ -277,11 +442,10 @@ release-publish.yml
│ └──────────▶ security-trivy-scan-callable.yml
└──────────────────────────▶ sbom-generation-callable.yml
test-workflows-nightly.yml (manual dispatch only — nightly schedule disabled, DEVP-544)
└──────────────────────────▶ test-workflows-callable.yml
test-sbom-nightly.yml
└──────────────────────────▶ sbom-validation-callable.yml
PR Comment Dispatchers (triggered by /command in PR comments):
test-workflows-pr-comment.yml
test-workflows-nightly.yml (manual dispatch only — nightly schedule disabled, DEVP-544)
└──────────────────────────▶ test-workflows-callable.yml
```
@ -323,7 +487,10 @@ test-workflows-pr-comment.yml
│ │ ├─ ensure-provenance-fields.mjs ───▶ Add license fields │
│ │ └─ npm publish (tag: rc or latest) │
│ ├─ publish-to-docker-hub ────────▶ docker-build-push.yml │
│ │ └─ Multi-arch: amd64 + arm64 │
│ │ ├─ Build the application once on amd64 │
│ │ ├─ Build/push images on native amd64 + arm64 runners │
│ │ └─ Merge manifests, then add release provenance │
│ │ └─ Release VEX/SBOM attestations follow provenance │
│ ├─ create-github-release │
│ ├─ create-sentry-release (sourcemaps) │
│ ├─ generate-sbom ────────────────▶ sbom-generation-callable.yml │
@ -397,14 +564,15 @@ out: `npm deprecate n8n@X.Y.Z "Failed release, use X.Y.(Z+1)"`.
## ci-master.yml
Runs on push to `master` or `1.x`:
Runs on push to `master`:
```
Push to master/1.x
├─ build-github (populate cache)
├─ unit-test (matrix: Node 22.23.2, 24.18.1)
Push to master
├─ build-and-format (Blacksmith: build, then format check; populate master cache)
├─ unit-test (matrix: Node 24.18.1, 26.5.1)
│ └─ Coverage only on 24.18.1
├─ lint
├─ (performance: CodSpeed benchmarks, paused until the plugin supports Vitest 5)
├─ verify-single-instance-npm (advisory; packages changed by this push)
└─ notify-on-failure (Slack #alerts-build)
```
@ -419,18 +587,20 @@ Push to master/1.x
| Daily 03:00 | `sec-sync-bundle-branches.yml` | Merge the base into `bundle/*` |
| Daily 00:00 | `docker-build-push.yml` | Nightly Docker images |
| Daily 00:00 | `test-db.yml` | Database compatibility |
| Daily 00:00 | `test-e2e-performance-reusable.yml`| Performance E2E |
| Daily 00:00 | `test-e2e-performance-reusable.yml`| Browser performance and idle memory baselines |
| Daily 00:00 | `release-storybook.yml` | Storybook deploy |
| Daily 00:00 | `release-chromatic.yml` | Visual regression |
| Daily 00:00 | `util-check-docs-urls.yml` | Doc link validation |
| Daily 01:30, 02:30, 03:30 | `test-benchmark-nightly.yml` | Performance benchmarks |
| Daily 02:00 | `test-get-n8n.yml` | get.n8n.io installer health |
| Daily 02:00 | `test-e2e-pc-nightly.yml` | E2E on the `-pc` image |
| Daily 04:00 | `test-sbom-nightly.yml` | Release and image SBOM license validation |
| Daily 05:00 | `test-benchmark-destroy-nightly.yml`| Cleanup benchmark env |
| Daily 06:00 | `util-sync-master-to-3x.yml` | Replay 3.x onto master (v3) |
| Daily 08:00 | `build-v3-nightly.yml` | Nightly v3 Docker images |
| Monday 00:00 | `util-update-node-popularity.yml` | Node usage stats |
| Monday 02:00 | `test-e2e-coverage-weekly.yml` | Weekly E2E coverage |
| Wednesday 04:00 | `util-update-get-n8n-versions.yml` | get-n8n stable versions |
| Saturday 22:00 | `test-evals-ai.yml` | AI workflow evals |
| 1st of month 04:00 | `util-refresh-cubic-schema.yml` | Refresh vendored cubic schema |
@ -438,18 +608,21 @@ Push to master/1.x
## v3 development (master + 3.x)
The sync runs automation code from the triggering `master` SHA while its working checkout
stays on `3.x`.
During the v3 release window, `master` carries normal feature work (behind opt-in
flags) and the long-lived `3.x` branch carries breaking changes. `util-sync-master-to-3x.yml`
syncs daily by **replaying the `3.x`-only commits onto `master` and force-pushing `3.x`**, so a
clean sync adds no commit and nothing is squashed. What it pushes is always verified to be
exactly the tree a merge of `3.x` and `master` produces, and marker-free. Conflicts confined
to mechanical, tool-generated files (the pnpm lockfile, bot-maintained data files — see
`MECHANICAL_PATHS` in `sync-master-to-3x.mjs`) are auto-resolved during the replay; the tree
check then applies to every path except those files. On a real code conflict `3.x` is left
untouched and a draft PR carrying the conflict markers (labeled `automation:v3-sync`, with
mechanical files pre-resolved) is opened on `sync/master-to-3x`, naming both ends of the
conflict — the breaking-commit authors and the `master` commits that touched the same files
— via `sync-conflict-owners.mjs`, posting to `#alerts-v3-sync` and pausing further syncs
to non-lockfile mechanical files (bot-maintained data files — see `MECHANICAL_PATHS` in
`sync-master-to-3x.mjs`) are auto-resolved during the replay. On a code or `pnpm-lock.yaml`
conflict, `3.x` is left untouched and a draft PR carrying the conflict markers (labeled
`automation:v3-sync`, with other mechanical files pre-resolved) is opened on
`sync/master-to-3x`. The lockfile is always left for the resolver. The PR names both ends of
the conflict — the breaking-commit authors and the `master` commits that touched the same
files — via `sync-conflict-owners.mjs`, posts to `#alerts-v3-sync`, and pauses further syncs
until it is resolved and merged normally. Delete/modify conflicts have no markers to carry,
so they are resolved toward `3.x` and listed as an explicit decision in the PR body.
`build-v3-nightly.yml` publishes `n8nio/n8n:v3-nightly[-<date>]` images from `3.x`
@ -472,18 +645,35 @@ Composite actions in `.github/actions/`:
| Action | Purpose | Used By |
|--------------------------|----------------------------------------------|--------------------|
| `setup-nodejs` | pnpm + Node.js + Turbo cache + Docker (opt) | Most CI workflows |
| `run-workflow-script` | Run a `.github/scripts` module with no setup or install | Owners and PR quality checks |
| `docker-registry-login` | GHCR + DockerHub + DHI authentication | Docker workflows |
### setup-nodejs
```yaml
inputs:
node-version: # default: '24.18.1'
node-version: # default: '26.5.1'
enable-docker-cache: # default: 'false' (Blacksmith Buildx)
docker-cache-key: # required when enable-docker-cache is true
build-command: # default: 'pnpm build'
```
The pnpm version comes from the `packageManager` field in the root
`package.json`, through `resolve-pnpm-version.mjs`. There is no version input to
keep in sync: a bump in `package.json` moves the setup step, the cache key and
the version check together.
The action caches the pnpm executable in `~/setup-pnpm` by OS, architecture and
version. A cache miss runs `pnpm/setup` and retries once if the registry request
fails. The action verifies the version and saves the cache before the rest of
the job can fail.
Concurrent jobs can all miss a new key before the first save completes. Jobs
that start after the save use the cached executable. Windows keeps the standard
`pnpm/setup` path because its runner cannot activate the cached POSIX home path.
The existing `actions/setup-node` cache continues to store the pnpm package
store.
The Blacksmith layer cache lives on a sticky disk identified by
`docker-cache-key`, and commits are last-writer-wins. Splitting the key per
image would avoid that, but Blacksmith currently never populates a
@ -492,6 +682,30 @@ while the build reports a successful commit. Every job therefore shares the
`n8n-io/n8n` key, which is the only disk that actually retains layers. Revisit
once new-disk retention works.
### run-workflow-script
```yaml
inputs:
script: # path of the module, relative to the repository root
github-token: # token for the Octokit client, also exported as GITHUB_TOKEN
```
Runs one `.github/scripts` module through `actions/github-script`. That action
brings its own Node.js and an Octokit client, so the job needs no
`setup-nodejs` step and no dependency install. The action loads
`github-helpers.mjs`, hands the client to `setOctokit`, then imports the module
and awaits its exported `main()`.
Use it for a module that imports only node builtins and other `.github/scripts`
modules. `github-helpers.mjs` loads `@actions/github` and `semver` only when
they are present, so it works in both modes. A module that needs an npm
package (`semver`, `yaml`, `minimatch`, ...) keeps the `setup-nodejs` path with
the `.github/scripts` install command.
Pair it with a sparse checkout of `.github` when the module reads nothing else
from the tree. Cone mode always includes the root files, so `OWNERS` and
`package.json` stay available.
### docker-registry-login
```yaml
@ -525,11 +739,13 @@ Workflows with `workflow_call` trigger:
| `test-linting-reusable.yml` | `ref`, `nodeVersion` | ESLint |
| `test-e2e-reusable.yml` | `branch`, `test-mode`, `shards`, `runner` | Core E2E executor |
| `test-workflows-callable.yml` | `git_ref`, `compare_schemas` | Workflow tests |
| `docker-build-push.yml` | `n8n_version`, `release_type`, `push_enabled`, `ref`, `date_tag` | Docker build |
| `docker-build-push.yml` | `n8n_version`, `release_type`, `push_enabled`, `ref`, `date_tag`, `create_attestations` | Docker build |
| `sec-ci-reusable.yml` | `ref` | Security orchestrator |
| `sec-poutine-reusable.yml` | `ref` | Poutine scanner |
| `sec-sync-retarget-prs.yml` | none | Move bundle PRs back onto `bundle/*` |
| `security-trivy-scan-callable.yml` | `image_ref` | Trivy scan |
| `sbom-generation-callable.yml` | `n8n_version`, `release_tag_ref` | SBOM generation |
| `sbom-validation-callable.yml` | `sha` | Read-only SBOM validation |
| `test-single-instance-npm.yml` | `scope`, `base-ref`, `base-branch`, `blocking`, `timeout-minutes` | Dependency duplication |
---
@ -554,9 +770,10 @@ Scripts in `.github/scripts/`:
|-------------------------|-------------------|------------------------|
| `docker/docker-config.mjs`| Build context | `docker-build-push.yml`|
| `docker/docker-tags.mjs` | Image tags | `docker-build-push.yml`|
| `docker/kafka-native-smoke-check.mjs`| Verify librdkafka binary loads in built image | `docker-build-push.yml`|
| `docker/kafka-native-smoke-check.mjs`| Verify librdkafka binary loads in built image | `docker-build-smoke.yml`|
| `docker/assert-manifest-format.mjs`| Assert a merged manifest is an OCI image index with the expected platforms | `docker-build-push.yml`|
| `docker/should-smoke-build.mjs`| Narrow the `pnpm-workspace.yaml` smoke trigger to native dependency pins | `docker-build-smoke.yml`|
| `attest-image-sbom.mjs` | Generate, validate, and optionally attest image SBOMs | `docker-build-push.yml`, `test-sbom-nightly.yml` |
### Validation Scripts
@ -564,10 +781,28 @@ Scripts in `.github/scripts/`:
|-------------------------|-------------------|---------------------------|
| `validate-docs-links.js`| Check doc URLs | `util-check-docs-urls.yml`|
| `send-build-stats.mjs` | Build telemetry | `setup-nodejs` action |
| `resolve-pnpm-version.mjs` | Publish the pinned pnpm version and its executable cache key | `setup-nodejs` action |
| `nightly-sbom-context.mjs` | Resolve the source SHA and image tag for nightly SBOM validation | `test-sbom-nightly.yml` |
| `db-test-matrix.mjs` | DB test matrix from `postgres-versions.json` | `ci-pull-requests.yml` |
| `quality/check-cubic-config.mjs` | Validate `cubic.yaml` against the vendored cubic schema; enforce its silent agent/character limits. `--refresh` re-pulls the schema | `test-workflow-scripts-reusable.yml`, `util-refresh-cubic-schema.yml` |
| `glob.mjs` | Builtin-only glob matcher for changed-file paths (`**`, `*`, dot segments) | `quality/check-pr-size.mjs` |
| `probe-registry.mjs` | Registry path throughput probe (temporary) | `util-probe-registry.yml` |
### Preview Scripts
| Script | Purpose | Called By |
|---------------------------------|-------------------------------------------------------------------------|--------------------------------|
| `codespace-preview.mjs` | Map a `pull_request` event or a manual operation onto a preview operation, comment the result | `util-codespace-preview.yml` |
| `../../scripts/codespace-preview/preview.mjs` | One codespace for each PR: `up`, `refresh`, `down`, `ls`. `--json` for CI | `codespace-preview.mjs`, developers |
| `../../scripts/codespace-preview/preview-remote-env.mjs` | Fetch extra environment for a preview from the webhook, inside the box | `../../scripts/codespace-preview/preview-serve.mjs` |
| `../../scripts/codespace-preview/preview-phases.mjs` | The phase vocabulary and its one-line marker, so the runner, the box and the comment cannot drift | `codespace-preview.mjs`, `../../scripts/codespace-preview/preview.mjs`, `../../scripts/codespace-preview/preview-serve.mjs` |
`scripts/codespace-preview/preview.mjs` is also the developer entry point (`pnpm preview up <pr>`).
In `--json` mode stdout carries one line for each phase and then the report object,
and all human progress goes to stderr. So a workflow can follow a run that also
streams an in-box build log. The reader tells the two apart by the `url` field: the
report has one, a phase line never does.
### Branch Replay Scripts
Both keep a long-lived branch that is "base + its own commits" in sync by rebasing those
@ -578,7 +813,7 @@ rewrite safe.
|----------------------------|----------------------------------------------------------------------|------------------------------------|
| `branch-replay.mjs` | Shared primitives: merge-tree, tree guard, marker scan | the two scripts below |
| `sync-master-to-3x.mjs` | master → `3.x`, rebased; auto-resolves mechanical files, opens a conflict PR | `util-sync-master-to-3x.yml` |
| `sync-bundle-branch.mjs` | base → `bundle/*` in n8n-private, merged; fail-loud, never resolves conflicts | `sec-sync-bundle-branches.yml` |
| `sync-bundle-branch.mjs` | base → `bundle/*` in n8n-private, merged; skips while the base is unpublished; fail-loud, never resolves conflicts | `sec-sync-bundle-branches.yml` |
### Slack Scripts
@ -603,13 +838,92 @@ See **[CI-TELEMETRY.md](CI-TELEMETRY.md)** for:
---
## CODEOWNERS
## OWNERS
Team ownership mappings in `CODEOWNERS`:
Team ownership lives in the top-level `OWNERS` file. The scripts that consume
it live in `.github/scripts/owners/`. Line format:
| Path Pattern | Team |
|--------------------------------------------------------------|----------------------------|
| `packages/@n8n/db/src/migrations/` | @n8n-io/migrations-review |
```
<pattern> <@org/team> [required]
```
Patterns are a catch-all (`*`), a directory prefix (`packages/x/`), or an
exact file path. Matching is last-match-wins, so specific rules must come
after general rules.
The format is strict, enforced by `node .github/scripts/owners/owners.mjs --check`:
- Tokens on a line come in a fixed order: pattern, one team, then options
such as `required`.
- Directory patterns end with `/` and must be existing directories; all other
patterns must be existing files. Duplicate patterns are rejected.
Team existence is not checked by `--check` (it needs an org read token, which
fork PRs do not have); a separate workflow covers it (DEVP-891).
The file drives four workflows:
| Workflow | Purpose |
|-----------------------------------|--------------------------------------------------------------------------|
| `ci-owners-validation.yml` | Validates OWNERS (syntax, dead paths) via `owners.mjs --check` |
| `ci-owners-review-recommendations.yml` | Advisory PR comment: reviewer teams, line stats, required reviews |
| `ci-owners-assign-reviewers.yml` | Opt-in reviewer auto-assignment (label-triggered) |
| `ci-owners-required-reviews.yml` | Enforces `required` entries via the "Required Reviews" commit status |
### Required reviews
An entry with the `required` option makes team approval mandatory: when a PR
changes a non-test file whose winning entry carries `required`, a member of each
listed team must approve the PR. Test files match the shared patterns in
`test-files.mjs` and do not trigger required reviews.
`ci-owners-required-reviews.yml` evaluates this on
PR changes and review events, and reports a commit status
named **Required Reviews** on the head SHA. A missing approval reports
`pending` ("Waiting for approval from: …"), not `failure`, so an unreviewed PR
does not show red CI; any non-success state blocks the merge equally. The
ruleset for a branch must list that status as a required check for the block
to take effect. Merge-queue runs report success on the queue head without
re-evaluating: a PR cannot enter the queue unless the status is green on its
head, and the queue does not change approvals.
The status is evaluated for a PR into any base branch, from a same-repo head
or a fork. Both matter because the ruleset that gates a PR is not always the
one on its base branch: GitHub applies the ruleset of a stack's target branch
to every PR in the stack, so a stacked PR into a feature branch is gated by
the `master` ruleset. A required status that no run ever writes leaves the PR
blocked on "Expected". Routes that skip the evaluation are listed in
`REQUIRED_REVIEW_EXEMPTIONS` in
`required-reviews.mjs`. An entry is `<head> -> <base>` or just `<base>`
(any head); `*` matches any run of characters. An exempt PR reports
`success` with the route in the description. Only heads in this repository
can match, so a fork branch with a matching name is still evaluated. Add a
route only when every commit it carries was already reviewed elsewhere, as
with `sync/master-to-3x -> 3.x`: its commits landed on `master` first.
The workflow reads OWNERS, its scripts and the exemption routes from `master`
only, never from the base branch or the PR: any writable branch can be a base,
so only `master` is trusted input. A PR cannot lift its own review
requirement. A retarget re-evaluates the PR, so a verdict computed against the
old base does not carry over.
Members of the `large-scale-changes` GitHub team can make the **Required
Reviews** status succeed without required OWNERS team approvals. A team member
must apply the `large-scale-change` label. The gate checks the actor from the
label event and verifies their current team membership. The label alone is not
sufficient. Label changes re-evaluate the status. Removing the label removes
the exemption.
Every path that writes the status runs in the base repository context, because
a fork-context run has no secrets and a read-only token. PR changes arrive
through `pull_request_target`, which is safe here because no step checks out
or runs PR code. Review events on a same-repo PR arrive through
`pull_request_review`. Review events on a fork PR arrive through the
`workflow_run` of `ci-pull-request-review.yml`, which runs on every submitted
or dismissed review; the owners workflow looks the PR up from that run's head
and skips same-repo heads, which the direct event already covers. A first
contribution whose runs still wait for approval gets no review-event
re-evaluation until a maintainer approves the runs; `workflow_dispatch` with
the PR number is the manual fallback.
---
@ -634,7 +948,7 @@ Team ownership mappings in `CODEOWNERS`:
**`blacksmith-4vcpu-ubuntu-2204`** - Unit tests (parallelized), linting (parallel file processing), typechecking (CPU-intensive), E2E test shards
**`blacksmith-8vcpu-ubuntu-2204`** - Heavy parallel workloads
**`blacksmith-8vcpu-ubuntu-2204`** - Heavy parallel workloads. The PR `install-and-build` job uses it with the default runner provider. Most PR jobs wait for that job, and a cold build keeps all 4 vCPUs of a smaller runner busy.
### Runner Provider Toggle
@ -732,6 +1046,13 @@ Packages whose license cannot be resolved from disk go in
`scripts/licenses/license-overrides.json` with a verified `source` citation — the upstream
LICENSE file, not registry metadata.
`test-sbom-nightly.yml` runs at 04:00 UTC. It waits up to two hours for the current scheduled
Docker build to complete. It builds the production deployment closure at that run's SHA and
validates the release SBOM. It also resolves the four immutable SHA image tags from that
build and validates each image SBOM. The validation uses the same enrichment and SPDX gates
as a release. It does not publish, attest, or upload an artifact. A failure reports to the
Developer Platform Slack channel.
### SLSA L3 Provenance
SLSA (Supply-chain Levels for Software Artifacts) Level 3 provides cryptographic proof of build integrity.
@ -820,11 +1141,28 @@ open the PR there. That PR **must stay a single-parent squash** — the publish
posts to `#alerts-build` when that gate fails on a PR opened *from* `bundle/2.x` or
`bundle/1.x` (link only, no PR title, since the branch is embargoed).
`sec-sync-bundle-branches.yml` keeps those branches current, daily plus whenever a PR is
merged into one (and on `workflow_dispatch`). It **merges the base into** the bundle branch
via [`scripts/sync-bundle-branch.mjs`](scripts/sync-bundle-branch.mjs) and pushes without
forcing. Every push is verified to carry exactly the tree a merge of the two sides would
produce (`git merge-tree`); a mismatch, or a conflict marker, fails the run instead of pushing.
`sec-sync-bundle-branches.yml` keeps those branches current nightly at 03:00 UTC and on
`workflow_dispatch`, including before a bundle cut. It **merges the public base into** the
bundle branch via
[`scripts/sync-bundle-branch.mjs`](scripts/sync-bundle-branch.mjs) and pushes without forcing.
Every push is verified to carry exactly the tree a merge of the two sides would produce (`git
merge-tree`); a mismatch, or a conflict marker, fails the run instead of pushing.
**A bundle branch is only ever built on published history.** The sync fetches `master` or `1.x`
directly from `https://github.com/n8n-io/n8n.git`. It creates a missing bundle branch at that
exact public SHA and merges that public SHA into an existing bundle branch. It never reads the
private base branch. This matters because the `chore: Bundle/*` squash on private `master` is
private-only until publication. The mirror can replace that commit with the public copy after
the bundle branch is deleted. The next bundle branch must not inherit the replaced commit.
Deleting a bundle branch takes its open PRs with it: GitHub moves each one onto the deleted
branch's own base, and re-creating the branch does not move them back. The next nightly or manual
sync re-creates the branch and then calls
[`sec-sync-retarget-prs.yml`](workflows/sec-sync-retarget-prs.yml), which moves every open PR
on `master` onto `bundle/2.x` and every one on `1.x` onto `bundle/1.x`. It skips a bundle
branch that does not exist, and skips PRs whose *head* is `bundle/*` — those are the
`chore: Bundle/*` cut PRs, which target the base on purpose. It is also dispatchable on its
own. The retarget runs inside the bundle sync after a missing branch is recreated.
**`bundle/*` is append-only — never rebase it, never force-push it.** These branches receive
PRs, and rewriting a branch that receives PRs orphans the copies of its commits that the open
@ -837,21 +1175,19 @@ itself. To refresh a fix branch, use GitHub's **Update branch** button or
sibling PR's merge base untouched, which is why only a rewrite breaks this.
The costs of merging are deliberate and paid for: a merge commit per run, and fixes that have
already been published staying in the branch's log (the old rebase dropped them as empty
commits). Neither reaches anything downstream, because a bundle publishes as one squashed
commit taken from the tree rather than the history — the `chore: Bundle/*` PR's **diff** stays
already been published staying in the branch's log. Neither reaches anything downstream,
because a bundle publishes as one squashed commit taken from the tree rather than the history —
the `chore: Bundle/*` PR's **diff** stays
exactly the pending fixes even when its commit list does not. For a list of what a bundle
actually carries, read the fix PRs merged into the branch since the last cut, not
`base..bundle`. A lower cadence than the base's is fine too: a base push never re-triggered
CI on the fix PRs, so syncing more often bought them nothing.
actually carries, read the fix PRs merged into the branch since the last cut, not `base..bundle`.
There is **one job per bundle branch**. A conflict is detected from the merge tree before the
working tree is touched, so the branch is left exactly as it was, that job **fails** (no
green runs hiding a stalled branch) and `#alerts-security` gets a run link — while the other
branch still syncs. Recovery is deliberate: merge the base into the branch locally, resolve,
push, then re-run the workflow — and that resolution then lives in the merge commit instead of
being re-litigated on every later run. The sync never resolves a conflict itself, unlike
`util-sync-master-to-3x.yml`.
branch still syncs. Recovery is deliberate: merge the public base into the branch locally,
resolve, push, then re-run the workflow — and that resolution then lives in the merge commit
instead of being re-litigated on every later run. The sync never resolves a conflict itself,
unlike `util-sync-master-to-3x.yml`.
See **[`../AGENTS.md`](../AGENTS.md)** ("Security Fix Hygiene") for the naming rules that
keep the vulnerability out of public branch names, commits, and test descriptions.
@ -921,12 +1257,6 @@ Adding a new channel requires inviting the bot first; the first run otherwise fa
## Future Vision
### Redundancy Review
Comment trigger (`/test-workflows`) is a workaround.
Long-term: Main CI should be reliable enough to not need these.
### Workflow Testability
- Tools like `act` for local testing

View File

@ -21,7 +21,7 @@ runs:
steps:
- name: Check cache for existing image
id: cache-check
uses: actions/cache/restore@640a1c2554105b57832a23eea0b4672fc7a790d5 # v4.2.3
uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
key: n8n-docker-image-v2-${{ inputs.build-variant }}-${{ github.sha }}
path: /tmp/n8n-images
@ -58,7 +58,7 @@ runs:
- name: Publish image tarball to cache
if: steps.cache-check.outputs.cache-hit != 'true'
uses: actions/cache/save@640a1c2554105b57832a23eea0b4672fc7a790d5 # v4.2.3
uses: actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
key: n8n-docker-image-v2-${{ inputs.build-variant }}-${{ github.sha }}
path: /tmp/n8n-images

View File

@ -13,6 +13,7 @@ import {
getChangedFiles,
getAddedFiles,
getMergeBase,
resolveHeadRef,
} from '../ci-filter.mjs';
// --- matchGlob ---
@ -57,6 +58,15 @@ describe('matchGlob', () => {
);
});
it('backend module pattern matches packaged module files', () => {
assert.ok(
matchGlob(
'packages/modules/insights/backend/src/insights.service.ts',
'packages/modules/*/backend/**',
),
);
});
it('* matches single-level only', () => {
assert.ok(matchGlob('README.md', '*.md'));
assert.ok(!matchGlob('docs/README.md', '*.md'));
@ -241,7 +251,7 @@ describe('runtime filter', () => {
'!**/CHANGELOG.md',
'!**/*.test.ts',
'!**/*.spec.ts',
'!packages/testing/playwright/**',
'!packages/quality/testing/playwright/**',
'!packages/frontend/@n8n/storybook/**',
'!scripts/agent-setup.mjs',
'!scripts/backend-module/**',
@ -264,7 +274,7 @@ describe('runtime filter', () => {
it('does not trigger on playwright tests', () => {
assert.equal(
evaluateFilter(['packages/testing/playwright/tests/x.spec.ts'], runtimePatterns),
evaluateFilter(['packages/quality/testing/playwright/tests/x.spec.ts'], runtimePatterns),
false,
);
});
@ -395,6 +405,100 @@ describe('getChangedFiles', () => {
});
});
// --- resolveHeadRef ---
describe('resolveHeadRef', () => {
it('diffs the PR head (second parent of the test merge) on pull request events', () => {
assert.equal(resolveHeadRef('pull_request'), 'HEAD^2');
assert.equal(resolveHeadRef('pull_request_review'), 'HEAD^2');
});
it('diffs HEAD on every other event', () => {
assert.equal(resolveHeadRef('merge_group'), 'HEAD');
assert.equal(resolveHeadRef('workflow_dispatch'), 'HEAD');
assert.equal(resolveHeadRef(undefined), 'HEAD');
});
});
// --- getChangedFiles on a stacked PR's test-merge commit (shallow clone) ---
describe('getChangedFiles (stacked PR test merge)', () => {
const builderDir = mkdtempSync(join(tmpdir(), 'ci-filter-build-stack-'));
const remoteDir = mkdtempSync(join(tmpdir(), 'ci-filter-remote-stack-'));
const repoDir = mkdtempSync(join(tmpdir(), 'ci-filter-stack-'));
const originalCwd = process.cwd();
const originalStep = process.env.CI_FILTER_DEEPEN_STEP;
const git = (args: string[], cwd: string) =>
execFileSync('git', args, { cwd, stdio: 'pipe' }).toString().trim();
before(() => {
execFileSync('git', ['init', '--bare', '-b', 'main', remoteDir], { stdio: 'pipe' });
git(['init', '-b', 'main'], builderDir);
git(['config', 'user.email', 'test@test.local'], builderDir);
git(['config', 'user.name', 'test'], builderDir);
git(['remote', 'add', 'origin', remoteDir], builderDir);
writeFileSync(join(builderDir, 'shared.ts'), 'shared\n');
git(['add', '.'], builderDir);
git(['commit', '-m', 'root'], builderDir);
git(['push', 'origin', 'main'], builderDir);
// Bottom of the stack: parent PR branch off root
git(['checkout', '-b', 'parent-branch'], builderDir);
writeFileSync(join(builderDir, 'parent.ts'), 'parent\n');
git(['add', '.'], builderDir);
git(['commit', '-m', 'parent PR change'], builderDir);
git(['push', 'origin', 'parent-branch'], builderDir);
// Top of the stack: this PR branches off the parent PR
git(['checkout', '-b', 'pr-branch'], builderDir);
writeFileSync(join(builderDir, 'pr-only.ts'), 'pr\n');
git(['add', '.'], builderDir);
git(['commit', '-m', 'PR change'], builderDir);
// main drifts after the stack was created
git(['checkout', 'main'], builderDir);
writeFileSync(join(builderDir, 'shared.ts'), 'shared\ndrift\n');
git(['commit', '-am', 'main moves'], builderDir);
git(['push', 'origin', 'main'], builderDir);
// GitHub builds the parent PR's test merge against main, then builds
// this PR's test merge on top of it instead of on parent-branch.
git(['checkout', '--detach', 'main'], builderDir);
git(['merge', '--no-ff', '-m', 'Merge parent-branch into main', 'parent-branch'], builderDir);
git(['merge', '--no-ff', '-m', 'Merge pr-branch into parent merge', 'pr-branch'], builderDir);
git(['push', 'origin', 'HEAD:refs/pull/1/merge'], builderDir);
// Mirror actions/checkout: depth-1 fetch of the test-merge ref
git(['init'], repoDir);
git(['remote', 'add', 'origin', `file://${remoteDir}`], repoDir);
git(['fetch', '--depth=1', 'origin', '+refs/pull/1/merge:refs/remotes/pull/1/merge'], repoDir);
git(['checkout', '--force', 'refs/remotes/pull/1/merge'], repoDir);
process.env.CI_FILTER_DEEPEN_STEP = '1';
process.chdir(repoDir);
});
after(() => {
process.chdir(originalCwd);
if (originalStep === undefined) delete process.env.CI_FILTER_DEEPEN_STEP;
else process.env.CI_FILTER_DEEPEN_STEP = originalStep;
rmSync(builderDir, { recursive: true, force: true });
rmSync(remoteDir, { recursive: true, force: true });
rmSync(repoDir, { recursive: true, force: true });
});
it('diffing HEAD reports the base-branch drift carried by the parent test merge', () => {
assert.equal(git(['rev-parse', '--is-shallow-repository'], repoDir), 'true');
assert.deepEqual(getChangedFiles('parent-branch', 'HEAD').sort(), ['pr-only.ts', 'shared.ts']);
});
it('diffing HEAD^2 reports only the files this PR changes', () => {
assert.deepEqual(getChangedFiles('parent-branch', 'HEAD^2'), ['pr-only.ts']);
assert.deepEqual(getAddedFiles('parent-branch', 'HEAD^2'), ['pr-only.ts']);
assert.equal(getMergeBase('HEAD^2'), git(['rev-parse', 'origin/parent-branch'], repoDir));
});
});
// --- getChangedFiles deep merge-base (shallow clone, stale base) ---
describe('getChangedFiles (shallow clone, stale base)', () => {

View File

@ -34,7 +34,7 @@ outputs:
description: 'Resolved base ref used for the diff (filter mode only)'
value: ${{ steps.run.outputs.base-ref }}
merge-base:
description: 'Merge-base SHA between FETCH_HEAD and HEAD (filter mode only)'
description: 'Merge-base SHA between the base ref and the PR head (filter mode only)'
value: ${{ steps.run.outputs.merge-base }}
runs:

View File

@ -95,7 +95,19 @@ export function parseFilters(input) {
const SAFE_REF = /^[a-zA-Z0-9_./-]+$/;
export function getChangedFiles(baseRef) {
/**
* Ref to diff against the base branch.
*
* On pull_request events checkout lands on GitHub's test-merge commit. For a
* stacked PR its first parent is the parent PR's test merge, which already
* carries every base-branch commit the stack lacks, so a diff against HEAD
* reports that drift as changed. The second parent is the PR head itself.
*/
export function resolveHeadRef(eventName = process.env.GITHUB_EVENT_NAME) {
return eventName === 'pull_request' || eventName === 'pull_request_review' ? 'HEAD^2' : 'HEAD';
}
export function getChangedFiles(baseRef, headRef = 'HEAD') {
if (!SAFE_REF.test(baseRef)) {
throw new Error(`Unsafe base ref: "${baseRef}"`);
}
@ -104,8 +116,8 @@ export function getChangedFiles(baseRef) {
// direction, so files added to base-branch after the PR diverged show up as
// "changed" — spuriously triggering path-filtered jobs. The merge base
// scopes the diff to PR-only changes.
fetchUntilMergeBase(baseRef);
const output = execSync('git diff --name-only --no-renames --merge-base FETCH_HEAD HEAD', {
fetchUntilMergeBase(baseRef, headRef);
const output = execSync(`git diff --name-only --no-renames --merge-base FETCH_HEAD ${headRef}`, {
encoding: 'utf-8',
});
return output
@ -116,12 +128,12 @@ export function getChangedFiles(baseRef) {
// Files added (not merely modified) in this PR. Same merge-base scoping as
// getChangedFiles; assumes the fetch it performs has already run.
export function getAddedFiles(baseRef) {
export function getAddedFiles(baseRef, headRef = 'HEAD') {
if (!SAFE_REF.test(baseRef)) {
throw new Error(`Unsafe base ref: "${baseRef}"`);
}
const output = execSync(
'git diff --name-only --no-renames --diff-filter=A --merge-base FETCH_HEAD HEAD',
`git diff --name-only --no-renames --diff-filter=A --merge-base FETCH_HEAD ${headRef}`,
{ encoding: 'utf-8' },
);
return output
@ -131,8 +143,8 @@ export function getAddedFiles(baseRef) {
}
/**
* Deepen the shallow clone until the merge base between the fetched base ref
* (FETCH_HEAD) and HEAD is reliably reachable.
* Fetch the base ref, then deepen only if its merge base with headRef is not
* reliably reachable from the checkout.
*
* A single fixed deepen is not enough for stale PRs whose divergence point is
* older than the shallow boundary. We fetch with an exponentially growing
@ -144,19 +156,33 @@ export function getAddedFiles(baseRef) {
* git's signed int32 `--deepen` limit), switch to `--unshallow` instead of
* passing an ever-larger integer that git would reject.
*/
function fetchUntilMergeBase(baseRef) {
function fetchUntilMergeBase(baseRef, headRef) {
let step = Number(process.env.CI_FILTER_DEEPEN_STEP) || 200;
const maxDeepen = Number(process.env.CI_FILTER_MAX_DEEPEN) || 20_000;
deepenFetch(baseRef, step, maxDeepen);
// A PR merge checkout already has the base commit as its first parent.
const hasBaseCommit = headRef === 'HEAD^2' && hasCommit('HEAD^1');
const depth = isShallow() && !hasBaseCommit ? `--depth=${step} ` : '';
execSync(`git fetch --no-tags --prune --filter=blob:none ${depth}origin ${baseRef}`, {
stdio: 'pipe',
});
while (!hasReliableMergeBase()) {
while (!hasReliableMergeBase(headRef)) {
if (!isShallow()) {
throw new Error(
`No merge base between FETCH_HEAD and HEAD after fetching the full history of "${baseRef}" (unrelated histories).`,
`No merge base between FETCH_HEAD and ${headRef} after fetching the full history of "${baseRef}" (unrelated histories).`,
);
}
step *= 2;
deepenFetch(baseRef, step, maxDeepen);
step *= 2;
}
}
function hasCommit(ref) {
try {
execSync(`git cat-file -e ${ref}^{commit}`, { stdio: 'pipe' });
return true;
} catch {
return false;
}
}
@ -168,9 +194,7 @@ function deepenFetch(baseRef, step, maxDeepen) {
}
function isShallow() {
return (
execSync('git rev-parse --is-shallow-repository', { encoding: 'utf-8' }).trim() === 'true'
);
return execSync('git rev-parse --is-shallow-repository', { encoding: 'utf-8' }).trim() === 'true';
}
/**
@ -179,12 +203,12 @@ function isShallow() {
* sub-history is truncated; that result is unreliable, so we must deepen further
* before trusting it.
*/
function hasReliableMergeBase() {
function hasReliableMergeBase(headRef) {
let base;
try {
base = execSync('git merge-base FETCH_HEAD HEAD', { encoding: 'utf-8' }).trim();
base = execSync(`git merge-base FETCH_HEAD ${headRef}`, { encoding: 'utf-8' }).trim();
} catch {
return false; // no common ancestor reachable yet
return false; // no common ancestor reachable yet, or headRef still hidden by the shallow graft
}
if (!base) return false;
return !readShallowBoundaries().has(base);
@ -208,12 +232,12 @@ function readShallowBoundaries() {
}
/**
* Resolve the merge-base SHA between FETCH_HEAD and HEAD.
* Resolve the merge-base SHA between FETCH_HEAD and headRef.
* Used to give downstream tools (e.g. janitor's AST diff) a stable, PR-only
* comparison point that doesn't drift when the base branch moves forward.
*/
export function getMergeBase() {
return execSync('git merge-base FETCH_HEAD HEAD', { encoding: 'utf-8' }).trim();
export function getMergeBase(headRef = 'HEAD') {
return execSync(`git merge-base FETCH_HEAD ${headRef}`, { encoding: 'utf-8' }).trim();
}
// --- Filter evaluation ---
@ -290,10 +314,12 @@ export function runFilter() {
}
const filters = parseFilters(filtersInput);
const changedFiles = getChangedFiles(baseRef);
const addedFiles = getAddedFiles(baseRef);
const mergeBase = getMergeBase();
const headRef = resolveHeadRef();
const changedFiles = getChangedFiles(baseRef, headRef);
const addedFiles = getAddedFiles(baseRef, headRef);
const mergeBase = getMergeBase(headRef);
console.log(`Diffing ${headRef} against ${baseRef}`);
console.log(`Merge base: ${mergeBase}`);
console.log(`Changed files (${changedFiles.length}):`);
for (const f of changedFiles) {

View File

@ -28,7 +28,7 @@ runs:
steps:
- name: Restore image tarball from cache
id: restore
uses: actions/cache/restore@640a1c2554105b57832a23eea0b4672fc7a790d5 # v4.2.3
uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
key: n8n-docker-image-v2-${{ inputs.build-variant }}-${{ inputs.cache-sha || github.sha }}
path: /tmp/n8n-images

View File

@ -0,0 +1,42 @@
# Runs one `.github/scripts` module without a Node.js setup or a dependency
# install. actions/github-script brings its own Node.js and Octokit client;
# the module receives that client through `setOctokit` in github-helpers.mjs.
#
# The module must export an async `main()` function and must import only
# node builtins and other `.github/scripts` modules. A module that needs an
# npm package (semver, yaml, ...) keeps using `setup-nodejs` with an
# install command instead.
name: 'Run workflow script'
description: 'Runs a .github/scripts module through actions/github-script, without a Node.js setup or dependency install.'
inputs:
script:
description: 'Path of the module, relative to the repository root. It must export an async main() function.'
required: true
github-token:
description: 'Token for the Octokit client. Also exported as GITHUB_TOKEN for the module.'
required: true
runs:
using: 'composite'
steps:
- name: Run workflow script
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
env:
SCRIPT: ${{ inputs.script }}
GITHUB_TOKEN: ${{ inputs.github-token }}
with:
github-token: ${{ inputs.github-token }}
script: |
const path = require('node:path');
const { pathToFileURL } = require('node:url');
const load = (file) => import(pathToFileURL(path.join(process.env.GITHUB_WORKSPACE, file)).href);
const { setOctokit } = await load('.github/scripts/github-helpers.mjs');
setOctokit(github);
const { main } = await load(process.env.SCRIPT);
if (typeof main !== 'function') {
throw new Error(`${process.env.SCRIPT} does not export main()`);
}
await main();

View File

@ -23,11 +23,11 @@ inputs:
required: false
default: 'pnpm build'
install-command:
description: 'Command to execute for installing project dependencies. Leave empty to skip install step.'
description: 'Command to install project dependencies. The action trusts the committed lockfile because SafeChain enforces the CI supply-chain policy. Leave empty to skip the install step.'
required: false
default: 'pnpm install --frozen-lockfile'
cache-dependency-path:
description: 'Path(s) to the lockfile(s) used to compute the pnpm-store cache key. Scope this down (e.g. to `.github/scripts/pnpm-lock.yaml`) when only installing a subset, to avoid restoring the full workspace store.'
description: 'Path(s) to the lockfile(s) used to compute the pnpm store and metadata cache keys. Scope this down (e.g. to `.github/scripts/pnpm-lock.yaml`) when only installing a subset.'
required: false
default: 'pnpm-lock.yaml'
@ -43,12 +43,66 @@ runs:
echo "::error::enable-docker-cache is true but docker-cache-key is empty"
exit 1
# Use one pin for setup, cache lookup and verification.
- name: Resolve pnpm version
id: pnpm-version
shell: bash
run: node .github/scripts/resolve-pnpm-version.mjs
# Windows cannot activate a POSIX home path from the restored cache.
- name: Restore pnpm executable
if: runner.os != 'Windows'
id: cache-pnpm-exe
uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: ~/setup-pnpm
key: ${{ steps.pnpm-version.outputs.cache-key }}
- name: Setup pnpm
uses: pnpm/setup@84cb39b217b10273981911c288cd62326dc7c6d2 # v2.0.2
id: setup-pnpm
if: steps.cache-pnpm-exe.outputs.cache-hit != 'true'
continue-on-error: true
uses: pnpm/setup@703c52620218391530e48b9e8870d5c0082e1b9b # v2.1.0
with:
install: false
version: 11.22.0
cache-dependency-path: ${{ inputs.cache-dependency-path }}
version: ${{ steps.pnpm-version.outputs.version }}
- name: Retry pnpm setup
if: steps.setup-pnpm.outcome == 'failure'
uses: pnpm/setup@703c52620218391530e48b9e8870d5c0082e1b9b # v2.1.0
with:
install: false
version: ${{ steps.pnpm-version.outputs.version }}
# Cache hits restore files but not the per-job environment.
- name: Activate cached pnpm executable
if: steps.cache-pnpm-exe.outputs.cache-hit == 'true'
shell: bash
run: | # zizmor: ignore[github-env] Fixed paths contain no external input.
PNPM_DEST="$HOME/setup-pnpm"
echo "PNPM_HOME=$PNPM_DEST" >> "$GITHUB_ENV"
echo "$PNPM_DEST" >> "$GITHUB_PATH"
echo "$PNPM_DEST/bin" >> "$GITHUB_PATH"
# A stale or truncated cache entry must fail here, not later in the install.
- name: Verify pnpm Version
shell: bash
env:
EXPECTED_PNPM_VERSION: ${{ steps.pnpm-version.outputs.version }}
run: |
ACTUAL_PNPM_VERSION="$(pnpm --version)"
if [ "$ACTUAL_PNPM_VERSION" != "$EXPECTED_PNPM_VERSION" ]; then
echo "::error::pnpm ${EXPECTED_PNPM_VERSION} is not active (got ${ACTUAL_PNPM_VERSION})"
exit 1
fi
# Save before later job failures can prevent cache creation.
- name: Save pnpm executable
if: runner.os != 'Windows' && steps.cache-pnpm-exe.outputs.cache-hit != 'true'
uses: actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: ~/setup-pnpm
key: ${{ steps.pnpm-version.outputs.cache-key }}
# Cache the Node toolcache so setup-node skips the ~33s nodejs.org download
# on every subsequent job. First fresh runner pays the download; later jobs
@ -64,7 +118,7 @@ runs:
# alongside the toolchain. Key bumped to v2 to invalidate stale entries.
- name: Restore Node.js Toolcache
if: runner.os == 'Linux' && runner.arch == 'X64'
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: /opt/hostedtoolcache/node/${{ inputs.node-version }}
key: node-toolcache-v2-${{ runner.os }}-${{ runner.arch }}-${{ inputs.node-version }}
@ -104,6 +158,24 @@ runs:
mkdir -p "$PNPM_STORE_PATH"
fi
# pnpm keeps registry metadata and its lockfile verification log outside the package store.
- name: Resolve pnpm metadata cache directory
if: ${{ inputs.install-command != '' }}
id: pnpm-metadata-cache
shell: bash
run: |
PNPM_CACHE_PATH="$(pnpm cache path --silent)"
mkdir -p "$PNPM_CACHE_PATH"
echo "path=$PNPM_CACHE_PATH" >> "$GITHUB_OUTPUT"
echo "version=$(pnpm --version)" >> "$GITHUB_OUTPUT"
- name: Restore pnpm metadata cache
if: ${{ inputs.install-command != '' }}
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: ${{ steps.pnpm-metadata-cache.outputs.path }}
key: pnpm-metadata-v1-${{ runner.os }}-${{ runner.arch }}-${{ steps.pnpm-metadata-cache.outputs.version }}-${{ hashFiles(inputs.cache-dependency-path) }}
- name: Configure SafeChain
shell: bash
run: |
@ -119,10 +191,10 @@ runs:
# CDN must be hit. Keep the version in sync with the download step below.
- name: Restore Aikido SafeChain Binary
id: cache-safe-chain
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: ~/.safe-chain/bin
key: safe-chain-1.5.7-${{ runner.os }}-${{ runner.arch }}
key: safe-chain-1.5.20-${{ runner.os }}-${{ runner.arch }}
# Download + install the SafeChain binary. This is the expensive,
# CDN-backed part and the only part worth gating on the cache. The install
@ -131,8 +203,8 @@ runs:
- name: Download Aikido SafeChain
if: steps.cache-safe-chain.outputs.cache-hit != 'true'
run: |
VERSION="1.5.7"
EXPECTED_SHA256="07ab512fd8795ce41b2275be369aced4c9a93cc7bca9b397951507891a955239"
VERSION="1.5.20"
EXPECTED_SHA256="0ad25efe15d1fa56105157a454d647223e78eb0c53d1f85e3d10afcd722e7bfd"
node .github/scripts/retry.mjs --attempts 3 --delay 10 -- \
curl -fsSL -o install-safe-chain.sh "https://github.com/AikidoSec/safe-chain/releases/download/${VERSION}/install-safe-chain.sh"
echo "${EXPECTED_SHA256} install-safe-chain.sh" | sha256sum -c -
@ -212,6 +284,7 @@ runs:
set +o pipefail
timeout --kill-after=30s 600s $INSTALL_COMMAND \
--trust-lockfile \
--config.logs-dir="$PNPM_DIAG_DIR/pnpm-logs" \
--reporter=append-only --config.loglevel=info 2>&1 | tee "$INSTALL_LOG"
rc=${PIPESTATUS[0]}

View File

@ -1,6 +1,6 @@
services:
postgres:
image: ${TEST_IMAGE_POSTGRES:?set it from packages/testing/containers/postgres-versions.json}
image: ${TEST_IMAGE_POSTGRES:?set it from packages/quality/environments/containers/postgres-versions.json}
restart: always
environment:
- POSTGRES_DB=n8n

View File

@ -1,8 +1,15 @@
## Summary
<!--
Describe what the PR does.
Photos and videos are recommended.
Start with 1 to 3 sentences of background for a new reader. Then describe what the PR does, in prose, not as a list of edits.
Add photos or videos for UI changes.
-->
## Why
<!--
State the problem or the constraint that makes the current code unfit. Do not repeat the Summary.
Add a rejected alternative only when a reviewer would ask about it, in one or two sentences.
-->
## How to test

View File

@ -8,7 +8,8 @@
* Image refs + digests come from the environment (set by docker-build-push.yml).
* An image with no digest (not built for this release type) is skipped.
*
* Usage: node .github/scripts/attest-image-sbom.mjs (run from the repo root)
* Usage: node .github/scripts/attest-image-sbom.mjs [--validate-only]
* (run from the repo root)
*/
import { execFileSync } from 'node:child_process';
import { readFileSync } from 'node:fs';
@ -62,7 +63,10 @@ export function assertSbomIsUsable(sbomPath, label) {
}
}
function attest({ label, image, digest }) {
export function processTarget(
{ label, image, digest },
{ shouldAttest = true, runCommand = run, assertUsable = assertSbomIsUsable } = {},
) {
const ref = `${image}@${digest}`;
const out = path.join(REPO_ROOT, `sbom-${label}.cdx.json`);
console.log(`::group::SBOM for ${label} (${ref})`);
@ -71,14 +75,14 @@ function attest({ label, image, digest }) {
// names the failing image renders inside a collapsed section.
try {
// Pull the (host-arch) image and scan its filesystem: OS packages + npm.
run('docker', ['pull', ref]);
runCommand('docker', ['pull', ref]);
// `docker:` pins the scan to the image just pulled. A bare ref lets syft's
// own provider order decide, and it may resolve the multi-arch index from
// the registry instead — describing a different manifest than the one
// cosign then attests to.
// syft reads licenses from the LICENSE files on disk, so this scan makes no
// registry requests. `-file` excludes its per-file catalogue, ~4000 entries.
run('syft', [
runCommand('syft', [
`docker:${ref}`,
'-o',
`cyclonedx-json@1.6=${out}`,
@ -90,52 +94,67 @@ function attest({ label, image, digest }) {
// Resolve first-party + override licenses (lenient: this image holds only a
// subset of the npm closure, so absent overrides are not stale pins) and drop
// scanner filesystem phantoms.
run(process.execPath, [ENRICH, out, '--lenient-config', '--drop-phantom-npm']);
runCommand(process.execPath, [ENRICH, out, '--lenient-config', '--drop-phantom-npm']);
// Release-blocking gate, scoped to npm — OS packages carry upstream-distro
// license strings we don't control, so they're inventoried but not gated.
run(process.execPath, [CHECK, out, ...ALLOW_REFS, '--enforce-prefix=pkg:npm/']);
assertSbomIsUsable(out, label);
runCommand(process.execPath, [CHECK, out, ...ALLOW_REFS, '--enforce-prefix=pkg:npm/']);
assertUsable(out, label);
// --replace, so re-running after a mid-loop failure does not leave the
// digest carrying two CycloneDX attestations.
run('cosign', [
'attest',
'--yes',
'--replace',
'--type',
'cyclonedx',
'--predicate',
out,
ref,
]);
if (shouldAttest) {
runCommand('cosign', [
'attest',
'--yes',
'--replace',
'--type',
'cyclonedx',
'--predicate',
out,
ref,
]);
}
} finally {
console.log('::endgroup::');
}
}
function main() {
const targets = parseTargets(process.env);
if (targets.length === 0) {
console.log('No images with digests to attest — skipping.');
return;
}
export function processTargets(targets, options = {}) {
const action = options.shouldAttest === false ? 'validation' : 'attestation';
const process = options.processTarget ?? processTarget;
// Attempt every image, then report. Aborting on the first failure leaves the
// later images silently unattested and hides whether they would have passed.
// later images silently unvalidated and hides whether they would have passed.
const failed = [];
for (const target of targets) {
try {
attest(target);
process(target, options);
} catch (err) {
failed.push(`${target.label}: ${err.message}`);
console.log(`::error title=SBOM attestation::${target.label}: ${err.message}`);
console.log(`::error title=SBOM ${action}::${target.label}: ${err.message}`);
}
}
if (failed.length > 0) {
throw new Error(`${failed.length} of ${targets.length} image(s) failed:\n ${failed.join('\n ')}`);
throw new Error(
`${failed.length} of ${targets.length} image(s) failed:\n ${failed.join('\n ')}`,
);
}
}
export function main({
env = process.env,
args = process.argv.slice(2),
processAll = processTargets,
} = {}) {
const shouldAttest = !args.includes('--validate-only');
const targets = parseTargets(env);
if (targets.length === 0) {
console.log(`No images with digests to ${shouldAttest ? 'attest' : 'validate'} - skipping.`);
return;
}
processAll(targets, { shouldAttest });
}
if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
try {
main();

View File

@ -3,7 +3,13 @@ import assert from 'node:assert/strict';
import { mkdtempSync, writeFileSync } from 'node:fs';
import os from 'node:os';
import path from 'node:path';
import { assertSbomIsUsable, parseTargets } from './attest-image-sbom.mjs';
import {
assertSbomIsUsable,
main,
parseTargets,
processTarget,
processTargets,
} from './attest-image-sbom.mjs';
describe('parseTargets', () => {
it('builds a target per image when both ref and digest are present', () => {
@ -82,3 +88,89 @@ describe('assertSbomIsUsable', () => {
});
});
});
describe('processTarget', () => {
const target = {
label: 'n8n-pc',
image: 'ghcr.io/n8n-io/n8n',
digest: 'sha256:aaa',
};
it('runs the complete validation chain without attesting in validation-only mode', () => {
const calls = [];
let asserted = false;
processTarget(target, {
shouldAttest: false,
runCommand: (command, args) => calls.push([command, args]),
assertUsable: (_sbomPath, label) => {
assert.equal(label, 'n8n-pc');
asserted = true;
},
});
assert.deepEqual(
calls.map(([command]) => path.basename(command)),
['docker', 'syft', 'node', 'node'],
);
assert.equal(calls[0][1][0], 'pull');
assert.equal(calls[1][1][0], 'docker:ghcr.io/n8n-io/n8n@sha256:aaa');
assert.ok(calls[2][1].includes('--drop-phantom-npm'));
assert.ok(calls[3][1].includes('--enforce-prefix=pkg:npm/'));
assert.equal(asserted, true);
});
it('attests after validation by default', () => {
const calls = [];
processTarget(target, {
runCommand: (command, args) => calls.push([command, args]),
assertUsable: () => {},
});
assert.equal(calls.at(-1)[0], 'cosign');
assert.deepEqual(calls.at(-1)[1].slice(0, 6), [
'attest',
'--yes',
'--replace',
'--type',
'cyclonedx',
'--predicate',
]);
});
});
describe('processTargets', () => {
it('validates every image before reporting aggregated failures', () => {
const attempted = [];
assert.throws(
() =>
processTargets([{ label: 'n8n' }, { label: 'n8n-pc' }, { label: 'runners' }], {
shouldAttest: false,
processTarget: (target) => {
attempted.push(target.label);
if (target.label !== 'n8n-pc') throw new Error('missing license');
},
}),
/2 of 3 image\(s\) failed/,
);
assert.deepEqual(attempted, ['n8n', 'n8n-pc', 'runners']);
});
});
describe('main', () => {
it('passes validation-only mode to image processing', () => {
let received;
main({
env: {
N8N_IMAGE: 'ghcr.io/n8n-io/n8n',
N8N_DIGEST: 'sha256:aaa',
},
args: ['--validate-only'],
processAll: (targets, options) => {
received = { targets, options };
},
});
assert.equal(received.targets[0].label, 'n8n');
assert.equal(received.options.shouldAttest, false);
});
});

545
.github/scripts/codespace-preview.mjs vendored Normal file
View File

@ -0,0 +1,545 @@
#!/usr/bin/env node
// Drives a PR preview instance from CI. `scripts/codespace-preview/preview.mjs` does the work; this
// script maps the pull_request event onto one of its operations and reports the
// result back to the PR as a single, edited-in-place comment.
//
// labeled -> up create or start the box, then serve the PR head
// synchronize -> refresh re-serve the new head in the box that already exists
// unlabeled -> down delete the box
// closed -> down
//
// A manual run sets PREVIEW_OPERATION instead, which wins over the event mapping.
//
// `refresh` never creates a box. A box that GitHub already deleted (24 h
// retention) is reported as expired, not as a failure.
//
// `up` and `refresh` take minutes, so the comment goes up before the work starts
// and is edited for each phase and once a minute after that. `preview.mjs --json`
// prints one JSON line per phase on the channel this already parses; the report is
// the line that carries a `url`.
//
// With `--report-cancelled` it posts one body and nothing else. The workflow runs
// that on a cancelled or timed-out job, which kills this process mid-checklist.
import { spawn } from 'node:child_process';
import { fileURLToPath } from 'node:url';
import { PREVIEW_LABEL_PREFIX } from '../../scripts/codespace-preview/preview-labels.mjs';
import { PREVIEW_PHASES, createLineSplitter, phaseIndex } from '../../scripts/codespace-preview/preview-phases.mjs';
import {
ensureEnvVar,
findCommentByMarker,
postOrUpdateComment,
updateCommentById,
} from './github-helpers.mjs';
export const BOT_MARKER = '<!-- codespace-preview -->';
// Only a progress body carries this. The cancelled-run step checks for it before it
// writes, so a run cancelled while it was still queued cannot replace a good ready
// comment left by the run before it.
export const PROGRESS_MARKER = '<!-- codespace-preview:progress -->';
export const PREVIEW_LABEL = 'codespace-preview';
// Copied from `preview.mjs`, which owns them, so they move together. They cannot be
// imported: that module runs its command switch on import. They appear in the comment
// so a reviewer knows how long the instance lasts without reading the script.
const IDLE_TIMEOUT = '2 hours';
const RETENTION_PERIOD = '24 hours';
const CODESPACE_ENV_VARIABLE_URL = "https://internal.users.n8n.cloud/form/codespace-environments";
// A slept box comes back with a private port, so every recovery hint points here.
export const WORKFLOW_URL =
'https://github.com/n8n-io/n8n/actions/workflows/util-codespace-preview.yml';
// What a manual run may ask for. `ls` is absent: it needs no PR and posts no comment.
export const DISPATCH_OPERATIONS = ['up', 'refresh', 'down'];
// Resolved against this file, so the script runs the same from any directory.
const PREVIEW_SCRIPT = fileURLToPath(new URL('../../scripts/codespace-preview/preview.mjs', import.meta.url));
// Often enough to look alive, rarely enough that the edit history stays readable.
const HEARTBEAT_MS = 60_000;
/**
* A `preview:*` label configures an instance that already exists, so toggling one
* re-serves the box rather than creating or deleting it.
*
* @param {string} action The pull_request event action.
* @param {string} [label] `github.event.label.name`, for a labeled/unlabeled event.
* @returns {'up' | 'refresh' | 'down' | undefined}
*/
export function operationFor(action, label) {
switch (action) {
case 'labeled':
case 'unlabeled':
if (label?.startsWith(PREVIEW_LABEL_PREFIX)) return 'refresh';
if (label !== PREVIEW_LABEL) return undefined;
return action === 'labeled' ? 'up' : 'down';
case 'synchronize':
return 'refresh';
case 'closed':
return 'down';
default:
return undefined;
}
}
/**
* A manual run says what to do, so its operation wins over the event mapping. An
* operation that is not one of ours stops here: `preview.mjs` would only print
* usage and exit 1, which reads as a broken preview rather than a bad input.
*
* @param {{action?: string, label?: string, operation?: string}} context
* @returns {'up' | 'refresh' | 'down' | undefined}
*/
export function resolveOperation({ action, label, operation }) {
if (operation) return DISPATCH_OPERATIONS.includes(operation) ? operation : undefined;
return operationFor(action ?? '', label);
}
/**
* `preview.mjs --json` keeps stdout clean, but a truncated or empty run still has
* to be told apart from a good one. Read the last JSON object that carries a url.
*
* @param {string} stdout
* @returns {{pr: number, sha: string, codespace: string, url: string, orgVisible: boolean} | undefined}
*/
export function parsePreviewJson(stdout) {
for (const line of stdout.split('\n').reverse()) {
const trimmed = line.trim();
if (!trimmed.startsWith('{')) continue;
try {
const parsed = JSON.parse(trimmed);
if (parsed && typeof parsed.url === 'string') return parsed;
} catch {}
}
return undefined;
}
/**
* `preview.mjs ls` prints one tab-separated row for each box:
* `preview/pr-<pr>\t<state>\t<name>\tlast used <iso>`. Compare the whole first
* field, or PR 3 would match the box for PR 37.
*
* @param {string} stdout
* @param {string | number} pr
*/
export function hasPreviewBox(stdout, pr) {
return stdout.split('\n').some((line) => line.split('\t')[0]?.trim() === `preview/pr-${pr}`);
}
/**
* A forwarded Codespaces URL is `https://<codespace>-<port>.app.github.dev`. The
* trailing dot anchors the match, so a codespace name that ends in digits is safe.
*
* @param {string} url
*/
export function portFromUrl(url) {
return new URL(url).hostname.match(/-(\d+)\./)?.[1];
}
/** @param {{url: string, codespace: string, sha: string, orgVisible: boolean, pr: string | number}} preview */
export function readyComment({ url, codespace, sha, orgVisible, pr }) {
// A failed port share is not fatal in preview.mjs, so the box can be up while
// the URL still answers 302 to everyone. Say so instead of implying it works.
const port = portFromUrl(url);
const access = orgVisible
? 'Every n8n org member who is signed in to GitHub can open it.'
: [
`**Port ${port} is still private.** The instance runs, but only its owner can`,
'open the URL. To share it, run:',
'',
'```',
`gh codespace ports visibility ${port}:org -c ${codespace}`,
'```',
].join('\n');
return [
BOT_MARKER,
`### Preview instance for \`${sha.slice(0, 7)}\``,
'',
`**[Open the preview](${url}/preview-signin)** — one click signs you in.`,
'',
`If you need to modify the environment variables of this instance, navigate to ${CODESPACE_ENV_VARIABLE_URL}?pr=${pr} and submit them.`,
'The instance will refresh and apply your variables.',
'',
'| | |',
'| --- | --- |',
`| URL | ${url} |`,
`| Codespace | \`preview/pr-${pr}\` |`,
`| Sign in | \`preview@n8n.io\` / \`PreviewInstance1\` |`,
'',
access,
'',
`The instance sleeps after ${IDLE_TIMEOUT} of no use and is deleted after ${RETENTION_PERIOD}.`,
`A box that slept comes back private, so wake it with [the preview workflow](${WORKFLOW_URL})`,
'(`Run workflow` → this PR number → `up`), or by pushing a commit.',
`Remove the \`${PREVIEW_LABEL}\` label to delete it now.`,
].join('\n');
}
/** @param {{pr: string | number}} context */
export function downComment({ pr }) {
return [
BOT_MARKER,
`### Preview instance deleted`,
'',
`The preview box for PR #${pr} is gone. Add the \`${PREVIEW_LABEL}\` label to get a new one.`,
].join('\n');
}
/** @param {{pr: string | number}} context */
export function expiredComment({ pr }) {
return [
BOT_MARKER,
`### Preview instance expired`,
'',
`The preview box for PR #${pr} no longer exists — GitHub deletes one after ${RETENTION_PERIOD}.`,
`Run [the preview workflow](${WORKFLOW_URL}) with \`up\` to get a new one.`,
].join('\n');
}
/**
* `4m 12s`, for a comment that is read while it counts. Anything unusable reads as
* `0s`: a wrong duration in a status line is worse than a boring one.
*
* @param {number} ms
*/
export function formatElapsed(ms) {
const total = Number.isFinite(ms) && ms > 0 ? Math.floor(ms / 1000) : 0;
const seconds = total % 60;
const minutes = Math.floor(total / 60) % 60;
const hours = Math.floor(total / 3600);
if (hours) return `${hours}h ${minutes}m`;
if (minutes) return `${minutes}m ${String(seconds).padStart(2, '0')}s`;
return `${seconds}s`;
}
/**
* The instance URL out of a body a previous run left. A refresh replaces the ready
* comment with a checklist, so without this the PR loses the URL for several
* minutes. Reads the URL row `readyComment` writes.
*
* @param {string} body
*/
export function previewUrlFromBody(body) {
return body.match(/^\| URL \| (\S+) \|$/m)?.[1];
}
/**
* One line of a `preview.mjs --json` run that reports a phase rather than the final
* result. A report carries a `url`; a progress line never does.
*
* @param {string} line
* @returns {{phase: string, detail?: string, sha?: string, codespace?: string} | undefined}
*/
export function parseProgressLine(line) {
const trimmed = line.trim();
if (!trimmed.startsWith('{')) return undefined;
try {
const parsed = JSON.parse(trimmed);
if (parsed && typeof parsed.phase === 'string' && parsed.url === undefined) return parsed;
} catch {}
return undefined;
}
/**
* The checklist, while the box is being set up. Pure: the caller passes the clock,
* so this renders the same body twice.
*
* @param {{
* pr: string | number,
* operation: 'up' | 'refresh',
* runUrl: string,
* sha?: string,
* phase?: string,
* detail?: string,
* previousUrl?: string,
* startedAt: number,
* phaseStartedAt: number,
* now: number,
* }} state
*/
export function progressComment({
pr,
operation,
runUrl,
sha,
phase,
detail,
previousUrl,
startedAt,
phaseStartedAt,
now,
}) {
// Everything before the current phase is done. Going by position rather than by
// the phases actually seen keeps the list right when one never reports — an old
// PR head has no in-box emitter.
const current = phaseIndex(phase);
const inPhaseMs = now - phaseStartedAt;
const steps = PREVIEW_PHASES.map((step, index) => {
if (index < current) return `- [x] ${step.label}`;
if (index !== current) return `- [ ] ${step.label}`;
return `- [ ] **${step.label}**${detail ? ` — ${detail}` : ''} · ${formatElapsed(inPhaseMs)}`;
});
const slow = PREVIEW_PHASES[current]?.slowAfterMs;
const target = sha ? `\`${sha.slice(0, 7)}\`` : `PR #${pr}`;
return [
BOT_MARKER,
PROGRESS_MARKER,
`### Preview instance ${operation === 'refresh' ? 'updating to' : 'starting for'} ${target}`,
'',
'Setting up the box. This usually takes a few minutes.',
'',
...steps,
...(slow && inPhaseMs > slow
? ['', `> ${PREVIEW_PHASES[current].label} is taking longer than usual.`]
: []),
// A refresh stops the old backend before it rebuilds, so the URL is down for
// the rest of this run. Keep it on the PR anyway: it is the same URL when the
// run finishes, and losing it for several minutes is worse than saying so.
...(previousUrl
? ['', `The URL does not change: ${previousUrl}. It stops answering until this finishes.`]
: []),
'',
`Elapsed ${formatElapsed(now - startedAt)} · updated ${new Date(now).toISOString().slice(11, 19)} UTC · [live log](${runUrl})`,
].join('\n');
}
/** @param {{pr: string | number, operation: string, runUrl: string}} context */
export function cancelledComment({ pr, operation, runUrl }) {
return [
BOT_MARKER,
`### Preview instance run stopped`,
'',
`The \`preview ${operation}\` run for PR #${pr} was cancelled or timed out, so the`,
'instance is in an unknown state. The box can still exist.',
'',
`Run [the preview workflow](${WORKFLOW_URL}) with \`up\` to finish the job, or \`down\` to delete the box.`,
`See [the workflow run](${runUrl}) for how far it got.`,
].join('\n');
}
/** @param {{operation: string, runUrl: string, message: string}} context */
export function failureComment({ operation, runUrl, message }) {
return [
BOT_MARKER,
`### Preview instance failed`,
'',
`\`preview ${operation}\` did not finish: ${message}`,
'',
`See [the workflow run](${runUrl}) for the full log.`,
`Run [the preview workflow](${WORKFLOW_URL}) with \`${operation}\` to try again.`,
].join('\n');
}
/**
* Human progress and the in-box build log go to stderr, so they stream into the job
* log live. Only stdout is captured, and its phase lines are handed over as they
* arrive — a run this long has to report itself while it runs, not at the end.
*
* @param {readonly string[]} args
* @param {{onProgress?: (progress: {phase: string, detail?: string, sha?: string}) => void}} [hooks]
* @returns {Promise<{status: number | null, stdout: string}>}
*/
function runPreview(args, { onProgress } = {}) {
return new Promise((resolve, reject) => {
const child = spawn('node', [PREVIEW_SCRIPT, ...args], {
stdio: ['ignore', 'pipe', 'inherit'],
});
const splitLines = createLineSplitter();
let stdout = '';
child.stdout.setEncoding('utf8');
child.stdout.on('data', (chunk) => {
// Kept whole for `parsePreviewJson`, which reads the report off the end.
stdout += chunk;
if (!onProgress) return;
for (const line of splitLines(chunk)) {
const progress = parseProgressLine(line);
if (progress) onProgress(progress);
}
});
child.on('error', reject);
child.on('close', (status) => resolve({ status, stdout }));
});
}
/**
* Keeps the checklist on the PR current while `preview.mjs` runs: one edit for each
* phase, plus a heartbeat so a long phase still looks alive.
*
* @param {{pr: string, operation: 'up' | 'refresh', runUrl: string, previousUrl?: string, commentId?: number}} context
*/
function createProgressReporter({ pr, operation, runUrl, previousUrl, commentId }) {
const startedAt = Date.now();
let phaseStartedAt = startedAt;
let id = commentId;
let phase;
let detail;
let sha;
let writes = Promise.resolve();
// One chain, so two edits never overlap, and every failure is swallowed: this
// Octokit carries no retry plugin, and a status edit must not fail a preview that
// is otherwise fine. Resetting the chain keeps one failure from poisoning the next.
function push() {
writes = writes
.then(async () => {
const body = progressComment({
pr,
operation,
runUrl,
sha,
phase,
detail,
previousUrl,
startedAt,
phaseStartedAt,
now: Date.now(),
});
// Hold the id from the first write: paginating every comment on the PR
// once a minute would be the expensive part of a heartbeat.
if (id === undefined) id = await postOrUpdateComment(Number(pr), body, BOT_MARKER);
else await updateCommentById(id, body);
})
.catch((error) => {
console.log(`::warning::Could not update the preview comment: ${error.message}`);
});
return writes;
}
const timer = setInterval(push, HEARTBEAT_MS);
timer.unref();
return {
start: push,
/** @param {{phase: string, detail?: string, sha?: string}} progress */
onProgress(progress) {
if (progress.sha) sha = progress.sha;
if (progress.phase === phase && progress.detail === detail) return;
phase = progress.phase;
detail = progress.detail;
phaseStartedAt = Date.now();
push();
},
/** Drains the queue, so the final body of the run is always the last write. */
async stop() {
clearInterval(timer);
await writes;
},
};
}
/**
* A cancelled or timed-out job kills this process mid-checklist, so the workflow
* runs it again just to say so. Only over a progress body: a run cancelled while it
* was queued never started work, and must leave a ready comment alone.
*
* @param {string} pr
* @param {string} operation
* @param {string} runUrl
*/
async function reportCancelled(pr, operation, runUrl) {
const existing = await findCommentByMarker(Number(pr), BOT_MARKER);
if (!existing?.body.includes(PROGRESS_MARKER)) {
console.log('The preview comment is not a checklist — leaving it as it is.');
return;
}
await updateCommentById(existing.id, cancelledComment({ pr, operation, runUrl }));
}
async function main() {
const pr = ensureEnvVar('PULL_REQUEST_NUMBER');
const runUrl = ensureEnvVar('RUN_URL');
const requested = process.env.PREVIEW_OPERATION;
// A manual run carries no event action, so it cannot be required there.
const action = requested ? '' : ensureEnvVar('EVENT_ACTION');
const label = process.env.LABEL_NAME;
// The dispatch input is free text. Refuse a non-number before anything tries to
// comment on it: postOrUpdateComment(NaN) 404s inside the catch below and
// reports that instead of the real cause.
if (!/^\d+$/.test(pr)) {
console.error(`::error::PULL_REQUEST_NUMBER must be a number, got "${pr}".`);
process.exitCode = 1;
return;
}
const operation = resolveOperation({ action, label, operation: requested });
if (!operation) {
console.log(
`No preview operation for action="${action}" label="${label ?? ''}" operation="${requested ?? ''}" — nothing to do.`,
);
return;
}
if (process.argv.includes('--report-cancelled')) {
await reportCancelled(pr, operation, runUrl);
return;
}
try {
if (operation === 'refresh') {
const list = await runPreview(['ls']);
if (list.status !== 0) throw new Error(`\`preview ls\` exited ${list.status}`);
if (!hasPreviewBox(list.stdout, pr)) {
console.log(`No preview box for PR #${pr} — reporting it as expired.`);
await postOrUpdateComment(Number(pr), expiredComment({ pr }), BOT_MARKER);
return;
}
}
if (operation === 'down') {
const { status } = await runPreview([operation, pr, '--json']);
if (status !== 0) throw new Error(`\`preview ${operation}\` exited ${status}`);
await postOrUpdateComment(Number(pr), downComment({ pr }), BOT_MARKER);
return;
}
// A checklist only earns its place in front of work that takes minutes. It also
// goes up after the `refresh` check above, so a box that is already gone never
// flashes a checklist before the expired body replaces it.
const existing = await findCommentByMarker(Number(pr), BOT_MARKER);
const reporter = createProgressReporter({
pr,
operation,
runUrl,
previousUrl: previewUrlFromBody(existing?.body ?? ''),
commentId: existing?.id,
});
await reporter.start();
let status;
let stdout;
try {
({ status, stdout } = await runPreview([operation, pr, '--json'], {
onProgress: reporter.onProgress,
}));
} finally {
await reporter.stop();
}
if (status !== 0) throw new Error(`\`preview ${operation}\` exited ${status}`);
const preview = parsePreviewJson(stdout);
if (!preview) throw new Error(`\`preview ${operation}\` printed no preview details`);
console.log(`Preview for PR #${pr}: ${preview.url}`);
await postOrUpdateComment(Number(pr), readyComment({ ...preview, pr }), BOT_MARKER);
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
console.error(`::error::Preview ${operation} failed for PR #${pr}: ${message}`);
// Report the failure on the PR too: a label that looks inert is worse than a
// label that says what went wrong.
await postOrUpdateComment(
Number(pr),
failureComment({ operation, runUrl, message }),
BOT_MARKER,
);
process.exitCode = 1;
}
}
// Importable for tests without running the orchestration.
if (process.argv[1] === fileURLToPath(import.meta.url)) {
await main();
}

View File

@ -0,0 +1,358 @@
import assert from 'node:assert/strict';
import { describe, it } from 'node:test';
import {
BOT_MARKER,
DISPATCH_OPERATIONS,
PROGRESS_MARKER,
WORKFLOW_URL,
cancelledComment,
downComment,
expiredComment,
failureComment,
formatElapsed,
hasPreviewBox,
operationFor,
parsePreviewJson,
parseProgressLine,
portFromUrl,
previewUrlFromBody,
progressComment,
readyComment,
resolveOperation,
} from './codespace-preview.mjs';
const PREVIEW = {
pr: 1234,
sha: 'abcdef1234567890abcdef1234567890abcdef12',
codespace: 'psychic-umbrella-q7w6gwx',
url: 'https://psychic-umbrella-q7w6gwx-5678.app.github.dev',
orgVisible: true,
};
const START = Date.parse('2026-09-17T09:35:00Z');
const PROGRESS = {
pr: 1234,
operation: 'up',
runUrl: 'https://github.com/n8n-io/n8n/actions/runs/1',
sha: PREVIEW.sha,
phase: 'build',
startedAt: START,
phaseStartedAt: START + 111_000,
now: START + 363_000,
};
describe('operationFor', () => {
const LABEL = 'codespace-preview';
it('creates and deletes on the trigger label', () => {
assert.equal(operationFor('labeled', LABEL), 'up');
assert.equal(operationFor('unlabeled', LABEL), 'down');
});
it('re-serves on a push or a preview: toggle, without creating a box', () => {
assert.equal(operationFor('synchronize'), 'refresh');
// A toggle configures an instance that exists; it must never create or delete one.
assert.equal(operationFor('labeled', 'preview:enterprise'), 'refresh');
assert.equal(operationFor('unlabeled', 'preview:enterprise'), 'refresh');
assert.equal(operationFor('labeled', 'preview:debug'), 'refresh');
});
it('deletes when the PR closes', () => {
assert.equal(operationFor('closed'), 'down');
});
it('ignores a label that is not ours', () => {
assert.equal(operationFor('labeled', 'bug'), undefined);
assert.equal(operationFor('unlabeled', 'Do Not Merge'), undefined);
// A near-miss must not be read as the trigger label.
assert.equal(operationFor('labeled', 'codespace-preview-2'), undefined);
assert.equal(operationFor('labeled', undefined), undefined);
});
it('returns undefined for an action the workflow does not handle', () => {
assert.equal(operationFor('opened'), undefined);
assert.equal(operationFor('reopened'), undefined);
assert.equal(operationFor(''), undefined);
});
});
describe('resolveOperation', () => {
it('falls back to the event mapping when no operation is requested', () => {
assert.equal(resolveOperation({ action: 'labeled', label: 'codespace-preview' }), 'up');
assert.equal(resolveOperation({ action: 'synchronize' }), 'refresh');
assert.equal(resolveOperation({ action: 'labeled', label: 'bug' }), undefined);
// The workflow sets the variable to '' on a pull_request run, not undefined.
assert.equal(resolveOperation({ action: 'closed', operation: '' }), 'down');
});
it('lets a manual run choose, over the event', () => {
for (const operation of DISPATCH_OPERATIONS) {
assert.equal(resolveOperation({ action: '', operation }), operation);
}
assert.equal(resolveOperation({ action: 'synchronize', operation: 'down' }), 'down');
});
it('rejects an operation that is not one of ours', () => {
// `ls` posts no comment and needs no PR, so the workflow does not offer it.
assert.equal(resolveOperation({ action: '', operation: 'ls' }), undefined);
assert.equal(resolveOperation({ action: '', operation: 'UP' }), undefined);
assert.equal(resolveOperation({ action: '', operation: 'up; rm -rf /' }), undefined);
});
});
describe('parsePreviewJson', () => {
it('finds the JSON line among progress and in-box build output', () => {
const stdout = [
'Creating a preview box for PR #1234 (my-branch)…',
'Waiting for psychic-umbrella-q7w6gwx to accept ssh…',
'> n8n@1.0.0 build /workspaces/n8n',
'Tasks: 112 successful, 112 total',
'Ready: the backend answers /healthz on port 5678.',
JSON.stringify(PREVIEW),
].join('\n');
assert.deepEqual(parsePreviewJson(stdout), PREVIEW);
});
it('takes the last preview object when several are printed', () => {
const stale = { ...PREVIEW, sha: '0'.repeat(40) };
const stdout = `${JSON.stringify(stale)}\n${JSON.stringify(PREVIEW)}\n`;
assert.equal(parsePreviewJson(stdout).sha, PREVIEW.sha);
});
// The phase lines share the channel with the report. This is the contract that
// makes that safe.
it('ignores the phase lines that precede the report', () => {
const stdout = [
'{"phase":"resolve"}',
'{"sha":"abcdef1","phase":"build"}',
JSON.stringify(PREVIEW),
].join('\n');
assert.deepEqual(parsePreviewJson(stdout), PREVIEW);
assert.equal(parsePreviewJson('{"phase":"build"}'), undefined);
});
it('ignores JSON that is not a preview report', () => {
// A build tool can print its own JSON on the same stream.
assert.equal(parsePreviewJson('{"level":"info","msg":"built"}'), undefined);
});
it('returns undefined for output with no JSON at all', () => {
assert.equal(parsePreviewJson(''), undefined);
assert.equal(parsePreviewJson('Serving abcdef1 on psychic-umbrella-q7w6gwx…'), undefined);
});
});
describe('hasPreviewBox', () => {
const ls = [
'preview/pr-37\tAvailable\tpsychic-umbrella-q7w6gwx\tlast used 2026-09-04T09:00:00Z',
'preview/pr-1234\tShutdown\tvigilant-broccoli-x9x8x9x\tlast used 2026-09-03T18:20:00Z',
].join('\n');
it('finds a box by its exact display name', () => {
assert.equal(hasPreviewBox(ls, 37), true);
assert.equal(hasPreviewBox(ls, '1234'), true);
});
it('does not treat a shorter PR number as a prefix match', () => {
// `preview/pr-3` must not match the row for `preview/pr-37`.
assert.equal(hasPreviewBox(ls, 3), false);
assert.equal(hasPreviewBox(ls, 123), false);
});
it('handles the empty listing preview.mjs prints when nothing exists', () => {
assert.equal(hasPreviewBox('No preview boxes on n8n-io/n8n.', 37), false);
assert.equal(hasPreviewBox('', 37), false);
});
});
describe('portFromUrl', () => {
it('reads the forwarded port', () => {
assert.equal(portFromUrl(PREVIEW.url), '5678');
});
it('is not confused by a codespace name that ends in digits', () => {
assert.equal(portFromUrl('https://opulent-broccoli-9936x9x-5678.app.github.dev'), '5678');
});
});
describe('comment bodies', () => {
const bodies = {
ready: readyComment(PREVIEW),
readyPrivate: readyComment({ ...PREVIEW, orgVisible: false }),
down: downComment({ pr: PREVIEW.pr }),
expired: expiredComment({ pr: PREVIEW.pr }),
failure: failureComment({
operation: 'up',
runUrl: 'https://github.com/n8n-io/n8n/actions/runs/1',
message: '`preview up` exited 1',
}),
progress: progressComment(PROGRESS),
cancelled: cancelledComment({
pr: PREVIEW.pr,
operation: 'up',
runUrl: 'https://github.com/n8n-io/n8n/actions/runs/1',
}),
};
it('every body starts with the marker, so postOrUpdateComment edits in place', () => {
for (const [name, body] of Object.entries(bodies)) {
assert.ok(body.startsWith(BOT_MARKER), `${name} must start with the bot marker`);
}
});
it('the ready comment links the one-click sign-in path', () => {
assert.match(bodies.ready, /\/preview-signin\)/);
assert.match(bodies.ready, /abcdef1/);
assert.match(bodies.ready, /preview\/pr-1234/);
});
it('the ready comment does not claim org access when the port share failed', () => {
assert.match(bodies.ready, /Every n8n org member/);
assert.doesNotMatch(bodies.readyPrivate, /Every n8n org member/);
assert.match(
bodies.readyPrivate,
/gh codespace ports visibility 5678:org -c psychic-umbrella-q7w6gwx/,
);
});
it('the failure comment carries the cause and the run link', () => {
assert.match(bodies.failure, /exited 1/);
assert.match(bodies.failure, /actions\/runs\/1/);
});
it('quotes the idle timeout preview.mjs actually sets', () => {
// preview.mjs creates a box with --idle-timeout 2h.
assert.match(bodies.ready, /sleeps after 2 hours/);
});
it('points a slept, expired or failed box at the manual run', () => {
for (const name of ['ready', 'expired', 'failure']) {
assert.ok(bodies[name].includes(WORKFLOW_URL), `${name} must link the workflow`);
}
});
// The cancelled-run step writes only over a checklist, so no settled body may
// carry the progress marker.
it('carries the progress marker on the checklist and nowhere else', () => {
assert.ok(bodies.progress.includes(PROGRESS_MARKER));
for (const name of ['ready', 'down', 'expired', 'failure', 'cancelled']) {
assert.ok(!bodies[name].includes(PROGRESS_MARKER), `${name} must not look in progress`);
}
});
it('the cancelled body says the box can still exist, and links both runs', () => {
assert.match(bodies.cancelled, /can still exist/);
assert.ok(bodies.cancelled.includes(WORKFLOW_URL));
assert.ok(bodies.cancelled.includes('actions/runs/1'));
});
});
describe('formatElapsed', () => {
it('reads as a duration a reviewer can scan', () => {
assert.equal(formatElapsed(0), '0s');
assert.equal(formatElapsed(59_000), '59s');
assert.equal(formatElapsed(60_000), '1m 00s');
assert.equal(formatElapsed(252_000), '4m 12s');
assert.equal(formatElapsed(3_780_000), '1h 3m');
});
// A wrong duration in a status line is worse than a boring one.
it('falls back to 0s for a value it cannot use', () => {
assert.equal(formatElapsed(-1), '0s');
assert.equal(formatElapsed(NaN), '0s');
assert.equal(formatElapsed(undefined), '0s');
});
});
describe('progressComment', () => {
it('ticks off the phases before the current one and leaves the rest open', () => {
const body = progressComment(PROGRESS);
assert.match(body, /- \[x\] Resolve the PR head/);
assert.match(body, /- \[x\] Install dependencies/);
assert.match(body, /- \[ \] \*\*Build the monorepo\*\* · 4m 12s/);
assert.match(body, /- \[ \] Share the port with the org/);
});
it('names the commit and the elapsed time, and links the run', () => {
const body = progressComment(PROGRESS);
assert.match(body, /starting for `abcdef1`/);
assert.match(body, /Elapsed 6m 03s/);
assert.ok(body.includes(PROGRESS.runUrl));
});
it('says what the phase is doing when the phase says so', () => {
const body = progressComment({ ...PROGRESS, phase: 'box', detail: 'reusing psychic-umbrella' });
assert.match(body, /\*\*Prepare the box\*\* — reusing psychic-umbrella/);
});
// A workflow_dispatch run reports the sha only once preview.mjs resolves it.
it('falls back to the PR number before the sha is known', () => {
const body = progressComment({ ...PROGRESS, sha: undefined });
assert.match(body, /starting for PR #1234/);
});
it('leaves every phase open before the first one reports', () => {
const body = progressComment({ ...PROGRESS, phase: undefined });
assert.ok(!body.includes('- [x]'));
assert.match(body, /- \[ \] Resolve the PR head/);
});
it('says a refresh is updating, not starting', () => {
assert.match(progressComment({ ...PROGRESS, operation: 'refresh' }), /updating to `abcdef1`/);
});
it('warns only once a phase passes the time it usually takes', () => {
const slow = { ...PROGRESS, phaseStartedAt: START - 600_001 };
assert.match(progressComment(slow), /Build the monorepo is taking longer than usual/);
assert.ok(!progressComment(PROGRESS).includes('longer than usual'));
});
// Replacing a ready comment with a checklist would otherwise take a working URL
// off the PR for several minutes.
it('keeps a URL a previous run reported', () => {
const body = progressComment({ ...PROGRESS, previousUrl: PREVIEW.url });
assert.ok(body.includes(PREVIEW.url));
assert.match(body, /stops answering/);
assert.ok(!progressComment(PROGRESS).includes('stops answering'));
});
});
describe('previewUrlFromBody', () => {
it('reads the URL back out of a ready body', () => {
assert.equal(previewUrlFromBody(readyComment(PREVIEW)), PREVIEW.url);
});
it('finds nothing in a body that reports no instance', () => {
assert.equal(previewUrlFromBody(downComment({ pr: 1234 })), undefined);
assert.equal(previewUrlFromBody(expiredComment({ pr: 1234 })), undefined);
assert.equal(previewUrlFromBody(progressComment(PROGRESS)), undefined);
assert.equal(previewUrlFromBody(''), undefined);
});
});
describe('parseProgressLine', () => {
it('reads a phase line', () => {
assert.deepEqual(parseProgressLine('{"phase":"build","sha":"abc"}'), {
phase: 'build',
sha: 'abc',
});
});
it('ignores the report and anything that is not a phase line', () => {
assert.equal(parseProgressLine(JSON.stringify(PREVIEW)), undefined);
assert.equal(parseProgressLine('Serving abcdef1 on psychic-umbrella-q7w6gwx…'), undefined);
assert.equal(parseProgressLine('{"level":"info","msg":"built"}'), undefined);
assert.equal(parseProgressLine('{not json'), undefined);
});
});

View File

@ -7,11 +7,16 @@ import { fileURLToPath } from 'node:url';
const REPO_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../..');
export const POSTGRES_VERSIONS_PATH = 'packages/testing/containers/postgres-versions.json';
export const POSTGRES_VERSIONS_PATH = 'packages/quality/environments/containers/postgres-versions.json';
/** The matrix wall is bounded by its slowest leg, so a larger runner buys nothing. */
const RUNNER = 'blacksmith-4vcpu-ubuntu-2204';
// Each leg runs one suite on its own runner, so use every CPU. The CI default
// (half the CPUs) is for jobs that run many suites at once. Every test file
// gets its own database, so more workers do not share state.
const MAX_WORKERS = '--maxWorkers=100%';
/**
* @typedef {Object} PostgresVersions
* @property {string} primary
@ -24,13 +29,25 @@ export function readPostgresVersions(repoRoot = REPO_ROOT) {
return JSON.parse(fs.readFileSync(file, 'utf8'));
}
const SCOPES = new Set(['pr', 'full']);
/**
* Coverage and the schema-docs check run on the primary Postgres leg only, since
* the committed docs come from that one version.
* The schema-docs check runs on the primary Postgres leg only, since the
* committed docs come from that one version. PRs collect coverage on that leg.
* Merge groups use the full scope and skip duplicate coverage collection.
*
* Scope 'pr' keeps SQLite plus the primary Postgres leg. Scope 'full' keeps
* every Postgres major. The merge queue generates with 'full', so every merge
* still gates on the older majors.
*
* @param {PostgresVersions} versions
* @param {'pr' | 'full'} scope
*/
export function buildMatrix(versions) {
export function buildMatrix(versions, scope = 'full') {
if (!SCOPES.has(scope)) {
throw new Error(`Unknown scope "${scope}", expected "pr" or "full"`);
}
const { primary, matrix } = versions;
if (!Array.isArray(matrix) || matrix.length === 0) {
@ -67,29 +84,34 @@ export function buildMatrix(versions) {
}
}
// Validation always runs on the full list; scope only trims the output.
const postgresLegs = scope === 'pr' ? matrix.filter(({ image }) => image === primary) : matrix;
return [
{
name: 'SQLite Pooled',
runner: RUNNER,
'test-cmd': 'pnpm test:sqlite',
'test-cmd': `pnpm test:sqlite ${MAX_WORKERS}`,
'migration-cmd': 'pnpm test:sqlite:migrations',
'schema-check-cmd': 'pnpm --filter=@n8n/db schema:check:sqlite',
TEST_IMAGE_POSTGRES: undefined,
collectCoverage: 'false',
},
...matrix.map(({ major, image }) => ({
...postgresLegs.map(({ major, image }) => ({
name: `Postgres ${major}`,
runner: RUNNER,
'test-cmd': 'pnpm test:postgres:integration:tc',
'migration-cmd': 'pnpm test:postgres:migrations:tc',
'test-cmd': `pnpm test:postgres:integration:tc ${MAX_WORKERS}`,
'migration-cmd': `pnpm test:postgres:migrations:tc ${MAX_WORKERS}`,
'schema-check-cmd': image === primary ? 'pnpm --filter=@n8n/db schema:check:postgres' : '',
TEST_IMAGE_POSTGRES: image,
collectCoverage: image === primary ? 'true' : 'false',
collectCoverage: scope === 'pr' && image === primary ? 'true' : 'false',
})),
];
}
// Skipped when imported by the tests.
if (process.argv[1] === fileURLToPath(import.meta.url)) {
console.log(JSON.stringify(buildMatrix(readPostgresVersions())));
const scopeArg = process.argv.find((arg) => arg.startsWith('--scope='));
const scope = scopeArg ? scopeArg.slice('--scope='.length) : 'full';
console.log(JSON.stringify(buildMatrix(readPostgresVersions(), scope)));
}

View File

@ -62,14 +62,27 @@ describe('buildMatrix', () => {
}
});
it('collects coverage and checks schema docs on the primary Postgres leg only', () => {
const legs = buildMatrix(versions());
it('collects coverage on the primary Postgres leg in pr scope only', () => {
const legs = buildMatrix(versions(), 'pr');
const collecting = legs.filter((leg) => leg.collectCoverage === 'true');
assert.deepEqual(
collecting.map((leg) => leg.name),
['Postgres 18'],
);
});
it('does not collect coverage in full scope', () => {
const legs = buildMatrix(versions(), 'full');
assert.equal(
legs.some((leg) => leg.collectCoverage === 'true'),
false,
);
});
it('checks schema docs on the primary Postgres leg only', () => {
const legs = buildMatrix(versions());
const checkingPostgresSchema = legs.filter(
(leg) => leg['schema-check-cmd'] === 'pnpm --filter=@n8n/db schema:check:postgres',
@ -125,4 +138,32 @@ describe('buildMatrix', () => {
it('rejects an empty matrix', () => {
assert.throws(() => buildMatrix({ primary: 'postgres:18.4-alpine', matrix: [] }), /non-empty/);
});
it('keeps only SQLite and the primary Postgres leg in pr scope', () => {
const legs = buildMatrix(versions(), 'pr');
assert.deepEqual(
legs.map((leg) => leg.name),
['SQLite Pooled', 'Postgres 18'],
);
});
it('keeps every Postgres leg in full scope', () => {
const legs = buildMatrix(versions(), 'full');
assert.deepEqual(
legs.map((leg) => leg.name),
['SQLite Pooled', 'Postgres 16', 'Postgres 17', 'Postgres 18'],
);
});
it('validates the full version list even in pr scope', () => {
const stale = { ...versions(), primary: 'postgres:17.10-alpine' };
assert.throws(() => buildMatrix(stale, 'pr'), /"primary".*must be the newest/);
});
it('rejects an unknown scope', () => {
assert.throws(() => buildMatrix(versions(), 'nightly'), /Unknown scope/);
});
});

View File

@ -14,6 +14,7 @@ import { appendFileSync, readFileSync } from 'node:fs';
import { pathToFileURL } from 'node:url';
const WORKSPACE_FILE = 'pnpm-workspace.yaml';
const ROOT_MANIFEST = 'package.json';
const BUMP_ONLY = [/^pnpm-workspace\.yaml$/, /^pnpm-lock\.yaml$/, /(^|\/)package\.json$/];
@ -69,6 +70,14 @@ export function nativePinsChanged(baseText, headText) {
return { changed: false, reason: 'no buildable dependency pin changed' };
}
export function packageManagerChanged(baseText, headText) {
try {
return JSON.parse(baseText).packageManager !== JSON.parse(headText).packageManager;
} catch {
return true;
}
}
function fileAt(ref, path) {
try {
return execFileSync('git', ['show', `${ref}:${path}`], { encoding: 'utf8' });
@ -88,12 +97,25 @@ function main(argv) {
let reason = 'a Docker-chain file changed';
if (isBumpOnly(files)) {
const baseText = fileAt(base, WORKSPACE_FILE);
const headText = fileAt('HEAD', WORKSPACE_FILE);
if (baseText === null || headText === null) {
reason = `could not read ${WORKSPACE_FILE} on both sides of the diff`;
const baseManifest = fileAt(base, ROOT_MANIFEST);
const headManifest = fileAt('HEAD', ROOT_MANIFEST);
if (files.includes(ROOT_MANIFEST) && (baseManifest === null || headManifest === null)) {
reason = `could not read ${ROOT_MANIFEST} on both sides of the diff`;
} else if (
files.includes(ROOT_MANIFEST) &&
baseManifest !== null &&
headManifest !== null &&
packageManagerChanged(baseManifest, headManifest)
) {
reason = 'the package manager pin changed';
} else {
({ changed: build, reason } = nativePinsChanged(baseText, headText));
const baseWorkspace = fileAt(base, WORKSPACE_FILE);
const headWorkspace = fileAt('HEAD', WORKSPACE_FILE);
if (baseWorkspace === null || headWorkspace === null) {
reason = `could not read ${WORKSPACE_FILE} on both sides of the diff`;
} else {
({ changed: build, reason } = nativePinsChanged(baseWorkspace, headWorkspace));
}
}
}

View File

@ -1,7 +1,12 @@
import { describe, it } from 'node:test';
import assert from 'node:assert/strict';
import { readFileSync } from 'node:fs';
import { buildableDeps, isBumpOnly, nativePinsChanged } from './should-smoke-build.mjs';
import {
buildableDeps,
isBumpOnly,
nativePinsChanged,
packageManagerChanged,
} from './should-smoke-build.mjs';
const workspace = ({ kafka = '1.9.1', vm = '^7.0.0', sentry = '10.70.0', allowVm = true } = {}) => `
catalog:
@ -86,3 +91,25 @@ describe('nativePinsChanged', () => {
assert.equal(nativePinsChanged(workspace(), workspace()).changed, false);
});
});
describe('packageManagerChanged', () => {
it('detects a pnpm version change', () => {
assert.equal(
packageManagerChanged(
JSON.stringify({ packageManager: 'pnpm@11.25.0' }),
JSON.stringify({ packageManager: 'pnpm@12.3.4' }),
),
true,
);
});
it('ignores unrelated manifest changes', () => {
assert.equal(
packageManagerChanged(
JSON.stringify({ packageManager: 'pnpm@12.3.4', version: '1.0.0' }),
JSON.stringify({ packageManager: 'pnpm@12.3.4', version: '1.0.1' }),
),
false,
);
});
});

View File

@ -0,0 +1,47 @@
#!/usr/bin/env node
import { writeFile } from 'node:fs/promises';
import { dirname, resolve } from 'node:path';
import { fileURLToPath, pathToFileURL } from 'node:url';
const repositoryRoot = resolve(dirname(fileURLToPath(import.meta.url)), '../..');
const outputFile = resolve(
repositoryRoot,
'packages/@n8n/instance-ai/src/tools/nodes/credential-setupability.json',
);
const endpoint = process.env.N8N_CREDENTIAL_SETUPABILITY_ENDPOINT;
const roundSetupability = (value) => (value === null ? null : Math.round(value * 20) / 20);
export const roundPopularity = (value) => (value === null ? null : Math.round(value * 10) / 10);
async function main() {
if (!endpoint) {
throw new Error('N8N_CREDENTIAL_SETUPABILITY_ENDPOINT is required.');
}
console.log('Fetching credential setupability data.');
const response = await fetch(endpoint, { signal: AbortSignal.timeout(10_000) });
if (!response.ok) {
throw new Error(`Credential setupability endpoint returned HTTP ${response.status}.`);
}
const data = await response.json();
if (!Array.isArray(data) || data.length === 0) {
throw new Error('Credential setupability endpoint returned no data.');
}
const metrics = data.map(({ id, setupability, popularity }) => ({
id,
setupability: roundSetupability(setupability),
popularity: roundPopularity(popularity),
}));
await writeFile(outputFile, `${JSON.stringify(metrics, null, '\t')}\n`, 'utf8');
console.log(`Saved credential setupability data for ${metrics.length} credential types.`);
}
if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
main().catch((error) => {
console.error(error instanceof Error ? error.message : String(error));
process.exitCode = 1;
});
}

View File

@ -0,0 +1,14 @@
import assert from 'node:assert/strict';
import { describe, it } from 'node:test';
import { roundPopularity } from './fetch-credential-setupability.mjs';
describe('roundPopularity', () => {
it('preserves a missing value', () => {
assert.equal(roundPopularity(null), null);
});
it('rounds a value to one decimal place', () => {
assert.equal(roundPopularity(0.26), 0.3);
});
});

View File

@ -1,8 +1,17 @@
import { getOctokit } from '@actions/github';
import { execFileSync } from 'node:child_process';
import fs from 'node:fs';
import { createRequire } from 'node:module';
import path from 'node:path';
import semver from 'semver';
// Scripts that run through `run-workflow-script` have no node_modules: they
// get their client through `setOctokit` and never call the release helpers.
const actionsGithub = await import('@actions/github').catch((error) => {
if (error.code !== 'ERR_MODULE_NOT_FOUND') throw error;
return null;
});
const require = createRequire(import.meta.url);
/** @returns { typeof import('semver') } */
const loadSemver = () => require('semver');
export const CURRENT_MAJOR_VERSION = 2;
export const RELEASE_CANDIDATE_BRANCH_PREFIX = 'release-candidate/';
@ -48,8 +57,8 @@ export function pickHighestReleaseTag(tags) {
const versions = tags
.filter((t) => t.startsWith(RELEASE_PREFIX))
.map((t) => ({ tag: t, v: stripReleasePrefixes(t) }))
.filter(({ v }) => semver.valid(v))
.sort((a, b) => semver.rcompare(a.v, b.v));
.filter(({ v }) => loadSemver().valid(v))
.sort((a, b) => loadSemver().rcompare(a.v, b.v));
return /** @type { ReleaseVersion } */ (versions[0]?.tag) ?? null;
}
@ -128,7 +137,7 @@ export function resolveRcBranchForTrack(track) {
const releaseTag = pickHighestReleaseTag(tagsAtCommit);
if (!releaseTag) return null;
const parsed = semver.parse(stripReleasePrefixes(releaseTag));
const parsed = loadSemver().parse(stripReleasePrefixes(releaseTag));
if (!parsed) return null;
return `release-candidate/${parsed.major}.${parsed.minor}.x`;
@ -145,12 +154,12 @@ export function resolveRcBranchForTrack(track) {
* */
export function tagVersionInfoToReleaseCandidateBranchName(tagVersionInfo) {
const version = tagVersionInfo.version;
const majorVersion = semver.major(version);
const majorVersion = loadSemver().major(version);
if (majorVersion < CURRENT_MAJOR_VERSION) {
return `${majorVersion}.x`;
}
return `${RELEASE_CANDIDATE_BRANCH_PREFIX}${majorVersion}.${semver.minor(version)}.x`;
return `${RELEASE_CANDIDATE_BRANCH_PREFIX}${majorVersion}.${loadSemver().minor(version)}.x`;
}
/**
@ -248,9 +257,10 @@ export function trySh(cmd, args, opts = {}) {
* Append outputs to GITHUB_OUTPUT if available.
*
* @param {Record<string, string | boolean>} obj
* @param {NodeJS.ProcessEnv} [env] Environment to read GITHUB_OUTPUT from.
*/
export function writeGithubOutput(obj) {
const path = process.env.GITHUB_OUTPUT;
export function writeGithubOutput(obj, env = process.env) {
const path = env.GITHUB_OUTPUT;
if (!path) return;
const lines = Object.entries(obj)
@ -350,18 +360,37 @@ export function localRefExists(ref) {
return res.ok;
}
/** @type { GitHubInstance | null } */
let injectedOctokit = null;
/**
* Initializes octokit with GITHUB_TOKEN from env vars.
* Use a ready client instead of building one from GITHUB_TOKEN.
* `run-workflow-script` passes the client that actions/github-script provides.
*
* Also ensures the existence of useful environment variables.
* @param { GitHubInstance | null } octokit
*/
export function setOctokit(octokit) {
injectedOctokit = octokit;
}
/**
* Returns the octokit client and the current repository.
*
* Uses the injected client when set, otherwise builds one from GITHUB_TOKEN.
* */
export function initGithub() {
const token = ensureEnvVar('GITHUB_TOKEN');
const repoFullName = ensureEnvVar('GITHUB_REPOSITORY');
const [owner, repo] = repoFullName.split('/');
const octokit = getOctokit(token);
if (injectedOctokit) {
return { octokit: injectedOctokit, owner, repo };
}
if (!actionsGithub) {
throw new Error('No Octokit client: call setOctokit() or install the .github/scripts dependencies');
}
const token = ensureEnvVar('GITHUB_TOKEN');
const octokit = actionsGithub.getOctokit(token);
return {
octokit,
@ -385,6 +414,26 @@ export async function getPullRequestById(pullRequestId) {
return pullRequest.data;
}
/**
* Open PRs whose head is `headOwner:headBranch`, newest first.
*
* @param { string } headOwner Owner of the head repository (a fork owner or this org).
* @param { string } headBranch
* @returns { Promise<any[]> }
*/
export async function listOpenPullRequestsByHead(headOwner, headBranch) {
const { octokit, owner, repo } = initGithub();
const pullRequests = await octokit.rest.pulls.list({
owner,
repo,
state: 'open',
head: `${headOwner}:${headBranch}`,
});
return pullRequests.data;
}
/**
* Returns the set of files changed in a PR, including previous filenames for renames.
*
@ -425,14 +474,97 @@ export async function getPrFiles(pullRequestNumber) {
}
/**
* Post a PR comment, or update the existing one if a previous run already
* left one identified by the provided bot marker.
* Returns all reviews submitted on a PR, in submission order.
*
* @param { number } pullRequestNumber
* @param { string } body
* @param { string } botMarker
* @returns { Promise<Array<{ user: { login: string } | null, state: string, submitted_at?: string }>> }
* */
export async function getPrReviews(pullRequestNumber) {
const { octokit, owner, repo } = initGithub();
return await octokit.paginate(octokit.rest.pulls.listReviews, {
owner,
repo,
pull_number: pullRequestNumber,
per_page: 100,
});
}
/**
* Returns all events for a PR, including label changes and their actors.
*
* @param { number } pullRequestNumber
* @returns { Promise<Array<{ event: string, actor: { login: string } | null, label?: { name?: string } | null, created_at?: string }>> }
* */
export async function getPrEvents(pullRequestNumber) {
const { octokit, owner, repo } = initGithub();
return await octokit.paginate(octokit.rest.issues.listEvents, {
owner,
repo,
issue_number: pullRequestNumber,
per_page: 100,
});
}
/**
* Test whether a user is an active member of an org team.
*
* Team slugs are the part after the org, e.g. `catalysts` for
* `@n8n-io/catalysts`. Requires a token with org members read access
* (the plain GITHUB_TOKEN cannot read team membership). Returns false
* for pending invitations and for teams that do not exist.
*
* @param { string } teamSlug
* @param { string } username
* @returns { Promise<boolean> }
* */
export async function isTeamMember(teamSlug, username) {
const { octokit, owner } = initGithub();
try {
const { data } = await octokit.rest.teams.getMembershipForUserInOrg({
org: owner,
team_slug: teamSlug,
username,
});
return data.state === 'active';
} catch (ex) {
if (ex?.status === 404) return false;
throw ex;
}
}
/**
* Create (or overwrite) a commit status on the given SHA. A ruleset can list
* the status context as a required check to gate merges on it.
*
* @param { string } sha
* @param {{ state: 'success' | 'failure' | 'pending' | 'error', context: string, description: string, targetUrl?: string }} status
*/
export async function postOrUpdateComment(pullRequestNumber, body, botMarker) {
export async function setCommitStatus(sha, { state, context, description, targetUrl }) {
const { octokit, owner, repo } = initGithub();
await octokit.rest.repos.createCommitStatus({
owner,
repo,
sha,
state,
context,
// The API rejects descriptions longer than 140 characters.
description: description.length > 140 ? `${description.slice(0, 139)}…` : description,
target_url: targetUrl,
});
}
/**
* The comment a previous run left, found by its bot marker.
*
* @param { number } pullRequestNumber
* @param { string } botMarker
* @returns { Promise<{ id: number, body: string } | undefined> }
*/
export async function findCommentByMarker(pullRequestNumber, botMarker) {
const { octokit, owner, repo } = initGithub();
const comments = await octokit.paginate(octokit.rest.issues.listComments, {
@ -444,21 +576,49 @@ export async function postOrUpdateComment(pullRequestNumber, body, botMarker) {
const existing = comments.find((c) => c.body?.includes(botMarker));
return existing ? { id: existing.id, body: existing.body ?? '' } : undefined;
}
/**
* Overwrite a comment whose id is already known. A caller that edits the same
* comment repeatedly uses this instead of paginating every comment each time.
*
* @param { number } commentId
* @param { string } body
*/
export async function updateCommentById(commentId, body) {
const { octokit, owner, repo } = initGithub();
await octokit.rest.issues.updateComment({ owner, repo, comment_id: commentId, body });
}
/**
* Post a PR comment, or update the existing one if a previous run already
* left one identified by the provided bot marker.
*
* @param { number } pullRequestNumber
* @param { string } body
* @param { string } botMarker
* @returns { Promise<number> } the id of the comment it wrote
*/
export async function postOrUpdateComment(pullRequestNumber, body, botMarker) {
const { octokit, owner, repo } = initGithub();
const existing = await findCommentByMarker(pullRequestNumber, botMarker);
if (existing) {
await octokit.rest.issues.updateComment({
owner,
repo,
comment_id: existing.id,
body,
});
} else {
await octokit.rest.issues.createComment({
owner,
repo,
issue_number: pullRequestNumber,
body,
});
await updateCommentById(existing.id, body);
return existing.id;
}
const created = await octokit.rest.issues.createComment({
owner,
repo,
issue_number: pullRequestNumber,
body,
});
return created.data.id;
}
/**

View File

@ -1,5 +1,8 @@
import { afterEach, beforeEach, describe, it, mock } from 'node:test';
import assert from 'node:assert/strict';
import { existsSync, mkdtempSync, readFileSync } from 'node:fs';
import { tmpdir } from 'node:os';
import path from 'node:path';
/**
* Run these tests by running
@ -15,10 +18,113 @@ mock.module('@actions/github', {
},
});
const { postOrUpdateComment } = await import('./github-helpers.mjs');
const {
findCommentByMarker,
getPrEvents,
initGithub,
postOrUpdateComment,
setOctokit,
updateCommentById,
writeGithubOutput,
} = await import('./github-helpers.mjs');
const ORIGINAL_ENV = { ...process.env };
describe('getPrEvents', () => {
beforeEach(() => {
process.env.GITHUB_REPOSITORY = 'n8n-io/n8n';
});
afterEach(() => {
setOctokit(null);
process.env = { ...ORIGINAL_ENV };
});
it('paginates issue events for the pull request', async () => {
const events = [{ event: 'labeled', actor: { login: 'ana' } }];
const listEvents = {};
const paginate = mock.fn(async () => events);
setOctokit({ paginate, rest: { issues: { listEvents } } });
assert.equal(await getPrEvents(42), events);
assert.equal(paginate.mock.calls[0].arguments[0], listEvents);
assert.deepEqual(paginate.mock.calls[0].arguments[1], {
owner: 'n8n-io',
repo: 'n8n',
issue_number: 42,
per_page: 100,
});
});
});
describe('findCommentByMarker', () => {
beforeEach(() => {
process.env.GITHUB_TOKEN = 'token';
process.env.GITHUB_REPOSITORY = 'n8n-io/n8n';
});
afterEach(() => {
process.env = { ...ORIGINAL_ENV };
});
const withComments = (comments) => {
octokitImpl = () => ({
paginate: mock.fn(async () => comments),
rest: { issues: { listComments: {}, createComment: mock.fn(), updateComment: mock.fn() } },
});
};
it('returns the id and the body of the marked comment', async () => {
withComments([
{ id: 1, body: 'unrelated' },
{ id: 42, body: '<!-- marker -->\nold body' },
]);
assert.deepEqual(await findCommentByMarker(123, '<!-- marker -->'), {
id: 42,
body: '<!-- marker -->\nold body',
});
});
it('returns undefined when nothing carries the marker', async () => {
withComments([{ id: 1, body: 'unrelated' }]);
assert.equal(await findCommentByMarker(123, '<!-- marker -->'), undefined);
});
});
describe('updateCommentById', () => {
beforeEach(() => {
process.env.GITHUB_TOKEN = 'token';
process.env.GITHUB_REPOSITORY = 'n8n-io/n8n';
});
afterEach(() => {
process.env = { ...ORIGINAL_ENV };
});
// This is the whole point of the helper: a heartbeat must not paginate every
// comment on the PR once a minute.
it('edits the comment without listing any', async () => {
const updateComment = mock.fn(async () => {});
const paginate = mock.fn(async () => []);
octokitImpl = () => ({
paginate,
rest: { issues: { listComments: {}, createComment: mock.fn(), updateComment } },
});
await updateCommentById(42, 'next body');
assert.equal(paginate.mock.calls.length, 0);
assert.deepEqual(updateComment.mock.calls[0].arguments[0], {
owner: 'n8n-io',
repo: 'n8n',
comment_id: 42,
body: 'next body',
});
});
});
describe('postOrUpdateComment', () => {
beforeEach(() => {
process.env.GITHUB_TOKEN = 'token';
@ -30,7 +136,7 @@ describe('postOrUpdateComment', () => {
});
it('creates a new comment when no existing bot-marker comment is found', async () => {
const createComment = mock.fn(async () => {});
const createComment = mock.fn(async () => ({ data: { id: 7 } }));
const updateComment = mock.fn(async () => {});
const paginate = mock.fn(async () => [{ id: 1, body: 'unrelated comment' }]);
octokitImpl = () => ({
@ -44,8 +150,9 @@ describe('postOrUpdateComment', () => {
},
});
await postOrUpdateComment(123, 'new body', '<!-- marker -->');
const id = await postOrUpdateComment(123, 'new body', '<!-- marker -->');
assert.equal(id, 7);
assert.equal(updateComment.mock.calls.length, 0);
assert.equal(createComment.mock.calls.length, 1);
assert.deepEqual(createComment.mock.calls[0].arguments[0], {
@ -63,7 +170,7 @@ describe('postOrUpdateComment', () => {
});
it('updates the existing comment when a bot-marker comment is found', async () => {
const createComment = mock.fn(async () => {});
const createComment = mock.fn(async () => ({ data: { id: 7 } }));
const updateComment = mock.fn(async () => {});
octokitImpl = () => ({
paginate: mock.fn(async () => [{ id: 42, body: '<!-- marker -->\nold body' }]),
@ -76,8 +183,9 @@ describe('postOrUpdateComment', () => {
},
});
await postOrUpdateComment(123, 'updated body', '<!-- marker -->');
const id = await postOrUpdateComment(123, 'updated body', '<!-- marker -->');
assert.equal(id, 42);
assert.equal(createComment.mock.calls.length, 0);
assert.equal(updateComment.mock.calls.length, 1);
assert.deepEqual(updateComment.mock.calls[0].arguments[0], {
@ -88,3 +196,76 @@ describe('postOrUpdateComment', () => {
});
});
});
describe('writeGithubOutput', () => {
let outputPath;
beforeEach(() => {
outputPath = path.join(mkdtempSync(path.join(tmpdir(), 'gh-output-')), 'output');
});
afterEach(() => {
process.env = { ...ORIGINAL_ENV };
});
it('appends key=value lines to GITHUB_OUTPUT from the given env', () => {
writeGithubOutput(
{ conflict_pr: 'https://example.test/pr/1', ok: true },
{ GITHUB_OUTPUT: outputPath },
);
assert.equal(
readFileSync(outputPath, 'utf8'),
'conflict_pr=https://example.test/pr/1\nok=true\n',
);
});
it('falls back to process.env when no env is given', () => {
process.env.GITHUB_OUTPUT = outputPath;
writeGithubOutput({ target_branches: 'a,b' });
assert.equal(readFileSync(outputPath, 'utf8'), 'target_branches=a,b\n');
});
it('does nothing when GITHUB_OUTPUT is unset', () => {
writeGithubOutput({ ignored: 'value' }, {});
assert.equal(existsSync(outputPath), false);
});
});
describe('initGithub', () => {
beforeEach(() => {
process.env.GITHUB_REPOSITORY = 'n8n-io/n8n';
delete process.env.GITHUB_TOKEN;
});
afterEach(() => {
setOctokit(null);
process.env = { ...ORIGINAL_ENV };
});
it('uses the injected client and needs no token', () => {
const injected = { rest: {} };
setOctokit(injected);
const result = initGithub();
assert.equal(result.octokit, injected);
assert.equal(result.owner, 'n8n-io');
assert.equal(result.repo, 'n8n');
});
it('builds a client from GITHUB_TOKEN when nothing is injected', () => {
const built = { rest: {} };
octokitImpl = () => built;
process.env.GITHUB_TOKEN = 'token';
assert.equal(initGithub().octokit, built);
});
it('requires GITHUB_TOKEN when nothing is injected', () => {
assert.throws(() => initGithub(), /GITHUB_TOKEN/);
});
});

49
.github/scripts/glob.mjs vendored Normal file
View File

@ -0,0 +1,49 @@
/**
* Minimal glob matcher for changed-file paths.
*
* Supports `**` (any number of path segments), `*` (any run of characters
* inside one segment) and literal text. Dot segments match like any other,
* so patterns reach files under `.github/`. Only node builtins, so scripts
* that run through `run-workflow-script` can use it without an install.
*/
/** @type { Map<string, RegExp> } */
const cache = new Map();
/**
* @param { string } pattern
* @returns { RegExp }
*/
export function globToRegExp(pattern) {
const cached = cache.get(pattern);
if (cached) return cached;
let source = '';
const segments = pattern.split('/');
for (const [index, segment] of segments.entries()) {
const last = index === segments.length - 1;
if (segment === '**') {
// `**/` matches zero or more segments; a trailing `**` matches the rest.
source += last ? '.*' : '(?:[^/]+/)*';
continue;
}
source += segment
.split('*')
.map((literal) => literal.replace(/[.+?^${}()|[\]\\]/g, '\\$&'))
.join('[^/]*');
if (!last) source += '/';
}
const regExp = new RegExp(`^${source}$`);
cache.set(pattern, regExp);
return regExp;
}
/**
* @param { string } file Repository-relative path with `/` separators.
* @param { string } pattern
* @returns { boolean }
*/
export function matchesGlob(file, pattern) {
return globToRegExp(pattern).test(file);
}

81
.github/scripts/glob.test.mjs vendored Normal file
View File

@ -0,0 +1,81 @@
import { describe, it } from 'node:test';
import assert from 'node:assert/strict';
import { minimatch } from 'minimatch';
import { matchesGlob } from './glob.mjs';
import { EXCLUDE_PATTERNS } from './quality/check-pr-size.mjs';
/**
* Run with:
* node --test --experimental-test-module-mocks ./.github/scripts/glob.test.mjs
* */
describe('matchesGlob', () => {
it('matches a file extension anywhere in the tree', () => {
assert.equal(matchesGlob('a.test.ts', '**/*.test.ts'), true);
assert.equal(matchesGlob('packages/cli/src/a.test.ts', '**/*.test.ts'), true);
assert.equal(matchesGlob('packages/cli/src/a.ts', '**/*.test.ts'), false);
});
it('matches dot segments', () => {
assert.equal(matchesGlob('.github/scripts/a.test.mjs', '**/*.test.mjs'), true);
assert.equal(matchesGlob('.github/scripts/glob.mjs', '**/*.test.mjs'), false);
});
it('matches a directory at any depth', () => {
assert.equal(matchesGlob('test/a.ts', '**/test/**'), true);
assert.equal(matchesGlob('packages/x/test/deep/a.ts', '**/test/**'), true);
assert.equal(matchesGlob('packages/x/tests/a.ts', '**/test/**'), false);
assert.equal(matchesGlob('packages/x/testing/a.ts', '**/test/**'), false);
});
it('matches a prefix pattern', () => {
assert.equal(matchesGlob('packages/quality/testing/playwright/a.ts', 'packages/quality/**'), true);
assert.equal(matchesGlob('packages/quality', 'packages/quality/**'), false);
assert.equal(matchesGlob('packages/cli/a.ts', 'packages/quality/**'), false);
});
it('matches a literal path only exactly', () => {
assert.equal(matchesGlob('pnpm-lock.yaml', 'pnpm-lock.yaml'), true);
assert.equal(matchesGlob('packages/cli/pnpm-lock.yaml', 'pnpm-lock.yaml'), false);
assert.equal(matchesGlob('pnpm-lockXyaml', 'pnpm-lock.yaml'), false);
});
it('keeps `*` inside one segment', () => {
assert.equal(matchesGlob('a/b.snap', '**/*.snap'), true);
assert.equal(matchesGlob('a/b/c.md', 'a/*.md'), false);
});
it('agrees with minimatch on the PR size patterns', () => {
const files = [
'packages/cli/src/a.ts',
'packages/cli/src/a.test.ts',
'packages/cli/test/a.ts',
'packages/cli/__tests__/a.ts',
'packages/cli/__snapshots__/a.ts.snap',
'packages/nodes-base/nodes/Foo/test/fixtures/a.json',
'packages/frontend/editor-ui/src/__mocks__/a.ts',
'packages/quality/testing/playwright/a.ts',
'packages/quality',
'.github/scripts/glob.test.mjs',
'.github/workflows/ci.yml',
'a/.hidden/b.spec.js',
'pnpm-lock.yaml',
'packages/cli/pnpm-lock.yaml',
'README.md',
'docs/a.mdx',
'src/tests.ts',
'src/test.ts',
'src/testing/a.ts',
];
for (const pattern of EXCLUDE_PATTERNS) {
for (const file of files) {
assert.equal(
matchesGlob(file, pattern),
minimatch(file, pattern, { dot: true }),
`${file} against ${pattern}`,
);
}
}
});
});

105
.github/scripts/nightly-sbom-context.mjs vendored Normal file
View File

@ -0,0 +1,105 @@
#!/usr/bin/env node
import { execFileSync } from 'node:child_process';
import { appendFileSync } from 'node:fs';
const MAX_AGE_MS = 6 * 60 * 60 * 1000;
const POLL_INTERVAL_MS = 5 * 60 * 1000;
const POLL_ATTEMPTS = 25;
function writeOutput(name, value) {
if (!process.env.GITHUB_OUTPUT) throw new Error('GITHUB_OUTPUT is not set.');
appendFileSync(process.env.GITHUB_OUTPUT, `${name}=${value}\n`);
}
function getLatestScheduledRun() {
const output = execFileSync(
'gh',
[
'run',
'list',
'--repo',
process.env.GITHUB_REPOSITORY,
'--workflow',
'docker-build-push.yml',
'--event',
'schedule',
'--limit',
'1',
'--json',
'conclusion,createdAt,headSha,status',
],
{ encoding: 'utf8' },
);
return JSON.parse(output)[0];
}
function isFullSha(value) {
return /^[0-9a-f]{40}$/.test(value ?? '');
}
async function resolveSource() {
if (process.env.GITHUB_EVENT_NAME === 'workflow_dispatch') {
if (!isFullSha(process.env.GITHUB_SHA)) throw new Error('GITHUB_SHA is not a full commit SHA.');
writeOutput('source_sha', process.env.GITHUB_SHA);
return;
}
for (let attempt = 1; attempt <= POLL_ATTEMPTS; attempt++) {
const run = getLatestScheduledRun();
const age = run?.createdAt ? Date.now() - Date.parse(run.createdAt) : MAX_AGE_MS + 1;
const isCurrent = age >= 0 && age <= MAX_AGE_MS;
if (
run?.status === 'completed' &&
run.conclusion === 'success' &&
isFullSha(run.headSha) &&
isCurrent
) {
writeOutput('source_sha', run.headSha);
return;
}
if (run?.status === 'completed' && run.createdAt && isCurrent) {
throw new Error(
`The current scheduled Docker build completed with conclusion '${run.conclusion}'.`,
);
}
if (attempt < POLL_ATTEMPTS) {
console.log(
`Waiting for the current scheduled Docker build (attempt ${attempt} of ${POLL_ATTEMPTS}).`,
);
await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL_MS));
}
}
throw new Error('No successful scheduled Docker build appeared within two hours.');
}
function resolveImageTag() {
if (process.env.GITHUB_EVENT_NAME === 'workflow_dispatch') {
writeOutput('image_tag', 'nightly');
return;
}
const shortSha = execFileSync('git', ['rev-parse', '--short=7', 'HEAD'], {
encoding: 'utf8',
}).trim();
writeOutput('image_tag', `nightly-${shortSha}`);
}
const command = process.argv[2];
try {
if (command === 'resolve-source') {
await resolveSource();
} else if (command === 'resolve-image-tag') {
resolveImageTag();
} else {
throw new Error('Expected resolve-source or resolve-image-tag.');
}
} catch (error) {
console.error(`::error::${error.message}`);
process.exit(1);
}

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