From ede1e71f8271cce2428a350dbfe73481f98b2dd8 Mon Sep 17 00:00:00 2001 From: Tanner Linsley Date: Fri, 2 Oct 2026 12:13:08 -0600 Subject: [PATCH 1/4] Improve TanChat access page and add an account waitlist --- drizzle/migrations/0040_tanchat_waitlist.sql | 4 + drizzle/migrations/meta/0040_snapshot.json | 10770 ++++++++++++++++ drizzle/migrations/meta/_journal.json | 7 + .../pending-runtime/chat-waitlist.test.ts | 55 + src/chat/access.functions.ts | 12 + src/chat/access.server.ts | 21 +- src/components/Navbar.tsx | 10 +- src/db/schema.ts | 9 + src/routes/chat-access.tsx | 300 +- src/styles/app.css | 8 +- 10 files changed, 11105 insertions(+), 91 deletions(-) create mode 100644 drizzle/migrations/0040_tanchat_waitlist.sql create mode 100644 drizzle/migrations/meta/0040_snapshot.json create mode 100644 harness-tests/pending-runtime/chat-waitlist.test.ts diff --git a/drizzle/migrations/0040_tanchat_waitlist.sql b/drizzle/migrations/0040_tanchat_waitlist.sql new file mode 100644 index 000000000..0f396ada4 --- /dev/null +++ b/drizzle/migrations/0040_tanchat_waitlist.sql @@ -0,0 +1,4 @@ +CREATE TABLE "chat_waitlist" ( + "user_id" uuid PRIMARY KEY REFERENCES "users"("id") ON DELETE CASCADE, + "created_at" timestamptz NOT NULL DEFAULT now() +); diff --git a/drizzle/migrations/meta/0040_snapshot.json b/drizzle/migrations/meta/0040_snapshot.json new file mode 100644 index 000000000..cae134181 --- /dev/null +++ b/drizzle/migrations/meta/0040_snapshot.json @@ -0,0 +1,10770 @@ +{ + "id": "55cd51ce-b633-4fdc-b962-e8584ddc883f", + "prevId": "8afd535d-44a9-41c6-b88f-9db03563080f", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.audit_logs": { + "name": "audit_logs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "actor_id": { + "name": "actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "action": { + "name": "action", + "type": "audit_action", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "target_type": { + "name": "target_type", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true + }, + "target_id": { + "name": "target_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "details": { + "name": "details", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "ip_address": { + "name": "ip_address", + "type": "varchar(45)", + "primaryKey": false, + "notNull": false + }, + "user_agent": { + "name": "user_agent", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "audit_logs_actor_id_idx": { + "name": "audit_logs_actor_id_idx", + "columns": [ + { + "expression": "actor_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "audit_logs_action_idx": { + "name": "audit_logs_action_idx", + "columns": [ + { + "expression": "action", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "audit_logs_target_type_idx": { + "name": "audit_logs_target_type_idx", + "columns": [ + { + "expression": "target_type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "audit_logs_target_id_idx": { + "name": "audit_logs_target_id_idx", + "columns": [ + { + "expression": "target_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "audit_logs_created_at_idx": { + "name": "audit_logs_created_at_idx", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "audit_logs_actor_id_users_id_fk": { + "name": "audit_logs_actor_id_users_id_fk", + "tableFrom": "audit_logs", + "tableTo": "users", + "columnsFrom": ["actor_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.builder_project_events": { + "name": "builder_project_events", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "project_id": { + "name": "project_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "owner_id": { + "name": "owner_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "thread_id": { + "name": "thread_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "revision_id": { + "name": "revision_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "message_id": { + "name": "message_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "run_id": { + "name": "run_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "sequence": { + "name": "sequence", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "client_event_id": { + "name": "client_event_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "client_mutation_id": { + "name": "client_mutation_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "browser_session_id": { + "name": "browser_session_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "type": { + "name": "type", + "type": "builder_project_event_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "payload": { + "name": "payload", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "occurred_at": { + "name": "occurred_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "builder_project_events_project_created_at_idx": { + "name": "builder_project_events_project_created_at_idx", + "columns": [ + { + "expression": "project_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "builder_project_events_run_sequence_idx": { + "name": "builder_project_events_run_sequence_idx", + "columns": [ + { + "expression": "run_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "sequence", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "builder_project_events_client_mutation_idx": { + "name": "builder_project_events_client_mutation_idx", + "columns": [ + { + "expression": "project_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "client_mutation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "builder_project_events_project_owner_fk": { + "name": "builder_project_events_project_owner_fk", + "tableFrom": "builder_project_events", + "tableTo": "builder_projects", + "columnsFrom": ["project_id", "owner_id"], + "columnsTo": ["id", "owner_id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "builder_project_events_thread_fk": { + "name": "builder_project_events_thread_fk", + "tableFrom": "builder_project_events", + "tableTo": "builder_project_threads", + "columnsFrom": ["project_id", "thread_id"], + "columnsTo": ["project_id", "id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "builder_project_events_revision_fk": { + "name": "builder_project_events_revision_fk", + "tableFrom": "builder_project_events", + "tableTo": "builder_project_revisions", + "columnsFrom": ["project_id", "revision_id"], + "columnsTo": ["project_id", "id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "builder_project_events_message_fk": { + "name": "builder_project_events_message_fk", + "tableFrom": "builder_project_events", + "tableTo": "builder_project_messages", + "columnsFrom": ["project_id", "message_id"], + "columnsTo": ["project_id", "id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "builder_project_events_run_fk": { + "name": "builder_project_events_run_fk", + "tableFrom": "builder_project_events", + "tableTo": "builder_project_runs", + "columnsFrom": ["project_id", "run_id"], + "columnsTo": ["project_id", "id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "builder_project_events_project_sequence_unique": { + "name": "builder_project_events_project_sequence_unique", + "nullsNotDistinct": false, + "columns": ["project_id", "sequence"] + }, + "builder_project_events_project_client_event_unique": { + "name": "builder_project_events_project_client_event_unique", + "nullsNotDistinct": false, + "columns": ["project_id", "client_event_id"] + } + }, + "policies": {}, + "checkConstraints": { + "builder_project_events_sequence_check": { + "name": "builder_project_events_sequence_check", + "value": "\"builder_project_events\".\"sequence\" BETWEEN 1 AND 9007199254740991" + }, + "builder_project_events_payload_check": { + "name": "builder_project_events_payload_check", + "value": "jsonb_typeof(\"builder_project_events\".\"payload\") = 'object' AND pg_column_size(\"builder_project_events\".\"payload\") <= 262144" + }, + "builder_project_events_entity_reference_check": { + "name": "builder_project_events_entity_reference_check", + "value": "((\"builder_project_events\".\"type\"::text NOT LIKE 'revision.%') OR \"builder_project_events\".\"revision_id\" IS NOT NULL)\n AND ((\"builder_project_events\".\"type\"::text NOT LIKE 'thread.%') OR \"builder_project_events\".\"thread_id\" IS NOT NULL)\n AND ((\"builder_project_events\".\"type\"::text NOT LIKE 'message.%') OR \"builder_project_events\".\"message_id\" IS NOT NULL)\n AND ((\"builder_project_events\".\"type\"::text NOT LIKE 'run.%') OR \"builder_project_events\".\"run_id\" IS NOT NULL)" + } + }, + "isRLSEnabled": false + }, + "public.builder_project_legacy_imports": { + "name": "builder_project_legacy_imports", + "schema": "", + "columns": { + "owner_id": { + "name": "owner_id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "builder_project_legacy_imports_owner_id_users_id_fk": { + "name": "builder_project_legacy_imports_owner_id_users_id_fk", + "tableFrom": "builder_project_legacy_imports", + "tableTo": "users", + "columnsFrom": ["owner_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.builder_project_messages": { + "name": "builder_project_messages", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "project_id": { + "name": "project_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "owner_id": { + "name": "owner_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "thread_id": { + "name": "thread_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "run_id": { + "name": "run_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "client_mutation_id": { + "name": "client_mutation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "position": { + "name": "position", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "builder_message_role", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "parts": { + "name": "parts", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "builder_project_messages_project_thread_created_at_idx": { + "name": "builder_project_messages_project_thread_created_at_idx", + "columns": [ + { + "expression": "project_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "thread_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "builder_project_messages_run_id_idx": { + "name": "builder_project_messages_run_id_idx", + "columns": [ + { + "expression": "run_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "builder_project_messages_project_owner_fk": { + "name": "builder_project_messages_project_owner_fk", + "tableFrom": "builder_project_messages", + "tableTo": "builder_projects", + "columnsFrom": ["project_id", "owner_id"], + "columnsTo": ["id", "owner_id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "builder_project_messages_thread_fk": { + "name": "builder_project_messages_thread_fk", + "tableFrom": "builder_project_messages", + "tableTo": "builder_project_threads", + "columnsFrom": ["project_id", "thread_id"], + "columnsTo": ["project_id", "id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "builder_project_messages_run_fk": { + "name": "builder_project_messages_run_fk", + "tableFrom": "builder_project_messages", + "tableTo": "builder_project_runs", + "columnsFrom": ["project_id", "thread_id", "run_id"], + "columnsTo": ["project_id", "thread_id", "id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "builder_project_messages_project_id_unique": { + "name": "builder_project_messages_project_id_unique", + "nullsNotDistinct": false, + "columns": ["project_id", "id"] + }, + "builder_project_messages_project_mutation_unique": { + "name": "builder_project_messages_project_mutation_unique", + "nullsNotDistinct": false, + "columns": ["project_id", "client_mutation_id"] + }, + "builder_project_messages_thread_position_unique": { + "name": "builder_project_messages_thread_position_unique", + "nullsNotDistinct": false, + "columns": ["project_id", "thread_id", "position"] + } + }, + "policies": {}, + "checkConstraints": { + "builder_project_messages_parts_check": { + "name": "builder_project_messages_parts_check", + "value": "jsonb_typeof(\"builder_project_messages\".\"parts\") = 'array' AND pg_column_size(\"builder_project_messages\".\"parts\") <= 1048576" + }, + "builder_project_messages_content_size_check": { + "name": "builder_project_messages_content_size_check", + "value": "octet_length(\"builder_project_messages\".\"content\") <= 1048576" + }, + "builder_project_messages_position_check": { + "name": "builder_project_messages_position_check", + "value": "\"builder_project_messages\".\"position\" BETWEEN 1 AND 9007199254740991" + } + }, + "isRLSEnabled": false + }, + "public.builder_project_mutation_receipts": { + "name": "builder_project_mutation_receipts", + "schema": "", + "columns": { + "project_id": { + "name": "project_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "client_mutation_id": { + "name": "client_mutation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "command_type": { + "name": "command_type", + "type": "varchar(40)", + "primaryKey": false, + "notNull": true + }, + "request_hash": { + "name": "request_hash", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "builder_project_mutation_receipts_project_fk": { + "name": "builder_project_mutation_receipts_project_fk", + "tableFrom": "builder_project_mutation_receipts", + "tableTo": "builder_projects", + "columnsFrom": ["project_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "builder_project_mutation_receipts_pk": { + "name": "builder_project_mutation_receipts_pk", + "columns": ["project_id", "client_mutation_id"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "builder_project_mutation_receipts_command_type_check": { + "name": "builder_project_mutation_receipts_command_type_check", + "value": "\"builder_project_mutation_receipts\".\"command_type\" IN ('project.create', 'project.revise', 'project.delete', 'thread.create', 'run.enqueue', 'run.claim', 'run.cancel', 'run.finish', 'run.finish.fallback', 'transcript.import')" + }, + "builder_project_mutation_receipts_request_hash_check": { + "name": "builder_project_mutation_receipts_request_hash_check", + "value": "\"builder_project_mutation_receipts\".\"request_hash\" ~ '^[a-f0-9]{64}$'" + } + }, + "isRLSEnabled": false + }, + "public.builder_project_revisions": { + "name": "builder_project_revisions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "project_id": { + "name": "project_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "owner_id": { + "name": "owner_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "client_mutation_id": { + "name": "client_mutation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "parent_revision_id": { + "name": "parent_revision_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "revision_number": { + "name": "revision_number", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "snapshot_hash": { + "name": "snapshot_hash", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "builder_project_revisions_project_created_at_idx": { + "name": "builder_project_revisions_project_created_at_idx", + "columns": [ + { + "expression": "project_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "builder_project_revisions_snapshot_hash_idx": { + "name": "builder_project_revisions_snapshot_hash_idx", + "columns": [ + { + "expression": "snapshot_hash", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "builder_project_revisions_project_owner_fk": { + "name": "builder_project_revisions_project_owner_fk", + "tableFrom": "builder_project_revisions", + "tableTo": "builder_projects", + "columnsFrom": ["project_id", "owner_id"], + "columnsTo": ["id", "owner_id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "builder_project_revisions_parent_fk": { + "name": "builder_project_revisions_parent_fk", + "tableFrom": "builder_project_revisions", + "tableTo": "builder_project_revisions", + "columnsFrom": ["project_id", "parent_revision_id"], + "columnsTo": ["project_id", "id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "builder_project_revisions_snapshot_hash_fk": { + "name": "builder_project_revisions_snapshot_hash_fk", + "tableFrom": "builder_project_revisions", + "tableTo": "builder_project_snapshots", + "columnsFrom": ["snapshot_hash"], + "columnsTo": ["hash"], + "onDelete": "restrict", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "builder_project_revisions_project_revision_unique": { + "name": "builder_project_revisions_project_revision_unique", + "nullsNotDistinct": false, + "columns": ["project_id", "revision_number"] + }, + "builder_project_revisions_project_id_unique": { + "name": "builder_project_revisions_project_id_unique", + "nullsNotDistinct": false, + "columns": ["project_id", "id"] + }, + "builder_project_revisions_project_mutation_unique": { + "name": "builder_project_revisions_project_mutation_unique", + "nullsNotDistinct": false, + "columns": ["project_id", "client_mutation_id"] + } + }, + "policies": {}, + "checkConstraints": { + "builder_project_revisions_snapshot_hash_check": { + "name": "builder_project_revisions_snapshot_hash_check", + "value": "\"builder_project_revisions\".\"snapshot_hash\" ~ '^[a-f0-9]{64}$'" + }, + "builder_project_revisions_revision_number_check": { + "name": "builder_project_revisions_revision_number_check", + "value": "\"builder_project_revisions\".\"revision_number\" > 0" + } + }, + "isRLSEnabled": false + }, + "public.builder_project_runs": { + "name": "builder_project_runs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "project_id": { + "name": "project_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "owner_id": { + "name": "owner_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "thread_id": { + "name": "thread_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "client_mutation_id": { + "name": "client_mutation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "builder_run_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "queue_kind": { + "name": "queue_kind", + "type": "varchar(10)", + "primaryKey": false, + "notNull": true, + "default": "'queue'" + }, + "provider": { + "name": "provider", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true + }, + "model": { + "name": "model", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true + }, + "base_revision_id": { + "name": "base_revision_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "result_revision_id": { + "name": "result_revision_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "lease_owner_id": { + "name": "lease_owner_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "lease_fencing_token": { + "name": "lease_fencing_token", + "type": "bigint", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "lease_expires_at": { + "name": "lease_expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "last_heartbeat_at": { + "name": "last_heartbeat_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "error": { + "name": "error", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "activity": { + "name": "activity", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "started_at": { + "name": "started_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "builder_project_runs_project_thread_created_at_idx": { + "name": "builder_project_runs_project_thread_created_at_idx", + "columns": [ + { + "expression": "project_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "thread_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "builder_project_runs_active_lease_idx": { + "name": "builder_project_runs_active_lease_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "lease_expires_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "builder_project_runs_active_project_unique": { + "name": "builder_project_runs_active_project_unique", + "columns": [ + { + "expression": "project_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"builder_project_runs\".\"status\" = 'running'", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "builder_project_runs_project_owner_fk": { + "name": "builder_project_runs_project_owner_fk", + "tableFrom": "builder_project_runs", + "tableTo": "builder_projects", + "columnsFrom": ["project_id", "owner_id"], + "columnsTo": ["id", "owner_id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "builder_project_runs_thread_fk": { + "name": "builder_project_runs_thread_fk", + "tableFrom": "builder_project_runs", + "tableTo": "builder_project_threads", + "columnsFrom": ["project_id", "thread_id"], + "columnsTo": ["project_id", "id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "builder_project_runs_base_revision_fk": { + "name": "builder_project_runs_base_revision_fk", + "tableFrom": "builder_project_runs", + "tableTo": "builder_project_revisions", + "columnsFrom": ["project_id", "base_revision_id"], + "columnsTo": ["project_id", "id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "builder_project_runs_result_revision_fk": { + "name": "builder_project_runs_result_revision_fk", + "tableFrom": "builder_project_runs", + "tableTo": "builder_project_revisions", + "columnsFrom": ["project_id", "result_revision_id"], + "columnsTo": ["project_id", "id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "builder_project_runs_project_id_unique": { + "name": "builder_project_runs_project_id_unique", + "nullsNotDistinct": false, + "columns": ["project_id", "id"] + }, + "builder_project_runs_project_thread_id_unique": { + "name": "builder_project_runs_project_thread_id_unique", + "nullsNotDistinct": false, + "columns": ["project_id", "thread_id", "id"] + }, + "builder_project_runs_project_mutation_unique": { + "name": "builder_project_runs_project_mutation_unique", + "nullsNotDistinct": false, + "columns": ["project_id", "client_mutation_id"] + } + }, + "policies": {}, + "checkConstraints": { + "builder_project_runs_queue_kind_check": { + "name": "builder_project_runs_queue_kind_check", + "value": "\"builder_project_runs\".\"queue_kind\" IN ('queue', 'steer')" + }, + "builder_project_runs_fencing_token_check": { + "name": "builder_project_runs_fencing_token_check", + "value": "\"builder_project_runs\".\"lease_fencing_token\" BETWEEN 0 AND 9007199254740991" + }, + "builder_project_runs_lease_pair_check": { + "name": "builder_project_runs_lease_pair_check", + "value": "(\"builder_project_runs\".\"lease_owner_id\" IS NULL) = (\"builder_project_runs\".\"lease_expires_at\" IS NULL)" + }, + "builder_project_runs_running_lease_check": { + "name": "builder_project_runs_running_lease_check", + "value": "(\"builder_project_runs\".\"status\" = 'running') = (\"builder_project_runs\".\"lease_owner_id\" IS NOT NULL)" + }, + "builder_project_runs_terminal_state_check": { + "name": "builder_project_runs_terminal_state_check", + "value": "(\"builder_project_runs\".\"status\" IN ('interrupted', 'completed', 'failed', 'cancelled')) = (\"builder_project_runs\".\"completed_at\" IS NOT NULL)" + }, + "builder_project_runs_timestamp_order_check": { + "name": "builder_project_runs_timestamp_order_check", + "value": "\"builder_project_runs\".\"completed_at\" IS NULL OR \"builder_project_runs\".\"started_at\" IS NULL OR \"builder_project_runs\".\"completed_at\" >= \"builder_project_runs\".\"started_at\"" + }, + "builder_project_runs_activity_size_check": { + "name": "builder_project_runs_activity_size_check", + "value": "\"builder_project_runs\".\"activity\" IS NULL OR (jsonb_typeof(\"builder_project_runs\".\"activity\") = 'object' AND pg_column_size(\"builder_project_runs\".\"activity\") <= 262144)" + }, + "builder_project_runs_error_size_check": { + "name": "builder_project_runs_error_size_check", + "value": "\"builder_project_runs\".\"error\" IS NULL OR (jsonb_typeof(\"builder_project_runs\".\"error\") = 'object' AND pg_column_size(\"builder_project_runs\".\"error\") <= 262144)" + }, + "builder_project_runs_result_revision_check": { + "name": "builder_project_runs_result_revision_check", + "value": "\"builder_project_runs\".\"result_revision_id\" IS NULL OR \"builder_project_runs\".\"status\" = 'completed'" + } + }, + "isRLSEnabled": false + }, + "public.builder_project_snapshot_reservations": { + "name": "builder_project_snapshot_reservations", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "owner_id": { + "name": "owner_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "snapshot_hash": { + "name": "snapshot_hash", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "builder_project_snapshot_reservations_owner_created_at_idx": { + "name": "builder_project_snapshot_reservations_owner_created_at_idx", + "columns": [ + { + "expression": "owner_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "builder_project_snapshot_reservations_snapshot_hash_idx": { + "name": "builder_project_snapshot_reservations_snapshot_hash_idx", + "columns": [ + { + "expression": "snapshot_hash", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "builder_project_snapshot_reservations_owner_id_users_id_fk": { + "name": "builder_project_snapshot_reservations_owner_id_users_id_fk", + "tableFrom": "builder_project_snapshot_reservations", + "tableTo": "users", + "columnsFrom": ["owner_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "builder_project_snapshot_reservations_snapshot_hash_builder_project_snapshots_hash_fk": { + "name": "builder_project_snapshot_reservations_snapshot_hash_builder_project_snapshots_hash_fk", + "tableFrom": "builder_project_snapshot_reservations", + "tableTo": "builder_project_snapshots", + "columnsFrom": ["snapshot_hash"], + "columnsTo": ["hash"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "builder_project_snapshot_reservations_owner_snapshot_unique": { + "name": "builder_project_snapshot_reservations_owner_snapshot_unique", + "nullsNotDistinct": false, + "columns": ["owner_id", "snapshot_hash"] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.builder_project_snapshots": { + "name": "builder_project_snapshots", + "schema": "", + "columns": { + "hash": { + "name": "hash", + "type": "varchar(64)", + "primaryKey": true, + "notNull": true + }, + "source_bytes": { + "name": "source_bytes", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "stored_at": { + "name": "stored_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "deleting_at": { + "name": "deleting_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "quarantined_at": { + "name": "quarantined_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "builder_project_snapshots_stored_at_idx": { + "name": "builder_project_snapshots_stored_at_idx", + "columns": [ + { + "expression": "stored_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "builder_project_snapshots_hash_check": { + "name": "builder_project_snapshots_hash_check", + "value": "\"builder_project_snapshots\".\"hash\" ~ '^[a-f0-9]{64}$'" + }, + "builder_project_snapshots_source_bytes_check": { + "name": "builder_project_snapshots_source_bytes_check", + "value": "\"builder_project_snapshots\".\"source_bytes\" IS NULL OR \"builder_project_snapshots\".\"source_bytes\" BETWEEN 1 AND 1048576" + } + }, + "isRLSEnabled": false + }, + "public.builder_project_threads": { + "name": "builder_project_threads", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "project_id": { + "name": "project_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "owner_id": { + "name": "owner_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "client_mutation_id": { + "name": "client_mutation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "varchar(160)", + "primaryKey": false, + "notNull": true + }, + "last_message_position": { + "name": "last_message_position", + "type": "bigint", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "archived_at": { + "name": "archived_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "builder_project_threads_project_updated_at_idx": { + "name": "builder_project_threads_project_updated_at_idx", + "columns": [ + { + "expression": "project_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "updated_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "builder_project_threads_project_owner_fk": { + "name": "builder_project_threads_project_owner_fk", + "tableFrom": "builder_project_threads", + "tableTo": "builder_projects", + "columnsFrom": ["project_id", "owner_id"], + "columnsTo": ["id", "owner_id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "builder_project_threads_project_id_unique": { + "name": "builder_project_threads_project_id_unique", + "nullsNotDistinct": false, + "columns": ["project_id", "id"] + }, + "builder_project_threads_project_mutation_unique": { + "name": "builder_project_threads_project_mutation_unique", + "nullsNotDistinct": false, + "columns": ["project_id", "client_mutation_id"] + } + }, + "policies": {}, + "checkConstraints": { + "builder_project_threads_message_position_check": { + "name": "builder_project_threads_message_position_check", + "value": "\"builder_project_threads\".\"last_message_position\" BETWEEN 0 AND 9007199254740991" + } + }, + "isRLSEnabled": false + }, + "public.builder_project_tombstones": { + "name": "builder_project_tombstones", + "schema": "", + "columns": { + "project_id": { + "name": "project_id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "owner_id": { + "name": "owner_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "builder_project_tombstones_owner_deleted_at_idx": { + "name": "builder_project_tombstones_owner_deleted_at_idx", + "columns": [ + { + "expression": "owner_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "deleted_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "builder_project_tombstones_owner_id_users_id_fk": { + "name": "builder_project_tombstones_owner_id_users_id_fk", + "tableFrom": "builder_project_tombstones", + "tableTo": "users", + "columnsFrom": ["owner_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.builder_project_usage": { + "name": "builder_project_usage", + "schema": "", + "columns": { + "project_id": { + "name": "project_id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "threads": { + "name": "threads", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "messages": { + "name": "messages", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "runs": { + "name": "runs", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "revisions": { + "name": "revisions", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "events": { + "name": "events", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "payload_bytes": { + "name": "payload_bytes", + "type": "bigint", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "builder_project_usage_project_id_builder_projects_id_fk": { + "name": "builder_project_usage_project_id_builder_projects_id_fk", + "tableFrom": "builder_project_usage", + "tableTo": "builder_projects", + "columnsFrom": ["project_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "builder_project_usage_nonnegative_check": { + "name": "builder_project_usage_nonnegative_check", + "value": "\"builder_project_usage\".\"threads\" >= 0 AND \"builder_project_usage\".\"messages\" >= 0 AND \"builder_project_usage\".\"runs\" >= 0 AND \"builder_project_usage\".\"revisions\" >= 0 AND \"builder_project_usage\".\"events\" >= 0 AND \"builder_project_usage\".\"payload_bytes\" >= 0" + } + }, + "isRLSEnabled": false + }, + "public.builder_projects": { + "name": "builder_projects", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "owner_id": { + "name": "owner_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "client_mutation_id": { + "name": "client_mutation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "forked_from_id": { + "name": "forked_from_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "title": { + "name": "title", + "type": "varchar(160)", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "varchar(1000)", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "snapshot_hash": { + "name": "snapshot_hash", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true + }, + "current_revision_number": { + "name": "current_revision_number", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "last_event_sequence": { + "name": "last_event_sequence", + "type": "bigint", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "last_lease_fencing_token": { + "name": "last_lease_fencing_token", + "type": "bigint", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "deleted_by_id": { + "name": "deleted_by_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "builder_projects_owner_id_idx": { + "name": "builder_projects_owner_id_idx", + "columns": [ + { + "expression": "owner_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "builder_projects_owner_updated_at_idx": { + "name": "builder_projects_owner_updated_at_idx", + "columns": [ + { + "expression": "owner_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "updated_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "builder_projects_deleted_at_idx": { + "name": "builder_projects_deleted_at_idx", + "columns": [ + { + "expression": "deleted_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "builder_projects_owner_id_users_id_fk": { + "name": "builder_projects_owner_id_users_id_fk", + "tableFrom": "builder_projects", + "tableTo": "users", + "columnsFrom": ["owner_id"], + "columnsTo": ["id"], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "builder_projects_deleted_by_id_users_id_fk": { + "name": "builder_projects_deleted_by_id_users_id_fk", + "tableFrom": "builder_projects", + "tableTo": "users", + "columnsFrom": ["deleted_by_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + }, + "builder_projects_forked_from_fk": { + "name": "builder_projects_forked_from_fk", + "tableFrom": "builder_projects", + "tableTo": "builder_projects", + "columnsFrom": ["forked_from_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + }, + "builder_projects_snapshot_hash_fk": { + "name": "builder_projects_snapshot_hash_fk", + "tableFrom": "builder_projects", + "tableTo": "builder_project_snapshots", + "columnsFrom": ["snapshot_hash"], + "columnsTo": ["hash"], + "onDelete": "restrict", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "builder_projects_id_owner_unique": { + "name": "builder_projects_id_owner_unique", + "nullsNotDistinct": false, + "columns": ["id", "owner_id"] + }, + "builder_projects_owner_mutation_unique": { + "name": "builder_projects_owner_mutation_unique", + "nullsNotDistinct": false, + "columns": ["owner_id", "client_mutation_id"] + } + }, + "policies": {}, + "checkConstraints": { + "builder_projects_snapshot_hash_check": { + "name": "builder_projects_snapshot_hash_check", + "value": "\"builder_projects\".\"snapshot_hash\" ~ '^[a-f0-9]{64}$'" + }, + "builder_projects_revision_number_check": { + "name": "builder_projects_revision_number_check", + "value": "\"builder_projects\".\"current_revision_number\" > 0" + }, + "builder_projects_event_sequence_check": { + "name": "builder_projects_event_sequence_check", + "value": "\"builder_projects\".\"last_event_sequence\" BETWEEN 0 AND 9007199254740991" + }, + "builder_projects_lease_fencing_token_check": { + "name": "builder_projects_lease_fencing_token_check", + "value": "\"builder_projects\".\"last_lease_fencing_token\" BETWEEN 0 AND 9007199254740991" + } + }, + "isRLSEnabled": false + }, + "public.chat_access": { + "name": "chat_access", + "schema": "", + "columns": { + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "invited_by": { + "name": "invited_by", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "chat_access_user_id_users_id_fk": { + "name": "chat_access_user_id_users_id_fk", + "tableFrom": "chat_access", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "chat_access_invited_by_users_id_fk": { + "name": "chat_access_invited_by_users_id_fk", + "tableFrom": "chat_access", + "tableTo": "users", + "columnsFrom": ["invited_by"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.chat_account_onboarding": { + "name": "chat_account_onboarding", + "schema": "", + "columns": { + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "revision": { + "name": "revision", + "type": "bigint", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "use_case": { + "name": "use_case", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "completed_at": { + "name": "completed_at", + "type": "bigint", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "chat_account_onboarding_user_id_users_id_fk": { + "name": "chat_account_onboarding_user_id_users_id_fk", + "tableFrom": "chat_account_onboarding", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.chat_account_onboarding_commands": { + "name": "chat_account_onboarding_commands", + "schema": "", + "columns": { + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "command_id": { + "name": "command_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "request_digest": { + "name": "request_digest", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "mutation_id": { + "name": "mutation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "result_json": { + "name": "result_json", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "chat_account_onboarding_commands_user_id_chat_account_onboarding_user_id_fk": { + "name": "chat_account_onboarding_commands_user_id_chat_account_onboarding_user_id_fk", + "tableFrom": "chat_account_onboarding_commands", + "tableTo": "chat_account_onboarding", + "columnsFrom": ["user_id"], + "columnsTo": ["user_id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "chat_account_onboarding_commands_user_id_command_id_pk": { + "name": "chat_account_onboarding_commands_user_id_command_id_pk", + "columns": ["user_id", "command_id"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.chat_account_preferences": { + "name": "chat_account_preferences", + "schema": "", + "columns": { + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "timezone": { + "name": "timezone", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "revision": { + "name": "revision", + "type": "bigint", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "timezone_confirmed_at": { + "name": "timezone_confirmed_at", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "response": { + "name": "response", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "appearance": { + "name": "appearance", + "type": "jsonb", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "chat_account_preferences_user_id_users_id_fk": { + "name": "chat_account_preferences_user_id_users_id_fk", + "tableFrom": "chat_account_preferences", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "chat_preferences_revision_safe": { + "name": "chat_preferences_revision_safe", + "value": "\"chat_account_preferences\".\"revision\" >= 0 AND \"chat_account_preferences\".\"revision\" <= 9007199254740991" + } + }, + "isRLSEnabled": false + }, + "public.chat_bot_drafts": { + "name": "chat_bot_drafts", + "schema": "", + "columns": { + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "id": { + "name": "id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "bot_id": { + "name": "bot_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "conversation_id": { + "name": "conversation_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "parent_id": { + "name": "parent_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "text": { + "name": "text", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "started_at": { + "name": "started_at", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "run_model": { + "name": "run_model", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "reference_inputs": { + "name": "reference_inputs", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "file_ids": { + "name": "file_ids", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "created_at": { + "name": "created_at", + "type": "bigint", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "chat_bot_draft_bot_idx": { + "name": "chat_bot_draft_bot_idx", + "columns": [ + { + "expression": "bot_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "chat_bot_draft_scope_fk": { + "name": "chat_bot_draft_scope_fk", + "tableFrom": "chat_bot_drafts", + "tableTo": "chat_file_drafts", + "columnsFrom": ["workspace_id", "user_id", "id"], + "columnsTo": ["workspace_id", "user_id", "id"], + "onDelete": "no action", + "onUpdate": "no action" + }, + "chat_bot_draft_conversation_fk": { + "name": "chat_bot_draft_conversation_fk", + "tableFrom": "chat_bot_drafts", + "tableTo": "chat_conversations", + "columnsFrom": ["conversation_id", "bot_id", "user_id"], + "columnsTo": ["id", "bot_id", "user_id"], + "onDelete": "no action", + "onUpdate": "no action" + }, + "chat_bot_draft_workspace_fk": { + "name": "chat_bot_draft_workspace_fk", + "tableFrom": "chat_bot_drafts", + "tableTo": "chat_bots", + "columnsFrom": ["workspace_id", "bot_id"], + "columnsTo": ["workspace_id", "id"], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "chat_bot_drafts_workspace_id_user_id_id_pk": { + "name": "chat_bot_drafts_workspace_id_user_id_id_pk", + "columns": ["workspace_id", "user_id", "id"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "chat_bot_draft_attachments": { + "name": "chat_bot_draft_attachments", + "value": "jsonb_typeof(\"chat_bot_drafts\".\"file_ids\")='array' AND jsonb_array_length(\"chat_bot_drafts\".\"file_ids\")<=5" + } + }, + "isRLSEnabled": false + }, + "public.chat_bot_schedule_suspensions": { + "name": "chat_bot_schedule_suspensions", + "schema": "", + "columns": { + "bot_id": { + "name": "bot_id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "generation": { + "name": "generation", + "type": "bigint", + "primaryKey": false, + "notNull": true, + "default": 0 + } + }, + "indexes": {}, + "foreignKeys": { + "chat_bot_schedule_suspensions_bot_id_chat_bots_id_fk": { + "name": "chat_bot_schedule_suspensions_bot_id_chat_bots_id_fk", + "tableFrom": "chat_bot_schedule_suspensions", + "tableTo": "chat_bots", + "columnsFrom": ["bot_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "chat_bot_generation_safe": { + "name": "chat_bot_generation_safe", + "value": "\"chat_bot_schedule_suspensions\".\"generation\" BETWEEN 0 AND 9007199254740991" + } + }, + "isRLSEnabled": false + }, + "public.chat_bot_sections": { + "name": "chat_bot_sections", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "position": { + "name": "position", + "type": "real", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "version": { + "name": "version", + "type": "bigint", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "sort_override": { + "name": "sort_override", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "chat_bot_sections_viewer": { + "name": "chat_bot_sections_viewer", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "chat_bot_sections_workspace_id_chat_workspaces_id_fk": { + "name": "chat_bot_sections_workspace_id_chat_workspaces_id_fk", + "tableFrom": "chat_bot_sections", + "tableTo": "chat_workspaces", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + }, + "chat_bot_sections_user_id_users_id_fk": { + "name": "chat_bot_sections_user_id_users_id_fk", + "tableFrom": "chat_bot_sections", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.chat_bot_viewer_state": { + "name": "chat_bot_viewer_state", + "schema": "", + "columns": { + "bot_id": { + "name": "bot_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "pinned": { + "name": "pinned", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "section_id": { + "name": "section_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "position": { + "name": "position", + "type": "real", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "tags": { + "name": "tags", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + } + }, + "indexes": {}, + "foreignKeys": { + "chat_bot_viewer_state_bot_id_chat_bots_id_fk": { + "name": "chat_bot_viewer_state_bot_id_chat_bots_id_fk", + "tableFrom": "chat_bot_viewer_state", + "tableTo": "chat_bots", + "columnsFrom": ["bot_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "chat_bot_viewer_state_user_id_users_id_fk": { + "name": "chat_bot_viewer_state_user_id_users_id_fk", + "tableFrom": "chat_bot_viewer_state", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + }, + "chat_bot_viewer_state_section_id_chat_bot_sections_id_fk": { + "name": "chat_bot_viewer_state_section_id_chat_bot_sections_id_fk", + "tableFrom": "chat_bot_viewer_state", + "tableTo": "chat_bot_sections", + "columnsFrom": ["section_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "chat_bot_viewer_state_bot_id_user_id_pk": { + "name": "chat_bot_viewer_state_bot_id_user_id_pk", + "columns": ["bot_id", "user_id"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.chat_bots": { + "name": "chat_bots", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "parent_id": { + "name": "parent_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "purpose": { + "name": "purpose", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "version": { + "name": "version", + "type": "bigint", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "avatar": { + "name": "avatar", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "deletion_batch": { + "name": "deletion_batch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "archived_at": { + "name": "archived_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "chat_bots_workspace_idx": { + "name": "chat_bots_workspace_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "chat_bots_workspace_id_chat_workspaces_id_fk": { + "name": "chat_bots_workspace_id_chat_workspaces_id_fk", + "tableFrom": "chat_bots", + "tableTo": "chat_workspaces", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "chat_bots_workspace_id_parent_id_chat_bots_workspace_id_id_fk": { + "name": "chat_bots_workspace_id_parent_id_chat_bots_workspace_id_id_fk", + "tableFrom": "chat_bots", + "tableTo": "chat_bots", + "columnsFrom": ["workspace_id", "parent_id"], + "columnsTo": ["workspace_id", "id"], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "chat_bots_workspace_id_unique": { + "name": "chat_bots_workspace_id_unique", + "nullsNotDistinct": false, + "columns": ["workspace_id", "id"] + } + }, + "policies": {}, + "checkConstraints": { + "chat_personal_assistant_active_check": { + "name": "chat_personal_assistant_active_check", + "value": "\"chat_bots\".\"id\" NOT LIKE 'assistant:%' OR (\"chat_bots\".\"archived_at\" IS NULL AND \"chat_bots\".\"deleted_at\" IS NULL)" + } + }, + "isRLSEnabled": false + }, + "public.chat_composer_drafts": { + "name": "chat_composer_drafts", + "schema": "", + "columns": { + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "scope": { + "name": "scope", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "value": { + "name": "value", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "revision": { + "name": "revision", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "bigint", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "chat_composer_drafts_user_id_users_id_fk": { + "name": "chat_composer_drafts_user_id_users_id_fk", + "tableFrom": "chat_composer_drafts", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "chat_composer_drafts_user_id_scope_pk": { + "name": "chat_composer_drafts_user_id_scope_pk", + "columns": ["user_id", "scope"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "chat_draft_revision_safe": { + "name": "chat_draft_revision_safe", + "value": "\"chat_composer_drafts\".\"revision\">0 AND \"chat_composer_drafts\".\"revision\"<=9007199254740991" + } + }, + "isRLSEnabled": false + }, + "public.chat_connected_devices": { + "name": "chat_connected_devices", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "token_hash": { + "name": "token_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "grants": { + "name": "grants", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'[]'" + }, + "last_seen": { + "name": "last_seen", + "type": "bigint", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "revoked_at": { + "name": "revoked_at", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "bigint", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "chat_devices_owner": { + "name": "chat_devices_owner", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "chat_connected_devices_user_id_users_id_fk": { + "name": "chat_connected_devices_user_id_users_id_fk", + "tableFrom": "chat_connected_devices", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "chat_connected_devices_token_hash_unique": { + "name": "chat_connected_devices_token_hash_unique", + "nullsNotDistinct": false, + "columns": ["token_hash"] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.chat_conversation_activity": { + "name": "chat_conversation_activity", + "schema": "", + "columns": { + "conversation_id": { + "name": "conversation_id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "bot_id": { + "name": "bot_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "activity_at": { + "name": "activity_at", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "event_version": { + "name": "event_version", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "read_version": { + "name": "read_version", + "type": "bigint", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "preview": { + "name": "preview", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "message_count": { + "name": "message_count", + "type": "bigint", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "queued_count": { + "name": "queued_count", + "type": "bigint", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "queue_paused": { + "name": "queue_paused", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + } + }, + "indexes": { + "chat_activity_user_time": { + "name": "chat_activity_user_time", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "activity_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "chat_conversation_activity_conversation_id_chat_conversations_id_fk": { + "name": "chat_conversation_activity_conversation_id_chat_conversations_id_fk", + "tableFrom": "chat_conversation_activity", + "tableTo": "chat_conversations", + "columnsFrom": ["conversation_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "chat_conversation_activity_bot_id_chat_bots_id_fk": { + "name": "chat_conversation_activity_bot_id_chat_bots_id_fk", + "tableFrom": "chat_conversation_activity", + "tableTo": "chat_bots", + "columnsFrom": ["bot_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "chat_conversation_activity_user_id_users_id_fk": { + "name": "chat_conversation_activity_user_id_users_id_fk", + "tableFrom": "chat_conversation_activity", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "chat_activity_status": { + "name": "chat_activity_status", + "value": "\"chat_conversation_activity\".\"status\" IN ('idle','running','approval','setup','error','completed')" + } + }, + "isRLSEnabled": false + }, + "public.chat_conversation_copies": { + "name": "chat_conversation_copies", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "retry_id": { + "name": "retry_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "source_bot_id": { + "name": "source_bot_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "source_conversation_id": { + "name": "source_conversation_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "target_bot_id": { + "name": "target_bot_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "target_conversation_id": { + "name": "target_conversation_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "idempotency_key": { + "name": "idempotency_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "request_digest": { + "name": "request_digest", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "kind": { + "name": "kind", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "boundary_json": { + "name": "boundary_json", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "purpose": { + "name": "purpose", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "parent_id": { + "name": "parent_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'copying'" + }, + "phase": { + "name": "phase", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'export'" + }, + "manifest_json": { + "name": "manifest_json", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "next_page": { + "name": "next_page", + "type": "bigint", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "work_version": { + "name": "work_version", + "type": "bigint", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "error_code": { + "name": "error_code", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "error_message": { + "name": "error_message", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "attempts": { + "name": "attempts", + "type": "bigint", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "retry_at": { + "name": "retry_at", + "type": "bigint", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "completed_at": { + "name": "completed_at", + "type": "bigint", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "chat_copies_pending": { + "name": "chat_copies_pending", + "columns": [ + { + "expression": "source_conversation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "phase", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "retry_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "chat_copies_viewer": { + "name": "chat_copies_viewer", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "chat_conversation_copies_retry_id_chat_conversation_retries_id_fk": { + "name": "chat_conversation_copies_retry_id_chat_conversation_retries_id_fk", + "tableFrom": "chat_conversation_copies", + "tableTo": "chat_conversation_retries", + "columnsFrom": ["retry_id"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + }, + "chat_conversation_copies_workspace_id_chat_workspaces_id_fk": { + "name": "chat_conversation_copies_workspace_id_chat_workspaces_id_fk", + "tableFrom": "chat_conversation_copies", + "tableTo": "chat_workspaces", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + }, + "chat_conversation_copies_user_id_users_id_fk": { + "name": "chat_conversation_copies_user_id_users_id_fk", + "tableFrom": "chat_conversation_copies", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "chat_conversation_copies_retry_id_unique": { + "name": "chat_conversation_copies_retry_id_unique", + "nullsNotDistinct": false, + "columns": ["retry_id"] + }, + "chat_conversation_copies_target_bot_id_unique": { + "name": "chat_conversation_copies_target_bot_id_unique", + "nullsNotDistinct": false, + "columns": ["target_bot_id"] + }, + "chat_conversation_copies_target_conversation_id_unique": { + "name": "chat_conversation_copies_target_conversation_id_unique", + "nullsNotDistinct": false, + "columns": ["target_conversation_id"] + }, + "chat_conversation_copies_workspace_id_user_id_idempotency_key_unique": { + "name": "chat_conversation_copies_workspace_id_user_id_idempotency_key_unique", + "nullsNotDistinct": false, + "columns": ["workspace_id", "user_id", "idempotency_key"] + } + }, + "policies": {}, + "checkConstraints": { + "chat_copy_kind": { + "name": "chat_copy_kind", + "value": "\"chat_conversation_copies\".\"kind\" IN ('duplicate','fork')" + }, + "chat_copy_status": { + "name": "chat_copy_status", + "value": "\"chat_conversation_copies\".\"status\" IN ('copying','ready','failed')" + }, + "chat_copy_phase": { + "name": "chat_copy_phase", + "value": "\"chat_conversation_copies\".\"phase\" IN ('export','transfer','import','publish','cleanup','done')" + } + }, + "isRLSEnabled": false + }, + "public.chat_conversation_mains": { + "name": "chat_conversation_mains", + "schema": "", + "columns": { + "bot_id": { + "name": "bot_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "conversation_id": { + "name": "conversation_id", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "chat_conversation_mains_conversation_id_bot_id_user_id_chat_conversations_id_bot_id_user_id_fk": { + "name": "chat_conversation_mains_conversation_id_bot_id_user_id_chat_conversations_id_bot_id_user_id_fk", + "tableFrom": "chat_conversation_mains", + "tableTo": "chat_conversations", + "columnsFrom": ["conversation_id", "bot_id", "user_id"], + "columnsTo": ["id", "bot_id", "user_id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "chat_conversation_mains_bot_id_user_id_pk": { + "name": "chat_conversation_mains_bot_id_user_id_pk", + "columns": ["bot_id", "user_id"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.chat_conversation_retries": { + "name": "chat_conversation_retries", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "source_bot_id": { + "name": "source_bot_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "source_conversation_id": { + "name": "source_conversation_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "message_id": { + "name": "message_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "idempotency_key": { + "name": "idempotency_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "source_json": { + "name": "source_json", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "file_plan_json": { + "name": "file_plan_json", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'preparing'" + }, + "error_code": { + "name": "error_code", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "error_message": { + "name": "error_message", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "bigint", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "chat_retries_viewer": { + "name": "chat_retries_viewer", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "chat_conversation_retries_workspace_id_chat_workspaces_id_fk": { + "name": "chat_conversation_retries_workspace_id_chat_workspaces_id_fk", + "tableFrom": "chat_conversation_retries", + "tableTo": "chat_workspaces", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + }, + "chat_conversation_retries_user_id_users_id_fk": { + "name": "chat_conversation_retries_user_id_users_id_fk", + "tableFrom": "chat_conversation_retries", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "chat_conversation_retries_workspace_id_user_id_idempotency_key_unique": { + "name": "chat_conversation_retries_workspace_id_user_id_idempotency_key_unique", + "nullsNotDistinct": false, + "columns": ["workspace_id", "user_id", "idempotency_key"] + } + }, + "policies": {}, + "checkConstraints": { + "chat_retry_status": { + "name": "chat_retry_status", + "value": "\"chat_conversation_retries\".\"status\" IN ('preparing','ready','failed')" + } + }, + "isRLSEnabled": false + }, + "public.chat_conversation_threads": { + "name": "chat_conversation_threads", + "schema": "", + "columns": { + "conversation_id": { + "name": "conversation_id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "parent_conversation_id": { + "name": "parent_conversation_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "bot_id": { + "name": "bot_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "source_message_id": { + "name": "source_message_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "source": { + "name": "source", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "version": { + "name": "version", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "archived_at": { + "name": "archived_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "chat_thread_parent_idx": { + "name": "chat_thread_parent_idx", + "columns": [ + { + "expression": "parent_conversation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "chat_thread_child_identity_fk": { + "name": "chat_thread_child_identity_fk", + "tableFrom": "chat_conversation_threads", + "tableTo": "chat_conversations", + "columnsFrom": ["conversation_id", "bot_id", "user_id"], + "columnsTo": ["id", "bot_id", "user_id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "chat_thread_parent_identity_fk": { + "name": "chat_thread_parent_identity_fk", + "tableFrom": "chat_conversation_threads", + "tableTo": "chat_conversations", + "columnsFrom": ["parent_conversation_id", "bot_id", "user_id"], + "columnsTo": ["id", "bot_id", "user_id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "chat_thread_not_self": { + "name": "chat_thread_not_self", + "value": "\"chat_conversation_threads\".\"conversation_id\" <> \"chat_conversation_threads\".\"parent_conversation_id\"" + }, + "chat_thread_version_nonnegative": { + "name": "chat_thread_version_nonnegative", + "value": "\"chat_conversation_threads\".\"version\" >= 0" + } + }, + "isRLSEnabled": false + }, + "public.chat_conversations": { + "name": "chat_conversations", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "bot_id": { + "name": "bot_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "chat_conversations_bot_id_chat_bots_id_fk": { + "name": "chat_conversations_bot_id_chat_bots_id_fk", + "tableFrom": "chat_conversations", + "tableTo": "chat_bots", + "columnsFrom": ["bot_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "chat_conversations_user_id_users_id_fk": { + "name": "chat_conversations_user_id_users_id_fk", + "tableFrom": "chat_conversations", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "chat_conversation_identity": { + "name": "chat_conversation_identity", + "nullsNotDistinct": false, + "columns": ["id", "bot_id", "user_id"] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.chat_credentials": { + "name": "chat_credentials", + "schema": "", + "columns": { + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "ciphertext": { + "name": "ciphertext", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "chat_credentials_user_id_users_id_fk": { + "name": "chat_credentials_user_id_users_id_fk", + "tableFrom": "chat_credentials", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.chat_daily_usage": { + "name": "chat_daily_usage", + "schema": "", + "columns": { + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "day": { + "name": "day", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "turns": { + "name": "turns", + "type": "bigint", + "primaryKey": false, + "notNull": true, + "default": 0 + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "chat_daily_usage_user_id_day_pk": { + "name": "chat_daily_usage_user_id_day_pk", + "columns": ["user_id", "day"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.chat_device_operations": { + "name": "chat_device_operations", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "device_id": { + "name": "device_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "conversation_id": { + "name": "conversation_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "request": { + "name": "request", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "result": { + "name": "result", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "bigint", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "chat_device_operations_pending": { + "name": "chat_device_operations_pending", + "columns": [ + { + "expression": "device_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "expires_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "chat_device_operations_device_id_chat_connected_devices_id_fk": { + "name": "chat_device_operations_device_id_chat_connected_devices_id_fk", + "tableFrom": "chat_device_operations", + "tableTo": "chat_connected_devices", + "columnsFrom": ["device_id"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + }, + "chat_device_operations_user_id_users_id_fk": { + "name": "chat_device_operations_user_id_users_id_fk", + "tableFrom": "chat_device_operations", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + }, + "chat_device_operations_conversation_id_chat_conversations_id_fk": { + "name": "chat_device_operations_conversation_id_chat_conversations_id_fk", + "tableFrom": "chat_device_operations", + "tableTo": "chat_conversations", + "columnsFrom": ["conversation_id"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.chat_execution_membership_generations": { + "name": "chat_execution_membership_generations", + "schema": "", + "columns": { + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "generation": { + "name": "generation", + "type": "bigint", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "chat_execution_membership_generations_workspace_id_chat_workspaces_id_fk": { + "name": "chat_execution_membership_generations_workspace_id_chat_workspaces_id_fk", + "tableFrom": "chat_execution_membership_generations", + "tableTo": "chat_workspaces", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "chat_execution_membership_generations_user_id_users_id_fk": { + "name": "chat_execution_membership_generations_user_id_users_id_fk", + "tableFrom": "chat_execution_membership_generations", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "chat_execution_membership_generations_workspace_id_user_id_pk": { + "name": "chat_execution_membership_generations_workspace_id_user_id_pk", + "columns": ["workspace_id", "user_id"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "chat_membership_generation_safe": { + "name": "chat_membership_generation_safe", + "value": "\"chat_execution_membership_generations\".\"generation\" BETWEEN 0 AND 9007199254740991" + } + }, + "isRLSEnabled": false + }, + "public.chat_file_drafts": { + "name": "chat_file_drafts", + "schema": "", + "columns": { + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "id": { + "name": "id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "bigint", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "chat_file_drafts_workspace_id_chat_workspaces_id_fk": { + "name": "chat_file_drafts_workspace_id_chat_workspaces_id_fk", + "tableFrom": "chat_file_drafts", + "tableTo": "chat_workspaces", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "chat_file_drafts_user_id_users_id_fk": { + "name": "chat_file_drafts_user_id_users_id_fk", + "tableFrom": "chat_file_drafts", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "chat_file_drafts_workspace_id_user_id_id_pk": { + "name": "chat_file_drafts_workspace_id_user_id_id_pk", + "columns": ["workspace_id", "user_id", "id"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.chat_funded_spend": { + "name": "chat_funded_spend", + "schema": "", + "columns": { + "conversation_id": { + "name": "conversation_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "run_id": { + "name": "run_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "day": { + "name": "day", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "reserved_micros": { + "name": "reserved_micros", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "billed_micros": { + "name": "billed_micros", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "observed_micros": { + "name": "observed_micros", + "type": "bigint", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "unknown": { + "name": "unknown", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + } + }, + "indexes": { + "chat_funded_spend_day_user": { + "name": "chat_funded_spend_day_user", + "columns": [ + { + "expression": "day", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "chat_funded_spend_conversation_id_chat_conversations_id_fk": { + "name": "chat_funded_spend_conversation_id_chat_conversations_id_fk", + "tableFrom": "chat_funded_spend", + "tableTo": "chat_conversations", + "columnsFrom": ["conversation_id"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + }, + "chat_funded_spend_user_id_users_id_fk": { + "name": "chat_funded_spend_user_id_users_id_fk", + "tableFrom": "chat_funded_spend", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "chat_funded_spend_conversation_id_run_id_pk": { + "name": "chat_funded_spend_conversation_id_run_id_pk", + "columns": ["conversation_id", "run_id"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.chat_invites": { + "name": "chat_invites", + "schema": "", + "columns": { + "token_hash": { + "name": "token_hash", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "created_by": { + "name": "created_by", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "redeemed_by": { + "name": "redeemed_by", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "redeemed_at": { + "name": "redeemed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "chat_invites_creator_idx": { + "name": "chat_invites_creator_idx", + "columns": [ + { + "expression": "created_by", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "chat_invites_created_by_users_id_fk": { + "name": "chat_invites_created_by_users_id_fk", + "tableFrom": "chat_invites", + "tableTo": "users", + "columnsFrom": ["created_by"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "chat_invites_redeemed_by_users_id_fk": { + "name": "chat_invites_redeemed_by_users_id_fk", + "tableFrom": "chat_invites", + "tableTo": "users", + "columnsFrom": ["redeemed_by"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.chat_kody_account_probe": { + "name": "chat_kody_account_probe", + "schema": "", + "columns": { + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "account_fingerprint": { + "name": "account_fingerprint", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "source_fingerprint": { + "name": "source_fingerprint", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "checked_at": { + "name": "checked_at", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "revision": { + "name": "revision", + "type": "bigint", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "chat_kody_account_probe_user_id_users_id_fk": { + "name": "chat_kody_account_probe_user_id_users_id_fk", + "tableFrom": "chat_kody_account_probe", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.chat_kody_account_reference_catalog": { + "name": "chat_kody_account_reference_catalog", + "schema": "", + "columns": { + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "account_fingerprint": { + "name": "account_fingerprint", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "metadata": { + "name": "metadata", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "fetched_at": { + "name": "fetched_at", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "revision": { + "name": "revision", + "type": "bigint", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "chat_kody_account_reference_catalog_user_id_users_id_fk": { + "name": "chat_kody_account_reference_catalog_user_id_users_id_fk", + "tableFrom": "chat_kody_account_reference_catalog", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.chat_kody_links": { + "name": "chat_kody_links", + "schema": "", + "columns": { + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "subject": { + "name": "subject", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "chat_kody_links_user_id_users_id_fk": { + "name": "chat_kody_links_user_id_users_id_fk", + "tableFrom": "chat_kody_links", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "chat_kody_links_subject_unique": { + "name": "chat_kody_links_subject_unique", + "nullsNotDistinct": false, + "columns": ["subject"] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.chat_kody_oauth_clients": { + "name": "chat_kody_oauth_clients", + "schema": "", + "columns": { + "origin": { + "name": "origin", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "client_id": { + "name": "client_id", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.chat_kody_oauth_pending": { + "name": "chat_kody_oauth_pending", + "schema": "", + "columns": { + "state_hash": { + "name": "state_hash", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "payload": { + "name": "payload", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "bigint", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "chat_kody_oauth_pending_expiry": { + "name": "chat_kody_oauth_pending_expiry", + "columns": [ + { + "expression": "expires_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "chat_kody_oauth_pending_user_id_users_id_fk": { + "name": "chat_kody_oauth_pending_user_id_users_id_fk", + "tableFrom": "chat_kody_oauth_pending", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.chat_kody_reference_catalog": { + "name": "chat_kody_reference_catalog", + "schema": "", + "columns": { + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "account_fingerprint": { + "name": "account_fingerprint", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "metadata": { + "name": "metadata", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "fetched_at": { + "name": "fetched_at", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "complete": { + "name": "complete", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "data_revision": { + "name": "data_revision", + "type": "bigint", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "refresh_started_at": { + "name": "refresh_started_at", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "revision": { + "name": "revision", + "type": "bigint", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "chat_kody_reference_catalog_user_id_users_id_fk": { + "name": "chat_kody_reference_catalog_user_id_users_id_fk", + "tableFrom": "chat_kody_reference_catalog", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.chat_kody_reference_items": { + "name": "chat_kody_reference_items", + "schema": "", + "columns": { + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "revision": { + "name": "revision", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "ordinal": { + "name": "ordinal", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "payload": { + "name": "payload", + "type": "jsonb", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "chat_kody_reference_items_user_id_users_id_fk": { + "name": "chat_kody_reference_items_user_id_users_id_fk", + "tableFrom": "chat_kody_reference_items", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "chat_kody_reference_items_user_id_revision_ordinal_pk": { + "name": "chat_kody_reference_items_user_id_revision_ordinal_pk", + "columns": ["user_id", "revision", "ordinal"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.chat_kody_refresh_claims": { + "name": "chat_kody_refresh_claims", + "schema": "", + "columns": { + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "token_fingerprint": { + "name": "token_fingerprint", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "claim_id": { + "name": "claim_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "lease_until": { + "name": "lease_until", + "type": "bigint", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "chat_kody_refresh_claims_user_id_users_id_fk": { + "name": "chat_kody_refresh_claims_user_id_users_id_fk", + "tableFrom": "chat_kody_refresh_claims", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "chat_kody_refresh_status": { + "name": "chat_kody_refresh_status", + "value": "\"chat_kody_refresh_claims\".\"status\" IN ('refreshing','completed','needs_auth')" + } + }, + "isRLSEnabled": false + }, + "public.chat_kody_skill_sync": { + "name": "chat_kody_skill_sync", + "schema": "", + "columns": { + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "account_fingerprint": { + "name": "account_fingerprint", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "fetched_at": { + "name": "fetched_at", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "revision": { + "name": "revision", + "type": "bigint", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "lease_until": { + "name": "lease_until", + "type": "bigint", + "primaryKey": false, + "notNull": true, + "default": 0 + } + }, + "indexes": {}, + "foreignKeys": { + "chat_kody_skill_sync_user_id_users_id_fk": { + "name": "chat_kody_skill_sync_user_id_users_id_fk", + "tableFrom": "chat_kody_skill_sync", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.chat_kody_skill_sync_stage": { + "name": "chat_kody_skill_sync_stage", + "schema": "", + "columns": { + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "revision": { + "name": "revision", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "id": { + "name": "id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "payload": { + "name": "payload", + "type": "jsonb", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "chat_kody_skill_sync_stage_user_id_users_id_fk": { + "name": "chat_kody_skill_sync_stage_user_id_users_id_fk", + "tableFrom": "chat_kody_skill_sync_stage", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "chat_kody_skill_sync_stage_user_id_revision_id_pk": { + "name": "chat_kody_skill_sync_stage_user_id_revision_id_pk", + "columns": ["user_id", "revision", "id"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.chat_kody_skill_versions": { + "name": "chat_kody_skill_versions", + "schema": "", + "columns": { + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "id": { + "name": "id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "version": { + "name": "version", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "package_id": { + "name": "package_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "source_skill_id": { + "name": "source_skill_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "document": { + "name": "document", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "files": { + "name": "files", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "current": { + "name": "current", + "type": "boolean", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "chat_kody_skill_current_idx": { + "name": "chat_kody_skill_current_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "current", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "chat_kody_skill_versions_user_id_users_id_fk": { + "name": "chat_kody_skill_versions_user_id_users_id_fk", + "tableFrom": "chat_kody_skill_versions", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "chat_kody_skill_versions_user_id_id_version_pk": { + "name": "chat_kody_skill_versions_user_id_id_version_pk", + "columns": ["user_id", "id", "version"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.chat_mcp_account_commands": { + "name": "chat_mcp_account_commands", + "schema": "", + "columns": { + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "command_id": { + "name": "command_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "request_hash": { + "name": "request_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "receipt": { + "name": "receipt", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "bigint", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "chat_mcp_account_commands_user_id_users_id_fk": { + "name": "chat_mcp_account_commands_user_id_users_id_fk", + "tableFrom": "chat_mcp_account_commands", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "chat_mcp_account_commands_user_id_command_id_pk": { + "name": "chat_mcp_account_commands_user_id_command_id_pk", + "columns": ["user_id", "command_id"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.chat_mcp_accounts": { + "name": "chat_mcp_accounts", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "label": { + "name": "label", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "url": { + "name": "url", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "auth_mode": { + "name": "auth_mode", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "removed": { + "name": "removed", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "revision": { + "name": "revision", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "grant_id": { + "name": "grant_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "token_revision": { + "name": "token_revision", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "ciphertext": { + "name": "ciphertext", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "checked_at": { + "name": "checked_at", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "error": { + "name": "error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "refresh_claim": { + "name": "refresh_claim", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "refresh_until": { + "name": "refresh_until", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "bigint", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "chat_mcp_owner": { + "name": "chat_mcp_owner", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "removed", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "chat_mcp_accounts_user_id_users_id_fk": { + "name": "chat_mcp_accounts_user_id_users_id_fk", + "tableFrom": "chat_mcp_accounts", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "chat_mcp_auth_mode": { + "name": "chat_mcp_auth_mode", + "value": "\"chat_mcp_accounts\".\"auth_mode\" IN ('none','token','oauth')" + }, + "chat_mcp_status": { + "name": "chat_mcp_status", + "value": "\"chat_mcp_accounts\".\"status\" IN ('configured','checked','needs_auth','error')" + }, + "chat_mcp_revision": { + "name": "chat_mcp_revision", + "value": "\"chat_mcp_accounts\".\"revision\">0" + }, + "chat_mcp_token_revision": { + "name": "chat_mcp_token_revision", + "value": "\"chat_mcp_accounts\".\"token_revision\">=0" + } + }, + "isRLSEnabled": false + }, + "public.chat_mcp_reference_catalog": { + "name": "chat_mcp_reference_catalog", + "schema": "", + "columns": { + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "server_id": { + "name": "server_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "fingerprint": { + "name": "fingerprint", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "metadata": { + "name": "metadata", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "fetched_at": { + "name": "fetched_at", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "refresh_started_at": { + "name": "refresh_started_at", + "type": "bigint", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "chat_mcp_reference_catalog_workspace_id_chat_workspaces_id_fk": { + "name": "chat_mcp_reference_catalog_workspace_id_chat_workspaces_id_fk", + "tableFrom": "chat_mcp_reference_catalog", + "tableTo": "chat_workspaces", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + }, + "chat_mcp_reference_catalog_user_id_users_id_fk": { + "name": "chat_mcp_reference_catalog_user_id_users_id_fk", + "tableFrom": "chat_mcp_reference_catalog", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "chat_mcp_reference_catalog_workspace_id_user_id_server_id_pk": { + "name": "chat_mcp_reference_catalog_workspace_id_user_id_server_id_pk", + "columns": ["workspace_id", "user_id", "server_id"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.chat_mcp_setup_attempts": { + "name": "chat_mcp_setup_attempts", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "request_hash": { + "name": "request_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "summary": { + "name": "summary", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "ciphertext": { + "name": "ciphertext", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "state_hash": { + "name": "state_hash", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "browser_hash": { + "name": "browser_hash", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "bigint", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "chat_mcp_setup_owner": { + "name": "chat_mcp_setup_owner", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "chat_mcp_setup_attempts_user_id_users_id_fk": { + "name": "chat_mcp_setup_attempts_user_id_users_id_fk", + "tableFrom": "chat_mcp_setup_attempts", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + }, + "chat_mcp_setup_attempts_workspace_id_chat_workspaces_id_fk": { + "name": "chat_mcp_setup_attempts_workspace_id_chat_workspaces_id_fk", + "tableFrom": "chat_mcp_setup_attempts", + "tableTo": "chat_workspaces", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "chat_mcp_setup_attempts_state_hash_unique": { + "name": "chat_mcp_setup_attempts_state_hash_unique", + "nullsNotDistinct": false, + "columns": ["state_hash"] + } + }, + "policies": {}, + "checkConstraints": { + "chat_mcp_setup_status": { + "name": "chat_mcp_setup_status", + "value": "\"chat_mcp_setup_attempts\".\"status\" IN ('review','starting','authorize','complete','failed')" + } + }, + "isRLSEnabled": false + }, + "public.chat_memberships": { + "name": "chat_memberships", + "schema": "", + "columns": { + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "chat_memberships_user_idx": { + "name": "chat_memberships_user_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "chat_memberships_workspace_id_chat_workspaces_id_fk": { + "name": "chat_memberships_workspace_id_chat_workspaces_id_fk", + "tableFrom": "chat_memberships", + "tableTo": "chat_workspaces", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "chat_memberships_user_id_users_id_fk": { + "name": "chat_memberships_user_id_users_id_fk", + "tableFrom": "chat_memberships", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "chat_memberships_workspace_id_user_id_pk": { + "name": "chat_memberships_workspace_id_user_id_pk", + "columns": ["workspace_id", "user_id"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "chat_memberships_role_check": { + "name": "chat_memberships_role_check", + "value": "\"chat_memberships\".\"role\" IN ('owner', 'admin', 'member')" + } + }, + "isRLSEnabled": false + }, + "public.chat_memories": { + "name": "chat_memories", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "conversation_id": { + "name": "conversation_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "revision": { + "name": "revision", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "body": { + "name": "body", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "source_message_id": { + "name": "source_message_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "source_run_id": { + "name": "source_run_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "bigint", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "chat_memory_scope_idx": { + "name": "chat_memory_scope_idx", + "columns": [ + { + "expression": "conversation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "chat_memories_conversation_id_chat_conversations_id_fk": { + "name": "chat_memories_conversation_id_chat_conversations_id_fk", + "tableFrom": "chat_memories", + "tableTo": "chat_conversations", + "columnsFrom": ["conversation_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "chat_memories_workspace_id_chat_workspaces_id_fk": { + "name": "chat_memories_workspace_id_chat_workspaces_id_fk", + "tableFrom": "chat_memories", + "tableTo": "chat_workspaces", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "chat_memories_user_id_users_id_fk": { + "name": "chat_memories_user_id_users_id_fk", + "tableFrom": "chat_memories", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.chat_memory_commands": { + "name": "chat_memory_commands", + "schema": "", + "columns": { + "conversation_id": { + "name": "conversation_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "command_id": { + "name": "command_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "request_hash": { + "name": "request_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "memory_id": { + "name": "memory_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "revision": { + "name": "revision", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "operation": { + "name": "operation", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "completed_at": { + "name": "completed_at", + "type": "bigint", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "chat_memory_command_identity_idx": { + "name": "chat_memory_command_identity_idx", + "columns": [ + { + "expression": "conversation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "memory_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "chat_memory_commands_conversation_id_chat_conversations_id_fk": { + "name": "chat_memory_commands_conversation_id_chat_conversations_id_fk", + "tableFrom": "chat_memory_commands", + "tableTo": "chat_conversations", + "columnsFrom": ["conversation_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "chat_memory_commands_conversation_id_command_id_pk": { + "name": "chat_memory_commands_conversation_id_command_id_pk", + "columns": ["conversation_id", "command_id"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.chat_memory_preferences": { + "name": "chat_memory_preferences", + "schema": "", + "columns": { + "conversation_id": { + "name": "conversation_id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "revision": { + "name": "revision", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "bigint", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "chat_memory_preferences_conversation_id_chat_conversations_id_fk": { + "name": "chat_memory_preferences_conversation_id_chat_conversations_id_fk", + "tableFrom": "chat_memory_preferences", + "tableTo": "chat_conversations", + "columnsFrom": ["conversation_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.chat_plugin_bindings": { + "name": "chat_plugin_bindings", + "schema": "", + "columns": { + "installation_id": { + "name": "installation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "version": { + "name": "version", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "requirement_key": { + "name": "requirement_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "server_id": { + "name": "server_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "endpoint": { + "name": "endpoint", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "chat_plugin_bindings_installation_id_version_chat_plugin_versions_installation_id_version_fk": { + "name": "chat_plugin_bindings_installation_id_version_chat_plugin_versions_installation_id_version_fk", + "tableFrom": "chat_plugin_bindings", + "tableTo": "chat_plugin_versions", + "columnsFrom": ["installation_id", "version"], + "columnsTo": ["installation_id", "version"], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "chat_plugin_bindings_installation_id_version_requirement_key_pk": { + "name": "chat_plugin_bindings_installation_id_version_requirement_key_pk", + "columns": ["installation_id", "version", "requirement_key"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.chat_plugin_commands": { + "name": "chat_plugin_commands", + "schema": "", + "columns": { + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "command_id": { + "name": "command_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "request_hash": { + "name": "request_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "receipt": { + "name": "receipt", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "bigint", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "chat_plugin_commands_workspace_id_chat_workspaces_id_fk": { + "name": "chat_plugin_commands_workspace_id_chat_workspaces_id_fk", + "tableFrom": "chat_plugin_commands", + "tableTo": "chat_workspaces", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + }, + "chat_plugin_commands_user_id_users_id_fk": { + "name": "chat_plugin_commands_user_id_users_id_fk", + "tableFrom": "chat_plugin_commands", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "chat_plugin_commands_workspace_id_user_id_command_id_pk": { + "name": "chat_plugin_commands_workspace_id_user_id_command_id_pk", + "columns": ["workspace_id", "user_id", "command_id"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.chat_plugin_files": { + "name": "chat_plugin_files", + "schema": "", + "columns": { + "installation_id": { + "name": "installation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "version": { + "name": "version", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "path": { + "name": "path", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "chat_plugin_files_installation_id_version_chat_plugin_versions_installation_id_version_fk": { + "name": "chat_plugin_files_installation_id_version_chat_plugin_versions_installation_id_version_fk", + "tableFrom": "chat_plugin_files", + "tableTo": "chat_plugin_versions", + "columnsFrom": ["installation_id", "version"], + "columnsTo": ["installation_id", "version"], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "chat_plugin_files_installation_id_version_path_pk": { + "name": "chat_plugin_files_installation_id_version_path_pk", + "columns": ["installation_id", "version", "path"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.chat_plugin_installations": { + "name": "chat_plugin_installations", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "current_version": { + "name": "current_version", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "revision": { + "name": "revision", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "removed": { + "name": "removed", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "bigint", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "chat_plugin_owner_list": { + "name": "chat_plugin_owner_list", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "removed", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "updated_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "chat_plugin_installations_workspace_id_chat_workspaces_id_fk": { + "name": "chat_plugin_installations_workspace_id_chat_workspaces_id_fk", + "tableFrom": "chat_plugin_installations", + "tableTo": "chat_workspaces", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + }, + "chat_plugin_installations_user_id_users_id_fk": { + "name": "chat_plugin_installations_user_id_users_id_fk", + "tableFrom": "chat_plugin_installations", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "chat_plugin_version_positive": { + "name": "chat_plugin_version_positive", + "value": "\"chat_plugin_installations\".\"current_version\">0" + }, + "chat_plugin_revision_positive": { + "name": "chat_plugin_revision_positive", + "value": "\"chat_plugin_installations\".\"revision\">0" + } + }, + "isRLSEnabled": false + }, + "public.chat_plugin_skill_identities": { + "name": "chat_plugin_skill_identities", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "installation_id": { + "name": "installation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "path": { + "name": "path", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "chat_plugin_skill_identities_installation_id_chat_plugin_installations_id_fk": { + "name": "chat_plugin_skill_identities_installation_id_chat_plugin_installations_id_fk", + "tableFrom": "chat_plugin_skill_identities", + "tableTo": "chat_plugin_installations", + "columnsFrom": ["installation_id"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "chat_plugin_skill_path": { + "name": "chat_plugin_skill_path", + "nullsNotDistinct": false, + "columns": ["installation_id", "path"] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.chat_plugin_skill_versions": { + "name": "chat_plugin_skill_versions", + "schema": "", + "columns": { + "skill_id": { + "name": "skill_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "version": { + "name": "version", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "document": { + "name": "document", + "type": "jsonb", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "chat_plugin_skill_versions_skill_id_chat_plugin_skill_identities_id_fk": { + "name": "chat_plugin_skill_versions_skill_id_chat_plugin_skill_identities_id_fk", + "tableFrom": "chat_plugin_skill_versions", + "tableTo": "chat_plugin_skill_identities", + "columnsFrom": ["skill_id"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "chat_plugin_skill_versions_skill_id_version_pk": { + "name": "chat_plugin_skill_versions_skill_id_version_pk", + "columns": ["skill_id", "version"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.chat_plugin_versions": { + "name": "chat_plugin_versions", + "schema": "", + "columns": { + "installation_id": { + "name": "installation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "version": { + "name": "version", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "digest": { + "name": "digest", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "preview": { + "name": "preview", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "bigint", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "chat_plugin_versions_installation_id_chat_plugin_installations_id_fk": { + "name": "chat_plugin_versions_installation_id_chat_plugin_installations_id_fk", + "tableFrom": "chat_plugin_versions", + "tableTo": "chat_plugin_installations", + "columnsFrom": ["installation_id"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "chat_plugin_versions_installation_id_version_pk": { + "name": "chat_plugin_versions_installation_id_version_pk", + "columns": ["installation_id", "version"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "chat_plugin_saved_version_positive": { + "name": "chat_plugin_saved_version_positive", + "value": "\"chat_plugin_versions\".\"version\">0" + } + }, + "isRLSEnabled": false + }, + "public.chat_recipes": { + "name": "chat_recipes", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "code": { + "name": "code", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "bigint", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "chat_recipes_workspace_id_chat_workspaces_id_fk": { + "name": "chat_recipes_workspace_id_chat_workspaces_id_fk", + "tableFrom": "chat_recipes", + "tableTo": "chat_workspaces", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.chat_run_usage_receipts": { + "name": "chat_run_usage_receipts", + "schema": "", + "columns": { + "conversation_id": { + "name": "conversation_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "run_id": { + "name": "run_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "bot_id": { + "name": "bot_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "scheduled": { + "name": "scheduled", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "day": { + "name": "day", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "attempt_id": { + "name": "attempt_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "chat_run_usage_receipts_conversation_id_chat_conversations_id_fk": { + "name": "chat_run_usage_receipts_conversation_id_chat_conversations_id_fk", + "tableFrom": "chat_run_usage_receipts", + "tableTo": "chat_conversations", + "columnsFrom": ["conversation_id"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + }, + "chat_run_usage_receipts_workspace_id_chat_workspaces_id_fk": { + "name": "chat_run_usage_receipts_workspace_id_chat_workspaces_id_fk", + "tableFrom": "chat_run_usage_receipts", + "tableTo": "chat_workspaces", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + }, + "chat_run_usage_receipts_user_id_users_id_fk": { + "name": "chat_run_usage_receipts_user_id_users_id_fk", + "tableFrom": "chat_run_usage_receipts", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + }, + "chat_run_usage_receipts_bot_id_chat_bots_id_fk": { + "name": "chat_run_usage_receipts_bot_id_chat_bots_id_fk", + "tableFrom": "chat_run_usage_receipts", + "tableTo": "chat_bots", + "columnsFrom": ["bot_id"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "chat_run_usage_receipts_conversation_id_run_id_pk": { + "name": "chat_run_usage_receipts_conversation_id_run_id_pk", + "columns": ["conversation_id", "run_id"] + } + }, + "uniqueConstraints": { + "chat_run_usage_receipts_attempt_id_unique": { + "name": "chat_run_usage_receipts_attempt_id_unique", + "nullsNotDistinct": false, + "columns": ["attempt_id"] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.chat_saved_file_imports": { + "name": "chat_saved_file_imports", + "schema": "", + "columns": { + "target_file_id": { + "name": "target_file_id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "target_conversation_id": { + "name": "target_conversation_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "source_conversation_id": { + "name": "source_conversation_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "source_file_id": { + "name": "source_file_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "sha256": { + "name": "sha256", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "media_type": { + "name": "media_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "size": { + "name": "size", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "source": { + "name": "source", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "bigint", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "chat_file_import_target_idx": { + "name": "chat_file_import_target_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "target_conversation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "chat_saved_file_imports_workspace_id_chat_workspaces_id_fk": { + "name": "chat_saved_file_imports_workspace_id_chat_workspaces_id_fk", + "tableFrom": "chat_saved_file_imports", + "tableTo": "chat_workspaces", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + }, + "chat_saved_file_imports_user_id_users_id_fk": { + "name": "chat_saved_file_imports_user_id_users_id_fk", + "tableFrom": "chat_saved_file_imports", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + }, + "chat_saved_file_imports_target_conversation_id_chat_conversations_id_fk": { + "name": "chat_saved_file_imports_target_conversation_id_chat_conversations_id_fk", + "tableFrom": "chat_saved_file_imports", + "tableTo": "chat_conversations", + "columnsFrom": ["target_conversation_id"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "chat_file_import_size": { + "name": "chat_file_import_size", + "value": "\"chat_saved_file_imports\".\"size\">=0 AND \"chat_saved_file_imports\".\"size\"<=2097152" + }, + "chat_file_import_digest": { + "name": "chat_file_import_digest", + "value": "\"chat_saved_file_imports\".\"sha256\" ~ '^[0-9a-f]{64}$'" + }, + "chat_file_import_source": { + "name": "chat_file_import_source", + "value": "\"chat_saved_file_imports\".\"source\" IN ('upload','assistant')" + } + }, + "isRLSEnabled": false + }, + "public.chat_saved_files": { + "name": "chat_saved_files", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "bot_id": { + "name": "bot_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "conversation_id": { + "name": "conversation_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "draft_id": { + "name": "draft_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "media_type": { + "name": "media_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "size": { + "name": "size", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "sha256": { + "name": "sha256", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "source": { + "name": "source", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "state": { + "name": "state", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "bigint", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "chat_file_conversation_idx": { + "name": "chat_file_conversation_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "conversation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "chat_file_draft_idx": { + "name": "chat_file_draft_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "draft_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "chat_saved_files_workspace_id_chat_workspaces_id_fk": { + "name": "chat_saved_files_workspace_id_chat_workspaces_id_fk", + "tableFrom": "chat_saved_files", + "tableTo": "chat_workspaces", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + }, + "chat_saved_files_user_id_users_id_fk": { + "name": "chat_saved_files_user_id_users_id_fk", + "tableFrom": "chat_saved_files", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + }, + "chat_file_draft_fk": { + "name": "chat_file_draft_fk", + "tableFrom": "chat_saved_files", + "tableTo": "chat_file_drafts", + "columnsFrom": ["workspace_id", "user_id", "draft_id"], + "columnsTo": ["workspace_id", "user_id", "id"], + "onDelete": "no action", + "onUpdate": "no action" + }, + "chat_file_conversation_fk": { + "name": "chat_file_conversation_fk", + "tableFrom": "chat_saved_files", + "tableTo": "chat_conversations", + "columnsFrom": ["conversation_id", "bot_id", "user_id"], + "columnsTo": ["id", "bot_id", "user_id"], + "onDelete": "no action", + "onUpdate": "no action" + }, + "chat_file_workspace_fk": { + "name": "chat_file_workspace_fk", + "tableFrom": "chat_saved_files", + "tableTo": "chat_bots", + "columnsFrom": ["workspace_id", "bot_id"], + "columnsTo": ["workspace_id", "id"], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "chat_file_scope": { + "name": "chat_file_scope", + "value": "(\"chat_saved_files\".\"draft_id\" IS NOT NULL AND \"chat_saved_files\".\"bot_id\" IS NULL AND \"chat_saved_files\".\"conversation_id\" IS NULL) OR (\"chat_saved_files\".\"draft_id\" IS NULL AND \"chat_saved_files\".\"bot_id\" IS NOT NULL AND \"chat_saved_files\".\"conversation_id\" IS NOT NULL)" + }, + "chat_file_size": { + "name": "chat_file_size", + "value": "\"chat_saved_files\".\"size\">=0 AND \"chat_saved_files\".\"size\"<=2097152" + }, + "chat_file_name": { + "name": "chat_file_name", + "value": "length(\"chat_saved_files\".\"name\") BETWEEN 1 AND 180" + }, + "chat_file_digest": { + "name": "chat_file_digest", + "value": "\"chat_saved_files\".\"sha256\" ~ '^[0-9a-f]{64}$'" + }, + "chat_file_source": { + "name": "chat_file_source", + "value": "\"chat_saved_files\".\"source\" IN ('upload','assistant')" + }, + "chat_file_state": { + "name": "chat_file_state", + "value": "\"chat_saved_files\".\"state\" IN ('pending','ready')" + } + }, + "isRLSEnabled": false + }, + "public.chat_scheduled_daily_usage": { + "name": "chat_scheduled_daily_usage", + "schema": "", + "columns": { + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "day": { + "name": "day", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "turns": { + "name": "turns", + "type": "bigint", + "primaryKey": false, + "notNull": true, + "default": 0 + } + }, + "indexes": {}, + "foreignKeys": { + "chat_scheduled_daily_usage_user_id_users_id_fk": { + "name": "chat_scheduled_daily_usage_user_id_users_id_fk", + "tableFrom": "chat_scheduled_daily_usage", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "chat_scheduled_daily_usage_user_id_day_pk": { + "name": "chat_scheduled_daily_usage_user_id_day_pk", + "columns": ["user_id", "day"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "chat_scheduled_turns_nonnegative": { + "name": "chat_scheduled_turns_nonnegative", + "value": "\"chat_scheduled_daily_usage\".\"turns\">=0" + } + }, + "isRLSEnabled": false + }, + "public.chat_skill_commands": { + "name": "chat_skill_commands", + "schema": "", + "columns": { + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "command_id": { + "name": "command_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "request_hash": { + "name": "request_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "receipt": { + "name": "receipt", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "bigint", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "chat_skill_commands_workspace_id_chat_workspaces_id_fk": { + "name": "chat_skill_commands_workspace_id_chat_workspaces_id_fk", + "tableFrom": "chat_skill_commands", + "tableTo": "chat_workspaces", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + }, + "chat_skill_commands_user_id_users_id_fk": { + "name": "chat_skill_commands_user_id_users_id_fk", + "tableFrom": "chat_skill_commands", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "chat_skill_commands_workspace_id_user_id_command_id_pk": { + "name": "chat_skill_commands_workspace_id_user_id_command_id_pk", + "columns": ["workspace_id", "user_id", "command_id"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.chat_skill_versions": { + "name": "chat_skill_versions", + "schema": "", + "columns": { + "skill_id": { + "name": "skill_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "version": { + "name": "version", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "document": { + "name": "document", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "content_hash": { + "name": "content_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "bigint", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "chat_skill_versions_skill_id_chat_skills_id_fk": { + "name": "chat_skill_versions_skill_id_chat_skills_id_fk", + "tableFrom": "chat_skill_versions", + "tableTo": "chat_skills", + "columnsFrom": ["skill_id"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "chat_skill_versions_skill_id_version_pk": { + "name": "chat_skill_versions_skill_id_version_pk", + "columns": ["skill_id", "version"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "chat_skill_saved_version": { + "name": "chat_skill_saved_version", + "value": "\"chat_skill_versions\".\"version\">0 AND \"chat_skill_versions\".\"version\"<=9007199254740991" + } + }, + "isRLSEnabled": false + }, + "public.chat_skills": { + "name": "chat_skills", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "version": { + "name": "version", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "revision": { + "name": "revision", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "archived": { + "name": "archived", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "bigint", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "chat_skill_owner_list": { + "name": "chat_skill_owner_list", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "archived", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "updated_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "chat_skills_workspace_id_chat_workspaces_id_fk": { + "name": "chat_skills_workspace_id_chat_workspaces_id_fk", + "tableFrom": "chat_skills", + "tableTo": "chat_workspaces", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + }, + "chat_skills_user_id_users_id_fk": { + "name": "chat_skills_user_id_users_id_fk", + "tableFrom": "chat_skills", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "chat_skill_version": { + "name": "chat_skill_version", + "value": "\"chat_skills\".\"version\">0 AND \"chat_skills\".\"version\"<=9007199254740991" + }, + "chat_skill_revision": { + "name": "chat_skill_revision", + "value": "\"chat_skills\".\"revision\">0 AND \"chat_skills\".\"revision\"<=9007199254740991" + } + }, + "isRLSEnabled": false + }, + "public.chat_thread_requests": { + "name": "chat_thread_requests", + "schema": "", + "columns": { + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "idempotency_key": { + "name": "idempotency_key", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "request_digest": { + "name": "request_digest", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "parent_conversation_id": { + "name": "parent_conversation_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "source_message_id": { + "name": "source_message_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "conversation_id": { + "name": "conversation_id", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "chat_thread_requests_workspace_id_chat_workspaces_id_fk": { + "name": "chat_thread_requests_workspace_id_chat_workspaces_id_fk", + "tableFrom": "chat_thread_requests", + "tableTo": "chat_workspaces", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "chat_thread_requests_user_id_users_id_fk": { + "name": "chat_thread_requests_user_id_users_id_fk", + "tableFrom": "chat_thread_requests", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "chat_thread_requests_conversation_id_chat_conversations_id_fk": { + "name": "chat_thread_requests_conversation_id_chat_conversations_id_fk", + "tableFrom": "chat_thread_requests", + "tableTo": "chat_conversations", + "columnsFrom": ["conversation_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "chat_thread_requests_workspace_id_user_id_idempotency_key_pk": { + "name": "chat_thread_requests_workspace_id_user_id_idempotency_key_pk", + "columns": ["workspace_id", "user_id", "idempotency_key"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.chat_thread_schedule_suspensions": { + "name": "chat_thread_schedule_suspensions", + "schema": "", + "columns": { + "conversation_id": { + "name": "conversation_id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "generation": { + "name": "generation", + "type": "bigint", + "primaryKey": false, + "notNull": true, + "default": 0 + } + }, + "indexes": {}, + "foreignKeys": { + "chat_thread_schedule_suspensions_conversation_id_chat_conversation_threads_conversation_id_fk": { + "name": "chat_thread_schedule_suspensions_conversation_id_chat_conversation_threads_conversation_id_fk", + "tableFrom": "chat_thread_schedule_suspensions", + "tableTo": "chat_conversation_threads", + "columnsFrom": ["conversation_id"], + "columnsTo": ["conversation_id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "chat_thread_generation_safe": { + "name": "chat_thread_generation_safe", + "value": "\"chat_thread_schedule_suspensions\".\"generation\" BETWEEN 0 AND 9007199254740991" + } + }, + "isRLSEnabled": false + }, + "public.chat_waitlist": { + "name": "chat_waitlist", + "schema": "", + "columns": { + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "chat_waitlist_user_id_users_id_fk": { + "name": "chat_waitlist_user_id_users_id_fk", + "tableFrom": "chat_waitlist", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.chat_workflow_children": { + "name": "chat_workflow_children", + "schema": "", + "columns": { + "conversation_id": { + "name": "conversation_id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "owner_conversation_id": { + "name": "owner_conversation_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "workflow_run_id": { + "name": "workflow_run_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "step_id": { + "name": "step_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "admission_json": { + "name": "admission_json", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "chat_workflow_children_conversation_id_chat_conversations_id_fk": { + "name": "chat_workflow_children_conversation_id_chat_conversations_id_fk", + "tableFrom": "chat_workflow_children", + "tableTo": "chat_conversations", + "columnsFrom": ["conversation_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "chat_workflow_children_owner_conversation_id_chat_conversations_id_fk": { + "name": "chat_workflow_children_owner_conversation_id_chat_conversations_id_fk", + "tableFrom": "chat_workflow_children", + "tableTo": "chat_conversations", + "columnsFrom": ["owner_conversation_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "chat_workflow_children_owner_conversation_id_workflow_run_id_step_id_unique": { + "name": "chat_workflow_children_owner_conversation_id_workflow_run_id_step_id_unique", + "nullsNotDistinct": false, + "columns": ["owner_conversation_id", "workflow_run_id", "step_id"] + } + }, + "policies": {}, + "checkConstraints": { + "chat_workflow_child_admission_json": { + "name": "chat_workflow_child_admission_json", + "value": "jsonb_typeof(\"chat_workflow_children\".\"admission_json\"::jsonb)='object'" + } + }, + "isRLSEnabled": false + }, + "public.chat_workflow_revisions": { + "name": "chat_workflow_revisions", + "schema": "", + "columns": { + "conversation_id": { + "name": "conversation_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "workflow_id": { + "name": "workflow_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "revision": { + "name": "revision", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "command_id": { + "name": "command_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "request_hash": { + "name": "request_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "definition_json": { + "name": "definition_json", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "archived": { + "name": "archived", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "bigint", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "chat_workflow_revisions_conversation_id_chat_conversations_id_fk": { + "name": "chat_workflow_revisions_conversation_id_chat_conversations_id_fk", + "tableFrom": "chat_workflow_revisions", + "tableTo": "chat_conversations", + "columnsFrom": ["conversation_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "chat_workflow_revisions_workspace_id_chat_workspaces_id_fk": { + "name": "chat_workflow_revisions_workspace_id_chat_workspaces_id_fk", + "tableFrom": "chat_workflow_revisions", + "tableTo": "chat_workspaces", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + }, + "chat_workflow_revisions_user_id_users_id_fk": { + "name": "chat_workflow_revisions_user_id_users_id_fk", + "tableFrom": "chat_workflow_revisions", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "chat_workflow_revisions_conversation_id_workflow_id_revision_pk": { + "name": "chat_workflow_revisions_conversation_id_workflow_id_revision_pk", + "columns": ["conversation_id", "workflow_id", "revision"] + } + }, + "uniqueConstraints": { + "chat_workflow_revisions_conversation_id_command_id_unique": { + "name": "chat_workflow_revisions_conversation_id_command_id_unique", + "nullsNotDistinct": false, + "columns": ["conversation_id", "command_id"] + } + }, + "policies": {}, + "checkConstraints": { + "chat_workflow_revision_positive": { + "name": "chat_workflow_revision_positive", + "value": "\"chat_workflow_revisions\".\"revision\">0" + } + }, + "isRLSEnabled": false + }, + "public.chat_workspace_sync_clock": { + "name": "chat_workspace_sync_clock", + "schema": "", + "columns": { + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "revision": { + "name": "revision", + "type": "bigint", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "published_revision": { + "name": "published_revision", + "type": "bigint", + "primaryKey": false, + "notNull": true, + "default": 0 + } + }, + "indexes": {}, + "foreignKeys": { + "chat_workspace_sync_clock_workspace_id_chat_workspaces_id_fk": { + "name": "chat_workspace_sync_clock_workspace_id_chat_workspaces_id_fk", + "tableFrom": "chat_workspace_sync_clock", + "tableTo": "chat_workspaces", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.chat_workspace_sync_members": { + "name": "chat_workspace_sync_members", + "schema": "", + "columns": { + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "generation": { + "name": "generation", + "type": "uuid", + "primaryKey": false, + "notNull": true, + "default": "gen_random_uuid()" + } + }, + "indexes": {}, + "foreignKeys": { + "chat_workspace_sync_members_workspace_id_user_id_chat_memberships_workspace_id_user_id_fk": { + "name": "chat_workspace_sync_members_workspace_id_user_id_chat_memberships_workspace_id_user_id_fk", + "tableFrom": "chat_workspace_sync_members", + "tableTo": "chat_memberships", + "columnsFrom": ["workspace_id", "user_id"], + "columnsTo": ["workspace_id", "user_id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "chat_workspace_sync_members_workspace_id_user_id_pk": { + "name": "chat_workspace_sync_members_workspace_id_user_id_pk", + "columns": ["workspace_id", "user_id"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.chat_workspaces": { + "name": "chat_workspaces", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "owner_id": { + "name": "owner_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "policy": { + "name": "policy", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "chat_workspaces_owner_idx": { + "name": "chat_workspaces_owner_idx", + "columns": [ + { + "expression": "owner_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "chat_workspaces_owner_id_users_id_fk": { + "name": "chat_workspaces_owner_id_users_id_fk", + "tableFrom": "chat_workspaces", + "tableTo": "users", + "columnsFrom": ["owner_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.doc_feedback": { + "name": "doc_feedback", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "doc_feedback_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "character_count": { + "name": "character_count", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "page_path": { + "name": "page_path", + "type": "varchar(500)", + "primaryKey": false, + "notNull": true + }, + "library_id": { + "name": "library_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "library_version": { + "name": "library_version", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true + }, + "block_selector": { + "name": "block_selector", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "block_content_hash": { + "name": "block_content_hash", + "type": "varchar(64)", + "primaryKey": false, + "notNull": false + }, + "block_markdown": { + "name": "block_markdown", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "doc_feedback_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "is_detached": { + "name": "is_detached", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "is_collapsed": { + "name": "is_collapsed", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "moderated_by": { + "name": "moderated_by", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "moderated_at": { + "name": "moderated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "moderation_note": { + "name": "moderation_note", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "doc_feedback_user_id_idx": { + "name": "doc_feedback_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "doc_feedback_status_idx": { + "name": "doc_feedback_status_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "doc_feedback_library_idx": { + "name": "doc_feedback_library_idx", + "columns": [ + { + "expression": "library_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "doc_feedback_page_path_idx": { + "name": "doc_feedback_page_path_idx", + "columns": [ + { + "expression": "page_path", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "doc_feedback_created_at_idx": { + "name": "doc_feedback_created_at_idx", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "doc_feedback_is_detached_idx": { + "name": "doc_feedback_is_detached_idx", + "columns": [ + { + "expression": "is_detached", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "doc_feedback_moderated_by_idx": { + "name": "doc_feedback_moderated_by_idx", + "columns": [ + { + "expression": "moderated_by", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "doc_feedback_user_id_users_id_fk": { + "name": "doc_feedback_user_id_users_id_fk", + "tableFrom": "doc_feedback", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "doc_feedback_moderated_by_users_id_fk": { + "name": "doc_feedback_moderated_by_users_id_fk", + "tableFrom": "doc_feedback", + "tableTo": "users", + "columnsFrom": ["moderated_by"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.docs_artifact_cache": { + "name": "docs_artifact_cache", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "repo": { + "name": "repo", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "git_ref": { + "name": "git_ref", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "docs_root": { + "name": "docs_root", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "artifact_type": { + "name": "artifact_type", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true + }, + "artifact_key": { + "name": "artifact_key", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "payload": { + "name": "payload", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "stale_at": { + "name": "stale_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "docs_artifact_cache_repo_ref_docs_root_idx": { + "name": "docs_artifact_cache_repo_ref_docs_root_idx", + "columns": [ + { + "expression": "repo", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "git_ref", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "docs_root", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "docs_artifact_cache_stale_at_idx": { + "name": "docs_artifact_cache_stale_at_idx", + "columns": [ + { + "expression": "stale_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "docs_artifact_cache_unique": { + "name": "docs_artifact_cache_unique", + "columns": [ + { + "expression": "repo", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "git_ref", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "docs_root", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "artifact_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "artifact_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.github_content_cache": { + "name": "github_content_cache", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "repo": { + "name": "repo", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "git_ref": { + "name": "git_ref", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "content_kind": { + "name": "content_kind", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true + }, + "path": { + "name": "path", + "type": "varchar(1024)", + "primaryKey": false, + "notNull": true + }, + "is_present": { + "name": "is_present", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "text_content": { + "name": "text_content", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "json_content": { + "name": "json_content", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "stale_at": { + "name": "stale_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "github_content_cache_repo_ref_idx": { + "name": "github_content_cache_repo_ref_idx", + "columns": [ + { + "expression": "repo", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "git_ref", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "github_content_cache_stale_at_idx": { + "name": "github_content_cache_stale_at_idx", + "columns": [ + { + "expression": "stale_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "github_content_cache_unique": { + "name": "github_content_cache_unique", + "columns": [ + { + "expression": "repo", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "git_ref", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "content_kind", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "path", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.github_stats_cache": { + "name": "github_stats_cache", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "cache_key": { + "name": "cache_key", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "stats": { + "name": "stats", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "previous_stats": { + "name": "previous_stats", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "github_stats_cache_key_idx": { + "name": "github_stats_cache_key_idx", + "columns": [ + { + "expression": "cache_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "github_stats_cache_expires_at_idx": { + "name": "github_stats_cache_expires_at_idx", + "columns": [ + { + "expression": "expires_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "github_stats_cache_cache_key_unique": { + "name": "github_stats_cache_cache_key_unique", + "nullsNotDistinct": false, + "columns": ["cache_key"] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.intent_package_versions": { + "name": "intent_package_versions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "always", + "name": "intent_package_versions_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "2147483647", + "cache": "1", + "cycle": false + } + }, + "package_name": { + "name": "package_name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "version": { + "name": "version", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true + }, + "skill_count": { + "name": "skill_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "published_at": { + "name": "published_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "tarball_url": { + "name": "tarball_url", + "type": "varchar(1024)", + "primaryKey": false, + "notNull": false + }, + "sync_status": { + "name": "sync_status", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "synced_at": { + "name": "synced_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "failure_reason": { + "name": "failure_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "intent_package_versions_package_name_idx": { + "name": "intent_package_versions_package_name_idx", + "columns": [ + { + "expression": "package_name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "intent_package_versions_sync_status_idx": { + "name": "intent_package_versions_sync_status_idx", + "columns": [ + { + "expression": "sync_status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "intent_package_versions_package_version_unique": { + "name": "intent_package_versions_package_version_unique", + "columns": [ + { + "expression": "package_name", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "version", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "intent_package_versions_package_name_intent_packages_name_fk": { + "name": "intent_package_versions_package_name_intent_packages_name_fk", + "tableFrom": "intent_package_versions", + "tableTo": "intent_packages", + "columnsFrom": ["package_name"], + "columnsTo": ["name"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.intent_packages": { + "name": "intent_packages", + "schema": "", + "columns": { + "name": { + "name": "name", + "type": "varchar(255)", + "primaryKey": true, + "notNull": true + }, + "verified": { + "name": "verified", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "first_seen_at": { + "name": "first_seen_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "last_synced_at": { + "name": "last_synced_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.intent_skill_content": { + "name": "intent_skill_content", + "schema": "", + "columns": { + "content_hash": { + "name": "content_hash", + "type": "varchar(64)", + "primaryKey": true, + "notNull": true + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.intent_skills": { + "name": "intent_skills", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "always", + "name": "intent_skills_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "2147483647", + "cache": "1", + "cycle": false + } + }, + "package_version_id": { + "name": "package_version_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "type": { + "name": "type", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false + }, + "framework": { + "name": "framework", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false + }, + "requires": { + "name": "requires", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "skill_path": { + "name": "skill_path", + "type": "varchar(500)", + "primaryKey": false, + "notNull": false + }, + "content_hash": { + "name": "content_hash", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true + }, + "line_count": { + "name": "line_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + } + }, + "indexes": { + "intent_skills_package_version_id_idx": { + "name": "intent_skills_package_version_id_idx", + "columns": [ + { + "expression": "package_version_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "intent_skills_name_idx": { + "name": "intent_skills_name_idx", + "columns": [ + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "intent_skills_framework_idx": { + "name": "intent_skills_framework_idx", + "columns": [ + { + "expression": "framework", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "intent_skills_type_idx": { + "name": "intent_skills_type_idx", + "columns": [ + { + "expression": "type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "intent_skills_package_version_skill_unique": { + "name": "intent_skills_package_version_skill_unique", + "columns": [ + { + "expression": "package_version_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "intent_skills_package_version_id_intent_package_versions_id_fk": { + "name": "intent_skills_package_version_id_intent_package_versions_id_fk", + "tableFrom": "intent_skills", + "tableTo": "intent_package_versions", + "columnsFrom": ["package_version_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "intent_skills_content_hash_intent_skill_content_content_hash_fk": { + "name": "intent_skills_content_hash_intent_skill_content_content_hash_fk", + "tableFrom": "intent_skills", + "tableTo": "intent_skill_content", + "columnsFrom": ["content_hash"], + "columnsTo": ["content_hash"], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.login_history": { + "name": "login_history", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "oauth_provider", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "ip_address": { + "name": "ip_address", + "type": "varchar(45)", + "primaryKey": false, + "notNull": false + }, + "user_agent": { + "name": "user_agent", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is_new_user": { + "name": "is_new_user", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "login_history_user_id_idx": { + "name": "login_history_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "login_history_created_at_idx": { + "name": "login_history_created_at_idx", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "login_history_provider_idx": { + "name": "login_history_provider_idx", + "columns": [ + { + "expression": "provider", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "login_history_user_id_users_id_fk": { + "name": "login_history_user_id_users_id_fk", + "tableFrom": "login_history", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.mcp_api_keys": { + "name": "mcp_api_keys", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "key_hash": { + "name": "key_hash", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true + }, + "key_prefix": { + "name": "key_prefix", + "type": "varchar(12)", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "rate_limit_per_minute": { + "name": "rate_limit_per_minute", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 200 + }, + "is_active": { + "name": "is_active", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "last_used_at": { + "name": "last_used_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "mcp_api_keys_key_hash_idx": { + "name": "mcp_api_keys_key_hash_idx", + "columns": [ + { + "expression": "key_hash", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "mcp_api_keys_user_id_idx": { + "name": "mcp_api_keys_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "mcp_api_keys_is_active_idx": { + "name": "mcp_api_keys_is_active_idx", + "columns": [ + { + "expression": "is_active", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "mcp_api_keys_user_id_users_id_fk": { + "name": "mcp_api_keys_user_id_users_id_fk", + "tableFrom": "mcp_api_keys", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "mcp_api_keys_key_hash_unique": { + "name": "mcp_api_keys_key_hash_unique", + "nullsNotDistinct": false, + "columns": ["key_hash"] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.mcp_rate_limits": { + "name": "mcp_rate_limits", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "identifier": { + "name": "identifier", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "identifier_type": { + "name": "identifier_type", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true + }, + "window_start": { + "name": "window_start", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "request_count": { + "name": "request_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + } + }, + "indexes": { + "mcp_rate_limits_identifier_window_unique": { + "name": "mcp_rate_limits_identifier_window_unique", + "columns": [ + { + "expression": "identifier", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "window_start", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "mcp_rate_limits_identifier_idx": { + "name": "mcp_rate_limits_identifier_idx", + "columns": [ + { + "expression": "identifier", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "mcp_rate_limits_window_start_idx": { + "name": "mcp_rate_limits_window_start_idx", + "columns": [ + { + "expression": "window_start", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.npm_download_chunks": { + "name": "npm_download_chunks", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "package_name": { + "name": "package_name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "date_from": { + "name": "date_from", + "type": "varchar(10)", + "primaryKey": false, + "notNull": true + }, + "date_to": { + "name": "date_to", + "type": "varchar(10)", + "primaryKey": false, + "notNull": true + }, + "bin_size": { + "name": "bin_size", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true, + "default": "'daily'" + }, + "total_downloads": { + "name": "total_downloads", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "daily_data": { + "name": "daily_data", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "is_immutable": { + "name": "is_immutable", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "npm_download_chunks_package_date_bin_unique": { + "name": "npm_download_chunks_package_date_bin_unique", + "columns": [ + { + "expression": "package_name", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "date_from", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "date_to", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "bin_size", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "npm_download_chunks_package_name_idx": { + "name": "npm_download_chunks_package_name_idx", + "columns": [ + { + "expression": "package_name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "npm_download_chunks_date_from_idx": { + "name": "npm_download_chunks_date_from_idx", + "columns": [ + { + "expression": "date_from", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "npm_download_chunks_date_to_idx": { + "name": "npm_download_chunks_date_to_idx", + "columns": [ + { + "expression": "date_to", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "npm_download_chunks_expires_at_idx": { + "name": "npm_download_chunks_expires_at_idx", + "columns": [ + { + "expression": "expires_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "npm_download_chunks_is_immutable_idx": { + "name": "npm_download_chunks_is_immutable_idx", + "columns": [ + { + "expression": "is_immutable", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.oauth_mcp_access_tokens": { + "name": "oauth_mcp_access_tokens", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "token_hash": { + "name": "token_hash", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "client_id": { + "name": "client_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "scope": { + "name": "scope", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'api'" + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "last_used_at": { + "name": "last_used_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "oauth_mcp_access_tokens_hash_idx": { + "name": "oauth_mcp_access_tokens_hash_idx", + "columns": [ + { + "expression": "token_hash", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "oauth_mcp_access_tokens_user_idx": { + "name": "oauth_mcp_access_tokens_user_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "oauth_mcp_access_tokens_expires_idx": { + "name": "oauth_mcp_access_tokens_expires_idx", + "columns": [ + { + "expression": "expires_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "oauth_mcp_access_tokens_user_id_users_id_fk": { + "name": "oauth_mcp_access_tokens_user_id_users_id_fk", + "tableFrom": "oauth_mcp_access_tokens", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "oauth_mcp_access_tokens_token_hash_unique": { + "name": "oauth_mcp_access_tokens_token_hash_unique", + "nullsNotDistinct": false, + "columns": ["token_hash"] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.oauth_accounts": { + "name": "oauth_accounts", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "oauth_provider", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "provider_account_id": { + "name": "provider_account_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "email": { + "name": "email", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "token_scope": { + "name": "token_scope", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "oauth_accounts_user_id_idx": { + "name": "oauth_accounts_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "oauth_accounts_provider_account_unique": { + "name": "oauth_accounts_provider_account_unique", + "columns": [ + { + "expression": "provider", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "provider_account_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "oauth_accounts_provider_account_idx": { + "name": "oauth_accounts_provider_account_idx", + "columns": [ + { + "expression": "provider", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "provider_account_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "oauth_accounts_user_id_users_id_fk": { + "name": "oauth_accounts_user_id_users_id_fk", + "tableFrom": "oauth_accounts", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.oauth_mcp_authorization_codes": { + "name": "oauth_mcp_authorization_codes", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "code_hash": { + "name": "code_hash", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "client_id": { + "name": "client_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "redirect_uri": { + "name": "redirect_uri", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "code_challenge": { + "name": "code_challenge", + "type": "varchar(128)", + "primaryKey": false, + "notNull": true + }, + "code_challenge_method": { + "name": "code_challenge_method", + "type": "varchar(8)", + "primaryKey": false, + "notNull": true, + "default": "'S256'" + }, + "scope": { + "name": "scope", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'api'" + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "oauth_mcp_auth_codes_hash_idx": { + "name": "oauth_mcp_auth_codes_hash_idx", + "columns": [ + { + "expression": "code_hash", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "oauth_mcp_auth_codes_expires_idx": { + "name": "oauth_mcp_auth_codes_expires_idx", + "columns": [ + { + "expression": "expires_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "oauth_mcp_authorization_codes_user_id_users_id_fk": { + "name": "oauth_mcp_authorization_codes_user_id_users_id_fk", + "tableFrom": "oauth_mcp_authorization_codes", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "oauth_mcp_authorization_codes_code_hash_unique": { + "name": "oauth_mcp_authorization_codes_code_hash_unique", + "nullsNotDistinct": false, + "columns": ["code_hash"] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.oauth_mcp_refresh_tokens": { + "name": "oauth_mcp_refresh_tokens", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "token_hash": { + "name": "token_hash", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "client_id": { + "name": "client_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "access_token_id": { + "name": "access_token_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "oauth_mcp_refresh_tokens_hash_idx": { + "name": "oauth_mcp_refresh_tokens_hash_idx", + "columns": [ + { + "expression": "token_hash", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "oauth_mcp_refresh_tokens_user_idx": { + "name": "oauth_mcp_refresh_tokens_user_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "oauth_mcp_refresh_tokens_user_id_users_id_fk": { + "name": "oauth_mcp_refresh_tokens_user_id_users_id_fk", + "tableFrom": "oauth_mcp_refresh_tokens", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "oauth_mcp_refresh_tokens_access_token_id_oauth_mcp_access_tokens_id_fk": { + "name": "oauth_mcp_refresh_tokens_access_token_id_oauth_mcp_access_tokens_id_fk", + "tableFrom": "oauth_mcp_refresh_tokens", + "tableTo": "oauth_mcp_access_tokens", + "columnsFrom": ["access_token_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "oauth_mcp_refresh_tokens_token_hash_unique": { + "name": "oauth_mcp_refresh_tokens_token_hash_unique", + "nullsNotDistinct": false, + "columns": ["token_hash"] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.oss_stats_cache": { + "name": "oss_stats_cache", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "scope_type": { + "name": "scope_type", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true + }, + "scope_key": { + "name": "scope_key", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "github_star_count": { + "name": "github_star_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "github_contributor_count": { + "name": "github_contributor_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "github_dependent_count": { + "name": "github_dependent_count", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "github_fork_count": { + "name": "github_fork_count", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "github_repository_count": { + "name": "github_repository_count", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "github_delta_star_count": { + "name": "github_delta_star_count", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "github_delta_contributor_count": { + "name": "github_delta_contributor_count", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "github_delta_dependent_count": { + "name": "github_delta_dependent_count", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "github_delta_fork_count": { + "name": "github_delta_fork_count", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "github_updated_at": { + "name": "github_updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "npm_total_downloads": { + "name": "npm_total_downloads", + "type": "bigint", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "npm_rate_per_day": { + "name": "npm_rate_per_day", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "npm_package_count": { + "name": "npm_package_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "npm_updated_at": { + "name": "npm_updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "time_delta_ms": { + "name": "time_delta_ms", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "oss_stats_cache_scope_unique": { + "name": "oss_stats_cache_scope_unique", + "columns": [ + { + "expression": "scope_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "scope_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "oss_stats_cache_scope_type_idx": { + "name": "oss_stats_cache_scope_type_idx", + "columns": [ + { + "expression": "scope_type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "oss_stats_cache_scope_key_idx": { + "name": "oss_stats_cache_scope_key_idx", + "columns": [ + { + "expression": "scope_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.role_assignments": { + "name": "role_assignments", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "role_id": { + "name": "role_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "role_assignments_user_id_idx": { + "name": "role_assignments_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "role_assignments_role_id_idx": { + "name": "role_assignments_role_id_idx", + "columns": [ + { + "expression": "role_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "role_assignments_user_role_unique": { + "name": "role_assignments_user_role_unique", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "role_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "role_assignments_user_id_users_id_fk": { + "name": "role_assignments_user_id_users_id_fk", + "tableFrom": "role_assignments", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "role_assignments_role_id_roles_id_fk": { + "name": "role_assignments_role_id_roles_id_fk", + "tableFrom": "role_assignments", + "tableTo": "roles", + "columnsFrom": ["role_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.roles": { + "name": "roles", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "name": { + "name": "name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "capabilities": { + "name": "capabilities", + "type": "capability[]", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "roles_name_idx": { + "name": "roles_name_idx", + "columns": [ + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "roles_name_unique": { + "name": "roles_name_unique", + "nullsNotDistinct": false, + "columns": ["name"] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.sessions": { + "name": "sessions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "token": { + "name": "token", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "ip_address": { + "name": "ip_address", + "type": "varchar(45)", + "primaryKey": false, + "notNull": false + }, + "user_agent": { + "name": "user_agent", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "sessions_token_idx": { + "name": "sessions_token_idx", + "columns": [ + { + "expression": "token", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "sessions_user_id_idx": { + "name": "sessions_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "sessions_expires_at_idx": { + "name": "sessions_expires_at_idx", + "columns": [ + { + "expression": "expires_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "sessions_user_id_users_id_fk": { + "name": "sessions_user_id_users_id_fk", + "tableFrom": "sessions", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "sessions_token_unique": { + "name": "sessions_token_unique", + "nullsNotDistinct": false, + "columns": ["token"] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.showcase_votes": { + "name": "showcase_votes", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "showcase_id": { + "name": "showcase_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "value": { + "name": "value", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "showcase_votes_showcase_id_idx": { + "name": "showcase_votes_showcase_id_idx", + "columns": [ + { + "expression": "showcase_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "showcase_votes_user_id_idx": { + "name": "showcase_votes_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "showcase_votes_showcase_id_showcases_id_fk": { + "name": "showcase_votes_showcase_id_showcases_id_fk", + "tableFrom": "showcase_votes", + "tableTo": "showcases", + "columnsFrom": ["showcase_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "showcase_votes_user_id_users_id_fk": { + "name": "showcase_votes_user_id_users_id_fk", + "tableFrom": "showcase_votes", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "showcase_votes_showcase_id_user_id_unique": { + "name": "showcase_votes_showcase_id_user_id_unique", + "nullsNotDistinct": false, + "columns": ["showcase_id", "user_id"] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.showcases": { + "name": "showcases", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "tagline": { + "name": "tagline", + "type": "varchar(500)", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "url": { + "name": "url", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "logo_url": { + "name": "logo_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "screenshot_url": { + "name": "screenshot_url", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "source_url": { + "name": "source_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "libraries": { + "name": "libraries", + "type": "text[]", + "primaryKey": false, + "notNull": true + }, + "use_cases": { + "name": "use_cases", + "type": "showcase_use_case[]", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "is_featured": { + "name": "is_featured", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "status": { + "name": "status", + "type": "showcase_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "placement": { + "name": "placement", + "type": "showcase_placement", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'private'" + }, + "review_reason": { + "name": "review_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "moderated_by": { + "name": "moderated_by", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "moderated_at": { + "name": "moderated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "moderation_note": { + "name": "moderation_note", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "tranco_rank": { + "name": "tranco_rank", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "tranco_rank_updated_at": { + "name": "tranco_rank_updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "vote_score": { + "name": "vote_score", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "showcases_user_id_idx": { + "name": "showcases_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "showcases_status_idx": { + "name": "showcases_status_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "showcases_status_placement_idx": { + "name": "showcases_status_placement_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "placement", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "showcases_is_featured_idx": { + "name": "showcases_is_featured_idx", + "columns": [ + { + "expression": "is_featured", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "showcases_created_at_idx": { + "name": "showcases_created_at_idx", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "showcases_moderated_by_idx": { + "name": "showcases_moderated_by_idx", + "columns": [ + { + "expression": "moderated_by", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "showcases_tranco_rank_idx": { + "name": "showcases_tranco_rank_idx", + "columns": [ + { + "expression": "tranco_rank", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "showcases_vote_score_idx": { + "name": "showcases_vote_score_idx", + "columns": [ + { + "expression": "vote_score", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "showcases_user_id_users_id_fk": { + "name": "showcases_user_id_users_id_fk", + "tableFrom": "showcases", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "showcases_moderated_by_users_id_fk": { + "name": "showcases_moderated_by_users_id_fk", + "tableFrom": "showcases", + "tableTo": "users", + "columnsFrom": ["moderated_by"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user_activity": { + "name": "user_activity", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "date": { + "name": "date", + "type": "date", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "user_activity_user_date_unique": { + "name": "user_activity_user_date_unique", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "date", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "user_activity_user_id_idx": { + "name": "user_activity_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "user_activity_date_idx": { + "name": "user_activity_date_idx", + "columns": [ + { + "expression": "date", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "user_activity_user_id_users_id_fk": { + "name": "user_activity_user_id_users_id_fk", + "tableFrom": "user_activity", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.users": { + "name": "users", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "email": { + "name": "email", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "display_username": { + "name": "display_username", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "oauth_image": { + "name": "oauth_image", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "capabilities": { + "name": "capabilities", + "type": "capability[]", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "ads_disabled": { + "name": "ads_disabled", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "interested_in_hiding_ads": { + "name": "interested_in_hiding_ads", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "last_used_framework": { + "name": "last_used_framework", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false + }, + "signup_sources": { + "name": "signup_sources", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "session_version": { + "name": "session_version", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "users_email_idx": { + "name": "users_email_idx", + "columns": [ + { + "expression": "email", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "users_created_at_idx": { + "name": "users_created_at_idx", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "users_email_unique": { + "name": "users_email_unique", + "nullsNotDistinct": false, + "columns": ["email"] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": { + "public.audit_action": { + "name": "audit_action", + "schema": "public", + "values": [ + "user.capabilities.update", + "user.adsDisabled.update", + "user.sessions.revoke", + "role.create", + "role.update", + "role.delete", + "role.assignment.create", + "role.assignment.delete", + "feedback.moderate", + "showcase.create", + "showcase.update", + "showcase.delete", + "showcase.moderate" + ] + }, + "public.builder_message_role": { + "name": "builder_message_role", + "schema": "public", + "values": ["user", "assistant"] + }, + "public.builder_project_event_type": { + "name": "builder_project_event_type", + "schema": "public", + "values": [ + "project.created", + "project.updated", + "project.deleted", + "thread.created", + "thread.updated", + "thread.archived", + "revision.created", + "message.created", + "message.updated", + "message.deleted", + "run.created", + "run.started", + "run.activity", + "run.interrupted", + "run.completed", + "run.failed", + "run.cancelled" + ] + }, + "public.builder_run_status": { + "name": "builder_run_status", + "schema": "public", + "values": [ + "pending", + "running", + "interrupted", + "completed", + "failed", + "cancelled" + ] + }, + "public.capability": { + "name": "capability", + "schema": "public", + "values": [ + "admin", + "api-keys", + "disableAds", + "builder", + "mcp", + "moderate-feedback", + "moderate-showcases" + ] + }, + "public.doc_feedback_status": { + "name": "doc_feedback_status", + "schema": "public", + "values": ["pending", "approved", "denied"] + }, + "public.doc_feedback_type": { + "name": "doc_feedback_type", + "schema": "public", + "values": ["note", "improvement"] + }, + "public.oauth_provider": { + "name": "oauth_provider", + "schema": "public", + "values": ["github", "google"] + }, + "public.showcase_placement": { + "name": "showcase_placement", + "schema": "public", + "values": ["showcase", "community", "private"] + }, + "public.showcase_status": { + "name": "showcase_status", + "schema": "public", + "values": ["pending", "approved", "denied"] + }, + "public.showcase_use_case": { + "name": "showcase_use_case", + "schema": "public", + "values": [ + "blog", + "e-commerce", + "saas", + "dashboard", + "documentation", + "portfolio", + "social", + "developer-tool", + "marketing", + "media", + "open-source" + ] + } + }, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} diff --git a/drizzle/migrations/meta/_journal.json b/drizzle/migrations/meta/_journal.json index 0c98b7543..b1b5c8f9d 100644 --- a/drizzle/migrations/meta/_journal.json +++ b/drizzle/migrations/meta/_journal.json @@ -281,6 +281,13 @@ "when": 1790889000000, "tag": "0039_tanchat_invites", "breakpoints": true + }, + { + "idx": 40, + "version": "7", + "when": 1790963800008, + "tag": "0040_tanchat_waitlist", + "breakpoints": true } ] } diff --git a/harness-tests/pending-runtime/chat-waitlist.test.ts b/harness-tests/pending-runtime/chat-waitlist.test.ts new file mode 100644 index 000000000..c3309a313 --- /dev/null +++ b/harness-tests/pending-runtime/chat-waitlist.test.ts @@ -0,0 +1,55 @@ +import { afterAll, beforeAll, expect, it } from 'vitest' +import postgres from 'postgres' +import process from 'node:process' +import type { AuthUser } from '../../src/auth/types' +import { + joinChatWaitlist, + readChatWaitlist, +} from '../../src/chat/access.server' + +const url = new URL(process.env.DATABASE_URL ?? '') +if ( + !['127.0.0.1', 'localhost', '[::1]'].includes(url.hostname) || + !url.pathname.startsWith('/tanchat_test') +) { + throw new Error( + 'Waitlist tests require a migrated local tanchat_test database.', + ) +} +const sql = postgres(url.href, { max: 1 }) +const first: AuthUser = { + userId: crypto.randomUUID(), + email: 'waitlist@example.com', + name: null, + image: null, + oauthImage: null, + displayUsername: null, + capabilities: [], + adsDisabled: null, + interestedInHidingAds: null, + lastUsedFramework: null, + signupSources: [], +} +const second = { ...first, userId: crypto.randomUUID() } +beforeAll(async () => { + await sql`INSERT INTO users(id) VALUES (${first.userId}), (${second.userId})` +}) +afterAll(async () => { + await sql`DELETE FROM users WHERE id IN (${first.userId}, ${second.userId})` + await sql.end() +}) +it('saves one request per account without granting chat access, and removes it with the account', async () => { + expect(await readChatWaitlist(first)).toBe(false) + await Promise.all([joinChatWaitlist(first), joinChatWaitlist(first)]) + expect(await readChatWaitlist(first)).toBe(true) + expect(await readChatWaitlist(second)).toBe(false) + const entries = + await sql`SELECT user_id,created_at FROM chat_waitlist WHERE user_id=${first.userId}` + expect(entries).toHaveLength(1) + expect(entries[0].created_at).toBeInstanceOf(Date) + expect( + await sql`SELECT user_id FROM chat_access WHERE user_id=${first.userId}`, + ).toHaveLength(0) + await sql`DELETE FROM users WHERE id=${first.userId}` + expect(await readChatWaitlist(first)).toBe(false) +}) diff --git a/src/chat/access.functions.ts b/src/chat/access.functions.ts index fef737b40..59f2117ec 100644 --- a/src/chat/access.functions.ts +++ b/src/chat/access.functions.ts @@ -6,6 +6,8 @@ import { readChatAccess, createChatInvite, redeemChatInvite, + readChatWaitlist, + joinChatWaitlist, } from './access.server' export const getChatAccess = createServerFn({ method: 'GET' }).handler( async () => { @@ -29,3 +31,13 @@ export const acceptChatInvite = createServerFn({ method: 'POST' }) data.token, ), ) + +export const getChatWaitlist = createServerFn({ method: 'GET' }).handler( + async () => { + const user = await getAuthGuards().getCurrentUser(getRequest()) + return user ? readChatWaitlist(user) : false + }, +) +export const requestChatAccess = createServerFn({ method: 'POST' }).handler( + async () => joinChatWaitlist(await getAuthGuards().requireAuth(getRequest())), +) diff --git a/src/chat/access.server.ts b/src/chat/access.server.ts index 3f3009c93..9c6c26306 100644 --- a/src/chat/access.server.ts +++ b/src/chat/access.server.ts @@ -1,6 +1,11 @@ import { and, eq, sql } from 'drizzle-orm' import { db } from '~/db/client' -import { oauthAccounts, chatAccess, chatInvites } from '~/db/schema' +import { + oauthAccounts, + chatAccess, + chatInvites, + chatWaitlist, +} from '~/db/schema' import { allMaintainers } from '~/libraries/maintainers' import { getAuthGuards } from '~/auth/index.server' import type { AuthUser } from '~/auth/types' @@ -162,3 +167,17 @@ export async function redeemChatInvite(user: AuthUser, token: string) { }) return { unlocked: true } } + +export async function readChatWaitlist(user: AuthUser) { + const [entry] = await db + .select({ userId: chatWaitlist.userId }) + .from(chatWaitlist) + .where(eq(chatWaitlist.userId, user.userId)) + return !!entry +} +export async function joinChatWaitlist(user: AuthUser) { + await db + .insert(chatWaitlist) + .values({ userId: user.userId }) + .onConflictDoNothing() +} diff --git a/src/components/Navbar.tsx b/src/components/Navbar.tsx index 3b892f8b6..86c5a91ba 100644 --- a/src/components/Navbar.tsx +++ b/src/components/Navbar.tsx @@ -15,7 +15,7 @@ const LazyMobileNavbarAuthControls = React.lazy(() => ) import { NavbarCartButton } from './NavbarCartButton' import { MegaMenuItem } from './MegaMenuItem' -import { Link, useLocation } from '@tanstack/react-router' +import { Link, useLocation, useNavigate } from '@tanstack/react-router' import { ArrowLeftIcon } from '@phosphor-icons/react/ArrowLeft' import { ArrowRightIcon } from '@phosphor-icons/react/ArrowRight' import { ArrowSquareOutIcon } from '@phosphor-icons/react/ArrowSquareOut' @@ -823,7 +823,9 @@ function DesktopNavTrigger({ )} )} - + {(group.sections.length > 0 || group.key === 'libraries') && ( + + )} ) } @@ -874,6 +876,7 @@ function MobileNavigation({ }) { const activeGroup = NAV_GROUPS.find((group) => group.key === activeKey) const { openAiDock, openSearch } = useSearchContext() + const navigate = useNavigate() const userQuery = useCurrentUserQuery() const openUtility = (utility: 'ai' | 'search') => { @@ -918,6 +921,9 @@ function MobileNavigation({ onClick={() => { if (group.key === 'libraries') { onOpenLibraries() + } else if ('to' in group && group.sections.length === 0) { + onNavigate() + void navigate({ to: group.to }) } else { onSelect(group.key) } diff --git a/src/db/schema.ts b/src/db/schema.ts index 2b69a82f8..37126fec9 100644 --- a/src/db/schema.ts +++ b/src/db/schema.ts @@ -3449,3 +3449,12 @@ export const chatInvites = pgTable( creatorIdx: index('chat_invites_creator_idx').on(table.createdBy), }), ) + +export const chatWaitlist = pgTable('chat_waitlist', { + userId: uuid('user_id') + .primaryKey() + .references(() => users.id, { onDelete: 'cascade' }), + createdAt: timestamp('created_at', { withTimezone: true }) + .notNull() + .defaultNow(), +}) diff --git a/src/routes/chat-access.tsx b/src/routes/chat-access.tsx index 8e672bebc..0da1bf02e 100644 --- a/src/routes/chat-access.tsx +++ b/src/routes/chat-access.tsx @@ -1,8 +1,18 @@ import { Button } from '~/ui/Button' import { useState } from 'react' -import { createFileRoute, Link, useRouter } from '@tanstack/react-router' +import { + createFileRoute, + createLink, + Link, + useRouter, +} from '@tanstack/react-router' +import { ArrowRightIcon } from '@phosphor-icons/react/ArrowRight' +import { CheckCircleIcon } from '@phosphor-icons/react/CheckCircle' +import { seo } from '~/utils/seo' import { getChatAccess, + getChatWaitlist, + requestChatAccess, issueChatInvite, acceptChatInvite, } from '~/chat/access.functions' @@ -11,11 +21,30 @@ export const Route = createFileRoute('/chat-access')({ validateSearch: (search: Record) => ({ invite: typeof search.invite === 'string' ? search.invite : undefined, }), - loader: () => getChatAccess(), + loader: async () => { + const access = await getChatAccess() + const waitlisted = + access && !access.unlocked ? await getChatWaitlist() : false + return { access, waitlisted } + }, + head: () => ({ + meta: seo({ + title: 'TanChat | TanStack', + description: + 'Something new is coming. Join the TanChat waitlist or redeem your invite.', + }), + }), + headers: () => ({ + 'Cache-Control': 'private, no-store', + 'Cloudflare-CDN-Cache-Control': 'no-store', + }), component: ChatAccess, }) + +const ButtonLink = createLink(Button) + function ChatAccess() { - const access = Route.useLoaderData() + const { access, waitlisted } = Route.useLoaderData() const { invite } = Route.useSearch() const router = useRouter() const [code, setCode] = useState(invite ?? '') @@ -34,95 +63,194 @@ function ChatAccess() { setBusy(false) } } + const signInSearch = { + returnTo: `/chat-access${invite ? `?invite=${encodeURIComponent(invite)}` : ''}`, + } + const buttonClass = + 'bg-text-primary text-background-default border-transparent hover:bg-text-secondary dark:bg-text-primary dark:text-background-default dark:hover:bg-text-secondary' + const inputClass = + 'w-full rounded-xl border border-border-default bg-background-default px-4 py-3 text-sm outline-none focus:border-border-focus focus:ring-2 focus:ring-border-focus/20' + return ( -
-

TanChat

- {!access ? ( - <> -

Sign in to use your invite.

- - Sign in - - - ) : access.unlocked ? ( - <> - -

Invite someone

-

- {access.unlimited - ? 'You have unlimited invites.' - : `${access.remaining} invites left.`}{' '} - Each invite can be used once and expires after seven days. -

- - {link && ( -
- +
+
+
+ + + + TanChat + + + Early alpha + +
+

+ Something new is coming. +

+
+ {!access ? ( + <> + + {invite + ? 'Sign in to use your invite' + : 'Sign in to join the waitlist'} + + +

+ {invite + ? 'Your invite will be ready after you sign in.' + : 'We’re opening access a little at a time. Already have an invite? Sign in to redeem it.'} +

+ + ) : access.unlocked ? ( + <> +
+

+ Invite someone +

+

+ {access.unlimited + ? 'You have unlimited invites.' + : `${access.remaining} invites left.`}{' '} + Each invite works once and expires in seven days. +

+ + {link && ( +
+ + +
+ )} +
+ + ) : ( + <> + {waitlisted ? ( +
+ +
+

You’re on the waitlist.

+

+ Your request is saved. You can still use an invite below. +

+
+
+ ) : ( + <> + +

+ We’re opening access a little at a time. +

+ + )} +
{ + event.preventDefault() void action(async () => { - await navigator.clipboard.writeText(link) + await acceptChatInvite({ data: { token: code.trim() } }) + await router.navigate({ to: '/chat' }) }) - } + }} > - Copy link - -
+ +
+ setCode(event.target.value)} + className={inputClass} + /> + +
+ + + )} + {error && ( +

+ {error} +

)} - - ) : ( - <> -

- TanChat is an early alpha. Ask someone with access for an invite. -

-
{ - event.preventDefault() - void action(async () => { - await acceptChatInvite({ data: { token: code.trim() } }) - await router.navigate({ to: '/chat' }) - }) - }} - > - - setCode(event.target.value)} - className="w-full rounded-lg border p-3" - /> - -
- - )} - {error &&

{error}

} +
+
) } diff --git a/src/styles/app.css b/src/styles/app.css index 7b23f079d..9b06041ab 100644 --- a/src/styles/app.css +++ b/src/styles/app.css @@ -1195,8 +1195,12 @@ button { pointer-events: none; } -.ts-mega-trigger-wrap:not([data-menu-dismissed='true']):hover::after, -.ts-mega-trigger-wrap:not([data-menu-dismissed='true']):focus-within::after { +.ts-mega-trigger-wrap:has(> .ts-mega-dropdown):not( + [data-menu-dismissed='true'] + ):hover::after, +.ts-mega-trigger-wrap:has(> .ts-mega-dropdown):not( + [data-menu-dismissed='true'] + ):focus-within::after { pointer-events: auto; } From 61cfd7eb2644475d24de9520308e7669ff898be0 Mon Sep 17 00:00:00 2001 From: Tanner Linsley Date: Fri, 2 Oct 2026 13:23:22 -0600 Subject: [PATCH 2/4] Make TanChat navigation immediate and reuse conversation reads --- harness-tests/core/chat-entry-search.test.ts | 74 +++++++++------- .../core/chat-runtime-headers.test.ts | 26 +++++- .../core/conversation-route-cache.test.ts | 88 +++++++++++++++++++ .../chat-workspace-open.test.ts | 53 +++++++++++ src/chat/components/App.tsx | 85 ++++++++++++------ src/chat/components/chatIdentityQuery.ts | 32 +++++++ src/chat/components/conversationRouteQuery.ts | 21 +++-- src/chat/components/useWorkspaceSearch.ts | 18 ++-- src/chat/workspace.server.ts | 62 +++++++++++++ src/routes/chat.b.$botId.tsx | 42 +++++++-- src/routes/chat.c.$conversationId.tsx | 6 -- src/routes/chat.index.tsx | 50 ++++++++--- src/routes/chat.tsx | 7 +- 13 files changed, 467 insertions(+), 97 deletions(-) create mode 100644 harness-tests/core/conversation-route-cache.test.ts create mode 100644 harness-tests/pending-runtime/chat-workspace-open.test.ts create mode 100644 src/chat/components/chatIdentityQuery.ts diff --git a/harness-tests/core/chat-entry-search.test.ts b/harness-tests/core/chat-entry-search.test.ts index 901605437..23489e499 100644 --- a/harness-tests/core/chat-entry-search.test.ts +++ b/harness-tests/core/chat-entry-search.test.ts @@ -2,46 +2,60 @@ import { expect, it, vi } from 'vitest' import { validateWorkspaceSearch } from '../../src/chat/core/navigation' const entry = vi.hoisted(() => { - const beforeLoads: ((context: { - search: ReturnType - }) => Promise)[] = [] - return { beforeLoads } + const components: (() => unknown)[] = [] + return { components, navigate: vi.fn(), setQueryData: vi.fn() } +}) +const search = validateWorkspaceSearch({ + settings: 'appearance', + sort: 'activity', + code: 'private', }) vi.mock('@tanstack/react-router', () => ({ - createFileRoute: - () => - (options: { beforeLoad: Parameters[0] }) => { - entry.beforeLoads.push(options.beforeLoad) - return options - }, - redirect: (options: Record) => options, + createFileRoute: () => (options: { component: () => unknown }) => { + entry.components.push(options.component) + return { options, useSearch: () => search } + }, + useNavigate: () => entry.navigate, })) -vi.mock('../../src/utils/auth.functions', () => ({ - getCurrentUser: async () => ({ userId: 'shared-user' }), +vi.mock('react', async (importOriginal) => ({ + ...(await importOriginal()), + useEffect: (effect: () => void) => effect(), })) -vi.mock('../../src/chat/workspace.functions', () => ({ - loadChatWorkspace: async () => ({ - workspace: { id: 'personal:shared-user' }, - assistantId: 'assistant:shared-user', - conversationId: 'conversation', +vi.mock('@tanstack/react-query', async (importOriginal) => ({ + ...(await importOriginal()), + useQuery: () => ({ + data: { + workspace: { id: 'personal:shared-user', ownerId: 'shared-user' }, + assistantId: 'assistant:shared-user', + conversationId: 'conversation', + }, }), + useQueryClient: () => ({ setQueryData: entry.setQueryData }), +})) +vi.mock('../../src/chat/workspace.functions', () => ({ + loadChatWorkspace: vi.fn(), + resolveChatRoute: vi.fn(), })) -it('preserves validated navigation while opening the authenticated personal assistant', async () => { - await import('../../src/routes/chat.index') - expect(entry.beforeLoads).toHaveLength(1) - const search = validateWorkspaceSearch({ - settings: 'appearance', - sort: 'activity', - code: 'private', - }) - await expect(entry.beforeLoads[0]({ search })).rejects.toEqual({ +it('preserves validated navigation while opening the personal assistant without a blocking route loader', async () => { + const { Route } = await import('../../src/routes/chat.index') + expect(Route.options.beforeLoad).toBeUndefined() + entry.components[0]() + expect(entry.navigate).toHaveBeenCalledWith({ to: '/chat/c/$conversationId', - params: { - conversationId: 'conversation', - }, + params: { conversationId: 'conversation' }, search: { ...search, conversation: undefined }, + replace: true, }) + expect(entry.setQueryData).toHaveBeenCalledWith( + ['chat-identity', 'conversation'], + { + conversationId: 'conversation', + botId: 'assistant:shared-user', + workspaceId: 'personal:shared-user', + userId: 'shared-user', + }, + ) expect(search).not.toHaveProperty('code') expect(search.settings).toBe('appearance') }) diff --git a/harness-tests/core/chat-runtime-headers.test.ts b/harness-tests/core/chat-runtime-headers.test.ts index 52381549f..8c5fb0293 100644 --- a/harness-tests/core/chat-runtime-headers.test.ts +++ b/harness-tests/core/chat-runtime-headers.test.ts @@ -1,9 +1,13 @@ -vi.mock('../../src/chat/access.functions', () => ({ getChatAccess: vi.fn() })) +const access = vi.hoisted(() => vi.fn()) +vi.mock('../../src/chat/access.functions', () => ({ getChatAccess: access })) import { expect, it, vi } from 'vitest' const captured = vi.hoisted(() => { const headers: (() => Record)[] = [] - return { headers } + const beforeLoads: ((input: { + cause: 'enter' | 'stay' | 'preload' + }) => Promise)[] = [] + return { headers, beforeLoads } }) vi.mock('../../src/chat/components/ChatShell', () => ({ ChatShell: () => null, @@ -13,8 +17,15 @@ vi.mock('@tanstack/react-start', () => ({ })) vi.mock('@tanstack/react-router', () => ({ createFileRoute: - () => (options: { headers: () => Record }) => { + () => + (options: { + headers: () => Record + beforeLoad: (input: { + cause: 'enter' | 'stay' | 'preload' + }) => Promise + }) => { captured.headers.push(options.headers) + captured.beforeLoads.push(options.beforeLoad) return options }, })) @@ -29,3 +40,12 @@ it('isolates authenticated chat for browser execution without caching private do 'Cloudflare-CDN-Cache-Control': 'no-store', }) }) + +it('checks access on entry without blocking navigation within the chat shell', async () => { + await import('../../src/routes/chat') + access.mockResolvedValue({ unlocked: true }) + await captured.beforeLoads[0]({ cause: 'enter' }) + await captured.beforeLoads[0]({ cause: 'stay' }) + await captured.beforeLoads[0]({ cause: 'stay' }) + expect(access).toHaveBeenCalledOnce() +}) diff --git a/harness-tests/core/conversation-route-cache.test.ts b/harness-tests/core/conversation-route-cache.test.ts new file mode 100644 index 000000000..9c62e990a --- /dev/null +++ b/harness-tests/core/conversation-route-cache.test.ts @@ -0,0 +1,88 @@ +import { expect, it, vi } from 'vitest' +import { QueryClient } from '@tanstack/react-query' +import type { WorkspaceBot } from '../../src/chat/core/bot-workspace' +vi.mock('../../src/chat/workspace.functions', () => ({ + resolveChatRoute: vi.fn(), +})) +import { chatIdentityQuery } from '../../src/chat/components/chatIdentityQuery' +import { + conversationRouteQuery, + conversationRouteFreshMs, +} from '../../src/chat/components/conversationRouteQuery' +const bot: WorkspaceBot = { + id: 'bot', + workspace_id: 'workspace', + parent_id: null, + name: 'Chat', + purpose: '', + created_at: 0, + mainConversationId: 'conversation', + version: 0, + archived_at: null, + deleted_at: null, + updated_at: 0, + pinned: false, + section_id: null, + position: 0, + tags: [], +} +const identity = { + userId: 'viewer', + workspaceId: 'workspace', + botId: 'bot', + conversationId: 'conversation', +} +it('uses the sidebar prefetch when opening its canonical conversation, and rechecks stale history', async () => { + const queries = new QueryClient() + const request = vi.fn().mockResolvedValue({ identity, messages: [] }) + const input = { + queries, + request, + workspaceId: 'workspace', + bot, + userId: 'viewer', + } + const prefetched = await queries.fetchQuery(conversationRouteQuery(input)) + const opened = conversationRouteQuery({ + ...input, + conversationId: 'conversation', + }) + expect(await queries.fetchQuery(opened)).toBe(prefetched) + expect(request).toHaveBeenCalledExactlyOnceWith( + 'conversations/conversation/history', + ) + expect( + queries.getQueryData(chatIdentityQuery('conversation').queryKey), + ).toEqual(identity) + queries.setQueryData(opened.queryKey, prefetched, { + updatedAt: Date.now() - conversationRouteFreshMs - 1, + }) + await queries.fetchQuery(opened) + expect(request).toHaveBeenCalledTimes(2) + queries.clear() +}) +it('never seeds navigation or history from a different account’s response', async () => { + const queries = new QueryClient() + const request = vi.fn().mockResolvedValue({ + identity: { ...identity, userId: 'other' }, + messages: [], + }) + await expect( + queries.fetchQuery( + conversationRouteQuery({ + queries, + request, + workspaceId: 'workspace', + bot, + userId: 'viewer', + }), + ), + ).rejects.toThrow('account and workspace') + expect( + queries.getQueryData(chatIdentityQuery('conversation').queryKey), + ).toBeUndefined() + expect( + queries.getQueryCache().findAll({ queryKey: ['history'] }), + ).toHaveLength(0) + queries.clear() +}) diff --git a/harness-tests/pending-runtime/chat-workspace-open.test.ts b/harness-tests/pending-runtime/chat-workspace-open.test.ts new file mode 100644 index 000000000..39209438a --- /dev/null +++ b/harness-tests/pending-runtime/chat-workspace-open.test.ts @@ -0,0 +1,53 @@ +import { afterAll, beforeAll, expect, it } from 'vitest' +import process from 'node:process' +import postgres from 'postgres' +import { openPersonalChatWorkspace } from '../../src/chat/workspace.server' + +const url = new URL(process.env.DATABASE_URL ?? '') +if ( + !['127.0.0.1', 'localhost', '[::1]'].includes(url.hostname) || + !url.pathname.startsWith('/tanchat_test') +) { + throw new Error( + 'Workspace-open tests require a migrated local tanchat_test database.', + ) +} +const sql = postgres(url.href, { max: 1 }) +const userId = crypto.randomUUID() +beforeAll(async () => { + await sql`INSERT INTO users(id) VALUES(${userId})` +}) +afterAll(async () => { + await sql`DELETE FROM users WHERE id=${userId}` + await sql.end() +}) +it('opens concurrently, preserves existing settings on warm opens, and repairs incomplete initialization', async () => { + const opened = await Promise.all( + Array.from({ length: 8 }, () => openPersonalChatWorkspace(userId)), + ) + const conversationIds = new Set() + for (const result of opened) conversationIds.add(result.conversationId) + expect(conversationIds.size).toBe(1) + const first = opened[0] + await sql`UPDATE chat_workspaces SET name='Existing workspace' WHERE id=${first.workspace.id}` + await sql`UPDATE chat_bots SET name='Existing assistant' WHERE id=${first.assistantId}` + const warm = await openPersonalChatWorkspace(userId) + expect(warm.workspace.name).toBe('Existing workspace') + for (const bot of warm.bots) { + if (bot.id === warm.assistantId) expect(bot.name).toBe('Existing assistant') + } + expect(warm.conversationId).toBe(first.conversationId) + expect( + await sql`SELECT user_id FROM chat_account_onboarding WHERE user_id=${userId}`, + ).toHaveLength(1) + await sql`DELETE FROM chat_account_onboarding WHERE user_id=${userId}` + const repaired = await openPersonalChatWorkspace(userId) + expect(repaired.conversationId).toBe(first.conversationId) + expect(repaired.workspace.name).toBe('Existing workspace') + expect( + await sql`SELECT user_id FROM chat_account_onboarding WHERE user_id=${userId}`, + ).toHaveLength(1) + expect( + await sql`SELECT id FROM chat_conversations WHERE user_id=${userId}`, + ).toHaveLength(1) +}) diff --git a/src/chat/components/App.tsx b/src/chat/components/App.tsx index e49734d81..7b1893101 100644 --- a/src/chat/components/App.tsx +++ b/src/chat/components/App.tsx @@ -11,6 +11,7 @@ import { useDebugDetails } from './useDebugDetails' import { TabPanels } from './ui/TabPanels' import { Button } from './ui/Button' import { Menu } from '@base-ui/react/menu' +import { conversationLocation } from '../core/conversation-destination' import type { TurnOutcome } from '../core/message-navigation' import { SelectField } from './SelectField' import { deferredPanel } from './deferredPanel' @@ -23,6 +24,8 @@ import { DraftBot } from './DraftBot' import { Dialog } from './Dialog' import { ConversationView } from './ConversationView' import { conversationRouteQuery } from './conversationRouteQuery' +import { chatIdentityQuery, rememberChatIdentity } from './chatIdentityQuery' +import { LoadingState } from './ui/LoadingState' import { ConversationDetailsToggle } from './ConversationDetailsCard' import { ConversationNavigator, @@ -51,7 +54,7 @@ import { Bookmark, Copy, } from 'lucide-react' -import { useNavigate, useParams, useRouteContext } from '@tanstack/react-router' +import { useNavigate, useParams } from '@tanstack/react-router' import { WorkspaceApiProvider, useWorkspaceApi, ApiError } from './WorkspaceApi' import { @@ -233,14 +236,26 @@ const names: Record = { } export function App() { const params = useParams({ strict: false }) - const { chatIdentity } = useRouteContext({ strict: false }) - const workspaceId = chatIdentity?.workspaceId ?? params.workspaceId + const identity = useQuery({ + ...chatIdentityQuery(params.conversationId ?? ''), + enabled: !!params.conversationId, + }) + if (params.conversationId && !identity.data) { + if (identity.error) + return ( +
+ {identity.error.message} +
+ ) + return Opening conversation… + } + const workspaceId = identity.data?.workspaceId ?? params.workspaceId return ( ) @@ -510,9 +525,21 @@ function WorkspaceApp({ const activityQuery = workspaceState.activityQuery const selectBot = (id: string) => { if (!data) return + const bot = + data.bots.find((candidate) => candidate.id === id) ?? + (data.personalAssistant?.id === id ? data.personalAssistant : undefined) + const identity = bot + ? rememberChatIdentity(queryClient, bot, data.user.id) + : undefined void navigate({ - to: '/chat/b/$botId', - params: { botId: id }, + ...conversationLocation( + { + workspaceId: identity?.workspaceId ?? data.workspace.id, + botId: id, + conversationId: identity?.conversationId, + }, + search, + ), search: { ...(id === botId && !search.draft && !search.conversation ? search @@ -566,9 +593,20 @@ function WorkspaceApp({ useEffect(() => { if (!data || botId || search.draft || params.homeSection) return if (workspaceId && data.personalAssistant) { + const identity = rememberChatIdentity( + queryClient, + data.personalAssistant, + data.user.id, + ) void navigate({ - to: '/chat/b/$botId', - params: { botId: data.personalAssistant.id }, + ...conversationLocation( + { + workspaceId: data.personalAssistant.workspace_id, + botId: data.personalAssistant.id, + conversationId: identity?.conversationId, + }, + search, + ), search: { ...search, conversation: undefined }, replace: true, }) @@ -585,7 +623,15 @@ function WorkspaceApp({ replace: true, }) } - }, [data, botId, workspaceId, navigate, search.draft, params.homeSection]) + }, [ + data, + botId, + workspaceId, + navigate, + search, + params.homeSection, + queryClient, + ]) if (bootstrap.isPending && !data) return if ( (workspaceError instanceof ApiError && workspaceError.status === 401) || @@ -656,16 +702,7 @@ function WorkspaceApp({ setMobile(false) if (id === 'chat') { if (data.personalAssistant) { - void navigate({ - to: '/chat/b/$botId', - params: { botId: data.personalAssistant.id }, - search: { - ...search, - view: 'bots', - sort: 'position', - group: 'section', - }, - }) + selectBot(data.personalAssistant.id) } else createDraft(null) } else { void navigate({ @@ -954,14 +991,7 @@ function WorkspaceApp({ onChanged={refresh} onOpen={() => { setMobile(false) - if (data.workspace.id === `personal:${data.user.id}`) - selectBot(data.personalAssistant!.id) - else - void navigate({ - to: '/chat/b/$botId', - params: { botId: data.personalAssistant!.id }, - search: defaultWorkspaceSearch, - }) + selectBot(data.personalAssistant!.id) }} /> )} @@ -997,6 +1027,7 @@ function WorkspaceApp({ }} onPrefetch={(bot) => { if (bot.deleted_at !== null) return + rememberChatIdentity(queryClient, bot, data.user.id) void queryClient.prefetchQuery( conversationRouteQuery({ queries: queryClient, diff --git a/src/chat/components/chatIdentityQuery.ts b/src/chat/components/chatIdentityQuery.ts new file mode 100644 index 000000000..19e0e428e --- /dev/null +++ b/src/chat/components/chatIdentityQuery.ts @@ -0,0 +1,32 @@ +import { queryOptions, type QueryClient } from '@tanstack/react-query' +import { resolveChatRoute } from '../workspace.functions' +import type { WorkspaceBot } from '../core/bot-workspace' + +export function chatIdentityQuery(conversationId: string) { + return queryOptions({ + queryKey: ['chat-identity', conversationId], + queryFn: () => resolveChatRoute({ data: { conversationId } }), + staleTime: Infinity, + refetchOnWindowFocus: false, + retry: false, + }) +} + +export function rememberChatIdentity( + queries: QueryClient, + bot: WorkspaceBot, + userId: string, +) { + if (!bot.mainConversationId) return + const identity = { + conversationId: bot.mainConversationId, + botId: bot.id, + workspaceId: bot.workspace_id, + userId, + } + queries.setQueryData( + chatIdentityQuery(identity.conversationId).queryKey, + identity, + ) + return identity +} diff --git a/src/chat/components/conversationRouteQuery.ts b/src/chat/components/conversationRouteQuery.ts index 609423e4f..8548149b4 100644 --- a/src/chat/components/conversationRouteQuery.ts +++ b/src/chat/components/conversationRouteQuery.ts @@ -1,3 +1,4 @@ +import { chatIdentityQuery } from './chatIdentityQuery' import type { QueryClient } from '@tanstack/react-query' import type { History } from './App' import type { WorkspaceBot } from '../core/bot-workspace' @@ -25,18 +26,19 @@ export function conversationRouteQuery({ conversationId?: string userId: string }) { + const requestedConversationId = conversationId ?? bot.mainConversationId return { queryKey: [ 'conversation-route', workspaceId, bot.id, - conversationId, + requestedConversationId, userId, ] as const, queryFn: async () => { const snapshot = await request( - conversationId - ? `conversations/${encodeURIComponent(conversationId)}/history` + requestedConversationId + ? `conversations/${encodeURIComponent(requestedConversationId)}/history` : `bots/${encodeURIComponent(bot.id)}/history`, ) const destination = conversationDestination( @@ -47,7 +49,16 @@ export function conversationRouteQuery({ mainConversationId: bot.mainConversationId, }, snapshot.identity, - conversationId, + requestedConversationId, + ) + queries.setQueryData( + chatIdentityQuery(destination.conversationId).queryKey, + { + conversationId: destination.conversationId, + botId: destination.botId, + workspaceId: destination.workspaceId, + userId: destination.userId, + }, ) queries.setQueryData( conversationQueryKey(destination, 'history'), @@ -56,7 +67,7 @@ export function conversationRouteQuery({ return { snapshot, destination } }, staleTime: conversationRouteFreshMs, - refetchOnMount: 'always' as const, + refetchOnMount: true, refetchOnWindowFocus: false, retry: false, } diff --git a/src/chat/components/useWorkspaceSearch.ts b/src/chat/components/useWorkspaceSearch.ts index 6399afb32..d7b2ca3da 100644 --- a/src/chat/components/useWorkspaceSearch.ts +++ b/src/chat/components/useWorkspaceSearch.ts @@ -1,11 +1,19 @@ +import { useMemo } from 'react' import { useParams, useSearch } from '@tanstack/react-router' import { validateWorkspaceSearch } from '../core/navigation' export function useWorkspaceSearch() { const params = useParams({ strict: false }) - return validateWorkspaceSearch({ - ...useSearch({ strict: false }), - ...(params.conversationId ? { conversation: params.conversationId } : {}), - ...(params.messageId ? { message: params.messageId } : {}), - }) + const search = useSearch({ strict: false }) + return useMemo( + () => + validateWorkspaceSearch({ + ...search, + ...(params.conversationId + ? { conversation: params.conversationId } + : {}), + ...(params.messageId ? { message: params.messageId } : {}), + }), + [search, params.conversationId, params.messageId], + ) } diff --git a/src/chat/workspace.server.ts b/src/chat/workspace.server.ts index 66073634a..d3554d9c2 100644 --- a/src/chat/workspace.server.ts +++ b/src/chat/workspace.server.ts @@ -15,6 +15,68 @@ export async function openPersonalChatWorkspace(userId: string) { const workspaceId = `personal:${userId}` const assistantId = `assistant:${userId}` const conversationId = `chat:${assistantId}:${userId}` + const [existing] = await db + .select({ workspace: chatWorkspaces }) + .from(chatWorkspaces) + .innerJoin( + chatMemberships, + and( + eq(chatMemberships.workspaceId, chatWorkspaces.id), + eq(chatMemberships.userId, userId), + eq(chatMemberships.role, 'owner'), + ), + ) + .innerJoin(chatAccountOnboarding, eq(chatAccountOnboarding.userId, userId)) + .innerJoin( + chatBots, + and( + eq(chatBots.id, assistantId), + eq(chatBots.workspaceId, chatWorkspaces.id), + ), + ) + .innerJoin( + chatConversations, + and( + eq(chatConversations.id, conversationId), + eq(chatConversations.botId, chatBots.id), + eq(chatConversations.userId, userId), + ), + ) + .innerJoin( + chatConversationMains, + and( + eq(chatConversationMains.conversationId, chatConversations.id), + eq(chatConversationMains.botId, chatBots.id), + eq(chatConversationMains.userId, userId), + ), + ) + .where( + and( + eq(chatWorkspaces.id, workspaceId), + eq(chatWorkspaces.ownerId, userId), + ), + ) + if (existing) { + const bots = await db + .select() + .from(chatBots) + .where( + and( + eq(chatBots.workspaceId, workspaceId), + isNull(chatBots.archivedAt), + isNull(chatBots.deletedAt), + ), + ) + return { + workspace: { + ...existing.workspace, + policy: chatPolicySchema.parse(existing.workspace.policy), + }, + bots, + assistantId, + conversationId, + } + } return db.transaction(async (tx) => { await tx .insert(chatWorkspaces) diff --git a/src/routes/chat.b.$botId.tsx b/src/routes/chat.b.$botId.tsx index fb1189a9d..34908dd86 100644 --- a/src/routes/chat.b.$botId.tsx +++ b/src/routes/chat.b.$botId.tsx @@ -1,16 +1,44 @@ -import { createFileRoute, redirect } from '@tanstack/react-router' +import { createFileRoute, useNavigate } from '@tanstack/react-router' +import { useQuery, useQueryClient } from '@tanstack/react-query' +import { useEffect } from 'react' import { resolveChatRoute } from '~/chat/workspace.functions' import { validateWorkspaceSearch } from '~/chat/core/navigation' +import { chatIdentityQuery } from '~/chat/components/chatIdentityQuery' +import { LoadingState } from '~/chat/components/ui/LoadingState' export const Route = createFileRoute('/chat/b/$botId')({ validateSearch: validateWorkspaceSearch, - beforeLoad: async ({ params, search }) => { - const identity = await resolveChatRoute({ data: { botId: params.botId } }) - throw redirect({ + component: BotChatEntry, +}) +function BotChatEntry() { + const { botId } = Route.useParams() + const search = Route.useSearch() + const navigate = useNavigate() + const queries = useQueryClient() + const identity = useQuery({ + queryKey: ['chat-bot-identity', botId], + queryFn: () => resolveChatRoute({ data: { botId } }), + staleTime: Infinity, + retry: false, + }) + useEffect(() => { + if (!identity.data) return + queries.setQueryData( + chatIdentityQuery(identity.data.conversationId).queryKey, + identity.data, + ) + void navigate({ to: '/chat/c/$conversationId', - params: { conversationId: identity.conversationId }, + params: { conversationId: identity.data.conversationId }, search: { ...search, conversation: undefined }, replace: true, }) - }, -}) + }, [identity.data, navigate, queries, search]) + if (identity.error) + return ( +
+ {identity.error.message} +
+ ) + return Opening conversation… +} diff --git a/src/routes/chat.c.$conversationId.tsx b/src/routes/chat.c.$conversationId.tsx index c33a750ff..19c1e3718 100644 --- a/src/routes/chat.c.$conversationId.tsx +++ b/src/routes/chat.c.$conversationId.tsx @@ -1,6 +1,5 @@ import { createFileRoute, stripSearchParams } from '@tanstack/react-router' import { App } from '~/chat/components/App' -import { resolveChatRoute } from '~/chat/workspace.functions' import { defaultWorkspaceSearch, validateWorkspaceSearch, @@ -9,10 +8,5 @@ import { export const Route = createFileRoute('/chat/c/$conversationId')({ validateSearch: validateWorkspaceSearch, search: { middlewares: [stripSearchParams(defaultWorkspaceSearch)] }, - beforeLoad: async ({ params }) => ({ - chatIdentity: await resolveChatRoute({ - data: { conversationId: params.conversationId }, - }), - }), component: App, }) diff --git a/src/routes/chat.index.tsx b/src/routes/chat.index.tsx index e34659ee9..21bd10b80 100644 --- a/src/routes/chat.index.tsx +++ b/src/routes/chat.index.tsx @@ -1,25 +1,51 @@ import { createFileRoute, - redirect, + useNavigate, type SearchSchemaInput, } from '@tanstack/react-router' -import { getCurrentUser } from '~/utils/auth.functions' +import { useQuery, useQueryClient } from '@tanstack/react-query' +import { useEffect } from 'react' import { loadChatWorkspace } from '~/chat/workspace.functions' import { validateWorkspaceSearch } from '~/chat/core/navigation' +import { chatIdentityQuery } from '~/chat/components/chatIdentityQuery' +import { LoadingState } from '~/chat/components/ui/LoadingState' export const Route = createFileRoute('/chat/')({ validateSearch: (search: Record & SearchSchemaInput) => validateWorkspaceSearch(search), - beforeLoad: async ({ search }) => { - const user = await getCurrentUser() - if (!user) throw redirect({ to: '/login', search: { returnTo: '/chat' } }) - const workspace = await loadChatWorkspace() - throw redirect({ + component: ChatEntry, +}) +function ChatEntry() { + const search = Route.useSearch() + const navigate = useNavigate() + const queries = useQueryClient() + const workspace = useQuery({ + queryKey: ['personal-chat-workspace'], + queryFn: () => loadChatWorkspace(), + staleTime: Infinity, + retry: false, + }) + useEffect(() => { + if (!workspace.data) return + const data = workspace.data + queries.setQueryData(chatIdentityQuery(data.conversationId).queryKey, { + conversationId: data.conversationId, + botId: data.assistantId, + workspaceId: data.workspace.id, + userId: data.workspace.ownerId, + }) + void navigate({ to: '/chat/c/$conversationId', - params: { - conversationId: search.conversation ?? workspace.conversationId, - }, + params: { conversationId: search.conversation ?? data.conversationId }, search: { ...search, conversation: undefined }, + replace: true, }) - }, -}) + }, [workspace.data, queries, navigate, search]) + if (workspace.error) + return ( +
+ {workspace.error.message} +
+ ) + return Opening TanChat… +} diff --git a/src/routes/chat.tsx b/src/routes/chat.tsx index 89010ce78..eade2280a 100644 --- a/src/routes/chat.tsx +++ b/src/routes/chat.tsx @@ -16,12 +16,15 @@ const initialAppearance = createServerFn({ method: 'GET' }).handler( export const Route = createFileRoute('/chat')({ staticData: { showNavbar: false }, - beforeLoad: async () => { + beforeLoad: async ({ cause }) => { + if (cause === 'stay') return const access = await getChatAccess() if (!access?.unlocked) throw redirect({ to: '/chat-access', search: { invite: undefined } }) }, - loader: () => initialAppearance(), + loader: () => (typeof window === 'undefined' ? initialAppearance() : null), + staleTime: Infinity, + shouldReload: false, headers: () => ({ ...webContainerHeaders, 'Cache-Control': 'private, no-store', From ab3db33b58a1a6e713130414b20ab90305fde925 Mon Sep 17 00:00:00 2001 From: Tanner Linsley Date: Fri, 2 Oct 2026 13:29:01 -0600 Subject: [PATCH 3/4] Fix admin table render loops and manage TanChat access --- harness-tests/core/chat-access-admin.test.ts | 186 +++++++++ src/chat/access-admin.functions.ts | 37 ++ src/chat/access-admin.server.ts | 136 +++++++ src/routeTree.gen.ts | 21 + src/routes/admin/audit.tsx | 7 +- src/routes/admin/chat-access.tsx | 398 +++++++++++++++++++ src/routes/admin/github-stats.tsx | 4 +- src/routes/admin/logins.tsx | 7 +- src/routes/admin/roles.$roleId.tsx | 4 +- src/routes/admin/roles.index.tsx | 4 +- src/routes/admin/route.tsx | 6 + src/routes/admin/users.tsx | 7 +- 12 files changed, 811 insertions(+), 6 deletions(-) create mode 100644 harness-tests/core/chat-access-admin.test.ts create mode 100644 src/chat/access-admin.functions.ts create mode 100644 src/chat/access-admin.server.ts create mode 100644 src/routes/admin/chat-access.tsx diff --git a/harness-tests/core/chat-access-admin.test.ts b/harness-tests/core/chat-access-admin.test.ts new file mode 100644 index 000000000..dcff1a956 --- /dev/null +++ b/harness-tests/core/chat-access-admin.test.ts @@ -0,0 +1,186 @@ +import { beforeEach, expect, it, vi } from 'vitest' +import { PgDialect } from 'drizzle-orm/pg-core' +import type { SQL } from 'drizzle-orm' + +const mocks = vi.hoisted(() => { + const rows: unknown[][] = [] + const filters: (SQL | undefined)[] = [] + return { + rows, + filters, + requireCapability: vi.fn(), + select: vi.fn(), + limit: vi.fn(), + offset: vi.fn(), + lock: vi.fn(), + insert: vi.fn(), + update: vi.fn(), + createInvite: vi.fn(), + } +}) +vi.mock('@tanstack/react-start/server', () => ({ + getRequest: () => new Request('https://tanstack.com/admin/chat-access'), +})) +vi.mock('~/auth/index.server', () => ({ + getAuthGuards: () => ({ requireCapability: mocks.requireCapability }), +})) +vi.mock('~/chat/access.server', () => ({ + createChatInvite: mocks.createInvite, +})) +vi.mock('~/db/client', () => { + function query() { + const rows = mocks.rows.shift() ?? [] + const chain = { + from: () => chain, + innerJoin: () => chain, + leftJoin: () => chain, + where: (filter: SQL | undefined) => { + mocks.filters.push(filter) + return chain + }, + orderBy: () => chain, + limit: (size: number) => { + mocks.limit(size) + return chain + }, + offset: (offset: number) => { + mocks.offset(offset) + return chain + }, + returning: () => chain, + then: (resolve: (value: unknown[]) => unknown) => + Promise.resolve(rows).then(resolve), + } + return chain + } + const db = { + select: (...args: unknown[]) => { + mocks.select(...args) + return query() + }, + execute: mocks.lock, + insert: () => ({ + values: (values: unknown) => { + mocks.insert(values) + return { onConflictDoNothing: vi.fn() } + }, + }), + update: () => ({ + set: (values: unknown) => { + mocks.update(values) + return query() + }, + }), + transaction: (run: (tx: unknown) => unknown) => run(db), + } + return { db } +}) +import { + createChatInviteAdmin, + expireChatInviteAdmin, + grantChatAccessAdmin, + listChatInvitesAdmin, + listChatWaitlistAdmin, +} from '~/chat/access-admin.server' + +beforeEach(() => { + vi.clearAllMocks() + mocks.rows.length = 0 + mocks.filters.length = 0 + mocks.requireCapability.mockResolvedValue({ userId: 'admin' }) +}) + +it.each([ + { + name: 'waitlist', + run: () => listChatWaitlistAdmin({ page: 0, status: 'waiting' }), + }, + { + name: 'invites', + run: () => listChatInvitesAdmin({ page: 0, status: 'pending' }), + }, + { name: 'grant', run: () => grantChatAccessAdmin('person') }, + { name: 'create', run: () => createChatInviteAdmin() }, + { name: 'expire', run: () => expireChatInviteAdmin('hash') }, +])('requires admin before $name reads or writes', async ({ run }) => { + mocks.requireCapability.mockRejectedValue(new Error('Missing admin')) + await expect(run()).rejects.toThrow('Missing admin') + expect(mocks.requireCapability).toHaveBeenCalledWith( + expect.any(Request), + 'admin', + ) + expect(mocks.select).not.toHaveBeenCalled() + expect(mocks.insert).not.toHaveBeenCalled() + expect(mocks.update).not.toHaveBeenCalled() + expect(mocks.createInvite).not.toHaveBeenCalled() +}) + +it('bounds waitlist reads and sorts oldest requests first', async () => { + mocks.rows.push([], [{ total: 80 }]) + expect(await listChatWaitlistAdmin({ page: 2, status: 'waiting' })).toEqual({ + entries: [], + total: 80, + pageSize: 25, + }) + expect(mocks.limit).toHaveBeenCalledWith(25) + expect(mocks.offset).toHaveBeenCalledWith(50) + const filter = mocks.filters[0] + if (!filter) throw new Error('Missing waiting filter') + expect(new PgDialect().sqlToQuery(filter).sql).toContain('is null') +}) + +it('only lists unredeemed, unexpired invites as pending', async () => { + mocks.rows.push([], [{ total: 3 }]) + await listChatInvitesAdmin({ page: 0, status: 'pending' }) + const filter = mocks.filters[0] + if (!filter) throw new Error('Missing pending filter') + const sql = new PgDialect().sqlToQuery(filter).sql + expect(sql).toContain('"redeemed_at" is null') + expect(sql).toContain('"expires_at" >') + expect(mocks.limit).toHaveBeenCalledWith(25) +}) + +it('locks the user before granting access and preserves an existing grant', async () => { + mocks.rows.push([{ userId: 'person' }]) + await grantChatAccessAdmin('person') + expect(mocks.lock).toHaveBeenCalledOnce() + expect(mocks.lock.mock.invocationCallOrder[0]).toBeLessThan( + mocks.insert.mock.invocationCallOrder[0], + ) + expect(mocks.insert).toHaveBeenCalledWith({ + userId: 'person', + invitedBy: 'admin', + }) +}) + +it('does not grant access to a user outside the waitlist', async () => { + mocks.rows.push([]) + await expect(grantChatAccessAdmin('person')).rejects.toThrow( + 'not on the TanChat waitlist', + ) + expect(mocks.insert).not.toHaveBeenCalled() +}) + +it('creates admin invites through the existing hashed-token flow', async () => { + mocks.createInvite.mockResolvedValue({ token: 'new-invite' }) + expect(await createChatInviteAdmin()).toEqual({ token: 'new-invite' }) + expect(mocks.createInvite).toHaveBeenCalledWith({ userId: 'admin' }) +}) + +it('expires only unused, active invites without deleting quota history', async () => { + mocks.rows.push([{ tokenHash: 'hash' }]) + await expireChatInviteAdmin('hash') + expect(mocks.update).toHaveBeenCalledWith({ expiresAt: expect.any(Date) }) + const filter = mocks.filters[0] + if (!filter) throw new Error('Missing expiry filter') + const sql = new PgDialect().sqlToQuery(filter).sql + expect(sql).toContain('"redeemed_at" is null') + expect(sql).toContain('"expires_at" >') +}) + +it('reports when redemption won the race against expiry', async () => { + mocks.rows.push([]) + await expect(expireChatInviteAdmin('hash')).rejects.toThrow( + 'already been used or expired', + ) +}) diff --git a/src/chat/access-admin.functions.ts b/src/chat/access-admin.functions.ts new file mode 100644 index 000000000..3507495e4 --- /dev/null +++ b/src/chat/access-admin.functions.ts @@ -0,0 +1,37 @@ +import { createServerFn } from '@tanstack/react-start' +import { z } from 'zod' +import { + listChatWaitlistAdmin, + listChatInvitesAdmin, + grantChatAccessAdmin, + createChatInviteAdmin, + expireChatInviteAdmin, +} from './access-admin.server' + +const pagination = z.object({ page: z.number().int().min(0).max(100000) }) + +export const listChatWaitlist = createServerFn({ method: 'GET' }) + .validator( + pagination.extend({ status: z.enum(['waiting', 'granted', 'all']) }), + ) + .handler(({ data }) => listChatWaitlistAdmin(data)) + +export const listChatInvites = createServerFn({ method: 'GET' }) + .validator( + pagination.extend({ + status: z.enum(['pending', 'redeemed', 'expired', 'all']), + }), + ) + .handler(({ data }) => listChatInvitesAdmin(data)) + +export const grantChatAccess = createServerFn({ method: 'POST' }) + .validator(z.object({ userId: z.uuid() })) + .handler(({ data }) => grantChatAccessAdmin(data.userId)) + +export const createAdminChatInvite = createServerFn({ method: 'POST' }).handler( + () => createChatInviteAdmin(), +) + +export const expireChatInvite = createServerFn({ method: 'POST' }) + .validator(z.object({ tokenHash: z.string().regex(/^[a-f0-9]{64}$/) })) + .handler(({ data }) => expireChatInviteAdmin(data.tokenHash)) diff --git a/src/chat/access-admin.server.ts b/src/chat/access-admin.server.ts new file mode 100644 index 000000000..439e20a8a --- /dev/null +++ b/src/chat/access-admin.server.ts @@ -0,0 +1,136 @@ +import { + and, + asc, + desc, + eq, + gt, + isNotNull, + isNull, + lte, + sql, +} from 'drizzle-orm' +import { alias } from 'drizzle-orm/pg-core' +import { getRequest } from '@tanstack/react-start/server' +import { getAuthGuards } from '~/auth/index.server' +import { db } from '~/db/client' +import { chatAccess, chatInvites, chatWaitlist, users } from '~/db/schema' +import { createChatInvite } from './access.server' + +const pageSize = 25 + +export async function listChatWaitlistAdmin(input: { + page: number + status: 'waiting' | 'granted' | 'all' +}) { + await getAuthGuards().requireCapability(getRequest(), 'admin') + const filter = + input.status === 'waiting' + ? isNull(chatAccess.userId) + : input.status === 'granted' + ? isNotNull(chatAccess.userId) + : undefined + const [entries, [count]] = await Promise.all([ + db + .select({ + userId: users.id, + name: users.name, + email: users.email, + joinedAt: chatWaitlist.createdAt, + grantedAt: chatAccess.createdAt, + }) + .from(chatWaitlist) + .innerJoin(users, eq(users.id, chatWaitlist.userId)) + .leftJoin(chatAccess, eq(chatAccess.userId, chatWaitlist.userId)) + .where(filter) + .orderBy(asc(chatWaitlist.createdAt), asc(chatWaitlist.userId)) + .limit(pageSize) + .offset(input.page * pageSize), + db + .select({ total: sql`count(*)::int` }) + .from(chatWaitlist) + .leftJoin(chatAccess, eq(chatAccess.userId, chatWaitlist.userId)) + .where(filter), + ]) + return { entries, total: count.total, pageSize } +} + +export async function listChatInvitesAdmin(input: { + page: number + status: 'pending' | 'redeemed' | 'expired' | 'all' +}) { + await getAuthGuards().requireCapability(getRequest(), 'admin') + const now = new Date() + const filter = + input.status === 'pending' + ? and(isNull(chatInvites.redeemedAt), gt(chatInvites.expiresAt, now)) + : input.status === 'redeemed' + ? isNotNull(chatInvites.redeemedAt) + : input.status === 'expired' + ? and(isNull(chatInvites.redeemedAt), lte(chatInvites.expiresAt, now)) + : undefined + const recipient = alias(users, 'invite_recipient') + const [entries, [count]] = await Promise.all([ + db + .select({ + tokenHash: chatInvites.tokenHash, + createdBy: users.email, + redeemedBy: recipient.email, + createdAt: chatInvites.createdAt, + expiresAt: chatInvites.expiresAt, + redeemedAt: chatInvites.redeemedAt, + }) + .from(chatInvites) + .innerJoin(users, eq(users.id, chatInvites.createdBy)) + .leftJoin(recipient, eq(recipient.id, chatInvites.redeemedBy)) + .where(filter) + .orderBy(desc(chatInvites.createdAt), asc(chatInvites.tokenHash)) + .limit(pageSize) + .offset(input.page * pageSize), + db + .select({ total: sql`count(*)::int` }) + .from(chatInvites) + .where(filter), + ]) + return { entries, total: count.total, pageSize, serverTime: now } +} + +export async function grantChatAccessAdmin(userId: string) { + const admin = await getAuthGuards().requireCapability(getRequest(), 'admin') + await db.transaction(async (tx) => { + // Use the same user lock as invite redemption so approval cannot race it. + await tx.execute(sql`select id from users where id = ${userId} for update`) + const [entry] = await tx + .select({ userId: chatWaitlist.userId }) + .from(chatWaitlist) + .where(eq(chatWaitlist.userId, userId)) + if (!entry) throw new Error('This user is not on the TanChat waitlist.') + await tx + .insert(chatAccess) + .values({ userId, invitedBy: admin.userId }) + .onConflictDoNothing() + }) + return { userId } +} + +export async function createChatInviteAdmin() { + const admin = await getAuthGuards().requireCapability(getRequest(), 'admin') + return createChatInvite(admin) +} + +export async function expireChatInviteAdmin(tokenHash: string) { + await getAuthGuards().requireCapability(getRequest(), 'admin') + const now = new Date() + const [invite] = await db + .update(chatInvites) + .set({ expiresAt: now }) + .where( + and( + eq(chatInvites.tokenHash, tokenHash), + isNull(chatInvites.redeemedAt), + gt(chatInvites.expiresAt, now), + ), + ) + .returning({ tokenHash: chatInvites.tokenHash }) + if (!invite) throw new Error('This invite has already been used or expired.') + return invite +} diff --git a/src/routeTree.gen.ts b/src/routeTree.gen.ts index e40da438b..c86675182 100644 --- a/src/routeTree.gen.ts +++ b/src/routeTree.gen.ts @@ -56,6 +56,7 @@ import { Route as AccountNotesRouteImport } from './routes/account/notes' import { Route as AccountSubmissionsRouteImport } from './routes/account/submissions' import { Route as AdminIndexRouteImport } from './routes/admin/index' import { Route as AdminAuditRouteImport } from './routes/admin/audit' +import { Route as AdminChatAccessRouteImport } from './routes/admin/chat-access' import { Route as AdminDocsRouteImport } from './routes/admin/docs' import { Route as AdminGithubStatsRouteImport } from './routes/admin/github-stats' import { Route as AdminIntentRouteImport } from './routes/admin/intent' @@ -559,6 +560,11 @@ const AdminAuditRoute = AdminAuditRouteImport.update({ path: '/audit', getParentRoute: () => AdminRouteRoute, } as any) +const AdminChatAccessRoute = AdminChatAccessRouteImport.update({ + id: '/chat-access', + path: '/chat-access', + getParentRoute: () => AdminRouteRoute, +} as any) const AdminDocsRoute = AdminDocsRouteImport.update({ id: '/docs', path: '/docs', @@ -2044,6 +2050,7 @@ export interface FileRoutesByFullPath { '/account/notes': typeof AccountNotesRoute '/account/submissions': typeof AccountSubmissionsRoute '/admin/audit': typeof AdminAuditRoute + '/admin/chat-access': typeof AdminChatAccessRoute '/admin/docs': typeof AdminDocsRoute '/admin/github-stats': typeof AdminGithubStatsRoute '/admin/intent': typeof AdminIntentRoute @@ -2350,6 +2357,7 @@ export interface FileRoutesByTo { '/account/notes': typeof AccountNotesRoute '/account/submissions': typeof AccountSubmissionsRoute '/admin/audit': typeof AdminAuditRoute + '/admin/chat-access': typeof AdminChatAccessRoute '/admin/docs': typeof AdminDocsRoute '/admin/github-stats': typeof AdminGithubStatsRoute '/admin/intent': typeof AdminIntentRoute @@ -2663,6 +2671,7 @@ export interface FileRoutesById { '/account/notes': typeof AccountNotesRoute '/account/submissions': typeof AccountSubmissionsRoute '/admin/audit': typeof AdminAuditRoute + '/admin/chat-access': typeof AdminChatAccessRoute '/admin/docs': typeof AdminDocsRoute '/admin/github-stats': typeof AdminGithubStatsRoute '/admin/intent': typeof AdminIntentRoute @@ -2980,6 +2989,7 @@ export interface FileRouteTypes { | '/account/notes' | '/account/submissions' | '/admin/audit' + | '/admin/chat-access' | '/admin/docs' | '/admin/github-stats' | '/admin/intent' @@ -3286,6 +3296,7 @@ export interface FileRouteTypes { | '/account/notes' | '/account/submissions' | '/admin/audit' + | '/admin/chat-access' | '/admin/docs' | '/admin/github-stats' | '/admin/intent' @@ -3598,6 +3609,7 @@ export interface FileRouteTypes { | '/account/notes' | '/account/submissions' | '/admin/audit' + | '/admin/chat-access' | '/admin/docs' | '/admin/github-stats' | '/admin/intent' @@ -4351,6 +4363,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof AdminAuditRouteImport parentRoute: typeof AdminRouteRoute } + '/admin/chat-access': { + id: '/admin/chat-access' + path: '/chat-access' + fullPath: '/admin/chat-access' + preLoaderRoute: typeof AdminChatAccessRouteImport + parentRoute: typeof AdminRouteRoute + } '/admin/docs': { id: '/admin/docs' path: '/docs' @@ -6237,6 +6256,7 @@ const AdminUsersRouteWithChildren = AdminUsersRoute._addFileChildren( interface AdminRouteRouteChildren { AdminAuditRoute: typeof AdminAuditRoute + AdminChatAccessRoute: typeof AdminChatAccessRoute AdminDocsRoute: typeof AdminDocsRoute AdminGithubStatsRoute: typeof AdminGithubStatsRoute AdminIntentRoute: typeof AdminIntentRoute @@ -6255,6 +6275,7 @@ interface AdminRouteRouteChildren { const AdminRouteRouteChildren: AdminRouteRouteChildren = { AdminAuditRoute: AdminAuditRoute, + AdminChatAccessRoute: AdminChatAccessRoute, AdminDocsRoute: AdminDocsRoute, AdminGithubStatsRoute: AdminGithubStatsRoute, AdminIntentRoute: AdminIntentRoute, diff --git a/src/routes/admin/audit.tsx b/src/routes/admin/audit.tsx index 76b0b0c87..5c78e6a2b 100644 --- a/src/routes/admin/audit.tsx +++ b/src/routes/admin/audit.tsx @@ -355,9 +355,14 @@ function AuditPage() { [], ) + const tableData = useMemo( + () => auditQuery.data?.page ?? [], + [auditQuery.data?.page], + ) + // eslint-disable-next-line react-hooks/incompatible-library const table = useReactTable({ - data: (auditQuery?.data?.page || []) as AuditLogEntry[], + data: tableData, columns, getCoreRowModel: getCoreRowModel(), manualPagination: true, diff --git a/src/routes/admin/chat-access.tsx b/src/routes/admin/chat-access.tsx new file mode 100644 index 000000000..b46fc56af --- /dev/null +++ b/src/routes/admin/chat-access.tsx @@ -0,0 +1,398 @@ +import { createFileRoute } from '@tanstack/react-router' +import { + keepPreviousData, + useMutation, + useQuery, + useQueryClient, +} from '@tanstack/react-query' +import { useState } from 'react' +import { ChatsCircleIcon } from '@phosphor-icons/react' +import { + createAdminChatInvite, + expireChatInvite, + grantChatAccess, + listChatInvites, + listChatWaitlist, +} from '~/chat/access-admin.functions' +import { useAdminGuard } from '~/hooks/useAdminGuard' +import { + AdminAccessDenied, + AdminLoading, + AdminPageHeader, +} from '~/components/admin' +import { + Table, + TableHeader, + TableHeaderRow, + TableHeaderCell, + TableBody, + TableRow, + TableCell, +} from '~/components/TableComponents' +import { Button } from '~/ui' + +export const Route = createFileRoute('/admin/chat-access')({ + component: ChatAccessAdminPage, +}) + +export function ChatAccessAdminPage() { + const guard = useAdminGuard() + const queryClient = useQueryClient() + const [tab, setTab] = useState<'waitlist' | 'invites'>('waitlist') + const [page, setPage] = useState(0) + const [waitlistStatus, setWaitlistStatus] = useState< + 'waiting' | 'granted' | 'all' + >('waiting') + const [inviteStatus, setInviteStatus] = useState< + 'pending' | 'redeemed' | 'expired' | 'all' + >('pending') + const [inviteLink, setInviteLink] = useState('') + const [copied, setCopied] = useState(false) + const [copyError, setCopyError] = useState('') + const waitlistQuery = useQuery({ + queryKey: ['admin', 'chat-access', 'waitlist', page, waitlistStatus], + queryFn: () => listChatWaitlist({ data: { page, status: waitlistStatus } }), + enabled: guard.status === 'authorized' && tab === 'waitlist', + placeholderData: keepPreviousData, + }) + const invitesQuery = useQuery({ + queryKey: ['admin', 'chat-access', 'invites', page, inviteStatus], + queryFn: () => listChatInvites({ data: { page, status: inviteStatus } }), + enabled: guard.status === 'authorized' && tab === 'invites', + placeholderData: keepPreviousData, + }) + const refresh = () => + queryClient.invalidateQueries({ queryKey: ['admin', 'chat-access'] }) + const grantMutation = useMutation({ + mutationFn: (userId: string) => grantChatAccess({ data: { userId } }), + onSuccess: refresh, + }) + const expireMutation = useMutation({ + mutationFn: (tokenHash: string) => + expireChatInvite({ data: { tokenHash } }), + onSuccess: refresh, + }) + const createMutation = useMutation({ + mutationFn: () => createAdminChatInvite(), + onSuccess: async ({ token }) => { + setInviteLink( + `${window.location.origin}/chat-access?invite=${encodeURIComponent(token)}`, + ) + setCopied(false) + setCopyError('') + await refresh() + }, + }) + + if (guard.status === 'loading') return + if (guard.status === 'denied') return + + const query = tab === 'waitlist' ? waitlistQuery : invitesQuery + const error = + query.error ?? + grantMutation.error ?? + expireMutation.error ?? + createMutation.error + const invitesData = invitesQuery.data + const data = query.data + const total = data?.total ?? 0 + const pageSize = data?.pageSize ?? 25 + const empty = data?.entries.length === 0 + const inputClass = + 'rounded-lg border border-gray-300 dark:border-gray-700 bg-white dark:bg-black/30 px-3 py-2 text-sm' + + return ( +
+
+ } + title="TanChat Access" + isLoading={query.isFetching} + actions={ + + } + /> +
+
+ + +
+ {tab === 'waitlist' ? ( + + ) : ( + <> + + + + )} +
+ {inviteLink && ( +
+ +
+ event.target.select()} + /> + +
+

+ Save this link before leaving. Existing invite links cannot be + recovered. +

+ {copyError && ( +

+ {copyError} +

+ )} +
+ )} + {error && ( +

+ {error.message} +

+ )} + {tab === 'waitlist' && ( +

+ Grants shown here exclude automatic access for admins and + maintainers. +

+ )} + {query.isPending ? ( + + ) : empty ? ( +

+ {tab === 'waitlist' + ? 'No waitlist entries match this status.' + : 'No invites match this status.'} +

+ ) : tab === 'waitlist' ? ( + + + + User + Joined + Access granted + Action + + + + {waitlistQuery.data?.entries.map((entry) => ( + + + {entry.name &&
{entry.name}
} + + {entry.email} + +
+ {entry.joinedAt.toLocaleString()} + + {entry.grantedAt?.toLocaleString() ?? 'Waiting'} + + + {!entry.grantedAt && ( + + )} + +
+ ))} +
+
+ ) : ( + + + + Created by + Created + Status + Recipient + Expires + Action + + + + {invitesData && + invitesData.entries.map((entry) => { + const pending = + !entry.redeemedAt && + entry.expiresAt.getTime() > invitesData.serverTime.getTime() + return ( + + {entry.createdBy} + {entry.createdAt.toLocaleString()} + + {entry.redeemedAt + ? `Redeemed ${entry.redeemedAt.toLocaleString()}` + : pending + ? 'Pending' + : 'Expired'} + + {entry.redeemedBy ?? 'None'} + {entry.expiresAt.toLocaleString()} + + {pending && ( + + )} + + + ) + })} + +
+ )} + +
+
+ ) +} diff --git a/src/routes/admin/github-stats.tsx b/src/routes/admin/github-stats.tsx index 8a36b7f72..2a447316c 100644 --- a/src/routes/admin/github-stats.tsx +++ b/src/routes/admin/github-stats.tsx @@ -309,9 +309,11 @@ function GitHubStatsAdmin() { [refreshingKey, refreshAllMutation, refreshMutation], ) + const tableData = useMemo(() => cacheEntries ?? [], [cacheEntries]) + // eslint-disable-next-line react-hooks/incompatible-library const table = useReactTable({ - data: cacheEntries ?? [], + data: tableData, columns, getCoreRowModel: getCoreRowModel(), }) diff --git a/src/routes/admin/logins.tsx b/src/routes/admin/logins.tsx index e00e3a38f..e373ace61 100644 --- a/src/routes/admin/logins.tsx +++ b/src/routes/admin/logins.tsx @@ -253,9 +253,14 @@ function LoginsPage() { [], ) + const tableData = useMemo( + () => loginsQuery.data?.page ?? [], + [loginsQuery.data?.page], + ) + // eslint-disable-next-line react-hooks/incompatible-library const table = useReactTable({ - data: (loginsQuery?.data?.page || []) as LoginHistoryEntry[], + data: tableData, columns, getCoreRowModel: getCoreRowModel(), manualPagination: true, diff --git a/src/routes/admin/roles.$roleId.tsx b/src/routes/admin/roles.$roleId.tsx index c8a663e83..707702e1e 100644 --- a/src/routes/admin/roles.$roleId.tsx +++ b/src/routes/admin/roles.$roleId.tsx @@ -219,9 +219,11 @@ function RoleDetailPage() { [selectedUserIds, usersWithRole, toggleAllSelection, toggleUserSelection], ) + const tableData = useMemo(() => usersWithRole ?? [], [usersWithRole]) + // eslint-disable-next-line react-hooks/incompatible-library const table = useReactTable({ - data: usersWithRole || [], + data: tableData, columns, getCoreRowModel: getCoreRowModel(), }) diff --git a/src/routes/admin/roles.index.tsx b/src/routes/admin/roles.index.tsx index ded0d002f..647cb891f 100644 --- a/src/routes/admin/roles.index.tsx +++ b/src/routes/admin/roles.index.tsx @@ -384,8 +384,10 @@ function RolesPage() { ) // Create table instance + const tableData = useMemo(() => roles ?? [], [roles]) + const table = useReactTable({ - data: roles || [], + data: tableData, columns, getCoreRowModel: getCoreRowModel(), }) diff --git a/src/routes/admin/route.tsx b/src/routes/admin/route.tsx index 5dffc6c60..5123e16bd 100644 --- a/src/routes/admin/route.tsx +++ b/src/routes/admin/route.tsx @@ -67,6 +67,12 @@ export function AdminLayout({ children }: { children: React.ReactNode }) { icon: , to: '/admin', }, + { + label: 'TanChat Access', + icon: , + to: '/admin/chat-access', + requiredCapability: 'admin', + }, { label: 'Users', icon: , diff --git a/src/routes/admin/users.tsx b/src/routes/admin/users.tsx index 65177d8cb..7bd83558c 100644 --- a/src/routes/admin/users.tsx +++ b/src/routes/admin/users.tsx @@ -754,8 +754,13 @@ function UsersPage() { const canGoNext = !usersQuery?.data?.isDone // Create table instance + const tableData = useMemo( + () => usersQuery.data?.page ?? [], + [usersQuery.data?.page], + ) + const table = useReactTable({ - data: (usersQuery?.data?.page || []) as User[], + data: tableData, columns, getCoreRowModel: getCoreRowModel(), manualPagination: true, From 96405936fe6bade4ff0553c74f0740fd77ccbcda Mon Sep 17 00:00:00 2001 From: Tanner Linsley Date: Fri, 2 Oct 2026 15:07:04 -0600 Subject: [PATCH 4/4] Hide the persistent Assistant from the conversation list --- src/chat/components/BotWorkspace.tsx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/chat/components/BotWorkspace.tsx b/src/chat/components/BotWorkspace.tsx index 9c4f4be8a..75bdd3484 100644 --- a/src/chat/components/BotWorkspace.tsx +++ b/src/chat/components/BotWorkspace.tsx @@ -342,7 +342,10 @@ export function BotWorkspace({ } const allGroups = useMemo(() => { const selected = selectBotGroups({ - bots: bots.filter((bot) => bot.id !== `kody:${viewerId}`), + bots: bots.filter( + (bot) => + bot.id !== `kody:${viewerId}` && bot.id !== `assistant:${viewerId}`, + ), sections, activity, view, @@ -377,7 +380,7 @@ export function BotWorkspace({ }, ) return result - }, [bots, sections, activity, view]) + }, [bots, sections, activity, view, viewerId]) const searching = !!view.q?.trim() const pendingGroupId = view.group === 'none'