mirror of
https://github.com/lobehub/lobehub.git
synced 2026-03-26 13:19:34 +07:00
👷 build: add agent task system database schema (#13280)
* 🗃️ chore: add agent task system database schema Add 6 new tables for the Agent Task System: - tasks: core task with tree structure, heartbeat, scheduling - task_dependencies: inter-task dependency graph (blocks/relates) - task_documents: MVP workspace document pinning - task_topics: topic tracking with handoff (jsonb) and review results - task_comments: user/agent comments with author tracking (text id: cmt_) - briefs: unresolved notification system (text id: brf_) All sub-tables include userId FK for row-level user isolation. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * 🗃️ chore: add self-referential FK on tasks.parentTaskId (ON DELETE SET NULL) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * 🐛 fix: use foreignKey() for self-referential parentTaskId to avoid TS circular inference Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * 🗃️ chore: add FK on task_topics.topic_id → topics.id (ON DELETE SET NULL) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * 🐛 fix: resolve pre-existing TS type-check errors - Fix i18next defaultValue type (string | null → string) - Fix i18next options type mismatches - Fix fieldTags.webhook possibly undefined Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * 🗃️ chore: add FK on tasks.currentTopicId → topics.id (ON DELETE SET NULL) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * 🗃️ chore: add FK constraints for assignee, author, topic, and parent fields - tasks.assigneeUserId → users.id (ON DELETE SET NULL) - tasks.assigneeAgentId → agents.id (ON DELETE SET NULL) - tasks.parentTaskId → tasks.id (ON DELETE SET NULL) - tasks.currentTopicId → topics.id (ON DELETE SET NULL) - task_comments.authorUserId → users.id (ON DELETE SET NULL) - task_comments.authorAgentId → agents.id (ON DELETE SET NULL) - task_topics.topicId → topics.id (ON DELETE SET NULL) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * 🗃️ chore: change task_topics.topicId FK to ON DELETE CASCADE Topic deleted → task_topic mapping row removed (not just nulled). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * ♻️ refactor: use inline .references() for currentTopicId FK No circular inference issue — only parentTaskId (self-ref) needs foreignKey(). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * 🗃️ chore: add FK on task_comments.briefId and topicId (ON DELETE SET NULL) - task_comments.briefId → briefs.id (SET NULL) - task_comments.topicId → topics.id (SET NULL) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * ♻️ refactor: merge briefs table into task.ts to fix circular dependency brief.ts imported task.ts (briefs.taskId FK) and task.ts imported brief.ts (taskComments.briefId FK), causing circular dependency error. Merged briefs into task.ts since briefs are part of the task system. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * 🗃️ chore: add FK on tasks.createdByAgentId → agents.id (ON DELETE SET NULL) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1340,6 +1340,166 @@ table sessions {
|
||||
}
|
||||
}
|
||||
|
||||
table briefs {
|
||||
id text [pk, not null]
|
||||
user_id text [not null]
|
||||
task_id text
|
||||
cron_job_id text
|
||||
topic_id text
|
||||
agent_id text
|
||||
type text [not null]
|
||||
priority text [default: 'info']
|
||||
title text [not null]
|
||||
summary text [not null]
|
||||
artifacts jsonb
|
||||
actions jsonb
|
||||
resolved_action text
|
||||
resolved_comment text
|
||||
read_at "timestamp with time zone"
|
||||
resolved_at "timestamp with time zone"
|
||||
created_at "timestamp with time zone" [not null, default: `now()`]
|
||||
|
||||
indexes {
|
||||
user_id [name: 'briefs_user_id_idx']
|
||||
task_id [name: 'briefs_task_id_idx']
|
||||
cron_job_id [name: 'briefs_cron_job_id_idx']
|
||||
agent_id [name: 'briefs_agent_id_idx']
|
||||
type [name: 'briefs_type_idx']
|
||||
priority [name: 'briefs_priority_idx']
|
||||
(user_id, resolved_at) [name: 'briefs_unresolved_idx']
|
||||
}
|
||||
}
|
||||
|
||||
table task_comments {
|
||||
id text [pk, not null]
|
||||
task_id text [not null]
|
||||
user_id text [not null]
|
||||
author_user_id text
|
||||
author_agent_id text
|
||||
content text [not null]
|
||||
editor_data jsonb
|
||||
brief_id text
|
||||
topic_id text
|
||||
accessed_at "timestamp with time zone" [not null, default: `now()`]
|
||||
created_at "timestamp with time zone" [not null, default: `now()`]
|
||||
updated_at "timestamp with time zone" [not null, default: `now()`]
|
||||
|
||||
indexes {
|
||||
task_id [name: 'task_comments_task_id_idx']
|
||||
user_id [name: 'task_comments_user_id_idx']
|
||||
author_user_id [name: 'task_comments_author_user_id_idx']
|
||||
author_agent_id [name: 'task_comments_agent_id_idx']
|
||||
brief_id [name: 'task_comments_brief_id_idx']
|
||||
topic_id [name: 'task_comments_topic_id_idx']
|
||||
}
|
||||
}
|
||||
|
||||
table task_dependencies {
|
||||
id uuid [pk, not null, default: `gen_random_uuid()`]
|
||||
task_id text [not null]
|
||||
depends_on_id text [not null]
|
||||
user_id text [not null]
|
||||
type text [not null, default: 'blocks']
|
||||
condition jsonb
|
||||
created_at "timestamp with time zone" [not null, default: `now()`]
|
||||
|
||||
indexes {
|
||||
(task_id, depends_on_id) [name: 'task_deps_unique_idx', unique]
|
||||
task_id [name: 'task_deps_task_id_idx']
|
||||
depends_on_id [name: 'task_deps_depends_on_id_idx']
|
||||
user_id [name: 'task_deps_user_id_idx']
|
||||
}
|
||||
}
|
||||
|
||||
table task_documents {
|
||||
id uuid [pk, not null, default: `gen_random_uuid()`]
|
||||
task_id text [not null]
|
||||
document_id text [not null]
|
||||
user_id text [not null]
|
||||
pinned_by text [not null, default: 'agent']
|
||||
created_at "timestamp with time zone" [not null, default: `now()`]
|
||||
|
||||
indexes {
|
||||
(task_id, document_id) [name: 'task_docs_unique_idx', unique]
|
||||
task_id [name: 'task_docs_task_id_idx']
|
||||
document_id [name: 'task_docs_document_id_idx']
|
||||
user_id [name: 'task_docs_user_id_idx']
|
||||
}
|
||||
}
|
||||
|
||||
table task_topics {
|
||||
id uuid [pk, not null, default: `gen_random_uuid()`]
|
||||
task_id text [not null]
|
||||
topic_id text
|
||||
user_id text [not null]
|
||||
seq integer [not null]
|
||||
operation_id text
|
||||
status text [not null, default: 'running']
|
||||
handoff jsonb
|
||||
review_passed integer
|
||||
review_score integer
|
||||
review_scores jsonb
|
||||
review_iteration integer
|
||||
reviewed_at "timestamp with time zone"
|
||||
accessed_at "timestamp with time zone" [not null, default: `now()`]
|
||||
created_at "timestamp with time zone" [not null, default: `now()`]
|
||||
updated_at "timestamp with time zone" [not null, default: `now()`]
|
||||
|
||||
indexes {
|
||||
(task_id, topic_id) [name: 'task_topics_unique_idx', unique]
|
||||
task_id [name: 'task_topics_task_id_idx']
|
||||
topic_id [name: 'task_topics_topic_id_idx']
|
||||
user_id [name: 'task_topics_user_id_idx']
|
||||
(task_id, status) [name: 'task_topics_status_idx']
|
||||
}
|
||||
}
|
||||
|
||||
table tasks {
|
||||
id text [pk, not null]
|
||||
identifier text [not null]
|
||||
seq integer [not null]
|
||||
created_by_user_id text [not null]
|
||||
created_by_agent_id text
|
||||
assignee_user_id text
|
||||
assignee_agent_id text
|
||||
parent_task_id text
|
||||
name text
|
||||
description varchar(255)
|
||||
instruction text [not null]
|
||||
status text [not null, default: 'backlog']
|
||||
priority integer [default: 0]
|
||||
sort_order integer [default: 0]
|
||||
heartbeat_interval integer [default: 300]
|
||||
heartbeat_timeout integer
|
||||
last_heartbeat_at "timestamp with time zone"
|
||||
schedule_pattern text
|
||||
schedule_timezone text [default: 'UTC']
|
||||
total_topics integer [default: 0]
|
||||
max_topics integer
|
||||
current_topic_id text
|
||||
context jsonb [default: `{}`]
|
||||
config jsonb [default: `{}`]
|
||||
error text
|
||||
started_at "timestamp with time zone"
|
||||
completed_at "timestamp with time zone"
|
||||
accessed_at "timestamp with time zone" [not null, default: `now()`]
|
||||
created_at "timestamp with time zone" [not null, default: `now()`]
|
||||
updated_at "timestamp with time zone" [not null, default: `now()`]
|
||||
|
||||
indexes {
|
||||
|
||||
(identifier, created_by_user_id) [name: 'tasks_identifier_idx', unique]
|
||||
created_by_user_id [name: 'tasks_created_by_user_id_idx']
|
||||
created_by_agent_id [name: 'tasks_created_by_agent_id_idx']
|
||||
assignee_user_id [name: 'tasks_assignee_user_id_idx']
|
||||
assignee_agent_id [name: 'tasks_assignee_agent_id_idx']
|
||||
parent_task_id [name: 'tasks_parent_task_id_idx']
|
||||
status [name: 'tasks_status_idx']
|
||||
priority [name: 'tasks_priority_idx']
|
||||
(status, last_heartbeat_at) [name: 'tasks_heartbeat_idx']
|
||||
}
|
||||
}
|
||||
|
||||
table threads {
|
||||
id text [pk, not null]
|
||||
title text
|
||||
|
||||
189
packages/database/migrations/0095_add_agent_task_system.sql
Normal file
189
packages/database/migrations/0095_add_agent_task_system.sql
Normal file
@@ -0,0 +1,189 @@
|
||||
CREATE TABLE IF NOT EXISTS "briefs" (
|
||||
"id" text PRIMARY KEY NOT NULL,
|
||||
"user_id" text NOT NULL,
|
||||
"task_id" text,
|
||||
"cron_job_id" text,
|
||||
"topic_id" text,
|
||||
"agent_id" text,
|
||||
"type" text NOT NULL,
|
||||
"priority" text DEFAULT 'info',
|
||||
"title" text NOT NULL,
|
||||
"summary" text NOT NULL,
|
||||
"artifacts" jsonb,
|
||||
"actions" jsonb,
|
||||
"resolved_action" text,
|
||||
"resolved_comment" text,
|
||||
"read_at" timestamp with time zone,
|
||||
"resolved_at" timestamp with time zone,
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE IF NOT EXISTS "task_comments" (
|
||||
"id" text PRIMARY KEY NOT NULL,
|
||||
"task_id" text NOT NULL,
|
||||
"user_id" text NOT NULL,
|
||||
"author_user_id" text,
|
||||
"author_agent_id" text,
|
||||
"content" text NOT NULL,
|
||||
"editor_data" jsonb,
|
||||
"brief_id" text,
|
||||
"topic_id" text,
|
||||
"accessed_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
"updated_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE IF NOT EXISTS "task_dependencies" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"task_id" text NOT NULL,
|
||||
"depends_on_id" text NOT NULL,
|
||||
"user_id" text NOT NULL,
|
||||
"type" text DEFAULT 'blocks' NOT NULL,
|
||||
"condition" jsonb,
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE IF NOT EXISTS "task_documents" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"task_id" text NOT NULL,
|
||||
"document_id" text NOT NULL,
|
||||
"user_id" text NOT NULL,
|
||||
"pinned_by" text DEFAULT 'agent' NOT NULL,
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE IF NOT EXISTS "task_topics" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"task_id" text NOT NULL,
|
||||
"topic_id" text,
|
||||
"user_id" text NOT NULL,
|
||||
"seq" integer NOT NULL,
|
||||
"operation_id" text,
|
||||
"status" text DEFAULT 'running' NOT NULL,
|
||||
"handoff" jsonb,
|
||||
"review_passed" integer,
|
||||
"review_score" integer,
|
||||
"review_scores" jsonb,
|
||||
"review_iteration" integer,
|
||||
"reviewed_at" timestamp with time zone,
|
||||
"accessed_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
"updated_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE IF NOT EXISTS "tasks" (
|
||||
"id" text PRIMARY KEY NOT NULL,
|
||||
"identifier" text NOT NULL,
|
||||
"seq" integer NOT NULL,
|
||||
"created_by_user_id" text NOT NULL,
|
||||
"created_by_agent_id" text,
|
||||
"assignee_user_id" text,
|
||||
"assignee_agent_id" text,
|
||||
"parent_task_id" text,
|
||||
"name" text,
|
||||
"description" varchar(255),
|
||||
"instruction" text NOT NULL,
|
||||
"status" text DEFAULT 'backlog' NOT NULL,
|
||||
"priority" integer DEFAULT 0,
|
||||
"sort_order" integer DEFAULT 0,
|
||||
"heartbeat_interval" integer DEFAULT 300,
|
||||
"heartbeat_timeout" integer,
|
||||
"last_heartbeat_at" timestamp with time zone,
|
||||
"schedule_pattern" text,
|
||||
"schedule_timezone" text DEFAULT 'UTC',
|
||||
"total_topics" integer DEFAULT 0,
|
||||
"max_topics" integer,
|
||||
"current_topic_id" text,
|
||||
"context" jsonb DEFAULT '{}'::jsonb,
|
||||
"config" jsonb DEFAULT '{}'::jsonb,
|
||||
"error" text,
|
||||
"started_at" timestamp with time zone,
|
||||
"completed_at" timestamp with time zone,
|
||||
"accessed_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
"updated_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
ALTER TABLE "briefs" DROP CONSTRAINT IF EXISTS "briefs_user_id_users_id_fk";--> statement-breakpoint
|
||||
ALTER TABLE "briefs" ADD CONSTRAINT "briefs_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "briefs" DROP CONSTRAINT IF EXISTS "briefs_task_id_tasks_id_fk";--> statement-breakpoint
|
||||
ALTER TABLE "briefs" ADD CONSTRAINT "briefs_task_id_tasks_id_fk" FOREIGN KEY ("task_id") REFERENCES "public"."tasks"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "briefs" DROP CONSTRAINT IF EXISTS "briefs_cron_job_id_agent_cron_jobs_id_fk";--> statement-breakpoint
|
||||
ALTER TABLE "briefs" ADD CONSTRAINT "briefs_cron_job_id_agent_cron_jobs_id_fk" FOREIGN KEY ("cron_job_id") REFERENCES "public"."agent_cron_jobs"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "task_comments" DROP CONSTRAINT IF EXISTS "task_comments_task_id_tasks_id_fk";--> statement-breakpoint
|
||||
ALTER TABLE "task_comments" ADD CONSTRAINT "task_comments_task_id_tasks_id_fk" FOREIGN KEY ("task_id") REFERENCES "public"."tasks"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "task_comments" DROP CONSTRAINT IF EXISTS "task_comments_user_id_users_id_fk";--> statement-breakpoint
|
||||
ALTER TABLE "task_comments" ADD CONSTRAINT "task_comments_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "task_comments" DROP CONSTRAINT IF EXISTS "task_comments_author_user_id_users_id_fk";--> statement-breakpoint
|
||||
ALTER TABLE "task_comments" ADD CONSTRAINT "task_comments_author_user_id_users_id_fk" FOREIGN KEY ("author_user_id") REFERENCES "public"."users"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "task_comments" DROP CONSTRAINT IF EXISTS "task_comments_author_agent_id_agents_id_fk";--> statement-breakpoint
|
||||
ALTER TABLE "task_comments" ADD CONSTRAINT "task_comments_author_agent_id_agents_id_fk" FOREIGN KEY ("author_agent_id") REFERENCES "public"."agents"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "task_comments" DROP CONSTRAINT IF EXISTS "task_comments_brief_id_briefs_id_fk";--> statement-breakpoint
|
||||
ALTER TABLE "task_comments" ADD CONSTRAINT "task_comments_brief_id_briefs_id_fk" FOREIGN KEY ("brief_id") REFERENCES "public"."briefs"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "task_comments" DROP CONSTRAINT IF EXISTS "task_comments_topic_id_topics_id_fk";--> statement-breakpoint
|
||||
ALTER TABLE "task_comments" ADD CONSTRAINT "task_comments_topic_id_topics_id_fk" FOREIGN KEY ("topic_id") REFERENCES "public"."topics"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "task_dependencies" DROP CONSTRAINT IF EXISTS "task_dependencies_task_id_tasks_id_fk";--> statement-breakpoint
|
||||
ALTER TABLE "task_dependencies" ADD CONSTRAINT "task_dependencies_task_id_tasks_id_fk" FOREIGN KEY ("task_id") REFERENCES "public"."tasks"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "task_dependencies" DROP CONSTRAINT IF EXISTS "task_dependencies_depends_on_id_tasks_id_fk";--> statement-breakpoint
|
||||
ALTER TABLE "task_dependencies" ADD CONSTRAINT "task_dependencies_depends_on_id_tasks_id_fk" FOREIGN KEY ("depends_on_id") REFERENCES "public"."tasks"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "task_dependencies" DROP CONSTRAINT IF EXISTS "task_dependencies_user_id_users_id_fk";--> statement-breakpoint
|
||||
ALTER TABLE "task_dependencies" ADD CONSTRAINT "task_dependencies_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "task_documents" DROP CONSTRAINT IF EXISTS "task_documents_task_id_tasks_id_fk";--> statement-breakpoint
|
||||
ALTER TABLE "task_documents" ADD CONSTRAINT "task_documents_task_id_tasks_id_fk" FOREIGN KEY ("task_id") REFERENCES "public"."tasks"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "task_documents" DROP CONSTRAINT IF EXISTS "task_documents_document_id_documents_id_fk";--> statement-breakpoint
|
||||
ALTER TABLE "task_documents" ADD CONSTRAINT "task_documents_document_id_documents_id_fk" FOREIGN KEY ("document_id") REFERENCES "public"."documents"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "task_documents" DROP CONSTRAINT IF EXISTS "task_documents_user_id_users_id_fk";--> statement-breakpoint
|
||||
ALTER TABLE "task_documents" ADD CONSTRAINT "task_documents_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "task_topics" DROP CONSTRAINT IF EXISTS "task_topics_task_id_tasks_id_fk";--> statement-breakpoint
|
||||
ALTER TABLE "task_topics" ADD CONSTRAINT "task_topics_task_id_tasks_id_fk" FOREIGN KEY ("task_id") REFERENCES "public"."tasks"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "task_topics" DROP CONSTRAINT IF EXISTS "task_topics_topic_id_topics_id_fk";--> statement-breakpoint
|
||||
ALTER TABLE "task_topics" ADD CONSTRAINT "task_topics_topic_id_topics_id_fk" FOREIGN KEY ("topic_id") REFERENCES "public"."topics"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "task_topics" DROP CONSTRAINT IF EXISTS "task_topics_user_id_users_id_fk";--> statement-breakpoint
|
||||
ALTER TABLE "task_topics" ADD CONSTRAINT "task_topics_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "tasks" DROP CONSTRAINT IF EXISTS "tasks_created_by_user_id_users_id_fk";--> statement-breakpoint
|
||||
ALTER TABLE "tasks" ADD CONSTRAINT "tasks_created_by_user_id_users_id_fk" FOREIGN KEY ("created_by_user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "tasks" DROP CONSTRAINT IF EXISTS "tasks_created_by_agent_id_agents_id_fk";--> statement-breakpoint
|
||||
ALTER TABLE "tasks" ADD CONSTRAINT "tasks_created_by_agent_id_agents_id_fk" FOREIGN KEY ("created_by_agent_id") REFERENCES "public"."agents"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "tasks" DROP CONSTRAINT IF EXISTS "tasks_assignee_user_id_users_id_fk";--> statement-breakpoint
|
||||
ALTER TABLE "tasks" ADD CONSTRAINT "tasks_assignee_user_id_users_id_fk" FOREIGN KEY ("assignee_user_id") REFERENCES "public"."users"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "tasks" DROP CONSTRAINT IF EXISTS "tasks_assignee_agent_id_agents_id_fk";--> statement-breakpoint
|
||||
ALTER TABLE "tasks" ADD CONSTRAINT "tasks_assignee_agent_id_agents_id_fk" FOREIGN KEY ("assignee_agent_id") REFERENCES "public"."agents"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "tasks" DROP CONSTRAINT IF EXISTS "tasks_current_topic_id_topics_id_fk";--> statement-breakpoint
|
||||
ALTER TABLE "tasks" ADD CONSTRAINT "tasks_current_topic_id_topics_id_fk" FOREIGN KEY ("current_topic_id") REFERENCES "public"."topics"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "tasks" DROP CONSTRAINT IF EXISTS "tasks_parent_task_id_tasks_id_fk";--> statement-breakpoint
|
||||
ALTER TABLE "tasks" ADD CONSTRAINT "tasks_parent_task_id_tasks_id_fk" FOREIGN KEY ("parent_task_id") REFERENCES "public"."tasks"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint
|
||||
CREATE INDEX IF NOT EXISTS "briefs_user_id_idx" ON "briefs" USING btree ("user_id");--> statement-breakpoint
|
||||
CREATE INDEX IF NOT EXISTS "briefs_task_id_idx" ON "briefs" USING btree ("task_id");--> statement-breakpoint
|
||||
CREATE INDEX IF NOT EXISTS "briefs_cron_job_id_idx" ON "briefs" USING btree ("cron_job_id");--> statement-breakpoint
|
||||
CREATE INDEX IF NOT EXISTS "briefs_agent_id_idx" ON "briefs" USING btree ("agent_id");--> statement-breakpoint
|
||||
CREATE INDEX IF NOT EXISTS "briefs_type_idx" ON "briefs" USING btree ("type");--> statement-breakpoint
|
||||
CREATE INDEX IF NOT EXISTS "briefs_priority_idx" ON "briefs" USING btree ("priority");--> statement-breakpoint
|
||||
CREATE INDEX IF NOT EXISTS "briefs_unresolved_idx" ON "briefs" USING btree ("user_id","resolved_at");--> statement-breakpoint
|
||||
CREATE INDEX IF NOT EXISTS "task_comments_task_id_idx" ON "task_comments" USING btree ("task_id");--> statement-breakpoint
|
||||
CREATE INDEX IF NOT EXISTS "task_comments_user_id_idx" ON "task_comments" USING btree ("user_id");--> statement-breakpoint
|
||||
CREATE INDEX IF NOT EXISTS "task_comments_author_user_id_idx" ON "task_comments" USING btree ("author_user_id");--> statement-breakpoint
|
||||
CREATE INDEX IF NOT EXISTS "task_comments_agent_id_idx" ON "task_comments" USING btree ("author_agent_id");--> statement-breakpoint
|
||||
CREATE INDEX IF NOT EXISTS "task_comments_brief_id_idx" ON "task_comments" USING btree ("brief_id");--> statement-breakpoint
|
||||
CREATE INDEX IF NOT EXISTS "task_comments_topic_id_idx" ON "task_comments" USING btree ("topic_id");--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS "task_deps_unique_idx" ON "task_dependencies" USING btree ("task_id","depends_on_id");--> statement-breakpoint
|
||||
CREATE INDEX IF NOT EXISTS "task_deps_task_id_idx" ON "task_dependencies" USING btree ("task_id");--> statement-breakpoint
|
||||
CREATE INDEX IF NOT EXISTS "task_deps_depends_on_id_idx" ON "task_dependencies" USING btree ("depends_on_id");--> statement-breakpoint
|
||||
CREATE INDEX IF NOT EXISTS "task_deps_user_id_idx" ON "task_dependencies" USING btree ("user_id");--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS "task_docs_unique_idx" ON "task_documents" USING btree ("task_id","document_id");--> statement-breakpoint
|
||||
CREATE INDEX IF NOT EXISTS "task_docs_task_id_idx" ON "task_documents" USING btree ("task_id");--> statement-breakpoint
|
||||
CREATE INDEX IF NOT EXISTS "task_docs_document_id_idx" ON "task_documents" USING btree ("document_id");--> statement-breakpoint
|
||||
CREATE INDEX IF NOT EXISTS "task_docs_user_id_idx" ON "task_documents" USING btree ("user_id");--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS "task_topics_unique_idx" ON "task_topics" USING btree ("task_id","topic_id");--> statement-breakpoint
|
||||
CREATE INDEX IF NOT EXISTS "task_topics_task_id_idx" ON "task_topics" USING btree ("task_id");--> statement-breakpoint
|
||||
CREATE INDEX IF NOT EXISTS "task_topics_topic_id_idx" ON "task_topics" USING btree ("topic_id");--> statement-breakpoint
|
||||
CREATE INDEX IF NOT EXISTS "task_topics_user_id_idx" ON "task_topics" USING btree ("user_id");--> statement-breakpoint
|
||||
CREATE INDEX IF NOT EXISTS "task_topics_status_idx" ON "task_topics" USING btree ("task_id","status");--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS "tasks_identifier_idx" ON "tasks" USING btree ("identifier","created_by_user_id");--> statement-breakpoint
|
||||
CREATE INDEX IF NOT EXISTS "tasks_created_by_user_id_idx" ON "tasks" USING btree ("created_by_user_id");--> statement-breakpoint
|
||||
CREATE INDEX IF NOT EXISTS "tasks_created_by_agent_id_idx" ON "tasks" USING btree ("created_by_agent_id");--> statement-breakpoint
|
||||
CREATE INDEX IF NOT EXISTS "tasks_assignee_user_id_idx" ON "tasks" USING btree ("assignee_user_id");--> statement-breakpoint
|
||||
CREATE INDEX IF NOT EXISTS "tasks_assignee_agent_id_idx" ON "tasks" USING btree ("assignee_agent_id");--> statement-breakpoint
|
||||
CREATE INDEX IF NOT EXISTS "tasks_parent_task_id_idx" ON "tasks" USING btree ("parent_task_id");--> statement-breakpoint
|
||||
CREATE INDEX IF NOT EXISTS "tasks_status_idx" ON "tasks" USING btree ("status");--> statement-breakpoint
|
||||
CREATE INDEX IF NOT EXISTS "tasks_priority_idx" ON "tasks" USING btree ("priority");--> statement-breakpoint
|
||||
CREATE INDEX IF NOT EXISTS "tasks_heartbeat_idx" ON "tasks" USING btree ("status","last_heartbeat_at");
|
||||
14306
packages/database/migrations/meta/0095_snapshot.json
Normal file
14306
packages/database/migrations/meta/0095_snapshot.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -665,6 +665,13 @@
|
||||
"when": 1773764776073,
|
||||
"tag": "0094_agent_bot_providers_add_settings",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 95,
|
||||
"version": "7",
|
||||
"when": 1774502940061,
|
||||
"tag": "0095_add_agent_task_system",
|
||||
"breakpoints": true
|
||||
}
|
||||
],
|
||||
"version": "6"
|
||||
|
||||
@@ -19,6 +19,7 @@ export * from './ragEvals';
|
||||
export * from './rbac';
|
||||
export * from './relations';
|
||||
export * from './session';
|
||||
export * from './task';
|
||||
export * from './topic';
|
||||
export * from './user';
|
||||
export * from './userMemories';
|
||||
|
||||
309
packages/database/src/schemas/task.ts
Normal file
309
packages/database/src/schemas/task.ts
Normal file
@@ -0,0 +1,309 @@
|
||||
import {
|
||||
foreignKey,
|
||||
index,
|
||||
integer,
|
||||
jsonb,
|
||||
pgTable,
|
||||
text,
|
||||
uniqueIndex,
|
||||
uuid,
|
||||
} from 'drizzle-orm/pg-core';
|
||||
|
||||
import { idGenerator } from '../utils/idGenerator';
|
||||
import { createdAt, timestamps, timestamptz, varchar255 } from './_helpers';
|
||||
import { agents } from './agent';
|
||||
import { agentCronJobs } from './agentCronJob';
|
||||
import { documents } from './file';
|
||||
import { topics } from './topic';
|
||||
import { users } from './user';
|
||||
|
||||
// ── Tasks ────────────────────────────────────────────────
|
||||
|
||||
export const tasks = pgTable(
|
||||
'tasks',
|
||||
{
|
||||
id: text('id')
|
||||
.primaryKey()
|
||||
.$defaultFn(() => idGenerator('tasks'))
|
||||
.notNull(),
|
||||
|
||||
// Workspace-level identifier (e.g. 'T-1', 'PROJ-42')
|
||||
identifier: text('identifier').notNull(),
|
||||
seq: integer('seq').notNull(),
|
||||
// Creator (user or agent)
|
||||
createdByUserId: text('created_by_user_id')
|
||||
.references(() => users.id, { onDelete: 'cascade' })
|
||||
.notNull(),
|
||||
createdByAgentId: text('created_by_agent_id').references(() => agents.id, {
|
||||
onDelete: 'set null',
|
||||
}),
|
||||
|
||||
// Assignee (user and agent can coexist, both nullable)
|
||||
assigneeUserId: text('assignee_user_id').references(() => users.id, { onDelete: 'set null' }),
|
||||
assigneeAgentId: text('assignee_agent_id').references(() => agents.id, {
|
||||
onDelete: 'set null',
|
||||
}),
|
||||
|
||||
// Tree structure (self-referencing, no depth limit)
|
||||
parentTaskId: text('parent_task_id'),
|
||||
|
||||
// Task definition
|
||||
name: text('name'),
|
||||
description: varchar255('description'),
|
||||
instruction: text('instruction').notNull(),
|
||||
|
||||
// Lifecycle (same state machine for user and agent)
|
||||
// 'backlog' | 'running' | 'paused' | 'completed' | 'failed' | 'canceled'
|
||||
status: text('status').notNull().default('backlog'),
|
||||
priority: integer('priority').default(0), // 'no' | 'urgent' | 'high' | 'normal' | 'low'
|
||||
sortOrder: integer('sort_order').default(0), // manual sort within parent, lower = higher
|
||||
|
||||
// Heartbeat
|
||||
heartbeatInterval: integer('heartbeat_interval').default(300), // seconds
|
||||
heartbeatTimeout: integer('heartbeat_timeout'), // seconds, null = disabled (default off)
|
||||
lastHeartbeatAt: timestamptz('last_heartbeat_at'),
|
||||
|
||||
// Schedule (optional)
|
||||
schedulePattern: text('schedule_pattern'),
|
||||
scheduleTimezone: text('schedule_timezone').default('UTC'),
|
||||
|
||||
// Topic management
|
||||
totalTopics: integer('total_topics').default(0),
|
||||
maxTopics: integer('max_topics'), // null = unlimited
|
||||
currentTopicId: text('current_topic_id').references(() => topics.id, { onDelete: 'set null' }),
|
||||
|
||||
// Context & config (each task independent, no inheritance from parent)
|
||||
context: jsonb('context').default({}),
|
||||
config: jsonb('config').default({}), // CheckpointConfig, ReviewConfig, etc.
|
||||
error: text('error'),
|
||||
|
||||
// Timestamps
|
||||
startedAt: timestamptz('started_at'),
|
||||
completedAt: timestamptz('completed_at'),
|
||||
...timestamps,
|
||||
},
|
||||
(t) => [
|
||||
// Self-referential FK (defined here to avoid TS circular inference)
|
||||
foreignKey({
|
||||
columns: [t.parentTaskId],
|
||||
foreignColumns: [t.id],
|
||||
name: 'tasks_parent_task_id_tasks_id_fk',
|
||||
}).onDelete('set null'),
|
||||
uniqueIndex('tasks_identifier_idx').on(t.identifier, t.createdByUserId),
|
||||
index('tasks_created_by_user_id_idx').on(t.createdByUserId),
|
||||
index('tasks_created_by_agent_id_idx').on(t.createdByAgentId),
|
||||
index('tasks_assignee_user_id_idx').on(t.assigneeUserId),
|
||||
index('tasks_assignee_agent_id_idx').on(t.assigneeAgentId),
|
||||
index('tasks_parent_task_id_idx').on(t.parentTaskId),
|
||||
index('tasks_status_idx').on(t.status),
|
||||
index('tasks_priority_idx').on(t.priority),
|
||||
index('tasks_heartbeat_idx').on(t.status, t.lastHeartbeatAt),
|
||||
],
|
||||
);
|
||||
|
||||
export type NewTask = typeof tasks.$inferInsert;
|
||||
export type TaskItem = typeof tasks.$inferSelect;
|
||||
|
||||
// ── Task Dependencies ────────────────────────────────────
|
||||
|
||||
export const taskDependencies = pgTable(
|
||||
'task_dependencies',
|
||||
{
|
||||
id: uuid('id').defaultRandom().primaryKey().notNull(),
|
||||
|
||||
taskId: text('task_id')
|
||||
.references(() => tasks.id, { onDelete: 'cascade' })
|
||||
.notNull(),
|
||||
dependsOnId: text('depends_on_id')
|
||||
.references(() => tasks.id, { onDelete: 'cascade' })
|
||||
.notNull(),
|
||||
userId: text('user_id')
|
||||
.references(() => users.id, { onDelete: 'cascade' })
|
||||
.notNull(),
|
||||
|
||||
// 'blocks' | 'relates'
|
||||
type: text('type').notNull().default('blocks'),
|
||||
|
||||
// Reserved for conditional dependencies: {"on": "success"} / {"on": "failure"}
|
||||
condition: jsonb('condition'),
|
||||
|
||||
createdAt: createdAt(),
|
||||
},
|
||||
(t) => [
|
||||
uniqueIndex('task_deps_unique_idx').on(t.taskId, t.dependsOnId),
|
||||
index('task_deps_task_id_idx').on(t.taskId),
|
||||
index('task_deps_depends_on_id_idx').on(t.dependsOnId),
|
||||
index('task_deps_user_id_idx').on(t.userId),
|
||||
],
|
||||
);
|
||||
|
||||
export type NewTaskDependency = typeof taskDependencies.$inferInsert;
|
||||
export type TaskDependencyItem = typeof taskDependencies.$inferSelect;
|
||||
|
||||
// ── Task Documents (MVP Workspace) ───────────────────────
|
||||
|
||||
export const taskDocuments = pgTable(
|
||||
'task_documents',
|
||||
{
|
||||
id: uuid('id').defaultRandom().primaryKey().notNull(),
|
||||
taskId: text('task_id')
|
||||
.references(() => tasks.id, { onDelete: 'cascade' })
|
||||
.notNull(),
|
||||
documentId: text('document_id')
|
||||
.references(() => documents.id, { onDelete: 'cascade' })
|
||||
.notNull(),
|
||||
userId: text('user_id')
|
||||
.references(() => users.id, { onDelete: 'cascade' })
|
||||
.notNull(),
|
||||
|
||||
// 'agent' | 'user' | 'system'
|
||||
pinnedBy: text('pinned_by').notNull().default('agent'),
|
||||
|
||||
createdAt: createdAt(),
|
||||
},
|
||||
(t) => [
|
||||
uniqueIndex('task_docs_unique_idx').on(t.taskId, t.documentId),
|
||||
index('task_docs_task_id_idx').on(t.taskId),
|
||||
index('task_docs_document_id_idx').on(t.documentId),
|
||||
index('task_docs_user_id_idx').on(t.userId),
|
||||
],
|
||||
);
|
||||
|
||||
export type NewTaskDocument = typeof taskDocuments.$inferInsert;
|
||||
export type TaskDocumentItem = typeof taskDocuments.$inferSelect;
|
||||
|
||||
// ── Task Topics ─────────────────────────────────────────
|
||||
|
||||
export const taskTopics = pgTable(
|
||||
'task_topics',
|
||||
{
|
||||
id: uuid('id').defaultRandom().primaryKey().notNull(),
|
||||
taskId: text('task_id')
|
||||
.references(() => tasks.id, { onDelete: 'cascade' })
|
||||
.notNull(),
|
||||
topicId: text('topic_id').references(() => topics.id, { onDelete: 'cascade' }),
|
||||
userId: text('user_id')
|
||||
.references(() => users.id, { onDelete: 'cascade' })
|
||||
.notNull(),
|
||||
|
||||
seq: integer('seq').notNull(), // topic sequence within task (1, 2, 3...)
|
||||
operationId: text('operation_id'), // agent execution operation ID
|
||||
// 'running' | 'completed' | 'failed' | 'timeout' | 'canceled'
|
||||
status: text('status').notNull().default('running'),
|
||||
|
||||
// Handoff (populated after topic completes via LLM summarization)
|
||||
// { title, summary, keyFindings: string[], nextAction }
|
||||
handoff: jsonb('handoff'),
|
||||
|
||||
// Review results (populated after topic completes + review runs)
|
||||
reviewPassed: integer('review_passed'), // 1 = passed, 0 = failed, null = not reviewed
|
||||
reviewScore: integer('review_score'), // overall score 0-100
|
||||
reviewScores: jsonb('review_scores'), // [{rubricId, score, passed, reason}]
|
||||
reviewIteration: integer('review_iteration'), // which iteration (1, 2, 3...)
|
||||
reviewedAt: timestamptz('reviewed_at'),
|
||||
|
||||
...timestamps,
|
||||
},
|
||||
(t) => [
|
||||
uniqueIndex('task_topics_unique_idx').on(t.taskId, t.topicId),
|
||||
index('task_topics_task_id_idx').on(t.taskId),
|
||||
index('task_topics_topic_id_idx').on(t.topicId),
|
||||
index('task_topics_user_id_idx').on(t.userId),
|
||||
index('task_topics_status_idx').on(t.taskId, t.status),
|
||||
],
|
||||
);
|
||||
|
||||
export type NewTaskTopic = typeof taskTopics.$inferInsert;
|
||||
export type TaskTopicItem = typeof taskTopics.$inferSelect;
|
||||
|
||||
// ── Briefs ─────────────────────────────────────────────
|
||||
|
||||
export const briefs = pgTable(
|
||||
'briefs',
|
||||
{
|
||||
id: text('id')
|
||||
.primaryKey()
|
||||
.$defaultFn(() => idGenerator('briefs'))
|
||||
.notNull(),
|
||||
userId: text('user_id')
|
||||
.references(() => users.id, { onDelete: 'cascade' })
|
||||
.notNull(),
|
||||
|
||||
// Source (polymorphic, fill as needed)
|
||||
taskId: text('task_id').references(() => tasks.id, { onDelete: 'cascade' }),
|
||||
cronJobId: text('cron_job_id').references(() => agentCronJobs.id, { onDelete: 'cascade' }),
|
||||
topicId: text('topic_id'),
|
||||
agentId: text('agent_id'),
|
||||
|
||||
// Content
|
||||
type: text('type').notNull(), // 'decision' | 'result' | 'insight' | 'error'
|
||||
priority: text('priority').default('info'), // 'urgent' | 'normal' | 'info'
|
||||
title: text('title').notNull(),
|
||||
summary: text('summary').notNull(),
|
||||
artifacts: jsonb('artifacts'), // document ids
|
||||
actions: jsonb('actions'), // BriefAction[]
|
||||
|
||||
// Resolution
|
||||
resolvedAction: text('resolved_action'),
|
||||
resolvedComment: text('resolved_comment'),
|
||||
readAt: timestamptz('read_at'),
|
||||
resolvedAt: timestamptz('resolved_at'),
|
||||
|
||||
createdAt: createdAt(),
|
||||
},
|
||||
(t) => [
|
||||
index('briefs_user_id_idx').on(t.userId),
|
||||
index('briefs_task_id_idx').on(t.taskId),
|
||||
index('briefs_cron_job_id_idx').on(t.cronJobId),
|
||||
index('briefs_agent_id_idx').on(t.agentId),
|
||||
index('briefs_type_idx').on(t.type),
|
||||
index('briefs_priority_idx').on(t.priority),
|
||||
index('briefs_unresolved_idx').on(t.userId, t.resolvedAt),
|
||||
],
|
||||
);
|
||||
|
||||
export type NewBrief = typeof briefs.$inferInsert;
|
||||
export type BriefItem = typeof briefs.$inferSelect;
|
||||
|
||||
// ── Task Comments ───────────────────────────────────────
|
||||
|
||||
export const taskComments = pgTable(
|
||||
'task_comments',
|
||||
{
|
||||
id: text('id')
|
||||
.primaryKey()
|
||||
.$defaultFn(() => idGenerator('taskComments'))
|
||||
.notNull(),
|
||||
taskId: text('task_id')
|
||||
.references(() => tasks.id, { onDelete: 'cascade' })
|
||||
.notNull(),
|
||||
userId: text('user_id')
|
||||
.references(() => users.id, { onDelete: 'cascade' })
|
||||
.notNull(),
|
||||
|
||||
// Author (user or agent, both nullable)
|
||||
authorUserId: text('author_user_id').references(() => users.id, { onDelete: 'set null' }),
|
||||
authorAgentId: text('author_agent_id').references(() => agents.id, { onDelete: 'set null' }),
|
||||
|
||||
// Content
|
||||
content: text('content').notNull(),
|
||||
editorData: jsonb('editor_data'),
|
||||
|
||||
// Optional references
|
||||
briefId: text('brief_id').references(() => briefs.id, { onDelete: 'set null' }),
|
||||
topicId: text('topic_id').references(() => topics.id, { onDelete: 'set null' }),
|
||||
|
||||
...timestamps,
|
||||
},
|
||||
(t) => [
|
||||
index('task_comments_task_id_idx').on(t.taskId),
|
||||
index('task_comments_user_id_idx').on(t.userId),
|
||||
index('task_comments_author_user_id_idx').on(t.authorUserId),
|
||||
index('task_comments_agent_id_idx').on(t.authorAgentId),
|
||||
index('task_comments_brief_id_idx').on(t.briefId),
|
||||
index('task_comments_topic_id_idx').on(t.topicId),
|
||||
],
|
||||
);
|
||||
|
||||
export type NewTaskComment = typeof taskComments.$inferInsert;
|
||||
export type TaskCommentItem = typeof taskComments.$inferSelect;
|
||||
@@ -8,6 +8,9 @@ export const createNanoId = (size = 8) =>
|
||||
const prefixes = {
|
||||
agentCronJobs: 'cron',
|
||||
agentSkills: 'skl',
|
||||
briefs: 'brf',
|
||||
taskComments: 'cmt',
|
||||
tasks: 'task',
|
||||
agents: 'agt',
|
||||
budget: 'bgt',
|
||||
chatGroups: 'cg',
|
||||
|
||||
@@ -26,7 +26,7 @@ const FailedPage = () => {
|
||||
<Flexbox gap={8}>
|
||||
<Text fontSize={16} type="secondary">
|
||||
{t('error.desc', {
|
||||
reason: t(`error.reason.${reason}` as any, { defaultValue: reason }),
|
||||
reason: t(`error.reason.${reason}` as any, { defaultValue: reason ?? '' }),
|
||||
})}
|
||||
</Text>
|
||||
{!!errorMessage && <Highlighter language={'log'}>{errorMessage}</Highlighter>}
|
||||
|
||||
@@ -64,7 +64,8 @@ export const usePluginContext = (): PluginContext => {
|
||||
return findTopicAcrossAllSessions(state.topicDataMap, topicId);
|
||||
},
|
||||
|
||||
t: (key: string, options?: Record<string, unknown>) => t(key as any, options) as string,
|
||||
t: (key: string, options?: Record<string, unknown>) =>
|
||||
t(key as any, options as any) as string,
|
||||
}),
|
||||
[agentMap, topicDataMap, sessionGroups, documents, t],
|
||||
);
|
||||
|
||||
@@ -69,7 +69,10 @@ export const useCronJobDropdownMenu = (
|
||||
modal.confirm({
|
||||
cancelText: t('cancel', { ns: 'common' }),
|
||||
centered: true,
|
||||
content: t('agentCronJobs.confirmClearTopics' as any, { count: topics.length }),
|
||||
content: t(
|
||||
'agentCronJobs.confirmClearTopics' as any,
|
||||
{ count: topics.length } as any,
|
||||
),
|
||||
okButtonProps: { danger: true },
|
||||
okText: t('ok', { ns: 'common' }),
|
||||
onOk: handleClearTopics,
|
||||
|
||||
@@ -217,7 +217,7 @@ const Body = memo<BodyProps>(
|
||||
components={{ bold: <strong /> }}
|
||||
i18nKey="channel.endpointUrlHint"
|
||||
ns="agent"
|
||||
values={{ fieldName: provider.fieldTags.webhook, name: provider.name }}
|
||||
values={{ fieldName: provider.fieldTags.webhook ?? '', name: provider.name }}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
|
||||
Reference in New Issue
Block a user