diff --git a/drizzle/0002_add_agent_directory_fields.sql b/drizzle/0002_add_agent_directory_fields.sql new file mode 100644 index 0000000..9a3afd0 --- /dev/null +++ b/drizzle/0002_add_agent_directory_fields.sql @@ -0,0 +1,21 @@ +-- Migration: Add directory fields to agents table +-- Adds: urlKey, description, specialties, chainOfCommand for agent directory feature + +ALTER TABLE agents ADD COLUMN url_key text; +ALTER TABLE agents ADD COLUMN description text; +ALTER TABLE agents ADD COLUMN specialties jsonb DEFAULT '[]'::jsonb; +ALTER TABLE agents ADD COLUMN chain_of_command jsonb; + +-- url_key should match name pattern and be unique (nullable for backward compat) +ALTER TABLE agents ADD CONSTRAINT agents_url_key_check + CHECK (url_key IS NULL OR url_key ~ '^[a-z0-9][a-z0-9-]{0,63}$'); + +-- Create unique index on url_key (partial index to allow NULLs) +CREATE UNIQUE INDEX agents_url_key_idx ON agents(url_key) WHERE url_key IS NOT NULL; + +-- Description max length +ALTER TABLE agents ADD CONSTRAINT agents_description_check + CHECK (description IS NULL OR length(description) BETWEEN 1 AND 2048); + +-- Update existing agents to use name as url_key +UPDATE agents SET url_key = name WHERE url_key IS NULL; diff --git a/src/db/schema.ts b/src/db/schema.ts index b054c43..815dc03 100644 --- a/src/db/schema.ts +++ b/src/db/schema.ts @@ -40,6 +40,10 @@ export const agents = pgTable( name: text('name').notNull().unique(), displayName: text('display_name').notNull(), role: text('role').notNull(), + urlKey: text('url_key'), + description: text('description'), + specialties: jsonb('specialties').default(sql`'[]'::jsonb`), + chainOfCommand: jsonb('chain_of_command'), createdAt: timestamp('created_at', { withTimezone: true, mode: 'date' }).notNull().defaultNow(), updatedAt: timestamp('updated_at', { withTimezone: true, mode: 'date' }).notNull().defaultNow(), }, @@ -50,7 +54,18 @@ export const agents = pgTable( sql`length(${table.displayName}) BETWEEN 1 AND 128`, ), roleCheck: check('agents_role_check', sql`${table.role} IN ('admin', 'agent')`), + urlKeyCheck: check( + 'agents_url_key_check', + sql`${table.urlKey} IS NULL OR ${table.urlKey} ~ '^[a-z0-9][a-z0-9-]{0,63}$'`, + ), + descriptionCheck: check( + 'agents_description_check', + sql`${table.description} IS NULL OR length(${table.description}) BETWEEN 1 AND 2048`, + ), roleIdx: index('agents_role_idx').on(table.role), + urlKeyIdx: index('agents_url_key_idx') + .on(table.urlKey) + .where(sql`${table.urlKey} IS NOT NULL`), }), );