feat(schema): 004 — agents, agent_tokens, conversations, messages
This commit is contained in:
50
lib/db/migrations/004_agents.sql
Normal file
50
lib/db/migrations/004_agents.sql
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
CREATE TABLE agents (
|
||||||
|
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
slug text NOT NULL UNIQUE,
|
||||||
|
name text NOT NULL,
|
||||||
|
kind text NOT NULL
|
||||||
|
CHECK (kind IN ('claude','ollama','mastra','mcp-client','external')),
|
||||||
|
model text,
|
||||||
|
persona_path text,
|
||||||
|
capabilities jsonb NOT NULL DEFAULT '{"read":true,"suggest":true,"write":false}'::jsonb,
|
||||||
|
scopes jsonb NOT NULL DEFAULT '{}'::jsonb,
|
||||||
|
created_at timestamptz NOT NULL DEFAULT now()
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE agent_tokens (
|
||||||
|
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
agent_id uuid NOT NULL REFERENCES agents(id) ON DELETE CASCADE,
|
||||||
|
label text,
|
||||||
|
token_hash text NOT NULL,
|
||||||
|
last_used timestamptz,
|
||||||
|
created_at timestamptz NOT NULL DEFAULT now(),
|
||||||
|
revoked_at timestamptz
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE conversations (
|
||||||
|
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
title text,
|
||||||
|
agent_id uuid REFERENCES agents(id) ON DELETE SET NULL,
|
||||||
|
participants text[] NOT NULL DEFAULT '{}',
|
||||||
|
status text NOT NULL DEFAULT 'open'
|
||||||
|
CHECK (status IN ('open','summarized','archived')),
|
||||||
|
summary text,
|
||||||
|
metadata jsonb NOT NULL DEFAULT '{}'::jsonb,
|
||||||
|
embedding vector(1024),
|
||||||
|
started_at timestamptz NOT NULL DEFAULT now(),
|
||||||
|
ended_at timestamptz
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE messages (
|
||||||
|
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
conversation_id uuid NOT NULL REFERENCES conversations(id) ON DELETE CASCADE,
|
||||||
|
role text NOT NULL CHECK (role IN ('user','assistant','system','tool')),
|
||||||
|
agent_id uuid REFERENCES agents(id) ON DELETE SET NULL,
|
||||||
|
body text NOT NULL,
|
||||||
|
metadata jsonb NOT NULL DEFAULT '{}'::jsonb,
|
||||||
|
created_at timestamptz NOT NULL DEFAULT now()
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX idx_messages_conv ON messages(conversation_id, created_at);
|
||||||
|
CREATE INDEX idx_messages_fts ON messages USING GIN (to_tsvector('english', body));
|
||||||
|
CREATE INDEX idx_agent_tokens_hash ON agent_tokens(token_hash) WHERE revoked_at IS NULL;
|
||||||
24
tests/db/migration_004.test.js
Normal file
24
tests/db/migration_004.test.js
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
import { describe, it, expect, beforeEach } from 'vitest';
|
||||||
|
import { resetDb, withClient } from '../helpers/db.js';
|
||||||
|
import { migrateUp } from '../../lib/db/migrate.js';
|
||||||
|
|
||||||
|
describe('migration 004 — agents', () => {
|
||||||
|
beforeEach(async () => { await resetDb(); await migrateUp(); });
|
||||||
|
|
||||||
|
it('creates agents, agent_tokens, conversations, messages', async () => {
|
||||||
|
await withClient(async (c) => {
|
||||||
|
for (const t of ['agents','agent_tokens','conversations','messages']) {
|
||||||
|
const { rows } = await c.query(`SELECT to_regclass('public.' || $1) AS t;`, [t]);
|
||||||
|
expect(rows[0].t).toBe(t);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('agents.kind enum is enforced', async () => {
|
||||||
|
await withClient(async (c) => {
|
||||||
|
await expect(c.query(
|
||||||
|
`INSERT INTO agents(slug, name, kind) VALUES('x','X','invalid');`
|
||||||
|
)).rejects.toThrow(/check/i);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user