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,65 @@
import { getSessionDb } from './connection.js';
export interface MessageInRow {
id: string;
kind: string;
timestamp: string;
status: string;
status_changed: string | null;
process_after: string | null;
recurrence: string | null;
tries: number;
platform_id: string | null;
channel_type: string | null;
thread_id: string | null;
content: string;
}
/** Fetch all pending messages that are due for processing. */
export function getPendingMessages(): MessageInRow[] {
return getSessionDb()
.prepare(
`SELECT * FROM messages_in
WHERE status = 'pending'
AND (process_after IS NULL OR process_after <= datetime('now'))
ORDER BY timestamp ASC`,
)
.all() as MessageInRow[];
}
/** Mark messages as processing. */
export function markProcessing(ids: string[]): void {
if (ids.length === 0) return;
const db = getSessionDb();
const stmt = db.prepare("UPDATE messages_in SET status = 'processing', status_changed = datetime('now'), tries = tries + 1 WHERE id = ?");
db.transaction(() => {
for (const id of ids) stmt.run(id);
})();
}
/** Mark messages as completed. */
export function markCompleted(ids: string[]): void {
if (ids.length === 0) return;
const db = getSessionDb();
const stmt = db.prepare("UPDATE messages_in SET status = 'completed', status_changed = datetime('now') WHERE id = ?");
db.transaction(() => {
for (const id of ids) stmt.run(id);
})();
}
/** Mark a single message as failed. */
export function markFailed(id: string): void {
getSessionDb().prepare("UPDATE messages_in SET status = 'failed', status_changed = datetime('now') WHERE id = ?").run(id);
}
/** Get a message by ID. */
export function getMessageIn(id: string): MessageInRow | undefined {
return getSessionDb().prepare('SELECT * FROM messages_in WHERE id = ?').get(id) as MessageInRow | undefined;
}
/** Find a pending response to a question (by questionId in content). */
export function findQuestionResponse(questionId: string): MessageInRow | undefined {
return getSessionDb()
.prepare("SELECT * FROM messages_in WHERE status = 'pending' AND content LIKE ?")
.get(`%"questionId":"${questionId}"%`) as MessageInRow | undefined;
}