v2 phase 1: foundation — types, DB layer, logging

Add the v2 data layer: typed interfaces, central DB with migration
runner, per-entity CRUD, and agent-runner session DB operations.

- src/log.ts: concise message-first logging API
- src/types-v2.ts: AgentGroup, MessagingGroup, Session, MessageIn/Out
- src/db/: connection (WAL), migration runner, 001-initial schema,
  CRUD for agent_groups, messaging_groups, sessions, pending_questions
- container/agent-runner/src/db/: session DB connection, messages_in
  reads + status transitions, messages_out writes
- 31 new tests, all 277 tests pass

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
gavrielc
2026-04-08 23:34:09 +03:00
parent 90acff28ad
commit 3f0451b7b0
15 changed files with 1267 additions and 0 deletions

View File

@@ -0,0 +1,68 @@
import type Database from 'better-sqlite3';
import type { Migration } from './index.js';
export const migration001: Migration = {
version: 1,
name: 'initial-v2-schema',
up(db: Database.Database) {
db.exec(`
CREATE TABLE agent_groups (
id TEXT PRIMARY KEY,
name TEXT NOT NULL,
folder TEXT NOT NULL UNIQUE,
is_admin INTEGER DEFAULT 0,
agent_provider TEXT,
container_config TEXT,
created_at TEXT NOT NULL
);
CREATE TABLE messaging_groups (
id TEXT PRIMARY KEY,
channel_type TEXT NOT NULL,
platform_id TEXT NOT NULL,
name TEXT,
is_group INTEGER DEFAULT 0,
admin_user_id TEXT,
created_at TEXT NOT NULL,
UNIQUE(channel_type, platform_id)
);
CREATE TABLE messaging_group_agents (
id TEXT PRIMARY KEY,
messaging_group_id TEXT NOT NULL REFERENCES messaging_groups(id),
agent_group_id TEXT NOT NULL REFERENCES agent_groups(id),
trigger_rules TEXT,
response_scope TEXT DEFAULT 'all',
session_mode TEXT DEFAULT 'shared',
priority INTEGER DEFAULT 0,
created_at TEXT NOT NULL,
UNIQUE(messaging_group_id, agent_group_id)
);
CREATE TABLE sessions (
id TEXT PRIMARY KEY,
agent_group_id TEXT NOT NULL REFERENCES agent_groups(id),
messaging_group_id TEXT REFERENCES messaging_groups(id),
thread_id TEXT,
agent_provider TEXT,
status TEXT DEFAULT 'active',
container_status TEXT DEFAULT 'stopped',
last_active TEXT,
created_at TEXT NOT NULL
);
CREATE INDEX idx_sessions_agent_group ON sessions(agent_group_id);
CREATE INDEX idx_sessions_lookup ON sessions(messaging_group_id, thread_id);
CREATE TABLE pending_questions (
question_id TEXT PRIMARY KEY,
session_id TEXT NOT NULL REFERENCES sessions(id),
message_out_id TEXT NOT NULL,
platform_id TEXT,
channel_type TEXT,
thread_id TEXT,
created_at TEXT NOT NULL
);
`);
},
};

View File

@@ -0,0 +1,46 @@
import type Database from 'better-sqlite3';
import { log } from '../../log.js';
import { migration001 } from './001-initial.js';
export interface Migration {
version: number;
name: string;
up: (db: Database.Database) => void;
}
const migrations: Migration[] = [migration001];
export function runMigrations(db: Database.Database): void {
db.exec(`
CREATE TABLE IF NOT EXISTS schema_version (
version INTEGER PRIMARY KEY,
name TEXT NOT NULL,
applied TEXT NOT NULL
);
`);
const currentVersion =
(db.prepare('SELECT MAX(version) as v FROM schema_version').get() as { v: number | null })?.v ?? 0;
const pending = migrations.filter((m) => m.version > currentVersion);
if (pending.length === 0) return;
log.info('Running migrations', {
from: currentVersion,
to: pending[pending.length - 1].version,
count: pending.length,
});
for (const m of pending) {
db.transaction(() => {
m.up(db);
db.prepare('INSERT INTO schema_version (version, name, applied) VALUES (?, ?, ?)').run(
m.version,
m.name,
new Date().toISOString(),
);
})();
log.info('Migration applied', { version: m.version, name: m.name });
}
}