diff --git a/docs/generated/postgres-schema/README.md b/docs/generated/postgres-schema/README.md
index 52084123841..db8fb29e8c6 100644
--- a/docs/generated/postgres-schema/README.md
+++ b/docs/generated/postgres-schema/README.md
@@ -9,7 +9,7 @@ Auto-generated from the PostgreSQL migrations in @n8n/db. Do not edit by hand.
| Name | Columns | Comment | Type |
| ---- | ------- | ------- | ---- |
| [public.activity_event](public.activity_event.md) | 11 | | BASE TABLE |
-| [public.agent_background_job](public.agent_background_job.md) | 20 | | BASE TABLE |
+| [public.agent_background_job](public.agent_background_job.md) | 23 | | BASE TABLE |
| [public.agent_budget_applied_call](public.agent_budget_applied_call.md) | 2 | | BASE TABLE |
| [public.agent_budget_spend](public.agent_budget_spend.md) | 4 | | BASE TABLE |
| [public.agent_channel_status](public.agent_channel_status.md) | 11 | | BASE TABLE |
@@ -23,7 +23,7 @@ Auto-generated from the PostgreSQL migrations in @n8n/db. Do not edit by hand.
| [public.agent_eval_run](public.agent_eval_run.md) | 14 | | BASE TABLE |
| [public.agent_execution](public.agent_execution.md) | 23 | | BASE TABLE |
| [public.agent_execution_message_links](public.agent_execution_message_links.md) | 5 | | BASE TABLE |
-| [public.agent_execution_threads](public.agent_execution_threads.md) | 19 | | BASE TABLE |
+| [public.agent_execution_threads](public.agent_execution_threads.md) | 20 | | BASE TABLE |
| [public.agent_files](public.agent_files.md) | 10 | | BASE TABLE |
| [public.agent_history](public.agent_history.md) | 9 | | BASE TABLE |
| [public.agent_message_queue](public.agent_message_queue.md) | 10 | | BASE TABLE |
@@ -200,6 +200,8 @@ erDiagram
"public.activity_event" }o--o| "public.user" : "FOREIGN KEY (#quot;userId#quot;) REFERENCES #quot;user#quot;(id) ON DELETE SET NULL"
"public.activity_event" }o--|| "public.project" : "FOREIGN KEY (#quot;projectId#quot;) REFERENCES project(id) ON DELETE CASCADE"
"public.agent_background_job" }o--|| "public.agents" : "FOREIGN KEY (#quot;parentAgentId#quot;) REFERENCES agents(id) ON DELETE CASCADE"
+"public.agent_background_job" }o--o| "public.agent_execution" : "FOREIGN KEY (#quot;sourceExecutionId#quot;) REFERENCES agent_execution(id) ON DELETE SET NULL"
+"public.agent_background_job" }o--o| "public.agent_plan" : "FOREIGN KEY (#quot;planId#quot;) REFERENCES agent_plan(id) ON DELETE SET NULL"
"public.agent_channel_status" }o--|| "public.agents" : "FOREIGN KEY (#quot;agentId#quot;) REFERENCES agents(id) ON DELETE CASCADE"
"public.agent_chat_attachments" }o--|| "public.project" : "FOREIGN KEY (#quot;projectId#quot;) REFERENCES project(id) ON DELETE CASCADE"
"public.agent_chat_attachments" }o--o| "public.agents" : "FOREIGN KEY (#quot;agentId#quot;) REFERENCES agents(id) ON DELETE CASCADE"
@@ -474,8 +476,11 @@ erDiagram
varchar_255_ parentResourceId
varchar_128_ parentThreadId
uuid pauseRequestId
+ uuid planId FK
+ uuid planTaskId
text result
timestamp_3__with_time_zone settledAt
+ varchar_36_ sourceExecutionId FK
varchar_16_ status
varchar_36_ subAgentId
timestamp_3__with_time_zone timeoutAt
@@ -643,6 +648,7 @@ erDiagram
varchar_255_ projectId FK
integer sessionNumber
varchar_32_ taskId
+ json taskStop
varchar_36_ taskVersionId FK
varchar_255_ title
integer totalCompletionTokens
diff --git a/docs/generated/postgres-schema/public.agent_background_job.md b/docs/generated/postgres-schema/public.agent_background_job.md
index 86a106e09be..3b05fb80d2d 100644
--- a/docs/generated/postgres-schema/public.agent_background_job.md
+++ b/docs/generated/postgres-schema/public.agent_background_job.md
@@ -16,8 +16,11 @@
| parentResourceId | varchar(255) | | false | | | Memory resource of the parent agent run |
| parentThreadId | varchar(128) | | false | | | |
| pauseRequestId | uuid | | true | | | Groups jobs stopped by one user request |
+| planId | uuid | | true | | [public.agent_plan](public.agent_plan.md) | Plan that owns this work |
+| planTaskId | uuid | | true | | | Task ID in the saved plan document |
| result | text | | true | | | Final answer of a settled sub-agent job |
| settledAt | timestamp(3) with time zone | | true | | | |
+| sourceExecutionId | varchar(36) | | true | | [public.agent_execution](public.agent_execution.md) | Execution that dispatched this job |
| status | varchar(16) | | false | | | running: child works; suspended: child awaits approval; paused: user stopped the child; completed, failed, cancelled: terminal |
| subAgentId | varchar(36) | | true | | | Sub-agent jobs only |
| timeoutAt | timestamp(3) with time zone | | true | | | When reconciliation fails the job as timed out; NULL means no timeout |
@@ -31,6 +34,8 @@
| ---- | ---- | ---------- |
| CHK_agent_background_job_kind | CHECK | CHECK (((kind)::text = ANY ((ARRAY['subagent'::character varying, 'workflow'::character varying])::text[]))) |
| CHK_agent_background_job_status | CHECK | CHECK (((status)::text = ANY ((ARRAY['running'::character varying, 'suspended'::character varying, 'paused'::character varying, 'completed'::character varying, 'failed'::character varying, 'cancelled'::character varying])::text[]))) |
+| FK_4db3b7ebea4ed4f69649982018c | FOREIGN KEY | FOREIGN KEY ("sourceExecutionId") REFERENCES agent_execution(id) ON DELETE SET NULL |
+| FK_ce131bff629d89015893e350e9c | FOREIGN KEY | FOREIGN KEY ("planId") REFERENCES agent_plan(id) ON DELETE SET NULL |
| FK_d46c6f00730c2ef8bcb6ee24b67 | FOREIGN KEY | FOREIGN KEY ("parentAgentId") REFERENCES agents(id) ON DELETE CASCADE |
| PK_6e0db58281aa2b4c956dc0d58e9 | PRIMARY KEY | PRIMARY KEY (id) |
| agent_background_job_createdAt_not_null | n | NOT NULL "createdAt" |
@@ -51,6 +56,8 @@
| IDX_93d62baabe9858816b5adafb44 | CREATE INDEX "IDX_93d62baabe9858816b5adafb44" ON public.agent_background_job USING btree ("parentThreadId", status) |
| IDX_agent_background_job_childExecutionId | CREATE UNIQUE INDEX "IDX_agent_background_job_childExecutionId" ON public.agent_background_job USING btree ("childExecutionId") WHERE ("childExecutionId" IS NOT NULL) |
| IDX_agent_background_job_parentThreadId | CREATE INDEX "IDX_agent_background_job_parentThreadId" ON public.agent_background_job USING btree ("parentThreadId") WHERE (((status)::text <> 'running'::text) AND ("notifiedAt" IS NULL)) |
+| IDX_agent_background_job_planId_planTaskId | CREATE INDEX "IDX_agent_background_job_planId_planTaskId" ON public.agent_background_job USING btree ("planId", "planTaskId") |
+| IDX_agent_background_job_sourceExecutionId | CREATE INDEX "IDX_agent_background_job_sourceExecutionId" ON public.agent_background_job USING btree ("sourceExecutionId") |
| IDX_agent_background_job_timeoutAt | CREATE INDEX "IDX_agent_background_job_timeoutAt" ON public.agent_background_job USING btree ("timeoutAt") WHERE ((status)::text = ANY ((ARRAY['running'::character varying, 'suspended'::character varying])::text[])) |
| IDX_d46c6f00730c2ef8bcb6ee24b6 | CREATE INDEX "IDX_d46c6f00730c2ef8bcb6ee24b6" ON public.agent_background_job USING btree ("parentAgentId") |
| IDX_e43e630272995a93dfeb94ab3e | CREATE INDEX "IDX_e43e630272995a93dfeb94ab3e" ON public.agent_background_job USING btree ("settledAt") |
@@ -62,6 +69,8 @@
erDiagram
"public.agent_background_job" }o--|| "public.agents" : "FOREIGN KEY (#quot;parentAgentId#quot;) REFERENCES agents(id) ON DELETE CASCADE"
+"public.agent_background_job" }o--o| "public.agent_plan" : "FOREIGN KEY (#quot;planId#quot;) REFERENCES agent_plan(id) ON DELETE SET NULL"
+"public.agent_background_job" }o--o| "public.agent_execution" : "FOREIGN KEY (#quot;sourceExecutionId#quot;) REFERENCES agent_execution(id) ON DELETE SET NULL"
"public.agent_background_job" {
varchar_36_ childExecutionId
@@ -76,8 +85,11 @@ erDiagram
varchar_255_ parentResourceId
varchar_128_ parentThreadId
uuid pauseRequestId
+ uuid planId FK
+ uuid planTaskId
text result
timestamp_3__with_time_zone settledAt
+ varchar_36_ sourceExecutionId FK
varchar_16_ status
varchar_36_ subAgentId
timestamp_3__with_time_zone timeoutAt
@@ -102,6 +114,41 @@ erDiagram
timestamp_3__with_time_zone updatedAt
varchar_36_ versionId
}
+"public.agent_plan" {
+ timestamp_3__with_time_zone closedAt
+ timestamp_3__with_time_zone createdAt
+ json data
+ integer formatVersion
+ uuid id
+ integer revision
+ varchar_128_ threadId FK
+ timestamp_3__with_time_zone updatedAt
+}
+"public.agent_execution" {
+ boolean acceptsSteering
+ json attachments
+ json author
+ integer completionTokens
+ double_precision cost
+ timestamp_3__with_time_zone createdAt
+ integer duration
+ text error
+ json failureSummary
+ varchar_16_ hitlStatus
+ varchar_36_ id
+ varchar_255_ model
+ integer promptTokens
+ varchar_32_ source
+ timestamp_3__with_time_zone startedAt
+ varchar_16_ status
+ timestamp_3__with_time_zone stoppedAt
+ varchar_2_ storedAt
+ varchar_128_ threadId FK
+ json timeline
+ integer totalTokens
+ timestamp_3__with_time_zone updatedAt
+ text userMessage
+}
```
---
diff --git a/docs/generated/postgres-schema/public.agent_execution.md b/docs/generated/postgres-schema/public.agent_execution.md
index 7094f46732d..594ae320762 100644
--- a/docs/generated/postgres-schema/public.agent_execution.md
+++ b/docs/generated/postgres-schema/public.agent_execution.md
@@ -14,7 +14,7 @@
| error | text | | true | | | |
| failureSummary | json | | true | | | Execution failure projection as {count, latest} for session list queries |
| hitlStatus | varchar(16) | | true | | | |
-| id | varchar(36) | | false | [public.agent_execution_message_links](public.agent_execution_message_links.md) [public.agent_message_queue](public.agent_message_queue.md) | | |
+| id | varchar(36) | | false | [public.agent_background_job](public.agent_background_job.md) [public.agent_execution_message_links](public.agent_execution_message_links.md) [public.agent_message_queue](public.agent_message_queue.md) | | |
| model | varchar(255) | | true | | | |
| promptTokens | integer | | true | | | |
| source | varchar(32) | | true | | | |
@@ -59,6 +59,7 @@
```mermaid
erDiagram
+"public.agent_background_job" }o--o| "public.agent_execution" : "FOREIGN KEY (#quot;sourceExecutionId#quot;) REFERENCES agent_execution(id) ON DELETE SET NULL"
"public.agent_execution_message_links" }o--|| "public.agent_execution" : "FOREIGN KEY (#quot;executionId#quot;) REFERENCES agent_execution(id) ON DELETE CASCADE"
"public.agent_message_queue" }o--o| "public.agent_execution" : "FOREIGN KEY (#quot;executionId#quot;) REFERENCES agent_execution(id)"
"public.agent_message_queue" }o--o| "public.agent_execution" : "FOREIGN KEY (#quot;steeringExecutionId#quot;) REFERENCES agent_execution(id)"
@@ -89,6 +90,31 @@ erDiagram
timestamp_3__with_time_zone updatedAt
text userMessage
}
+"public.agent_background_job" {
+ varchar_36_ childExecutionId
+ varchar_128_ childThreadId
+ timestamp_3__with_time_zone createdAt
+ text error
+ varchar_36_ id
+ varchar_16_ kind
+ timestamp_3__with_time_zone notifiedAt
+ varchar_36_ parentAgentId FK
+ varchar_64_ parentPrincipalHash
+ varchar_255_ parentResourceId
+ varchar_128_ parentThreadId
+ uuid pauseRequestId
+ uuid planId FK
+ uuid planTaskId
+ text result
+ timestamp_3__with_time_zone settledAt
+ varchar_36_ sourceExecutionId FK
+ varchar_16_ status
+ varchar_36_ subAgentId
+ timestamp_3__with_time_zone timeoutAt
+ varchar_255_ title
+ timestamp_3__with_time_zone updatedAt
+ varchar_36_ workflowId
+}
"public.agent_execution_message_links" {
timestamp_3__with_time_zone createdAt
varchar_6_ direction
@@ -121,6 +147,7 @@ erDiagram
varchar_255_ projectId FK
integer sessionNumber
varchar_32_ taskId
+ json taskStop
varchar_36_ taskVersionId FK
varchar_255_ title
integer totalCompletionTokens
diff --git a/docs/generated/postgres-schema/public.agent_execution_threads.md b/docs/generated/postgres-schema/public.agent_execution_threads.md
index f63f8e89a50..f5e559f9e7e 100644
--- a/docs/generated/postgres-schema/public.agent_execution_threads.md
+++ b/docs/generated/postgres-schema/public.agent_execution_threads.md
@@ -16,6 +16,7 @@
| projectId | varchar(255) | | false | | [public.project](public.project.md) | |
| sessionNumber | integer | 0 | false | | | |
| taskId | varchar(32) | | true | | | Published task ID that triggered this session; not an FK because published runs can outlive draft task definition rows |
+| taskStop | json | | true | | | Latest task stop boundary and unresolved stop errors |
| taskVersionId | varchar(36) | | true | | [public.agent_history](public.agent_history.md) | Published agent_history version that supplied the task snapshot |
| title | varchar(255) | | true | | | |
| totalCompletionTokens | integer | 0 | false | | | |
@@ -84,6 +85,7 @@ erDiagram
varchar_255_ projectId FK
integer sessionNumber
varchar_32_ taskId
+ json taskStop
varchar_36_ taskVersionId FK
varchar_255_ title
integer totalCompletionTokens
diff --git a/docs/generated/postgres-schema/public.agent_history.md b/docs/generated/postgres-schema/public.agent_history.md
index efc11c1b7ba..b542bd06813 100644
--- a/docs/generated/postgres-schema/public.agent_history.md
+++ b/docs/generated/postgres-schema/public.agent_history.md
@@ -104,6 +104,7 @@ erDiagram
varchar_255_ projectId FK
integer sessionNumber
varchar_32_ taskId
+ json taskStop
varchar_36_ taskVersionId FK
varchar_255_ title
integer totalCompletionTokens
diff --git a/docs/generated/postgres-schema/public.agent_message_queue.md b/docs/generated/postgres-schema/public.agent_message_queue.md
index a2d4b104d97..ec316b58998 100644
--- a/docs/generated/postgres-schema/public.agent_message_queue.md
+++ b/docs/generated/postgres-schema/public.agent_message_queue.md
@@ -118,6 +118,7 @@ erDiagram
varchar_255_ projectId FK
integer sessionNumber
varchar_32_ taskId
+ json taskStop
varchar_36_ taskVersionId FK
varchar_255_ title
integer totalCompletionTokens
diff --git a/docs/generated/postgres-schema/public.agent_plan.md b/docs/generated/postgres-schema/public.agent_plan.md
index 91b7c0f4be0..426bd998699 100644
--- a/docs/generated/postgres-schema/public.agent_plan.md
+++ b/docs/generated/postgres-schema/public.agent_plan.md
@@ -8,7 +8,7 @@
| createdAt | timestamp(3) with time zone | CURRENT_TIMESTAMP(3) | false | | | |
| data | json | | false | | | Opaque plan document |
| formatVersion | integer | | false | | | Version of the plan JSON format |
-| id | uuid | | false | [public.agent_plan_history](public.agent_plan_history.md) | | Plan ID generated by the caller |
+| id | uuid | | false | [public.agent_background_job](public.agent_background_job.md) [public.agent_plan_history](public.agent_plan_history.md) | | Plan ID generated by the caller |
| revision | integer | 1 | false | | | Current document revision |
| threadId | varchar(128) | | false | | [public.agent_execution_threads](public.agent_execution_threads.md) | Session that owns the plan |
| updatedAt | timestamp(3) with time zone | CURRENT_TIMESTAMP(3) | false | | | |
@@ -42,6 +42,7 @@
```mermaid
erDiagram
+"public.agent_background_job" }o--o| "public.agent_plan" : "FOREIGN KEY (#quot;planId#quot;) REFERENCES agent_plan(id) ON DELETE SET NULL"
"public.agent_plan_history" }o--|| "public.agent_plan" : "FOREIGN KEY (#quot;planId#quot;) REFERENCES agent_plan(id) ON DELETE CASCADE"
"public.agent_plan" }o--|| "public.agent_execution_threads" : "FOREIGN KEY (#quot;threadId#quot;) REFERENCES agent_execution_threads(id) ON DELETE CASCADE"
@@ -55,6 +56,31 @@ erDiagram
varchar_128_ threadId FK
timestamp_3__with_time_zone updatedAt
}
+"public.agent_background_job" {
+ varchar_36_ childExecutionId
+ varchar_128_ childThreadId
+ timestamp_3__with_time_zone createdAt
+ text error
+ varchar_36_ id
+ varchar_16_ kind
+ timestamp_3__with_time_zone notifiedAt
+ varchar_36_ parentAgentId FK
+ varchar_64_ parentPrincipalHash
+ varchar_255_ parentResourceId
+ varchar_128_ parentThreadId
+ uuid pauseRequestId
+ uuid planId FK
+ uuid planTaskId
+ text result
+ timestamp_3__with_time_zone settledAt
+ varchar_36_ sourceExecutionId FK
+ varchar_16_ status
+ varchar_36_ subAgentId
+ timestamp_3__with_time_zone timeoutAt
+ varchar_255_ title
+ timestamp_3__with_time_zone updatedAt
+ varchar_36_ workflowId
+}
"public.agent_plan_history" {
timestamp_3__with_time_zone closedAt
timestamp_3__with_time_zone createdAt
@@ -76,6 +102,7 @@ erDiagram
varchar_255_ projectId FK
integer sessionNumber
varchar_32_ taskId
+ json taskStop
varchar_36_ taskVersionId FK
varchar_255_ title
integer totalCompletionTokens
diff --git a/docs/generated/postgres-schema/public.agent_thread_grants.md b/docs/generated/postgres-schema/public.agent_thread_grants.md
index 2bcafbbd9e7..83602d84eb7 100644
--- a/docs/generated/postgres-schema/public.agent_thread_grants.md
+++ b/docs/generated/postgres-schema/public.agent_thread_grants.md
@@ -52,6 +52,7 @@ erDiagram
varchar_255_ projectId FK
integer sessionNumber
varchar_32_ taskId
+ json taskStop
varchar_36_ taskVersionId FK
varchar_255_ title
integer totalCompletionTokens
diff --git a/docs/generated/postgres-schema/public.agents.md b/docs/generated/postgres-schema/public.agents.md
index 3716bbeb0ca..c04a1115954 100644
--- a/docs/generated/postgres-schema/public.agents.md
+++ b/docs/generated/postgres-schema/public.agents.md
@@ -119,8 +119,11 @@ erDiagram
varchar_255_ parentResourceId
varchar_128_ parentThreadId
uuid pauseRequestId
+ uuid planId FK
+ uuid planTaskId
text result
timestamp_3__with_time_zone settledAt
+ varchar_36_ sourceExecutionId FK
varchar_16_ status
varchar_36_ subAgentId
timestamp_3__with_time_zone timeoutAt
@@ -202,6 +205,7 @@ erDiagram
varchar_255_ projectId FK
integer sessionNumber
varchar_32_ taskId
+ json taskStop
varchar_36_ taskVersionId FK
varchar_255_ title
integer totalCompletionTokens
diff --git a/docs/generated/postgres-schema/public.project.md b/docs/generated/postgres-schema/public.project.md
index 7f114875982..04f8b2aeead 100644
--- a/docs/generated/postgres-schema/public.project.md
+++ b/docs/generated/postgres-schema/public.project.md
@@ -130,6 +130,7 @@ erDiagram
varchar_255_ projectId FK
integer sessionNumber
varchar_32_ taskId
+ json taskStop
varchar_36_ taskVersionId FK
varchar_255_ title
integer totalCompletionTokens
diff --git a/docs/generated/postgres-schema/public.user.md b/docs/generated/postgres-schema/public.user.md
index 24f19c09860..341031e23ea 100644
--- a/docs/generated/postgres-schema/public.user.md
+++ b/docs/generated/postgres-schema/public.user.md
@@ -178,6 +178,7 @@ erDiagram
varchar_255_ projectId FK
integer sessionNumber
varchar_32_ taskId
+ json taskStop
varchar_36_ taskVersionId FK
varchar_255_ title
integer totalCompletionTokens
diff --git a/docs/generated/sqlite-schema/README.md b/docs/generated/sqlite-schema/README.md
index af3996f06fa..c88487f119d 100644
--- a/docs/generated/sqlite-schema/README.md
+++ b/docs/generated/sqlite-schema/README.md
@@ -9,7 +9,7 @@ Auto-generated from the SQLite migrations in @n8n/db. Do not edit by hand.
| Name | Columns | Comment | Type |
| ---- | ------- | ------- | ---- |
| [activity_event](activity_event.md) | 11 | | table |
-| [agent_background_job](agent_background_job.md) | 20 | | table |
+| [agent_background_job](agent_background_job.md) | 23 | | table |
| [agent_budget_applied_call](agent_budget_applied_call.md) | 2 | | table |
| [agent_budget_spend](agent_budget_spend.md) | 4 | | table |
| [agent_channel_status](agent_channel_status.md) | 11 | | table |
@@ -23,7 +23,7 @@ Auto-generated from the SQLite migrations in @n8n/db. Do not edit by hand.
| [agent_eval_run](agent_eval_run.md) | 14 | | table |
| [agent_execution](agent_execution.md) | 23 | | table |
| [agent_execution_message_links](agent_execution_message_links.md) | 5 | | table |
-| [agent_execution_threads](agent_execution_threads.md) | 19 | | table |
+| [agent_execution_threads](agent_execution_threads.md) | 20 | | table |
| [agent_files](agent_files.md) | 10 | | table |
| [agent_history](agent_history.md) | 9 | | table |
| [agent_message_queue](agent_message_queue.md) | 10 | | table |
@@ -181,6 +181,8 @@ erDiagram
"activity_event" }o--|| "project" : "FOREIGN KEY (projectId) REFERENCES project (id) ON UPDATE NO ACTION ON DELETE CASCADE MATCH NONE"
"activity_event" }o--o| "user" : "FOREIGN KEY (userId) REFERENCES user (id) ON UPDATE NO ACTION ON DELETE SET NULL MATCH NONE"
+"agent_background_job" }o--o| "agent_plan" : "FOREIGN KEY (planId) REFERENCES agent_plan (id) ON UPDATE NO ACTION ON DELETE SET NULL MATCH NONE"
+"agent_background_job" }o--o| "agent_execution" : "FOREIGN KEY (sourceExecutionId) REFERENCES agent_execution (id) ON UPDATE NO ACTION ON DELETE SET NULL MATCH NONE"
"agent_background_job" }o--|| "agents" : "FOREIGN KEY (parentAgentId) REFERENCES agents (id) ON UPDATE NO ACTION ON DELETE CASCADE MATCH NONE"
"agent_channel_status" |o--|| "agents" : "FOREIGN KEY (agentId) REFERENCES agents (id) ON UPDATE NO ACTION ON DELETE CASCADE MATCH NONE"
"agent_chat_attachments" }o--|| "project" : "FOREIGN KEY (projectId) REFERENCES project (id) ON UPDATE NO ACTION ON DELETE CASCADE MATCH NONE"
@@ -460,8 +462,11 @@ erDiagram
varchar_255_ parentResourceId
varchar_128_ parentThreadId
varchar pauseRequestId
+ varchar planId FK
+ varchar planTaskId
TEXT result
datetime_3_ settledAt
+ varchar_36_ sourceExecutionId FK
varchar_16_ status
varchar_36_ subAgentId
datetime_3_ timeoutAt
@@ -629,6 +634,7 @@ erDiagram
varchar_255_ projectId FK
INTEGER sessionNumber
varchar_32_ taskId
+ TEXT taskStop
varchar_36_ taskVersionId FK
varchar_255_ title
INTEGER totalCompletionTokens
diff --git a/docs/generated/sqlite-schema/agent_background_job.md b/docs/generated/sqlite-schema/agent_background_job.md
index ecb211cb620..da963912db9 100644
--- a/docs/generated/sqlite-schema/agent_background_job.md
+++ b/docs/generated/sqlite-schema/agent_background_job.md
@@ -6,7 +6,7 @@
Table Definition
```sql
-CREATE TABLE "agent_background_job" ("id" varchar(36) PRIMARY KEY NOT NULL, "kind" varchar(16) NOT NULL, "status" varchar(16) NOT NULL, "parentAgentId" varchar(36) NOT NULL, "parentThreadId" varchar(128) NOT NULL, "title" varchar(255) NOT NULL, "subAgentId" varchar(36), "childThreadId" varchar(128), "childExecutionId" varchar(36), "workflowId" varchar(36), "timeoutAt" datetime(3), "result" text, "error" text, "settledAt" datetime(3), "createdAt" datetime(3) NOT NULL DEFAULT (STRFTIME('%Y-%m-%d %H:%M:%f', 'NOW')), "updatedAt" datetime(3) NOT NULL DEFAULT (STRFTIME('%Y-%m-%d %H:%M:%f', 'NOW')), "notifiedAt" datetime(3), "parentResourceId" varchar(255) NOT NULL, "parentPrincipalHash" varchar(64) NOT NULL, "pauseRequestId" varchar, CONSTRAINT "CHK_agent_background_job_kind" CHECK (((("kind" IN ('subagent', 'workflow'))))), CONSTRAINT "CHK_agent_background_job_status" CHECK ("status" IN ('running', 'suspended', 'paused', 'completed', 'failed', 'cancelled')), CONSTRAINT "FK_d46c6f00730c2ef8bcb6ee24b67" FOREIGN KEY ("parentAgentId") REFERENCES "agents" ("id") ON DELETE CASCADE ON UPDATE NO ACTION)
+CREATE TABLE "agent_background_job" ("id" varchar(36) PRIMARY KEY NOT NULL, "kind" varchar(16) NOT NULL, "status" varchar(16) NOT NULL, "parentAgentId" varchar(36) NOT NULL, "parentThreadId" varchar(128) NOT NULL, "title" varchar(255) NOT NULL, "subAgentId" varchar(36), "childThreadId" varchar(128), "childExecutionId" varchar(36), "workflowId" varchar(36), "timeoutAt" datetime(3), "result" text, "error" text, "settledAt" datetime(3), "createdAt" datetime(3) NOT NULL DEFAULT (STRFTIME('%Y-%m-%d %H:%M:%f', 'NOW')), "updatedAt" datetime(3) NOT NULL DEFAULT (STRFTIME('%Y-%m-%d %H:%M:%f', 'NOW')), "notifiedAt" datetime(3), "parentResourceId" varchar(255) NOT NULL, "parentPrincipalHash" varchar(64) NOT NULL, "pauseRequestId" varchar, "sourceExecutionId" varchar(36), "planId" varchar, "planTaskId" varchar, CONSTRAINT "CHK_agent_background_job_kind" CHECK (((("kind" IN ('subagent', 'workflow'))))), CONSTRAINT "CHK_agent_background_job_status" CHECK ("status" IN ('running', 'suspended', 'paused', 'completed', 'failed', 'cancelled')), CONSTRAINT "FK_d46c6f00730c2ef8bcb6ee24b67" FOREIGN KEY ("parentAgentId") REFERENCES "agents" ("id") ON DELETE CASCADE ON UPDATE NO ACTION, CONSTRAINT "FK_4db3b7ebea4ed4f69649982018c" FOREIGN KEY ("sourceExecutionId") REFERENCES "agent_execution" ("id") ON DELETE SET NULL, CONSTRAINT "FK_ce131bff629d89015893e350e9c" FOREIGN KEY ("planId") REFERENCES "agent_plan" ("id") ON DELETE SET NULL)
```
@@ -27,8 +27,11 @@ CREATE TABLE "agent_background_job" ("id" varchar(36) PRIMARY KEY NOT NULL, "kin
| parentResourceId | varchar(255) | | false | | | |
| parentThreadId | varchar(128) | | false | | | |
| pauseRequestId | varchar | | true | | | |
+| planId | varchar | | true | | [agent_plan](agent_plan.md) | |
+| planTaskId | varchar | | true | | | |
| result | TEXT | | true | | | |
| settledAt | datetime(3) | | true | | | |
+| sourceExecutionId | varchar(36) | | true | | [agent_execution](agent_execution.md) | |
| status | varchar(16) | | false | | | |
| subAgentId | varchar(36) | | true | | | |
| timeoutAt | datetime(3) | | true | | | |
@@ -42,7 +45,9 @@ CREATE TABLE "agent_background_job" ("id" varchar(36) PRIMARY KEY NOT NULL, "kin
| ---- | ---- | ---------- |
| - | CHECK | CHECK (((("kind" IN ('subagent', 'workflow'))))) |
| - | CHECK | CHECK ("status" IN ('running', 'suspended', 'paused', 'completed', 'failed', 'cancelled')) |
-| - (Foreign key ID: 0) | FOREIGN KEY | FOREIGN KEY (parentAgentId) REFERENCES agents (id) ON UPDATE NO ACTION ON DELETE CASCADE MATCH NONE |
+| - (Foreign key ID: 0) | FOREIGN KEY | FOREIGN KEY (planId) REFERENCES agent_plan (id) ON UPDATE NO ACTION ON DELETE SET NULL MATCH NONE |
+| - (Foreign key ID: 1) | FOREIGN KEY | FOREIGN KEY (sourceExecutionId) REFERENCES agent_execution (id) ON UPDATE NO ACTION ON DELETE SET NULL MATCH NONE |
+| - (Foreign key ID: 2) | FOREIGN KEY | FOREIGN KEY (parentAgentId) REFERENCES agents (id) ON UPDATE NO ACTION ON DELETE CASCADE MATCH NONE |
| id | PRIMARY KEY | PRIMARY KEY (id) |
| sqlite_autoindex_agent_background_job_1 | PRIMARY KEY | PRIMARY KEY (id) |
@@ -53,6 +58,8 @@ CREATE TABLE "agent_background_job" ("id" varchar(36) PRIMARY KEY NOT NULL, "kin
| IDX_93d62baabe9858816b5adafb44 | CREATE INDEX "IDX_93d62baabe9858816b5adafb44" ON "agent_background_job" ("parentThreadId", "status") |
| IDX_agent_background_job_childExecutionId | CREATE UNIQUE INDEX "IDX_agent_background_job_childExecutionId" ON "agent_background_job" ("childExecutionId") WHERE "childExecutionId" IS NOT NULL |
| IDX_agent_background_job_parentThreadId | CREATE INDEX "IDX_agent_background_job_parentThreadId" ON "agent_background_job" ("parentThreadId") WHERE "status" <> 'running' AND "notifiedAt" IS NULL |
+| IDX_agent_background_job_planId_planTaskId | CREATE INDEX "IDX_agent_background_job_planId_planTaskId" ON "agent_background_job" ("planId", "planTaskId") |
+| IDX_agent_background_job_sourceExecutionId | CREATE INDEX "IDX_agent_background_job_sourceExecutionId" ON "agent_background_job" ("sourceExecutionId") |
| IDX_agent_background_job_timeoutAt | CREATE INDEX "IDX_agent_background_job_timeoutAt" ON "agent_background_job" ("timeoutAt") WHERE "status" IN ('running', 'suspended') |
| IDX_d46c6f00730c2ef8bcb6ee24b6 | CREATE INDEX "IDX_d46c6f00730c2ef8bcb6ee24b6" ON "agent_background_job" ("parentAgentId") |
| IDX_e43e630272995a93dfeb94ab3e | CREATE INDEX "IDX_e43e630272995a93dfeb94ab3e" ON "agent_background_job" ("settledAt") |
@@ -64,6 +71,8 @@ CREATE TABLE "agent_background_job" ("id" varchar(36) PRIMARY KEY NOT NULL, "kin
erDiagram
"agent_background_job" }o--|| "agents" : "FOREIGN KEY (parentAgentId) REFERENCES agents (id) ON UPDATE NO ACTION ON DELETE CASCADE MATCH NONE"
+"agent_background_job" }o--o| "agent_plan" : "FOREIGN KEY (planId) REFERENCES agent_plan (id) ON UPDATE NO ACTION ON DELETE SET NULL MATCH NONE"
+"agent_background_job" }o--o| "agent_execution" : "FOREIGN KEY (sourceExecutionId) REFERENCES agent_execution (id) ON UPDATE NO ACTION ON DELETE SET NULL MATCH NONE"
"agent_background_job" {
varchar_36_ childExecutionId
@@ -78,8 +87,11 @@ erDiagram
varchar_255_ parentResourceId
varchar_128_ parentThreadId
varchar pauseRequestId
+ varchar planId FK
+ varchar planTaskId
TEXT result
datetime_3_ settledAt
+ varchar_36_ sourceExecutionId FK
varchar_16_ status
varchar_36_ subAgentId
datetime_3_ timeoutAt
@@ -104,6 +116,41 @@ erDiagram
datetime_3_ updatedAt
varchar_36_ versionId
}
+"agent_plan" {
+ datetime_3_ closedAt
+ datetime_3_ createdAt
+ TEXT data
+ INTEGER formatVersion
+ varchar id PK
+ INTEGER revision
+ varchar_128_ threadId FK
+ datetime_3_ updatedAt
+}
+"agent_execution" {
+ BOOLEAN acceptsSteering
+ TEXT attachments
+ TEXT author
+ INTEGER completionTokens
+ REAL cost
+ datetime_3_ createdAt
+ INTEGER duration
+ TEXT error
+ TEXT failureSummary
+ varchar_16_ hitlStatus
+ varchar_36_ id PK
+ varchar_255_ model
+ INTEGER promptTokens
+ varchar_32_ source
+ datetime_3_ startedAt
+ varchar_16_ status
+ datetime_3_ stoppedAt
+ varchar_2_ storedAt
+ varchar_128_ threadId FK
+ TEXT timeline
+ INTEGER totalTokens
+ datetime_3_ updatedAt
+ TEXT userMessage
+}
```
---
diff --git a/docs/generated/sqlite-schema/agent_execution.md b/docs/generated/sqlite-schema/agent_execution.md
index 3f5e86bd2a2..11f9f28f5e0 100644
--- a/docs/generated/sqlite-schema/agent_execution.md
+++ b/docs/generated/sqlite-schema/agent_execution.md
@@ -25,7 +25,7 @@ CREATE TABLE "agent_execution" ("id" varchar(36) PRIMARY KEY NOT NULL, "threadId
| error | TEXT | | true | | | |
| failureSummary | TEXT | | true | | | |
| hitlStatus | varchar(16) | | true | | | |
-| id | varchar(36) | | false | [agent_execution_message_links](agent_execution_message_links.md) [agent_message_queue](agent_message_queue.md) | | |
+| id | varchar(36) | | false | [agent_background_job](agent_background_job.md) [agent_execution_message_links](agent_execution_message_links.md) [agent_message_queue](agent_message_queue.md) | | |
| model | varchar(255) | | true | | | |
| promptTokens | INTEGER | | true | | | |
| source | varchar(32) | | true | | | |
@@ -63,6 +63,7 @@ CREATE TABLE "agent_execution" ("id" varchar(36) PRIMARY KEY NOT NULL, "threadId
```mermaid
erDiagram
+"agent_background_job" }o--o| "agent_execution" : "FOREIGN KEY (sourceExecutionId) REFERENCES agent_execution (id) ON UPDATE NO ACTION ON DELETE SET NULL MATCH NONE"
"agent_execution_message_links" |o--|| "agent_execution" : "FOREIGN KEY (executionId) REFERENCES agent_execution (id) ON UPDATE NO ACTION ON DELETE CASCADE MATCH NONE"
"agent_message_queue" }o--o| "agent_execution" : "FOREIGN KEY (executionId) REFERENCES agent_execution (id) ON UPDATE NO ACTION ON DELETE NO ACTION MATCH NONE"
"agent_message_queue" }o--o| "agent_execution" : "FOREIGN KEY (steeringExecutionId) REFERENCES agent_execution (id) ON UPDATE NO ACTION ON DELETE NO ACTION MATCH NONE"
@@ -93,6 +94,31 @@ erDiagram
datetime_3_ updatedAt
TEXT userMessage
}
+"agent_background_job" {
+ varchar_36_ childExecutionId
+ varchar_128_ childThreadId
+ datetime_3_ createdAt
+ TEXT error
+ varchar_36_ id PK
+ varchar_16_ kind
+ datetime_3_ notifiedAt
+ varchar_36_ parentAgentId FK
+ varchar_64_ parentPrincipalHash
+ varchar_255_ parentResourceId
+ varchar_128_ parentThreadId
+ varchar pauseRequestId
+ varchar planId FK
+ varchar planTaskId
+ TEXT result
+ datetime_3_ settledAt
+ varchar_36_ sourceExecutionId FK
+ varchar_16_ status
+ varchar_36_ subAgentId
+ datetime_3_ timeoutAt
+ varchar_255_ title
+ datetime_3_ updatedAt
+ varchar_36_ workflowId
+}
"agent_execution_message_links" {
datetime_3_ createdAt
varchar_6_ direction
@@ -125,6 +151,7 @@ erDiagram
varchar_255_ projectId FK
INTEGER sessionNumber
varchar_32_ taskId
+ TEXT taskStop
varchar_36_ taskVersionId FK
varchar_255_ title
INTEGER totalCompletionTokens
diff --git a/docs/generated/sqlite-schema/agent_execution_threads.md b/docs/generated/sqlite-schema/agent_execution_threads.md
index 3174f435166..405eda5a1d1 100644
--- a/docs/generated/sqlite-schema/agent_execution_threads.md
+++ b/docs/generated/sqlite-schema/agent_execution_threads.md
@@ -6,7 +6,7 @@
Table Definition
```sql
-CREATE TABLE "agent_execution_threads" ("id" varchar(128) PRIMARY KEY NOT NULL, "agentId" varchar(36) NOT NULL, "agentName" varchar(255) NOT NULL, "projectId" varchar(255) NOT NULL, "sessionNumber" integer NOT NULL DEFAULT (0), "totalPromptTokens" integer NOT NULL DEFAULT (0), "totalCompletionTokens" integer NOT NULL DEFAULT (0), "totalCost" real NOT NULL DEFAULT (0), "totalDuration" integer NOT NULL DEFAULT (0), "title" varchar(255), "emoji" varchar(8), "createdAt" datetime(3) NOT NULL DEFAULT (STRFTIME('%Y-%m-%d %H:%M:%f', 'NOW')), "updatedAt" datetime(3) NOT NULL DEFAULT (STRFTIME('%Y-%m-%d %H:%M:%f', 'NOW')), "taskId" varchar(32), "taskVersionId" varchar(36), "parentThreadId" varchar(128), "parentAgentId" varchar(36), "ownerId" varchar, "accessScope" varchar(16) NOT NULL DEFAULT ('user'), CONSTRAINT "CHK_agent_execution_threads_accessScope" CHECK ("accessScope" IN ('user', 'project')), CONSTRAINT "FK_f00b52d74fe11838e1fe086deea" FOREIGN KEY ("taskVersionId") REFERENCES "agent_history" ("versionId") ON DELETE SET NULL ON UPDATE NO ACTION, CONSTRAINT "FK_0468a9dc35597314e641d4722aa" FOREIGN KEY ("agentId") REFERENCES "agents" ("id") ON DELETE CASCADE ON UPDATE NO ACTION, CONSTRAINT "FK_0e2f8bf92a7a9c88b89670f701c" FOREIGN KEY ("projectId") REFERENCES "project" ("id") ON DELETE CASCADE ON UPDATE NO ACTION, CONSTRAINT "FK_agent_execution_threads_owner" FOREIGN KEY ("ownerId") REFERENCES "user" ("id") ON DELETE SET NULL)
+CREATE TABLE "agent_execution_threads" ("id" varchar(128) PRIMARY KEY NOT NULL, "agentId" varchar(36) NOT NULL, "agentName" varchar(255) NOT NULL, "projectId" varchar(255) NOT NULL, "sessionNumber" integer NOT NULL DEFAULT (0), "totalPromptTokens" integer NOT NULL DEFAULT (0), "totalCompletionTokens" integer NOT NULL DEFAULT (0), "totalCost" real NOT NULL DEFAULT (0), "totalDuration" integer NOT NULL DEFAULT (0), "title" varchar(255), "emoji" varchar(8), "createdAt" datetime(3) NOT NULL DEFAULT (STRFTIME('%Y-%m-%d %H:%M:%f', 'NOW')), "updatedAt" datetime(3) NOT NULL DEFAULT (STRFTIME('%Y-%m-%d %H:%M:%f', 'NOW')), "taskId" varchar(32), "taskVersionId" varchar(36), "parentThreadId" varchar(128), "parentAgentId" varchar(36), "ownerId" varchar, "accessScope" varchar(16) NOT NULL DEFAULT ('user'), "taskStop" TEXT, CONSTRAINT "CHK_agent_execution_threads_accessScope" CHECK ("accessScope" IN ('user', 'project')), CONSTRAINT "FK_f00b52d74fe11838e1fe086deea" FOREIGN KEY ("taskVersionId") REFERENCES "agent_history" ("versionId") ON DELETE SET NULL ON UPDATE NO ACTION, CONSTRAINT "FK_0468a9dc35597314e641d4722aa" FOREIGN KEY ("agentId") REFERENCES "agents" ("id") ON DELETE CASCADE ON UPDATE NO ACTION, CONSTRAINT "FK_0e2f8bf92a7a9c88b89670f701c" FOREIGN KEY ("projectId") REFERENCES "project" ("id") ON DELETE CASCADE ON UPDATE NO ACTION, CONSTRAINT "FK_agent_execution_threads_owner" FOREIGN KEY ("ownerId") REFERENCES "user" ("id") ON DELETE SET NULL)
```
@@ -27,6 +27,7 @@ CREATE TABLE "agent_execution_threads" ("id" varchar(128) PRIMARY KEY NOT NULL,
| projectId | varchar(255) | | false | | [project](project.md) | |
| sessionNumber | INTEGER | 0 | false | | | |
| taskId | varchar(32) | | true | | | |
+| taskStop | TEXT | | true | | | |
| taskVersionId | varchar(36) | | true | | [agent_history](agent_history.md) | |
| title | varchar(255) | | true | | | |
| totalCompletionTokens | INTEGER | 0 | false | | | |
@@ -84,6 +85,7 @@ erDiagram
varchar_255_ projectId FK
INTEGER sessionNumber
varchar_32_ taskId
+ TEXT taskStop
varchar_36_ taskVersionId FK
varchar_255_ title
INTEGER totalCompletionTokens
diff --git a/docs/generated/sqlite-schema/agent_history.md b/docs/generated/sqlite-schema/agent_history.md
index f8e84c9c323..862be7a22a4 100644
--- a/docs/generated/sqlite-schema/agent_history.md
+++ b/docs/generated/sqlite-schema/agent_history.md
@@ -111,6 +111,7 @@ erDiagram
varchar_255_ projectId FK
INTEGER sessionNumber
varchar_32_ taskId
+ TEXT taskStop
varchar_36_ taskVersionId FK
varchar_255_ title
INTEGER totalCompletionTokens
diff --git a/docs/generated/sqlite-schema/agent_message_queue.md b/docs/generated/sqlite-schema/agent_message_queue.md
index 667af0c8609..31434931ce2 100644
--- a/docs/generated/sqlite-schema/agent_message_queue.md
+++ b/docs/generated/sqlite-schema/agent_message_queue.md
@@ -121,6 +121,7 @@ erDiagram
varchar_255_ projectId FK
INTEGER sessionNumber
varchar_32_ taskId
+ TEXT taskStop
varchar_36_ taskVersionId FK
varchar_255_ title
INTEGER totalCompletionTokens
diff --git a/docs/generated/sqlite-schema/agent_plan.md b/docs/generated/sqlite-schema/agent_plan.md
index 1d296d0d846..a20a2d564bb 100644
--- a/docs/generated/sqlite-schema/agent_plan.md
+++ b/docs/generated/sqlite-schema/agent_plan.md
@@ -19,7 +19,7 @@ CREATE TABLE "agent_plan" ("id" varchar PRIMARY KEY NOT NULL, "threadId" varchar
| createdAt | datetime(3) | STRFTIME('%Y-%m-%d %H:%M:%f', 'NOW') | false | | | |
| data | TEXT | | false | | | |
| formatVersion | INTEGER | | false | | | |
-| id | varchar | | false | [agent_plan_history](agent_plan_history.md) | | |
+| id | varchar | | false | [agent_background_job](agent_background_job.md) [agent_plan_history](agent_plan_history.md) | | |
| revision | INTEGER | 1 | false | | | |
| threadId | varchar(128) | | false | | [agent_execution_threads](agent_execution_threads.md) | |
| updatedAt | datetime(3) | STRFTIME('%Y-%m-%d %H:%M:%f', 'NOW') | false | | | |
@@ -47,6 +47,7 @@ CREATE TABLE "agent_plan" ("id" varchar PRIMARY KEY NOT NULL, "threadId" varchar
```mermaid
erDiagram
+"agent_background_job" }o--o| "agent_plan" : "FOREIGN KEY (planId) REFERENCES agent_plan (id) ON UPDATE NO ACTION ON DELETE SET NULL MATCH NONE"
"agent_plan_history" |o--|| "agent_plan" : "FOREIGN KEY (planId) REFERENCES agent_plan (id) ON UPDATE NO ACTION ON DELETE CASCADE MATCH NONE"
"agent_plan" }o--|| "agent_execution_threads" : "FOREIGN KEY (threadId) REFERENCES agent_execution_threads (id) ON UPDATE NO ACTION ON DELETE CASCADE MATCH NONE"
@@ -60,6 +61,31 @@ erDiagram
varchar_128_ threadId FK
datetime_3_ updatedAt
}
+"agent_background_job" {
+ varchar_36_ childExecutionId
+ varchar_128_ childThreadId
+ datetime_3_ createdAt
+ TEXT error
+ varchar_36_ id PK
+ varchar_16_ kind
+ datetime_3_ notifiedAt
+ varchar_36_ parentAgentId FK
+ varchar_64_ parentPrincipalHash
+ varchar_255_ parentResourceId
+ varchar_128_ parentThreadId
+ varchar pauseRequestId
+ varchar planId FK
+ varchar planTaskId
+ TEXT result
+ datetime_3_ settledAt
+ varchar_36_ sourceExecutionId FK
+ varchar_16_ status
+ varchar_36_ subAgentId
+ datetime_3_ timeoutAt
+ varchar_255_ title
+ datetime_3_ updatedAt
+ varchar_36_ workflowId
+}
"agent_plan_history" {
datetime_3_ closedAt
datetime_3_ createdAt
@@ -81,6 +107,7 @@ erDiagram
varchar_255_ projectId FK
INTEGER sessionNumber
varchar_32_ taskId
+ TEXT taskStop
varchar_36_ taskVersionId FK
varchar_255_ title
INTEGER totalCompletionTokens
diff --git a/docs/generated/sqlite-schema/agent_thread_grants.md b/docs/generated/sqlite-schema/agent_thread_grants.md
index ebbc6fcdc37..288a6488f14 100644
--- a/docs/generated/sqlite-schema/agent_thread_grants.md
+++ b/docs/generated/sqlite-schema/agent_thread_grants.md
@@ -61,6 +61,7 @@ erDiagram
varchar_255_ projectId FK
INTEGER sessionNumber
varchar_32_ taskId
+ TEXT taskStop
varchar_36_ taskVersionId FK
varchar_255_ title
INTEGER totalCompletionTokens
diff --git a/docs/generated/sqlite-schema/agents.md b/docs/generated/sqlite-schema/agents.md
index 3c29aeabe19..5e3684802c7 100644
--- a/docs/generated/sqlite-schema/agents.md
+++ b/docs/generated/sqlite-schema/agents.md
@@ -121,8 +121,11 @@ erDiagram
varchar_255_ parentResourceId
varchar_128_ parentThreadId
varchar pauseRequestId
+ varchar planId FK
+ varchar planTaskId
TEXT result
datetime_3_ settledAt
+ varchar_36_ sourceExecutionId FK
varchar_16_ status
varchar_36_ subAgentId
datetime_3_ timeoutAt
@@ -204,6 +207,7 @@ erDiagram
varchar_255_ projectId FK
INTEGER sessionNumber
varchar_32_ taskId
+ TEXT taskStop
varchar_36_ taskVersionId FK
varchar_255_ title
INTEGER totalCompletionTokens
diff --git a/docs/generated/sqlite-schema/project.md b/docs/generated/sqlite-schema/project.md
index 6dd22d0d03f..ca9f24c6110 100644
--- a/docs/generated/sqlite-schema/project.md
+++ b/docs/generated/sqlite-schema/project.md
@@ -136,6 +136,7 @@ erDiagram
varchar_255_ projectId FK
INTEGER sessionNumber
varchar_32_ taskId
+ TEXT taskStop
varchar_36_ taskVersionId FK
varchar_255_ title
INTEGER totalCompletionTokens
diff --git a/docs/generated/sqlite-schema/user.md b/docs/generated/sqlite-schema/user.md
index 995ae1e8161..a1086bcc6f1 100644
--- a/docs/generated/sqlite-schema/user.md
+++ b/docs/generated/sqlite-schema/user.md
@@ -184,6 +184,7 @@ erDiagram
varchar_255_ projectId FK
INTEGER sessionNumber
varchar_32_ taskId
+ TEXT taskStop
varchar_36_ taskVersionId FK
varchar_255_ title
INTEGER totalCompletionTokens
diff --git a/packages/@n8n/api-types/src/agents/background-job.ts b/packages/@n8n/api-types/src/agents/background-job.ts
index 5c1152ccd1f..2b00b3ab1de 100644
--- a/packages/@n8n/api-types/src/agents/background-job.ts
+++ b/packages/@n8n/api-types/src/agents/background-job.ts
@@ -1,6 +1,9 @@
+import type { AgentPlanTaskRef } from './task-cancellation';
+
import type { AgentBuilderOpenSuspension } from './types';
export interface AgentBackgroundJobDto {
+ planTask?: AgentPlanTaskRef;
id: string;
title: string;
/** Source of the background job. */
diff --git a/packages/@n8n/api-types/src/agents/dto.ts b/packages/@n8n/api-types/src/agents/dto.ts
index 58d4a8e8ca6..1646d5ec53f 100644
--- a/packages/@n8n/api-types/src/agents/dto.ts
+++ b/packages/@n8n/api-types/src/agents/dto.ts
@@ -457,3 +457,7 @@ export interface VectorStoreTestResult {
export class AgentChatCancelDto extends Z.class({
scope: z.enum(['foreground', 'all']).optional(),
}) {}
+
+export class AgentTaskCancellationDto extends Z.class({
+ planId: z.string().uuid().toLowerCase().nullable(),
+}) {}
diff --git a/packages/@n8n/api-types/src/agents/index.ts b/packages/@n8n/api-types/src/agents/index.ts
index f5e86f490bd..80b71847091 100644
--- a/packages/@n8n/api-types/src/agents/index.ts
+++ b/packages/@n8n/api-types/src/agents/index.ts
@@ -48,3 +48,5 @@ export {
type CancellationResumeData,
} from '../agent-builder-interactive';
export * from './agent-interaction.schema';
+
+export type * from './task-cancellation';
diff --git a/packages/@n8n/api-types/src/agents/task-cancellation.ts b/packages/@n8n/api-types/src/agents/task-cancellation.ts
new file mode 100644
index 00000000000..c8e7692a8a5
--- /dev/null
+++ b/packages/@n8n/api-types/src/agents/task-cancellation.ts
@@ -0,0 +1,59 @@
+export interface AgentPlanTaskRef {
+ planId: string;
+ planTaskId: string;
+}
+
+/** Runtime state does not change the saved task statuses. */
+export interface AgentPlanWorkState {
+ stoppedTaskIds: string[];
+ stoppingTaskIds: string[];
+ fullyStopped: boolean;
+ stoppedAt: string | null;
+ taskStateAvailable: boolean;
+}
+
+export interface AgentPlanItemDto {
+ id: string;
+ title: string;
+ description?: string;
+ resultSummary?: string;
+ status: 'pending' | 'in_progress' | 'done' | 'failed' | 'cancelled';
+ dependsOn: string[];
+}
+
+export interface AgentPlanSnapshotDto {
+ workState?: AgentPlanWorkState;
+ readiness: { ready: string[]; blocked: Array<{ id: string; blockedBy: string[] }> };
+ planId: string;
+ revision: number;
+ closed: boolean;
+ startedAt: string | null;
+ finishedAt?: string | null;
+ closedAt: string | null;
+ document: {
+ title: string;
+ description?: string;
+ presentation?: { label: string; detail?: string };
+ items: Array<
+ | (AgentPlanItemDto & { kind: 'task'; fallbackFor?: string })
+ | (AgentPlanItemDto & {
+ kind: 'group';
+ tasks: Array;
+ })
+ >;
+ };
+}
+
+export interface AgentTaskStopFailure {
+ jobId: string;
+ title: string;
+}
+
+export interface AgentTaskCancellationState {
+ planId: string | null;
+ status: 'stopping' | 'failed' | 'stopped' | 'resumed';
+ reportFailed?: boolean;
+ requestedAt: string;
+ failures: AgentTaskStopFailure[];
+ plan: AgentPlanSnapshotDto | null;
+}
diff --git a/packages/@n8n/db/src/migrations/common/1792083059203-AddSourceExecutionToAgentBackgroundJob.ts b/packages/@n8n/db/src/migrations/common/1792083059203-AddSourceExecutionToAgentBackgroundJob.ts
new file mode 100644
index 00000000000..6bb610b7140
--- /dev/null
+++ b/packages/@n8n/db/src/migrations/common/1792083059203-AddSourceExecutionToAgentBackgroundJob.ts
@@ -0,0 +1,28 @@
+import type { MigrationContext, ReversibleMigration } from '../migration-types';
+
+export class AddSourceExecutionToAgentBackgroundJob1792083059203 implements ReversibleMigration {
+ async up({
+ schemaBuilder: { addColumns, addForeignKey, column, createIndex },
+ }: MigrationContext) {
+ await addColumns(
+ 'agent_background_job',
+ [column('sourceExecutionId').varchar(36).comment('Execution that dispatched this job')],
+ { recreatesOnSqlite: true },
+ );
+ await addForeignKey(
+ 'agent_background_job',
+ 'sourceExecutionId',
+ ['agent_execution', 'id'],
+ undefined,
+ 'SET NULL',
+ );
+ await createIndex('agent_background_job', ['sourceExecutionId']);
+ }
+
+ async down({ schemaBuilder: { dropColumns, dropIndex } }: MigrationContext) {
+ await dropIndex('agent_background_job', ['sourceExecutionId']);
+ await dropColumns('agent_background_job', ['sourceExecutionId'], {
+ recreatesOnSqlite: true,
+ });
+ }
+}
diff --git a/packages/@n8n/db/src/migrations/common/1792083059437-AddTaskStopToAgentExecutionThreads.ts b/packages/@n8n/db/src/migrations/common/1792083059437-AddTaskStopToAgentExecutionThreads.ts
new file mode 100644
index 00000000000..f26e8a282d0
--- /dev/null
+++ b/packages/@n8n/db/src/migrations/common/1792083059437-AddTaskStopToAgentExecutionThreads.ts
@@ -0,0 +1,28 @@
+import type { MigrationContext, ReversibleMigration } from '../migration-types';
+
+export class AddTaskStopToAgentExecutionThreads1792083059437 implements ReversibleMigration {
+ async up({
+ schemaBuilder: { addColumns, column },
+ isSqlite,
+ escape,
+ runQuery,
+ }: MigrationContext) {
+ if (isSqlite) {
+ await runQuery(
+ `ALTER TABLE ${escape.tableName('agent_execution_threads')} ADD COLUMN ${escape.columnName('taskStop')} TEXT`,
+ );
+ return;
+ }
+ await addColumns(
+ 'agent_execution_threads',
+ [column('taskStop').json.comment('Latest task stop boundary and unresolved stop errors')],
+ { recreatesOnSqlite: true },
+ );
+ }
+
+ async down({ escape, runQuery }: MigrationContext) {
+ await runQuery(
+ `ALTER TABLE ${escape.tableName('agent_execution_threads')} DROP COLUMN ${escape.columnName('taskStop')}`,
+ );
+ }
+}
diff --git a/packages/@n8n/db/src/migrations/common/1792083059654-AddPlanTaskToAgentBackgroundJob.ts b/packages/@n8n/db/src/migrations/common/1792083059654-AddPlanTaskToAgentBackgroundJob.ts
new file mode 100644
index 00000000000..77e400c5af6
--- /dev/null
+++ b/packages/@n8n/db/src/migrations/common/1792083059654-AddPlanTaskToAgentBackgroundJob.ts
@@ -0,0 +1,31 @@
+import type { MigrationContext, ReversibleMigration } from '../migration-types';
+
+export class AddPlanTaskToAgentBackgroundJob1792083059654 implements ReversibleMigration {
+ async up({
+ schemaBuilder: { addColumns, column, addForeignKey, createIndex },
+ }: MigrationContext) {
+ await addColumns(
+ 'agent_background_job',
+ [
+ column('planId').uuid.comment('Plan that owns this work'),
+ column('planTaskId').uuid.comment('Task ID in the saved plan document'),
+ ],
+ { recreatesOnSqlite: true },
+ );
+ await addForeignKey(
+ 'agent_background_job',
+ 'planId',
+ ['agent_plan', 'id'],
+ undefined,
+ 'SET NULL',
+ );
+ await createIndex('agent_background_job', ['planId', 'planTaskId']);
+ }
+
+ async down({ schemaBuilder: { dropIndex, dropColumns } }: MigrationContext) {
+ await dropIndex('agent_background_job', ['planId', 'planTaskId']);
+ await dropColumns('agent_background_job', ['planId', 'planTaskId'], {
+ recreatesOnSqlite: true,
+ });
+ }
+}
diff --git a/packages/cli/src/modules/agents/entities/agent-background-job.entity.ts b/packages/cli/src/modules/agents/entities/agent-background-job.entity.ts
index d32e7f1409d..a0695f581c7 100644
--- a/packages/cli/src/modules/agents/entities/agent-background-job.entity.ts
+++ b/packages/cli/src/modules/agents/entities/agent-background-job.entity.ts
@@ -1,6 +1,8 @@
+import type { AgentPlan } from './agent-plan.entity';
+import type { AgentExecution } from './agent-execution.entity';
import type { AgentBackgroundJobDto } from '@n8n/api-types';
import { DateTimeColumn, WithTimestampsAndStringId } from '@n8n/db';
-import { Column, Entity, Index } from '@n8n/typeorm';
+import { Column, Entity, Index, ManyToOne, JoinColumn, type Relation } from '@n8n/typeorm';
export type AgentBackgroundJobKind = AgentBackgroundJobDto['kind'];
export type AgentBackgroundJobStatus = AgentBackgroundJobDto['status'];
@@ -15,11 +17,35 @@ export type AgentBackgroundJobStatus = AgentBackgroundJobDto['status'];
@Entity({ name: 'agent_background_job' })
@Index(['parentThreadId', 'status'])
@Index(['parentAgentId'])
+@Index(['sourceExecutionId'])
+@Index(['planId', 'planTaskId'])
@Index(['settledAt'])
@Index(['childExecutionId'], { unique: true, where: '"childExecutionId" IS NOT NULL' })
@Index(['timeoutAt'], { where: "\"status\" IN ('running', 'suspended')" })
@Index(['parentThreadId'], { where: '"status" <> \'running\' AND "notifiedAt" IS NULL' })
export class AgentBackgroundJob extends WithTimestampsAndStringId {
+ @Column({
+ type: 'varchar',
+ length: 36,
+ nullable: true,
+ comment: 'Execution that dispatched this job',
+ })
+ sourceExecutionId: string | null;
+
+ @ManyToOne('AgentExecution', { nullable: true, onDelete: 'SET NULL' })
+ @JoinColumn({ name: 'sourceExecutionId' })
+ sourceExecution: Relation | null;
+
+ @Column({ type: 'uuid', nullable: true, comment: 'Plan that owns this work' })
+ planId: string | null;
+
+ @Column({ type: 'uuid', nullable: true, comment: 'Task ID in the saved plan document' })
+ planTaskId: string | null;
+
+ @ManyToOne('AgentPlan', { nullable: true, onDelete: 'SET NULL' })
+ @JoinColumn({ name: 'planId' })
+ plan: Relation | null;
+
@Column({ type: 'varchar', length: 16 })
kind: AgentBackgroundJobKind;
diff --git a/packages/cli/src/modules/agents/entities/agent-execution-thread.entity.ts b/packages/cli/src/modules/agents/entities/agent-execution-thread.entity.ts
index 0619a4c1f7d..b4d83c877a2 100644
--- a/packages/cli/src/modules/agents/entities/agent-execution-thread.entity.ts
+++ b/packages/cli/src/modules/agents/entities/agent-execution-thread.entity.ts
@@ -1,9 +1,10 @@
import { AGENT_TASK_ID_MAX_LENGTH } from '@n8n/api-types';
-import { Project, User, WithTimestampsAndStringId } from '@n8n/db';
+import { JsonColumn, Project, User, WithTimestampsAndStringId } from '@n8n/db';
import { Column, Entity, Index, JoinColumn, ManyToOne, type Relation } from '@n8n/typeorm';
import { AgentHistory } from './agent-history.entity';
import { Agent } from './agent.entity';
+import type { StoredAgentTaskStop } from '../types/agent-task-stop';
/**
* One conversation between a user and an agent. Aggregates per-session
@@ -20,6 +21,9 @@ import { Agent } from './agent.entity';
*/
@Entity({ name: 'agent_execution_threads' })
export class AgentExecutionThread extends WithTimestampsAndStringId {
+ @JsonColumn({ nullable: true, comment: 'Latest task stop boundary and unresolved stop errors' })
+ taskStop: StoredAgentTaskStop | null;
+
@ManyToOne(() => User, { nullable: true, onDelete: 'SET NULL' })
@JoinColumn({ name: 'ownerId', foreignKeyConstraintName: 'FK_agent_execution_threads_owner' })
owner: Relation | null;
diff --git a/packages/cli/src/modules/agents/types/agent-task-stop.ts b/packages/cli/src/modules/agents/types/agent-task-stop.ts
new file mode 100644
index 00000000000..db150c17cff
--- /dev/null
+++ b/packages/cli/src/modules/agents/types/agent-task-stop.ts
@@ -0,0 +1,28 @@
+import type { AgentTaskStopFailure } from '@n8n/api-types';
+
+/** Keep the stop and its acknowledgement available between jobs and across restarts. */
+export interface AgentTaskStop {
+ planId: string | null;
+ requestedAt: string;
+ /** Unfinished leaf tasks and empty groups captured when Stop was requested. */
+ stoppedTaskIds?: string[];
+ /** Explicitly selected workflow tasks that still need a replacement job. */
+ restartTaskIds?: string[];
+ planRevision?: number;
+ generation: { executionIds: string[]; jobIds: string[]; threadIds: string[] };
+ failures: AgentTaskStopFailure[];
+ /** Reuse background checkpoints and deliver one report for this stop. */
+ pause: {
+ id: string;
+ reportExecutionId?: string;
+ reportedAt?: string;
+ reportFailed?: boolean;
+ /** Chat admission only. This does not resume stopped tasks. */
+ resumedAt?: string;
+ };
+}
+
+/** Earlier stop records have no pause coordination. */
+export type StoredAgentTaskStop = Omit & {
+ pause?: AgentTaskStop['pause'];
+};
diff --git a/packages/cli/test/migration/1792083059203-add-source-execution-to-agent-background-job.test.ts b/packages/cli/test/migration/1792083059203-add-source-execution-to-agent-background-job.test.ts
new file mode 100644
index 00000000000..8a1cdd6d39d
--- /dev/null
+++ b/packages/cli/test/migration/1792083059203-add-source-execution-to-agent-background-job.test.ts
@@ -0,0 +1,128 @@
+import {
+ createTestMigrationContext,
+ initDbUpToMigration,
+ runSingleMigration,
+ undoLastSingleMigration,
+ type TestMigrationContext,
+} from '@n8n/backend-test-utils';
+import { DbConnection } from '@n8n/db';
+import { Container } from '@n8n/di';
+import { DataSource } from '@n8n/typeorm';
+import { randomUUID } from 'node:crypto';
+
+const migrationName = 'AddSourceExecutionToAgentBackgroundJob1792083059203';
+
+describe('Agent background job source migration', () => {
+ const threadId = randomUUID();
+ const executionId = randomUUID();
+ const jobId = randomUUID();
+
+ async function withContext(fn: (context: TestMigrationContext) => Promise): Promise {
+ const context = createTestMigrationContext(Container.get(DataSource));
+ try {
+ return await fn(context);
+ } finally {
+ await context.queryRunner.release();
+ }
+ }
+
+ async function insert(
+ context: TestMigrationContext,
+ table: string,
+ values: Record,
+ ) {
+ const columns = Object.keys(values);
+ await context.runQuery(
+ `INSERT INTO ${context.escape.tableName(table)}
+ (${columns.map(context.escape.columnName).join(', ')})
+ VALUES (${columns.map((name) => `:${name}`).join(', ')})`,
+ values,
+ );
+ }
+
+ beforeAll(async () => {
+ await Container.get(DbConnection).init();
+ await withContext(async (context) => await context.queryRunner.clearDatabase());
+ await initDbUpToMigration(migrationName);
+ await withContext(async (context) => {
+ const projectId = randomUUID();
+ const agentId = randomUUID();
+ await insert(context, 'project', { id: projectId, name: 'Stop test project', type: 'team' });
+ await insert(context, 'agents', {
+ id: agentId,
+ projectId,
+ name: 'Stop test agent',
+ integrations: '[]',
+ tools: '{}',
+ skills: '{}',
+ });
+ await insert(context, 'agent_execution_threads', {
+ id: threadId,
+ projectId,
+ agentId,
+ agentName: 'Stop test agent',
+ });
+ await insert(context, 'agent_execution', { id: executionId, threadId, status: 'success' });
+ await insert(context, 'agent_background_job', {
+ id: jobId,
+ parentAgentId: agentId,
+ parentThreadId: threadId,
+ parentResourceId: 'draft-chat:test',
+ parentPrincipalHash: 'principal',
+ title: 'Saved result',
+ kind: 'workflow',
+ status: 'completed',
+ result: '{"completed":true}',
+ });
+ });
+ });
+
+ afterAll(async () => await Container.get(DbConnection).close());
+
+ it('preserves jobs and supports source deletion, rollback, and reapplication', async () => {
+ await runSingleMigration(migrationName);
+ await withContext(async ({ escape, runQuery, queryRunner, tablePrefix }) => {
+ const table = await queryRunner.getTable(`${tablePrefix}agent_background_job`);
+ expect(table?.findColumnByName('sourceExecutionId')).toMatchObject({ isNullable: true });
+ expect(
+ table?.foreignKeys.filter((key) => key.columnNames.includes('sourceExecutionId')),
+ ).toHaveLength(1);
+ expect(
+ table?.indices.some((index) => index.columnNames.join(',') === 'sourceExecutionId'),
+ ).toBe(true);
+ await runQuery(
+ `UPDATE ${escape.tableName('agent_background_job')} SET ${escape.columnName('sourceExecutionId')} = :executionId WHERE ${escape.columnName('id')} = :jobId`,
+ { executionId, jobId },
+ );
+ await runQuery(
+ `DELETE FROM ${escape.tableName('agent_execution')} WHERE ${escape.columnName('id')} = :executionId`,
+ { executionId },
+ );
+ expect(
+ await runQuery(
+ `SELECT ${escape.columnName('sourceExecutionId')}, ${escape.columnName('result')} FROM ${escape.tableName('agent_background_job')}`,
+ ),
+ ).toEqual([{ sourceExecutionId: null, result: '{"completed":true}' }]);
+ });
+ await undoLastSingleMigration();
+ await withContext(async ({ escape, runQuery, queryRunner, tablePrefix }) => {
+ expect(
+ (await queryRunner.getTable(`${tablePrefix}agent_background_job`))?.findColumnByName(
+ 'sourceExecutionId',
+ ),
+ ).toBeUndefined();
+ expect(
+ await runQuery(
+ `SELECT ${escape.columnName('result')} FROM ${escape.tableName('agent_background_job')}`,
+ ),
+ ).toEqual([{ result: '{"completed":true}' }]);
+ });
+ await runSingleMigration(migrationName);
+ await withContext(async ({ queryRunner, tablePrefix }) => {
+ const table = await queryRunner.getTable(`${tablePrefix}agent_background_job`);
+ expect(
+ table?.foreignKeys.filter((key) => key.columnNames.includes('sourceExecutionId')),
+ ).toHaveLength(1);
+ });
+ });
+});
diff --git a/packages/cli/test/migration/1792083059437-add-task-stop-to-agent-execution-threads.test.ts b/packages/cli/test/migration/1792083059437-add-task-stop-to-agent-execution-threads.test.ts
new file mode 100644
index 00000000000..5583c8f0db8
--- /dev/null
+++ b/packages/cli/test/migration/1792083059437-add-task-stop-to-agent-execution-threads.test.ts
@@ -0,0 +1,122 @@
+import {
+ createTestMigrationContext,
+ initDbUpToMigration,
+ runSingleMigration,
+ undoLastSingleMigration,
+ type TestMigrationContext,
+} from '@n8n/backend-test-utils';
+import { DbConnection } from '@n8n/db';
+import { Container } from '@n8n/di';
+import { DataSource } from '@n8n/typeorm';
+import { randomUUID } from 'node:crypto';
+
+const migrationName = 'AddTaskStopToAgentExecutionThreads1792083059437';
+
+describe('AddTaskStopToAgentExecutionThreads migration', () => {
+ const threadId = randomUUID();
+ const executionId = randomUUID();
+
+ async function withContext(fn: (context: TestMigrationContext) => Promise): Promise {
+ const context = createTestMigrationContext(Container.get(DataSource));
+ try {
+ return await fn(context);
+ } finally {
+ await context.queryRunner.release();
+ }
+ }
+
+ async function insert(
+ context: TestMigrationContext,
+ table: string,
+ values: Record,
+ ) {
+ const columns = Object.keys(values);
+ await context.runQuery(
+ `INSERT INTO ${context.escape.tableName(table)}
+ (${columns.map(context.escape.columnName).join(', ')})
+ VALUES (${columns.map((name) => `:${name}`).join(', ')})`,
+ values,
+ );
+ }
+
+ async function readThreads() {
+ return await withContext(
+ async ({ escape, runQuery }) =>
+ await runQuery(`SELECT * FROM ${escape.tableName('agent_execution_threads')}`),
+ );
+ }
+
+ beforeAll(async () => {
+ await Container.get(DbConnection).init();
+ await withContext(async (context) => await context.queryRunner.clearDatabase());
+ await initDbUpToMigration(migrationName);
+ await withContext(async (context) => {
+ const projectId = randomUUID();
+ const agentId = randomUUID();
+ await insert(context, 'project', { id: projectId, name: 'Stop test project', type: 'team' });
+ await insert(context, 'agents', {
+ id: agentId,
+ projectId,
+ name: 'Stop test agent',
+ integrations: '[]',
+ tools: '{}',
+ skills: '{}',
+ });
+ await insert(context, 'agent_execution_threads', {
+ id: threadId,
+ projectId,
+ agentId,
+ agentName: 'Stop test agent',
+ });
+ await insert(context, 'agent_execution', { id: executionId, threadId, status: 'success' });
+ });
+ });
+
+ afterAll(async () => await Container.get(DbConnection).close());
+
+ it('preserves chats and their execution records through upgrade, rollback, and reapplication', async () => {
+ const original = await readThreads();
+ await runSingleMigration(migrationName);
+ await withContext(async ({ escape, runQuery, queryRunner, tablePrefix, isSqlite }) => {
+ expect(
+ (await queryRunner.getTable(`${tablePrefix}agent_execution_threads`))?.findColumnByName(
+ 'taskStop',
+ ),
+ ).toMatchObject({
+ isNullable: true,
+ type: isSqlite ? 'text' : 'json',
+ });
+ expect(
+ await runQuery(
+ `SELECT ${escape.columnName('taskStop')} FROM ${escape.tableName('agent_execution_threads')}`,
+ ),
+ ).toEqual([{ taskStop: null }]);
+ await runQuery(
+ `UPDATE ${escape.tableName('agent_execution_threads')} SET ${escape.columnName('taskStop')} = :stop WHERE ${escape.columnName('id')} = :threadId`,
+ { threadId, stop: JSON.stringify({ requestedAt: '2026-10-08T10:00:00.000Z' }) },
+ );
+ });
+ await undoLastSingleMigration();
+ expect(await readThreads()).toEqual(original);
+ await withContext(async ({ queryRunner, tablePrefix }) => {
+ expect(
+ (await queryRunner.getTable(`${tablePrefix}agent_execution_threads`))?.findColumnByName(
+ 'taskStop',
+ ),
+ ).toBeUndefined();
+ });
+ await runSingleMigration(migrationName);
+ await withContext(async ({ escape, runQuery, queryRunner, tablePrefix }) => {
+ expect(
+ await runQuery(
+ `SELECT ${escape.columnName('id')} FROM ${escape.tableName('agent_execution')}`,
+ ),
+ ).toEqual([{ id: executionId }]);
+ expect(
+ (await queryRunner.getTable(`${tablePrefix}agent_execution_threads`))?.findColumnByName(
+ 'taskStop',
+ ),
+ ).toMatchObject({ isNullable: true });
+ });
+ });
+});
diff --git a/packages/cli/test/migration/1792083059654-add-plan-task-to-agent-background-job.test.ts b/packages/cli/test/migration/1792083059654-add-plan-task-to-agent-background-job.test.ts
new file mode 100644
index 00000000000..70b4bbadfaa
--- /dev/null
+++ b/packages/cli/test/migration/1792083059654-add-plan-task-to-agent-background-job.test.ts
@@ -0,0 +1,134 @@
+import {
+ createTestMigrationContext,
+ initDbUpToMigration,
+ runSingleMigration,
+ undoLastSingleMigration,
+ type TestMigrationContext,
+} from '@n8n/backend-test-utils';
+import { DbConnection } from '@n8n/db';
+import { Container } from '@n8n/di';
+import { DataSource } from '@n8n/typeorm';
+import { randomUUID } from 'node:crypto';
+
+const migrationName = 'AddPlanTaskToAgentBackgroundJob1792083059654';
+
+describe('Agent background job plan task migration', () => {
+ const threadId = randomUUID();
+ const planId = randomUUID();
+ const planTaskId = randomUUID();
+ const jobId = randomUUID();
+
+ async function withContext(fn: (context: TestMigrationContext) => Promise): Promise {
+ const context = createTestMigrationContext(Container.get(DataSource));
+ try {
+ return await fn(context);
+ } finally {
+ await context.queryRunner.release();
+ }
+ }
+
+ async function insert(
+ context: TestMigrationContext,
+ table: string,
+ values: Record,
+ ) {
+ const columns = Object.keys(values);
+ await context.runQuery(
+ `INSERT INTO ${context.escape.tableName(table)}
+ (${columns.map(context.escape.columnName).join(', ')})
+ VALUES (${columns.map((name) => `:${name}`).join(', ')})`,
+ values,
+ );
+ }
+
+ beforeAll(async () => {
+ await Container.get(DbConnection).init();
+ await withContext(async (context) => await context.queryRunner.clearDatabase());
+ await initDbUpToMigration(migrationName);
+ await withContext(async (context) => {
+ const projectId = randomUUID();
+ const agentId = randomUUID();
+ await insert(context, 'project', { id: projectId, name: 'Stop test project', type: 'team' });
+ await insert(context, 'agents', {
+ id: agentId,
+ projectId,
+ name: 'Stop test agent',
+ integrations: '[]',
+ tools: '{}',
+ skills: '{}',
+ });
+ await insert(context, 'agent_execution_threads', {
+ id: threadId,
+ projectId,
+ agentId,
+ agentName: 'Stop test agent',
+ });
+ await insert(context, 'agent_plan', { id: planId, threadId, formatVersion: '1', data: '{}' });
+ await insert(context, 'agent_background_job', {
+ id: jobId,
+ parentAgentId: agentId,
+ parentThreadId: threadId,
+ parentResourceId: 'draft-chat:test',
+ parentPrincipalHash: 'principal',
+ title: 'Saved result',
+ kind: 'workflow',
+ status: 'completed',
+ result: '{"completed":true}',
+ });
+ });
+ });
+
+ afterAll(async () => await Container.get(DbConnection).close());
+
+ it('preserves jobs and supports plan deletion, rollback, and reapplication', async () => {
+ await runSingleMigration(migrationName);
+ await withContext(async ({ escape, runQuery, queryRunner, tablePrefix }) => {
+ const table = await queryRunner.getTable(`${tablePrefix}agent_background_job`);
+ expect(table?.findColumnByName('planId')).toMatchObject({ isNullable: true });
+ expect(table?.findColumnByName('planTaskId')).toMatchObject({ isNullable: true });
+ expect(
+ table?.indices.some((index) => index.columnNames.join(',') === 'planId,planTaskId'),
+ ).toBe(true);
+ expect(table?.foreignKeys.filter((key) => key.columnNames.includes('planId'))).toHaveLength(
+ 1,
+ );
+ expect(table?.findColumnByName('detached')).toBeUndefined();
+ expect(
+ (await queryRunner.getTable(`${tablePrefix}agent_message_queue`))?.findColumnByName('held'),
+ ).toBeUndefined();
+ await runQuery(
+ `UPDATE ${escape.tableName('agent_background_job')} SET ${escape.columnName('planId')} = :planId, ${escape.columnName('planTaskId')} = :planTaskId WHERE ${escape.columnName('id')} = :jobId`,
+ { planId, planTaskId, jobId },
+ );
+ await runQuery(
+ `DELETE FROM ${escape.tableName('agent_plan')} WHERE ${escape.columnName('id')} = :planId`,
+ { planId },
+ );
+ expect(
+ await runQuery(
+ `SELECT ${escape.columnName('planId')}, ${escape.columnName('result')} FROM ${escape.tableName('agent_background_job')}`,
+ ),
+ ).toEqual([{ planId: null, result: '{"completed":true}' }]);
+ });
+ await undoLastSingleMigration();
+ await withContext(async ({ escape, runQuery, queryRunner, tablePrefix }) => {
+ expect(
+ (await queryRunner.getTable(`${tablePrefix}agent_background_job`))?.findColumnByName(
+ 'planId',
+ ),
+ ).toBeUndefined();
+ expect(
+ await runQuery(
+ `SELECT ${escape.columnName('result')} FROM ${escape.tableName('agent_background_job')}`,
+ ),
+ ).toEqual([{ result: '{"completed":true}' }]);
+ });
+ await runSingleMigration(migrationName);
+ await withContext(async ({ queryRunner, tablePrefix }) => {
+ const table = await queryRunner.getTable(`${tablePrefix}agent_background_job`);
+ expect(table?.foreignKeys.filter((key) => key.columnNames.includes('planId'))).toHaveLength(
+ 1,
+ );
+ });
+ });
+});