diff --git a/docs/live-visitors.md b/docs/live-visitors.md index 8c61d9046..147170e6f 100644 --- a/docs/live-visitors.md +++ b/docs/live-visitors.md @@ -7,14 +7,46 @@ function. Admin queries refresh every three seconds while the page is visible. The dashboard opens with a company grid. Individual sessions are hidden until a company is expanded, and each session has its own expandable tabs and activity. Unmatched traffic stays in one collapsed row at the bottom. Columns support -sorting, and search matches companies, industries, libraries, and page paths. +multi-column sorting with Shift-click, column visibility, row selection, +pagination, compact density, and CSV export. Search matches companies, industries, +libraries, and page paths. Library, industry, size, and funnel-stage filters can +be combined with minimum website views and recent actions. Values within a +filter match with OR, while different filters match with AND. The overview counts identified companies once per domain. Library breakdowns count a company once per library, so one company can appear in several bars. -Industry breakdowns exclude unmatched traffic. The company trend records real -polling snapshots in the open dashboard tab, up to five minutes, and resets on -reload. Grid action totals count named interactions in the last five minutes, -excluding page views. +Industry and size breakdowns exclude unmatched traffic. Clicking a breakdown +applies its filter. Size buckets use the lower bound of Scarf's employee range. +The status widget and sortable Status column classify a company as Active when +at least one online tab is visible, Background when all online tabs are hidden, +or Left when no tabs remain online. Each company occupies one status, even when +it has both visible and hidden tabs. Active describes visibility, not confirmed +attention or interaction. Status filters combine with the other company filters. +Grid action totals count named interactions in the last five minutes, excluding +page views. Pause freezes polling, including expanded sessions and history. + +## Scarf company history + +The server uses the existing `ANALYTICS_SCARF_API_TOKEN` bearer token for Scarf's +`/v3/insights/TanStack/aggregations/export` endpoint. Reporting is separate from +the lookup key and requires Scarf reporting access. Exports cover the selected +7, 30, 90, or 365 UTC calendar days, including today. Growth compares website +pixel views with the preceding equal-length window, so today's totals are +incomplete. These are window totals, not lifetime totals. + +Website views include only the site's registered Scarf pixels. Site events +come from the TanStack Site Telemetry package, including browser interactions +and server requests. Events are not added to views. Sources and endpoints are +Scarf's distinct network counts, not people. Company reports join live company +domains exactly, without guessing parent-company relationships. Missing rows or +failed reports show N/A rather than zero. No company data is automatically +unlocked and no Scarf credits are spent on enrichment. + +Successful aggregate exports are cached at the edge for 15 minutes and partial +or unavailable responses for one minute. Live presence still refreshes every +three seconds. Sorting and filtering do not request new exports. Only admins +can request reporting data, and source identifiers and credentials are never +returned to the browser. ## Enable the experiment @@ -46,9 +78,11 @@ contact, not proof that a person is looking at the page. `pageshow` handles restoration from the browser's back/forward cache. Sequence numbers prevent a late heartbeat or departure from overwriting newer tab state. -Each tab retains up to 40 named actions. The admin includes departures from the -last five minutes when requested and returns at most 500 recently seen tabs, -with a visible truncation notice. Sessions disappear from reads at 24 hours and +Each tab retains up to 40 named actions. The company overview aggregates all +eligible tabs without a 500-tab cap. Individual company sessions are fetched +only when expanded, with at most 500 eligible recently seen tabs per company +and a visible truncation notice. The admin includes departures from the last +five minutes when requested. Sessions disappear from reads at 24 hours and are deleted, with their tabs, on the next minute cleanup. ## Company lookup @@ -82,17 +116,35 @@ DNT and GPC prevent collection in both the browser and server. Browser collectio is disabled in development, staging, and outside `tanstack.com`. `isLivePage` allows public library content and selected public site routes. +Showcase submission, editing, and individual detail pages are excluded because +detail pages can include owner-only drafts. Existing records with excluded paths +are filtered out of admin responses and expire through normal retention. Account, admin, auth, chat, private builder projects, shop, and internal endpoints are excluded. Unknown paths are excluded until added to the public allowlist. Queries, fragments, clipboard text, form values, event properties, account IDs, emails, and user agents are not stored. Only explicitly named actions from the existing analytics hooks are collected; there is no arbitrary click-text capture. -Visitor IPs exist only during the server-side lookup and are not written to the -presence tables or admin responses. Lookup tracing and Sentry breadcrumbs are +Visitor IPs are used transiently for the server-side lookup and Cloudflare edge +rate-limit counters, and are not written to the application database or admin +responses. Lookup tracing and Sentry breadcrumbs are suppressed to keep IP query parameters out of telemetry. The lookup key stays server-side. There is no joining sessions by IP, fingerprint, or login. +## Intake limits + +Cloudflare edge bindings limit intake to 600 requests per IP per minute and new +company lookups to 30 per IP per minute, per Cloudflare location. These are +approximate abuse limits, not a global billing quota. Intake returns 429 before +reading a body or writing presence when limited, and 503 if the limiter is +unavailable. A lookup limit leaves presence intact with a separate rate-limited +status. Corporate networks share an IP, so these limits can affect shared +networks during large bursts. Raw IPs are not added to the database-backed MCP +rate-limit table. Presence cleanup runs independently of workflow processing, so +a cleanup failure does not prevent scheduled workflow work. + +[Cloudflare rate-limit bindings](https://developers.cloudflare.com/workers/runtime-apis/bindings/rate-limit/) + ## Validation Focused tests cover privacy signals, intake validation, public-route boundaries, diff --git a/docs/visitor-chat.md b/docs/visitor-chat.md new file mode 100644 index 000000000..fd8ab3a23 --- /dev/null +++ b/docs/visitor-chat.md @@ -0,0 +1,112 @@ +# Visitor chat + +Visitor chat is enabled in production with `VISITOR_CHAT_ENABLED=true`. +Set it to `false` to stop chat requests and invitations. + +Admins open a company, choose a browser session, and send the first message. +That message opens a small visitor chat using the sender's display name and +profile image. Existing conversations open their transcript directly. Every +admin with access to the live dashboard shares the same inbox and can reply in +the same thread. Senders are assigned from the authenticated server session, +never from browser-provided names or account IDs. No access is granted to new +users by this feature. + +## Loading and delivery + +The existing 20-second presence request carries the invitation signal. Its +response stays empty until a conversation has a committed first message. +Only then does the browser import the visitor chat module and stylesheet. +Chat-specific utilities are excluded from the initial site stylesheet and built +into deferred stylesheets for the visitor window and admin inbox. The shared admin +inbox is also a separate lazy module, opened by Chats or a session's chat action. +The bridge adds no always-open chat connection or additional idle polling loop. +A non-secret browser hint restores an existing chat on reload, including when +presence tracking is off or the visitor is on a private route. The API still +requires the private signed cookie, and the hint never grants access to messages. +The root page contains only shared feature flags, so public HTML caching cannot +cause chat resources to load for another visitor. This does not collect private +page paths. Visitors with a page loaded +before this release must reload before chat becomes +available, and the admin action explains that state. + +Invitations can take up to 20 seconds to appear. Once loaded, visitor and admin +messages poll every two seconds. The browser acknowledges the messages it +actually received, and only marks them seen while the chat is expanded in a +visible tab. These are browser signals, not proof of attention. The admin's +Chats badge and inbox show conversations whose latest message needs a reply. +Chat updates continue independently of pausing the live-company grid. + +Conversation row locks order simultaneous sends. Client message IDs make retries +idempotent. History loads in pages of 100, and older pages remain available. +Messages are plain text. Enter sends and Shift+Enter inserts a line break. +There are no attachments, email delivery, push notifications, or typing indicators +in this version. Leaving the site does not turn a message into an email. + +## Identity and retention + +A random private proof accompanies the existing anonymous presence session. The +server stores its hash. The proof is never returned in admin responses. It binds +the initial invitation to the intended browser, rather than treating a visible +session ID as permission to read messages. + +Once invited, the visitor receives a signed, HttpOnly, Secure, SameSite cookie. +It has a separate signing purpose from account cookies and lasts one year. +Reloads, tabs, and later presence sessions can resume the same conversation. +Clearing cookies, changing browsers, expiration, or signing-key rotation can +remove the visitor's connection to retained history. No IP, fingerprint, or +logged-in visitor account is used to recover it. Conversations target individual +browser sessions, not everyone at the matched company. + +Chat history remains until an admin manually deletes it. Closing a conversation +keeps its history and permits reopening. Stop chat hides the visitor interface, +mutes the conversation, and sets a browser opt-out cookie. Admins cannot reopen +a visitor-muted conversation. Minimizing the window persists across reloads and +does not mark new messages seen. The latest unread messages are counted in its +collapsed header. + +The chat tables do not depend on the 24-hour lifetime of presence records. +Sender names and avatar snapshots stay with their messages if an admin account +changes or is deleted. The company snapshot describes the network at initiation, +not a verified employer, and is shown only to admins. Manual deletion cascades +to all messages and clears presence references. + +## Boundaries + +All admin reads and writes check the current admin capability. Visitor APIs use +the signed chat cookie, not a supplied chat ID, and reject cross-site writes. +The new Cloudflare `VISITOR_CHAT_RATE_LIMITER` binding bounds requests per IP +without storing IPs in the application database. Missing or unavailable limits +fail closed. Message size and request body size are bounded. + +Chat regions are excluded from clipboard analytics, including selections that +span a chat region. Message bodies, cookies, visitor details, and database +parameters are removed from Sentry events and transactions for these endpoints. +Driver write errors are replaced with a message that contains no SQL parameters. +The chat interface does not send message content to Scarf or GA. + +## Release + +1. Apply migration `0043_visitor_chat` through the normal migration flow. +2. Deploy the site Worker with the `VISITOR_CHAT_RATE_LIMITER` binding. The existing + `SESSION_SECRET` signs the separately scoped visitor credential. +3. Verify invitation loading, two different admins in one thread, reload recovery, + manual closure/deletion, visitor opt-out, and cold-page chunk loading. +4. Enable `VISITOR_CHAT_ENABLED=true` when ready to invite production visitors. + +Local integration tests require a disposable, migrated `tanchat_test` PostgreSQL +database, and refuse a production database URL. The preview used synthetic users +and conversations and did not contact real site visitors. + +## Test your own chat + +Click **Test chat** in the live dashboard. It opens the public site in a new tab +with a test message from your admin profile, and opens the matching admin tab +labeled **Test chat ยท You**. Reply in either tab to test both sides. The visitor +tab uses the same window, history, reload recovery, and opt-out as a real chat. +Allow pop-ups for tanstack.com if the browser blocks the new tab. + +This action requires admin access. It creates a separate conversation labeled +Test chat, without waiting for Scarf or adding a synthetic presence session. +The signed visitor cookie connects only your own browser to that conversation. +Each test creates a new conversation and makes it this browser's visitor chat. +Delete the test from the admin tab when finished. Other visitors are unaffected. diff --git a/drizzle/migrations/0043_visitor_chat.sql b/drizzle/migrations/0043_visitor_chat.sql new file mode 100644 index 000000000..0ca987116 --- /dev/null +++ b/drizzle/migrations/0043_visitor_chat.sql @@ -0,0 +1,35 @@ +CREATE TABLE "visitor_chat_messages" ( + "id" uuid PRIMARY KEY NOT NULL, + "chat_id" uuid NOT NULL, + "sequence" integer NOT NULL, + "sender_id" uuid, + "sender_role" text NOT NULL, + "sender_name" text NOT NULL, + "sender_image" text, + "body" text NOT NULL, + "created_at" timestamp with time zone DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "visitor_chats" ( + "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, + "origin_session_id" uuid NOT NULL, + "visitor_proof_hash" text NOT NULL, + "company" jsonb, + "next_sequence" integer DEFAULT 0 NOT NULL, + "delivered_through" integer DEFAULT 0 NOT NULL, + "read_through" integer DEFAULT 0 NOT NULL, + "last_visitor_seen_at" timestamp with time zone, + "muted_at" timestamp with time zone, + "closed_at" timestamp with time zone, + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "updated_at" timestamp with time zone DEFAULT now() NOT NULL, + CONSTRAINT "visitor_chats_origin_session_id_unique" UNIQUE("origin_session_id") +); +--> statement-breakpoint +ALTER TABLE "live_sessions" ADD COLUMN "chat_proof_hash" text;--> statement-breakpoint +ALTER TABLE "live_sessions" ADD COLUMN "visitor_chat_id" uuid;--> statement-breakpoint +ALTER TABLE "visitor_chat_messages" ADD CONSTRAINT "visitor_chat_messages_chat_id_visitor_chats_id_fk" FOREIGN KEY ("chat_id") REFERENCES "public"."visitor_chats"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "visitor_chat_messages" ADD CONSTRAINT "visitor_chat_messages_sender_id_users_id_fk" FOREIGN KEY ("sender_id") REFERENCES "public"."users"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint +CREATE UNIQUE INDEX "visitor_chat_messages_sequence_idx" ON "visitor_chat_messages" USING btree ("chat_id","sequence");--> statement-breakpoint +CREATE INDEX "visitor_chats_updated_at_idx" ON "visitor_chats" USING btree ("updated_at");--> statement-breakpoint +ALTER TABLE "live_sessions" ADD CONSTRAINT "live_sessions_visitor_chat_id_visitor_chats_id_fk" FOREIGN KEY ("visitor_chat_id") REFERENCES "public"."visitor_chats"("id") ON DELETE set null ON UPDATE no action; \ No newline at end of file diff --git a/drizzle/migrations/meta/0043_snapshot.json b/drizzle/migrations/meta/0043_snapshot.json new file mode 100644 index 000000000..4bf14b657 --- /dev/null +++ b/drizzle/migrations/meta/0043_snapshot.json @@ -0,0 +1,11170 @@ +{ + "id": "00ad39cc-d4dd-43d3-ad23-f01085ac9d79", + "prevId": "a833fd20-cc30-4f35-ab72-0ac271d0d37f", + "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.live_sessions": { + "name": "live_sessions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "chat_proof_hash": { + "name": "chat_proof_hash", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "visitor_chat_id": { + "name": "visitor_chat_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "company": { + "name": "company", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "lookup_status": { + "name": "lookup_status", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "live_sessions_created_at_idx": { + "name": "live_sessions_created_at_idx", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "live_sessions_visitor_chat_id_visitor_chats_id_fk": { + "name": "live_sessions_visitor_chat_id_visitor_chats_id_fk", + "tableFrom": "live_sessions", + "tableTo": "visitor_chats", + "columnsFrom": ["visitor_chat_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.live_tabs": { + "name": "live_tabs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "session_id": { + "name": "session_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "page": { + "name": "page", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "visible": { + "name": "visible", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "sequence": { + "name": "sequence", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "last_seen": { + "name": "last_seen", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "left_at": { + "name": "left_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "activity": { + "name": "activity", + "type": "jsonb", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "live_tabs_last_seen_idx": { + "name": "live_tabs_last_seen_idx", + "columns": [ + { + "expression": "last_seen", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "live_tabs_session_idx": { + "name": "live_tabs_session_idx", + "columns": [ + { + "expression": "session_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "live_tabs_session_id_live_sessions_id_fk": { + "name": "live_tabs_session_id_live_sessions_id_fk", + "tableFrom": "live_tabs", + "tableTo": "live_sessions", + "columnsFrom": ["session_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "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 + }, + "public.visitor_chat_messages": { + "name": "visitor_chat_messages", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "chat_id": { + "name": "chat_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "sequence": { + "name": "sequence", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "sender_id": { + "name": "sender_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "sender_role": { + "name": "sender_role", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "sender_name": { + "name": "sender_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "sender_image": { + "name": "sender_image", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "body": { + "name": "body", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "visitor_chat_messages_sequence_idx": { + "name": "visitor_chat_messages_sequence_idx", + "columns": [ + { + "expression": "chat_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "sequence", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "visitor_chat_messages_chat_id_visitor_chats_id_fk": { + "name": "visitor_chat_messages_chat_id_visitor_chats_id_fk", + "tableFrom": "visitor_chat_messages", + "tableTo": "visitor_chats", + "columnsFrom": ["chat_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "visitor_chat_messages_sender_id_users_id_fk": { + "name": "visitor_chat_messages_sender_id_users_id_fk", + "tableFrom": "visitor_chat_messages", + "tableTo": "users", + "columnsFrom": ["sender_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.visitor_chats": { + "name": "visitor_chats", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "origin_session_id": { + "name": "origin_session_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "visitor_proof_hash": { + "name": "visitor_proof_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "company": { + "name": "company", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "next_sequence": { + "name": "next_sequence", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "delivered_through": { + "name": "delivered_through", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "read_through": { + "name": "read_through", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "last_visitor_seen_at": { + "name": "last_visitor_seen_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "muted_at": { + "name": "muted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "closed_at": { + "name": "closed_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": { + "visitor_chats_updated_at_idx": { + "name": "visitor_chats_updated_at_idx", + "columns": [ + { + "expression": "updated_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "visitor_chats_origin_session_id_unique": { + "name": "visitor_chats_origin_session_id_unique", + "nullsNotDistinct": false, + "columns": ["origin_session_id"] + } + }, + "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 46d6ebc53..5b1233674 100644 --- a/drizzle/migrations/meta/_journal.json +++ b/drizzle/migrations/meta/_journal.json @@ -302,6 +302,13 @@ "when": 1793217188347, "tag": "0042_live_visitors", "breakpoints": true + }, + { + "idx": 43, + "version": "7", + "when": 1793298817210, + "tag": "0043_visitor_chat", + "breakpoints": true } ] } diff --git a/harness-tests/pending-runtime/visitor-chat.test.ts b/harness-tests/pending-runtime/visitor-chat.test.ts new file mode 100644 index 000000000..f9e9a5eb6 --- /dev/null +++ b/harness-tests/pending-runtime/visitor-chat.test.ts @@ -0,0 +1,316 @@ +import { afterAll, expect, it } from 'vitest' +import postgres from 'postgres' +import type { AuthUser } from '../../src/auth/types' +import { runWithHostRuntimeEnv } from '../../src/server/runtime/host.server' +import { + recordLiveEvent, + pruneLiveVisitors, + receiveLiveEvent, +} from '../../src/utils/analytics/live.server' +import { type LiveEvent } from '../../src/utils/analytics/live' +import { + chatCookieHeader, + hashChatProof, +} from '../../src/utils/visitor-chat/auth.server' +import { + acknowledgeVisitorChat, + chatsNeedingReply, + createAdminTestChat, + appendMessage, + openChatForSession, + readChat, + startAdminChat, +} from '../../src/utils/visitor-chat/store.server' +import { + offerVisitorChat, + receiveVisitorChat, +} from '../../src/utils/visitor-chat/visitor.server' +import { chatStateSchema } from '../../src/utils/visitor-chat/contract' + +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( + 'Visitor chat tests require a disposable local tanchat_test database', + ) +const sql = postgres(url.href, { max: 1 }) +const admin: AuthUser = { + userId: crypto.randomUUID(), + email: 'tanner@example.invalid', + name: 'Tanner', + displayUsername: 'Tanner', + image: 'https://example.invalid/tanner.jpg', + oauthImage: null, + capabilities: ['admin'], + adsDisabled: null, + interestedInHidingAds: null, + lastUsedFramework: null, + signupSources: [], +} +const jonny: AuthUser = { + ...admin, + userId: crypto.randomUUID(), + name: 'Jonny', + displayUsername: 'Jonny', + image: 'https://example.invalid/jonny.jpg', +} +const event: LiveEvent = { + sessionId: crypto.randomUUID(), + tabId: crypto.randomUUID(), + sequence: 1, + page: '/query/latest', + visible: true, + kind: 'update', + chatProof: crypto.randomUUID(), +} +const ids: string[] = [] +const sessionIds = [event.sessionId] +const environment = { + VISITOR_CHAT_ENABLED: 'true', + LIVE_VISITORS_ENABLED: 'true', + SESSION_SECRET: 'local-chat-test-secret', + LIVE_PRESENCE_RATE_LIMITER: { limit: async () => ({ success: true }) }, + VISITOR_CHAT_RATE_LIMITER: { limit: async () => ({ success: true }) }, +} +const message = (body: string) => ({ id: crypto.randomUUID(), body }) +const visitorRequest = (cookie: string, body?: unknown) => + new Request('https://tanstack.com/_a/visitor-chat', { + method: body ? 'POST' : 'GET', + headers: { + cookie, + origin: 'https://tanstack.com', + 'cf-connecting-ip': '192.0.2.1', + 'Content-Type': 'application/json', + }, + body: body ? JSON.stringify(body) : undefined, + }) +afterAll(async () => { + for (const id of ids) await sql`DELETE FROM visitor_chats WHERE id=${id}` + for (const id of sessionIds) + await sql`DELETE FROM live_sessions WHERE id=${id}` + await sql`DELETE FROM users WHERE id IN (${admin.userId},${jonny.userId})` + await sql.end() +}) +it('keeps a private, shared, durable conversation through concurrent admins, retries, reloads and session expiry', async () => { + await runWithHostRuntimeEnv(environment, async () => { + const pendingBefore = await chatsNeedingReply() + await sql`INSERT INTO users(id) VALUES (${admin.userId}),(${jonny.userId})` + await expect( + startAdminChat(event.sessionId, message('unauthenticated')), + ).rejects.toThrow() + await expect(createAdminTestChat()).rejects.toThrow() + await recordLiveEvent(event, '192.0.2.1', undefined, undefined) + const baseline = new Request('https://tanstack.com/_a/live', { + method: 'POST', + headers: { + origin: 'https://tanstack.com', + 'cf-connecting-ip': '192.0.2.1', + }, + body: JSON.stringify({ ...event, sequence: 2 }), + }) + expect((await receiveLiveEvent(baseline)).status).toBe(204) + const [first, second] = await Promise.all([ + openChatForSession(event.sessionId, message('Tanner opening'), admin), + openChatForSession(event.sessionId, message('Jonny joining'), jonny), + ]) + expect(first.id).toBe(second.id) + ids.push(first.id) + const chatId = first.id + const announcement = await receiveLiveEvent( + new Request('https://tanstack.com/_a/live', { + method: 'POST', + headers: { + origin: 'https://tanstack.com', + 'cf-connecting-ip': '192.0.2.1', + }, + body: JSON.stringify({ ...event, sequence: 3 }), + }), + ) + expect(announcement.status).toBe(200) + expect(announcement.headers.getSetCookie()).toHaveLength(2) + expect( + announcement.headers + .getSetCookie() + .some((value) => value.startsWith('__Host-tanstack_chat_started=1;')), + ).toBe(true) + const shared = await readChat(chatId) + expect(shared.messages.map((m) => m.sequence)).toEqual([1, 2]) + expect(new Set(shared.messages.map((m) => m.senderName))).toEqual( + new Set(['Tanner', 'Jonny']), + ) + expect( + shared.messages.every((m) => + m.senderImage?.startsWith('https://example.invalid/'), + ), + ).toBe(true) + const wrongProof = { ...event, chatProof: crypto.randomUUID() } + expect( + await offerVisitorChat( + new Request('https://tanstack.com/_a/live'), + wrongProof, + false, + ), + ).toBeNull() + const offer = await offerVisitorChat( + new Request('https://tanstack.com/_a/live'), + event, + false, + ) + expect(offer?.chat).toBe(true) + const cookie = offer!.cookie!.split(';')[0] + const loaded = await receiveVisitorChat(visitorRequest(cookie)) + expect(loaded.status).toBe(200) + const body = await loaded.text() + expect(body).not.toContain('visitorProofHash') + expect(body).not.toContain(event.chatProof!) + expect(body).not.toContain(admin.email) + expect(body).not.toContain('senderId') + const reply = message('visitor reply') + const sent = await receiveVisitorChat( + visitorRequest(cookie, { + action: 'send', + message: { ...reply, senderRole: 'admin', senderName: 'Tanner' }, + chatId: crypto.randomUUID(), + }), + ) + expect(sent.status).toBe(200) + expect(await chatsNeedingReply()).toBe(pendingBefore + 1) + const sentState = chatStateSchema.parse(await sent.json()) + expect(sentState.messages.at(-1)?.senderRole).toBe('visitor') + expect(sentState.messages.at(-1)?.senderName).toBe('Visitor') + await receiveVisitorChat( + visitorRequest(cookie, { action: 'send', message: reply }), + ) + expect((await readChat(chatId)).messages).toHaveLength(3) + const next = await appendMessage(chatId, message('admin after snapshot'), { + senderRole: 'admin', + senderId: admin.userId, + senderName: 'Tanner', + senderImage: null, + }) + expect(next.messages).toHaveLength(4) + expect(await chatsNeedingReply()).toBe(pendingBefore) + await acknowledgeVisitorChat(chatId, 3, 3) + expect((await readChat(chatId)).deliveredThrough).toBe(3) + expect((await readChat(chatId)).readThrough).toBe(3) + const reloaded = chatStateSchema.parse( + await (await receiveVisitorChat(visitorRequest(cookie))).json(), + ) + expect(reloaded.messages).toHaveLength(4) + const otherId = crypto.randomUUID() + ids.push(otherId) + await sql`INSERT INTO visitor_chats(id,origin_session_id,visitor_proof_hash) VALUES (${otherId},${crypto.randomUUID()},${await hashChatProof(crypto.randomUUID())})` + const otherCookie = (await chatCookieHeader(otherId)).split(';')[0] + const otherState = chatStateSchema.parse( + await (await receiveVisitorChat(visitorRequest(otherCookie))).json(), + ) + expect(otherState.messages).toHaveLength(0) + await expect( + appendMessage( + otherId, + { id: reply.id, body: 'private-text-must-not-appear-in-errors' }, + { + senderRole: 'visitor', + senderId: null, + senderName: 'Visitor', + senderImage: null, + }, + ), + ).rejects.toThrow('Could not save the message. Please try again.') + await sql`UPDATE live_sessions SET created_at=now()-interval '2 days' WHERE id=${event.sessionId}` + await pruneLiveVisitors(Date.now()) + expect((await readChat(chatId)).messages).toHaveLength(4) + expect((await receiveVisitorChat(visitorRequest(cookie))).status).toBe(200) + const stopped = await receiveVisitorChat( + visitorRequest(cookie, { action: 'stop' }), + ) + expect(stopped.status).toBe(204) + expect( + stopped.headers + .getSetCookie() + .some((value) => value.startsWith('__Host-tanstack_chat_started=;')), + ).toBe(true) + expect(stopped.headers.get('set-cookie')).toContain( + 'visitor_chat_stopped=1', + ) + expect( + ( + await receiveVisitorChat( + visitorRequest(cookie, { + action: 'send', + message: message('blocked'), + }), + ) + ).status, + ).toBe(409) + expect( + await offerVisitorChat( + new Request('https://tanstack.com/_a/live', { headers: { cookie } }), + event, + false, + ), + ).toBeNull() + await sql`DELETE FROM visitor_chats WHERE id=${chatId}` + const [remaining] = + await sql`SELECT count(*)::int AS count FROM visitor_chat_messages WHERE chat_id=${chatId}` + expect(remaining.count).toBe(0) + expect((await receiveVisitorChat(visitorRequest(cookie))).status).toBe(404) + const fresh = { + ...event, + sessionId: crypto.randomUUID(), + tabId: crypto.randomUUID(), + chatProof: crypto.randomUUID(), + } + sessionIds.push(fresh.sessionId) + await recordLiveEvent(fresh, '192.0.2.1', undefined, undefined) + const replacement = await openChatForSession( + fresh.sessionId, + message('New conversation after deletion'), + admin, + ) + ids.push(replacement.id) + const replacementOffer = await offerVisitorChat( + new Request('https://tanstack.com/_a/live', { headers: { cookie } }), + fresh, + false, + ) + expect(replacementOffer?.cookie).toBeTruthy() + const replacementCookie = replacementOffer!.cookie!.split(';')[0] + expect( + chatStateSchema.parse( + await ( + await receiveVisitorChat(visitorRequest(replacementCookie)) + ).json(), + ).id, + ).toBe(replacement.id) + expect( + await offerVisitorChat( + new Request('https://tanstack.com/_a/live', { + headers: { + cookie: `${replacementCookie}; __Host-tanstack_visitor_chat_stopped=1`, + }, + }), + fresh, + false, + ), + ).toBeNull() + }) +}) + +it('pages through retained history without truncating it permanently', async () => { + const id = crypto.randomUUID() + ids.push(id) + await sql`INSERT INTO visitor_chats(id,origin_session_id,visitor_proof_hash,next_sequence) VALUES (${id},${crypto.randomUUID()},${await hashChatProof(crypto.randomUUID())},205)` + await sql`INSERT INTO visitor_chat_messages(id,chat_id,sequence,sender_role,sender_name,body) SELECT gen_random_uuid(),${id},n,'visitor','Visitor','Synthetic history message' FROM generate_series(1,205) n` + const recent = await readChat(id) + expect(recent.messages).toHaveLength(100) + expect(recent.messages[0].sequence).toBe(106) + expect(recent.hasOlder).toBe(true) + const middle = await readChat(id, 106) + expect(middle.messages[0].sequence).toBe(6) + const first = await readChat(id, 6) + expect(first.messages.map((m) => m.sequence)).toEqual([1, 2, 3, 4, 5]) + expect(first.hasOlder).toBe(false) +}) diff --git a/scripts/measure-r2-operations.mjs b/scripts/measure-r2-operations.mjs new file mode 100644 index 000000000..e1626ae5a --- /dev/null +++ b/scripts/measure-r2-operations.mjs @@ -0,0 +1,51 @@ +const token = process.env.CLOUDFLARE_API_TOKEN || process.env.CF_API_TOKEN +if (!token) + throw new Error( + 'Set CLOUDFLARE_API_TOKEN with Account Analytics Read permission.', + ) + +// Optional ISO timestamps let before/after samples use matching windows. +const end = process.argv[3] ?? new Date().toISOString() +const start = + process.argv[2] ?? new Date(Date.now() - 24 * 60 * 60 * 1000).toISOString() +const response = await fetch('https://api.cloudflare.com/client/v4/graphql', { + method: 'POST', + headers: { + Authorization: `Bearer ${token}`, + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + query: `query($accountTag: string!, $startDate: Time, $endDate: Time) { + viewer { + accounts(filter: { accountTag: $accountTag }) { + r2OperationsAdaptiveGroups(limit: 10000, filter: { + datetime_geq: $startDate, datetime_leq: $endDate + }) { + sum { requests } + dimensions { bucketName actionType actionStatus } + } + } + } + }`, + variables: { + accountTag: '8da95258a9c70b54c3e2b374a0079106', + startDate: start, + endDate: end, + }, + }), +}) +if (!response.ok) + throw new Error(`Cloudflare analytics returned ${response.status}`) +const result = await response.json() +if (result.errors?.length) throw new Error(JSON.stringify(result.errors)) +const buckets = new Set([ + 'tanstack-github-content-cache', + 'tanstack-npm-download-cache', + 'tanstack-notebook-projects', +]) +const operations = result.data.viewer.accounts.flatMap((account) => + account.r2OperationsAdaptiveGroups.filter((operation) => + buckets.has(operation.dimensions.bucketName), + ), +) +console.log(JSON.stringify({ start, end, operations }, null, 2)) diff --git a/src/components/ProductScarf.tsx b/src/components/ProductScarf.tsx index c37c50416..1f7054551 100644 --- a/src/components/ProductScarf.tsx +++ b/src/components/ProductScarf.tsx @@ -1,18 +1,13 @@ import { useRouterState } from '@tanstack/react-router' import { Scarf } from './Scarf' -const products = [ - { path: '/application-starter', id: 'ed471ec3-aa08-430f-8481-258b3a1f94dc' }, - { path: '/builder', id: 'aa4dcee9-86a2-49b4-9981-40c97cc1f55b' }, - { path: '/stats/npm', id: 'e729f879-e127-48e1-be19-c68f6786c9e8' }, - { path: '/intent/registry', id: '811c34af-38bd-4f06-9762-3f58dea0650a' }, -] +import { productScarfPixels } from '~/utils/analytics/scarf-pixels' export function ProductScarf() { const product = useRouterState({ select: (state) => { const pathname = state.resolvedLocation?.pathname - return products.find( + return productScarfPixels.find( ({ path }) => pathname === path || pathname?.startsWith(`${path}/`), ) }, diff --git a/src/components/admin/CompanyFacetFilter.tsx b/src/components/admin/CompanyFacetFilter.tsx new file mode 100644 index 000000000..78920ade2 --- /dev/null +++ b/src/components/admin/CompanyFacetFilter.tsx @@ -0,0 +1,64 @@ +import { useState } from 'react' +import { CaretDownIcon } from '@phosphor-icons/react' + +export function CompanyFacetFilter({ + label, + items, + selected, + onToggle, +}: { + label: string + items: { name: string; value: number }[] + selected: string[] + onToggle: (values: string[]) => void +}) { + const [search, setSearch] = useState('') + const visible = items.filter((item) => + item.name.toLowerCase().includes(search.toLowerCase()), + ) + return ( +
+ + {label} + {selected.length > 0 && ( + {selected.length} + )} + +
+ {label} + setSearch(event.target.value)} + className="mb-1 w-full rounded border border-gray-500/20 bg-transparent px-2 py-1.5 text-xs" + /> + {visible.map((item) => ( + + ))} + {!visible.length && ( +

No matching values.

+ )} +
+
+ ) +} diff --git a/src/components/admin/LiveCompanyCharts.tsx b/src/components/admin/LiveCompanyCharts.tsx index 93bc4de29..b1124d46b 100644 --- a/src/components/admin/LiveCompanyCharts.tsx +++ b/src/components/admin/LiveCompanyCharts.tsx @@ -1,158 +1,64 @@ -import { useMemo } from 'react' -import { areaY, defineChart, lineY } from '@tanstack/charts' -import { Chart } from '@tanstack/charts/react' -import { scaleLinear, scaleUtc } from 'd3' - -export type CompanySample = { time: number; companies: number } - -export function LiveCompanyTrend({ - samples, - count, -}: { - samples: CompanySample[] - count: number -}) { - const definition = useMemo(() => { - const first = samples[0]?.time ?? Date.now() - const last = Math.max(first + 1_000, samples.at(-1)?.time ?? first) - const points = samples.map((sample) => ({ - ...sample, - date: new Date(sample.time), - })) - return defineChart({ - marks: [ - areaY(points, { - x: 'date', - y1: 0, - y2: 'companies', - fill: '#10b981', - fillOpacity: 0.12, - }), - lineY(points, { - x: 'date', - y: 'companies', - stroke: '#10b981', - strokeWidth: 2, - }), - ], - scales: { - x: { - scale: scaleUtc().domain([new Date(first), new Date(last)]), - axis: { - ticks: { - count: 3, - format: (value) => - new Date(value).toLocaleTimeString([], { - minute: '2-digit', - second: '2-digit', - }), - }, - }, - }, - y: { - scale: scaleLinear() - .domain([ - 0, - Math.max(3, ...samples.map((sample) => sample.companies)), - ]) - .nice(), - grid: true, - axis: { - ticks: { - count: 3, - format: (value) => (Number.isInteger(value) ? String(value) : ''), - }, - }, - }, - }, - margin: { top: 10, right: 10, bottom: 24, left: 25 }, - theme: { - background: 'transparent', - foreground: 'currentColor', - muted: '#6b7280', - grid: 'rgba(107, 114, 128, 0.15)', - }, - }) - }, [samples]) - return ( -
-
-

- Identified companies online -

- - Since opened ยท up to 5m - -
-

- {count} -

-
- {samples.length < 2 ? ( -

- Trend starts with the next live update. -

- ) : ( - - )} -
-
- ) -} - export function CompanyBreakdownChart({ title, items, color, + selected, + onSelect, note, }: { title: string - items: { name: string; value: number }[] + items: { name: string; value: number; values: string[] }[] color: string + selected: string[] + onSelect: (values: string[]) => void note?: string }) { const maximum = Math.max(1, ...items.map((item) => item.value)) return ( -
-
-

+
+
+

{title}

- Companies + Companies
- {items.length === 0 ? ( -

- No identified companies online yet. + {!items.length ? ( +

+ No companies match.

) : ( -
    - {items.map((item) => ( -
  • -
    - - {item.name} - - {item.value} -
    -
    -
    -
    -
  • - ))} +
      + {items.map((item) => { + const active = item.values.every((value) => + selected.includes(value), + ) + return ( +
    • + +
    • + ) + })}
    )}
diff --git a/src/components/admin/LiveCompanySessions.tsx b/src/components/admin/LiveCompanySessions.tsx new file mode 100644 index 000000000..08afe85b8 --- /dev/null +++ b/src/components/admin/LiveCompanySessions.tsx @@ -0,0 +1,211 @@ +import { useQuery } from '@tanstack/react-query' +import { CaretRightIcon, CaretDownIcon } from '@phosphor-icons/react' +import { getLiveCompanySessions } from '~/utils/analytics/live.functions' +import { + groupLiveCompanies, + type LiveTab, +} from '~/utils/analytics/live-dashboard' +import { liveActions } from '~/utils/analytics/live' + +const lookupLabels: Record = { + pending: 'Looking up company', + unknown: 'No cached company match', + failed: 'Company lookup failed', + rate_limited: 'Company lookup rate limited', + unconfigured: 'Company lookup not configured', +} +function liveAge(time: Date | string | number, now: string) { + const seconds = Math.max( + 0, + Math.floor((new Date(now).getTime() - new Date(time).getTime()) / 1000), + ) + return seconds < 5 + ? 'Just now' + : seconds < 60 + ? `${seconds}s ago` + : `${Math.floor(seconds / 60)}m ago` +} +export function LiveCompanySessions({ + domain, + paused, +}: { + domain: string + paused: boolean +}) { + const query = useQuery({ + queryKey: ['admin', 'live-company-sessions', domain], + queryFn: () => getLiveCompanySessions({ data: { domain } }), + refetchInterval: paused ? false : 3000, + refetchIntervalInBackground: false, + refetchOnWindowFocus: !paused, + refetchOnReconnect: !paused, + retry: false, + }) + if (query.isPending) + return

Loading sessions...

+ if (query.isError) + return ( +
+

Could not load sessions.

+ +
+ ) + const sessions = groupLiveCompanies(query.data, true).flatMap( + (company) => company.sessions, + ) + return ( +
+ {query.data.truncated && ( +

+ Showing this company's 500 most recently seen tabs. +

+ )} + {sessions.length ? ( + sessions.map((tabs) => ( + + )) + ) : ( +

No recent sessions remain.

+ )} +
+ ) +} + +function SessionDetails({ + tabs, + now, + chatEnabled, +}: { + tabs: LiveTab[] + now: string + chatEnabled: boolean +}) { + const first = tabs[0] + const current = + tabs.find((tab) => tab.active && tab.visible) ?? + tabs.find((tab) => tab.active) ?? + first + const active = tabs.some((tab) => tab.active) + const activity = tabs + .flatMap((tab) => tab.activity) + .sort((a, b) => b.at.localeCompare(a.at)) + .slice(0, 40) + return ( +
+ + +
+
+

+ Tabs {tabs.length} +

+
    + {tabs.map((tab) => ( +
  • + + {tab.active + ? tab.visible + ? 'Visible' + : 'Background' + : 'Left'} + + {tab.page} +
  • + ))} +
+

+ Arrived {liveAge(first.arrivedAt, now)} +

+
+
+

Recent activity

+ {activity.length ? ( +
    + {activity.map((event, index) => ( +
  1. + +
    +

    {liveActions[event.action]}

    +

    + {event.page} +

    +
    +
  2. + ))} +
+ ) : ( +

No actions recorded yet.

+ )} +
+
+
+ ) +} diff --git a/src/components/admin/LiveVisitors.tsx b/src/components/admin/LiveVisitors.tsx index 47eb27b0b..21dd1944c 100644 --- a/src/components/admin/LiveVisitors.tsx +++ b/src/components/admin/LiveVisitors.tsx @@ -1,223 +1,964 @@ -import { Fragment, useEffect, useMemo, useState } from 'react' +import { Fragment, useEffect, useMemo, useRef, useState } from 'react' +import { useQuery } from '@tanstack/react-query' +import { z } from 'zod' +import { csvFormat } from 'd3' import { createSortedRowModel, + createPaginatedRowModel, rowSortingFeature, + rowPaginationFeature, + rowSelectionFeature, + columnVisibilityFeature, sortFn_alphanumeric, sortFn_basic, tableFeatures, useTable, type ColumnDef, type SortingState, + type PaginationState, + type ColumnVisibilityState, + type RowSelectionState, } from '@tanstack/react-table' import { + ArrowsDownUpIcon, CaretDownIcon, CaretRightIcon, - ArrowsDownUpIcon, MagnifyingGlassIcon, + XIcon, + DownloadSimpleIcon, + ColumnsIcon, } from '@phosphor-icons/react' -import { liveActions } from '~/utils/analytics/live' +import { getScarfCompanyHistory } from '~/utils/analytics/live.functions' +import { historyDaysSchema } from '~/utils/analytics/scarf-history' import { - companyBreakdown, - groupLiveCompanies, - type LiveCompanyRow, - type LiveData, - type LiveTab, + buildCompanyGridRows, + companyFacetCounts, + companyWidgetItems, + filterCompanyRows, + emptyCompanyFilters, + companyGrowth, + csvSafeText, + type CompanyGridRow, + type CompanyFacet, + type LiveOverview, } from '~/utils/analytics/live-dashboard' -import { - CompanyBreakdownChart, - LiveCompanyTrend, - type CompanySample, -} from './LiveCompanyCharts' +import { CompanyBreakdownChart } from './LiveCompanyCharts' +import { CompanyFacetFilter } from './CompanyFacetFilter' +import { LiveCompanySessions } from './LiveCompanySessions' const features = tableFeatures({ rowSortingFeature, sortedRowModel: createSortedRowModel(), + rowPaginationFeature, + paginatedRowModel: createPaginatedRowModel(), + rowSelectionFeature, + columnVisibilityFeature, }) -const initialSorting: SortingState = [{ id: 'name', desc: false }] -const columns: ColumnDef[] = [ - { accessorKey: 'name', header: 'Company', sortFn: sortFn_alphanumeric }, +const initialSorting: SortingState = [ + { id: 'unmatched', desc: false }, + { id: 'siteViews', desc: true }, + { id: 'actions', desc: true }, + { id: 'sessions', desc: true }, +] +const initialPagination: PaginationState = { pageIndex: 0, pageSize: 50 } +const initialVisibility: ColumnVisibilityState = { + unmatched: false, + tabs: false, + recentViews: false, + siteEvents: false, + origins: false, + endpoints: false, + lastScarfSeen: false, +} +const initialSelection: RowSelectionState = {} +const visibilitySchema = z.record(z.string(), z.boolean()) +const facets: CompanyFacet[] = [ + 'status', + 'library', + 'size', + 'industry', + 'stage', +] +const widgetFacets: CompanyFacet[] = ['status', 'library', 'size', 'industry'] +const facetLabels: Record = { + library: 'Library', + size: 'Company size', + industry: 'Industry', + stage: 'Funnel stage', + status: 'Status', +} +const number = new Intl.NumberFormat('en-US') +function numeric(value: number | undefined) { + return value === undefined ? ( + + N/A + + ) : ( + number.format(value) + ) +} +function age(value: Date, now: string) { + const seconds = Math.max( + 0, + Math.floor((new Date(now).getTime() - new Date(value).getTime()) / 1000), + ) + return seconds < 5 + ? 'Now' + : seconds < 60 + ? `${seconds}s` + : `${Math.floor(seconds / 60)}m` +} +function growthLabel(row: CompanyGridRow) { + const growth = companyGrowth(row) + if (row.history?.previousViews === 0 && (row.history.views ?? 0) > 0) + return 'New' + return growth === undefined + ? 'N/A' + : `${growth > 0 ? '+' : ''}${growth.toFixed(0)}%` +} +const columns: ColumnDef[] = [ + { + id: 'unmatched', + accessorFn: (row) => (row.id ? 0 : 1), + sortFn: sortFn_basic, + enableHiding: false, + }, + { id: 'select', header: 'Select', enableSorting: false, enableHiding: false }, + { + id: 'name', + accessorKey: 'name', + header: 'Company', + sortFn: sortFn_alphanumeric, + enableHiding: false, + }, + { + id: 'status', + accessorKey: 'status', + header: 'Status', + sortFn: sortFn_alphanumeric, + cell: ({ row }) => ( + + {row.original.status} + + ), + }, { id: 'industry', - accessorFn: (row) => row.company?.industry ?? '', + accessorKey: 'industry', header: 'Industry', sortFn: sortFn_alphanumeric, + cell: ({ row }) => ( + + {row.original.industry} + + ), }, { id: 'size', - accessorFn: (row) => row.company?.company_size ?? '', - header: 'Company size', - enableSorting: false, + accessorFn: (row) => row.employeeMin, + header: 'Employees', + sortFn: sortFn_basic, + sortUndefined: 'last', + cell: ({ row }) => row.original.company?.company_size || 'N/A', }, { id: 'libraries', accessorFn: (row) => row.libraries.join(', '), - header: 'On site', - enableSorting: false, + header: 'Libraries', + sortFn: sortFn_alphanumeric, + cell: ({ row }) => ( + + {row.original.libraries.join(', ') || 'None active'} + + ), + }, + { + id: 'sessions', + accessorKey: 'activeSessions', + header: 'Sessions', + sortFn: sortFn_basic, + cell: ({ row }) => numeric(row.original.activeSessions), + }, + { + id: 'tabs', + accessorKey: 'activeTabs', + header: 'Tabs', + sortFn: sortFn_basic, + cell: ({ row }) => numeric(row.original.activeTabs), + }, + { + id: 'actions', + accessorKey: 'actions', + header: 'Actions ยท 5m', + sortFn: sortFn_basic, + cell: ({ row }) => numeric(row.original.actions), + }, + { + id: 'recentViews', + accessorKey: 'recentViews', + header: 'Views ยท 5m', + sortFn: sortFn_basic, + cell: ({ row }) => numeric(row.original.recentViews), + }, + { + id: 'siteViews', + accessorFn: (row) => row.history?.views, + header: 'Site views', + sortFn: sortFn_basic, + sortUndefined: 'last', + cell: ({ row }) => numeric(row.original.history?.views), + }, + { + id: 'growth', + accessorFn: (row) => companyGrowth(row), + header: 'Change', + sortFn: sortFn_basic, + sortUndefined: 'last', + cell: ({ row }) => ( + 0 + ? 'text-emerald-600 dark:text-emerald-400' + : (companyGrowth(row.original) ?? 0) < 0 + ? 'text-amber-600 dark:text-amber-400' + : 'text-gray-500' + } + > + {growthLabel(row.original)} + + ), + }, + { + id: 'siteEvents', + accessorFn: (row) => row.history?.siteEvents, + header: 'Site events', + sortFn: sortFn_basic, + sortUndefined: 'last', + cell: ({ row }) => numeric(row.original.history?.siteEvents), + }, + { + id: 'origins', + accessorFn: (row) => row.history?.origins, + header: 'Sources', + sortFn: sortFn_basic, + sortUndefined: 'last', + cell: ({ row }) => numeric(row.original.history?.origins), + }, + { + id: 'endpoints', + accessorFn: (row) => row.history?.endpoints, + header: 'Endpoints', + sortFn: sortFn_basic, + sortUndefined: 'last', + cell: ({ row }) => numeric(row.original.history?.endpoints), + }, + { + id: 'stage', + accessorFn: (row) => row.history?.stage, + header: 'Funnel stage', + sortFn: sortFn_alphanumeric, + sortUndefined: 'last', + cell: ({ row }) => ( + + {row.original.history?.stage?.replaceAll('-', ' ') || 'N/A'} + + ), + }, + { + id: 'lastScarfSeen', + accessorFn: (row) => row.history?.lastSeen, + header: 'Scarf last seen', + sortFn: sortFn_alphanumeric, + sortUndefined: 'last', + cell: ({ row }) => + row.original.history?.lastSeen + ? new Date(row.original.history.lastSeen).toLocaleDateString() + : 'N/A', }, { - id: 'pages', - accessorFn: (row) => row.pages.length, - header: 'Pages now', + id: 'lastSeen', + accessorFn: (row) => new Date(row.lastSeen).getTime(), + header: 'Seen', sortFn: sortFn_basic, }, - { accessorKey: 'actions', header: 'Actions ยท 5m', sortFn: sortFn_basic }, - { accessorKey: 'lastSeen', header: 'Last seen', sortFn: sortFn_basic }, ] -const lookupLabels: Record = { - pending: 'Looking up company', - unknown: 'No cached company match', - failed: 'Company lookup failed', - unconfigured: 'Company lookup not configured', -} -function age(time: Date | string | number, now: string) { - const seconds = Math.max( - 0, - Math.floor((new Date(now).getTime() - new Date(time).getTime()) / 1000), - ) - return seconds < 5 - ? 'Just now' - : seconds < 60 - ? `${seconds}s ago` - : `${Math.floor(seconds / 60)}m ago` +const columnHelp: Record = { + status: + 'Active: at least one online tab is visible. Background: every online tab is hidden. Left: no online tabs. Visibility does not prove attention or interaction.', + sessions: 'Anonymous sessions with a heartbeat in the last 65 seconds.', + actions: + 'Recorded interactions from the last five minutes, limited to 40 retained events per tab.', + recentViews: + 'Recorded browser page-view events in the last five minutes, from retained tab activity.', + siteViews: + 'Scarf pixel views for the pixels embedded in tanstack.com, in the selected history window. Not unique people.', + growth: + 'Pixel-view change against the previous equal-length window. N/A means a comparison was not reported.', + siteEvents: + 'Events from the dedicated TanStack Site Telemetry package. Includes page views, actions, and server requests. These are not added to pixel views.', + origins: + 'Scarf unique origins across the selected site pixels. These are sources, not identified people.', + endpoints: + 'Scarf unique endpoints across the selected site pixels. These are not individual identities.', } -export function LiveVisitors({ data }: { data: LiveData }) { +export function LiveVisitors({ + data, + paused = false, +}: { + data: LiveOverview + paused?: boolean +}) { const [expanded, setExpanded] = useState(new Set()) - const [showRecent, setShowRecent] = useState(false) - const [search, setSearch] = useState('') + const [filters, setFilters] = useState(emptyCompanyFilters) const [sorting, setSorting] = useState(initialSorting) - const initialSamples: CompanySample[] = [] - const [samples, setSamples] = useState(initialSamples) - const activeCompanies = useMemo(() => groupLiveCompanies(data, false), [data]) - const count = activeCompanies.filter((company) => company.id).length + const [pagination, setPagination] = useState(initialPagination) + const [visibility, setVisibility] = useState(initialVisibility) + const [selection, setSelection] = useState(initialSelection) + const [selectedOnly, setSelectedOnly] = useState(false) + const [comfortable, setComfortable] = useState(false) + const [showBreakdowns, setShowBreakdowns] = useState(true) + const [days, setDays] = useState(historyDaysSchema.parse(30)) + const [preferencesLoaded, setPreferencesLoaded] = useState(false) + const searchInput: { current: HTMLInputElement | null } = useRef(null) + const history = useQuery({ + queryKey: ['admin', 'scarf-company-history', days], + queryFn: () => getScarfCompanyHistory({ data: { days } }), + staleTime: 15 * 60_000, + refetchInterval: paused ? false : 15 * 60_000, + refetchOnWindowFocus: false, + refetchOnReconnect: !paused, + retry: false, + }) useEffect(() => { - const time = new Date(data.now).getTime() - setSamples((previous) => - previous.at(-1)?.time === time - ? previous - : [ - ...previous.filter((sample) => sample.time >= time - 5 * 60_000), - { time, companies: count }, - ], - ) - }, [data.now, count]) - const companies = useMemo( + try { + const saved = localStorage.getItem('tanstack.live-companies.columns.v1') + if (saved) { + const parsed = visibilitySchema.safeParse(JSON.parse(saved)) + if (parsed.success) + setVisibility({ + ...initialVisibility, + ...parsed.data, + unmatched: false, + }) + } + } catch {} + setPreferencesLoaded(true) + }, []) + useEffect(() => { + if (preferencesLoaded) + try { + localStorage.setItem( + 'tanstack.live-companies.columns.v1', + JSON.stringify(visibility), + ) + } catch {} + }, [visibility, preferencesLoaded]) + useEffect(() => { + const onKey = (event: KeyboardEvent) => { + const target = event.target + if ( + event.key === '/' && + !event.metaKey && + !event.ctrlKey && + !( + target instanceof HTMLElement && + (target.matches('input,textarea,select') || target.isContentEditable) + ) + ) { + event.preventDefault() + searchInput.current?.focus() + } + } + window.addEventListener('keydown', onKey) + return () => window.removeEventListener('keydown', onKey) + }, []) + const allRows = useMemo( + () => buildCompanyGridRows(data, history.data), + [data, history.data], + ) + const filtered = useMemo( + () => filterCompanyRows(allRows, filters), + [allRows, filters], + ) + const gridRows = useMemo( () => - groupLiveCompanies(data, showRecent).filter((row) => - `${row.name} ${row.id} ${row.company?.industry ?? ''} ${row.libraries.join(' ')} ${row.sessions - .flat() - .map((tab) => tab.page) - .join(' ')}` - .toLowerCase() - .includes(search.trim().toLowerCase()), - ), - [data, showRecent, search], + selectedOnly + ? filtered.filter((row) => selection[row.id || 'unmatched']) + : filtered, + [filtered, selectedOnly, selection], ) + const activeRows = allRows.filter((row) => row.active) + const activeCount = activeRows.filter((row) => row.id).length + const summaryRows = gridRows.filter((row) => row.active) + const selectedRows = allRows.filter((row) => selection[row.id || 'unmatched']) const table = useTable({ features, columns, - data: companies, + data: gridRows, getRowId: (row) => row.id || 'unmatched', - state: { sorting }, - onSortingChange: setSorting, + state: { + sorting, + pagination, + columnVisibility: visibility, + rowSelection: selection, + }, + onSortingChange: (updater) => + setSorting((previous) => [ + { id: 'unmatched', desc: false }, + ...(typeof updater === 'function' ? updater(previous) : updater).filter( + (sort) => sort.id !== 'unmatched', + ), + ]), + onPaginationChange: setPagination, + onColumnVisibilityChange: setVisibility, + onRowSelectionChange: setSelection, + autoResetPageIndex: false, }) - const sorted = table.getRowModel().rows - const rows = [ - ...sorted.filter((row) => row.original.id), - ...sorted.filter((row) => !row.original.id), - ] - const toggle = (id: string) => + useEffect(() => { + const lastPage = Math.max( + 0, + Math.ceil(gridRows.length / pagination.pageSize) - 1, + ) + if (pagination.pageIndex > lastPage) + setPagination((previous) => ({ ...previous, pageIndex: lastPage })) + }, [gridRows.length, pagination.pageSize, pagination.pageIndex]) + useEffect(() => { + setPagination((previous) => + previous.pageIndex === 0 ? previous : { ...previous, pageIndex: 0 }, + ) + }, [filters]) + const toggleFacet = (facet: CompanyFacet, values: string[]) => { + setFilters((previous) => ({ + ...previous, + [facet]: values.every((value) => previous[facet].includes(value)) + ? previous[facet].filter((value) => !values.includes(value)) + : [...new Set([...previous[facet], ...values])], + })) + setPagination((previous) => ({ ...previous, pageIndex: 0 })) + } + const toggleCompany = (id: string) => setExpanded((previous) => { const next = new Set(previous) if (next.has(id)) next.delete(id) else next.add(id) return next }) + const filterCount = + facets.reduce((count, facet) => count + filters[facet].length, 0) + + Number(Boolean(filters.search)) + + Number(Boolean(filters.minViews)) + + Number(Boolean(filters.minActions)) + + Number(filters.identifiedOnly) + + Number(filters.showRecent) + const exportRows = () => { + const records = ( + selectedRows.length + ? selectedRows + : table.getPrePaginatedRowModel().rows.map((row) => row.original) + ).map((row) => ({ + Company: csvSafeText(row.name), + Domain: csvSafeText(row.id), + Industry: csvSafeText(row.industry), + Employees: csvSafeText(row.company?.company_size || ''), + Libraries: csvSafeText(row.libraries.join(', ')), + Sessions: row.activeSessions, + Status: row.status, + Actions5m: row.actions, + RecentViews5m: row.recentViews, + SitePixelViews: row.history?.views ?? '', + PreviousPixelViews: row.history?.previousViews ?? '', + Change: csvSafeText(growthLabel(row)), + SiteEvents: row.history?.siteEvents ?? '', + Sources: row.history?.origins ?? '', + Endpoints: row.history?.endpoints ?? '', + FunnelStage: csvSafeText(row.history?.stage || ''), + HistoryStart: history.data?.start ?? '', + HistoryEndExclusive: history.data?.end ?? '', + LastSeen: new Date(row.lastSeen).toISOString(), + })) + const url = URL.createObjectURL( + new Blob([csvFormat(records)], { type: 'text/csv;charset=utf-8' }), + ) + const link = document.createElement('a') + link.href = url + link.download = `live-companies-${new Date().toISOString().slice(0, 10)}.csv` + link.click() + URL.revokeObjectURL(url) + } + const rows = table.getRowModel().rows return ( -
+
{!data.enabled && ( -

- Visitor collection is off. +

+ Collection is off.

)} {!data.lookupConfigured && ( -

+

Company matching needs the Scarf lookup key.

)} - {data.truncated && ( -

- Overview and grid cover the 500 most recently seen tabs. -

+
+
+ + + {summaryRows.filter((row) => row.id).length} + {' '} + + companies online + + + + + {number.format( + summaryRows.reduce( + (total, row) => total + row.activeSessions, + 0, + ), + )} + {' '} + sessions + + + + {number.format( + summaryRows.reduce((total, row) => total + row.actions, 0), + )} + {' '} + actions ยท 5m + +
+
+ + +
+
+ {showBreakdowns && ( +
+ {widgetFacets.map((facet) => ( + toggleFacet(facet, values)} + note={ + facet === 'status' + ? 'Active means at least one visible tab. Background means all online tabs are hidden. Each company is counted once.' + : facet === 'size' + ? "Grouped by the lower bound of Scarf's reported employee range." + : facet === 'library' + ? 'A company can appear in multiple libraries. Other combines the remaining category counts.' + : undefined + } + /> + ))} +
)} -
- - - +
+ {history.isFetching ? ( + Loading Scarf history... + ) : history.isError ? ( + <> + + Scarf history could not load. + + + + ) : history.data?.status === 'unconfigured' ? ( + + Scarf reporting token is not configured. + + ) : history.data?.status === 'unavailable' ? ( + <> + + Scarf history unavailable. Live activity is still updating. + + + + ) : ( + history.data && ( + <> + + Scarf synced{' '} + {new Date(history.data.fetchedAt).toLocaleTimeString()} + + {history.data.status === 'partial' && ( + + Some metrics unavailable + + )} + + Pixel views and site events are separate measurements. + + + ) + )}
-
-

- Companies on site{' '} - - {companies.filter((row) => row.id).length} - -

-
- -
-
+
+
+
+ {facets.map((facet) => ( + toggleFacet(facet, values)} + /> + ))} + + +
+ +
+ + +
+ Visible columns + {table + .getAllLeafColumns() + .filter((column) => column.getCanHide()) + .map((column) => ( + + ))} +
+
+ +
-
+
+ + {filtered.filter((row) => row.id).length} /{' '} + { + allRows.filter( + (row) => row.id && (filters.showRecent || row.active), + ).length + }{' '} + companies + + + + {(selectedRows.length > 0 || selectedOnly) && ( + <> + + + + )} + {filterCount > 0 && ( + + )} +
+ {facets.some((facet) => filters[facet].length > 0) && ( +
+ {facets.flatMap((facet) => + filters[facet].map((value) => ( + + )), + )} +
+ )} +
- + {table.getHeaderGroups().map((group) => ( {group.headers.map((header) => { const sort = header.column.getIsSorted() + const index = sorting.findIndex( + (item) => item.id === header.column.id, + ) return ( - - - - - - - + + ))} {open && ( )} ) })} - {rows.length === 0 && ( + {!rows.length && ( @@ -379,137 +1152,54 @@ export function LiveVisitors({ data }: { data: LiveData }) {
- {header.column.getCanSort() ? ( + {header.column.id === 'select' ? ( + { + if (element) + element.indeterminate = + table.getIsSomePageRowsSelected() + }} + onChange={(event) => + table.toggleAllPageRowsSelected( + event.target.checked, + ) + } + className="accent-emerald-600" + /> + ) : header.column.getCanSort() ? ( @@ -262,116 +1024,127 @@ export function LiveVisitors({ data }: { data: LiveData }) { return (
- - {open ? ( - - - {company.company?.industry || 'Unknown'} - - - {company.company?.company_size || 'Unknown'} - -
- {company.libraries.slice(0, 2).map((library) => ( + + ) : cell.column.id === 'lastSeen' ? ( - {library} - - ))} - {company.libraries.length > 2 && ( - - +{company.libraries.length - 2} + {age(company.lastSeen, data.now)} + ) : ( + )} -
-
- {company.pages.length} - - {company.actions} - -
- +
+
+ {company.name} + {company.id && ( + + {company.id} + + )} + + {company.activeSessions} active sessions ยท{' '} + {company.activeTabs} tabs + + {company.company?.linkedin_url && + /^https:\/\/(www\.)?linkedin\.com\//.test( + company.company.linkedin_url, + ) && ( + + LinkedIn + + )} +
+ +
- {search - ? 'No companies or pages match your search.' + {filterCount || selectedOnly + ? 'No companies match these filters.' : 'No companies on public pages right now.'}
-
- - Company matches describe the network, not a verified employer. - - Missing heartbeats expire after 65 seconds. +
+ Network matches, not verified employers. +
+ + + + {Math.min( + pagination.pageIndex + 1, + Math.max(1, table.getPageCount()), + )}{' '} + / {Math.max(1, table.getPageCount())} + + +
) } - -function CompanySessions({ - company, - now, -}: { - company: LiveCompanyRow - now: string -}) { - return ( -
-
-

Sessions at {company.name}

- - { - company.sessions.filter((tabs) => tabs.some((tab) => tab.active)) - .length - }{' '} - active ยท {company.sessions.length} in view - -
-
- {company.sessions.map((tabs) => ( - - ))} -
-
- ) -} - -function SessionDetails({ tabs, now }: { tabs: LiveTab[]; now: string }) { - const first = tabs[0] - const current = - tabs.find((tab) => tab.active && tab.visible) ?? - tabs.find((tab) => tab.active) ?? - first - const active = tabs.some((tab) => tab.active) - const activity = tabs - .flatMap((tab) => tab.activity) - .sort((a, b) => b.at.localeCompare(a.at)) - .slice(0, 40) - return ( -
- - -
-
-

- Tabs {tabs.length} -

-
    - {tabs.map((tab) => ( -
  • - - {tab.active - ? tab.visible - ? 'Visible' - : 'Background' - : 'Left'} - - {tab.page} -
  • - ))} -
-

- Arrived {age(first.arrivedAt, now)} -

-
-
-

Recent activity

- {activity.length ? ( -
    - {activity.map((event, index) => ( -
  1. - -
    -

    {liveActions[event.action]}

    -

    - {event.page} -

    -
    -
  2. - ))} -
- ) : ( -

No actions recorded yet.

- )} -
-
-
- ) -} diff --git a/src/components/visitor-chat/AdminVisitorChats.css b/src/components/visitor-chat/AdminVisitorChats.css new file mode 100644 index 000000000..7bc9ef3f4 --- /dev/null +++ b/src/components/visitor-chat/AdminVisitorChats.css @@ -0,0 +1,35 @@ +@layer utilities { + .visitor-chat-dock { + @tailwind utilities source(none); + } +} +@reference 'tailwindcss/theme.css'; +@custom-variant dark (&:is(.dark, .dark *)); +@source './AdminVisitorChats.tsx'; + +.visitor-chat-dock { + position: fixed; + z-index: 60; + right: 12px; + bottom: 0; + display: flex; + align-items: flex-end; + gap: 8px; + max-width: calc(100vw - 24px); + overflow-x: auto; + pointer-events: none; +} + +.visitor-chat-panel { + flex: 0 0 320px; + width: 320px; + max-width: calc(100vw - 24px); + height: min(520px, calc(100dvh - 80px)); + pointer-events: auto; +} + +.visitor-chat-panel.visitor-chat-minimized { + flex-basis: 200px; + width: 200px; + height: auto; +} diff --git a/src/components/visitor-chat/AdminVisitorChats.tsx b/src/components/visitor-chat/AdminVisitorChats.tsx new file mode 100644 index 000000000..b78e399b9 --- /dev/null +++ b/src/components/visitor-chat/AdminVisitorChats.tsx @@ -0,0 +1,526 @@ +import { useEffect, useRef, useState } from 'react' +import { createPortal } from 'react-dom' +import './AdminVisitorChats.css' +import { useQuery, useQueryClient } from '@tanstack/react-query' +import { + closeVisitorChat, + deleteVisitorChat, + getVisitorChat, + getVisitorChats, + reopenVisitorChat, + sendVisitorChat, + startVisitorChat, +} from '~/utils/visitor-chat/admin.functions' +import { + chatPollMs, + chatStateSchema, + mergeChatState, + type VisitorChatState, +} from '~/utils/visitor-chat/contract' + +type ChatTarget = { sessionId: string; label: string; chatId?: string | null } + +export default function AdminVisitorChats({ + target, + request, +}: { + target: ChatTarget | null + request: number +}) { + const [tabs, setTabs] = useState((): ChatTarget[] => []) + const [minimized, setMinimized] = useState((): string[] => []) + const [inboxOpen, setInboxOpen] = useState(false) + const open = (next: ChatTarget) => { + const existing = tabs.find( + (item) => + item.sessionId === next.sessionId || + (next.chatId && item.chatId === next.chatId), + ) + setTabs((previous) => + existing + ? previous.map((item) => + item.sessionId === existing.sessionId + ? { ...item, chatId: next.chatId || item.chatId } + : item, + ) + : [...previous, next], + ) + setMinimized((previous) => + previous.filter((id) => id !== (existing?.sessionId || next.sessionId)), + ) + } + + useEffect(() => { + if (target) open(target) + else if (request > 0) setInboxOpen(true) + }, [target, request]) + return createPortal( +
+ {tabs.map((item) => ( + + setMinimized((previous) => + previous.includes(item.sessionId) + ? previous.filter((id) => id !== item.sessionId) + : [...previous, item.sessionId], + ) + } + onClose={() => + setTabs((previous) => + previous.filter((tab) => tab.sessionId !== item.sessionId), + ) + } + onOpen={open} + /> + ))} + setInboxOpen((value) => !value)} + onClose={() => setInboxOpen(false)} + onOpen={open} + /> +
, + document.body, + ) +} + +function ChatPanel({ + target, + minimized, + onToggle, + onClose, + onOpen, +}: { + target: ChatTarget | null + minimized: boolean + onToggle: () => void + onClose: () => void + onOpen: (target: ChatTarget) => void +}) { + const client = useQueryClient() + const [selected, setSelected] = useState(target?.chatId || '') + const [search, setSearch] = useState('') + const [includeClosed, setIncludeClosed] = useState(false) + const [chat, setChat] = useState((): VisitorChatState | undefined => { + const cached = chatStateSchema.safeParse( + client.getQueryData(['admin', 'visitor-chat', selected]), + ) + return cached.success ? cached.data : undefined + }) + const [draft, setDraft] = useState('') + const [sending, setSending] = useState(false) + const [error, setError] = useState('') + const pending: { current: { id: string; body: string } | undefined } = + useRef(undefined) + const tail: { current: HTMLDivElement | null } = useRef(null) + const inbox = useQuery({ + queryKey: ['admin', 'visitor-chats', search, includeClosed], + queryFn: () => getVisitorChats({ data: { search, includeClosed } }), + enabled: !target && !minimized, + refetchInterval: chatPollMs, + retry: false, + }) + const thread = useQuery({ + queryKey: ['admin', 'visitor-chat', selected], + queryFn: () => getVisitorChat({ data: { chatId: selected } }), + enabled: Boolean(selected), + refetchInterval: chatPollMs, + retry: false, + }) + useEffect(() => { + setChat(undefined) + setDraft('') + setError('') + pending.current = undefined + }, [selected]) + useEffect(() => { + const next = thread.data + if (next) setChat((previous) => mergeChatState(previous, next)) + else if (thread.isSuccess && next === null) { + setChat(undefined) + onClose() + void client.invalidateQueries({ queryKey: ['admin', 'visitor-chats'] }) + } + }, [thread.data, thread.isSuccess, client]) + const lastSequence = chat?.messages.at(-1)?.sequence + useEffect(() => { + tail.current?.scrollIntoView({ block: 'nearest' }) + }, [lastSequence]) + const send = async () => { + const body = draft.trim() + if (!body || sending || (!selected && !target)) return + const message = + pending.current?.body === body + ? pending.current + : { id: crypto.randomUUID(), body } + pending.current = message + setSending(true) + setError('') + try { + const next = selected + ? await sendVisitorChat({ data: { chatId: selected, message } }) + : target + ? await startVisitorChat({ + data: { sessionId: target.sessionId, message }, + }) + : undefined + if (next) { + if (target) onOpen({ ...target, chatId: next.id }) + setSelected(next.id) + setChat((previous) => mergeChatState(previous, next)) + client.setQueryData(['admin', 'visitor-chat', next.id], next) + } + setDraft('') + pending.current = undefined + void client.invalidateQueries({ queryKey: ['admin', 'visitor-chats'] }) + } catch (error) { + setError( + error instanceof Error ? error.message : 'Could not send. Try again.', + ) + } finally { + setSending(false) + } + } + const label = selected + ? target?.label || `Visitor ${selected.slice(0, 8)}` + : target?.label || 'Select a chat' + return ( +