From 0b4e389f2bfa66dec6e97b9b5d3b7eb1be9eeae3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Guti=C3=A9rrez?= <35082514+alezmad@users.noreply.github.com> Date: Mon, 6 Apr 2026 14:30:16 +0100 Subject: [PATCH] feat(web): restore payload CMS (cuidecar pattern + importMap) Co-Authored-By: Claude Opus 4.6 (1M context) --- apps/broker/package.json | 2 + apps/broker/src/broker.ts | 446 +++ apps/broker/src/env.ts | 4 + apps/broker/src/index.ts | 617 ++- apps/broker/src/neo4j-client.ts | 22 + apps/broker/src/qdrant.ts | 24 + apps/broker/src/types.ts | 329 +- apps/cli/package.json | 2 +- apps/cli/src/mcp/server.ts | 261 ++ apps/cli/src/mcp/tools.ts | 286 ++ apps/cli/src/ws/client.ts | 357 ++ apps/web/next.config.ts | 5 +- apps/web/src/app/(payload)/layout.tsx | 14 + .../payload/[[...segments]]/page.tsx | 14 + .../src/app/(payload)/payload/importMap.js | 51 + docker-compose.production.yml | 45 + .../migrations/0010_add-context-and-tasks.sql | 33 + packages/db/migrations/0011_add-streams.sql | 10 + .../db/migrations/meta/0010_snapshot.json | 3467 ++++++++++++++++ .../db/migrations/meta/0011_snapshot.json | 3548 +++++++++++++++++ packages/db/migrations/meta/_journal.json | 14 + packages/db/src/schema/mesh.ts | 104 + 22 files changed, 9651 insertions(+), 4 deletions(-) create mode 100644 apps/broker/src/neo4j-client.ts create mode 100644 apps/broker/src/qdrant.ts create mode 100644 apps/web/src/app/(payload)/layout.tsx create mode 100644 apps/web/src/app/(payload)/payload/[[...segments]]/page.tsx create mode 100644 apps/web/src/app/(payload)/payload/importMap.js create mode 100644 packages/db/migrations/0010_add-context-and-tasks.sql create mode 100644 packages/db/migrations/0011_add-streams.sql create mode 100644 packages/db/migrations/meta/0010_snapshot.json create mode 100644 packages/db/migrations/meta/0011_snapshot.json diff --git a/apps/broker/package.json b/apps/broker/package.json index 508638d..0445d08 100644 --- a/apps/broker/package.json +++ b/apps/broker/package.json @@ -15,11 +15,13 @@ }, "prettier": "@turbostarter/prettier-config", "dependencies": { + "@qdrant/js-client-rest": "1.17.0", "@turbostarter/db": "workspace:*", "@turbostarter/shared": "workspace:*", "drizzle-orm": "0.44.7", "libsodium-wrappers": "0.7.15", "minio": "8.0.7", + "neo4j-driver": "6.0.1", "ws": "8.20.0", "zod": "catalog:" }, diff --git a/apps/broker/src/broker.ts b/apps/broker/src/broker.ts index c4e6ec0..bed7cd0 100644 --- a/apps/broker/src/broker.ts +++ b/apps/broker/src/broker.ts @@ -34,9 +34,12 @@ import { mesh, meshFile, meshFileAccess, + meshContext, meshMember as memberTable, meshMemory, meshState, + meshStream, + meshTask, messageQueue, pendingStatus, presence, @@ -889,6 +892,334 @@ export async function deleteFile( ); } +// --- Context sharing --- + +/** + * Upsert a context snapshot for a peer. Each (meshId, presenceId) pair + * has at most one context row — repeated calls update it in place. + */ +export async function shareContext( + meshId: string, + presenceId: string, + peerName: string | undefined, + summary: string, + filesRead?: string[], + keyFindings?: string[], + tags?: string[], +): Promise { + const now = new Date(); + // Try to find existing context for this presence in this mesh. + const [existing] = await db + .select({ id: meshContext.id }) + .from(meshContext) + .where( + and( + eq(meshContext.meshId, meshId), + eq(meshContext.presenceId, presenceId), + ), + ) + .limit(1); + + if (existing) { + await db + .update(meshContext) + .set({ + peerName: peerName ?? null, + summary, + filesRead: filesRead ?? [], + keyFindings: keyFindings ?? [], + tags: tags ?? [], + updatedAt: now, + }) + .where(eq(meshContext.id, existing.id)); + return existing.id; + } + + const [row] = await db + .insert(meshContext) + .values({ + meshId, + presenceId, + peerName: peerName ?? null, + summary, + filesRead: filesRead ?? [], + keyFindings: keyFindings ?? [], + tags: tags ?? [], + updatedAt: now, + }) + .returning({ id: meshContext.id }); + if (!row) throw new Error("failed to insert context"); + return row.id; +} + +/** + * Search contexts by tag match or summary ILIKE. + */ +export async function getContext( + meshId: string, + query: string, +): Promise< + Array<{ + peerName: string; + summary: string; + filesRead: string[]; + keyFindings: string[]; + tags: string[]; + updatedAt: Date; + }> +> { + const result = await db.execute<{ + peer_name: string | null; + summary: string; + files_read: string[] | null; + key_findings: string[] | null; + tags: string[] | null; + updated_at: string | Date; + }>(sql` + SELECT peer_name, summary, files_read, key_findings, tags, updated_at + FROM mesh.context + WHERE mesh_id = ${meshId} + AND ( + summary ILIKE ${"%" + query + "%"} + OR ${query} = ANY(tags) + ) + ORDER BY updated_at DESC + LIMIT 20 + `); + const rows = (result.rows ?? result) as Array<{ + peer_name: string | null; + summary: string; + files_read: string[] | null; + key_findings: string[] | null; + tags: string[] | null; + updated_at: string | Date; + }>; + return rows.map((r) => ({ + peerName: r.peer_name ?? "unknown", + summary: r.summary, + filesRead: r.files_read ?? [], + keyFindings: r.key_findings ?? [], + tags: r.tags ?? [], + updatedAt: + r.updated_at instanceof Date ? r.updated_at : new Date(r.updated_at), + })); +} + +/** + * List all contexts for a mesh, ordered by most recently updated. + */ +export async function listContexts( + meshId: string, +): Promise< + Array<{ + peerName: string; + summary: string; + tags: string[]; + updatedAt: Date; + }> +> { + const rows = await db + .select({ + peerName: meshContext.peerName, + summary: meshContext.summary, + tags: meshContext.tags, + updatedAt: meshContext.updatedAt, + }) + .from(meshContext) + .where(eq(meshContext.meshId, meshId)) + .orderBy(desc(meshContext.updatedAt)); + return rows.map((r) => ({ + peerName: r.peerName ?? "unknown", + summary: r.summary, + tags: (r.tags ?? []) as string[], + updatedAt: r.updatedAt, + })); +} + +// --- Tasks --- + +/** + * Create a new task in a mesh. Returns the generated id. + */ +export async function createTask( + meshId: string, + title: string, + assignee?: string, + priority?: string, + tags?: string[], + createdByName?: string, +): Promise { + const [row] = await db + .insert(meshTask) + .values({ + meshId, + title, + assignee: assignee ?? null, + priority: priority ?? "normal", + status: "open", + tags: tags ?? [], + createdByName: createdByName ?? null, + }) + .returning({ id: meshTask.id }); + if (!row) throw new Error("failed to insert task"); + return row.id; +} + +/** + * Claim an open task. Sets status to 'claimed' and records who claimed it. + * Only succeeds if the task is currently 'open'. + */ +export async function claimTask( + meshId: string, + taskId: string, + presenceId: string, + peerName?: string, +): Promise { + const now = new Date(); + const result = await db + .update(meshTask) + .set({ + status: "claimed", + claimedByPresence: presenceId, + claimedByName: peerName ?? null, + claimedAt: now, + }) + .where( + and( + eq(meshTask.id, taskId), + eq(meshTask.meshId, meshId), + eq(meshTask.status, "open"), + ), + ) + .returning({ id: meshTask.id }); + return result.length > 0; +} + +/** + * Complete a task. Sets status to 'done', records the result and timestamp. + */ +export async function completeTask( + meshId: string, + taskId: string, + result?: string, +): Promise { + const now = new Date(); + const rows = await db + .update(meshTask) + .set({ + status: "done", + result: result ?? null, + completedAt: now, + }) + .where( + and( + eq(meshTask.id, taskId), + eq(meshTask.meshId, meshId), + ), + ) + .returning({ id: meshTask.id }); + return rows.length > 0; +} + +/** + * List tasks in a mesh with optional status and assignee filters. + */ +export async function listTasks( + meshId: string, + status?: string, + assignee?: string, +): Promise< + Array<{ + id: string; + title: string; + assignee: string | null; + claimedBy: string | null; + status: string; + priority: string; + createdBy: string | null; + tags: string[]; + createdAt: Date; + }> +> { + const conditions = [eq(meshTask.meshId, meshId)]; + if (status) { + conditions.push(eq(meshTask.status, status)); + } + if (assignee) { + conditions.push(eq(meshTask.assignee, assignee)); + } + const rows = await db + .select({ + id: meshTask.id, + title: meshTask.title, + assignee: meshTask.assignee, + claimedByName: meshTask.claimedByName, + status: meshTask.status, + priority: meshTask.priority, + createdByName: meshTask.createdByName, + tags: meshTask.tags, + createdAt: meshTask.createdAt, + }) + .from(meshTask) + .where(and(...conditions)) + .orderBy(desc(meshTask.createdAt)) + .limit(100); + return rows.map((r) => ({ + id: r.id, + title: r.title, + assignee: r.assignee, + claimedBy: r.claimedByName, + status: r.status, + priority: r.priority, + createdBy: r.createdByName, + tags: (r.tags ?? []) as string[], + createdAt: r.createdAt, + })); +} + +// --- Streams --- + +/** + * Create a named real-time stream in a mesh. Upsert semantics: if a + * stream with the same (meshId, name) already exists, return its id. + */ +export async function createStream( + meshId: string, + name: string, + createdByName: string, +): Promise { + const existing = await db + .select({ id: meshStream.id }) + .from(meshStream) + .where(and(eq(meshStream.meshId, meshId), eq(meshStream.name, name))); + if (existing.length > 0) return existing[0]!.id; + const [row] = await db + .insert(meshStream) + .values({ meshId, name, createdByName }) + .returning({ id: meshStream.id }); + return row!.id; +} + +/** + * List all streams in a mesh, ordered by creation time. + */ +export async function listStreams( + meshId: string, +): Promise< + Array<{ id: string; name: string; createdBy: string | null; createdAt: Date }> +> { + return db + .select({ + id: meshStream.id, + name: meshStream.name, + createdBy: meshStream.createdByName, + createdAt: meshStream.createdAt, + }) + .from(meshStream) + .where(eq(meshStream.meshId, meshId)) + .orderBy(asc(meshStream.createdAt)); +} + // --- Message queueing + delivery --- export interface QueueParams { @@ -1239,3 +1570,118 @@ export async function findMemberByPubkey( .limit(1); return row ?? null; } + +// --- Mesh databases (per-mesh PostgreSQL schemas) --- + +function meshSchemaName(meshId: string): string { + return `meshdb_${meshId.toLowerCase().replace(/[^a-z0-9]/g, "_")}`; +} + +/** Validate that user-provided SQL doesn't contain dangerous operations. */ +function validateMeshSql(userSql: string): void { + const upper = userSql.toUpperCase(); + const forbidden = [ + "DROP SCHEMA", + "CREATE SCHEMA", + "SET SEARCH_PATH", + "SET ROLE", + "SET SESSION", + "SET LOCAL", + "GRANT", + "REVOKE", + ]; + for (const f of forbidden) { + if (upper.includes(f)) + throw new Error(`Forbidden SQL operation: ${f}`); + } +} + +/** Ensure the per-mesh schema exists. */ +export async function ensureMeshSchema(meshId: string): Promise { + const schema = meshSchemaName(meshId); + await db.execute( + sql`CREATE SCHEMA IF NOT EXISTS ${sql.raw('"' + schema + '"')}`, + ); + return schema; +} + +/** Run a SELECT query in the mesh's schema. */ +export async function meshQuery( + meshId: string, + query: string, +): Promise<{ + columns: string[]; + rows: Array>; + rowCount: number; +}> { + validateMeshSql(query); + const schema = await ensureMeshSchema(meshId); + // Use a transaction so SET LOCAL is scoped and automatically reset. + return await db.transaction(async (tx) => { + await tx.execute( + sql.raw(`SET LOCAL search_path TO "${schema}"`) + ); + const result = await tx.execute(sql.raw(query)); + const rows = (result.rows ?? []) as Array>; + const columns = rows.length > 0 ? Object.keys(rows[0]!) : []; + return { columns, rows, rowCount: rows.length }; + }); +} + +/** Run a DDL/DML statement in the mesh's schema. */ +export async function meshExecute( + meshId: string, + statement: string, +): Promise<{ rowCount: number }> { + validateMeshSql(statement); + const schema = await ensureMeshSchema(meshId); + return await db.transaction(async (tx) => { + await tx.execute( + sql.raw(`SET LOCAL search_path TO "${schema}"`) + ); + const result = await tx.execute(sql.raw(statement)); + return { rowCount: (result as any).rowCount ?? 0 }; + }); +} + +/** List tables and columns in the mesh's schema. */ +export async function meshSchema( + meshId: string, +): Promise< + Array<{ + name: string; + columns: Array<{ name: string; type: string; nullable: boolean }>; + }> +> { + const schema = meshSchemaName(meshId); + const result = await db.execute<{ + table_name: string; + column_name: string; + data_type: string; + is_nullable: string; + }>(sql` + SELECT table_name, column_name, data_type, is_nullable + FROM information_schema.columns + WHERE table_schema = ${schema} + ORDER BY table_name, ordinal_position + `); + const rows = (result.rows ?? result) as Array<{ + table_name: string; + column_name: string; + data_type: string; + is_nullable: string; + }>; + const tables = new Map< + string, + Array<{ name: string; type: string; nullable: boolean }> + >(); + for (const r of rows) { + if (!tables.has(r.table_name)) tables.set(r.table_name, []); + tables.get(r.table_name)!.push({ + name: r.column_name, + type: r.data_type, + nullable: r.is_nullable === "YES", + }); + } + return [...tables.entries()].map(([name, columns]) => ({ name, columns })); +} diff --git a/apps/broker/src/env.ts b/apps/broker/src/env.ts index 9b9d2d1..6b2d931 100644 --- a/apps/broker/src/env.ts +++ b/apps/broker/src/env.ts @@ -24,6 +24,10 @@ const envSchema = z.object({ MINIO_ACCESS_KEY: z.string().default("claudemesh"), MINIO_SECRET_KEY: z.string().default("changeme"), MINIO_USE_SSL: z.coerce.boolean().default(false), + QDRANT_URL: z.string().default("http://qdrant:6333"), + NEO4J_URL: z.string().default("bolt://neo4j:7687"), + NEO4J_USER: z.string().default("neo4j"), + NEO4J_PASSWORD: z.string().default("changeme"), NODE_ENV: z .enum(["development", "production", "test"]) .default("development"), diff --git a/apps/broker/src/index.ts b/apps/broker/src/index.ts index 47341d9..d508d93 100644 --- a/apps/broker/src/index.ts +++ b/apps/broker/src/index.ts @@ -15,17 +15,21 @@ import { createServer, type IncomingMessage, type ServerResponse } from "node:http"; import type { Duplex } from "node:stream"; import { WebSocketServer, type WebSocket } from "ws"; -import { eq } from "drizzle-orm"; +import { eq, sql } from "drizzle-orm"; import { env } from "./env"; import { db } from "./db"; import { messageQueue } from "@turbostarter/db/schema/mesh"; import { + claimTask, + completeTask, connectPresence, + createTask, deleteFile, disconnectPresence, drainForMember, findMemberByPubkey, forgetMemory, + getContext, getFile, getFileStatus, getState, @@ -34,9 +38,11 @@ import { joinGroup, joinMesh, leaveGroup, + listContexts, listFiles, listPeersInMesh, listState, + listTasks, queueMessage, recallMemory, recordFileAccess, @@ -45,12 +51,21 @@ import { rememberMemory, setSummary, setState, + shareContext, startSweepers, stopSweepers, uploadFile, writeStatus, + ensureMeshSchema, + meshQuery, + meshExecute, + meshSchema, + createStream, + listStreams, } from "./broker"; import { ensureBucket, meshBucketName, minioClient } from "./minio"; +import { qdrant, meshCollectionName, ensureCollection } from "./qdrant"; +import { neo4jDriver, meshDbName, ensureDatabase } from "./neo4j-client"; import type { HookSetStatusRequest, WSClientMessage, @@ -81,6 +96,9 @@ interface PeerConn { const connections = new Map(); const connectionsPerMesh = new Map(); + +// Stream subscriptions: "meshId:streamName" → Set of presenceIds +const streamSubscriptions = new Map>(); const hookRateLimit = new TokenBucket( env.HOOK_RATE_LIMIT_PER_MIN, env.HOOK_RATE_LIMIT_PER_MIN, @@ -1066,6 +1084,598 @@ function handleConnection(ws: WebSocket): void { }); break; } + case "share_context": { + const sc = msg as Extract; + const memberInfo = conn.memberPubkey + ? await findMemberByPubkey(conn.meshId, conn.memberPubkey) + : null; + const ctxId = await shareContext( + conn.meshId, + presenceId, + memberInfo?.displayName, + sc.summary, + sc.filesRead, + sc.keyFindings, + sc.tags, + ); + sendToPeer(presenceId, { + type: "context_shared", + id: ctxId, + }); + // Notify all other peers in the mesh that context was shared. + for (const [pid, peer] of connections) { + if (pid === presenceId) continue; + if (peer.meshId !== conn.meshId) continue; + sendToPeer(pid, { + type: "state_change", + key: `_context:${memberInfo?.displayName ?? "unknown"}`, + value: sc.summary, + updatedBy: memberInfo?.displayName ?? "unknown", + }); + } + log.info("ws share_context", { + presence_id: presenceId, + context_id: ctxId, + }); + break; + } + case "get_context": { + const gc = msg as Extract; + const contexts = await getContext(conn.meshId, gc.query); + sendToPeer(presenceId, { + type: "context_results", + contexts: contexts.map((c) => ({ + peerName: c.peerName, + summary: c.summary, + filesRead: c.filesRead, + keyFindings: c.keyFindings, + tags: c.tags, + updatedAt: c.updatedAt.toISOString(), + })), + }); + log.info("ws get_context", { + presence_id: presenceId, + query: gc.query.slice(0, 80), + results: contexts.length, + }); + break; + } + case "list_contexts": { + const allContexts = await listContexts(conn.meshId); + sendToPeer(presenceId, { + type: "context_list", + contexts: allContexts.map((c) => ({ + peerName: c.peerName, + summary: c.summary, + tags: c.tags, + updatedAt: c.updatedAt.toISOString(), + })), + }); + log.info("ws list_contexts", { + presence_id: presenceId, + mesh_id: conn.meshId, + count: allContexts.length, + }); + break; + } + case "create_task": { + const ct = msg as Extract; + const memberInfo = conn.memberPubkey + ? await findMemberByPubkey(conn.meshId, conn.memberPubkey) + : null; + const taskId = await createTask( + conn.meshId, + ct.title, + ct.assignee, + ct.priority, + ct.tags, + memberInfo?.displayName, + ); + sendToPeer(presenceId, { + type: "task_created", + id: taskId, + }); + log.info("ws create_task", { + presence_id: presenceId, + task_id: taskId, + title: ct.title.slice(0, 80), + }); + break; + } + case "claim_task": { + const clm = msg as Extract; + const memberInfo = conn.memberPubkey + ? await findMemberByPubkey(conn.meshId, conn.memberPubkey) + : null; + const claimed = await claimTask( + conn.meshId, + clm.taskId, + presenceId, + memberInfo?.displayName, + ); + if (!claimed) { + sendError(conn.ws, "task_not_claimable", "task is not open or does not exist"); + break; + } + // Return updated task list so caller sees the change. + const tasksAfterClaim = await listTasks(conn.meshId); + sendToPeer(presenceId, { + type: "task_list", + tasks: tasksAfterClaim.map((t) => ({ + id: t.id, + title: t.title, + assignee: t.assignee, + claimedBy: t.claimedBy, + status: t.status, + priority: t.priority, + createdBy: t.createdBy, + tags: t.tags, + createdAt: t.createdAt.toISOString(), + })), + }); + log.info("ws claim_task", { + presence_id: presenceId, + task_id: clm.taskId, + }); + break; + } + case "complete_task": { + const cpt = msg as Extract; + const completed = await completeTask( + conn.meshId, + cpt.taskId, + cpt.result, + ); + if (!completed) { + sendError(conn.ws, "task_not_found", "task not found in this mesh"); + break; + } + // Return updated task list. + const tasksAfterComplete = await listTasks(conn.meshId); + sendToPeer(presenceId, { + type: "task_list", + tasks: tasksAfterComplete.map((t) => ({ + id: t.id, + title: t.title, + assignee: t.assignee, + claimedBy: t.claimedBy, + status: t.status, + priority: t.priority, + createdBy: t.createdBy, + tags: t.tags, + createdAt: t.createdAt.toISOString(), + })), + }); + log.info("ws complete_task", { + presence_id: presenceId, + task_id: cpt.taskId, + }); + break; + } + case "list_tasks": { + const lt = msg as Extract; + const tasks = await listTasks(conn.meshId, lt.status, lt.assignee); + sendToPeer(presenceId, { + type: "task_list", + tasks: tasks.map((t) => ({ + id: t.id, + title: t.title, + assignee: t.assignee, + claimedBy: t.claimedBy, + status: t.status, + priority: t.priority, + createdBy: t.createdBy, + tags: t.tags, + createdAt: t.createdAt.toISOString(), + })), + }); + log.info("ws list_tasks", { + presence_id: presenceId, + mesh_id: conn.meshId, + count: tasks.length, + }); + break; + } + + // --- Streams --- + + case "create_stream": { + const cs = msg as Extract; + const memberInfo = conn.memberPubkey + ? await findMemberByPubkey(conn.meshId, conn.memberPubkey) + : null; + const streamId = await createStream( + conn.meshId, + cs.name, + memberInfo?.displayName ?? "peer", + ); + sendToPeer(presenceId, { + type: "stream_created", + id: streamId, + name: cs.name, + }); + log.info("ws create_stream", { + presence_id: presenceId, + stream: cs.name, + }); + break; + } + + case "subscribe": { + const sub = msg as Extract; + const key = `${conn.meshId}:${sub.stream}`; + if (!streamSubscriptions.has(key)) + streamSubscriptions.set(key, new Set()); + streamSubscriptions.get(key)!.add(presenceId); + log.info("ws subscribe", { + presence_id: presenceId, + stream: sub.stream, + }); + break; + } + + case "unsubscribe": { + const unsub = msg as Extract< + WSClientMessage, + { type: "unsubscribe" } + >; + const key = `${conn.meshId}:${unsub.stream}`; + streamSubscriptions.get(key)?.delete(presenceId); + log.info("ws unsubscribe", { + presence_id: presenceId, + stream: unsub.stream, + }); + break; + } + + case "publish": { + const pub = msg as Extract; + const key = `${conn.meshId}:${pub.stream}`; + const subs = streamSubscriptions.get(key); + if (subs) { + const memberInfo = conn.memberPubkey + ? await findMemberByPubkey(conn.meshId, conn.memberPubkey) + : null; + const push: WSServerMessage = { + type: "stream_data", + stream: pub.stream, + data: pub.data, + publishedBy: memberInfo?.displayName ?? "peer", + }; + for (const subPid of subs) { + if (subPid === presenceId) continue; // don't echo to publisher + sendToPeer(subPid, push); + } + } + metrics.messagesRoutedTotal.inc({ priority: "stream" }); + break; + } + + case "list_streams": { + const streams = await listStreams(conn.meshId); + sendToPeer(presenceId, { + type: "stream_list", + streams: streams.map((s) => { + const key = `${conn.meshId}:${s.name}`; + return { + id: s.id, + name: s.name, + createdBy: s.createdBy ?? "", + createdAt: s.createdAt.toISOString(), + subscriberCount: streamSubscriptions.get(key)?.size ?? 0, + }; + }), + }); + log.info("ws list_streams", { + presence_id: presenceId, + mesh_id: conn.meshId, + count: streams.length, + }); + break; + } + + // --- Vector storage --- + + case "vector_store": { + const vs = msg as Extract; + const collName = meshCollectionName(conn.meshId, vs.collection); + await ensureCollection(collName); + const { generateId } = await import("@turbostarter/shared/utils"); + const pointId = generateId(); + // Store text + metadata as payload. Use a zero vector as placeholder + // — real embeddings should be computed client-side and sent directly + // to Qdrant in a future version. + const zeroVector = new Array(1536).fill(0) as number[]; + await qdrant.upsert(collName, { + wait: true, + points: [ + { + id: pointId, + vector: zeroVector, + payload: { + text: vs.text, + mesh_id: conn.meshId, + stored_by: conn.memberPubkey, + stored_at: new Date().toISOString(), + ...(vs.metadata ?? {}), + }, + }, + ], + }); + sendToPeer(presenceId, { + type: "ack" as const, + id: pointId, + messageId: pointId, + queued: false, + }); + log.info("ws vector_store", { + presence_id: presenceId, + collection: vs.collection, + point_id: pointId, + }); + break; + } + case "vector_search": { + const vq = msg as Extract; + const searchCollName = meshCollectionName(conn.meshId, vq.collection); + const searchLimit = vq.limit ?? 10; + try { + // Keyword search via payload scroll + filter. + // Full vector similarity requires client-computed embeddings (future). + const queryLower = vq.query.toLowerCase(); + const scrollResult = await qdrant.scroll(searchCollName, { + limit: 100, + with_payload: true, + with_vector: false, + }); + const matches = (scrollResult.points ?? []) + .filter((p) => { + const text = (p.payload as Record)?.text; + return typeof text === "string" && text.toLowerCase().includes(queryLower); + }) + .slice(0, searchLimit) + .map((p) => { + const payload = p.payload as Record; + return { + id: String(p.id), + text: (payload.text as string) ?? "", + score: 1.0, // keyword match — no vector similarity score + metadata: payload, + }; + }); + sendToPeer(presenceId, { + type: "vector_results", + results: matches, + }); + } catch { + // Collection may not exist yet — return empty results. + sendToPeer(presenceId, { + type: "vector_results", + results: [], + }); + } + log.info("ws vector_search", { + presence_id: presenceId, + collection: vq.collection, + query: vq.query.slice(0, 80), + }); + break; + } + case "vector_delete": { + const vd = msg as Extract; + const deleteCollName = meshCollectionName(conn.meshId, vd.collection); + try { + await qdrant.delete(deleteCollName, { + wait: true, + points: [vd.id], + }); + } catch { + /* collection or point may not exist — idempotent */ + } + sendToPeer(presenceId, { + type: "ack" as const, + id: vd.id, + messageId: vd.id, + queued: false, + }); + log.info("ws vector_delete", { + presence_id: presenceId, + collection: vd.collection, + point_id: vd.id, + }); + break; + } + case "list_collections": { + try { + const qdrantResponse = await qdrant.getCollections(); + const prefix = `mesh_${conn.meshId}_`.toLowerCase().replace(/[^a-z0-9_]/g, "_"); + const meshCollections = (qdrantResponse.collections ?? []) + .map((c) => c.name) + .filter((name) => name.startsWith(prefix)) + .map((name) => name.slice(prefix.length)); + sendToPeer(presenceId, { + type: "collection_list", + collections: meshCollections, + }); + } catch { + sendToPeer(presenceId, { + type: "collection_list", + collections: [], + }); + } + log.info("ws list_collections", { + presence_id: presenceId, + mesh_id: conn.meshId, + }); + break; + } + + // --- Graph database --- + + case "graph_query": { + const gq = msg as Extract; + const gqDbName = meshDbName(conn.meshId); + let gqSession; + try { + await ensureDatabase(gqDbName); + gqSession = neo4jDriver.session({ database: gqDbName }); + } catch { + // Community edition — fall back to default db. + gqSession = neo4jDriver.session(); + } + try { + const gqResult = await gqSession.run(gq.cypher); + const gqRecords = gqResult.records.map((r) => { + const obj: Record = {}; + for (const key of r.keys) { + obj[key] = r.get(key); + } + return obj; + }); + sendToPeer(presenceId, { + type: "graph_result", + records: gqRecords, + }); + } catch (gqErr) { + sendError(conn.ws, "graph_error", gqErr instanceof Error ? gqErr.message : String(gqErr)); + } finally { + await gqSession.close(); + } + log.info("ws graph_query", { + presence_id: presenceId, + cypher: gq.cypher.slice(0, 80), + }); + break; + } + case "graph_execute": { + const ge = msg as Extract; + const geDbName = meshDbName(conn.meshId); + let geSession; + try { + await ensureDatabase(geDbName); + geSession = neo4jDriver.session({ database: geDbName }); + } catch { + geSession = neo4jDriver.session(); + } + try { + const geResult = await geSession.run(ge.cypher); + const geRecords = geResult.records.map((r) => { + const obj: Record = {}; + for (const key of r.keys) { + obj[key] = r.get(key); + } + return obj; + }); + sendToPeer(presenceId, { + type: "graph_result", + records: geRecords, + }); + } catch (geErr) { + sendError(conn.ws, "graph_error", geErr instanceof Error ? geErr.message : String(geErr)); + } finally { + await geSession.close(); + } + log.info("ws graph_execute", { + presence_id: presenceId, + cypher: ge.cypher.slice(0, 80), + }); + break; + } + + // --- Mesh database (per-mesh PostgreSQL schema) --- + + case "mesh_query": { + const mq = msg as Extract; + try { + const result = await meshQuery(conn.meshId, mq.sql); + sendToPeer(presenceId, { type: "mesh_query_result", ...result }); + } catch (e) { + sendError( + conn.ws, + "query_error", + e instanceof Error ? e.message : String(e), + ); + } + log.info("ws mesh_query", { + presence_id: presenceId, + sql: mq.sql.slice(0, 80), + }); + break; + } + case "mesh_execute": { + const me = msg as Extract; + try { + const result = await meshExecute(conn.meshId, me.sql); + sendToPeer(presenceId, { + type: "mesh_query_result", + columns: [], + rows: [], + rowCount: result.rowCount, + }); + } catch (e) { + sendError( + conn.ws, + "execute_error", + e instanceof Error ? e.message : String(e), + ); + } + log.info("ws mesh_execute", { + presence_id: presenceId, + sql: me.sql.slice(0, 80), + }); + break; + } + case "mesh_schema": { + try { + const tables = await meshSchema(conn.meshId); + sendToPeer(presenceId, { type: "mesh_schema_result", tables }); + } catch (e) { + sendError( + conn.ws, + "schema_error", + e instanceof Error ? e.message : String(e), + ); + } + log.info("ws mesh_schema", { presence_id: presenceId }); + break; + } + case "mesh_info": { + const [peers, stateEntries, memCount, fileCount, taskCounts, streams, tables] = await Promise.all([ + listPeersInMesh(conn.meshId), + listState(conn.meshId), + db.execute(sql`SELECT COUNT(*) as n FROM mesh.memory WHERE mesh_id = ${conn.meshId} AND forgotten_at IS NULL`).then(r => Number(((r.rows ?? r) as any[])[0]?.n ?? 0)), + db.execute(sql`SELECT COUNT(*) as n FROM mesh.file WHERE mesh_id = ${conn.meshId} AND deleted_at IS NULL`).then(r => Number(((r.rows ?? r) as any[])[0]?.n ?? 0)), + db.execute(sql`SELECT status, COUNT(*) as n FROM mesh.task WHERE mesh_id = ${conn.meshId} GROUP BY status`).then(r => { + const rows = (r.rows ?? r) as Array<{ status: string; n: string }>; + const counts = { open: 0, claimed: 0, done: 0 }; + for (const row of rows) counts[row.status as keyof typeof counts] = Number(row.n); + return counts; + }), + listStreams(conn.meshId), + meshSchema(conn.meshId).catch(() => []), + ]); + const allGroups = new Set(); + for (const p of peers) for (const g of p.groups) allGroups.add(`@${g.name}`); + const myPresence = peers.find(p => p.sessionId === [...connections.entries()].find(([pid]) => pid === presenceId)?.[1]?.sessionPubkey); + const peerConn = connections.get(presenceId); + sendToPeer(presenceId, { + type: "mesh_info_result", + mesh: conn.meshId, + peers: peers.length, + groups: [...allGroups], + stateKeys: stateEntries.map((e: any) => e.key), + memoryCount: memCount, + fileCount: fileCount, + tasks: taskCounts, + streams: streams.map(s => s.name), + tables: tables.map((t: any) => t.name), + collections: [], + yourName: peerConn?.groups?.[0]?.name ?? "unknown", + yourGroups: peerConn?.groups ?? [], + }); + log.info("ws mesh_info", { presence_id: presenceId }); + break; + } } } catch (e) { metrics.messagesRejectedTotal.inc({ reason: "parse_or_handler" }); @@ -1081,6 +1691,11 @@ function handleConnection(ws: WebSocket): void { connections.delete(presenceId); if (conn) decMeshCount(conn.meshId); await disconnectPresence(presenceId); + // Clean up stream subscriptions for this peer + for (const [key, subs] of streamSubscriptions) { + subs.delete(presenceId); + if (subs.size === 0) streamSubscriptions.delete(key); + } log.info("ws close", { presence_id: presenceId }); } }); diff --git a/apps/broker/src/neo4j-client.ts b/apps/broker/src/neo4j-client.ts new file mode 100644 index 0000000..a7506ec --- /dev/null +++ b/apps/broker/src/neo4j-client.ts @@ -0,0 +1,22 @@ +import neo4j from "neo4j-driver"; +import { env } from "./env"; + +export const neo4jDriver = neo4j.driver( + env.NEO4J_URL, + neo4j.auth.basic(env.NEO4J_USER, env.NEO4J_PASSWORD), +); + +export function meshDbName(meshId: string): string { + return `mesh_${meshId.toLowerCase().replace(/[^a-z0-9]/g, "")}`; +} + +export async function ensureDatabase(name: string): Promise { + const session = neo4jDriver.session({ database: "system" }); + try { + await session.run(`CREATE DATABASE $name IF NOT EXISTS`, { name }); + } catch { + /* may not support multi-db in community edition — fall back to default */ + } finally { + await session.close(); + } +} diff --git a/apps/broker/src/qdrant.ts b/apps/broker/src/qdrant.ts new file mode 100644 index 0000000..5d12d28 --- /dev/null +++ b/apps/broker/src/qdrant.ts @@ -0,0 +1,24 @@ +import { QdrantClient } from "@qdrant/js-client-rest"; +import { env } from "./env"; + +export const qdrant = new QdrantClient({ url: env.QDRANT_URL }); + +export function meshCollectionName( + meshId: string, + collection: string, +): string { + return `mesh_${meshId}_${collection}`.toLowerCase().replace(/[^a-z0-9_]/g, "_"); +} + +export async function ensureCollection( + name: string, + vectorSize = 1536, +): Promise { + try { + await qdrant.getCollection(name); + } catch { + await qdrant.createCollection(name, { + vectors: { size: vectorSize, distance: "Cosine" }, + }); + } +} diff --git a/apps/broker/src/types.ts b/apps/broker/src/types.ts index 75b75c0..502a858 100644 --- a/apps/broker/src/types.ts +++ b/apps/broker/src/types.ts @@ -230,6 +230,133 @@ export interface WSMemoryResultsMessage { }>; } +// --- Vector storage messages --- + +/** Client → broker: store a text document in a vector collection. */ +export interface WSVectorStoreMessage { + type: "vector_store"; + collection: string; + text: string; + metadata?: Record; +} + +/** Client → broker: search a vector collection. */ +export interface WSVectorSearchMessage { + type: "vector_search"; + collection: string; + query: string; + limit?: number; +} + +/** Client → broker: delete a point from a vector collection. */ +export interface WSVectorDeleteMessage { + type: "vector_delete"; + collection: string; + id: string; +} + +/** Client → broker: list all vector collections for this mesh. */ +export interface WSListCollectionsMessage { + type: "list_collections"; +} + +// --- Graph database messages --- + +/** Client → broker: run a read-only Cypher query. */ +export interface WSGraphQueryMessage { + type: "graph_query"; + cypher: string; +} + +/** Client → broker: run a write Cypher statement. */ +export interface WSGraphExecuteMessage { + type: "graph_execute"; + cypher: string; +} + +// --- Mesh database (per-mesh PostgreSQL schema) messages --- + +/** Client → broker: run a SELECT query in the mesh's schema. */ +export interface WSMeshQueryMessage { + type: "mesh_query"; + sql: string; +} + +/** Client → broker: run a DDL/DML statement in the mesh's schema. */ +export interface WSMeshExecuteMessage { + type: "mesh_execute"; + sql: string; +} + +/** Client → broker: list tables and columns in the mesh's schema. */ +export interface WSMeshSchemaMessage { + type: "mesh_schema"; +} + +// --- Vector/Graph response messages --- + +/** Broker → client: vector search results. */ +export interface WSVectorResultsMessage { + type: "vector_results"; + results: Array<{ + id: string; + text: string; + score: number; + metadata?: Record; + }>; +} + +/** Broker → client: list of vector collections. */ +export interface WSCollectionListMessage { + type: "collection_list"; + collections: string[]; +} + +/** Broker → client: graph query results. */ +export interface WSGraphResultMessage { + type: "graph_result"; + records: Array>; +} + +/** Broker → client: mesh SQL query results. */ +export interface WSMeshQueryResultMessage { + type: "mesh_query_result"; + columns: string[]; + rows: Array>; + rowCount: number; +} + +/** Broker → client: mesh schema introspection results. */ +export interface WSMeshSchemaResultMessage { + type: "mesh_schema_result"; + tables: Array<{ + name: string; + columns: Array<{ name: string; type: string; nullable: boolean }>; + }>; +} + +/** Client → broker: get full mesh overview. */ +export interface WSMeshInfoMessage { + type: "mesh_info"; +} + +/** Broker → client: aggregated mesh overview. */ +export interface WSMeshInfoResultMessage { + type: "mesh_info_result"; + mesh: string; + peers: number; + groups: string[]; + stateKeys: string[]; + memoryCount: number; + fileCount: number; + tasks: { open: number; claimed: number; done: number }; + streams: string[]; + tables: string[]; + collections: string[]; + yourName: string; + yourGroups: Array<{ name: string; role?: string }>; +} + /** Client → broker: check delivery status of a message. */ export interface WSMessageStatusMessage { type: "message_status"; @@ -309,6 +436,170 @@ export interface WSFileStatusResultMessage { }>; } +// --- Context sharing messages --- + +/** Client → broker: share current working context. */ +export interface WSShareContextMessage { + type: "share_context"; + summary: string; + filesRead?: string[]; + keyFindings?: string[]; + tags?: string[]; +} + +/** Client → broker: search contexts by query. */ +export interface WSGetContextMessage { + type: "get_context"; + query: string; +} + +/** Client → broker: list all contexts in the mesh. */ +export interface WSListContextsMessage { + type: "list_contexts"; +} + +/** Broker → client: acknowledgement for share_context. */ +export interface WSContextSharedMessage { + type: "context_shared"; + id: string; +} + +/** Broker → client: response to get_context. */ +export interface WSContextResultsMessage { + type: "context_results"; + contexts: Array<{ + peerName: string; + summary: string; + filesRead: string[]; + keyFindings: string[]; + tags: string[]; + updatedAt: string; + }>; +} + +/** Broker → client: response to list_contexts. */ +export interface WSContextListMessage { + type: "context_list"; + contexts: Array<{ + peerName: string; + summary: string; + tags: string[]; + updatedAt: string; + }>; +} + +// --- Task messages --- + +/** Client → broker: create a task. */ +export interface WSCreateTaskMessage { + type: "create_task"; + title: string; + assignee?: string; + priority?: string; + tags?: string[]; +} + +/** Client → broker: claim an open task. */ +export interface WSClaimTaskMessage { + type: "claim_task"; + taskId: string; +} + +/** Client → broker: mark a task as done. */ +export interface WSCompleteTaskMessage { + type: "complete_task"; + taskId: string; + result?: string; +} + +/** Client → broker: list tasks with optional filters. */ +export interface WSListTasksMessage { + type: "list_tasks"; + status?: string; + assignee?: string; +} + +/** Broker → client: acknowledgement for create_task. */ +export interface WSTaskCreatedMessage { + type: "task_created"; + id: string; +} + +/** Broker → client: response to list_tasks, claim_task, complete_task. */ +export interface WSTaskListMessage { + type: "task_list"; + tasks: Array<{ + id: string; + title: string; + assignee: string | null; + claimedBy: string | null; + status: string; + priority: string; + createdBy: string | null; + tags: string[]; + createdAt: string; + }>; +} + +// --- Stream messages --- + +/** Client → broker: create a named real-time stream. */ +export interface WSCreateStreamMessage { + type: "create_stream"; + name: string; +} + +/** Client → broker: publish data to a stream. */ +export interface WSPublishMessage { + type: "publish"; + stream: string; + data: unknown; +} + +/** Client → broker: subscribe to a stream. */ +export interface WSSubscribeMessage { + type: "subscribe"; + stream: string; +} + +/** Client → broker: unsubscribe from a stream. */ +export interface WSUnsubscribeMessage { + type: "unsubscribe"; + stream: string; +} + +/** Client → broker: list all streams in the mesh. */ +export interface WSListStreamsMessage { + type: "list_streams"; +} + +/** Broker → client: acknowledgement for create_stream. */ +export interface WSStreamCreatedMessage { + type: "stream_created"; + id: string; + name: string; +} + +/** Broker → client: real-time data pushed from a stream. */ +export interface WSStreamDataMessage { + type: "stream_data"; + stream: string; + data: unknown; + publishedBy: string; +} + +/** Broker → client: response to list_streams. */ +export interface WSStreamListMessage { + type: "stream_list"; + streams: Array<{ + id: string; + name: string; + createdBy: string; + createdAt: string; + subscriberCount: number; + }>; +} + /** Broker → client: structured error. */ export interface WSErrorMessage { type: "error"; @@ -335,7 +626,29 @@ export type WSClientMessage = | WSGetFileMessage | WSListFilesMessage | WSFileStatusMessage - | WSDeleteFileMessage; + | WSDeleteFileMessage + | WSShareContextMessage + | WSGetContextMessage + | WSListContextsMessage + | WSCreateTaskMessage + | WSClaimTaskMessage + | WSCompleteTaskMessage + | WSListTasksMessage + | WSVectorStoreMessage + | WSVectorSearchMessage + | WSVectorDeleteMessage + | WSListCollectionsMessage + | WSGraphQueryMessage + | WSGraphExecuteMessage + | WSMeshQueryMessage + | WSMeshExecuteMessage + | WSMeshSchemaMessage + | WSCreateStreamMessage + | WSPublishMessage + | WSSubscribeMessage + | WSUnsubscribeMessage + | WSListStreamsMessage + | WSMeshInfoMessage; export type WSServerMessage = | WSHelloAckMessage @@ -351,4 +664,18 @@ export type WSServerMessage = | WSFileUrlMessage | WSFileListMessage | WSFileStatusResultMessage + | WSContextSharedMessage + | WSContextResultsMessage + | WSContextListMessage + | WSTaskCreatedMessage + | WSTaskListMessage + | WSVectorResultsMessage + | WSCollectionListMessage + | WSGraphResultMessage + | WSMeshQueryResultMessage + | WSMeshSchemaResultMessage + | WSStreamCreatedMessage + | WSStreamDataMessage + | WSStreamListMessage + | WSMeshInfoResultMessage | WSErrorMessage; diff --git a/apps/cli/package.json b/apps/cli/package.json index e35789c..9186d9f 100644 --- a/apps/cli/package.json +++ b/apps/cli/package.json @@ -1,6 +1,6 @@ { "name": "claudemesh-cli", - "version": "0.4.0", + "version": "0.5.0", "description": "Claude Code MCP client for claudemesh — peer mesh messaging between Claude sessions.", "keywords": [ "claude-code", diff --git a/apps/cli/src/mcp/server.ts b/apps/cli/src/mcp/server.ts index b93076c..aab12ab 100644 --- a/apps/cli/src/mcp/server.ts +++ b/apps/cli/src/mcp/server.ts @@ -166,6 +166,26 @@ When you receive a message, RESPOND IMMEDIATEL | list_files(query?, from?) | Find files shared in the mesh. | | file_status(id) | Check who has accessed a file. | | delete_file(id) | Remove a shared file from the mesh. | +| vector_store(collection, text, metadata?) | Store embedding in per-mesh Qdrant collection. | +| vector_search(collection, query, limit?) | Semantic search over stored embeddings. | +| vector_delete(collection, id) | Remove an embedding. | +| list_collections() | List vector collections in this mesh. | +| graph_query(cypher) | Read-only Cypher query on per-mesh Neo4j. | +| graph_execute(cypher) | Write Cypher query (CREATE, MERGE, DELETE). | +| mesh_query(sql) | Run a SELECT query on the per-mesh shared database. | +| mesh_execute(sql) | Run DDL/DML on the per-mesh database (CREATE TABLE, INSERT, UPDATE, DELETE). | +| mesh_schema() | List tables and columns in the per-mesh shared database. | +| create_stream(name) | Create a real-time data stream in the mesh. | +| publish(stream, data) | Push data to a stream. Subscribers receive it in real-time. | +| subscribe(stream) | Subscribe to a stream. Data pushes arrive as channel notifications. | +| list_streams() | List active streams in the mesh. | +| share_context(summary, files_read?, key_findings?, tags?) | Share session understanding with peers. | +| get_context(query) | Find context from peers who explored an area. | +| list_contexts() | See what all peers currently know. | +| create_task(title, assignee?, priority?, tags?) | Create a work item. | +| claim_task(id) | Claim an unclaimed task. | +| complete_task(id, result?) | Mark task done with optional result. | +| list_tasks(status?, assignee?) | List tasks filtered by status/assignee. | If multiple meshes are joined, prefix \`to\` with \`:\` to disambiguate (e.g. \`dev-team:Alice\`). @@ -192,6 +212,24 @@ Persistent knowledge that survives across sessions. Use remember(content, tags?) share_file for persistent references, send_message(file:) for ephemeral attachments. Tags on shared files make them searchable. Use list_files to find what peers shared. +## Vectors +Store and search semantic embeddings. Use vector_store to index content, vector_search to find similar content. + +## Graph +Build and query entity relationship graphs. Use graph_execute for writes (CREATE, MERGE), graph_query for reads (MATCH). + +## Mesh Database +Per-mesh PostgreSQL database. Use mesh_execute for DDL/DML (CREATE TABLE, INSERT), mesh_query for SELECT, mesh_schema to inspect tables. Schema auto-created on first use. + +## Streams +Real-time data channels. create_stream to start one, publish to push data, subscribe to receive pushes. Use for build logs, deploy status, live metrics. + +## Context +Share your session understanding with peers. Use share_context after exploring a codebase area. Check get_context before re-reading files another peer already analyzed. + +## Tasks +Create and claim work items. create_task to propose work, claim_task to take ownership, complete_task when done. Prevents duplicate effort. + ## Priority - "now": interrupt immediately, even if recipient is in DND (use for urgent: broken deploy, blocking issue) - "next" (default): deliver when recipient goes idle (normal coordination) @@ -455,6 +493,213 @@ Call list_peers at session start to understand who is online, their roles, and w return text(`Deleted: ${id}`); } + // --- Vectors --- + case "vector_store": { + const { collection, text: storeText, metadata } = (args ?? {}) as { collection?: string; text?: string; metadata?: Record }; + if (!collection || !storeText) return text("vector_store: `collection` and `text` required", true); + const client = allClients()[0]; + if (!client) return text("vector_store: not connected", true); + const id = await client.vectorStore(collection, storeText, metadata); + return text(`Stored in ${collection}${id ? ` (${id})` : ""}`); + } + case "vector_search": { + const { collection, query, limit } = (args ?? {}) as { collection?: string; query?: string; limit?: number }; + if (!collection || !query) return text("vector_search: `collection` and `query` required", true); + const client = allClients()[0]; + if (!client) return text("vector_search: not connected", true); + const results = await client.vectorSearch(collection, query, limit); + if (results.length === 0) return text(`No results in ${collection} for "${query}".`); + const lines = results.map(r => `- [${r.id.slice(0, 8)}…] (score: ${r.score.toFixed(3)}) ${r.text.slice(0, 120)}${r.text.length > 120 ? "…" : ""}`); + return text(`${results.length} result(s) in ${collection}:\n${lines.join("\n")}`); + } + case "vector_delete": { + const { collection, id } = (args ?? {}) as { collection?: string; id?: string }; + if (!collection || !id) return text("vector_delete: `collection` and `id` required", true); + const client = allClients()[0]; + if (!client) return text("vector_delete: not connected", true); + await client.vectorDelete(collection, id); + return text(`Deleted ${id} from ${collection}`); + } + case "list_collections": { + const client = allClients()[0]; + if (!client) return text("list_collections: not connected", true); + const collections = await client.listCollections(); + if (collections.length === 0) return text("No vector collections."); + return text(`Collections:\n${collections.map(c => `- ${c}`).join("\n")}`); + } + + // --- Graph --- + case "graph_query": { + const { cypher } = (args ?? {}) as { cypher?: string }; + if (!cypher) return text("graph_query: `cypher` required", true); + const client = allClients()[0]; + if (!client) return text("graph_query: not connected", true); + const rows = await client.graphQuery(cypher); + if (rows.length === 0) return text("No results."); + return text(JSON.stringify(rows, null, 2)); + } + case "graph_execute": { + const { cypher } = (args ?? {}) as { cypher?: string }; + if (!cypher) return text("graph_execute: `cypher` required", true); + const client = allClients()[0]; + if (!client) return text("graph_execute: not connected", true); + const rows = await client.graphExecute(cypher); + return text(rows.length > 0 ? JSON.stringify(rows, null, 2) : "Executed successfully."); + } + + // --- Context --- + case "share_context": { + const { summary, files_read, key_findings, tags } = (args ?? {}) as { summary?: string; files_read?: string[]; key_findings?: string[]; tags?: string[] }; + if (!summary) return text("share_context: `summary` required", true); + const client = allClients()[0]; + if (!client) return text("share_context: not connected", true); + await client.shareContext(summary, files_read, key_findings, tags); + return text(`Context shared: "${summary.slice(0, 80)}${summary.length > 80 ? "…" : ""}"`); + } + case "get_context": { + const { query } = (args ?? {}) as { query?: string }; + if (!query) return text("get_context: `query` required", true); + const client = allClients()[0]; + if (!client) return text("get_context: not connected", true); + const contexts = await client.getContext(query); + if (contexts.length === 0) return text(`No context found for "${query}".`); + const lines = contexts.map(c => { + const files = c.filesRead.length ? `\n Files: ${c.filesRead.join(", ")}` : ""; + const findings = c.keyFindings.length ? `\n Findings: ${c.keyFindings.join("; ")}` : ""; + return `- **${c.peerName}** (${c.updatedAt}): ${c.summary}${files}${findings}`; + }); + return text(`${contexts.length} context(s):\n${lines.join("\n")}`); + } + case "list_contexts": { + const client = allClients()[0]; + if (!client) return text("list_contexts: not connected", true); + const contexts = await client.listContexts(); + if (contexts.length === 0) return text("No peer contexts shared yet."); + const lines = contexts.map(c => `- **${c.peerName}**: ${c.summary}${c.tags.length ? ` [${c.tags.join(", ")}]` : ""}`); + return text(`Peer contexts:\n${lines.join("\n")}`); + } + + // --- Tasks --- + case "create_task": { + const { title, assignee, priority, tags } = (args ?? {}) as { title?: string; assignee?: string; priority?: string; tags?: string[] }; + if (!title) return text("create_task: `title` required", true); + const client = allClients()[0]; + if (!client) return text("create_task: not connected", true); + const id = await client.createTask(title, assignee, priority, tags); + return text(`Task created${id ? ` (${id})` : ""}: "${title}"${assignee ? ` → ${assignee}` : ""}`); + } + case "claim_task": { + const { id } = (args ?? {}) as { id?: string }; + if (!id) return text("claim_task: `id` required", true); + const client = allClients()[0]; + if (!client) return text("claim_task: not connected", true); + await client.claimTask(id); + return text(`Claimed task: ${id}`); + } + case "complete_task": { + const { id, result } = (args ?? {}) as { id?: string; result?: string }; + if (!id) return text("complete_task: `id` required", true); + const client = allClients()[0]; + if (!client) return text("complete_task: not connected", true); + await client.completeTask(id, result); + return text(`Completed task: ${id}${result ? ` — ${result}` : ""}`); + } + case "list_tasks": { + const { status, assignee } = (args ?? {}) as { status?: string; assignee?: string }; + const client = allClients()[0]; + if (!client) return text("list_tasks: not connected", true); + const tasks = await client.listTasks(status, assignee); + if (tasks.length === 0) return text("No tasks found."); + const lines = tasks.map(t => `- [${t.id.slice(0, 8)}…] **${t.title}** (${t.status}, ${t.priority}) ${t.assignee ? `→ ${t.assignee}` : "unassigned"} (by ${t.createdBy})`); + return text(`${tasks.length} task(s):\n${lines.join("\n")}`); + } + + // --- Mesh Database --- + case "mesh_query": { + const { sql: querySql } = (args ?? {}) as { sql?: string }; + if (!querySql) return text("mesh_query: `sql` required", true); + const client = allClients()[0]; + if (!client) return text("mesh_query: not connected", true); + const result = await client.meshQuery(querySql); + if (!result) return text("mesh_query: query failed or timed out", true); + if (result.rows.length === 0) return text(`Query returned 0 rows.`); + const header = `| ${result.columns.join(" | ")} |`; + const sep = `| ${result.columns.map(() => "---").join(" | ")} |`; + const rows = result.rows.map(r => `| ${result.columns.map(c => String(r[c] ?? "")).join(" | ")} |`); + return text(`${result.rowCount} row(s):\n${header}\n${sep}\n${rows.join("\n")}`); + } + case "mesh_execute": { + const { sql: execSql } = (args ?? {}) as { sql?: string }; + if (!execSql) return text("mesh_execute: `sql` required", true); + const client = allClients()[0]; + if (!client) return text("mesh_execute: not connected", true); + await client.meshExecute(execSql); + return text(`Executed.`); + } + case "mesh_schema": { + const client = allClients()[0]; + if (!client) return text("mesh_schema: not connected", true); + const tables = await client.meshSchema(); + if (!tables || tables.length === 0) return text("No tables in mesh database."); + const lines = tables.map(t => `**${t.name}**: ${t.columns.map(c => `${c.name} (${c.type}${c.nullable ? ", nullable" : ""})`).join(", ")}`); + return text(lines.join("\n")); + } + + // --- Streams --- + case "create_stream": { + const { name: streamName } = (args ?? {}) as { name?: string }; + if (!streamName) return text("create_stream: `name` required", true); + const client = allClients()[0]; + if (!client) return text("create_stream: not connected", true); + const streamId = await client.createStream(streamName); + return text(`Stream created: ${streamName}${streamId ? ` (${streamId})` : ""}`); + } + case "publish": { + const { stream: pubStream, data: pubData } = (args ?? {}) as { stream?: string; data?: unknown }; + if (!pubStream) return text("publish: `stream` required", true); + const client = allClients()[0]; + if (!client) return text("publish: not connected", true); + await client.publish(pubStream, pubData); + return text(`Published to ${pubStream}.`); + } + case "subscribe": { + const { stream: subStream } = (args ?? {}) as { stream?: string }; + if (!subStream) return text("subscribe: `stream` required", true); + const client = allClients()[0]; + if (!client) return text("subscribe: not connected", true); + await client.subscribe(subStream); + return text(`Subscribed to ${subStream}. Data pushes will arrive as channel notifications.`); + } + case "list_streams": { + const client = allClients()[0]; + if (!client) return text("list_streams: not connected", true); + const streams = await client.listStreams(); + if (streams.length === 0) return text("No active streams."); + const lines = streams.map(s => `- **${s.name}** (${s.id.slice(0, 8)}…) by ${s.createdBy}, ${s.subscriberCount} subscriber(s)`); + return text(lines.join("\n")); + } + + case "mesh_info": { + const client = allClients()[0]; + if (!client) return text("mesh_info: not connected", true); + const info = await client.meshInfo(); + if (!info) return text("mesh_info: timed out", true); + const lines = [ + `**Mesh**: ${info.mesh}`, + `**Peers**: ${info.peers}`, + `**Groups**: ${(info.groups as string[])?.join(", ") || "none"}`, + `**State keys**: ${(info.stateKeys as string[])?.join(", ") || "none"}`, + `**Memories**: ${info.memoryCount}`, + `**Files**: ${info.fileCount}`, + `**Tasks**: open=${(info.tasks as any)?.open ?? 0}, claimed=${(info.tasks as any)?.claimed ?? 0}, done=${(info.tasks as any)?.done ?? 0}`, + `**Streams**: ${(info.streams as string[])?.join(", ") || "none"}`, + `**Tables**: ${(info.tables as string[])?.join(", ") || "none"}`, + `**Your name**: ${info.yourName}`, + `**Your groups**: ${(info.yourGroups as any[])?.map((g: any) => `@${g.name}${g.role ? ':' + g.role : ''}`).join(", ") || "none"}`, + ]; + return text(lines.join("\n")); + } + default: return text(`Unknown tool: ${name}`, true); } @@ -499,6 +744,22 @@ Call list_peers at session start to understand who is online, their roles, and w } }); + client.onStreamData(async (evt) => { + try { + await server.notification({ + method: "notifications/claude/channel", + params: { + content: `[stream:${evt.stream}] from ${evt.publishedBy}: ${JSON.stringify(evt.data)}`, + meta: { + kind: "stream_data", + stream: evt.stream, + published_by: evt.publishedBy, + }, + }, + }); + } catch { /* best effort */ } + }); + client.onStateChange(async (change) => { try { await server.notification({ diff --git a/apps/cli/src/mcp/tools.ts b/apps/cli/src/mcp/tools.ts index f7ce533..77113ff 100644 --- a/apps/cli/src/mcp/tools.ts +++ b/apps/cli/src/mcp/tools.ts @@ -269,4 +269,290 @@ export const TOOLS: Tool[] = [ required: ["id"], }, }, + + // --- Vector tools --- + { + name: "vector_store", + description: + "Store an embedding in a per-mesh Qdrant collection. Auto-creates the collection on first use.", + inputSchema: { + type: "object", + properties: { + collection: { type: "string", description: "Collection name" }, + text: { type: "string", description: "Text to embed and store" }, + metadata: { + type: "object", + description: "Optional metadata to attach", + }, + }, + required: ["collection", "text"], + }, + }, + { + name: "vector_search", + description: "Semantic search over stored embeddings in a collection.", + inputSchema: { + type: "object", + properties: { + collection: { type: "string", description: "Collection name" }, + query: { type: "string", description: "Search query text" }, + limit: { + type: "number", + description: "Max results (default: 10)", + }, + }, + required: ["collection", "query"], + }, + }, + { + name: "vector_delete", + description: "Remove an embedding from a collection.", + inputSchema: { + type: "object", + properties: { + collection: { type: "string", description: "Collection name" }, + id: { type: "string", description: "Embedding ID to delete" }, + }, + required: ["collection", "id"], + }, + }, + { + name: "list_collections", + description: "List vector collections in this mesh.", + inputSchema: { type: "object", properties: {} }, + }, + + // --- Graph tools --- + { + name: "graph_query", + description: + "Run a read-only Cypher query on the per-mesh Neo4j database.", + inputSchema: { + type: "object", + properties: { + cypher: { type: "string", description: "Cypher MATCH query" }, + }, + required: ["cypher"], + }, + }, + { + name: "graph_execute", + description: + "Run a write Cypher query (CREATE, MERGE, DELETE) on the per-mesh Neo4j database.", + inputSchema: { + type: "object", + properties: { + cypher: { type: "string", description: "Cypher write query" }, + }, + required: ["cypher"], + }, + }, + + // --- Mesh Database tools --- + { + name: "mesh_query", + description: + "Run a SELECT query on the per-mesh shared database.", + inputSchema: { + type: "object", + properties: { + sql: { type: "string", description: "SQL SELECT query" }, + }, + required: ["sql"], + }, + }, + { + name: "mesh_execute", + description: + "Run DDL/DML on the per-mesh database (CREATE TABLE, INSERT, UPDATE, DELETE).", + inputSchema: { + type: "object", + properties: { + sql: { type: "string", description: "SQL statement" }, + }, + required: ["sql"], + }, + }, + { + name: "mesh_schema", + description: + "List tables and columns in the per-mesh shared database.", + inputSchema: { type: "object", properties: {} }, + }, + + // --- Stream tools --- + { + name: "create_stream", + description: + "Create a real-time data stream in the mesh.", + inputSchema: { + type: "object", + properties: { + name: { type: "string", description: "Stream name" }, + }, + required: ["name"], + }, + }, + { + name: "publish", + description: + "Push data to a stream. Subscribers receive it in real-time.", + inputSchema: { + type: "object", + properties: { + stream: { type: "string", description: "Stream name" }, + data: { description: "Any JSON data to publish" }, + }, + required: ["stream", "data"], + }, + }, + { + name: "subscribe", + description: + "Subscribe to a stream. Data pushes arrive as channel notifications.", + inputSchema: { + type: "object", + properties: { + stream: { type: "string", description: "Stream name" }, + }, + required: ["stream"], + }, + }, + { + name: "list_streams", + description: + "List active streams in the mesh.", + inputSchema: { type: "object", properties: {} }, + }, + + // --- Context tools --- + { + name: "share_context", + description: + "Share your session understanding with the mesh. Call after exploring a codebase area.", + inputSchema: { + type: "object", + properties: { + summary: { + type: "string", + description: "Summary of what you explored/learned", + }, + files_read: { + type: "array", + items: { type: "string" }, + description: "File paths you read", + }, + key_findings: { + type: "array", + items: { type: "string" }, + description: "Key findings or insights", + }, + tags: { + type: "array", + items: { type: "string" }, + description: "Tags for categorization", + }, + }, + required: ["summary"], + }, + }, + { + name: "get_context", + description: + "Find context from peers who explored an area. Check before re-reading files another peer already analyzed.", + inputSchema: { + type: "object", + properties: { + query: { + type: "string", + description: "Search query (file path, topic, etc.)", + }, + }, + required: ["query"], + }, + }, + { + name: "list_contexts", + description: "See what all peers currently know about the codebase.", + inputSchema: { type: "object", properties: {} }, + }, + + // --- Task tools --- + { + name: "create_task", + description: "Create a work item for the mesh.", + inputSchema: { + type: "object", + properties: { + title: { type: "string", description: "Task title" }, + assignee: { + type: "string", + description: "Peer name to assign (optional)", + }, + priority: { + type: "string", + enum: ["low", "normal", "high", "urgent"], + description: "Priority level (default: normal)", + }, + tags: { + type: "array", + items: { type: "string" }, + description: "Tags for categorization", + }, + }, + required: ["title"], + }, + }, + { + name: "claim_task", + description: "Claim an unclaimed task to take ownership.", + inputSchema: { + type: "object", + properties: { + id: { type: "string", description: "Task ID" }, + }, + required: ["id"], + }, + }, + { + name: "complete_task", + description: "Mark a task as done with an optional result summary.", + inputSchema: { + type: "object", + properties: { + id: { type: "string", description: "Task ID" }, + result: { + type: "string", + description: "Summary of what was done", + }, + }, + required: ["id"], + }, + }, + { + name: "list_tasks", + description: "List tasks filtered by status and/or assignee.", + inputSchema: { + type: "object", + properties: { + status: { + type: "string", + enum: ["open", "claimed", "completed"], + description: "Filter by status", + }, + assignee: { + type: "string", + description: "Filter by assignee name", + }, + }, + }, + }, + + // --- Mesh info --- + { + name: "mesh_info", + description: + "Get a complete overview of the mesh: peers, groups, state, memory, files, tasks, streams, tables. Call on session start for full situational awareness.", + inputSchema: { type: "object", properties: {} }, + }, ]; diff --git a/apps/cli/src/ws/client.ts b/apps/cli/src/ws/client.ts index 91bad07..3dd452f 100644 --- a/apps/cli/src/ws/client.ts +++ b/apps/cli/src/ws/client.ts @@ -415,6 +415,19 @@ export class BrokerClient { private fileUrlResolvers: Array<(result: { url: string; name: string } | null) => void> = []; private fileListResolvers: Array<(files: Array<{ id: string; name: string; size: number; tags: string[]; uploadedBy: string; uploadedAt: string; persistent: boolean }>) => void> = []; private fileStatusResolvers: Array<(accesses: Array<{ peerName: string; accessedAt: string }>) => void> = []; + private vectorStoredResolvers: Array<(id: string | null) => void> = []; + private vectorResultsResolvers: Array<(results: Array<{ id: string; text: string; score: number; metadata?: Record }>) => void> = []; + private collectionListResolvers: Array<(collections: string[]) => void> = []; + private graphResultResolvers: Array<(rows: Array>) => void> = []; + private contextListResolvers: Array<(contexts: Array<{ peerName: string; summary: string; tags: string[]; updatedAt: string }>) => void> = []; + private contextResultsResolvers: Array<(contexts: Array<{ peerName: string; summary: string; filesRead: string[]; keyFindings: string[]; tags: string[]; updatedAt: string }>) => void> = []; + private taskCreatedResolvers: Array<(id: string | null) => void> = []; + private taskListResolvers: Array<(tasks: Array<{ id: string; title: string; assignee: string; status: string; priority: string; createdBy: string }>) => void> = []; + private meshQueryResolvers: Array<(result: { columns: string[]; rows: Array>; rowCount: number } | null) => void> = []; + private meshSchemaResolvers: Array<(tables: Array<{ name: string; columns: Array<{ name: string; type: string; nullable: boolean }> }>) => void> = []; + private streamCreatedResolvers: Array<(id: string | null) => void> = []; + private streamListResolvers: Array<(streams: Array<{ id: string; name: string; createdBy: string; subscriberCount: number }>) => void> = []; + private streamDataHandlers = new Set<(data: { stream: string; data: unknown; publishedBy: string }) => void>(); async messageStatus(messageId: string): Promise<{ messageId: string; targetSpec: string; delivered: boolean; deliveredAt: string | null; recipients: Array<{ name: string; pubkey: string; status: string }> } | null> { if (!this.ws || this.ws.readyState !== this.ws.OPEN) return null; @@ -517,12 +530,262 @@ export class BrokerClient { return body.fileId ?? null; } + // --- Vectors --- + + /** Store an embedding in a per-mesh Qdrant collection. */ + async vectorStore(collection: string, text: string, metadata?: Record): Promise { + if (!this.ws || this.ws.readyState !== this.ws.OPEN) return null; + return new Promise((resolve) => { + this.vectorStoredResolvers.push(resolve); + this.ws!.send(JSON.stringify({ type: "vector_store", collection, text, metadata })); + setTimeout(() => { + const idx = this.vectorStoredResolvers.indexOf(resolve); + if (idx !== -1) { this.vectorStoredResolvers.splice(idx, 1); resolve(null); } + }, 5_000); + }); + } + + /** Semantic search over stored embeddings. */ + async vectorSearch(collection: string, query: string, limit?: number): Promise }>> { + if (!this.ws || this.ws.readyState !== this.ws.OPEN) return []; + return new Promise((resolve) => { + this.vectorResultsResolvers.push(resolve); + this.ws!.send(JSON.stringify({ type: "vector_search", collection, query, limit })); + setTimeout(() => { + const idx = this.vectorResultsResolvers.indexOf(resolve); + if (idx !== -1) { this.vectorResultsResolvers.splice(idx, 1); resolve([]); } + }, 5_000); + }); + } + + /** Remove an embedding from a collection. */ + async vectorDelete(collection: string, id: string): Promise { + if (!this.ws || this.ws.readyState !== this.ws.OPEN) return; + this.ws.send(JSON.stringify({ type: "vector_delete", collection, id })); + } + + /** List vector collections in this mesh. */ + async listCollections(): Promise { + if (!this.ws || this.ws.readyState !== this.ws.OPEN) return []; + return new Promise((resolve) => { + this.collectionListResolvers.push(resolve); + this.ws!.send(JSON.stringify({ type: "list_collections" })); + setTimeout(() => { + const idx = this.collectionListResolvers.indexOf(resolve); + if (idx !== -1) { this.collectionListResolvers.splice(idx, 1); resolve([]); } + }, 5_000); + }); + } + + // --- Graph --- + + /** Run a read query on the per-mesh Neo4j database. */ + async graphQuery(cypher: string): Promise>> { + if (!this.ws || this.ws.readyState !== this.ws.OPEN) return []; + return new Promise((resolve) => { + this.graphResultResolvers.push(resolve); + this.ws!.send(JSON.stringify({ type: "graph_query", cypher })); + setTimeout(() => { + const idx = this.graphResultResolvers.indexOf(resolve); + if (idx !== -1) { this.graphResultResolvers.splice(idx, 1); resolve([]); } + }, 5_000); + }); + } + + /** Run a write query (CREATE, MERGE, DELETE) on the per-mesh Neo4j database. */ + async graphExecute(cypher: string): Promise>> { + if (!this.ws || this.ws.readyState !== this.ws.OPEN) return []; + return new Promise((resolve) => { + this.graphResultResolvers.push(resolve); + this.ws!.send(JSON.stringify({ type: "graph_execute", cypher })); + setTimeout(() => { + const idx = this.graphResultResolvers.indexOf(resolve); + if (idx !== -1) { this.graphResultResolvers.splice(idx, 1); resolve([]); } + }, 5_000); + }); + } + + // --- Context --- + + /** Share session understanding with the mesh. */ + async shareContext(summary: string, filesRead?: string[], keyFindings?: string[], tags?: string[]): Promise { + if (!this.ws || this.ws.readyState !== this.ws.OPEN) return; + this.ws.send(JSON.stringify({ type: "share_context", summary, filesRead, keyFindings, tags })); + } + + /** Find context from peers who explored an area. */ + async getContext(query: string): Promise> { + if (!this.ws || this.ws.readyState !== this.ws.OPEN) return []; + return new Promise((resolve) => { + this.contextResultsResolvers.push(resolve); + this.ws!.send(JSON.stringify({ type: "get_context", query })); + setTimeout(() => { + const idx = this.contextResultsResolvers.indexOf(resolve); + if (idx !== -1) { this.contextResultsResolvers.splice(idx, 1); resolve([]); } + }, 5_000); + }); + } + + /** See what all peers currently know. */ + async listContexts(): Promise> { + if (!this.ws || this.ws.readyState !== this.ws.OPEN) return []; + return new Promise((resolve) => { + this.contextListResolvers.push(resolve); + this.ws!.send(JSON.stringify({ type: "list_contexts" })); + setTimeout(() => { + const idx = this.contextListResolvers.indexOf(resolve); + if (idx !== -1) { this.contextListResolvers.splice(idx, 1); resolve([]); } + }, 5_000); + }); + } + + // --- Tasks --- + + /** Create a work item. */ + async createTask(title: string, assignee?: string, priority?: string, tags?: string[]): Promise { + if (!this.ws || this.ws.readyState !== this.ws.OPEN) return null; + return new Promise((resolve) => { + this.taskCreatedResolvers.push(resolve); + this.ws!.send(JSON.stringify({ type: "create_task", title, assignee, priority, tags })); + setTimeout(() => { + const idx = this.taskCreatedResolvers.indexOf(resolve); + if (idx !== -1) { this.taskCreatedResolvers.splice(idx, 1); resolve(null); } + }, 5_000); + }); + } + + /** Claim an unclaimed task. */ + async claimTask(id: string): Promise { + if (!this.ws || this.ws.readyState !== this.ws.OPEN) return; + this.ws.send(JSON.stringify({ type: "claim_task", id })); + } + + /** Mark a task done with optional result. */ + async completeTask(id: string, result?: string): Promise { + if (!this.ws || this.ws.readyState !== this.ws.OPEN) return; + this.ws.send(JSON.stringify({ type: "complete_task", id, result })); + } + + /** List tasks filtered by status/assignee. */ + async listTasks(status?: string, assignee?: string): Promise> { + if (!this.ws || this.ws.readyState !== this.ws.OPEN) return []; + return new Promise((resolve) => { + this.taskListResolvers.push(resolve); + this.ws!.send(JSON.stringify({ type: "list_tasks", status, assignee })); + setTimeout(() => { + const idx = this.taskListResolvers.indexOf(resolve); + if (idx !== -1) { this.taskListResolvers.splice(idx, 1); resolve([]); } + }, 5_000); + }); + } + + // --- Mesh Database --- + + /** Run a SELECT query on the per-mesh shared database. */ + async meshQuery(sql: string): Promise<{ columns: string[]; rows: Array>; rowCount: number } | null> { + if (!this.ws || this.ws.readyState !== this.ws.OPEN) return null; + return new Promise((resolve) => { + this.meshQueryResolvers.push(resolve); + this.ws!.send(JSON.stringify({ type: "mesh_query", sql })); + setTimeout(() => { + const idx = this.meshQueryResolvers.indexOf(resolve); + if (idx !== -1) { this.meshQueryResolvers.splice(idx, 1); resolve(null); } + }, 5_000); + }); + } + + /** Run DDL/DML on the per-mesh database (CREATE TABLE, INSERT, UPDATE, DELETE). */ + async meshExecute(sql: string): Promise { + if (!this.ws || this.ws.readyState !== this.ws.OPEN) return; + this.ws.send(JSON.stringify({ type: "mesh_execute", sql })); + } + + /** List tables and columns in the per-mesh shared database. */ + async meshSchema(): Promise }>> { + if (!this.ws || this.ws.readyState !== this.ws.OPEN) return []; + return new Promise((resolve) => { + this.meshSchemaResolvers.push(resolve); + this.ws!.send(JSON.stringify({ type: "mesh_schema" })); + setTimeout(() => { + const idx = this.meshSchemaResolvers.indexOf(resolve); + if (idx !== -1) { this.meshSchemaResolvers.splice(idx, 1); resolve([]); } + }, 5_000); + }); + } + + // --- Streams --- + + /** Create a real-time data stream in the mesh. */ + async createStream(name: string): Promise { + if (!this.ws || this.ws.readyState !== this.ws.OPEN) return null; + return new Promise((resolve) => { + this.streamCreatedResolvers.push(resolve); + this.ws!.send(JSON.stringify({ type: "create_stream", name })); + setTimeout(() => { + const idx = this.streamCreatedResolvers.indexOf(resolve); + if (idx !== -1) { this.streamCreatedResolvers.splice(idx, 1); resolve(null); } + }, 5_000); + }); + } + + /** Push data to a stream. Subscribers receive it in real-time. */ + async publish(stream: string, data: unknown): Promise { + if (!this.ws || this.ws.readyState !== this.ws.OPEN) return; + this.ws.send(JSON.stringify({ type: "publish", stream, data })); + } + + /** Subscribe to a stream. Data pushes arrive via onStreamData handler. */ + async subscribe(stream: string): Promise { + if (!this.ws || this.ws.readyState !== this.ws.OPEN) return; + this.ws.send(JSON.stringify({ type: "subscribe", stream })); + } + + /** Unsubscribe from a stream. */ + async unsubscribe(stream: string): Promise { + if (!this.ws || this.ws.readyState !== this.ws.OPEN) return; + this.ws.send(JSON.stringify({ type: "unsubscribe", stream })); + } + + /** List active streams in the mesh. */ + async listStreams(): Promise> { + if (!this.ws || this.ws.readyState !== this.ws.OPEN) return []; + return new Promise((resolve) => { + this.streamListResolvers.push(resolve); + this.ws!.send(JSON.stringify({ type: "list_streams" })); + setTimeout(() => { + const idx = this.streamListResolvers.indexOf(resolve); + if (idx !== -1) { this.streamListResolvers.splice(idx, 1); resolve([]); } + }, 5_000); + }); + } + + /** Subscribe to stream data pushes. Returns an unsubscribe function. */ + onStreamData(handler: (data: { stream: string; data: unknown; publishedBy: string }) => void): () => void { + this.streamDataHandlers.add(handler); + return () => this.streamDataHandlers.delete(handler); + } + /** Subscribe to state change notifications. Returns an unsubscribe function. */ onStateChange(handler: (change: { key: string; value: unknown; updatedBy: string }) => void): () => void { this.stateChangeHandlers.add(handler); return () => this.stateChangeHandlers.delete(handler); } + // --- Mesh info --- + private meshInfoResolvers: Array<(result: Record | null) => void> = []; + + async meshInfo(): Promise | null> { + if (!this.ws || this.ws.readyState !== this.ws.OPEN) return null; + return new Promise((resolve) => { + this.meshInfoResolvers.push(resolve); + this.ws!.send(JSON.stringify({ type: "mesh_info" })); + setTimeout(() => { + const idx = this.meshInfoResolvers.indexOf(resolve); + if (idx !== -1) { this.meshInfoResolvers.splice(idx, 1); resolve(null); } + }, 5_000); + }); + } + close(): void { this.closed = true; if (this.helloTimer) clearTimeout(this.helloTimer); @@ -698,6 +961,100 @@ export class BrokerClient { if (resolver) resolver(accesses); return; } + if (msg.type === "vector_stored") { + const resolver = this.vectorStoredResolvers.shift(); + if (resolver) resolver(msg.id ? String(msg.id) : null); + return; + } + if (msg.type === "vector_results") { + const results = (msg.results as Array<{ id: string; text: string; score: number; metadata?: Record }>) ?? []; + const resolver = this.vectorResultsResolvers.shift(); + if (resolver) resolver(results); + return; + } + if (msg.type === "collection_list") { + const collections = (msg.collections as string[]) ?? []; + const resolver = this.collectionListResolvers.shift(); + if (resolver) resolver(collections); + return; + } + if (msg.type === "graph_result") { + const rows = (msg.rows as Array>) ?? []; + const resolver = this.graphResultResolvers.shift(); + if (resolver) resolver(rows); + return; + } + if (msg.type === "context_list") { + const contexts = (msg.contexts as Array<{ peerName: string; summary: string; tags: string[]; updatedAt: string }>) ?? []; + const resolver = this.contextListResolvers.shift(); + if (resolver) resolver(contexts); + return; + } + if (msg.type === "context_results") { + const contexts = (msg.contexts as Array<{ peerName: string; summary: string; filesRead: string[]; keyFindings: string[]; tags: string[]; updatedAt: string }>) ?? []; + const resolver = this.contextResultsResolvers.shift(); + if (resolver) resolver(contexts); + return; + } + if (msg.type === "task_created") { + const resolver = this.taskCreatedResolvers.shift(); + if (resolver) resolver(msg.id ? String(msg.id) : null); + return; + } + if (msg.type === "task_list") { + const tasks = (msg.tasks as Array<{ id: string; title: string; assignee: string; status: string; priority: string; createdBy: string }>) ?? []; + const resolver = this.taskListResolvers.shift(); + if (resolver) resolver(tasks); + return; + } + if (msg.type === "mesh_query_result") { + const resolver = this.meshQueryResolvers.shift(); + if (resolver) { + if (msg.columns) { + resolver({ + columns: (msg.columns as string[]) ?? [], + rows: (msg.rows as Array>) ?? [], + rowCount: (msg.rowCount as number) ?? 0, + }); + } else { + resolver(null); + } + } + return; + } + if (msg.type === "mesh_schema_result") { + const tables = (msg.tables as Array<{ name: string; columns: Array<{ name: string; type: string; nullable: boolean }> }>) ?? []; + const resolver = this.meshSchemaResolvers.shift(); + if (resolver) resolver(tables); + return; + } + if (msg.type === "stream_created") { + const resolver = this.streamCreatedResolvers.shift(); + if (resolver) resolver(msg.id ? String(msg.id) : null); + return; + } + if (msg.type === "stream_list") { + const streams = (msg.streams as Array<{ id: string; name: string; createdBy: string; subscriberCount: number }>) ?? []; + const resolver = this.streamListResolvers.shift(); + if (resolver) resolver(streams); + return; + } + if (msg.type === "stream_data") { + const evt = { + stream: String(msg.stream ?? ""), + data: msg.data, + publishedBy: String(msg.publishedBy ?? ""), + }; + for (const h of this.streamDataHandlers) { + try { h(evt); } catch { /* handler errors are not the transport's problem */ } + } + return; + } + if (msg.type === "mesh_info_result") { + const resolver = this.meshInfoResolvers.shift(); + if (resolver) resolver(msg as Record); + return; + } if (msg.type === "error") { this.debug(`broker error: ${msg.code} ${msg.message}`); const id = msg.id ? String(msg.id) : null; diff --git a/apps/web/next.config.ts b/apps/web/next.config.ts index e02b8b2..6dc3727 100644 --- a/apps/web/next.config.ts +++ b/apps/web/next.config.ts @@ -1,5 +1,8 @@ import type { NextConfig } from "next"; +// eslint-disable-next-line @typescript-eslint/no-require-imports +const { withPayload } = require("@payloadcms/next/withPayload"); + import env from "./env.config"; const INTERNAL_PACKAGES = [ @@ -130,4 +133,4 @@ const withBundleAnalyzer = require("@next/bundle-analyzer")({ enabled: env.ANALYZE, }); -export default withBundleAnalyzer(config); +export default withPayload(withBundleAnalyzer(config)); diff --git a/apps/web/src/app/(payload)/layout.tsx b/apps/web/src/app/(payload)/layout.tsx new file mode 100644 index 0000000..bdcfe53 --- /dev/null +++ b/apps/web/src/app/(payload)/layout.tsx @@ -0,0 +1,14 @@ +import "@payloadcms/next/css"; +import type { ReactNode } from "react"; + +export const metadata = { + title: "CMS — claudemesh", +}; + +export default function PayloadLayout({ children }: { children: ReactNode }) { + return ( + + {children} + + ); +} diff --git a/apps/web/src/app/(payload)/payload/[[...segments]]/page.tsx b/apps/web/src/app/(payload)/payload/[[...segments]]/page.tsx new file mode 100644 index 0000000..d72f5cd --- /dev/null +++ b/apps/web/src/app/(payload)/payload/[[...segments]]/page.tsx @@ -0,0 +1,14 @@ +/* eslint-disable */ +// @ts-nocheck — Payload generates these types at build time +import { RootPage, generatePageMetadata } from "@payloadcms/next/views"; +import { importMap } from "../importMap"; +import config from "@payload-config"; + +type Args = { params: Promise<{ segments: string[] }> }; + +export const generateMetadata = ({ params }: Args) => + generatePageMetadata({ config, params }); + +export default function Page({ params }: Args) { + return ; +} diff --git a/apps/web/src/app/(payload)/payload/importMap.js b/apps/web/src/app/(payload)/payload/importMap.js new file mode 100644 index 0000000..d275fa9 --- /dev/null +++ b/apps/web/src/app/(payload)/payload/importMap.js @@ -0,0 +1,51 @@ +import { RscEntryLexicalCell as RscEntryLexicalCell_44fe37237e0ebf4470c9990d8cb7b07e } from '@payloadcms/richtext-lexical/rsc' +import { RscEntryLexicalField as RscEntryLexicalField_44fe37237e0ebf4470c9990d8cb7b07e } from '@payloadcms/richtext-lexical/rsc' +import { LexicalDiffComponent as LexicalDiffComponent_44fe37237e0ebf4470c9990d8cb7b07e } from '@payloadcms/richtext-lexical/rsc' +import { InlineToolbarFeatureClient as InlineToolbarFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client' +import { HorizontalRuleFeatureClient as HorizontalRuleFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client' +import { UploadFeatureClient as UploadFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client' +import { BlockquoteFeatureClient as BlockquoteFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client' +import { RelationshipFeatureClient as RelationshipFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client' +import { LinkFeatureClient as LinkFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client' +import { ChecklistFeatureClient as ChecklistFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client' +import { OrderedListFeatureClient as OrderedListFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client' +import { UnorderedListFeatureClient as UnorderedListFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client' +import { IndentFeatureClient as IndentFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client' +import { AlignFeatureClient as AlignFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client' +import { HeadingFeatureClient as HeadingFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client' +import { ParagraphFeatureClient as ParagraphFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client' +import { InlineCodeFeatureClient as InlineCodeFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client' +import { SuperscriptFeatureClient as SuperscriptFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client' +import { SubscriptFeatureClient as SubscriptFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client' +import { StrikethroughFeatureClient as StrikethroughFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client' +import { UnderlineFeatureClient as UnderlineFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client' +import { BoldFeatureClient as BoldFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client' +import { ItalicFeatureClient as ItalicFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client' +import { CollectionCards as CollectionCards_f9c02e79a4aed9a3924487c0cd4cafb1 } from '@payloadcms/next/rsc' + +export const importMap = { + "@payloadcms/richtext-lexical/rsc#RscEntryLexicalCell": RscEntryLexicalCell_44fe37237e0ebf4470c9990d8cb7b07e, + "@payloadcms/richtext-lexical/rsc#RscEntryLexicalField": RscEntryLexicalField_44fe37237e0ebf4470c9990d8cb7b07e, + "@payloadcms/richtext-lexical/rsc#LexicalDiffComponent": LexicalDiffComponent_44fe37237e0ebf4470c9990d8cb7b07e, + "@payloadcms/richtext-lexical/client#InlineToolbarFeatureClient": InlineToolbarFeatureClient_e70f5e05f09f93e00b997edb1ef0c864, + "@payloadcms/richtext-lexical/client#HorizontalRuleFeatureClient": HorizontalRuleFeatureClient_e70f5e05f09f93e00b997edb1ef0c864, + "@payloadcms/richtext-lexical/client#UploadFeatureClient": UploadFeatureClient_e70f5e05f09f93e00b997edb1ef0c864, + "@payloadcms/richtext-lexical/client#BlockquoteFeatureClient": BlockquoteFeatureClient_e70f5e05f09f93e00b997edb1ef0c864, + "@payloadcms/richtext-lexical/client#RelationshipFeatureClient": RelationshipFeatureClient_e70f5e05f09f93e00b997edb1ef0c864, + "@payloadcms/richtext-lexical/client#LinkFeatureClient": LinkFeatureClient_e70f5e05f09f93e00b997edb1ef0c864, + "@payloadcms/richtext-lexical/client#ChecklistFeatureClient": ChecklistFeatureClient_e70f5e05f09f93e00b997edb1ef0c864, + "@payloadcms/richtext-lexical/client#OrderedListFeatureClient": OrderedListFeatureClient_e70f5e05f09f93e00b997edb1ef0c864, + "@payloadcms/richtext-lexical/client#UnorderedListFeatureClient": UnorderedListFeatureClient_e70f5e05f09f93e00b997edb1ef0c864, + "@payloadcms/richtext-lexical/client#IndentFeatureClient": IndentFeatureClient_e70f5e05f09f93e00b997edb1ef0c864, + "@payloadcms/richtext-lexical/client#AlignFeatureClient": AlignFeatureClient_e70f5e05f09f93e00b997edb1ef0c864, + "@payloadcms/richtext-lexical/client#HeadingFeatureClient": HeadingFeatureClient_e70f5e05f09f93e00b997edb1ef0c864, + "@payloadcms/richtext-lexical/client#ParagraphFeatureClient": ParagraphFeatureClient_e70f5e05f09f93e00b997edb1ef0c864, + "@payloadcms/richtext-lexical/client#InlineCodeFeatureClient": InlineCodeFeatureClient_e70f5e05f09f93e00b997edb1ef0c864, + "@payloadcms/richtext-lexical/client#SuperscriptFeatureClient": SuperscriptFeatureClient_e70f5e05f09f93e00b997edb1ef0c864, + "@payloadcms/richtext-lexical/client#SubscriptFeatureClient": SubscriptFeatureClient_e70f5e05f09f93e00b997edb1ef0c864, + "@payloadcms/richtext-lexical/client#StrikethroughFeatureClient": StrikethroughFeatureClient_e70f5e05f09f93e00b997edb1ef0c864, + "@payloadcms/richtext-lexical/client#UnderlineFeatureClient": UnderlineFeatureClient_e70f5e05f09f93e00b997edb1ef0c864, + "@payloadcms/richtext-lexical/client#BoldFeatureClient": BoldFeatureClient_e70f5e05f09f93e00b997edb1ef0c864, + "@payloadcms/richtext-lexical/client#ItalicFeatureClient": ItalicFeatureClient_e70f5e05f09f93e00b997edb1ef0c864, + "@payloadcms/next/rsc#CollectionCards": CollectionCards_f9c02e79a4aed9a3924487c0cd4cafb1 +} diff --git a/docker-compose.production.yml b/docker-compose.production.yml index 35b5d6b..2bb9d89 100644 --- a/docker-compose.production.yml +++ b/docker-compose.production.yml @@ -48,6 +48,41 @@ services: start_period: 10s retries: 3 + qdrant: + image: qdrant/qdrant + restart: always + volumes: + - qdrant-data:/qdrant/storage + expose: + - "6333" + networks: + - claudemesh-internal + healthcheck: + test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:6333/readyz"] + interval: 15s + timeout: 5s + retries: 3 + + neo4j: + image: neo4j:5 + restart: always + environment: + NEO4J_AUTH: neo4j/${NEO4J_PASSWORD:-changeme} + NEO4J_PLUGINS: '[]' + volumes: + - neo4j-data:/data + expose: + - "7687" + - "7474" + networks: + - claudemesh-internal + healthcheck: + test: ["CMD", "cypher-shell", "-u", "neo4j", "-p", "${NEO4J_PASSWORD:-changeme}", "RETURN 1"] + interval: 15s + timeout: 5s + start_period: 30s + retries: 3 + broker: image: ${BROKER_IMAGE:-claudemesh-broker:latest} restart: always @@ -64,6 +99,10 @@ services: MINIO_ACCESS_KEY: claudemesh MINIO_SECRET_KEY: ${MINIO_SECRET_KEY:-changeme} MINIO_USE_SSL: "false" + QDRANT_URL: http://qdrant:6333 + NEO4J_URL: bolt://neo4j:7687 + NEO4J_USER: neo4j + NEO4J_PASSWORD: ${NEO4J_PASSWORD:-changeme} expose: - "7900" networks: @@ -72,6 +111,10 @@ services: depends_on: minio: condition: service_healthy + qdrant: + condition: service_healthy + neo4j: + condition: service_healthy healthcheck: test: ["CMD", "bun", "-e", "fetch('http://localhost:7900/health').then(r=>{process.exit(r.ok?0:1)}).catch(()=>process.exit(1))"] interval: 15s @@ -114,6 +157,8 @@ services: volumes: minio-data: + qdrant-data: + neo4j-data: networks: # Coolify's shared Traefik network — must already exist on the host diff --git a/packages/db/migrations/0010_add-context-and-tasks.sql b/packages/db/migrations/0010_add-context-and-tasks.sql new file mode 100644 index 0000000..042bdc6 --- /dev/null +++ b/packages/db/migrations/0010_add-context-and-tasks.sql @@ -0,0 +1,33 @@ +CREATE TABLE "mesh"."context" ( + "id" text PRIMARY KEY NOT NULL, + "mesh_id" text NOT NULL, + "presence_id" text, + "peer_name" text, + "summary" text NOT NULL, + "files_read" text[] DEFAULT '{}', + "key_findings" text[] DEFAULT '{}', + "tags" text[] DEFAULT '{}', + "updated_at" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "mesh"."task" ( + "id" text PRIMARY KEY NOT NULL, + "mesh_id" text NOT NULL, + "title" text NOT NULL, + "assignee" text, + "claimed_by_name" text, + "claimed_by_presence" text, + "priority" text DEFAULT 'normal' NOT NULL, + "status" text DEFAULT 'open' NOT NULL, + "tags" text[] DEFAULT '{}', + "result" text, + "created_by_name" text, + "created_at" timestamp DEFAULT now() NOT NULL, + "claimed_at" timestamp, + "completed_at" timestamp +); +--> statement-breakpoint +ALTER TABLE "mesh"."context" ADD CONSTRAINT "context_mesh_id_mesh_id_fk" FOREIGN KEY ("mesh_id") REFERENCES "mesh"."mesh"("id") ON DELETE cascade ON UPDATE cascade;--> statement-breakpoint +ALTER TABLE "mesh"."context" ADD CONSTRAINT "context_presence_id_presence_id_fk" FOREIGN KEY ("presence_id") REFERENCES "mesh"."presence"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "mesh"."task" ADD CONSTRAINT "task_mesh_id_mesh_id_fk" FOREIGN KEY ("mesh_id") REFERENCES "mesh"."mesh"("id") ON DELETE cascade ON UPDATE cascade;--> statement-breakpoint +ALTER TABLE "mesh"."task" ADD CONSTRAINT "task_claimed_by_presence_presence_id_fk" FOREIGN KEY ("claimed_by_presence") REFERENCES "mesh"."presence"("id") ON DELETE no action ON UPDATE no action; \ No newline at end of file diff --git a/packages/db/migrations/0011_add-streams.sql b/packages/db/migrations/0011_add-streams.sql new file mode 100644 index 0000000..e3dad97 --- /dev/null +++ b/packages/db/migrations/0011_add-streams.sql @@ -0,0 +1,10 @@ +CREATE TABLE "mesh"."stream" ( + "id" text PRIMARY KEY NOT NULL, + "mesh_id" text NOT NULL, + "name" text NOT NULL, + "created_by_name" text, + "created_at" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +ALTER TABLE "mesh"."stream" ADD CONSTRAINT "stream_mesh_id_mesh_id_fk" FOREIGN KEY ("mesh_id") REFERENCES "mesh"."mesh"("id") ON DELETE cascade ON UPDATE cascade;--> statement-breakpoint +CREATE UNIQUE INDEX "stream_mesh_name_idx" ON "mesh"."stream" USING btree ("mesh_id","name"); \ No newline at end of file diff --git a/packages/db/migrations/meta/0010_snapshot.json b/packages/db/migrations/meta/0010_snapshot.json new file mode 100644 index 0000000..cb12fcb --- /dev/null +++ b/packages/db/migrations/meta/0010_snapshot.json @@ -0,0 +1,3467 @@ +{ + "id": "44001cdd-a307-4f03-9ba1-41b6ffefa574", + "prevId": "cced9051-7468-4428-8d46-472edd5f4509", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.account": { + "name": "account", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "account_id": { + "name": "account_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider_id": { + "name": "provider_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "refresh_token": { + "name": "refresh_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "id_token": { + "name": "id_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "access_token_expires_at": { + "name": "access_token_expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "refresh_token_expires_at": { + "name": "refresh_token_expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "scope": { + "name": "scope", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "account_userId_idx": { + "name": "account_userId_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "account_user_id_user_id_fk": { + "name": "account_user_id_user_id_fk", + "tableFrom": "account", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.invitation": { + "name": "invitation", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "inviter_id": { + "name": "inviter_id", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "invitation_organizationId_idx": { + "name": "invitation_organizationId_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "invitation_email_idx": { + "name": "invitation_email_idx", + "columns": [ + { + "expression": "email", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "invitation_organization_id_organization_id_fk": { + "name": "invitation_organization_id_organization_id_fk", + "tableFrom": "invitation", + "tableTo": "organization", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "invitation_inviter_id_user_id_fk": { + "name": "invitation_inviter_id_user_id_fk", + "tableFrom": "invitation", + "tableTo": "user", + "columnsFrom": [ + "inviter_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.member": { + "name": "member", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'member'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "member_organizationId_idx": { + "name": "member_organizationId_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "member_userId_idx": { + "name": "member_userId_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "member_organization_id_organization_id_fk": { + "name": "member_organization_id_organization_id_fk", + "tableFrom": "member", + "tableTo": "organization", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "member_user_id_user_id_fk": { + "name": "member_user_id_user_id_fk", + "tableFrom": "member", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.organization": { + "name": "organization", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "logo": { + "name": "logo", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "metadata": { + "name": "metadata", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "organization_slug_unique": { + "name": "organization_slug_unique", + "nullsNotDistinct": false, + "columns": [ + "slug" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.passkey": { + "name": "passkey", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "public_key": { + "name": "public_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "credential_id": { + "name": "credential_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "counter": { + "name": "counter", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "device_type": { + "name": "device_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "backed_up": { + "name": "backed_up", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "transports": { + "name": "transports", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "aaguid": { + "name": "aaguid", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "passkey_userId_idx": { + "name": "passkey_userId_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "passkey_credentialID_idx": { + "name": "passkey_credentialID_idx", + "columns": [ + { + "expression": "credential_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "passkey_user_id_user_id_fk": { + "name": "passkey_user_id_user_id_fk", + "tableFrom": "passkey", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.session": { + "name": "session", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "ip_address": { + "name": "ip_address", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_agent": { + "name": "user_agent", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "impersonated_by": { + "name": "impersonated_by", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "active_organization_id": { + "name": "active_organization_id", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "session_userId_idx": { + "name": "session_userId_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "session_user_id_user_id_fk": { + "name": "session_user_id_user_id_fk", + "tableFrom": "session", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "session_token_unique": { + "name": "session_token_unique", + "nullsNotDistinct": false, + "columns": [ + "token" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.two_factor": { + "name": "two_factor", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "secret": { + "name": "secret", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "backup_codes": { + "name": "backup_codes", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "twoFactor_secret_idx": { + "name": "twoFactor_secret_idx", + "columns": [ + { + "expression": "secret", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "twoFactor_userId_idx": { + "name": "twoFactor_userId_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "two_factor_user_id_user_id_fk": { + "name": "two_factor_user_id_user_id_fk", + "tableFrom": "two_factor", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user": { + "name": "user", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email_verified": { + "name": "email_verified", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "two_factor_enabled": { + "name": "two_factor_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "is_anonymous": { + "name": "is_anonymous", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "banned": { + "name": "banned", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "ban_reason": { + "name": "ban_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "ban_expires": { + "name": "ban_expires", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "user_email_unique": { + "name": "user_email_unique", + "nullsNotDistinct": false, + "columns": [ + "email" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.verification": { + "name": "verification", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "identifier": { + "name": "identifier", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "value": { + "name": "value", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "verification_identifier_idx": { + "name": "verification_identifier_idx", + "columns": [ + { + "expression": "identifier", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.credit_transaction": { + "name": "credit_transaction", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "customer_id": { + "name": "customer_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "amount": { + "name": "amount", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "credit_transaction_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "reason": { + "name": "reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "metadata": { + "name": "metadata", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "balance_after": { + "name": "balance_after", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "created_by": { + "name": "created_by", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "credit_transaction_customer_id_customer_id_fk": { + "name": "credit_transaction_customer_id_customer_id_fk", + "tableFrom": "credit_transaction", + "tableTo": "customer", + "columnsFrom": [ + "customer_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.customer": { + "name": "customer", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "customer_id": { + "name": "customer_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "status", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "plan": { + "name": "plan", + "type": "plan", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "credits": { + "name": "credits", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 100 + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "customer_user_id_user_id_fk": { + "name": "customer_user_id_user_id_fk", + "tableFrom": "customer", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "customer_userId_unique": { + "name": "customer_userId_unique", + "nullsNotDistinct": false, + "columns": [ + "user_id" + ] + }, + "customer_customerId_unique": { + "name": "customer_customerId_unique", + "nullsNotDistinct": false, + "columns": [ + "customer_id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "chat.chat": { + "name": "chat", + "schema": "chat", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "chat_user_id_user_id_fk": { + "name": "chat_user_id_user_id_fk", + "tableFrom": "chat", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "chat.message": { + "name": "message", + "schema": "chat", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "chat_id": { + "name": "chat_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "role", + "typeSchema": "chat", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "message_chat_id_chat_id_fk": { + "name": "message_chat_id_chat_id_fk", + "tableFrom": "message", + "tableTo": "chat", + "schemaTo": "chat", + "columnsFrom": [ + "chat_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "chat.part": { + "name": "part", + "schema": "chat", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "message_id": { + "name": "message_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "order": { + "name": "order", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "details": { + "name": "details", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "part_message_id_message_id_fk": { + "name": "part_message_id_message_id_fk", + "tableFrom": "part", + "tableTo": "message", + "schemaTo": "chat", + "columnsFrom": [ + "message_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "pdf.chat": { + "name": "chat", + "schema": "pdf", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "chat_user_id_user_id_fk": { + "name": "chat_user_id_user_id_fk", + "tableFrom": "chat", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "pdf.citation_unit": { + "name": "citation_unit", + "schema": "pdf", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "document_id": { + "name": "document_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "retrieval_chunk_id": { + "name": "retrieval_chunk_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "page_number": { + "name": "page_number", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "paragraph_index": { + "name": "paragraph_index", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "char_start": { + "name": "char_start", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "char_end": { + "name": "char_end", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "bbox_x": { + "name": "bbox_x", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "bbox_y": { + "name": "bbox_y", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "bbox_width": { + "name": "bbox_width", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "bbox_height": { + "name": "bbox_height", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "section_title": { + "name": "section_title", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "unit_type": { + "name": "unit_type", + "type": "unit_type", + "typeSchema": "pdf", + "primaryKey": false, + "notNull": false, + "default": "'prose'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": { + "idx_cu_document": { + "name": "idx_cu_document", + "columns": [ + { + "expression": "document_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_cu_retrieval": { + "name": "idx_cu_retrieval", + "columns": [ + { + "expression": "retrieval_chunk_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_cu_page": { + "name": "idx_cu_page", + "columns": [ + { + "expression": "document_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "page_number", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_cu_unique": { + "name": "idx_cu_unique", + "columns": [ + { + "expression": "document_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "page_number", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "paragraph_index", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "citation_unit_document_id_document_id_fk": { + "name": "citation_unit_document_id_document_id_fk", + "tableFrom": "citation_unit", + "tableTo": "document", + "schemaTo": "pdf", + "columnsFrom": [ + "document_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "citation_unit_retrieval_chunk_id_retrieval_chunk_id_fk": { + "name": "citation_unit_retrieval_chunk_id_retrieval_chunk_id_fk", + "tableFrom": "citation_unit", + "tableTo": "retrieval_chunk", + "schemaTo": "pdf", + "columnsFrom": [ + "retrieval_chunk_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "pdf.document": { + "name": "document", + "schema": "pdf", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "chat_id": { + "name": "chat_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "path": { + "name": "path", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "processing_status": { + "name": "processing_status", + "type": "processing_status", + "typeSchema": "pdf", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "processing_error": { + "name": "processing_error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "document_chat_id_chat_id_fk": { + "name": "document_chat_id_chat_id_fk", + "tableFrom": "document", + "tableTo": "chat", + "schemaTo": "pdf", + "columnsFrom": [ + "chat_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "pdf.embedding": { + "name": "embedding", + "schema": "pdf", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "document_id": { + "name": "document_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "embedding": { + "name": "embedding", + "type": "vector(1536)", + "primaryKey": false, + "notNull": true + }, + "page_number": { + "name": "page_number", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "char_start": { + "name": "char_start", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "char_end": { + "name": "char_end", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "section_title": { + "name": "section_title", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": { + "pdf_embeddingIndex": { + "name": "pdf_embeddingIndex", + "columns": [ + { + "expression": "embedding", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "vector_cosine_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "hnsw", + "with": {} + } + }, + "foreignKeys": { + "embedding_document_id_document_id_fk": { + "name": "embedding_document_id_document_id_fk", + "tableFrom": "embedding", + "tableTo": "document", + "schemaTo": "pdf", + "columnsFrom": [ + "document_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "pdf.message": { + "name": "message", + "schema": "pdf", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "chat_id": { + "name": "chat_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "role", + "typeSchema": "pdf", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "message_chat_id_chat_id_fk": { + "name": "message_chat_id_chat_id_fk", + "tableFrom": "message", + "tableTo": "chat", + "schemaTo": "pdf", + "columnsFrom": [ + "chat_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "pdf.retrieval_chunk": { + "name": "retrieval_chunk", + "schema": "pdf", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "document_id": { + "name": "document_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "embedding": { + "name": "embedding", + "type": "vector(1536)", + "primaryKey": false, + "notNull": false + }, + "page_start": { + "name": "page_start", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "page_end": { + "name": "page_end", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "section_hierarchy": { + "name": "section_hierarchy", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "chunk_type": { + "name": "chunk_type", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'prose'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": { + "idx_rc_document": { + "name": "idx_rc_document", + "columns": [ + { + "expression": "document_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_rc_embedding": { + "name": "idx_rc_embedding", + "columns": [ + { + "expression": "embedding", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "vector_cosine_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "hnsw", + "with": {} + } + }, + "foreignKeys": { + "retrieval_chunk_document_id_document_id_fk": { + "name": "retrieval_chunk_document_id_document_id_fk", + "tableFrom": "retrieval_chunk", + "tableTo": "document", + "schemaTo": "pdf", + "columnsFrom": [ + "document_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "image.generation": { + "name": "generation", + "schema": "image", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "prompt": { + "name": "prompt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "model": { + "name": "model", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "aspect_ratio": { + "name": "aspect_ratio", + "type": "aspect_ratio", + "typeSchema": "image", + "primaryKey": false, + "notNull": true, + "default": "'square'" + }, + "count": { + "name": "count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "generation_user_id_user_id_fk": { + "name": "generation_user_id_user_id_fk", + "tableFrom": "generation", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "image.image": { + "name": "image", + "schema": "image", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "generation_id": { + "name": "generation_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "url": { + "name": "url", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "image_generation_id_generation_id_fk": { + "name": "image_generation_id_generation_id_fk", + "tableFrom": "image", + "tableTo": "generation", + "schemaTo": "image", + "columnsFrom": [ + "generation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "mesh.audit_log": { + "name": "audit_log", + "schema": "mesh", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "mesh_id": { + "name": "mesh_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "event_type": { + "name": "event_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "actor_peer_id": { + "name": "actor_peer_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "target_peer_id": { + "name": "target_peer_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "audit_log_mesh_id_mesh_id_fk": { + "name": "audit_log_mesh_id_mesh_id_fk", + "tableFrom": "audit_log", + "tableTo": "mesh", + "schemaTo": "mesh", + "columnsFrom": [ + "mesh_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "mesh.invite": { + "name": "invite", + "schema": "mesh", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "mesh_id": { + "name": "mesh_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "token_bytes": { + "name": "token_bytes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "max_uses": { + "name": "max_uses", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "used_count": { + "name": "used_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "role": { + "name": "role", + "type": "role", + "typeSchema": "mesh", + "primaryKey": false, + "notNull": true, + "default": "'member'" + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "created_by": { + "name": "created_by", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "revoked_at": { + "name": "revoked_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "invite_mesh_id_mesh_id_fk": { + "name": "invite_mesh_id_mesh_id_fk", + "tableFrom": "invite", + "tableTo": "mesh", + "schemaTo": "mesh", + "columnsFrom": [ + "mesh_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "invite_created_by_user_id_fk": { + "name": "invite_created_by_user_id_fk", + "tableFrom": "invite", + "tableTo": "user", + "columnsFrom": [ + "created_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "invite_token_unique": { + "name": "invite_token_unique", + "nullsNotDistinct": false, + "columns": [ + "token" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "mesh.mesh": { + "name": "mesh", + "schema": "mesh", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "owner_user_id": { + "name": "owner_user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "visibility": { + "name": "visibility", + "type": "visibility", + "typeSchema": "mesh", + "primaryKey": false, + "notNull": true, + "default": "'private'" + }, + "transport": { + "name": "transport", + "type": "transport", + "typeSchema": "mesh", + "primaryKey": false, + "notNull": true, + "default": "'managed'" + }, + "max_peers": { + "name": "max_peers", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "tier": { + "name": "tier", + "type": "tier", + "typeSchema": "mesh", + "primaryKey": false, + "notNull": true, + "default": "'free'" + }, + "owner_pubkey": { + "name": "owner_pubkey", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "owner_secret_key": { + "name": "owner_secret_key", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "root_key": { + "name": "root_key", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "archived_at": { + "name": "archived_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "mesh_owner_user_id_user_id_fk": { + "name": "mesh_owner_user_id_user_id_fk", + "tableFrom": "mesh", + "tableTo": "user", + "columnsFrom": [ + "owner_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "mesh_slug_unique": { + "name": "mesh_slug_unique", + "nullsNotDistinct": false, + "columns": [ + "slug" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "mesh.context": { + "name": "context", + "schema": "mesh", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "mesh_id": { + "name": "mesh_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "presence_id": { + "name": "presence_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "peer_name": { + "name": "peer_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "summary": { + "name": "summary", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "files_read": { + "name": "files_read", + "type": "text[]", + "primaryKey": false, + "notNull": false, + "default": "'{}'" + }, + "key_findings": { + "name": "key_findings", + "type": "text[]", + "primaryKey": false, + "notNull": false, + "default": "'{}'" + }, + "tags": { + "name": "tags", + "type": "text[]", + "primaryKey": false, + "notNull": false, + "default": "'{}'" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "context_mesh_id_mesh_id_fk": { + "name": "context_mesh_id_mesh_id_fk", + "tableFrom": "context", + "tableTo": "mesh", + "schemaTo": "mesh", + "columnsFrom": [ + "mesh_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "context_presence_id_presence_id_fk": { + "name": "context_presence_id_presence_id_fk", + "tableFrom": "context", + "tableTo": "presence", + "schemaTo": "mesh", + "columnsFrom": [ + "presence_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "mesh.file": { + "name": "file", + "schema": "mesh", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "mesh_id": { + "name": "mesh_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "size_bytes": { + "name": "size_bytes", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "mime_type": { + "name": "mime_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "minio_key": { + "name": "minio_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "tags": { + "name": "tags", + "type": "text[]", + "primaryKey": false, + "notNull": false, + "default": "'{}'" + }, + "persistent": { + "name": "persistent", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "uploaded_by_name": { + "name": "uploaded_by_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "uploaded_by_member": { + "name": "uploaded_by_member", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "target_spec": { + "name": "target_spec", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "uploaded_at": { + "name": "uploaded_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "file_mesh_id_mesh_id_fk": { + "name": "file_mesh_id_mesh_id_fk", + "tableFrom": "file", + "tableTo": "mesh", + "schemaTo": "mesh", + "columnsFrom": [ + "mesh_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "file_uploaded_by_member_member_id_fk": { + "name": "file_uploaded_by_member_member_id_fk", + "tableFrom": "file", + "tableTo": "member", + "schemaTo": "mesh", + "columnsFrom": [ + "uploaded_by_member" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "mesh.file_access": { + "name": "file_access", + "schema": "mesh", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "file_id": { + "name": "file_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "peer_session_pubkey": { + "name": "peer_session_pubkey", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "peer_name": { + "name": "peer_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "accessed_at": { + "name": "accessed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "file_access_file_id_file_id_fk": { + "name": "file_access_file_id_file_id_fk", + "tableFrom": "file_access", + "tableTo": "file", + "schemaTo": "mesh", + "columnsFrom": [ + "file_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "mesh.member": { + "name": "member", + "schema": "mesh", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "mesh_id": { + "name": "mesh_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "peer_pubkey": { + "name": "peer_pubkey", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "display_name": { + "name": "display_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "role", + "typeSchema": "mesh", + "primaryKey": false, + "notNull": true, + "default": "'member'" + }, + "joined_at": { + "name": "joined_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "last_seen_at": { + "name": "last_seen_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "revoked_at": { + "name": "revoked_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "member_mesh_id_mesh_id_fk": { + "name": "member_mesh_id_mesh_id_fk", + "tableFrom": "member", + "tableTo": "mesh", + "schemaTo": "mesh", + "columnsFrom": [ + "mesh_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "member_user_id_user_id_fk": { + "name": "member_user_id_user_id_fk", + "tableFrom": "member", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "mesh.memory": { + "name": "memory", + "schema": "mesh", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "mesh_id": { + "name": "mesh_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "tags": { + "name": "tags", + "type": "text[]", + "primaryKey": false, + "notNull": false, + "default": "'{}'" + }, + "remembered_by": { + "name": "remembered_by", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "remembered_by_name": { + "name": "remembered_by_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "remembered_at": { + "name": "remembered_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "forgotten_at": { + "name": "forgotten_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "memory_mesh_id_mesh_id_fk": { + "name": "memory_mesh_id_mesh_id_fk", + "tableFrom": "memory", + "tableTo": "mesh", + "schemaTo": "mesh", + "columnsFrom": [ + "mesh_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "memory_remembered_by_member_id_fk": { + "name": "memory_remembered_by_member_id_fk", + "tableFrom": "memory", + "tableTo": "member", + "schemaTo": "mesh", + "columnsFrom": [ + "remembered_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "mesh.state": { + "name": "state", + "schema": "mesh", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "mesh_id": { + "name": "mesh_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "key": { + "name": "key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "value": { + "name": "value", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "updated_by_presence": { + "name": "updated_by_presence", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "updated_by_name": { + "name": "updated_by_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "state_mesh_key_idx": { + "name": "state_mesh_key_idx", + "columns": [ + { + "expression": "mesh_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "state_mesh_id_mesh_id_fk": { + "name": "state_mesh_id_mesh_id_fk", + "tableFrom": "state", + "tableTo": "mesh", + "schemaTo": "mesh", + "columnsFrom": [ + "mesh_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "mesh.task": { + "name": "task", + "schema": "mesh", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "mesh_id": { + "name": "mesh_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "assignee": { + "name": "assignee", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "claimed_by_name": { + "name": "claimed_by_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "claimed_by_presence": { + "name": "claimed_by_presence", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "priority": { + "name": "priority", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'normal'" + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'open'" + }, + "tags": { + "name": "tags", + "type": "text[]", + "primaryKey": false, + "notNull": false, + "default": "'{}'" + }, + "result": { + "name": "result", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_by_name": { + "name": "created_by_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "claimed_at": { + "name": "claimed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "task_mesh_id_mesh_id_fk": { + "name": "task_mesh_id_mesh_id_fk", + "tableFrom": "task", + "tableTo": "mesh", + "schemaTo": "mesh", + "columnsFrom": [ + "mesh_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "task_claimed_by_presence_presence_id_fk": { + "name": "task_claimed_by_presence_presence_id_fk", + "tableFrom": "task", + "tableTo": "presence", + "schemaTo": "mesh", + "columnsFrom": [ + "claimed_by_presence" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "mesh.message_queue": { + "name": "message_queue", + "schema": "mesh", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "mesh_id": { + "name": "mesh_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "sender_member_id": { + "name": "sender_member_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "sender_session_pubkey": { + "name": "sender_session_pubkey", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "target_spec": { + "name": "target_spec", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "priority": { + "name": "priority", + "type": "message_priority", + "typeSchema": "mesh", + "primaryKey": false, + "notNull": true, + "default": "'next'" + }, + "nonce": { + "name": "nonce", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "ciphertext": { + "name": "ciphertext", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "delivered_at": { + "name": "delivered_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "message_queue_mesh_id_mesh_id_fk": { + "name": "message_queue_mesh_id_mesh_id_fk", + "tableFrom": "message_queue", + "tableTo": "mesh", + "schemaTo": "mesh", + "columnsFrom": [ + "mesh_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "message_queue_sender_member_id_member_id_fk": { + "name": "message_queue_sender_member_id_member_id_fk", + "tableFrom": "message_queue", + "tableTo": "member", + "schemaTo": "mesh", + "columnsFrom": [ + "sender_member_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "mesh.pending_status": { + "name": "pending_status", + "schema": "mesh", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "pid": { + "name": "pid", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "cwd": { + "name": "cwd", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status_source": { + "name": "status_source", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "applied_at": { + "name": "applied_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "mesh.presence": { + "name": "presence", + "schema": "mesh", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "member_id": { + "name": "member_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "session_id": { + "name": "session_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "session_pubkey": { + "name": "session_pubkey", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "display_name": { + "name": "display_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "pid": { + "name": "pid", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "cwd": { + "name": "cwd", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "presence_status", + "typeSchema": "mesh", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "status_source": { + "name": "status_source", + "type": "presence_status_source", + "typeSchema": "mesh", + "primaryKey": false, + "notNull": true, + "default": "'jsonl'" + }, + "status_updated_at": { + "name": "status_updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "summary": { + "name": "summary", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "groups": { + "name": "groups", + "type": "jsonb", + "primaryKey": false, + "notNull": false, + "default": "'[]'::jsonb" + }, + "connected_at": { + "name": "connected_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "last_ping_at": { + "name": "last_ping_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "disconnected_at": { + "name": "disconnected_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "presence_member_id_member_id_fk": { + "name": "presence_member_id_member_id_fk", + "tableFrom": "presence", + "tableTo": "member", + "schemaTo": "mesh", + "columnsFrom": [ + "member_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": { + "public.credit_transaction_type": { + "name": "credit_transaction_type", + "schema": "public", + "values": [ + "signup", + "purchase", + "usage", + "admin_grant", + "admin_deduct", + "refund", + "promo", + "referral", + "expiry" + ] + }, + "public.status": { + "name": "status", + "schema": "public", + "values": [ + "active", + "canceled", + "incomplete", + "incomplete_expired", + "past_due", + "paused", + "trialing", + "unpaid" + ] + }, + "public.plan": { + "name": "plan", + "schema": "public", + "values": [ + "free", + "premium", + "enterprise" + ] + }, + "chat.role": { + "name": "role", + "schema": "chat", + "values": [ + "system", + "assistant", + "user" + ] + }, + "pdf.role": { + "name": "role", + "schema": "pdf", + "values": [ + "user", + "assistant", + "system" + ] + }, + "pdf.processing_status": { + "name": "processing_status", + "schema": "pdf", + "values": [ + "pending", + "processing", + "ready", + "failed" + ] + }, + "pdf.unit_type": { + "name": "unit_type", + "schema": "pdf", + "values": [ + "prose", + "heading", + "list", + "table", + "code" + ] + }, + "image.aspect_ratio": { + "name": "aspect_ratio", + "schema": "image", + "values": [ + "square", + "standard", + "landscape", + "portrait" + ] + }, + "mesh.role": { + "name": "role", + "schema": "mesh", + "values": [ + "admin", + "member" + ] + }, + "mesh.tier": { + "name": "tier", + "schema": "mesh", + "values": [ + "free", + "pro", + "team", + "enterprise" + ] + }, + "mesh.transport": { + "name": "transport", + "schema": "mesh", + "values": [ + "managed", + "tailscale", + "self_hosted" + ] + }, + "mesh.visibility": { + "name": "visibility", + "schema": "mesh", + "values": [ + "private", + "public" + ] + }, + "mesh.message_priority": { + "name": "message_priority", + "schema": "mesh", + "values": [ + "now", + "next", + "low" + ] + }, + "mesh.presence_status": { + "name": "presence_status", + "schema": "mesh", + "values": [ + "idle", + "working", + "dnd" + ] + }, + "mesh.presence_status_source": { + "name": "presence_status_source", + "schema": "mesh", + "values": [ + "hook", + "manual", + "jsonl" + ] + } + }, + "schemas": { + "chat": "chat", + "pdf": "pdf", + "image": "image", + "mesh": "mesh" + }, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/packages/db/migrations/meta/0011_snapshot.json b/packages/db/migrations/meta/0011_snapshot.json new file mode 100644 index 0000000..6fb03e6 --- /dev/null +++ b/packages/db/migrations/meta/0011_snapshot.json @@ -0,0 +1,3548 @@ +{ + "id": "f4e49724-34ae-4772-8786-2ffd54499b9e", + "prevId": "44001cdd-a307-4f03-9ba1-41b6ffefa574", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.account": { + "name": "account", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "account_id": { + "name": "account_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider_id": { + "name": "provider_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "refresh_token": { + "name": "refresh_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "id_token": { + "name": "id_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "access_token_expires_at": { + "name": "access_token_expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "refresh_token_expires_at": { + "name": "refresh_token_expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "scope": { + "name": "scope", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "account_userId_idx": { + "name": "account_userId_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "account_user_id_user_id_fk": { + "name": "account_user_id_user_id_fk", + "tableFrom": "account", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.invitation": { + "name": "invitation", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "inviter_id": { + "name": "inviter_id", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "invitation_organizationId_idx": { + "name": "invitation_organizationId_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "invitation_email_idx": { + "name": "invitation_email_idx", + "columns": [ + { + "expression": "email", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "invitation_organization_id_organization_id_fk": { + "name": "invitation_organization_id_organization_id_fk", + "tableFrom": "invitation", + "tableTo": "organization", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "invitation_inviter_id_user_id_fk": { + "name": "invitation_inviter_id_user_id_fk", + "tableFrom": "invitation", + "tableTo": "user", + "columnsFrom": [ + "inviter_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.member": { + "name": "member", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'member'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "member_organizationId_idx": { + "name": "member_organizationId_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "member_userId_idx": { + "name": "member_userId_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "member_organization_id_organization_id_fk": { + "name": "member_organization_id_organization_id_fk", + "tableFrom": "member", + "tableTo": "organization", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "member_user_id_user_id_fk": { + "name": "member_user_id_user_id_fk", + "tableFrom": "member", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.organization": { + "name": "organization", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "logo": { + "name": "logo", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "metadata": { + "name": "metadata", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "organization_slug_unique": { + "name": "organization_slug_unique", + "nullsNotDistinct": false, + "columns": [ + "slug" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.passkey": { + "name": "passkey", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "public_key": { + "name": "public_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "credential_id": { + "name": "credential_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "counter": { + "name": "counter", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "device_type": { + "name": "device_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "backed_up": { + "name": "backed_up", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "transports": { + "name": "transports", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "aaguid": { + "name": "aaguid", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "passkey_userId_idx": { + "name": "passkey_userId_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "passkey_credentialID_idx": { + "name": "passkey_credentialID_idx", + "columns": [ + { + "expression": "credential_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "passkey_user_id_user_id_fk": { + "name": "passkey_user_id_user_id_fk", + "tableFrom": "passkey", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.session": { + "name": "session", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "ip_address": { + "name": "ip_address", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_agent": { + "name": "user_agent", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "impersonated_by": { + "name": "impersonated_by", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "active_organization_id": { + "name": "active_organization_id", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "session_userId_idx": { + "name": "session_userId_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "session_user_id_user_id_fk": { + "name": "session_user_id_user_id_fk", + "tableFrom": "session", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "session_token_unique": { + "name": "session_token_unique", + "nullsNotDistinct": false, + "columns": [ + "token" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.two_factor": { + "name": "two_factor", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "secret": { + "name": "secret", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "backup_codes": { + "name": "backup_codes", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "twoFactor_secret_idx": { + "name": "twoFactor_secret_idx", + "columns": [ + { + "expression": "secret", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "twoFactor_userId_idx": { + "name": "twoFactor_userId_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "two_factor_user_id_user_id_fk": { + "name": "two_factor_user_id_user_id_fk", + "tableFrom": "two_factor", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user": { + "name": "user", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email_verified": { + "name": "email_verified", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "two_factor_enabled": { + "name": "two_factor_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "is_anonymous": { + "name": "is_anonymous", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "banned": { + "name": "banned", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "ban_reason": { + "name": "ban_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "ban_expires": { + "name": "ban_expires", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "user_email_unique": { + "name": "user_email_unique", + "nullsNotDistinct": false, + "columns": [ + "email" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.verification": { + "name": "verification", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "identifier": { + "name": "identifier", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "value": { + "name": "value", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "verification_identifier_idx": { + "name": "verification_identifier_idx", + "columns": [ + { + "expression": "identifier", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.credit_transaction": { + "name": "credit_transaction", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "customer_id": { + "name": "customer_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "amount": { + "name": "amount", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "credit_transaction_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "reason": { + "name": "reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "metadata": { + "name": "metadata", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "balance_after": { + "name": "balance_after", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "created_by": { + "name": "created_by", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "credit_transaction_customer_id_customer_id_fk": { + "name": "credit_transaction_customer_id_customer_id_fk", + "tableFrom": "credit_transaction", + "tableTo": "customer", + "columnsFrom": [ + "customer_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.customer": { + "name": "customer", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "customer_id": { + "name": "customer_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "status", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "plan": { + "name": "plan", + "type": "plan", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "credits": { + "name": "credits", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 100 + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "customer_user_id_user_id_fk": { + "name": "customer_user_id_user_id_fk", + "tableFrom": "customer", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "customer_userId_unique": { + "name": "customer_userId_unique", + "nullsNotDistinct": false, + "columns": [ + "user_id" + ] + }, + "customer_customerId_unique": { + "name": "customer_customerId_unique", + "nullsNotDistinct": false, + "columns": [ + "customer_id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "chat.chat": { + "name": "chat", + "schema": "chat", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "chat_user_id_user_id_fk": { + "name": "chat_user_id_user_id_fk", + "tableFrom": "chat", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "chat.message": { + "name": "message", + "schema": "chat", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "chat_id": { + "name": "chat_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "role", + "typeSchema": "chat", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "message_chat_id_chat_id_fk": { + "name": "message_chat_id_chat_id_fk", + "tableFrom": "message", + "tableTo": "chat", + "schemaTo": "chat", + "columnsFrom": [ + "chat_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "chat.part": { + "name": "part", + "schema": "chat", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "message_id": { + "name": "message_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "order": { + "name": "order", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "details": { + "name": "details", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "part_message_id_message_id_fk": { + "name": "part_message_id_message_id_fk", + "tableFrom": "part", + "tableTo": "message", + "schemaTo": "chat", + "columnsFrom": [ + "message_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "pdf.chat": { + "name": "chat", + "schema": "pdf", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "chat_user_id_user_id_fk": { + "name": "chat_user_id_user_id_fk", + "tableFrom": "chat", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "pdf.citation_unit": { + "name": "citation_unit", + "schema": "pdf", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "document_id": { + "name": "document_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "retrieval_chunk_id": { + "name": "retrieval_chunk_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "page_number": { + "name": "page_number", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "paragraph_index": { + "name": "paragraph_index", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "char_start": { + "name": "char_start", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "char_end": { + "name": "char_end", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "bbox_x": { + "name": "bbox_x", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "bbox_y": { + "name": "bbox_y", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "bbox_width": { + "name": "bbox_width", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "bbox_height": { + "name": "bbox_height", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "section_title": { + "name": "section_title", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "unit_type": { + "name": "unit_type", + "type": "unit_type", + "typeSchema": "pdf", + "primaryKey": false, + "notNull": false, + "default": "'prose'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": { + "idx_cu_document": { + "name": "idx_cu_document", + "columns": [ + { + "expression": "document_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_cu_retrieval": { + "name": "idx_cu_retrieval", + "columns": [ + { + "expression": "retrieval_chunk_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_cu_page": { + "name": "idx_cu_page", + "columns": [ + { + "expression": "document_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "page_number", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_cu_unique": { + "name": "idx_cu_unique", + "columns": [ + { + "expression": "document_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "page_number", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "paragraph_index", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "citation_unit_document_id_document_id_fk": { + "name": "citation_unit_document_id_document_id_fk", + "tableFrom": "citation_unit", + "tableTo": "document", + "schemaTo": "pdf", + "columnsFrom": [ + "document_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "citation_unit_retrieval_chunk_id_retrieval_chunk_id_fk": { + "name": "citation_unit_retrieval_chunk_id_retrieval_chunk_id_fk", + "tableFrom": "citation_unit", + "tableTo": "retrieval_chunk", + "schemaTo": "pdf", + "columnsFrom": [ + "retrieval_chunk_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "pdf.document": { + "name": "document", + "schema": "pdf", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "chat_id": { + "name": "chat_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "path": { + "name": "path", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "processing_status": { + "name": "processing_status", + "type": "processing_status", + "typeSchema": "pdf", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "processing_error": { + "name": "processing_error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "document_chat_id_chat_id_fk": { + "name": "document_chat_id_chat_id_fk", + "tableFrom": "document", + "tableTo": "chat", + "schemaTo": "pdf", + "columnsFrom": [ + "chat_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "pdf.embedding": { + "name": "embedding", + "schema": "pdf", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "document_id": { + "name": "document_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "embedding": { + "name": "embedding", + "type": "vector(1536)", + "primaryKey": false, + "notNull": true + }, + "page_number": { + "name": "page_number", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "char_start": { + "name": "char_start", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "char_end": { + "name": "char_end", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "section_title": { + "name": "section_title", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": { + "pdf_embeddingIndex": { + "name": "pdf_embeddingIndex", + "columns": [ + { + "expression": "embedding", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "vector_cosine_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "hnsw", + "with": {} + } + }, + "foreignKeys": { + "embedding_document_id_document_id_fk": { + "name": "embedding_document_id_document_id_fk", + "tableFrom": "embedding", + "tableTo": "document", + "schemaTo": "pdf", + "columnsFrom": [ + "document_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "pdf.message": { + "name": "message", + "schema": "pdf", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "chat_id": { + "name": "chat_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "role", + "typeSchema": "pdf", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "message_chat_id_chat_id_fk": { + "name": "message_chat_id_chat_id_fk", + "tableFrom": "message", + "tableTo": "chat", + "schemaTo": "pdf", + "columnsFrom": [ + "chat_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "pdf.retrieval_chunk": { + "name": "retrieval_chunk", + "schema": "pdf", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "document_id": { + "name": "document_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "embedding": { + "name": "embedding", + "type": "vector(1536)", + "primaryKey": false, + "notNull": false + }, + "page_start": { + "name": "page_start", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "page_end": { + "name": "page_end", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "section_hierarchy": { + "name": "section_hierarchy", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "chunk_type": { + "name": "chunk_type", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'prose'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": { + "idx_rc_document": { + "name": "idx_rc_document", + "columns": [ + { + "expression": "document_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_rc_embedding": { + "name": "idx_rc_embedding", + "columns": [ + { + "expression": "embedding", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "vector_cosine_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "hnsw", + "with": {} + } + }, + "foreignKeys": { + "retrieval_chunk_document_id_document_id_fk": { + "name": "retrieval_chunk_document_id_document_id_fk", + "tableFrom": "retrieval_chunk", + "tableTo": "document", + "schemaTo": "pdf", + "columnsFrom": [ + "document_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "image.generation": { + "name": "generation", + "schema": "image", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "prompt": { + "name": "prompt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "model": { + "name": "model", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "aspect_ratio": { + "name": "aspect_ratio", + "type": "aspect_ratio", + "typeSchema": "image", + "primaryKey": false, + "notNull": true, + "default": "'square'" + }, + "count": { + "name": "count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "generation_user_id_user_id_fk": { + "name": "generation_user_id_user_id_fk", + "tableFrom": "generation", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "image.image": { + "name": "image", + "schema": "image", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "generation_id": { + "name": "generation_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "url": { + "name": "url", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "image_generation_id_generation_id_fk": { + "name": "image_generation_id_generation_id_fk", + "tableFrom": "image", + "tableTo": "generation", + "schemaTo": "image", + "columnsFrom": [ + "generation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "mesh.audit_log": { + "name": "audit_log", + "schema": "mesh", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "mesh_id": { + "name": "mesh_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "event_type": { + "name": "event_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "actor_peer_id": { + "name": "actor_peer_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "target_peer_id": { + "name": "target_peer_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "audit_log_mesh_id_mesh_id_fk": { + "name": "audit_log_mesh_id_mesh_id_fk", + "tableFrom": "audit_log", + "tableTo": "mesh", + "schemaTo": "mesh", + "columnsFrom": [ + "mesh_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "mesh.invite": { + "name": "invite", + "schema": "mesh", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "mesh_id": { + "name": "mesh_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "token_bytes": { + "name": "token_bytes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "max_uses": { + "name": "max_uses", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "used_count": { + "name": "used_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "role": { + "name": "role", + "type": "role", + "typeSchema": "mesh", + "primaryKey": false, + "notNull": true, + "default": "'member'" + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "created_by": { + "name": "created_by", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "revoked_at": { + "name": "revoked_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "invite_mesh_id_mesh_id_fk": { + "name": "invite_mesh_id_mesh_id_fk", + "tableFrom": "invite", + "tableTo": "mesh", + "schemaTo": "mesh", + "columnsFrom": [ + "mesh_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "invite_created_by_user_id_fk": { + "name": "invite_created_by_user_id_fk", + "tableFrom": "invite", + "tableTo": "user", + "columnsFrom": [ + "created_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "invite_token_unique": { + "name": "invite_token_unique", + "nullsNotDistinct": false, + "columns": [ + "token" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "mesh.mesh": { + "name": "mesh", + "schema": "mesh", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "owner_user_id": { + "name": "owner_user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "visibility": { + "name": "visibility", + "type": "visibility", + "typeSchema": "mesh", + "primaryKey": false, + "notNull": true, + "default": "'private'" + }, + "transport": { + "name": "transport", + "type": "transport", + "typeSchema": "mesh", + "primaryKey": false, + "notNull": true, + "default": "'managed'" + }, + "max_peers": { + "name": "max_peers", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "tier": { + "name": "tier", + "type": "tier", + "typeSchema": "mesh", + "primaryKey": false, + "notNull": true, + "default": "'free'" + }, + "owner_pubkey": { + "name": "owner_pubkey", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "owner_secret_key": { + "name": "owner_secret_key", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "root_key": { + "name": "root_key", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "archived_at": { + "name": "archived_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "mesh_owner_user_id_user_id_fk": { + "name": "mesh_owner_user_id_user_id_fk", + "tableFrom": "mesh", + "tableTo": "user", + "columnsFrom": [ + "owner_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "mesh_slug_unique": { + "name": "mesh_slug_unique", + "nullsNotDistinct": false, + "columns": [ + "slug" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "mesh.context": { + "name": "context", + "schema": "mesh", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "mesh_id": { + "name": "mesh_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "presence_id": { + "name": "presence_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "peer_name": { + "name": "peer_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "summary": { + "name": "summary", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "files_read": { + "name": "files_read", + "type": "text[]", + "primaryKey": false, + "notNull": false, + "default": "'{}'" + }, + "key_findings": { + "name": "key_findings", + "type": "text[]", + "primaryKey": false, + "notNull": false, + "default": "'{}'" + }, + "tags": { + "name": "tags", + "type": "text[]", + "primaryKey": false, + "notNull": false, + "default": "'{}'" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "context_mesh_id_mesh_id_fk": { + "name": "context_mesh_id_mesh_id_fk", + "tableFrom": "context", + "tableTo": "mesh", + "schemaTo": "mesh", + "columnsFrom": [ + "mesh_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "context_presence_id_presence_id_fk": { + "name": "context_presence_id_presence_id_fk", + "tableFrom": "context", + "tableTo": "presence", + "schemaTo": "mesh", + "columnsFrom": [ + "presence_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "mesh.file": { + "name": "file", + "schema": "mesh", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "mesh_id": { + "name": "mesh_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "size_bytes": { + "name": "size_bytes", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "mime_type": { + "name": "mime_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "minio_key": { + "name": "minio_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "tags": { + "name": "tags", + "type": "text[]", + "primaryKey": false, + "notNull": false, + "default": "'{}'" + }, + "persistent": { + "name": "persistent", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "uploaded_by_name": { + "name": "uploaded_by_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "uploaded_by_member": { + "name": "uploaded_by_member", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "target_spec": { + "name": "target_spec", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "uploaded_at": { + "name": "uploaded_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "file_mesh_id_mesh_id_fk": { + "name": "file_mesh_id_mesh_id_fk", + "tableFrom": "file", + "tableTo": "mesh", + "schemaTo": "mesh", + "columnsFrom": [ + "mesh_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "file_uploaded_by_member_member_id_fk": { + "name": "file_uploaded_by_member_member_id_fk", + "tableFrom": "file", + "tableTo": "member", + "schemaTo": "mesh", + "columnsFrom": [ + "uploaded_by_member" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "mesh.file_access": { + "name": "file_access", + "schema": "mesh", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "file_id": { + "name": "file_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "peer_session_pubkey": { + "name": "peer_session_pubkey", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "peer_name": { + "name": "peer_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "accessed_at": { + "name": "accessed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "file_access_file_id_file_id_fk": { + "name": "file_access_file_id_file_id_fk", + "tableFrom": "file_access", + "tableTo": "file", + "schemaTo": "mesh", + "columnsFrom": [ + "file_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "mesh.member": { + "name": "member", + "schema": "mesh", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "mesh_id": { + "name": "mesh_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "peer_pubkey": { + "name": "peer_pubkey", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "display_name": { + "name": "display_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "role", + "typeSchema": "mesh", + "primaryKey": false, + "notNull": true, + "default": "'member'" + }, + "joined_at": { + "name": "joined_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "last_seen_at": { + "name": "last_seen_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "revoked_at": { + "name": "revoked_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "member_mesh_id_mesh_id_fk": { + "name": "member_mesh_id_mesh_id_fk", + "tableFrom": "member", + "tableTo": "mesh", + "schemaTo": "mesh", + "columnsFrom": [ + "mesh_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "member_user_id_user_id_fk": { + "name": "member_user_id_user_id_fk", + "tableFrom": "member", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "mesh.memory": { + "name": "memory", + "schema": "mesh", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "mesh_id": { + "name": "mesh_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "tags": { + "name": "tags", + "type": "text[]", + "primaryKey": false, + "notNull": false, + "default": "'{}'" + }, + "remembered_by": { + "name": "remembered_by", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "remembered_by_name": { + "name": "remembered_by_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "remembered_at": { + "name": "remembered_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "forgotten_at": { + "name": "forgotten_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "memory_mesh_id_mesh_id_fk": { + "name": "memory_mesh_id_mesh_id_fk", + "tableFrom": "memory", + "tableTo": "mesh", + "schemaTo": "mesh", + "columnsFrom": [ + "mesh_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "memory_remembered_by_member_id_fk": { + "name": "memory_remembered_by_member_id_fk", + "tableFrom": "memory", + "tableTo": "member", + "schemaTo": "mesh", + "columnsFrom": [ + "remembered_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "mesh.state": { + "name": "state", + "schema": "mesh", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "mesh_id": { + "name": "mesh_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "key": { + "name": "key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "value": { + "name": "value", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "updated_by_presence": { + "name": "updated_by_presence", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "updated_by_name": { + "name": "updated_by_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "state_mesh_key_idx": { + "name": "state_mesh_key_idx", + "columns": [ + { + "expression": "mesh_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "state_mesh_id_mesh_id_fk": { + "name": "state_mesh_id_mesh_id_fk", + "tableFrom": "state", + "tableTo": "mesh", + "schemaTo": "mesh", + "columnsFrom": [ + "mesh_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "mesh.stream": { + "name": "stream", + "schema": "mesh", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "mesh_id": { + "name": "mesh_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_by_name": { + "name": "created_by_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "stream_mesh_name_idx": { + "name": "stream_mesh_name_idx", + "columns": [ + { + "expression": "mesh_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "stream_mesh_id_mesh_id_fk": { + "name": "stream_mesh_id_mesh_id_fk", + "tableFrom": "stream", + "tableTo": "mesh", + "schemaTo": "mesh", + "columnsFrom": [ + "mesh_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "mesh.task": { + "name": "task", + "schema": "mesh", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "mesh_id": { + "name": "mesh_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "assignee": { + "name": "assignee", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "claimed_by_name": { + "name": "claimed_by_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "claimed_by_presence": { + "name": "claimed_by_presence", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "priority": { + "name": "priority", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'normal'" + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'open'" + }, + "tags": { + "name": "tags", + "type": "text[]", + "primaryKey": false, + "notNull": false, + "default": "'{}'" + }, + "result": { + "name": "result", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_by_name": { + "name": "created_by_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "claimed_at": { + "name": "claimed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "task_mesh_id_mesh_id_fk": { + "name": "task_mesh_id_mesh_id_fk", + "tableFrom": "task", + "tableTo": "mesh", + "schemaTo": "mesh", + "columnsFrom": [ + "mesh_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "task_claimed_by_presence_presence_id_fk": { + "name": "task_claimed_by_presence_presence_id_fk", + "tableFrom": "task", + "tableTo": "presence", + "schemaTo": "mesh", + "columnsFrom": [ + "claimed_by_presence" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "mesh.message_queue": { + "name": "message_queue", + "schema": "mesh", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "mesh_id": { + "name": "mesh_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "sender_member_id": { + "name": "sender_member_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "sender_session_pubkey": { + "name": "sender_session_pubkey", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "target_spec": { + "name": "target_spec", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "priority": { + "name": "priority", + "type": "message_priority", + "typeSchema": "mesh", + "primaryKey": false, + "notNull": true, + "default": "'next'" + }, + "nonce": { + "name": "nonce", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "ciphertext": { + "name": "ciphertext", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "delivered_at": { + "name": "delivered_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "message_queue_mesh_id_mesh_id_fk": { + "name": "message_queue_mesh_id_mesh_id_fk", + "tableFrom": "message_queue", + "tableTo": "mesh", + "schemaTo": "mesh", + "columnsFrom": [ + "mesh_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "message_queue_sender_member_id_member_id_fk": { + "name": "message_queue_sender_member_id_member_id_fk", + "tableFrom": "message_queue", + "tableTo": "member", + "schemaTo": "mesh", + "columnsFrom": [ + "sender_member_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "mesh.pending_status": { + "name": "pending_status", + "schema": "mesh", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "pid": { + "name": "pid", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "cwd": { + "name": "cwd", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status_source": { + "name": "status_source", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "applied_at": { + "name": "applied_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "mesh.presence": { + "name": "presence", + "schema": "mesh", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "member_id": { + "name": "member_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "session_id": { + "name": "session_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "session_pubkey": { + "name": "session_pubkey", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "display_name": { + "name": "display_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "pid": { + "name": "pid", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "cwd": { + "name": "cwd", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "presence_status", + "typeSchema": "mesh", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "status_source": { + "name": "status_source", + "type": "presence_status_source", + "typeSchema": "mesh", + "primaryKey": false, + "notNull": true, + "default": "'jsonl'" + }, + "status_updated_at": { + "name": "status_updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "summary": { + "name": "summary", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "groups": { + "name": "groups", + "type": "jsonb", + "primaryKey": false, + "notNull": false, + "default": "'[]'::jsonb" + }, + "connected_at": { + "name": "connected_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "last_ping_at": { + "name": "last_ping_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "disconnected_at": { + "name": "disconnected_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "presence_member_id_member_id_fk": { + "name": "presence_member_id_member_id_fk", + "tableFrom": "presence", + "tableTo": "member", + "schemaTo": "mesh", + "columnsFrom": [ + "member_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": { + "public.credit_transaction_type": { + "name": "credit_transaction_type", + "schema": "public", + "values": [ + "signup", + "purchase", + "usage", + "admin_grant", + "admin_deduct", + "refund", + "promo", + "referral", + "expiry" + ] + }, + "public.status": { + "name": "status", + "schema": "public", + "values": [ + "active", + "canceled", + "incomplete", + "incomplete_expired", + "past_due", + "paused", + "trialing", + "unpaid" + ] + }, + "public.plan": { + "name": "plan", + "schema": "public", + "values": [ + "free", + "premium", + "enterprise" + ] + }, + "chat.role": { + "name": "role", + "schema": "chat", + "values": [ + "system", + "assistant", + "user" + ] + }, + "pdf.role": { + "name": "role", + "schema": "pdf", + "values": [ + "user", + "assistant", + "system" + ] + }, + "pdf.processing_status": { + "name": "processing_status", + "schema": "pdf", + "values": [ + "pending", + "processing", + "ready", + "failed" + ] + }, + "pdf.unit_type": { + "name": "unit_type", + "schema": "pdf", + "values": [ + "prose", + "heading", + "list", + "table", + "code" + ] + }, + "image.aspect_ratio": { + "name": "aspect_ratio", + "schema": "image", + "values": [ + "square", + "standard", + "landscape", + "portrait" + ] + }, + "mesh.role": { + "name": "role", + "schema": "mesh", + "values": [ + "admin", + "member" + ] + }, + "mesh.tier": { + "name": "tier", + "schema": "mesh", + "values": [ + "free", + "pro", + "team", + "enterprise" + ] + }, + "mesh.transport": { + "name": "transport", + "schema": "mesh", + "values": [ + "managed", + "tailscale", + "self_hosted" + ] + }, + "mesh.visibility": { + "name": "visibility", + "schema": "mesh", + "values": [ + "private", + "public" + ] + }, + "mesh.message_priority": { + "name": "message_priority", + "schema": "mesh", + "values": [ + "now", + "next", + "low" + ] + }, + "mesh.presence_status": { + "name": "presence_status", + "schema": "mesh", + "values": [ + "idle", + "working", + "dnd" + ] + }, + "mesh.presence_status_source": { + "name": "presence_status_source", + "schema": "mesh", + "values": [ + "hook", + "manual", + "jsonl" + ] + } + }, + "schemas": { + "chat": "chat", + "pdf": "pdf", + "image": "image", + "mesh": "mesh" + }, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/packages/db/migrations/meta/_journal.json b/packages/db/migrations/meta/_journal.json index a8fa4f1..c375790 100644 --- a/packages/db/migrations/meta/_journal.json +++ b/packages/db/migrations/meta/_journal.json @@ -71,6 +71,20 @@ "when": 1775480008546, "tag": "0009_add-file-tables", "breakpoints": true + }, + { + "idx": 10, + "version": "7", + "when": 1775480729014, + "tag": "0010_add-context-and-tasks", + "breakpoints": true + }, + { + "idx": 11, + "version": "7", + "when": 1775481222701, + "tag": "0011_add-streams", + "breakpoints": true } ] } \ No newline at end of file diff --git a/packages/db/src/schema/mesh.ts b/packages/db/src/schema/mesh.ts index 820c360..33380e2 100644 --- a/packages/db/src/schema/mesh.ts +++ b/packages/db/src/schema/mesh.ts @@ -327,6 +327,68 @@ export const meshFileAccess = meshSchema.table("file_access", { accessedAt: timestamp().defaultNow().notNull(), }); +/** + * Per-peer context snapshot. Each peer (presence) has at most one context + * entry per mesh, upserted on each share_context call. Allows peers to + * discover what others are working on, which files they've read, and + * key findings — without sending a direct message. + */ +export const meshContext = meshSchema.table("context", { + id: text().primaryKey().notNull().$defaultFn(generateId), + meshId: text() + .references(() => mesh.id, { onDelete: "cascade", onUpdate: "cascade" }) + .notNull(), + presenceId: text().references(() => presence.id, { onDelete: "cascade" }), + peerName: text(), + summary: text().notNull(), + filesRead: text().array().default([]), + keyFindings: text().array().default([]), + tags: text().array().default([]), + updatedAt: timestamp().defaultNow().notNull(), +}); + +/** + * Mesh-scoped task board. Peers can create tasks, claim them, and mark + * them done. Lightweight project management for multi-agent workflows. + */ +export const meshTask = meshSchema.table("task", { + id: text().primaryKey().notNull().$defaultFn(generateId), + meshId: text() + .references(() => mesh.id, { onDelete: "cascade", onUpdate: "cascade" }) + .notNull(), + title: text().notNull(), + assignee: text(), + claimedByName: text(), + claimedByPresence: text().references(() => presence.id), + priority: text().notNull().default("normal"), + status: text().notNull().default("open"), + tags: text().array().default([]), + result: text(), + createdByName: text(), + createdAt: timestamp().defaultNow().notNull(), + claimedAt: timestamp(), + completedAt: timestamp(), +}); + +/** + * Named real-time data channels within a mesh. One peer publishes, all + * subscribers receive. No message history — streams are live. + * Use cases: build logs, deploy status, monitoring data, live code diffs. + */ +export const meshStream = meshSchema.table( + "stream", + { + id: text().primaryKey().notNull().$defaultFn(generateId), + meshId: text() + .references(() => mesh.id, { onDelete: "cascade", onUpdate: "cascade" }) + .notNull(), + name: text().notNull(), + createdByName: text(), + createdAt: timestamp().defaultNow().notNull(), + }, + (table) => [uniqueIndex("stream_mesh_name_idx").on(table.meshId, table.name)], +); + export const meshRelations = relations(mesh, ({ one, many }) => ({ owner: one(user, { fields: [mesh.ownerUserId], @@ -469,3 +531,45 @@ export type SelectMeshFile = typeof meshFile.$inferSelect; export type InsertMeshFile = typeof meshFile.$inferInsert; export type SelectMeshFileAccess = typeof meshFileAccess.$inferSelect; export type InsertMeshFileAccess = typeof meshFileAccess.$inferInsert; +export const selectMeshContextSchema = createSelectSchema(meshContext); +export const insertMeshContextSchema = createInsertSchema(meshContext); +export const selectMeshTaskSchema = createSelectSchema(meshTask); +export const insertMeshTaskSchema = createInsertSchema(meshTask); +export type SelectMeshContext = typeof meshContext.$inferSelect; +export type InsertMeshContext = typeof meshContext.$inferInsert; +export type SelectMeshTask = typeof meshTask.$inferSelect; +export type InsertMeshTask = typeof meshTask.$inferInsert; + +export const meshContextRelations = relations(meshContext, ({ one }) => ({ + mesh: one(mesh, { + fields: [meshContext.meshId], + references: [mesh.id], + }), + presence: one(presence, { + fields: [meshContext.presenceId], + references: [presence.id], + }), +})); + +export const meshTaskRelations = relations(meshTask, ({ one }) => ({ + mesh: one(mesh, { + fields: [meshTask.meshId], + references: [mesh.id], + }), + claimedPresence: one(presence, { + fields: [meshTask.claimedByPresence], + references: [presence.id], + }), +})); + +export const meshStreamRelations = relations(meshStream, ({ one }) => ({ + mesh: one(mesh, { + fields: [meshStream.meshId], + references: [mesh.id], + }), +})); + +export const selectMeshStreamSchema = createSelectSchema(meshStream); +export const insertMeshStreamSchema = createInsertSchema(meshStream); +export type SelectMeshStream = typeof meshStream.$inferSelect; +export type InsertMeshStream = typeof meshStream.$inferInsert;