mirror of
https://github.com/lobehub/lobehub.git
synced 2026-03-26 13:19:34 +07:00
👷 build: add agent_documents table (#12944)
This commit is contained in:
50
packages/database/migrations/0092_add_agent_documents.sql
Normal file
50
packages/database/migrations/0092_add_agent_documents.sql
Normal file
@@ -0,0 +1,50 @@
|
||||
CREATE TABLE IF NOT EXISTS "agent_documents" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"user_id" text NOT NULL,
|
||||
"agent_id" text NOT NULL,
|
||||
"document_id" varchar(255) NOT NULL,
|
||||
"template_id" varchar(100),
|
||||
"access_self" integer DEFAULT 31 NOT NULL,
|
||||
"access_shared" integer DEFAULT 0 NOT NULL,
|
||||
"access_public" integer DEFAULT 0 NOT NULL,
|
||||
"policy_load" varchar(30) DEFAULT 'always' NOT NULL,
|
||||
"policy" jsonb,
|
||||
"policy_load_position" varchar(50) DEFAULT 'before-first-user' NOT NULL,
|
||||
"policy_load_format" varchar(20) DEFAULT 'raw' NOT NULL,
|
||||
"policy_load_rule" varchar(50) DEFAULT 'always' NOT NULL,
|
||||
"deleted_at" timestamp with time zone,
|
||||
"deleted_by_user_id" text,
|
||||
"deleted_by_agent_id" text,
|
||||
"delete_reason" text,
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
"updated_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
|
||||
ALTER TABLE "agent_documents" DROP CONSTRAINT IF EXISTS "agent_documents_user_id_users_id_fk";--> statement-breakpoint
|
||||
ALTER TABLE "agent_documents" ADD CONSTRAINT "agent_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 "agent_documents" DROP CONSTRAINT IF EXISTS "agent_documents_agent_id_agents_id_fk";--> statement-breakpoint
|
||||
ALTER TABLE "agent_documents" ADD CONSTRAINT "agent_documents_agent_id_agents_id_fk" FOREIGN KEY ("agent_id") REFERENCES "public"."agents"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "agent_documents" DROP CONSTRAINT IF EXISTS "agent_documents_document_id_documents_id_fk";--> statement-breakpoint
|
||||
ALTER TABLE "agent_documents" ADD CONSTRAINT "agent_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 "agent_documents" DROP CONSTRAINT IF EXISTS "agent_documents_deleted_by_user_id_users_id_fk";--> statement-breakpoint
|
||||
ALTER TABLE "agent_documents" ADD CONSTRAINT "agent_documents_deleted_by_user_id_users_id_fk" FOREIGN KEY ("deleted_by_user_id") REFERENCES "public"."users"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "agent_documents" DROP CONSTRAINT IF EXISTS "agent_documents_deleted_by_agent_id_agents_id_fk";--> statement-breakpoint
|
||||
ALTER TABLE "agent_documents" ADD CONSTRAINT "agent_documents_deleted_by_agent_id_agents_id_fk" FOREIGN KEY ("deleted_by_agent_id") REFERENCES "public"."agents"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint
|
||||
|
||||
CREATE INDEX IF NOT EXISTS "agent_documents_user_id_idx" ON "agent_documents" USING btree ("user_id");--> statement-breakpoint
|
||||
CREATE INDEX IF NOT EXISTS "agent_documents_agent_id_idx" ON "agent_documents" USING btree ("agent_id");--> statement-breakpoint
|
||||
CREATE INDEX IF NOT EXISTS "agent_documents_access_self_idx" ON "agent_documents" USING btree ("access_self");--> statement-breakpoint
|
||||
CREATE INDEX IF NOT EXISTS "agent_documents_access_shared_idx" ON "agent_documents" USING btree ("access_shared");--> statement-breakpoint
|
||||
CREATE INDEX IF NOT EXISTS "agent_documents_access_public_idx" ON "agent_documents" USING btree ("access_public");--> statement-breakpoint
|
||||
CREATE INDEX IF NOT EXISTS "agent_documents_policy_load_idx" ON "agent_documents" USING btree ("policy_load");--> statement-breakpoint
|
||||
CREATE INDEX IF NOT EXISTS "agent_documents_template_id_idx" ON "agent_documents" USING btree ("template_id");--> statement-breakpoint
|
||||
CREATE INDEX IF NOT EXISTS "agent_documents_policy_load_position_idx" ON "agent_documents" USING btree ("policy_load_position");--> statement-breakpoint
|
||||
CREATE INDEX IF NOT EXISTS "agent_documents_policy_load_format_idx" ON "agent_documents" USING btree ("policy_load_format");--> statement-breakpoint
|
||||
CREATE INDEX IF NOT EXISTS "agent_documents_policy_load_rule_idx" ON "agent_documents" USING btree ("policy_load_rule");--> statement-breakpoint
|
||||
CREATE INDEX IF NOT EXISTS "agent_documents_agent_load_position_idx" ON "agent_documents" USING btree ("agent_id","policy_load_position");--> statement-breakpoint
|
||||
CREATE INDEX IF NOT EXISTS "agent_documents_deleted_at_idx" ON "agent_documents" USING btree ("deleted_at");--> statement-breakpoint
|
||||
CREATE INDEX IF NOT EXISTS "agent_documents_agent_autoload_deleted_idx" ON "agent_documents" USING btree ("agent_id","deleted_at","policy_load");--> statement-breakpoint
|
||||
CREATE INDEX IF NOT EXISTS "agent_documents_document_id_idx" ON "agent_documents" USING btree ("document_id");--> statement-breakpoint
|
||||
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS "agent_documents_agent_document_user_unique" ON "agent_documents" USING btree ("agent_id","document_id","user_id");
|
||||
12871
packages/database/migrations/meta/0092_snapshot.json
Normal file
12871
packages/database/migrations/meta/0092_snapshot.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -644,6 +644,13 @@
|
||||
"when": 1773308533505,
|
||||
"tag": "0091_topics_add_description",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 92,
|
||||
"version": "7",
|
||||
"when": 1773419250145,
|
||||
"tag": "0092_add_agent_documents",
|
||||
"breakpoints": true
|
||||
}
|
||||
],
|
||||
"version": "6"
|
||||
|
||||
183
packages/database/src/schemas/agentDocuments.ts
Normal file
183
packages/database/src/schemas/agentDocuments.ts
Normal file
@@ -0,0 +1,183 @@
|
||||
import {
|
||||
index,
|
||||
integer,
|
||||
jsonb,
|
||||
pgTable,
|
||||
text,
|
||||
uniqueIndex,
|
||||
uuid,
|
||||
varchar,
|
||||
} from 'drizzle-orm/pg-core';
|
||||
|
||||
import { createdAt, timestamptz, updatedAt } from './_helpers';
|
||||
import { agents } from './agent';
|
||||
import { documents } from './file';
|
||||
import { users } from './user';
|
||||
|
||||
/**
|
||||
* Agent document settings mapped to canonical documents rows.
|
||||
*
|
||||
* Access model (5-bit bitmask):
|
||||
* - bit4 (16): delete
|
||||
* - bit3 (8): list
|
||||
* - bit2 (4): write
|
||||
* - bit1 (2): read
|
||||
* - bit0 (1): execute
|
||||
* - full access: 31, no access: 0
|
||||
*
|
||||
* Access domains:
|
||||
* - accessSelf: owner/self channel access
|
||||
* - accessShared: shared users/agents channel access
|
||||
* - accessPublic: public channel access
|
||||
*
|
||||
* Policy load:
|
||||
* - policyLoad controls injection pipeline participation (enum-like string values).
|
||||
* - deletedAt/deletedByUserId/deletedByAgentId/deleteReason implement soft delete.
|
||||
*
|
||||
* User actions:
|
||||
* - Disable document:
|
||||
* 1) set accessSelf/accessShared/accessPublic to 0 as needed
|
||||
* 2) set policyLoad = 'disabled'
|
||||
* 3) keep deletedAt = null (still restorable as active record)
|
||||
* - Enable document:
|
||||
* 1) restore access bitmask (example: 10 => list + read)
|
||||
* 2) set policyLoad = 'always'
|
||||
* 3) ensure deletedAt = null
|
||||
* - Delete document:
|
||||
* 1) set deletedAt + one of deletedByUserId/deletedByAgentId + deleteReason
|
||||
* 2) force policyLoad = 'disabled'
|
||||
* 3) keep row for recovery/audit
|
||||
*/
|
||||
export const agentDocuments = pgTable(
|
||||
'agent_documents',
|
||||
{
|
||||
id: uuid('id').primaryKey().defaultRandom(),
|
||||
userId: text('user_id')
|
||||
.notNull()
|
||||
.references(() => users.id, { onDelete: 'cascade' }),
|
||||
agentId: text('agent_id')
|
||||
.notNull()
|
||||
.references(() => agents.id, { onDelete: 'cascade' }),
|
||||
documentId: varchar('document_id', { length: 255 })
|
||||
.notNull()
|
||||
.references(() => documents.id, { onDelete: 'cascade' }),
|
||||
|
||||
/**
|
||||
* Template source label (e.g. 'claw', 'custom').
|
||||
* Used for grouping and re-initialization workflows.
|
||||
*/
|
||||
templateId: varchar('template_id', { length: 100 }),
|
||||
|
||||
/**
|
||||
* Access bitmask for owner/self channel.
|
||||
*
|
||||
* Bit positions (b4 b3 b2 b1 b0):
|
||||
* - b4 (1 << 4 = 16): delete
|
||||
* - b3 (1 << 3 = 8): list
|
||||
* - b2 (1 << 2 = 4): write
|
||||
* - b1 (1 << 1 = 2): read
|
||||
* - b0 (1 << 0 = 1): execute
|
||||
*
|
||||
* Examples:
|
||||
* - 00000b = 0 => no permissions
|
||||
* - 01010b = 10 => list + read
|
||||
* - 11111b = 31 => full permissions (default)
|
||||
*
|
||||
* Shared-access profile examples:
|
||||
* - locked prompt: accessSelf=10, accessShared=10, accessPublic=0
|
||||
* - executable skill: accessSelf=31, accessShared=15, accessPublic=0
|
||||
* - public reference doc: accessSelf=31, accessShared=10, accessPublic=10
|
||||
*/
|
||||
accessSelf: integer('access_self').notNull().default(31),
|
||||
/**
|
||||
* Access bitmask for shared channel (shared users/agents).
|
||||
* Same 5-bit semantics as accessSelf.
|
||||
*/
|
||||
accessShared: integer('access_shared').notNull().default(0),
|
||||
/**
|
||||
* Access bitmask for public channel.
|
||||
* Same 5-bit semantics as accessSelf.
|
||||
*/
|
||||
accessPublic: integer('access_public').notNull().default(0),
|
||||
|
||||
/**
|
||||
* Controls whether this document participates in automatic context injection.
|
||||
* Example values: 'always', 'disabled'. Keep enum values unchanged.
|
||||
* This is independent from access bitmask values.
|
||||
*/
|
||||
policyLoad: varchar('policy_load', { length: 30 }).notNull().default('always'),
|
||||
|
||||
/**
|
||||
* Canonical behavior config for context/retrieval policy.
|
||||
* Keep extensible fields here.
|
||||
*/
|
||||
policy: jsonb('policy').$type<Record<string, any>>(),
|
||||
/**
|
||||
* Indexed projection of policy.context.position for fast filtering/sorting.
|
||||
*/
|
||||
policyLoadPosition: varchar('policy_load_position', { length: 50 })
|
||||
.notNull()
|
||||
.default('before-first-user'),
|
||||
/**
|
||||
* Indexed projection of policy.context.policyLoadFormat for rendering strategy.
|
||||
* Example values: 'raw', 'file'.
|
||||
*/
|
||||
policyLoadFormat: varchar('policy_load_format', { length: 20 }).notNull().default('raw'),
|
||||
/**
|
||||
* Indexed projection of policy.context.rule for fast filtering.
|
||||
*/
|
||||
policyLoadRule: varchar('policy_load_rule', { length: 50 }).notNull().default('always'),
|
||||
|
||||
/**
|
||||
* Soft delete timestamp. Non-null means logically deleted.
|
||||
*/
|
||||
deletedAt: timestamptz('deleted_at'),
|
||||
/**
|
||||
* User actor who performed soft delete.
|
||||
*/
|
||||
deletedByUserId: text('deleted_by_user_id').references(() => users.id, {
|
||||
onDelete: 'set null',
|
||||
}),
|
||||
/**
|
||||
* Agent actor who performed soft delete.
|
||||
*/
|
||||
deletedByAgentId: text('deleted_by_agent_id').references(() => agents.id, {
|
||||
onDelete: 'set null',
|
||||
}),
|
||||
/**
|
||||
* Optional operator-supplied reason for deletion.
|
||||
*/
|
||||
deleteReason: text('delete_reason'),
|
||||
|
||||
createdAt: createdAt(),
|
||||
updatedAt: updatedAt(),
|
||||
},
|
||||
(table) => [
|
||||
index('agent_documents_user_id_idx').on(table.userId),
|
||||
index('agent_documents_agent_id_idx').on(table.agentId),
|
||||
index('agent_documents_access_self_idx').on(table.accessSelf),
|
||||
index('agent_documents_access_shared_idx').on(table.accessShared),
|
||||
index('agent_documents_access_public_idx').on(table.accessPublic),
|
||||
index('agent_documents_policy_load_idx').on(table.policyLoad),
|
||||
index('agent_documents_template_id_idx').on(table.templateId),
|
||||
index('agent_documents_policy_load_position_idx').on(table.policyLoadPosition),
|
||||
index('agent_documents_policy_load_format_idx').on(table.policyLoadFormat),
|
||||
index('agent_documents_policy_load_rule_idx').on(table.policyLoadRule),
|
||||
index('agent_documents_agent_load_position_idx').on(table.agentId, table.policyLoadPosition),
|
||||
index('agent_documents_deleted_at_idx').on(table.deletedAt),
|
||||
index('agent_documents_agent_autoload_deleted_idx').on(
|
||||
table.agentId,
|
||||
table.deletedAt,
|
||||
table.policyLoad,
|
||||
),
|
||||
index('agent_documents_document_id_idx').on(table.documentId),
|
||||
uniqueIndex('agent_documents_agent_document_user_unique').on(
|
||||
table.agentId,
|
||||
table.documentId,
|
||||
table.userId,
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
export type AgentDocument = typeof agentDocuments.$inferSelect;
|
||||
export type NewAgentDocument = typeof agentDocuments.$inferInsert;
|
||||
@@ -1,6 +1,7 @@
|
||||
export * from './agent';
|
||||
export * from './agentBotProvider';
|
||||
export * from './agentCronJob';
|
||||
export * from './agentDocuments';
|
||||
export * from './agentEvals';
|
||||
export * from './agentSkill';
|
||||
export * from './aiInfra';
|
||||
|
||||
Reference in New Issue
Block a user