From 43693d41aff3716f3d89e88e278dcb854256755a Mon Sep 17 00:00:00 2001 From: conoremclaughlin Date: Sun, 15 Feb 2026 01:14:12 -0800 Subject: [PATCH 1/2] feat: thread-bound sessions via threadKey for cross-agent continuity When an agent gets triggered to review PR #32, there's no way to route them to the session where they previously reviewed it. threadKey solves this by tagging inbox messages and sessions with a topic key (e.g., "pr:32"), enabling automatic session matching across triggers. - Migration: add thread_key column to sessions and agent_inbox tables with partial indexes for fast active-session lookups - Repository: new getActiveSessionByThreadKey() method, thread_key in startSession insert and rowToSession mapping - Session handlers: threadKey matching priority in start_session (threadKey > studioId > default), threadKey in bootstrap activeSessions - Inbox handlers: threadKey in send_to_inbox schema/insert/response, soft hint when threadKey is missing, threadKey in get_inbox mapping - Trigger handlers: threadKey passthrough in trigger_agent schema/payload - Gateway types: threadKey on AgentTriggerPayload - Tests: 16 new unit tests covering schema, matching priority, fallback, and inbox threadKey behavior Co-Authored-By: Claude Opus 4.6 --- packages/api/src/channels/agent-gateway.ts | 2 + packages/api/src/data/models/memory.ts | 3 + .../data/repositories/memory-repository.ts | 33 + packages/api/src/data/supabase/types.ts | 5176 +++++++++-------- packages/api/src/mcp/tools/agent-triggers.ts | 5 + .../api/src/mcp/tools/inbox-handlers.test.ts | 235 + packages/api/src/mcp/tools/inbox-handlers.ts | 16 + packages/api/src/mcp/tools/index.ts | 14 +- .../api/src/mcp/tools/memory-handlers.test.ts | 226 + packages/api/src/mcp/tools/memory-handlers.ts | 37 +- 10 files changed, 3183 insertions(+), 2564 deletions(-) create mode 100644 packages/api/src/mcp/tools/inbox-handlers.test.ts diff --git a/packages/api/src/channels/agent-gateway.ts b/packages/api/src/channels/agent-gateway.ts index 3e2d93c1..6be41620 100644 --- a/packages/api/src/channels/agent-gateway.ts +++ b/packages/api/src/channels/agent-gateway.ts @@ -28,6 +28,8 @@ export interface AgentTriggerPayload { summary?: string; /** Priority level */ priority?: 'low' | 'normal' | 'high' | 'urgent'; + /** Thread key for session routing (e.g., "pr:32") */ + threadKey?: string; /** Additional metadata */ metadata?: Record; } diff --git a/packages/api/src/data/models/memory.ts b/packages/api/src/data/models/memory.ts index 29d6bff0..f769f47e 100644 --- a/packages/api/src/data/models/memory.ts +++ b/packages/api/src/data/models/memory.ts @@ -82,6 +82,7 @@ export interface Session { * @deprecated Use studioId. Kept for backward compatibility during migration. */ workspaceId?: string; + threadKey?: string; currentPhase?: string; startedAt: Date; endedAt?: Date; @@ -97,6 +98,7 @@ export interface SessionCreateInput { * @deprecated Use studioId. Kept for backward compatibility during migration. */ workspaceId?: string; + threadKey?: string; metadata?: Record; } @@ -151,6 +153,7 @@ export interface SessionRow { agent_id: string | null; studio_id: string | null; workspace_id: string | null; + thread_key: string | null; current_phase: string | null; started_at: string; ended_at: string | null; diff --git a/packages/api/src/data/repositories/memory-repository.ts b/packages/api/src/data/repositories/memory-repository.ts index 2fa2fb3b..1717fa23 100644 --- a/packages/api/src/data/repositories/memory-repository.ts +++ b/packages/api/src/data/repositories/memory-repository.ts @@ -198,6 +198,9 @@ export class MemoryRepository { // Backward compatibility for older server versions still reading workspace_id. insertData.workspace_id = scopedStudioId; } + if (input.threadKey) { + insertData.thread_key = input.threadKey; + } const { data, error } = await this.supabase .from('sessions') @@ -345,6 +348,35 @@ export class MemoryRepository { return data ? this.rowToSession(data) : null; } + /** + * Get active session by threadKey for a user+agent. + * Returns the most recent active session with a matching thread_key, or null. + */ + async getActiveSessionByThreadKey( + userId: string, + agentId: string, + threadKey: string + ): Promise { + const { data, error } = await this.supabase + .from('sessions') + .select('*') + .eq('user_id', userId) + .eq('agent_id', agentId) + .eq('thread_key', threadKey) + .is('ended_at', null) + .order('started_at', { ascending: false }) + .limit(1) + .single(); + + if (error) { + if (error.code === 'PGRST116') return null; + logger.error('Failed to get active session by threadKey:', error); + throw new Error(`Failed to get active session by threadKey: ${error.message}`); + } + + return data ? this.rowToSession(data) : null; + } + /** * Get all active sessions for a user (without ended_at), ordered most recent first. * Used by bootstrap to return all active sessions so the client can pick the right one. @@ -705,6 +737,7 @@ export class MemoryRepository { agentId: row.agent_id || undefined, studioId, workspaceId: studioId, + threadKey: row.thread_key || undefined, currentPhase: row.current_phase || undefined, startedAt: new Date(row.started_at), endedAt: row.ended_at ? new Date(row.ended_at) : undefined, diff --git a/packages/api/src/data/supabase/types.ts b/packages/api/src/data/supabase/types.ts index ca94d5ef..4e3a2384 100644 --- a/packages/api/src/data/supabase/types.ts +++ b/packages/api/src/data/supabase/types.ts @@ -1,3064 +1,3130 @@ -export type Json = string | number | boolean | null | { [key: string]: Json | undefined } | Json[]; +export type Json = + | string + | number + | boolean + | null + | { [key: string]: Json | undefined } + | Json[] export type Database = { // Allows to automatically instantiate createClient with right options // instead of createClient(URL, KEY) __InternalSupabase: { - PostgrestVersion: '13.0.5'; - }; + PostgrestVersion: "13.0.5" + } public: { Tables: { activity_stream: { Row: { - agent_id: string; - artifact_id: string | null; - child_session_id: string | null; - completed_at: string | null; - contact_id: string | null; - content: string; - correlation_id: string | null; - created_at: string; - duration_ms: number | null; - id: string; - is_dm: boolean | null; - parent_id: string | null; - payload: Json; - platform: string | null; - platform_chat_id: string | null; - platform_message_id: string | null; - session_id: string | null; - status: string | null; - subtype: string | null; - type: Database['public']['Enums']['activity_type']; - user_id: string; - }; + agent_id: string + artifact_id: string | null + child_session_id: string | null + completed_at: string | null + contact_id: string | null + content: string + correlation_id: string | null + created_at: string + duration_ms: number | null + id: string + identity_id: string | null + is_dm: boolean | null + parent_id: string | null + payload: Json + platform: string | null + platform_chat_id: string | null + platform_message_id: string | null + session_id: string | null + status: string | null + subtype: string | null + type: Database["public"]["Enums"]["activity_type"] + user_id: string + } Insert: { - agent_id: string; - artifact_id?: string | null; - child_session_id?: string | null; - completed_at?: string | null; - contact_id?: string | null; - content: string; - correlation_id?: string | null; - created_at?: string; - duration_ms?: number | null; - id?: string; - is_dm?: boolean | null; - parent_id?: string | null; - payload?: Json; - platform?: string | null; - platform_chat_id?: string | null; - platform_message_id?: string | null; - session_id?: string | null; - status?: string | null; - subtype?: string | null; - type: Database['public']['Enums']['activity_type']; - user_id: string; - }; + agent_id: string + artifact_id?: string | null + child_session_id?: string | null + completed_at?: string | null + contact_id?: string | null + content: string + correlation_id?: string | null + created_at?: string + duration_ms?: number | null + id?: string + identity_id?: string | null + is_dm?: boolean | null + parent_id?: string | null + payload?: Json + platform?: string | null + platform_chat_id?: string | null + platform_message_id?: string | null + session_id?: string | null + status?: string | null + subtype?: string | null + type: Database["public"]["Enums"]["activity_type"] + user_id: string + } Update: { - agent_id?: string; - artifact_id?: string | null; - child_session_id?: string | null; - completed_at?: string | null; - contact_id?: string | null; - content?: string; - correlation_id?: string | null; - created_at?: string; - duration_ms?: number | null; - id?: string; - is_dm?: boolean | null; - parent_id?: string | null; - payload?: Json; - platform?: string | null; - platform_chat_id?: string | null; - platform_message_id?: string | null; - session_id?: string | null; - status?: string | null; - subtype?: string | null; - type?: Database['public']['Enums']['activity_type']; - user_id?: string; - }; + agent_id?: string + artifact_id?: string | null + child_session_id?: string | null + completed_at?: string | null + contact_id?: string | null + content?: string + correlation_id?: string | null + created_at?: string + duration_ms?: number | null + id?: string + identity_id?: string | null + is_dm?: boolean | null + parent_id?: string | null + payload?: Json + platform?: string | null + platform_chat_id?: string | null + platform_message_id?: string | null + session_id?: string | null + status?: string | null + subtype?: string | null + type?: Database["public"]["Enums"]["activity_type"] + user_id?: string + } Relationships: [ { - foreignKeyName: 'activity_stream_contact_id_fkey'; - columns: ['contact_id']; - isOneToOne: false; - referencedRelation: 'contacts'; - referencedColumns: ['id']; + foreignKeyName: "activity_stream_contact_id_fkey" + columns: ["contact_id"] + isOneToOne: false + referencedRelation: "contacts" + referencedColumns: ["id"] + }, + { + foreignKeyName: "activity_stream_identity_id_fkey" + columns: ["identity_id"] + isOneToOne: false + referencedRelation: "agent_identities" + referencedColumns: ["id"] }, { - foreignKeyName: 'activity_stream_parent_id_fkey'; - columns: ['parent_id']; - isOneToOne: false; - referencedRelation: 'activity_stream'; - referencedColumns: ['id']; + foreignKeyName: "activity_stream_parent_id_fkey" + columns: ["parent_id"] + isOneToOne: false + referencedRelation: "activity_stream" + referencedColumns: ["id"] }, { - foreignKeyName: 'activity_stream_session_id_fkey'; - columns: ['session_id']; - isOneToOne: false; - referencedRelation: 'sessions'; - referencedColumns: ['id']; + foreignKeyName: "activity_stream_session_id_fkey" + columns: ["session_id"] + isOneToOne: false + referencedRelation: "sessions" + referencedColumns: ["id"] }, { - foreignKeyName: 'activity_stream_user_id_fkey'; - columns: ['user_id']; - isOneToOne: false; - referencedRelation: 'users'; - referencedColumns: ['id']; + foreignKeyName: "activity_stream_user_id_fkey" + columns: ["user_id"] + isOneToOne: false + referencedRelation: "users" + referencedColumns: ["id"] }, - ]; - }; + ] + } agent_identities: { Row: { - agent_id: string; - backend: string | null; - capabilities: Json | null; - created_at: string | null; - description: string | null; - heartbeat: string | null; - id: string; - metadata: Json | null; - name: string; - relationships: Json | null; - role: string; - soul: string | null; - updated_at: string | null; - user_id: string; - values: Json | null; - version: number | null; - workspace_id: string | null; - }; + agent_id: string + backend: string | null + capabilities: Json | null + created_at: string | null + description: string | null + heartbeat: string | null + id: string + metadata: Json | null + name: string + relationships: Json | null + role: string + soul: string | null + updated_at: string | null + user_id: string + values: Json | null + version: number | null + workspace_id: string | null + } Insert: { - agent_id: string; - backend?: string | null; - capabilities?: Json | null; - created_at?: string | null; - description?: string | null; - heartbeat?: string | null; - id?: string; - metadata?: Json | null; - name: string; - relationships?: Json | null; - role: string; - soul?: string | null; - updated_at?: string | null; - user_id: string; - values?: Json | null; - version?: number | null; - workspace_id?: string | null; - }; + agent_id: string + backend?: string | null + capabilities?: Json | null + created_at?: string | null + description?: string | null + heartbeat?: string | null + id?: string + metadata?: Json | null + name: string + relationships?: Json | null + role: string + soul?: string | null + updated_at?: string | null + user_id: string + values?: Json | null + version?: number | null + workspace_id?: string | null + } Update: { - agent_id?: string; - backend?: string | null; - capabilities?: Json | null; - created_at?: string | null; - description?: string | null; - heartbeat?: string | null; - id?: string; - metadata?: Json | null; - name?: string; - relationships?: Json | null; - role?: string; - soul?: string | null; - updated_at?: string | null; - user_id?: string; - values?: Json | null; - version?: number | null; - workspace_id?: string | null; - }; + agent_id?: string + backend?: string | null + capabilities?: Json | null + created_at?: string | null + description?: string | null + heartbeat?: string | null + id?: string + metadata?: Json | null + name?: string + relationships?: Json | null + role?: string + soul?: string | null + updated_at?: string | null + user_id?: string + values?: Json | null + version?: number | null + workspace_id?: string | null + } Relationships: [ { - foreignKeyName: 'agent_identities_user_id_fkey'; - columns: ['user_id']; - isOneToOne: false; - referencedRelation: 'users'; - referencedColumns: ['id']; + foreignKeyName: "agent_identities_user_id_fkey" + columns: ["user_id"] + isOneToOne: false + referencedRelation: "users" + referencedColumns: ["id"] }, { - foreignKeyName: 'agent_identities_workspace_id_fkey'; - columns: ['workspace_id']; - isOneToOne: false; - referencedRelation: 'workspace_containers'; - referencedColumns: ['id']; + foreignKeyName: "agent_identities_workspace_id_fkey" + columns: ["workspace_id"] + isOneToOne: false + referencedRelation: "workspace_containers" + referencedColumns: ["id"] }, - ]; - }; + ] + } agent_identity_history: { Row: { - agent_id: string; - archived_at: string | null; - backend: string | null; - capabilities: Json | null; - change_type: string; - created_at: string; - description: string | null; - heartbeat: string | null; - id: string; - identity_id: string; - metadata: Json | null; - name: string; - relationships: Json | null; - role: string; - soul: string | null; - user_id: string; - values: Json | null; - version: number; - workspace_id: string | null; - }; + agent_id: string + archived_at: string | null + backend: string | null + capabilities: Json | null + change_type: string + created_at: string + description: string | null + heartbeat: string | null + id: string + identity_id: string + metadata: Json | null + name: string + relationships: Json | null + role: string + soul: string | null + user_id: string + values: Json | null + version: number + workspace_id: string | null + } Insert: { - agent_id: string; - archived_at?: string | null; - backend?: string | null; - capabilities?: Json | null; - change_type?: string; - created_at: string; - description?: string | null; - heartbeat?: string | null; - id?: string; - identity_id: string; - metadata?: Json | null; - name: string; - relationships?: Json | null; - role: string; - soul?: string | null; - user_id: string; - values?: Json | null; - version: number; - workspace_id?: string | null; - }; + agent_id: string + archived_at?: string | null + backend?: string | null + capabilities?: Json | null + change_type?: string + created_at: string + description?: string | null + heartbeat?: string | null + id?: string + identity_id: string + metadata?: Json | null + name: string + relationships?: Json | null + role: string + soul?: string | null + user_id: string + values?: Json | null + version: number + workspace_id?: string | null + } Update: { - agent_id?: string; - archived_at?: string | null; - backend?: string | null; - capabilities?: Json | null; - change_type?: string; - created_at?: string; - description?: string | null; - heartbeat?: string | null; - id?: string; - identity_id?: string; - metadata?: Json | null; - name?: string; - relationships?: Json | null; - role?: string; - soul?: string | null; - user_id?: string; - values?: Json | null; - version?: number; - workspace_id?: string | null; - }; + agent_id?: string + archived_at?: string | null + backend?: string | null + capabilities?: Json | null + change_type?: string + created_at?: string + description?: string | null + heartbeat?: string | null + id?: string + identity_id?: string + metadata?: Json | null + name?: string + relationships?: Json | null + role?: string + soul?: string | null + user_id?: string + values?: Json | null + version?: number + workspace_id?: string | null + } Relationships: [ { - foreignKeyName: 'agent_identity_history_user_id_fkey'; - columns: ['user_id']; - isOneToOne: false; - referencedRelation: 'users'; - referencedColumns: ['id']; + foreignKeyName: "agent_identity_history_user_id_fkey" + columns: ["user_id"] + isOneToOne: false + referencedRelation: "users" + referencedColumns: ["id"] }, { - foreignKeyName: 'agent_identity_history_workspace_id_fkey'; - columns: ['workspace_id']; - isOneToOne: false; - referencedRelation: 'workspace_containers'; - referencedColumns: ['id']; + foreignKeyName: "agent_identity_history_workspace_id_fkey" + columns: ["workspace_id"] + isOneToOne: false + referencedRelation: "workspace_containers" + referencedColumns: ["id"] }, - ]; - }; + ] + } agent_inbox: { Row: { - acknowledged_at: string | null; - content: string; - created_at: string | null; - expires_at: string | null; - id: string; - message_type: string; - metadata: Json | null; - priority: string; - read_at: string | null; - recipient_agent_id: string; - recipient_user_id: string; - related_artifact_uri: string | null; - related_session_id: string | null; - sender_agent_id: string | null; - sender_user_id: string | null; - status: string; - subject: string | null; - }; + acknowledged_at: string | null + content: string + created_at: string | null + expires_at: string | null + id: string + message_type: string + metadata: Json | null + priority: string + read_at: string | null + recipient_agent_id: string + recipient_identity_id: string | null + recipient_user_id: string + related_artifact_uri: string | null + related_session_id: string | null + sender_agent_id: string | null + sender_identity_id: string | null + sender_user_id: string | null + status: string + subject: string | null + thread_key: string | null + } Insert: { - acknowledged_at?: string | null; - content: string; - created_at?: string | null; - expires_at?: string | null; - id?: string; - message_type?: string; - metadata?: Json | null; - priority?: string; - read_at?: string | null; - recipient_agent_id: string; - recipient_user_id: string; - related_artifact_uri?: string | null; - related_session_id?: string | null; - sender_agent_id?: string | null; - sender_user_id?: string | null; - status?: string; - subject?: string | null; - }; + acknowledged_at?: string | null + content: string + created_at?: string | null + expires_at?: string | null + id?: string + message_type?: string + metadata?: Json | null + priority?: string + read_at?: string | null + recipient_agent_id: string + recipient_identity_id?: string | null + recipient_user_id: string + related_artifact_uri?: string | null + related_session_id?: string | null + sender_agent_id?: string | null + sender_identity_id?: string | null + sender_user_id?: string | null + status?: string + subject?: string | null + thread_key?: string | null + } Update: { - acknowledged_at?: string | null; - content?: string; - created_at?: string | null; - expires_at?: string | null; - id?: string; - message_type?: string; - metadata?: Json | null; - priority?: string; - read_at?: string | null; - recipient_agent_id?: string; - recipient_user_id?: string; - related_artifact_uri?: string | null; - related_session_id?: string | null; - sender_agent_id?: string | null; - sender_user_id?: string | null; - status?: string; - subject?: string | null; - }; + acknowledged_at?: string | null + content?: string + created_at?: string | null + expires_at?: string | null + id?: string + message_type?: string + metadata?: Json | null + priority?: string + read_at?: string | null + recipient_agent_id?: string + recipient_identity_id?: string | null + recipient_user_id?: string + related_artifact_uri?: string | null + related_session_id?: string | null + sender_agent_id?: string | null + sender_identity_id?: string | null + sender_user_id?: string | null + status?: string + subject?: string | null + thread_key?: string | null + } Relationships: [ { - foreignKeyName: 'agent_inbox_recipient_user_id_fkey'; - columns: ['recipient_user_id']; - isOneToOne: false; - referencedRelation: 'users'; - referencedColumns: ['id']; + foreignKeyName: "agent_inbox_recipient_identity_id_fkey" + columns: ["recipient_identity_id"] + isOneToOne: false + referencedRelation: "agent_identities" + referencedColumns: ["id"] + }, + { + foreignKeyName: "agent_inbox_recipient_user_id_fkey" + columns: ["recipient_user_id"] + isOneToOne: false + referencedRelation: "users" + referencedColumns: ["id"] + }, + { + foreignKeyName: "agent_inbox_related_session_id_fkey" + columns: ["related_session_id"] + isOneToOne: false + referencedRelation: "sessions" + referencedColumns: ["id"] }, { - foreignKeyName: 'agent_inbox_related_session_id_fkey'; - columns: ['related_session_id']; - isOneToOne: false; - referencedRelation: 'sessions'; - referencedColumns: ['id']; + foreignKeyName: "agent_inbox_sender_identity_id_fkey" + columns: ["sender_identity_id"] + isOneToOne: false + referencedRelation: "agent_identities" + referencedColumns: ["id"] }, { - foreignKeyName: 'agent_inbox_sender_user_id_fkey'; - columns: ['sender_user_id']; - isOneToOne: false; - referencedRelation: 'users'; - referencedColumns: ['id']; + foreignKeyName: "agent_inbox_sender_user_id_fkey" + columns: ["sender_user_id"] + isOneToOne: false + referencedRelation: "users" + referencedColumns: ["id"] }, - ]; - }; + ] + } agent_sessions: { Row: { - backend: string; - created_at: string; - ended_at: string | null; - id: string; - last_activity_at: string; - mcp_config_path: string | null; - message_count: number | null; - model: string | null; - platform: string | null; - platform_chat_id: string | null; - session_id: string; - session_key: string | null; - status: string; - total_cost: number | null; - updated_at: string; - user_id: string; - working_directory: string | null; - }; + backend: string + created_at: string + ended_at: string | null + id: string + last_activity_at: string + mcp_config_path: string | null + message_count: number | null + model: string | null + platform: string | null + platform_chat_id: string | null + session_id: string + session_key: string | null + status: string + total_cost: number | null + updated_at: string + user_id: string + working_directory: string | null + } Insert: { - backend?: string; - created_at?: string; - ended_at?: string | null; - id?: string; - last_activity_at?: string; - mcp_config_path?: string | null; - message_count?: number | null; - model?: string | null; - platform?: string | null; - platform_chat_id?: string | null; - session_id: string; - session_key?: string | null; - status?: string; - total_cost?: number | null; - updated_at?: string; - user_id: string; - working_directory?: string | null; - }; + backend?: string + created_at?: string + ended_at?: string | null + id?: string + last_activity_at?: string + mcp_config_path?: string | null + message_count?: number | null + model?: string | null + platform?: string | null + platform_chat_id?: string | null + session_id: string + session_key?: string | null + status?: string + total_cost?: number | null + updated_at?: string + user_id: string + working_directory?: string | null + } Update: { - backend?: string; - created_at?: string; - ended_at?: string | null; - id?: string; - last_activity_at?: string; - mcp_config_path?: string | null; - message_count?: number | null; - model?: string | null; - platform?: string | null; - platform_chat_id?: string | null; - session_id?: string; - session_key?: string | null; - status?: string; - total_cost?: number | null; - updated_at?: string; - user_id?: string; - working_directory?: string | null; - }; + backend?: string + created_at?: string + ended_at?: string | null + id?: string + last_activity_at?: string + mcp_config_path?: string | null + message_count?: number | null + model?: string | null + platform?: string | null + platform_chat_id?: string | null + session_id?: string + session_key?: string | null + status?: string + total_cost?: number | null + updated_at?: string + user_id?: string + working_directory?: string | null + } Relationships: [ { - foreignKeyName: 'agent_sessions_user_id_fkey'; - columns: ['user_id']; - isOneToOne: false; - referencedRelation: 'users'; - referencedColumns: ['id']; + foreignKeyName: "agent_sessions_user_id_fkey" + columns: ["user_id"] + isOneToOne: false + referencedRelation: "users" + referencedColumns: ["id"] }, - ]; - }; + ] + } artifact_comments: { Row: { - artifact_id: string; - content: string; - created_at: string | null; - created_by_identity_id: string | null; - created_by_user_id: string | null; - deleted_at: string | null; - id: string; - metadata: Json | null; - parent_comment_id: string | null; - updated_at: string | null; - user_id: string; - workspace_id: string | null; - }; + artifact_id: string + content: string + created_at: string | null + created_by_identity_id: string | null + created_by_user_id: string | null + deleted_at: string | null + id: string + metadata: Json | null + parent_comment_id: string | null + updated_at: string | null + user_id: string + workspace_id: string | null + } Insert: { - artifact_id: string; - content: string; - created_at?: string | null; - created_by_identity_id?: string | null; - created_by_user_id?: string | null; - deleted_at?: string | null; - id?: string; - metadata?: Json | null; - parent_comment_id?: string | null; - updated_at?: string | null; - user_id: string; - workspace_id?: string | null; - }; + artifact_id: string + content: string + created_at?: string | null + created_by_identity_id?: string | null + created_by_user_id?: string | null + deleted_at?: string | null + id?: string + metadata?: Json | null + parent_comment_id?: string | null + updated_at?: string | null + user_id: string + workspace_id?: string | null + } Update: { - artifact_id?: string; - content?: string; - created_at?: string | null; - created_by_identity_id?: string | null; - created_by_user_id?: string | null; - deleted_at?: string | null; - id?: string; - metadata?: Json | null; - parent_comment_id?: string | null; - updated_at?: string | null; - user_id?: string; - workspace_id?: string | null; - }; + artifact_id?: string + content?: string + created_at?: string | null + created_by_identity_id?: string | null + created_by_user_id?: string | null + deleted_at?: string | null + id?: string + metadata?: Json | null + parent_comment_id?: string | null + updated_at?: string | null + user_id?: string + workspace_id?: string | null + } Relationships: [ { - foreignKeyName: 'artifact_comments_artifact_id_fkey'; - columns: ['artifact_id']; - isOneToOne: false; - referencedRelation: 'artifacts'; - referencedColumns: ['id']; + foreignKeyName: "artifact_comments_artifact_id_fkey" + columns: ["artifact_id"] + isOneToOne: false + referencedRelation: "artifacts" + referencedColumns: ["id"] }, { - foreignKeyName: 'artifact_comments_created_by_identity_id_fkey'; - columns: ['created_by_identity_id']; - isOneToOne: false; - referencedRelation: 'agent_identities'; - referencedColumns: ['id']; + foreignKeyName: "artifact_comments_created_by_identity_id_fkey" + columns: ["created_by_identity_id"] + isOneToOne: false + referencedRelation: "agent_identities" + referencedColumns: ["id"] }, { - foreignKeyName: 'artifact_comments_created_by_user_id_fkey'; - columns: ['created_by_user_id']; - isOneToOne: false; - referencedRelation: 'users'; - referencedColumns: ['id']; + foreignKeyName: "artifact_comments_created_by_user_id_fkey" + columns: ["created_by_user_id"] + isOneToOne: false + referencedRelation: "users" + referencedColumns: ["id"] }, { - foreignKeyName: 'artifact_comments_parent_comment_id_fkey'; - columns: ['parent_comment_id']; - isOneToOne: false; - referencedRelation: 'artifact_comments'; - referencedColumns: ['id']; + foreignKeyName: "artifact_comments_parent_comment_id_fkey" + columns: ["parent_comment_id"] + isOneToOne: false + referencedRelation: "artifact_comments" + referencedColumns: ["id"] }, { - foreignKeyName: 'artifact_comments_user_id_fkey'; - columns: ['user_id']; - isOneToOne: false; - referencedRelation: 'users'; - referencedColumns: ['id']; + foreignKeyName: "artifact_comments_user_id_fkey" + columns: ["user_id"] + isOneToOne: false + referencedRelation: "users" + referencedColumns: ["id"] }, { - foreignKeyName: 'artifact_comments_workspace_id_fkey'; - columns: ['workspace_id']; - isOneToOne: false; - referencedRelation: 'workspace_containers'; - referencedColumns: ['id']; + foreignKeyName: "artifact_comments_workspace_id_fkey" + columns: ["workspace_id"] + isOneToOne: false + referencedRelation: "workspace_containers" + referencedColumns: ["id"] }, - ]; - }; + ] + } artifact_history: { Row: { - artifact_id: string; - change_summary: string | null; - change_type: string | null; - changed_by_agent_id: string | null; - changed_by_identity_id: string | null; - changed_by_user_id: string | null; - content: string; - created_at: string | null; - id: string; - title: string; - version: number; - workspace_id: string | null; - }; + artifact_id: string + change_summary: string | null + change_type: string | null + changed_by_identity_id: string | null + changed_by_user_id: string | null + content: string + created_at: string | null + id: string + title: string + version: number + workspace_id: string | null + } Insert: { - artifact_id: string; - change_summary?: string | null; - change_type?: string | null; - changed_by_agent_id?: string | null; - changed_by_identity_id?: string | null; - changed_by_user_id?: string | null; - content: string; - created_at?: string | null; - id?: string; - title: string; - version: number; - workspace_id?: string | null; - }; + artifact_id: string + change_summary?: string | null + change_type?: string | null + changed_by_identity_id?: string | null + changed_by_user_id?: string | null + content: string + created_at?: string | null + id?: string + title: string + version: number + workspace_id?: string | null + } Update: { - artifact_id?: string; - change_summary?: string | null; - change_type?: string | null; - changed_by_agent_id?: string | null; - changed_by_identity_id?: string | null; - changed_by_user_id?: string | null; - content?: string; - created_at?: string | null; - id?: string; - title?: string; - version?: number; - workspace_id?: string | null; - }; + artifact_id?: string + change_summary?: string | null + change_type?: string | null + changed_by_identity_id?: string | null + changed_by_user_id?: string | null + content?: string + created_at?: string | null + id?: string + title?: string + version?: number + workspace_id?: string | null + } Relationships: [ { - foreignKeyName: 'artifact_history_artifact_id_fkey'; - columns: ['artifact_id']; - isOneToOne: false; - referencedRelation: 'artifacts'; - referencedColumns: ['id']; + foreignKeyName: "artifact_history_artifact_id_fkey" + columns: ["artifact_id"] + isOneToOne: false + referencedRelation: "artifacts" + referencedColumns: ["id"] }, { - foreignKeyName: 'artifact_history_changed_by_identity_id_fkey'; - columns: ['changed_by_identity_id']; - isOneToOne: false; - referencedRelation: 'agent_identities'; - referencedColumns: ['id']; + foreignKeyName: "artifact_history_changed_by_identity_id_fkey" + columns: ["changed_by_identity_id"] + isOneToOne: false + referencedRelation: "agent_identities" + referencedColumns: ["id"] }, { - foreignKeyName: 'artifact_history_changed_by_user_id_fkey'; - columns: ['changed_by_user_id']; - isOneToOne: false; - referencedRelation: 'users'; - referencedColumns: ['id']; + foreignKeyName: "artifact_history_changed_by_user_id_fkey" + columns: ["changed_by_user_id"] + isOneToOne: false + referencedRelation: "users" + referencedColumns: ["id"] }, { - foreignKeyName: 'artifact_history_workspace_id_fkey'; - columns: ['workspace_id']; - isOneToOne: false; - referencedRelation: 'workspace_containers'; - referencedColumns: ['id']; + foreignKeyName: "artifact_history_workspace_id_fkey" + columns: ["workspace_id"] + isOneToOne: false + referencedRelation: "workspace_containers" + referencedColumns: ["id"] }, - ]; - }; + ] + } artifacts: { Row: { - artifact_type: string; - collaborators: string[] | null; - content: string; - content_type: string | null; - created_at: string | null; - created_by_agent_id: string | null; - created_by_identity_id: string | null; - id: string; - metadata: Json | null; - tags: string[] | null; - title: string; - updated_at: string | null; - uri: string; - user_id: string; - version: number | null; - visibility: string | null; - workspace_id: string | null; - }; + artifact_type: string + collaborators: string[] | null + content: string + content_type: string | null + created_at: string | null + created_by_identity_id: string | null + id: string + metadata: Json | null + tags: string[] | null + title: string + updated_at: string | null + uri: string + user_id: string + version: number | null + visibility: string | null + workspace_id: string | null + } Insert: { - artifact_type?: string; - collaborators?: string[] | null; - content: string; - content_type?: string | null; - created_at?: string | null; - created_by_agent_id?: string | null; - created_by_identity_id?: string | null; - id?: string; - metadata?: Json | null; - tags?: string[] | null; - title: string; - updated_at?: string | null; - uri: string; - user_id: string; - version?: number | null; - visibility?: string | null; - workspace_id?: string | null; - }; + artifact_type?: string + collaborators?: string[] | null + content: string + content_type?: string | null + created_at?: string | null + created_by_identity_id?: string | null + id?: string + metadata?: Json | null + tags?: string[] | null + title: string + updated_at?: string | null + uri: string + user_id: string + version?: number | null + visibility?: string | null + workspace_id?: string | null + } Update: { - artifact_type?: string; - collaborators?: string[] | null; - content?: string; - content_type?: string | null; - created_at?: string | null; - created_by_agent_id?: string | null; - created_by_identity_id?: string | null; - id?: string; - metadata?: Json | null; - tags?: string[] | null; - title?: string; - updated_at?: string | null; - uri?: string; - user_id?: string; - version?: number | null; - visibility?: string | null; - workspace_id?: string | null; - }; + artifact_type?: string + collaborators?: string[] | null + content?: string + content_type?: string | null + created_at?: string | null + created_by_identity_id?: string | null + id?: string + metadata?: Json | null + tags?: string[] | null + title?: string + updated_at?: string | null + uri?: string + user_id?: string + version?: number | null + visibility?: string | null + workspace_id?: string | null + } Relationships: [ { - foreignKeyName: 'artifacts_created_by_identity_id_fkey'; - columns: ['created_by_identity_id']; - isOneToOne: false; - referencedRelation: 'agent_identities'; - referencedColumns: ['id']; + foreignKeyName: "artifacts_created_by_identity_id_fkey" + columns: ["created_by_identity_id"] + isOneToOne: false + referencedRelation: "agent_identities" + referencedColumns: ["id"] }, { - foreignKeyName: 'artifacts_user_id_fkey'; - columns: ['user_id']; - isOneToOne: false; - referencedRelation: 'users'; - referencedColumns: ['id']; + foreignKeyName: "artifacts_user_id_fkey" + columns: ["user_id"] + isOneToOne: false + referencedRelation: "users" + referencedColumns: ["id"] }, { - foreignKeyName: 'artifacts_workspace_id_fkey'; - columns: ['workspace_id']; - isOneToOne: false; - referencedRelation: 'workspace_containers'; - referencedColumns: ['id']; + foreignKeyName: "artifacts_workspace_id_fkey" + columns: ["workspace_id"] + isOneToOne: false + referencedRelation: "workspace_containers" + referencedColumns: ["id"] }, - ]; - }; + ] + } audit_log: { Row: { - action: string; - backend: string | null; - category: string; - conversation_id: string | null; - id: string; - metadata: Json | null; - platform: string | null; - platform_user_id: string | null; - request_summary: string | null; - response_status: string | null; - response_summary: string | null; - session_id: string | null; - target: string | null; - timestamp: string; - user_id: string | null; - }; + action: string + backend: string | null + category: string + conversation_id: string | null + id: string + metadata: Json | null + platform: string | null + platform_user_id: string | null + request_summary: string | null + response_status: string | null + response_summary: string | null + session_id: string | null + target: string | null + timestamp: string + user_id: string | null + } Insert: { - action: string; - backend?: string | null; - category: string; - conversation_id?: string | null; - id?: string; - metadata?: Json | null; - platform?: string | null; - platform_user_id?: string | null; - request_summary?: string | null; - response_status?: string | null; - response_summary?: string | null; - session_id?: string | null; - target?: string | null; - timestamp?: string; - user_id?: string | null; - }; + action: string + backend?: string | null + category: string + conversation_id?: string | null + id?: string + metadata?: Json | null + platform?: string | null + platform_user_id?: string | null + request_summary?: string | null + response_status?: string | null + response_summary?: string | null + session_id?: string | null + target?: string | null + timestamp?: string + user_id?: string | null + } Update: { - action?: string; - backend?: string | null; - category?: string; - conversation_id?: string | null; - id?: string; - metadata?: Json | null; - platform?: string | null; - platform_user_id?: string | null; - request_summary?: string | null; - response_status?: string | null; - response_summary?: string | null; - session_id?: string | null; - target?: string | null; - timestamp?: string; - user_id?: string | null; - }; + action?: string + backend?: string | null + category?: string + conversation_id?: string | null + id?: string + metadata?: Json | null + platform?: string | null + platform_user_id?: string | null + request_summary?: string | null + response_status?: string | null + response_summary?: string | null + session_id?: string | null + target?: string | null + timestamp?: string + user_id?: string | null + } Relationships: [ { - foreignKeyName: 'audit_log_user_id_fkey'; - columns: ['user_id']; - isOneToOne: false; - referencedRelation: 'users'; - referencedColumns: ['id']; + foreignKeyName: "audit_log_user_id_fkey" + columns: ["user_id"] + isOneToOne: false + referencedRelation: "users" + referencedColumns: ["id"] }, - ]; - }; + ] + } authorized_groups: { Row: { - authorization_method: string | null; - authorized_at: string | null; - authorized_by: string | null; - group_name: string | null; - id: string; - platform: string; - platform_group_id: string; - revoked_at: string | null; - revoked_by: string | null; - status: string; - workspace_id: string | null; - }; + authorization_method: string | null + authorized_at: string | null + authorized_by: string | null + group_name: string | null + id: string + platform: string + platform_group_id: string + revoked_at: string | null + revoked_by: string | null + status: string + workspace_id: string | null + } Insert: { - authorization_method?: string | null; - authorized_at?: string | null; - authorized_by?: string | null; - group_name?: string | null; - id?: string; - platform: string; - platform_group_id: string; - revoked_at?: string | null; - revoked_by?: string | null; - status?: string; - workspace_id?: string | null; - }; + authorization_method?: string | null + authorized_at?: string | null + authorized_by?: string | null + group_name?: string | null + id?: string + platform: string + platform_group_id: string + revoked_at?: string | null + revoked_by?: string | null + status?: string + workspace_id?: string | null + } Update: { - authorization_method?: string | null; - authorized_at?: string | null; - authorized_by?: string | null; - group_name?: string | null; - id?: string; - platform?: string; - platform_group_id?: string; - revoked_at?: string | null; - revoked_by?: string | null; - status?: string; - workspace_id?: string | null; - }; + authorization_method?: string | null + authorized_at?: string | null + authorized_by?: string | null + group_name?: string | null + id?: string + platform?: string + platform_group_id?: string + revoked_at?: string | null + revoked_by?: string | null + status?: string + workspace_id?: string | null + } Relationships: [ { - foreignKeyName: 'authorized_groups_authorized_by_fkey'; - columns: ['authorized_by']; - isOneToOne: false; - referencedRelation: 'users'; - referencedColumns: ['id']; + foreignKeyName: "authorized_groups_authorized_by_fkey" + columns: ["authorized_by"] + isOneToOne: false + referencedRelation: "users" + referencedColumns: ["id"] }, { - foreignKeyName: 'authorized_groups_revoked_by_fkey'; - columns: ['revoked_by']; - isOneToOne: false; - referencedRelation: 'users'; - referencedColumns: ['id']; + foreignKeyName: "authorized_groups_revoked_by_fkey" + columns: ["revoked_by"] + isOneToOne: false + referencedRelation: "users" + referencedColumns: ["id"] }, { - foreignKeyName: 'authorized_groups_workspace_id_fkey'; - columns: ['workspace_id']; - isOneToOne: false; - referencedRelation: 'workspace_containers'; - referencedColumns: ['id']; + foreignKeyName: "authorized_groups_workspace_id_fkey" + columns: ["workspace_id"] + isOneToOne: false + referencedRelation: "workspace_containers" + referencedColumns: ["id"] }, - ]; - }; + ] + } connected_accounts: { Row: { - access_token: string; - avatar_url: string | null; - created_at: string | null; - display_name: string | null; - email: string | null; - expires_at: string | null; - id: string; - last_error: string | null; - last_used_at: string | null; - metadata: Json | null; - provider: string; - provider_account_id: string; - refresh_token: string | null; - refresh_token_expires_at: string | null; - scopes: string[] | null; - status: string | null; - token_type: string | null; - updated_at: string | null; - user_id: string; - workspace_id: string | null; - }; + access_token: string + avatar_url: string | null + created_at: string | null + display_name: string | null + email: string | null + expires_at: string | null + id: string + last_error: string | null + last_used_at: string | null + metadata: Json | null + provider: string + provider_account_id: string + refresh_token: string | null + refresh_token_expires_at: string | null + scopes: string[] | null + status: string | null + token_type: string | null + updated_at: string | null + user_id: string + workspace_id: string | null + } Insert: { - access_token: string; - avatar_url?: string | null; - created_at?: string | null; - display_name?: string | null; - email?: string | null; - expires_at?: string | null; - id?: string; - last_error?: string | null; - last_used_at?: string | null; - metadata?: Json | null; - provider: string; - provider_account_id: string; - refresh_token?: string | null; - refresh_token_expires_at?: string | null; - scopes?: string[] | null; - status?: string | null; - token_type?: string | null; - updated_at?: string | null; - user_id: string; - workspace_id?: string | null; - }; + access_token: string + avatar_url?: string | null + created_at?: string | null + display_name?: string | null + email?: string | null + expires_at?: string | null + id?: string + last_error?: string | null + last_used_at?: string | null + metadata?: Json | null + provider: string + provider_account_id: string + refresh_token?: string | null + refresh_token_expires_at?: string | null + scopes?: string[] | null + status?: string | null + token_type?: string | null + updated_at?: string | null + user_id: string + workspace_id?: string | null + } Update: { - access_token?: string; - avatar_url?: string | null; - created_at?: string | null; - display_name?: string | null; - email?: string | null; - expires_at?: string | null; - id?: string; - last_error?: string | null; - last_used_at?: string | null; - metadata?: Json | null; - provider?: string; - provider_account_id?: string; - refresh_token?: string | null; - refresh_token_expires_at?: string | null; - scopes?: string[] | null; - status?: string | null; - token_type?: string | null; - updated_at?: string | null; - user_id?: string; - workspace_id?: string | null; - }; + access_token?: string + avatar_url?: string | null + created_at?: string | null + display_name?: string | null + email?: string | null + expires_at?: string | null + id?: string + last_error?: string | null + last_used_at?: string | null + metadata?: Json | null + provider?: string + provider_account_id?: string + refresh_token?: string | null + refresh_token_expires_at?: string | null + scopes?: string[] | null + status?: string | null + token_type?: string | null + updated_at?: string | null + user_id?: string + workspace_id?: string | null + } Relationships: [ { - foreignKeyName: 'connected_accounts_user_id_fkey'; - columns: ['user_id']; - isOneToOne: false; - referencedRelation: 'users'; - referencedColumns: ['id']; + foreignKeyName: "connected_accounts_user_id_fkey" + columns: ["user_id"] + isOneToOne: false + referencedRelation: "users" + referencedColumns: ["id"] }, { - foreignKeyName: 'connected_accounts_workspace_id_fkey'; - columns: ['workspace_id']; - isOneToOne: false; - referencedRelation: 'workspace_containers'; - referencedColumns: ['id']; + foreignKeyName: "connected_accounts_workspace_id_fkey" + columns: ["workspace_id"] + isOneToOne: false + referencedRelation: "workspace_containers" + referencedColumns: ["id"] }, - ]; - }; + ] + } contacts: { Row: { - aliases: string[] | null; - created_at: string | null; - discord_id: string | null; - display_name: string | null; - email: string | null; - id: string; - imessage_id: string | null; - name: string; - notes: string | null; - phone: string | null; - tags: string[] | null; - telegram_id: string | null; - telegram_username: string | null; - updated_at: string | null; - user_id: string; - whatsapp_id: string | null; - }; + aliases: string[] | null + created_at: string | null + discord_id: string | null + display_name: string | null + email: string | null + id: string + imessage_id: string | null + name: string + notes: string | null + phone: string | null + tags: string[] | null + telegram_id: string | null + telegram_username: string | null + updated_at: string | null + user_id: string + whatsapp_id: string | null + } Insert: { - aliases?: string[] | null; - created_at?: string | null; - discord_id?: string | null; - display_name?: string | null; - email?: string | null; - id?: string; - imessage_id?: string | null; - name: string; - notes?: string | null; - phone?: string | null; - tags?: string[] | null; - telegram_id?: string | null; - telegram_username?: string | null; - updated_at?: string | null; - user_id: string; - whatsapp_id?: string | null; - }; + aliases?: string[] | null + created_at?: string | null + discord_id?: string | null + display_name?: string | null + email?: string | null + id?: string + imessage_id?: string | null + name: string + notes?: string | null + phone?: string | null + tags?: string[] | null + telegram_id?: string | null + telegram_username?: string | null + updated_at?: string | null + user_id: string + whatsapp_id?: string | null + } Update: { - aliases?: string[] | null; - created_at?: string | null; - discord_id?: string | null; - display_name?: string | null; - email?: string | null; - id?: string; - imessage_id?: string | null; - name?: string; - notes?: string | null; - phone?: string | null; - tags?: string[] | null; - telegram_id?: string | null; - telegram_username?: string | null; - updated_at?: string | null; - user_id?: string; - whatsapp_id?: string | null; - }; + aliases?: string[] | null + created_at?: string | null + discord_id?: string | null + display_name?: string | null + email?: string | null + id?: string + imessage_id?: string | null + name?: string + notes?: string | null + phone?: string | null + tags?: string[] | null + telegram_id?: string | null + telegram_username?: string | null + updated_at?: string | null + user_id?: string + whatsapp_id?: string | null + } Relationships: [ { - foreignKeyName: 'contacts_user_id_fkey'; - columns: ['user_id']; - isOneToOne: false; - referencedRelation: 'users'; - referencedColumns: ['id']; + foreignKeyName: "contacts_user_id_fkey" + columns: ["user_id"] + isOneToOne: false + referencedRelation: "users" + referencedColumns: ["id"] }, - ]; - }; + ] + } context_history: { Row: { - archived_at: string | null; - change_type: string; - context_id: string; - context_key: string | null; - context_type: string; - created_at: string; - id: string; - metadata: Json | null; - summary: string; - user_id: string; - version: number; - }; + archived_at: string | null + change_type: string + context_id: string + context_key: string | null + context_type: string + created_at: string + id: string + metadata: Json | null + summary: string + user_id: string + version: number + } Insert: { - archived_at?: string | null; - change_type?: string; - context_id: string; - context_key?: string | null; - context_type: string; - created_at: string; - id?: string; - metadata?: Json | null; - summary: string; - user_id: string; - version: number; - }; + archived_at?: string | null + change_type?: string + context_id: string + context_key?: string | null + context_type: string + created_at: string + id?: string + metadata?: Json | null + summary: string + user_id: string + version: number + } Update: { - archived_at?: string | null; - change_type?: string; - context_id?: string; - context_key?: string | null; - context_type?: string; - created_at?: string; - id?: string; - metadata?: Json | null; - summary?: string; - user_id?: string; - version?: number; - }; + archived_at?: string | null + change_type?: string + context_id?: string + context_key?: string | null + context_type?: string + created_at?: string + id?: string + metadata?: Json | null + summary?: string + user_id?: string + version?: number + } Relationships: [ { - foreignKeyName: 'context_history_user_id_fkey'; - columns: ['user_id']; - isOneToOne: false; - referencedRelation: 'users'; - referencedColumns: ['id']; + foreignKeyName: "context_history_user_id_fkey" + columns: ["user_id"] + isOneToOne: false + referencedRelation: "users" + referencedColumns: ["id"] }, - ]; - }; + ] + } context_summaries: { Row: { - context_key: string | null; - context_type: string; - created_at: string | null; - id: string; - metadata: Json | null; - summary: string; - updated_at: string | null; - user_id: string; - version: number | null; - }; + context_key: string | null + context_type: string + created_at: string | null + id: string + metadata: Json | null + summary: string + updated_at: string | null + user_id: string + version: number | null + } Insert: { - context_key?: string | null; - context_type: string; - created_at?: string | null; - id?: string; - metadata?: Json | null; - summary: string; - updated_at?: string | null; - user_id: string; - version?: number | null; - }; + context_key?: string | null + context_type: string + created_at?: string | null + id?: string + metadata?: Json | null + summary: string + updated_at?: string | null + user_id: string + version?: number | null + } Update: { - context_key?: string | null; - context_type?: string; - created_at?: string | null; - id?: string; - metadata?: Json | null; - summary?: string; - updated_at?: string | null; - user_id?: string; - version?: number | null; - }; + context_key?: string | null + context_type?: string + created_at?: string | null + id?: string + metadata?: Json | null + summary?: string + updated_at?: string | null + user_id?: string + version?: number | null + } Relationships: [ { - foreignKeyName: 'context_summaries_user_id_fkey'; - columns: ['user_id']; - isOneToOne: false; - referencedRelation: 'users'; - referencedColumns: ['id']; + foreignKeyName: "context_summaries_user_id_fkey" + columns: ["user_id"] + isOneToOne: false + referencedRelation: "users" + referencedColumns: ["id"] }, - ]; - }; + ] + } conversations: { Row: { - created_at: string | null; - id: string; - metadata: Json | null; - platform: string; - platform_conversation_id: string; - title: string | null; - updated_at: string | null; - user_id: string; - }; + created_at: string | null + id: string + metadata: Json | null + platform: string + platform_conversation_id: string + title: string | null + updated_at: string | null + user_id: string + } Insert: { - created_at?: string | null; - id?: string; - metadata?: Json | null; - platform: string; - platform_conversation_id: string; - title?: string | null; - updated_at?: string | null; - user_id: string; - }; + created_at?: string | null + id?: string + metadata?: Json | null + platform: string + platform_conversation_id: string + title?: string | null + updated_at?: string | null + user_id: string + } Update: { - created_at?: string | null; - id?: string; - metadata?: Json | null; - platform?: string; - platform_conversation_id?: string; - title?: string | null; - updated_at?: string | null; - user_id?: string; - }; + created_at?: string | null + id?: string + metadata?: Json | null + platform?: string + platform_conversation_id?: string + title?: string | null + updated_at?: string | null + user_id?: string + } Relationships: [ { - foreignKeyName: 'conversations_user_id_fkey'; - columns: ['user_id']; - isOneToOne: false; - referencedRelation: 'users'; - referencedColumns: ['id']; + foreignKeyName: "conversations_user_id_fkey" + columns: ["user_id"] + isOneToOne: false + referencedRelation: "users" + referencedColumns: ["id"] }, - ]; - }; + ] + } group_challenge_codes: { Row: { - code: string; - created_at: string | null; - created_by: string | null; - expires_at: string | null; - id: string; - used_at: string | null; - used_for_group_id: string | null; - used_for_platform: string | null; - workspace_id: string | null; - }; + code: string + created_at: string | null + created_by: string | null + expires_at: string | null + id: string + used_at: string | null + used_for_group_id: string | null + used_for_platform: string | null + workspace_id: string | null + } Insert: { - code: string; - created_at?: string | null; - created_by?: string | null; - expires_at?: string | null; - id?: string; - used_at?: string | null; - used_for_group_id?: string | null; - used_for_platform?: string | null; - workspace_id?: string | null; - }; + code: string + created_at?: string | null + created_by?: string | null + expires_at?: string | null + id?: string + used_at?: string | null + used_for_group_id?: string | null + used_for_platform?: string | null + workspace_id?: string | null + } Update: { - code?: string; - created_at?: string | null; - created_by?: string | null; - expires_at?: string | null; - id?: string; - used_at?: string | null; - used_for_group_id?: string | null; - used_for_platform?: string | null; - workspace_id?: string | null; - }; + code?: string + created_at?: string | null + created_by?: string | null + expires_at?: string | null + id?: string + used_at?: string | null + used_for_group_id?: string | null + used_for_platform?: string | null + workspace_id?: string | null + } Relationships: [ { - foreignKeyName: 'group_challenge_codes_created_by_fkey'; - columns: ['created_by']; - isOneToOne: false; - referencedRelation: 'users'; - referencedColumns: ['id']; + foreignKeyName: "group_challenge_codes_created_by_fkey" + columns: ["created_by"] + isOneToOne: false + referencedRelation: "users" + referencedColumns: ["id"] }, { - foreignKeyName: 'group_challenge_codes_workspace_id_fkey'; - columns: ['workspace_id']; - isOneToOne: false; - referencedRelation: 'workspace_containers'; - referencedColumns: ['id']; + foreignKeyName: "group_challenge_codes_workspace_id_fkey" + columns: ["workspace_id"] + isOneToOne: false + referencedRelation: "workspace_containers" + referencedColumns: ["id"] }, - ]; - }; + ] + } heartbeat_state: { Row: { - last_checks: Json | null; - quiet_end: string | null; - quiet_start: string | null; - timezone: string | null; - updated_at: string | null; - user_id: string; - }; + last_checks: Json | null + quiet_end: string | null + quiet_start: string | null + timezone: string | null + updated_at: string | null + user_id: string + } Insert: { - last_checks?: Json | null; - quiet_end?: string | null; - quiet_start?: string | null; - timezone?: string | null; - updated_at?: string | null; - user_id: string; - }; + last_checks?: Json | null + quiet_end?: string | null + quiet_start?: string | null + timezone?: string | null + updated_at?: string | null + user_id: string + } Update: { - last_checks?: Json | null; - quiet_end?: string | null; - quiet_start?: string | null; - timezone?: string | null; - updated_at?: string | null; - user_id?: string; - }; + last_checks?: Json | null + quiet_end?: string | null + quiet_start?: string | null + timezone?: string | null + updated_at?: string | null + user_id?: string + } Relationships: [ { - foreignKeyName: 'heartbeat_state_user_id_fkey'; - columns: ['user_id']; - isOneToOne: true; - referencedRelation: 'users'; - referencedColumns: ['id']; + foreignKeyName: "heartbeat_state_user_id_fkey" + columns: ["user_id"] + isOneToOne: true + referencedRelation: "users" + referencedColumns: ["id"] }, - ]; - }; + ] + } integration_health: { Row: { - created_at: string | null; - error_code: string | null; - error_message: string | null; - id: string; - last_check_at: string | null; - last_healthy_at: string | null; - metadata: Json | null; - reported_by_agent_id: string | null; - service: string; - status: string; - updated_at: string | null; - user_id: string; - }; + created_at: string | null + error_code: string | null + error_message: string | null + id: string + last_check_at: string | null + last_healthy_at: string | null + metadata: Json | null + reported_by_agent_id: string | null + service: string + status: string + updated_at: string | null + user_id: string + } Insert: { - created_at?: string | null; - error_code?: string | null; - error_message?: string | null; - id?: string; - last_check_at?: string | null; - last_healthy_at?: string | null; - metadata?: Json | null; - reported_by_agent_id?: string | null; - service: string; - status?: string; - updated_at?: string | null; - user_id: string; - }; + created_at?: string | null + error_code?: string | null + error_message?: string | null + id?: string + last_check_at?: string | null + last_healthy_at?: string | null + metadata?: Json | null + reported_by_agent_id?: string | null + service: string + status?: string + updated_at?: string | null + user_id: string + } Update: { - created_at?: string | null; - error_code?: string | null; - error_message?: string | null; - id?: string; - last_check_at?: string | null; - last_healthy_at?: string | null; - metadata?: Json | null; - reported_by_agent_id?: string | null; - service?: string; - status?: string; - updated_at?: string | null; - user_id?: string; - }; + created_at?: string | null + error_code?: string | null + error_message?: string | null + id?: string + last_check_at?: string | null + last_healthy_at?: string | null + metadata?: Json | null + reported_by_agent_id?: string | null + service?: string + status?: string + updated_at?: string | null + user_id?: string + } Relationships: [ { - foreignKeyName: 'integration_health_user_id_fkey'; - columns: ['user_id']; - isOneToOne: false; - referencedRelation: 'users'; - referencedColumns: ['id']; + foreignKeyName: "integration_health_user_id_fkey" + columns: ["user_id"] + isOneToOne: false + referencedRelation: "users" + referencedColumns: ["id"] }, - ]; - }; + ] + } links: { Row: { - created_at: string | null; - description: string | null; - embedding: string | null; - id: string; - metadata: Json | null; - source: string | null; - tags: string[] | null; - title: string | null; - updated_at: string | null; - url: string; - user_id: string; - }; + created_at: string | null + description: string | null + embedding: string | null + id: string + metadata: Json | null + source: string | null + tags: string[] | null + title: string | null + updated_at: string | null + url: string + user_id: string + } Insert: { - created_at?: string | null; - description?: string | null; - embedding?: string | null; - id?: string; - metadata?: Json | null; - source?: string | null; - tags?: string[] | null; - title?: string | null; - updated_at?: string | null; - url: string; - user_id: string; - }; + created_at?: string | null + description?: string | null + embedding?: string | null + id?: string + metadata?: Json | null + source?: string | null + tags?: string[] | null + title?: string | null + updated_at?: string | null + url: string + user_id: string + } Update: { - created_at?: string | null; - description?: string | null; - embedding?: string | null; - id?: string; - metadata?: Json | null; - source?: string | null; - tags?: string[] | null; - title?: string | null; - updated_at?: string | null; - url?: string; - user_id?: string; - }; + created_at?: string | null + description?: string | null + embedding?: string | null + id?: string + metadata?: Json | null + source?: string | null + tags?: string[] | null + title?: string | null + updated_at?: string | null + url?: string + user_id?: string + } Relationships: [ { - foreignKeyName: 'links_user_id_fkey'; - columns: ['user_id']; - isOneToOne: false; - referencedRelation: 'users'; - referencedColumns: ['id']; + foreignKeyName: "links_user_id_fkey" + columns: ["user_id"] + isOneToOne: false + referencedRelation: "users" + referencedColumns: ["id"] }, - ]; - }; + ] + } mcp_tokens: { Row: { - client_id: string; - created_at: string | null; - expires_at: string; - id: string; - last_used_at: string | null; - refresh_token: string; - scopes: string[] | null; - supabase_refresh_token: string | null; - updated_at: string | null; - user_id: string; - }; + client_id: string + created_at: string | null + expires_at: string + id: string + last_used_at: string | null + refresh_token: string + scopes: string[] | null + supabase_refresh_token: string | null + updated_at: string | null + user_id: string + } Insert: { - client_id: string; - created_at?: string | null; - expires_at: string; - id?: string; - last_used_at?: string | null; - refresh_token: string; - scopes?: string[] | null; - supabase_refresh_token?: string | null; - updated_at?: string | null; - user_id: string; - }; + client_id: string + created_at?: string | null + expires_at: string + id?: string + last_used_at?: string | null + refresh_token: string + scopes?: string[] | null + supabase_refresh_token?: string | null + updated_at?: string | null + user_id: string + } Update: { - client_id?: string; - created_at?: string | null; - expires_at?: string; - id?: string; - last_used_at?: string | null; - refresh_token?: string; - scopes?: string[] | null; - supabase_refresh_token?: string | null; - updated_at?: string | null; - user_id?: string; - }; + client_id?: string + created_at?: string | null + expires_at?: string + id?: string + last_used_at?: string | null + refresh_token?: string + scopes?: string[] | null + supabase_refresh_token?: string | null + updated_at?: string | null + user_id?: string + } Relationships: [ { - foreignKeyName: 'mcp_tokens_user_id_fkey'; - columns: ['user_id']; - isOneToOne: false; - referencedRelation: 'users'; - referencedColumns: ['id']; + foreignKeyName: "mcp_tokens_user_id_fkey" + columns: ["user_id"] + isOneToOne: false + referencedRelation: "users" + referencedColumns: ["id"] }, - ]; - }; + ] + } memories: { Row: { - agent_id: string | null; - content: string; - created_at: string | null; - embedding: string | null; - expires_at: string | null; - id: string; - metadata: Json | null; - salience: string; - source: string; - topics: string[] | null; - user_id: string; - version: number; - }; + agent_id: string | null + content: string + created_at: string | null + embedding: string | null + expires_at: string | null + id: string + identity_id: string | null + metadata: Json | null + salience: string + source: string + topics: string[] | null + user_id: string + version: number + } Insert: { - agent_id?: string | null; - content: string; - created_at?: string | null; - embedding?: string | null; - expires_at?: string | null; - id?: string; - metadata?: Json | null; - salience?: string; - source?: string; - topics?: string[] | null; - user_id: string; - version?: number; - }; + agent_id?: string | null + content: string + created_at?: string | null + embedding?: string | null + expires_at?: string | null + id?: string + identity_id?: string | null + metadata?: Json | null + salience?: string + source?: string + topics?: string[] | null + user_id: string + version?: number + } Update: { - agent_id?: string | null; - content?: string; - created_at?: string | null; - embedding?: string | null; - expires_at?: string | null; - id?: string; - metadata?: Json | null; - salience?: string; - source?: string; - topics?: string[] | null; - user_id?: string; - version?: number; - }; + agent_id?: string | null + content?: string + created_at?: string | null + embedding?: string | null + expires_at?: string | null + id?: string + identity_id?: string | null + metadata?: Json | null + salience?: string + source?: string + topics?: string[] | null + user_id?: string + version?: number + } Relationships: [ { - foreignKeyName: 'memories_user_id_fkey'; - columns: ['user_id']; - isOneToOne: false; - referencedRelation: 'users'; - referencedColumns: ['id']; + foreignKeyName: "memories_identity_id_fkey" + columns: ["identity_id"] + isOneToOne: false + referencedRelation: "agent_identities" + referencedColumns: ["id"] }, - ]; - }; + { + foreignKeyName: "memories_user_id_fkey" + columns: ["user_id"] + isOneToOne: false + referencedRelation: "users" + referencedColumns: ["id"] + }, + ] + } memory_history: { Row: { - archived_at: string | null; - change_type: string; - content: string; - created_at: string; - id: string; - memory_id: string; - metadata: Json | null; - salience: string; - source: string; - topics: string[] | null; - user_id: string; - version: number; - }; + archived_at: string | null + change_type: string + content: string + created_at: string + id: string + memory_id: string + metadata: Json | null + salience: string + source: string + topics: string[] | null + user_id: string + version: number + } Insert: { - archived_at?: string | null; - change_type?: string; - content: string; - created_at: string; - id?: string; - memory_id: string; - metadata?: Json | null; - salience: string; - source: string; - topics?: string[] | null; - user_id: string; - version?: number; - }; + archived_at?: string | null + change_type?: string + content: string + created_at: string + id?: string + memory_id: string + metadata?: Json | null + salience: string + source: string + topics?: string[] | null + user_id: string + version?: number + } Update: { - archived_at?: string | null; - change_type?: string; - content?: string; - created_at?: string; - id?: string; - memory_id?: string; - metadata?: Json | null; - salience?: string; - source?: string; - topics?: string[] | null; - user_id?: string; - version?: number; - }; + archived_at?: string | null + change_type?: string + content?: string + created_at?: string + id?: string + memory_id?: string + metadata?: Json | null + salience?: string + source?: string + topics?: string[] | null + user_id?: string + version?: number + } Relationships: [ { - foreignKeyName: 'memory_history_user_id_fkey'; - columns: ['user_id']; - isOneToOne: false; - referencedRelation: 'users'; - referencedColumns: ['id']; + foreignKeyName: "memory_history_user_id_fkey" + columns: ["user_id"] + isOneToOne: false + referencedRelation: "users" + referencedColumns: ["id"] }, - ]; - }; + ] + } messages: { Row: { - content: string; - conversation_id: string; - created_at: string | null; - embedding: string | null; - id: string; - message_type: string | null; - metadata: Json | null; - platform_message_id: string | null; - user_id: string; - }; + content: string + conversation_id: string + created_at: string | null + embedding: string | null + id: string + message_type: string | null + metadata: Json | null + platform_message_id: string | null + user_id: string + } Insert: { - content: string; - conversation_id: string; - created_at?: string | null; - embedding?: string | null; - id?: string; - message_type?: string | null; - metadata?: Json | null; - platform_message_id?: string | null; - user_id: string; - }; + content: string + conversation_id: string + created_at?: string | null + embedding?: string | null + id?: string + message_type?: string | null + metadata?: Json | null + platform_message_id?: string | null + user_id: string + } Update: { - content?: string; - conversation_id?: string; - created_at?: string | null; - embedding?: string | null; - id?: string; - message_type?: string | null; - metadata?: Json | null; - platform_message_id?: string | null; - user_id?: string; - }; + content?: string + conversation_id?: string + created_at?: string | null + embedding?: string | null + id?: string + message_type?: string | null + metadata?: Json | null + platform_message_id?: string | null + user_id?: string + } Relationships: [ { - foreignKeyName: 'messages_conversation_id_fkey'; - columns: ['conversation_id']; - isOneToOne: false; - referencedRelation: 'conversations'; - referencedColumns: ['id']; + foreignKeyName: "messages_conversation_id_fkey" + columns: ["conversation_id"] + isOneToOne: false + referencedRelation: "conversations" + referencedColumns: ["id"] }, { - foreignKeyName: 'messages_user_id_fkey'; - columns: ['user_id']; - isOneToOne: false; - referencedRelation: 'users'; - referencedColumns: ['id']; + foreignKeyName: "messages_user_id_fkey" + columns: ["user_id"] + isOneToOne: false + referencedRelation: "users" + referencedColumns: ["id"] }, - ]; - }; + ] + } mini_app_records: { Row: { - amount: number | null; - app_name: string; - contact_id: string | null; - created_at: string; - data: Json; - id: string; - metadata: Json | null; - recorded_at: string | null; - related_entity_id: string | null; - related_entity_type: string | null; - related_record_id: string | null; - tags: string[] | null; - text: string | null; - type: string; - updated_at: string; - user_id: string; - }; + amount: number | null + app_name: string + contact_id: string | null + created_at: string + data: Json + id: string + metadata: Json | null + recorded_at: string | null + related_entity_id: string | null + related_entity_type: string | null + related_record_id: string | null + tags: string[] | null + text: string | null + type: string + updated_at: string + user_id: string + } Insert: { - amount?: number | null; - app_name: string; - contact_id?: string | null; - created_at?: string; - data?: Json; - id?: string; - metadata?: Json | null; - recorded_at?: string | null; - related_entity_id?: string | null; - related_entity_type?: string | null; - related_record_id?: string | null; - tags?: string[] | null; - text?: string | null; - type: string; - updated_at?: string; - user_id: string; - }; + amount?: number | null + app_name: string + contact_id?: string | null + created_at?: string + data?: Json + id?: string + metadata?: Json | null + recorded_at?: string | null + related_entity_id?: string | null + related_entity_type?: string | null + related_record_id?: string | null + tags?: string[] | null + text?: string | null + type: string + updated_at?: string + user_id: string + } Update: { - amount?: number | null; - app_name?: string; - contact_id?: string | null; - created_at?: string; - data?: Json; - id?: string; - metadata?: Json | null; - recorded_at?: string | null; - related_entity_id?: string | null; - related_entity_type?: string | null; - related_record_id?: string | null; - tags?: string[] | null; - text?: string | null; - type?: string; - updated_at?: string; - user_id?: string; - }; + amount?: number | null + app_name?: string + contact_id?: string | null + created_at?: string + data?: Json + id?: string + metadata?: Json | null + recorded_at?: string | null + related_entity_id?: string | null + related_entity_type?: string | null + related_record_id?: string | null + tags?: string[] | null + text?: string | null + type?: string + updated_at?: string + user_id?: string + } Relationships: [ { - foreignKeyName: 'mini_app_records_contact_id_fkey'; - columns: ['contact_id']; - isOneToOne: false; - referencedRelation: 'contacts'; - referencedColumns: ['id']; + foreignKeyName: "mini_app_records_contact_id_fkey" + columns: ["contact_id"] + isOneToOne: false + referencedRelation: "contacts" + referencedColumns: ["id"] }, { - foreignKeyName: 'mini_app_records_related_record_id_fkey'; - columns: ['related_record_id']; - isOneToOne: false; - referencedRelation: 'mini_app_records'; - referencedColumns: ['id']; + foreignKeyName: "mini_app_records_related_record_id_fkey" + columns: ["related_record_id"] + isOneToOne: false + referencedRelation: "mini_app_records" + referencedColumns: ["id"] }, { - foreignKeyName: 'mini_app_records_user_id_fkey'; - columns: ['user_id']; - isOneToOne: false; - referencedRelation: 'users'; - referencedColumns: ['id']; + foreignKeyName: "mini_app_records_user_id_fkey" + columns: ["user_id"] + isOneToOne: false + referencedRelation: "users" + referencedColumns: ["id"] }, - ]; - }; + ] + } notes: { Row: { - content: string; - created_at: string | null; - embedding: string | null; - id: string; - is_private: boolean | null; - metadata: Json | null; - tags: string[] | null; - title: string | null; - updated_at: string | null; - user_id: string; - }; + content: string + created_at: string | null + embedding: string | null + id: string + is_private: boolean | null + metadata: Json | null + tags: string[] | null + title: string | null + updated_at: string | null + user_id: string + } Insert: { - content: string; - created_at?: string | null; - embedding?: string | null; - id?: string; - is_private?: boolean | null; - metadata?: Json | null; - tags?: string[] | null; - title?: string | null; - updated_at?: string | null; - user_id: string; - }; + content: string + created_at?: string | null + embedding?: string | null + id?: string + is_private?: boolean | null + metadata?: Json | null + tags?: string[] | null + title?: string | null + updated_at?: string | null + user_id: string + } Update: { - content?: string; - created_at?: string | null; - embedding?: string | null; - id?: string; - is_private?: boolean | null; - metadata?: Json | null; - tags?: string[] | null; - title?: string | null; - updated_at?: string | null; - user_id?: string; - }; + content?: string + created_at?: string | null + embedding?: string | null + id?: string + is_private?: boolean | null + metadata?: Json | null + tags?: string[] | null + title?: string | null + updated_at?: string | null + user_id?: string + } Relationships: [ { - foreignKeyName: 'notes_user_id_fkey'; - columns: ['user_id']; - isOneToOne: false; - referencedRelation: 'users'; - referencedColumns: ['id']; + foreignKeyName: "notes_user_id_fkey" + columns: ["user_id"] + isOneToOne: false + referencedRelation: "users" + referencedColumns: ["id"] }, - ]; - }; + ] + } pcp_config: { Row: { - key: string; - updated_at: string | null; - value: string; - }; + key: string + updated_at: string | null + value: string + } Insert: { - key: string; - updated_at?: string | null; - value: string; - }; + key: string + updated_at?: string | null + value: string + } Update: { - key?: string; - updated_at?: string | null; - value?: string; - }; - Relationships: []; - }; + key?: string + updated_at?: string | null + value?: string + } + Relationships: [] + } permission_definitions: { Row: { - category: string; - created_at: string | null; - default_enabled: boolean | null; - description: string | null; - id: string; - name: string; - risk_level: string; - }; + category: string + created_at: string | null + default_enabled: boolean | null + description: string | null + id: string + name: string + risk_level: string + } Insert: { - category: string; - created_at?: string | null; - default_enabled?: boolean | null; - description?: string | null; - id: string; - name: string; - risk_level?: string; - }; + category: string + created_at?: string | null + default_enabled?: boolean | null + description?: string | null + id: string + name: string + risk_level?: string + } Update: { - category?: string; - created_at?: string | null; - default_enabled?: boolean | null; - description?: string | null; - id?: string; - name?: string; - risk_level?: string; - }; - Relationships: []; - }; + category?: string + created_at?: string | null + default_enabled?: boolean | null + description?: string | null + id?: string + name?: string + risk_level?: string + } + Relationships: [] + } project_tasks: { Row: { - blocked_by: string[] | null; - completed_at: string | null; - created_at: string; - created_by: string | null; - description: string | null; - id: string; - priority: string | null; - project_id: string; - status: string; - tags: string[] | null; - title: string; - updated_at: string; - user_id: string; - }; + blocked_by: string[] | null + completed_at: string | null + created_at: string + created_by: string | null + description: string | null + id: string + priority: string | null + project_id: string + status: string + tags: string[] | null + title: string + updated_at: string + user_id: string + } Insert: { - blocked_by?: string[] | null; - completed_at?: string | null; - created_at?: string; - created_by?: string | null; - description?: string | null; - id?: string; - priority?: string | null; - project_id: string; - status?: string; - tags?: string[] | null; - title: string; - updated_at?: string; - user_id: string; - }; + blocked_by?: string[] | null + completed_at?: string | null + created_at?: string + created_by?: string | null + description?: string | null + id?: string + priority?: string | null + project_id: string + status?: string + tags?: string[] | null + title: string + updated_at?: string + user_id: string + } Update: { - blocked_by?: string[] | null; - completed_at?: string | null; - created_at?: string; - created_by?: string | null; - description?: string | null; - id?: string; - priority?: string | null; - project_id?: string; - status?: string; - tags?: string[] | null; - title?: string; - updated_at?: string; - user_id?: string; - }; + blocked_by?: string[] | null + completed_at?: string | null + created_at?: string + created_by?: string | null + description?: string | null + id?: string + priority?: string | null + project_id?: string + status?: string + tags?: string[] | null + title?: string + updated_at?: string + user_id?: string + } Relationships: [ { - foreignKeyName: 'project_tasks_project_id_fkey'; - columns: ['project_id']; - isOneToOne: false; - referencedRelation: 'projects'; - referencedColumns: ['id']; + foreignKeyName: "project_tasks_project_id_fkey" + columns: ["project_id"] + isOneToOne: false + referencedRelation: "projects" + referencedColumns: ["id"] }, { - foreignKeyName: 'project_tasks_user_id_fkey'; - columns: ['user_id']; - isOneToOne: false; - referencedRelation: 'users'; - referencedColumns: ['id']; + foreignKeyName: "project_tasks_user_id_fkey" + columns: ["user_id"] + isOneToOne: false + referencedRelation: "users" + referencedColumns: ["id"] }, - ]; - }; + ] + } projects: { Row: { - created_at: string | null; - description: string | null; - goals: string[] | null; - id: string; - metadata: Json | null; - name: string; - repository_url: string | null; - status: string | null; - tech_stack: string[] | null; - updated_at: string | null; - user_id: string; - }; + created_at: string | null + description: string | null + goals: string[] | null + id: string + metadata: Json | null + name: string + repository_url: string | null + status: string | null + tech_stack: string[] | null + updated_at: string | null + user_id: string + } Insert: { - created_at?: string | null; - description?: string | null; - goals?: string[] | null; - id?: string; - metadata?: Json | null; - name: string; - repository_url?: string | null; - status?: string | null; - tech_stack?: string[] | null; - updated_at?: string | null; - user_id: string; - }; + created_at?: string | null + description?: string | null + goals?: string[] | null + id?: string + metadata?: Json | null + name: string + repository_url?: string | null + status?: string | null + tech_stack?: string[] | null + updated_at?: string | null + user_id: string + } Update: { - created_at?: string | null; - description?: string | null; - goals?: string[] | null; - id?: string; - metadata?: Json | null; - name?: string; - repository_url?: string | null; - status?: string | null; - tech_stack?: string[] | null; - updated_at?: string | null; - user_id?: string; - }; + created_at?: string | null + description?: string | null + goals?: string[] | null + id?: string + metadata?: Json | null + name?: string + repository_url?: string | null + status?: string | null + tech_stack?: string[] | null + updated_at?: string | null + user_id?: string + } Relationships: [ { - foreignKeyName: 'projects_user_id_fkey'; - columns: ['user_id']; - isOneToOne: false; - referencedRelation: 'users'; - referencedColumns: ['id']; + foreignKeyName: "projects_user_id_fkey" + columns: ["user_id"] + isOneToOne: false + referencedRelation: "users" + referencedColumns: ["id"] }, - ]; - }; + ] + } reminder_history: { Row: { - delivered_at: string | null; - error_message: string | null; - id: string; - reminder_id: string; - response_at: string | null; - response_content: string | null; - response_received: boolean | null; - status: string; - triggered_at: string | null; - }; + delivered_at: string | null + error_message: string | null + id: string + reminder_id: string + response_at: string | null + response_content: string | null + response_received: boolean | null + status: string + triggered_at: string | null + } Insert: { - delivered_at?: string | null; - error_message?: string | null; - id?: string; - reminder_id: string; - response_at?: string | null; - response_content?: string | null; - response_received?: boolean | null; - status: string; - triggered_at?: string | null; - }; + delivered_at?: string | null + error_message?: string | null + id?: string + reminder_id: string + response_at?: string | null + response_content?: string | null + response_received?: boolean | null + status: string + triggered_at?: string | null + } Update: { - delivered_at?: string | null; - error_message?: string | null; - id?: string; - reminder_id?: string; - response_at?: string | null; - response_content?: string | null; - response_received?: boolean | null; - status?: string; - triggered_at?: string | null; - }; + delivered_at?: string | null + error_message?: string | null + id?: string + reminder_id?: string + response_at?: string | null + response_content?: string | null + response_received?: boolean | null + status?: string + triggered_at?: string | null + } Relationships: [ { - foreignKeyName: 'reminder_history_reminder_id_fkey'; - columns: ['reminder_id']; - isOneToOne: false; - referencedRelation: 'scheduled_reminders'; - referencedColumns: ['id']; + foreignKeyName: "reminder_history_reminder_id_fkey" + columns: ["reminder_id"] + isOneToOne: false + referencedRelation: "scheduled_reminders" + referencedColumns: ["id"] }, - ]; - }; + ] + } reminders: { Row: { - channel: string; - created_at: string | null; - id: string; - message: string; - metadata: Json | null; - recurrence: Json | null; - reminder_time: string; - sent_at: string | null; - status: string | null; - updated_at: string | null; - user_id: string; - }; + channel: string + created_at: string | null + id: string + message: string + metadata: Json | null + recurrence: Json | null + reminder_time: string + sent_at: string | null + status: string | null + updated_at: string | null + user_id: string + } Insert: { - channel: string; - created_at?: string | null; - id?: string; - message: string; - metadata?: Json | null; - recurrence?: Json | null; - reminder_time: string; - sent_at?: string | null; - status?: string | null; - updated_at?: string | null; - user_id: string; - }; + channel: string + created_at?: string | null + id?: string + message: string + metadata?: Json | null + recurrence?: Json | null + reminder_time: string + sent_at?: string | null + status?: string | null + updated_at?: string | null + user_id: string + } Update: { - channel?: string; - created_at?: string | null; - id?: string; - message?: string; - metadata?: Json | null; - recurrence?: Json | null; - reminder_time?: string; - sent_at?: string | null; - status?: string | null; - updated_at?: string | null; - user_id?: string; - }; + channel?: string + created_at?: string | null + id?: string + message?: string + metadata?: Json | null + recurrence?: Json | null + reminder_time?: string + sent_at?: string | null + status?: string | null + updated_at?: string | null + user_id?: string + } Relationships: [ { - foreignKeyName: 'reminders_user_id_fkey'; - columns: ['user_id']; - isOneToOne: false; - referencedRelation: 'users'; - referencedColumns: ['id']; + foreignKeyName: "reminders_user_id_fkey" + columns: ["user_id"] + isOneToOne: false + referencedRelation: "users" + referencedColumns: ["id"] }, - ]; - }; + ] + } scheduled_reminders: { Row: { - created_at: string | null; - cron_expression: string | null; - delivery_channel: string; - delivery_target: string | null; - description: string | null; - id: string; - last_run_at: string | null; - max_runs: number | null; - metadata: Json | null; - next_run_at: string; - run_count: number | null; - status: string; - title: string; - updated_at: string | null; - user_id: string; - }; + created_at: string | null + cron_expression: string | null + delivery_channel: string + delivery_target: string | null + description: string | null + id: string + last_run_at: string | null + max_runs: number | null + metadata: Json | null + next_run_at: string + run_count: number | null + status: string + title: string + updated_at: string | null + user_id: string + } Insert: { - created_at?: string | null; - cron_expression?: string | null; - delivery_channel?: string; - delivery_target?: string | null; - description?: string | null; - id?: string; - last_run_at?: string | null; - max_runs?: number | null; - metadata?: Json | null; - next_run_at: string; - run_count?: number | null; - status?: string; - title: string; - updated_at?: string | null; - user_id: string; - }; + created_at?: string | null + cron_expression?: string | null + delivery_channel?: string + delivery_target?: string | null + description?: string | null + id?: string + last_run_at?: string | null + max_runs?: number | null + metadata?: Json | null + next_run_at: string + run_count?: number | null + status?: string + title: string + updated_at?: string | null + user_id: string + } Update: { - created_at?: string | null; - cron_expression?: string | null; - delivery_channel?: string; - delivery_target?: string | null; - description?: string | null; - id?: string; - last_run_at?: string | null; - max_runs?: number | null; - metadata?: Json | null; - next_run_at?: string; - run_count?: number | null; - status?: string; - title?: string; - updated_at?: string | null; - user_id?: string; - }; + created_at?: string | null + cron_expression?: string | null + delivery_channel?: string + delivery_target?: string | null + description?: string | null + id?: string + last_run_at?: string | null + max_runs?: number | null + metadata?: Json | null + next_run_at?: string + run_count?: number | null + status?: string + title?: string + updated_at?: string | null + user_id?: string + } Relationships: [ { - foreignKeyName: 'scheduled_reminders_user_id_fkey'; - columns: ['user_id']; - isOneToOne: false; - referencedRelation: 'users'; - referencedColumns: ['id']; + foreignKeyName: "scheduled_reminders_user_id_fkey" + columns: ["user_id"] + isOneToOne: false + referencedRelation: "users" + referencedColumns: ["id"] }, - ]; - }; + ] + } session_focus: { Row: { - context_snapshot: Json | null; - created_at: string | null; - focus_summary: string | null; - id: string; - project_id: string | null; - session_id: string | null; - updated_at: string | null; - user_id: string; - }; + context_snapshot: Json | null + created_at: string | null + focus_summary: string | null + id: string + project_id: string | null + session_id: string | null + updated_at: string | null + user_id: string + } Insert: { - context_snapshot?: Json | null; - created_at?: string | null; - focus_summary?: string | null; - id?: string; - project_id?: string | null; - session_id?: string | null; - updated_at?: string | null; - user_id: string; - }; + context_snapshot?: Json | null + created_at?: string | null + focus_summary?: string | null + id?: string + project_id?: string | null + session_id?: string | null + updated_at?: string | null + user_id: string + } Update: { - context_snapshot?: Json | null; - created_at?: string | null; - focus_summary?: string | null; - id?: string; - project_id?: string | null; - session_id?: string | null; - updated_at?: string | null; - user_id?: string; - }; + context_snapshot?: Json | null + created_at?: string | null + focus_summary?: string | null + id?: string + project_id?: string | null + session_id?: string | null + updated_at?: string | null + user_id?: string + } Relationships: [ { - foreignKeyName: 'session_focus_project_id_fkey'; - columns: ['project_id']; - isOneToOne: false; - referencedRelation: 'projects'; - referencedColumns: ['id']; + foreignKeyName: "session_focus_project_id_fkey" + columns: ["project_id"] + isOneToOne: false + referencedRelation: "projects" + referencedColumns: ["id"] }, { - foreignKeyName: 'session_focus_user_id_fkey'; - columns: ['user_id']; - isOneToOne: false; - referencedRelation: 'users'; - referencedColumns: ['id']; + foreignKeyName: "session_focus_user_id_fkey" + columns: ["user_id"] + isOneToOne: false + referencedRelation: "users" + referencedColumns: ["id"] }, - ]; - }; + ] + } session_logs: { Row: { - compacted_at: string | null; - compacted_into_memory_id: string | null; - content: string; - created_at: string | null; - id: string; - salience: string; - session_id: string; - }; + compacted_at: string | null + compacted_into_memory_id: string | null + content: string + created_at: string | null + id: string + salience: string + session_id: string + } Insert: { - compacted_at?: string | null; - compacted_into_memory_id?: string | null; - content: string; - created_at?: string | null; - id?: string; - salience?: string; - session_id: string; - }; + compacted_at?: string | null + compacted_into_memory_id?: string | null + content: string + created_at?: string | null + id?: string + salience?: string + session_id: string + } Update: { - compacted_at?: string | null; - compacted_into_memory_id?: string | null; - content?: string; - created_at?: string | null; - id?: string; - salience?: string; - session_id?: string; - }; + compacted_at?: string | null + compacted_into_memory_id?: string | null + content?: string + created_at?: string | null + id?: string + salience?: string + session_id?: string + } Relationships: [ { - foreignKeyName: 'session_logs_compacted_into_memory_id_fkey'; - columns: ['compacted_into_memory_id']; - isOneToOne: false; - referencedRelation: 'memories'; - referencedColumns: ['id']; + foreignKeyName: "session_logs_compacted_into_memory_id_fkey" + columns: ["compacted_into_memory_id"] + isOneToOne: false + referencedRelation: "memories" + referencedColumns: ["id"] }, { - foreignKeyName: 'session_logs_session_id_fkey'; - columns: ['session_id']; - isOneToOne: false; - referencedRelation: 'sessions'; - referencedColumns: ['id']; + foreignKeyName: "session_logs_session_id_fkey" + columns: ["session_id"] + isOneToOne: false + referencedRelation: "sessions" + referencedColumns: ["id"] }, - ]; - }; + ] + } sessions: { Row: { - agent_id: string | null; - backend: string | null; - backend_session_id: string | null; - claude_session_id: string | null; - compacting_since: string | null; - context: string | null; - current_phase: string | null; - ended_at: string | null; - id: string; - message_count: number | null; - metadata: Json | null; - model: string | null; - started_at: string | null; - status: string | null; - studio_id: string | null; - summary: string | null; - token_count: number | null; - updated_at: string | null; - user_id: string; - working_dir: string | null; - workspace_id: string | null; - }; + agent_id: string | null + backend: string | null + backend_session_id: string | null + claude_session_id: string | null + compacting_since: string | null + context: string | null + current_phase: string | null + ended_at: string | null + id: string + identity_id: string | null + message_count: number | null + metadata: Json | null + model: string | null + started_at: string | null + status: string | null + studio_id: string | null + summary: string | null + thread_key: string | null + token_count: number | null + updated_at: string | null + user_id: string + working_dir: string | null + workspace_id: string | null + } Insert: { - agent_id?: string | null; - backend?: string | null; - backend_session_id?: string | null; - claude_session_id?: string | null; - compacting_since?: string | null; - context?: string | null; - current_phase?: string | null; - ended_at?: string | null; - id?: string; - message_count?: number | null; - metadata?: Json | null; - model?: string | null; - started_at?: string | null; - status?: string | null; - studio_id?: string | null; - summary?: string | null; - token_count?: number | null; - updated_at?: string | null; - user_id: string; - working_dir?: string | null; - workspace_id?: string | null; - }; + agent_id?: string | null + backend?: string | null + backend_session_id?: string | null + claude_session_id?: string | null + compacting_since?: string | null + context?: string | null + current_phase?: string | null + ended_at?: string | null + id?: string + identity_id?: string | null + message_count?: number | null + metadata?: Json | null + model?: string | null + started_at?: string | null + status?: string | null + studio_id?: string | null + summary?: string | null + thread_key?: string | null + token_count?: number | null + updated_at?: string | null + user_id: string + working_dir?: string | null + workspace_id?: string | null + } Update: { - agent_id?: string | null; - backend?: string | null; - backend_session_id?: string | null; - claude_session_id?: string | null; - compacting_since?: string | null; - context?: string | null; - current_phase?: string | null; - ended_at?: string | null; - id?: string; - message_count?: number | null; - metadata?: Json | null; - model?: string | null; - started_at?: string | null; - status?: string | null; - studio_id?: string | null; - summary?: string | null; - token_count?: number | null; - updated_at?: string | null; - user_id?: string; - working_dir?: string | null; - workspace_id?: string | null; - }; + agent_id?: string | null + backend?: string | null + backend_session_id?: string | null + claude_session_id?: string | null + compacting_since?: string | null + context?: string | null + current_phase?: string | null + ended_at?: string | null + id?: string + identity_id?: string | null + message_count?: number | null + metadata?: Json | null + model?: string | null + started_at?: string | null + status?: string | null + studio_id?: string | null + summary?: string | null + thread_key?: string | null + token_count?: number | null + updated_at?: string | null + user_id?: string + working_dir?: string | null + workspace_id?: string | null + } Relationships: [ { - foreignKeyName: 'sessions_studio_id_fkey'; - columns: ['studio_id']; - isOneToOne: false; - referencedRelation: 'workspaces'; - referencedColumns: ['id']; + foreignKeyName: "sessions_identity_id_fkey" + columns: ["identity_id"] + isOneToOne: false + referencedRelation: "agent_identities" + referencedColumns: ["id"] + }, + { + foreignKeyName: "sessions_studio_id_fkey" + columns: ["studio_id"] + isOneToOne: false + referencedRelation: "workspaces" + referencedColumns: ["id"] }, { - foreignKeyName: 'sessions_user_id_fkey'; - columns: ['user_id']; - isOneToOne: false; - referencedRelation: 'users'; - referencedColumns: ['id']; + foreignKeyName: "sessions_user_id_fkey" + columns: ["user_id"] + isOneToOne: false + referencedRelation: "users" + referencedColumns: ["id"] }, { - foreignKeyName: 'sessions_workspace_id_fkey'; - columns: ['workspace_id']; - isOneToOne: false; - referencedRelation: 'workspaces'; - referencedColumns: ['id']; + foreignKeyName: "sessions_workspace_id_fkey" + columns: ["workspace_id"] + isOneToOne: false + referencedRelation: "workspaces" + referencedColumns: ["id"] }, - ]; - }; + ] + } skill_installations: { Row: { - config: Json | null; - enabled: boolean | null; - id: string; - installed_at: string | null; - last_used_at: string | null; - skill_id: string; - usage_count: number | null; - user_id: string; - version_pinned: string | null; - }; + config: Json | null + enabled: boolean | null + id: string + installed_at: string | null + last_used_at: string | null + skill_id: string + usage_count: number | null + user_id: string + version_pinned: string | null + } Insert: { - config?: Json | null; - enabled?: boolean | null; - id?: string; - installed_at?: string | null; - last_used_at?: string | null; - skill_id: string; - usage_count?: number | null; - user_id: string; - version_pinned?: string | null; - }; + config?: Json | null + enabled?: boolean | null + id?: string + installed_at?: string | null + last_used_at?: string | null + skill_id: string + usage_count?: number | null + user_id: string + version_pinned?: string | null + } Update: { - config?: Json | null; - enabled?: boolean | null; - id?: string; - installed_at?: string | null; - last_used_at?: string | null; - skill_id?: string; - usage_count?: number | null; - user_id?: string; - version_pinned?: string | null; - }; + config?: Json | null + enabled?: boolean | null + id?: string + installed_at?: string | null + last_used_at?: string | null + skill_id?: string + usage_count?: number | null + user_id?: string + version_pinned?: string | null + } Relationships: [ { - foreignKeyName: 'skill_installations_skill_id_fkey'; - columns: ['skill_id']; - isOneToOne: false; - referencedRelation: 'skills'; - referencedColumns: ['id']; + foreignKeyName: "skill_installations_skill_id_fkey" + columns: ["skill_id"] + isOneToOne: false + referencedRelation: "skills" + referencedColumns: ["id"] }, { - foreignKeyName: 'skill_installations_skill_id_fkey'; - columns: ['skill_id']; - isOneToOne: false; - referencedRelation: 'user_installed_skills'; - referencedColumns: ['skill_id']; + foreignKeyName: "skill_installations_skill_id_fkey" + columns: ["skill_id"] + isOneToOne: false + referencedRelation: "user_installed_skills" + referencedColumns: ["skill_id"] }, { - foreignKeyName: 'skill_installations_user_id_fkey'; - columns: ['user_id']; - isOneToOne: false; - referencedRelation: 'users'; - referencedColumns: ['id']; + foreignKeyName: "skill_installations_user_id_fkey" + columns: ["user_id"] + isOneToOne: false + referencedRelation: "users" + referencedColumns: ["id"] }, - ]; - }; + ] + } skill_versions: { Row: { - changelog: string | null; - content: string; - id: string; - manifest: Json; - published_at: string | null; - published_by: string | null; - skill_id: string; - version: string; - }; + changelog: string | null + content: string + id: string + manifest: Json + published_at: string | null + published_by: string | null + skill_id: string + version: string + } Insert: { - changelog?: string | null; - content?: string; - id?: string; - manifest?: Json; - published_at?: string | null; - published_by?: string | null; - skill_id: string; - version: string; - }; + changelog?: string | null + content?: string + id?: string + manifest?: Json + published_at?: string | null + published_by?: string | null + skill_id: string + version: string + } Update: { - changelog?: string | null; - content?: string; - id?: string; - manifest?: Json; - published_at?: string | null; - published_by?: string | null; - skill_id?: string; - version?: string; - }; + changelog?: string | null + content?: string + id?: string + manifest?: Json + published_at?: string | null + published_by?: string | null + skill_id?: string + version?: string + } Relationships: [ { - foreignKeyName: 'skill_versions_published_by_fkey'; - columns: ['published_by']; - isOneToOne: false; - referencedRelation: 'users'; - referencedColumns: ['id']; + foreignKeyName: "skill_versions_published_by_fkey" + columns: ["published_by"] + isOneToOne: false + referencedRelation: "users" + referencedColumns: ["id"] }, { - foreignKeyName: 'skill_versions_skill_id_fkey'; - columns: ['skill_id']; - isOneToOne: false; - referencedRelation: 'skills'; - referencedColumns: ['id']; + foreignKeyName: "skill_versions_skill_id_fkey" + columns: ["skill_id"] + isOneToOne: false + referencedRelation: "skills" + referencedColumns: ["id"] }, { - foreignKeyName: 'skill_versions_skill_id_fkey'; - columns: ['skill_id']; - isOneToOne: false; - referencedRelation: 'user_installed_skills'; - referencedColumns: ['skill_id']; + foreignKeyName: "skill_versions_skill_id_fkey" + columns: ["skill_id"] + isOneToOne: false + referencedRelation: "user_installed_skills" + referencedColumns: ["skill_id"] }, - ]; - }; + ] + } skills: { Row: { - author: string | null; - author_user_id: string | null; - category: string | null; - content: string; - created_at: string | null; - current_version: string; - deprecated_at: string | null; - deprecated_by: string | null; - deprecation_message: string | null; - description: string; - display_name: string; - emoji: string | null; - forked_from_id: string | null; - homepage_url: string | null; - id: string; - install_count: number | null; - is_official: boolean | null; - is_public: boolean | null; - is_verified: boolean | null; - last_published_by: string | null; - manifest: Json; - name: string; - published_at: string | null; - repository_url: string | null; - status: string | null; - tags: string[] | null; - type: string; - updated_at: string | null; - }; + author: string | null + author_user_id: string | null + category: string | null + content: string + created_at: string | null + current_version: string + deprecated_at: string | null + deprecated_by: string | null + deprecation_message: string | null + description: string + display_name: string + emoji: string | null + forked_from_id: string | null + homepage_url: string | null + id: string + install_count: number | null + is_official: boolean | null + is_public: boolean | null + is_verified: boolean | null + last_published_by: string | null + manifest: Json + name: string + published_at: string | null + repository_url: string | null + status: string | null + tags: string[] | null + type: string + updated_at: string | null + } Insert: { - author?: string | null; - author_user_id?: string | null; - category?: string | null; - content?: string; - created_at?: string | null; - current_version?: string; - deprecated_at?: string | null; - deprecated_by?: string | null; - deprecation_message?: string | null; - description: string; - display_name: string; - emoji?: string | null; - forked_from_id?: string | null; - homepage_url?: string | null; - id?: string; - install_count?: number | null; - is_official?: boolean | null; - is_public?: boolean | null; - is_verified?: boolean | null; - last_published_by?: string | null; - manifest?: Json; - name: string; - published_at?: string | null; - repository_url?: string | null; - status?: string | null; - tags?: string[] | null; - type: string; - updated_at?: string | null; - }; + author?: string | null + author_user_id?: string | null + category?: string | null + content?: string + created_at?: string | null + current_version?: string + deprecated_at?: string | null + deprecated_by?: string | null + deprecation_message?: string | null + description: string + display_name: string + emoji?: string | null + forked_from_id?: string | null + homepage_url?: string | null + id?: string + install_count?: number | null + is_official?: boolean | null + is_public?: boolean | null + is_verified?: boolean | null + last_published_by?: string | null + manifest?: Json + name: string + published_at?: string | null + repository_url?: string | null + status?: string | null + tags?: string[] | null + type: string + updated_at?: string | null + } Update: { - author?: string | null; - author_user_id?: string | null; - category?: string | null; - content?: string; - created_at?: string | null; - current_version?: string; - deprecated_at?: string | null; - deprecated_by?: string | null; - deprecation_message?: string | null; - description?: string; - display_name?: string; - emoji?: string | null; - forked_from_id?: string | null; - homepage_url?: string | null; - id?: string; - install_count?: number | null; - is_official?: boolean | null; - is_public?: boolean | null; - is_verified?: boolean | null; - last_published_by?: string | null; - manifest?: Json; - name?: string; - published_at?: string | null; - repository_url?: string | null; - status?: string | null; - tags?: string[] | null; - type?: string; - updated_at?: string | null; - }; + author?: string | null + author_user_id?: string | null + category?: string | null + content?: string + created_at?: string | null + current_version?: string + deprecated_at?: string | null + deprecated_by?: string | null + deprecation_message?: string | null + description?: string + display_name?: string + emoji?: string | null + forked_from_id?: string | null + homepage_url?: string | null + id?: string + install_count?: number | null + is_official?: boolean | null + is_public?: boolean | null + is_verified?: boolean | null + last_published_by?: string | null + manifest?: Json + name?: string + published_at?: string | null + repository_url?: string | null + status?: string | null + tags?: string[] | null + type?: string + updated_at?: string | null + } Relationships: [ { - foreignKeyName: 'skills_author_user_id_fkey'; - columns: ['author_user_id']; - isOneToOne: false; - referencedRelation: 'users'; - referencedColumns: ['id']; + foreignKeyName: "skills_author_user_id_fkey" + columns: ["author_user_id"] + isOneToOne: false + referencedRelation: "users" + referencedColumns: ["id"] }, { - foreignKeyName: 'skills_deprecated_by_fkey'; - columns: ['deprecated_by']; - isOneToOne: false; - referencedRelation: 'users'; - referencedColumns: ['id']; + foreignKeyName: "skills_deprecated_by_fkey" + columns: ["deprecated_by"] + isOneToOne: false + referencedRelation: "users" + referencedColumns: ["id"] }, { - foreignKeyName: 'skills_forked_from_id_fkey'; - columns: ['forked_from_id']; - isOneToOne: false; - referencedRelation: 'skills'; - referencedColumns: ['id']; + foreignKeyName: "skills_forked_from_id_fkey" + columns: ["forked_from_id"] + isOneToOne: false + referencedRelation: "skills" + referencedColumns: ["id"] }, { - foreignKeyName: 'skills_forked_from_id_fkey'; - columns: ['forked_from_id']; - isOneToOne: false; - referencedRelation: 'user_installed_skills'; - referencedColumns: ['skill_id']; + foreignKeyName: "skills_forked_from_id_fkey" + columns: ["forked_from_id"] + isOneToOne: false + referencedRelation: "user_installed_skills" + referencedColumns: ["skill_id"] }, { - foreignKeyName: 'skills_last_published_by_fkey'; - columns: ['last_published_by']; - isOneToOne: false; - referencedRelation: 'users'; - referencedColumns: ['id']; + foreignKeyName: "skills_last_published_by_fkey" + columns: ["last_published_by"] + isOneToOne: false + referencedRelation: "users" + referencedColumns: ["id"] }, - ]; - }; + ] + } tasks: { Row: { - completed_at: string | null; - created_at: string | null; - description: string | null; - due_date: string | null; - id: string; - metadata: Json | null; - priority: string | null; - status: string | null; - tags: string[] | null; - title: string; - updated_at: string | null; - user_id: string; - }; + completed_at: string | null + created_at: string | null + description: string | null + due_date: string | null + id: string + metadata: Json | null + priority: string | null + status: string | null + tags: string[] | null + title: string + updated_at: string | null + user_id: string + } Insert: { - completed_at?: string | null; - created_at?: string | null; - description?: string | null; - due_date?: string | null; - id?: string; - metadata?: Json | null; - priority?: string | null; - status?: string | null; - tags?: string[] | null; - title: string; - updated_at?: string | null; - user_id: string; - }; + completed_at?: string | null + created_at?: string | null + description?: string | null + due_date?: string | null + id?: string + metadata?: Json | null + priority?: string | null + status?: string | null + tags?: string[] | null + title: string + updated_at?: string | null + user_id: string + } Update: { - completed_at?: string | null; - created_at?: string | null; - description?: string | null; - due_date?: string | null; - id?: string; - metadata?: Json | null; - priority?: string | null; - status?: string | null; - tags?: string[] | null; - title?: string; - updated_at?: string | null; - user_id?: string; - }; + completed_at?: string | null + created_at?: string | null + description?: string | null + due_date?: string | null + id?: string + metadata?: Json | null + priority?: string | null + status?: string | null + tags?: string[] | null + title?: string + updated_at?: string | null + user_id?: string + } Relationships: [ { - foreignKeyName: 'tasks_user_id_fkey'; - columns: ['user_id']; - isOneToOne: false; - referencedRelation: 'users'; - referencedColumns: ['id']; + foreignKeyName: "tasks_user_id_fkey" + columns: ["user_id"] + isOneToOne: false + referencedRelation: "users" + referencedColumns: ["id"] }, - ]; - }; + ] + } trusted_users: { Row: { - added_at: string | null; - added_by: string | null; - id: string; - platform: string; - platform_user_id: string; - trust_level: Database['public']['Enums']['trust_level']; - user_id: string | null; - workspace_id: string | null; - }; + added_at: string | null + added_by: string | null + id: string + platform: string + platform_user_id: string + trust_level: Database["public"]["Enums"]["trust_level"] + user_id: string | null + workspace_id: string | null + } Insert: { - added_at?: string | null; - added_by?: string | null; - id?: string; - platform: string; - platform_user_id: string; - trust_level?: Database['public']['Enums']['trust_level']; - user_id?: string | null; - workspace_id?: string | null; - }; + added_at?: string | null + added_by?: string | null + id?: string + platform: string + platform_user_id: string + trust_level?: Database["public"]["Enums"]["trust_level"] + user_id?: string | null + workspace_id?: string | null + } Update: { - added_at?: string | null; - added_by?: string | null; - id?: string; - platform?: string; - platform_user_id?: string; - trust_level?: Database['public']['Enums']['trust_level']; - user_id?: string | null; - workspace_id?: string | null; - }; + added_at?: string | null + added_by?: string | null + id?: string + platform?: string + platform_user_id?: string + trust_level?: Database["public"]["Enums"]["trust_level"] + user_id?: string | null + workspace_id?: string | null + } Relationships: [ { - foreignKeyName: 'trusted_users_added_by_fkey'; - columns: ['added_by']; - isOneToOne: false; - referencedRelation: 'users'; - referencedColumns: ['id']; + foreignKeyName: "trusted_users_added_by_fkey" + columns: ["added_by"] + isOneToOne: false + referencedRelation: "users" + referencedColumns: ["id"] }, { - foreignKeyName: 'trusted_users_user_id_fkey'; - columns: ['user_id']; - isOneToOne: false; - referencedRelation: 'users'; - referencedColumns: ['id']; + foreignKeyName: "trusted_users_user_id_fkey" + columns: ["user_id"] + isOneToOne: false + referencedRelation: "users" + referencedColumns: ["id"] }, { - foreignKeyName: 'trusted_users_workspace_id_fkey'; - columns: ['workspace_id']; - isOneToOne: false; - referencedRelation: 'workspace_containers'; - referencedColumns: ['id']; + foreignKeyName: "trusted_users_workspace_id_fkey" + columns: ["workspace_id"] + isOneToOne: false + referencedRelation: "workspace_containers" + referencedColumns: ["id"] }, - ]; - }; + ] + } user_identity: { Row: { - created_at: string | null; - id: string; - process_md: string | null; - shared_values_md: string | null; - updated_at: string | null; - user_id: string; - user_profile_md: string | null; - version: number | null; - workspace_id: string | null; - }; + created_at: string | null + id: string + process_md: string | null + shared_values_md: string | null + updated_at: string | null + user_id: string + user_profile_md: string | null + version: number | null + workspace_id: string | null + } Insert: { - created_at?: string | null; - id?: string; - process_md?: string | null; - shared_values_md?: string | null; - updated_at?: string | null; - user_id: string; - user_profile_md?: string | null; - version?: number | null; - workspace_id?: string | null; - }; + created_at?: string | null + id?: string + process_md?: string | null + shared_values_md?: string | null + updated_at?: string | null + user_id: string + user_profile_md?: string | null + version?: number | null + workspace_id?: string | null + } Update: { - created_at?: string | null; - id?: string; - process_md?: string | null; - shared_values_md?: string | null; - updated_at?: string | null; - user_id?: string; - user_profile_md?: string | null; - version?: number | null; - workspace_id?: string | null; - }; + created_at?: string | null + id?: string + process_md?: string | null + shared_values_md?: string | null + updated_at?: string | null + user_id?: string + user_profile_md?: string | null + version?: number | null + workspace_id?: string | null + } Relationships: [ { - foreignKeyName: 'user_identity_user_id_fkey'; - columns: ['user_id']; - isOneToOne: false; - referencedRelation: 'users'; - referencedColumns: ['id']; + foreignKeyName: "user_identity_user_id_fkey" + columns: ["user_id"] + isOneToOne: false + referencedRelation: "users" + referencedColumns: ["id"] }, { - foreignKeyName: 'user_identity_workspace_id_fkey'; - columns: ['workspace_id']; - isOneToOne: false; - referencedRelation: 'workspace_containers'; - referencedColumns: ['id']; + foreignKeyName: "user_identity_workspace_id_fkey" + columns: ["workspace_id"] + isOneToOne: false + referencedRelation: "workspace_containers" + referencedColumns: ["id"] }, - ]; - }; + ] + } user_identity_history: { Row: { - archived_at: string | null; - change_type: string; - created_at: string; - id: string; - identity_id: string; - process_md: string | null; - shared_values_md: string | null; - user_id: string; - user_profile_md: string | null; - version: number; - workspace_id: string | null; - }; + archived_at: string | null + change_type: string + created_at: string + id: string + identity_id: string + process_md: string | null + shared_values_md: string | null + user_id: string + user_profile_md: string | null + version: number + workspace_id: string | null + } Insert: { - archived_at?: string | null; - change_type?: string; - created_at: string; - id?: string; - identity_id: string; - process_md?: string | null; - shared_values_md?: string | null; - user_id: string; - user_profile_md?: string | null; - version: number; - workspace_id?: string | null; - }; + archived_at?: string | null + change_type?: string + created_at: string + id?: string + identity_id: string + process_md?: string | null + shared_values_md?: string | null + user_id: string + user_profile_md?: string | null + version: number + workspace_id?: string | null + } Update: { - archived_at?: string | null; - change_type?: string; - created_at?: string; - id?: string; - identity_id?: string; - process_md?: string | null; - shared_values_md?: string | null; - user_id?: string; - user_profile_md?: string | null; - version?: number; - workspace_id?: string | null; - }; + archived_at?: string | null + change_type?: string + created_at?: string + id?: string + identity_id?: string + process_md?: string | null + shared_values_md?: string | null + user_id?: string + user_profile_md?: string | null + version?: number + workspace_id?: string | null + } Relationships: [ { - foreignKeyName: 'user_identity_history_user_id_fkey'; - columns: ['user_id']; - isOneToOne: false; - referencedRelation: 'users'; - referencedColumns: ['id']; + foreignKeyName: "user_identity_history_user_id_fkey" + columns: ["user_id"] + isOneToOne: false + referencedRelation: "users" + referencedColumns: ["id"] }, { - foreignKeyName: 'user_identity_history_workspace_id_fkey'; - columns: ['workspace_id']; - isOneToOne: false; - referencedRelation: 'workspace_containers'; - referencedColumns: ['id']; + foreignKeyName: "user_identity_history_workspace_id_fkey" + columns: ["workspace_id"] + isOneToOne: false + referencedRelation: "workspace_containers" + referencedColumns: ["id"] }, - ]; - }; + ] + } user_permissions: { Row: { - enabled: boolean; - expires_at: string | null; - granted_at: string | null; - granted_by: string | null; - id: string; - permission_id: string; - reason: string | null; - user_id: string; - }; + enabled: boolean + expires_at: string | null + granted_at: string | null + granted_by: string | null + id: string + permission_id: string + reason: string | null + user_id: string + } Insert: { - enabled: boolean; - expires_at?: string | null; - granted_at?: string | null; - granted_by?: string | null; - id?: string; - permission_id: string; - reason?: string | null; - user_id: string; - }; + enabled: boolean + expires_at?: string | null + granted_at?: string | null + granted_by?: string | null + id?: string + permission_id: string + reason?: string | null + user_id: string + } Update: { - enabled?: boolean; - expires_at?: string | null; - granted_at?: string | null; - granted_by?: string | null; - id?: string; - permission_id?: string; - reason?: string | null; - user_id?: string; - }; + enabled?: boolean + expires_at?: string | null + granted_at?: string | null + granted_by?: string | null + id?: string + permission_id?: string + reason?: string | null + user_id?: string + } Relationships: [ { - foreignKeyName: 'user_permissions_granted_by_fkey'; - columns: ['granted_by']; - isOneToOne: false; - referencedRelation: 'users'; - referencedColumns: ['id']; + foreignKeyName: "user_permissions_granted_by_fkey" + columns: ["granted_by"] + isOneToOne: false + referencedRelation: "users" + referencedColumns: ["id"] }, { - foreignKeyName: 'user_permissions_permission_id_fkey'; - columns: ['permission_id']; - isOneToOne: false; - referencedRelation: 'permission_definitions'; - referencedColumns: ['id']; + foreignKeyName: "user_permissions_permission_id_fkey" + columns: ["permission_id"] + isOneToOne: false + referencedRelation: "permission_definitions" + referencedColumns: ["id"] }, { - foreignKeyName: 'user_permissions_user_id_fkey'; - columns: ['user_id']; - isOneToOne: false; - referencedRelation: 'users'; - referencedColumns: ['id']; + foreignKeyName: "user_permissions_user_id_fkey" + columns: ["user_id"] + isOneToOne: false + referencedRelation: "users" + referencedColumns: ["id"] }, - ]; - }; + ] + } users: { Row: { - created_at: string | null; - discord_id: string | null; - email: string | null; - first_name: string | null; - id: string; - last_login_at: string | null; - last_name: string | null; - phone_number: string | null; - preferences: Json | null; - telegram_id: number | null; - telegram_username: string | null; - timezone: string | null; - updated_at: string | null; - username: string | null; - whatsapp_id: string | null; - }; + created_at: string | null + discord_id: string | null + email: string | null + first_name: string | null + id: string + last_name: string | null + phone_number: string | null + preferences: Json | null + telegram_id: number | null + telegram_username: string | null + timezone: string | null + updated_at: string | null + username: string | null + whatsapp_id: string | null + } Insert: { - created_at?: string | null; - discord_id?: string | null; - email?: string | null; - first_name?: string | null; - id?: string; - last_login_at?: string | null; - last_name?: string | null; - phone_number?: string | null; - preferences?: Json | null; - telegram_id?: number | null; - telegram_username?: string | null; - timezone?: string | null; - updated_at?: string | null; - username?: string | null; - whatsapp_id?: string | null; - }; + created_at?: string | null + discord_id?: string | null + email?: string | null + first_name?: string | null + id?: string + last_name?: string | null + phone_number?: string | null + preferences?: Json | null + telegram_id?: number | null + telegram_username?: string | null + timezone?: string | null + updated_at?: string | null + username?: string | null + whatsapp_id?: string | null + } Update: { - created_at?: string | null; - discord_id?: string | null; - email?: string | null; - first_name?: string | null; - id?: string; - last_login_at?: string | null; - last_name?: string | null; - phone_number?: string | null; - preferences?: Json | null; - telegram_id?: number | null; - telegram_username?: string | null; - timezone?: string | null; - updated_at?: string | null; - username?: string | null; - whatsapp_id?: string | null; - }; - Relationships: []; - }; + created_at?: string | null + discord_id?: string | null + email?: string | null + first_name?: string | null + id?: string + last_name?: string | null + phone_number?: string | null + preferences?: Json | null + telegram_id?: number | null + telegram_username?: string | null + timezone?: string | null + updated_at?: string | null + username?: string | null + whatsapp_id?: string | null + } + Relationships: [] + } workspace_containers: { Row: { - archived_at: string | null; - created_at: string | null; - description: string | null; - id: string; - metadata: Json | null; - name: string; - slug: string; - type: string; - updated_at: string | null; - user_id: string; - }; + archived_at: string | null + created_at: string | null + description: string | null + id: string + metadata: Json | null + name: string + slug: string + type: string + updated_at: string | null + user_id: string + } Insert: { - archived_at?: string | null; - created_at?: string | null; - description?: string | null; - id?: string; - metadata?: Json | null; - name: string; - slug: string; - type?: string; - updated_at?: string | null; - user_id: string; - }; + archived_at?: string | null + created_at?: string | null + description?: string | null + id?: string + metadata?: Json | null + name: string + slug: string + type?: string + updated_at?: string | null + user_id: string + } Update: { - archived_at?: string | null; - created_at?: string | null; - description?: string | null; - id?: string; - metadata?: Json | null; - name?: string; - slug?: string; - type?: string; - updated_at?: string | null; - user_id?: string; - }; + archived_at?: string | null + created_at?: string | null + description?: string | null + id?: string + metadata?: Json | null + name?: string + slug?: string + type?: string + updated_at?: string | null + user_id?: string + } Relationships: [ { - foreignKeyName: 'workspace_containers_user_id_fkey'; - columns: ['user_id']; - isOneToOne: false; - referencedRelation: 'users'; - referencedColumns: ['id']; + foreignKeyName: "workspace_containers_user_id_fkey" + columns: ["user_id"] + isOneToOne: false + referencedRelation: "users" + referencedColumns: ["id"] }, - ]; - }; + ] + } workspace_members: { Row: { - created_at: string | null; - id: string; - role: string; - user_id: string; - workspace_id: string; - }; + created_at: string | null + id: string + role: string + user_id: string + workspace_id: string + } Insert: { - created_at?: string | null; - id?: string; - role?: string; - user_id: string; - workspace_id: string; - }; + created_at?: string | null + id?: string + role?: string + user_id: string + workspace_id: string + } Update: { - created_at?: string | null; - id?: string; - role?: string; - user_id?: string; - workspace_id?: string; - }; + created_at?: string | null + id?: string + role?: string + user_id?: string + workspace_id?: string + } Relationships: [ { - foreignKeyName: 'workspace_members_user_id_fkey'; - columns: ['user_id']; - isOneToOne: false; - referencedRelation: 'users'; - referencedColumns: ['id']; + foreignKeyName: "workspace_members_user_id_fkey" + columns: ["user_id"] + isOneToOne: false + referencedRelation: "users" + referencedColumns: ["id"] }, { - foreignKeyName: 'workspace_members_workspace_id_fkey'; - columns: ['workspace_id']; - isOneToOne: false; - referencedRelation: 'workspace_containers'; - referencedColumns: ['id']; + foreignKeyName: "workspace_members_workspace_id_fkey" + columns: ["workspace_id"] + isOneToOne: false + referencedRelation: "workspace_containers" + referencedColumns: ["id"] }, - ]; - }; + ] + } workspaces: { Row: { - agent_id: string | null; - archived_at: string | null; - base_branch: string | null; - branch: string; - cleaned_at: string | null; - created_at: string | null; - id: string; - metadata: Json | null; - purpose: string | null; - repo_root: string; - session_id: string | null; - status: string; - updated_at: string | null; - user_id: string; - work_type: string | null; - worktree_path: string; - }; + agent_id: string | null + archived_at: string | null + base_branch: string | null + branch: string + cleaned_at: string | null + created_at: string | null + id: string + identity_id: string | null + metadata: Json | null + purpose: string | null + repo_root: string + session_id: string | null + status: string + updated_at: string | null + user_id: string + work_type: string | null + worktree_path: string + } Insert: { - agent_id?: string | null; - archived_at?: string | null; - base_branch?: string | null; - branch: string; - cleaned_at?: string | null; - created_at?: string | null; - id?: string; - metadata?: Json | null; - purpose?: string | null; - repo_root: string; - session_id?: string | null; - status?: string; - updated_at?: string | null; - user_id: string; - work_type?: string | null; - worktree_path: string; - }; + agent_id?: string | null + archived_at?: string | null + base_branch?: string | null + branch: string + cleaned_at?: string | null + created_at?: string | null + id?: string + identity_id?: string | null + metadata?: Json | null + purpose?: string | null + repo_root: string + session_id?: string | null + status?: string + updated_at?: string | null + user_id: string + work_type?: string | null + worktree_path: string + } Update: { - agent_id?: string | null; - archived_at?: string | null; - base_branch?: string | null; - branch?: string; - cleaned_at?: string | null; - created_at?: string | null; - id?: string; - metadata?: Json | null; - purpose?: string | null; - repo_root?: string; - session_id?: string | null; - status?: string; - updated_at?: string | null; - user_id?: string; - work_type?: string | null; - worktree_path?: string; - }; + agent_id?: string | null + archived_at?: string | null + base_branch?: string | null + branch?: string + cleaned_at?: string | null + created_at?: string | null + id?: string + identity_id?: string | null + metadata?: Json | null + purpose?: string | null + repo_root?: string + session_id?: string | null + status?: string + updated_at?: string | null + user_id?: string + work_type?: string | null + worktree_path?: string + } Relationships: [ { - foreignKeyName: 'workspaces_session_id_fkey'; - columns: ['session_id']; - isOneToOne: false; - referencedRelation: 'sessions'; - referencedColumns: ['id']; + foreignKeyName: "workspaces_identity_id_fkey" + columns: ["identity_id"] + isOneToOne: false + referencedRelation: "agent_identities" + referencedColumns: ["id"] + }, + { + foreignKeyName: "workspaces_session_id_fkey" + columns: ["session_id"] + isOneToOne: false + referencedRelation: "sessions" + referencedColumns: ["id"] }, { - foreignKeyName: 'workspaces_user_id_fkey'; - columns: ['user_id']; - isOneToOne: false; - referencedRelation: 'users'; - referencedColumns: ['id']; + foreignKeyName: "workspaces_user_id_fkey" + columns: ["user_id"] + isOneToOne: false + referencedRelation: "users" + referencedColumns: ["id"] }, - ]; - }; - }; + ] + } + } Views: { user_installed_skills: { Row: { - author: string | null; - category: string | null; - content: string | null; - current_version: string | null; - description: string | null; - display_name: string | null; - emoji: string | null; - enabled: boolean | null; - installation_id: string | null; - installed_at: string | null; - is_official: boolean | null; - is_verified: boolean | null; - last_used_at: string | null; - manifest: Json | null; - name: string | null; - repository_url: string | null; - resolved_content: string | null; - resolved_manifest: Json | null; - resolved_version: string | null; - skill_id: string | null; - tags: string[] | null; - type: string | null; - usage_count: number | null; - user_config: Json | null; - user_id: string | null; - version_pinned: string | null; - }; + author: string | null + category: string | null + content: string | null + current_version: string | null + description: string | null + display_name: string | null + emoji: string | null + enabled: boolean | null + installation_id: string | null + installed_at: string | null + is_official: boolean | null + is_verified: boolean | null + last_used_at: string | null + manifest: Json | null + name: string | null + repository_url: string | null + resolved_content: string | null + resolved_manifest: Json | null + resolved_version: string | null + skill_id: string | null + tags: string[] | null + type: string | null + usage_count: number | null + user_config: Json | null + user_id: string | null + version_pinned: string | null + } Relationships: [ { - foreignKeyName: 'skill_installations_user_id_fkey'; - columns: ['user_id']; - isOneToOne: false; - referencedRelation: 'users'; - referencedColumns: ['id']; + foreignKeyName: "skill_installations_user_id_fkey" + columns: ["user_id"] + isOneToOne: false + referencedRelation: "users" + referencedColumns: ["id"] }, - ]; - }; - }; + ] + } + } Functions: { match_links: { Args: { - match_count?: number; - match_threshold?: number; - p_user_id?: string; - query_embedding: string; - }; + match_count?: number + match_threshold?: number + p_user_id?: string + query_embedding: string + } Returns: { - description: string; - id: string; - similarity: number; - tags: string[]; - title: string; - url: string; - }[]; - }; + description: string + id: string + similarity: number + tags: string[] + title: string + url: string + }[] + } match_messages: { Args: { - match_count?: number; - match_threshold?: number; - p_user_id?: string; - query_embedding: string; - }; + match_count?: number + match_threshold?: number + p_user_id?: string + query_embedding: string + } Returns: { - content: string; - conversation_id: string; - created_at: string; - id: string; - similarity: number; - }[]; - }; + content: string + conversation_id: string + created_at: string + id: string + similarity: number + }[] + } match_notes: { Args: { - match_count?: number; - match_threshold?: number; - p_user_id?: string; - query_embedding: string; - }; + match_count?: number + match_threshold?: number + p_user_id?: string + query_embedding: string + } Returns: { - content: string; - id: string; - similarity: number; - tags: string[]; - title: string; - }[]; - }; - show_limit: { Args: never; Returns: number }; - show_trgm: { Args: { '': string }; Returns: string[] }; - trigger_heartbeat: { Args: never; Returns: undefined }; - }; + content: string + id: string + similarity: number + tags: string[] + title: string + }[] + } + show_limit: { Args: never; Returns: number } + show_trgm: { Args: { "": string }; Returns: string[] } + trigger_heartbeat: { Args: never; Returns: undefined } + } Enums: { activity_type: - | 'message_in' - | 'message_out' - | 'tool_call' - | 'tool_result' - | 'agent_spawn' - | 'agent_complete' - | 'state_change' - | 'thinking' - | 'error'; - trust_level: 'owner' | 'admin' | 'member'; - }; + | "message_in" + | "message_out" + | "tool_call" + | "tool_result" + | "agent_spawn" + | "agent_complete" + | "state_change" + | "thinking" + | "error" + trust_level: "owner" | "admin" | "member" + } CompositeTypes: { - [_ in never]: never; - }; - }; -}; + [_ in never]: never + } + } +} -type DatabaseWithoutInternals = Omit; +type DatabaseWithoutInternals = Omit -type DefaultSchema = DatabaseWithoutInternals[Extract]; +type DefaultSchema = DatabaseWithoutInternals[Extract] export type Tables< DefaultSchemaTableNameOrOptions extends - | keyof (DefaultSchema['Tables'] & DefaultSchema['Views']) + | keyof (DefaultSchema["Tables"] & DefaultSchema["Views"]) | { schema: keyof DatabaseWithoutInternals }, TableName extends DefaultSchemaTableNameOrOptions extends { - schema: keyof DatabaseWithoutInternals; + schema: keyof DatabaseWithoutInternals } - ? keyof (DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions['schema']]['Tables'] & - DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions['schema']]['Views']) + ? keyof (DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Tables"] & + DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Views"]) : never = never, > = DefaultSchemaTableNameOrOptions extends { - schema: keyof DatabaseWithoutInternals; + schema: keyof DatabaseWithoutInternals } - ? (DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions['schema']]['Tables'] & - DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions['schema']]['Views'])[TableName] extends { - Row: infer R; + ? (DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Tables"] & + DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Views"])[TableName] extends { + Row: infer R } ? R : never - : DefaultSchemaTableNameOrOptions extends keyof (DefaultSchema['Tables'] & DefaultSchema['Views']) - ? (DefaultSchema['Tables'] & DefaultSchema['Views'])[DefaultSchemaTableNameOrOptions] extends { - Row: infer R; + : DefaultSchemaTableNameOrOptions extends keyof (DefaultSchema["Tables"] & + DefaultSchema["Views"]) + ? (DefaultSchema["Tables"] & + DefaultSchema["Views"])[DefaultSchemaTableNameOrOptions] extends { + Row: infer R } ? R : never - : never; + : never export type TablesInsert< DefaultSchemaTableNameOrOptions extends - | keyof DefaultSchema['Tables'] + | keyof DefaultSchema["Tables"] | { schema: keyof DatabaseWithoutInternals }, TableName extends DefaultSchemaTableNameOrOptions extends { - schema: keyof DatabaseWithoutInternals; + schema: keyof DatabaseWithoutInternals } - ? keyof DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions['schema']]['Tables'] + ? keyof DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Tables"] : never = never, > = DefaultSchemaTableNameOrOptions extends { - schema: keyof DatabaseWithoutInternals; + schema: keyof DatabaseWithoutInternals } - ? DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions['schema']]['Tables'][TableName] extends { - Insert: infer I; + ? DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Tables"][TableName] extends { + Insert: infer I } ? I : never - : DefaultSchemaTableNameOrOptions extends keyof DefaultSchema['Tables'] - ? DefaultSchema['Tables'][DefaultSchemaTableNameOrOptions] extends { - Insert: infer I; + : DefaultSchemaTableNameOrOptions extends keyof DefaultSchema["Tables"] + ? DefaultSchema["Tables"][DefaultSchemaTableNameOrOptions] extends { + Insert: infer I } ? I : never - : never; + : never export type TablesUpdate< DefaultSchemaTableNameOrOptions extends - | keyof DefaultSchema['Tables'] + | keyof DefaultSchema["Tables"] | { schema: keyof DatabaseWithoutInternals }, TableName extends DefaultSchemaTableNameOrOptions extends { - schema: keyof DatabaseWithoutInternals; + schema: keyof DatabaseWithoutInternals } - ? keyof DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions['schema']]['Tables'] + ? keyof DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Tables"] : never = never, > = DefaultSchemaTableNameOrOptions extends { - schema: keyof DatabaseWithoutInternals; + schema: keyof DatabaseWithoutInternals } - ? DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions['schema']]['Tables'][TableName] extends { - Update: infer U; + ? DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Tables"][TableName] extends { + Update: infer U } ? U : never - : DefaultSchemaTableNameOrOptions extends keyof DefaultSchema['Tables'] - ? DefaultSchema['Tables'][DefaultSchemaTableNameOrOptions] extends { - Update: infer U; + : DefaultSchemaTableNameOrOptions extends keyof DefaultSchema["Tables"] + ? DefaultSchema["Tables"][DefaultSchemaTableNameOrOptions] extends { + Update: infer U } ? U : never - : never; + : never export type Enums< DefaultSchemaEnumNameOrOptions extends - | keyof DefaultSchema['Enums'] + | keyof DefaultSchema["Enums"] | { schema: keyof DatabaseWithoutInternals }, EnumName extends DefaultSchemaEnumNameOrOptions extends { - schema: keyof DatabaseWithoutInternals; + schema: keyof DatabaseWithoutInternals } - ? keyof DatabaseWithoutInternals[DefaultSchemaEnumNameOrOptions['schema']]['Enums'] + ? keyof DatabaseWithoutInternals[DefaultSchemaEnumNameOrOptions["schema"]]["Enums"] : never = never, > = DefaultSchemaEnumNameOrOptions extends { - schema: keyof DatabaseWithoutInternals; + schema: keyof DatabaseWithoutInternals } - ? DatabaseWithoutInternals[DefaultSchemaEnumNameOrOptions['schema']]['Enums'][EnumName] - : DefaultSchemaEnumNameOrOptions extends keyof DefaultSchema['Enums'] - ? DefaultSchema['Enums'][DefaultSchemaEnumNameOrOptions] - : never; + ? DatabaseWithoutInternals[DefaultSchemaEnumNameOrOptions["schema"]]["Enums"][EnumName] + : DefaultSchemaEnumNameOrOptions extends keyof DefaultSchema["Enums"] + ? DefaultSchema["Enums"][DefaultSchemaEnumNameOrOptions] + : never export type CompositeTypes< PublicCompositeTypeNameOrOptions extends - | keyof DefaultSchema['CompositeTypes'] + | keyof DefaultSchema["CompositeTypes"] | { schema: keyof DatabaseWithoutInternals }, CompositeTypeName extends PublicCompositeTypeNameOrOptions extends { - schema: keyof DatabaseWithoutInternals; + schema: keyof DatabaseWithoutInternals } - ? keyof DatabaseWithoutInternals[PublicCompositeTypeNameOrOptions['schema']]['CompositeTypes'] + ? keyof DatabaseWithoutInternals[PublicCompositeTypeNameOrOptions["schema"]]["CompositeTypes"] : never = never, > = PublicCompositeTypeNameOrOptions extends { - schema: keyof DatabaseWithoutInternals; + schema: keyof DatabaseWithoutInternals } - ? DatabaseWithoutInternals[PublicCompositeTypeNameOrOptions['schema']]['CompositeTypes'][CompositeTypeName] - : PublicCompositeTypeNameOrOptions extends keyof DefaultSchema['CompositeTypes'] - ? DefaultSchema['CompositeTypes'][PublicCompositeTypeNameOrOptions] - : never; + ? DatabaseWithoutInternals[PublicCompositeTypeNameOrOptions["schema"]]["CompositeTypes"][CompositeTypeName] + : PublicCompositeTypeNameOrOptions extends keyof DefaultSchema["CompositeTypes"] + ? DefaultSchema["CompositeTypes"][PublicCompositeTypeNameOrOptions] + : never export const Constants = { public: { Enums: { activity_type: [ - 'message_in', - 'message_out', - 'tool_call', - 'tool_result', - 'agent_spawn', - 'agent_complete', - 'state_change', - 'thinking', - 'error', + "message_in", + "message_out", + "tool_call", + "tool_result", + "agent_spawn", + "agent_complete", + "state_change", + "thinking", + "error", ], - trust_level: ['owner', 'admin', 'member'], + trust_level: ["owner", "admin", "member"], }, }, -} as const; +} as const + diff --git a/packages/api/src/mcp/tools/agent-triggers.ts b/packages/api/src/mcp/tools/agent-triggers.ts index 0a2ac0d0..56ca1ba1 100644 --- a/packages/api/src/mcp/tools/agent-triggers.ts +++ b/packages/api/src/mcp/tools/agent-triggers.ts @@ -46,6 +46,10 @@ export const triggerAgentSchema = z.object({ .optional() .default('normal') .describe('Priority level for the trigger'), + threadKey: z + .string() + .optional() + .describe('Thread key for session routing on the recipient side (e.g., "pr:32")'), metadata: z .record(z.unknown()) .optional() @@ -77,6 +81,7 @@ export async function handleTriggerAgent( summary: args.summary, inboxMessageId: args.inboxMessageId, priority: args.priority, + threadKey: args.threadKey, metadata: args.metadata, }; diff --git a/packages/api/src/mcp/tools/inbox-handlers.test.ts b/packages/api/src/mcp/tools/inbox-handlers.test.ts new file mode 100644 index 00000000..0c313ed2 --- /dev/null +++ b/packages/api/src/mcp/tools/inbox-handlers.test.ts @@ -0,0 +1,235 @@ +/** + * Inbox Handler Tests - threadKey + * + * Tests for threadKey support in send_to_inbox and get_inbox tools. + */ + +import { describe, it, expect, vi, beforeEach } from 'vitest'; +import { handleSendToInbox, handleGetInbox } from './inbox-handlers'; + +// Mock user-resolver +vi.mock('../../services/user-resolver', async (importOriginal) => { + const actual = await importOriginal(); + return { + ...actual, + resolveUserOrThrow: vi.fn().mockResolvedValue({ + user: { id: 'user-123' }, + resolvedBy: 'userId', + }), + }; +}); + +// Mock logger +vi.mock('../../utils/logger', () => ({ + logger: { + info: vi.fn(), + warn: vi.fn(), + error: vi.fn(), + debug: vi.fn(), + }, +})); + +// Mock agent gateway +vi.mock('../../channels/agent-gateway.js', () => ({ + getAgentGateway: vi.fn().mockReturnValue({ + processTrigger: vi.fn().mockResolvedValue({ + success: true, + triggerId: 'trigger-1', + processed: true, + }), + }), +})); + +function createMockSupabase(overrides: { + insertReturn?: { data: unknown; error: unknown }; + selectReturn?: { data: unknown; error: unknown; count?: number }; +} = {}) { + const defaultMessage = { + id: 'msg-123', + created_at: '2026-02-15T10:00:00Z', + thread_key: null, + recipient_agent_id: 'lumen', + sender_agent_id: 'wren', + subject: 'PR review needed', + content: 'Please review PR #32', + message_type: 'task_request', + priority: 'normal', + status: 'unread', + related_session_id: null, + related_artifact_uri: null, + metadata: {}, + read_at: null, + }; + + const insertReturn = overrides.insertReturn || { data: defaultMessage, error: null }; + + const chainable = { + insert: vi.fn().mockReturnValue({ + select: vi.fn().mockReturnValue({ + single: vi.fn().mockResolvedValue(insertReturn), + }), + }), + select: vi.fn().mockReturnValue({ + eq: vi.fn().mockReturnThis(), + order: vi.fn().mockReturnThis(), + limit: vi.fn().mockReturnThis(), + or: vi.fn().mockResolvedValue( + overrides.selectReturn || { data: [defaultMessage], error: null } + ), + }), + }; + + // For count query + const countChainable = { + select: vi.fn().mockReturnValue({ + eq: vi.fn().mockReturnValue({ + eq: vi.fn().mockReturnValue({ + eq: vi.fn().mockResolvedValue({ count: 1 }), + }), + }), + }), + }; + + const fromFn = vi.fn().mockImplementation((table: string) => { + // Return different chainable objects depending on usage + return chainable; + }); + + return { from: fromFn, _chainable: chainable, _countChainable: countChainable }; +} + +function createMockDataComposer(supabase?: ReturnType) { + const sb = supabase || createMockSupabase(); + return { + getClient: vi.fn().mockReturnValue(sb), + repositories: {}, + }; +} + +// ===================================================== +// SEND TO INBOX - threadKey +// ===================================================== + +describe('handleSendToInbox - threadKey', () => { + it('should include threadKey in DB insert when provided', async () => { + const mockSb = createMockSupabase(); + const mockDc = createMockDataComposer(mockSb); + + await handleSendToInbox( + { + email: 'test@test.com', + recipientAgentId: 'lumen', + senderAgentId: 'wren', + content: 'Review PR #32', + messageType: 'task_request', + threadKey: 'pr:32', + }, + mockDc as never + ); + + // Verify the insert was called with thread_key + expect(mockSb._chainable.insert).toHaveBeenCalledWith( + expect.objectContaining({ + thread_key: 'pr:32', + }) + ); + }); + + it('should insert null thread_key when threadKey not provided', async () => { + const mockSb = createMockSupabase(); + const mockDc = createMockDataComposer(mockSb); + + await handleSendToInbox( + { + email: 'test@test.com', + recipientAgentId: 'lumen', + senderAgentId: 'wren', + content: 'Hello', + }, + mockDc as never + ); + + expect(mockSb._chainable.insert).toHaveBeenCalledWith( + expect.objectContaining({ + thread_key: null, + }) + ); + }); + + it('should include threadKey in response when provided', async () => { + const mockSb = createMockSupabase({ + insertReturn: { + data: { + id: 'msg-456', + created_at: '2026-02-15T10:00:00Z', + thread_key: 'pr:32', + }, + error: null, + }, + }); + const mockDc = createMockDataComposer(mockSb); + + const result = await handleSendToInbox( + { + email: 'test@test.com', + recipientAgentId: 'lumen', + senderAgentId: 'wren', + content: 'Review PR #32', + threadKey: 'pr:32', + }, + mockDc as never + ); + + const parsed = JSON.parse(result.content[0].text); + expect(parsed.threadKey).toBe('pr:32'); + expect(parsed.hint).toBeUndefined(); + }); + + it('should include hint when threadKey is missing', async () => { + const mockSb = createMockSupabase(); + const mockDc = createMockDataComposer(mockSb); + + const result = await handleSendToInbox( + { + email: 'test@test.com', + recipientAgentId: 'lumen', + senderAgentId: 'wren', + content: 'Hello', + }, + mockDc as never + ); + + const parsed = JSON.parse(result.content[0].text); + expect(parsed.threadKey).toBeNull(); + expect(parsed.hint).toBeDefined(); + expect(parsed.hint).toContain('threadKey'); + }); + + it('should pass threadKey in trigger payload', async () => { + const { getAgentGateway } = await import('../../channels/agent-gateway.js'); + const mockGateway = (getAgentGateway as ReturnType)(); + + const mockSb = createMockSupabase(); + const mockDc = createMockDataComposer(mockSb); + + await handleSendToInbox( + { + email: 'test@test.com', + recipientAgentId: 'lumen', + senderAgentId: 'wren', + content: 'Review PR #32', + messageType: 'task_request', + trigger: true, + threadKey: 'pr:32', + }, + mockDc as never + ); + + // The trigger is fire-and-forget, so check the gateway was called with threadKey + expect(mockGateway.processTrigger).toHaveBeenCalledWith( + expect.objectContaining({ + threadKey: 'pr:32', + }) + ); + }); +}); diff --git a/packages/api/src/mcp/tools/inbox-handlers.ts b/packages/api/src/mcp/tools/inbox-handlers.ts index 684638ad..f60830c7 100644 --- a/packages/api/src/mcp/tools/inbox-handlers.ts +++ b/packages/api/src/mcp/tools/inbox-handlers.ts @@ -39,6 +39,12 @@ const sendToInboxSchema = userIdentifierBaseSchema.extend({ relatedArtifactUri: z.string().optional().describe('Related artifact URI'), metadata: z.record(z.unknown()).optional().describe('Additional metadata'), expiresAt: z.string().datetime().optional().describe('When this message expires'), + threadKey: z + .string() + .optional() + .describe( + 'Thread key for conversation continuity (e.g., "pr:32", "spec:cli-hooks"). Messages with the same threadKey are routed to the same session on the recipient side. See PROCESS.md for format guidelines.' + ), // Trigger options - automatically trigger the recipient after sending trigger: z .boolean() @@ -98,6 +104,7 @@ export async function handleSendToInbox(args: unknown, dataComposer: DataCompose expiresAt, triggerType, triggerSummary, + threadKey, } = parsed; // Default trigger behavior based on message type: @@ -121,6 +128,7 @@ export async function handleSendToInbox(args: unknown, dataComposer: DataCompose related_artifact_uri: relatedArtifactUri || null, metadata: metadata as Json, expires_at: expiresAt || null, + thread_key: threadKey || null, }) .select() .single(); @@ -158,6 +166,7 @@ export async function handleSendToInbox(args: unknown, dataComposer: DataCompose triggerType: triggerType || 'message', summary: triggerSummary || subject || `New ${messageType} from ${senderAgentId}`, priority, + threadKey, }; // Fire-and-forget: don't await the trigger processing @@ -208,8 +217,14 @@ export async function handleSendToInbox(args: unknown, dataComposer: DataCompose recipientAgentId, messageType, priority, + threadKey: threadKey || null, createdAt: message.created_at, trigger: triggerResult, + ...(!threadKey + ? { + hint: 'Consider adding a threadKey (e.g., "pr:32", "spec:cli-hooks") so the recipient can resume the same session for follow-up messages on this topic.', + } + : {}), }), }, ], @@ -276,6 +291,7 @@ export async function handleGetInbox(args: unknown, dataComposer: DataComposer) priority: m.priority, status: m.status, senderAgentId: m.sender_agent_id, + threadKey: m.thread_key || null, relatedSessionId: m.related_session_id, relatedArtifactUri: m.related_artifact_uri, metadata: m.metadata, diff --git a/packages/api/src/mcp/tools/index.ts b/packages/api/src/mcp/tools/index.ts index 523bfbcc..68b09e63 100644 --- a/packages/api/src/mcp/tools/index.ts +++ b/packages/api/src/mcp/tools/index.ts @@ -1193,10 +1193,12 @@ User can be identified by ONE of: userId, email, phone, or platform + platformId { description: `Start a new AI session. Sessions track work done across a conversation and can be logged to. -If studioId is provided, the session is scoped to that studio — allowing multiple active sessions per agent (one per studio). Read studioId from .pcp/identity.json if available. -workspaceId is accepted as a deprecated alias. +Session matching priority: +1. threadKey — if provided, returns an existing active session with the same agent+threadKey (enables cross-trigger session continuity, e.g., "pr:32"). +2. studioId — scopes the session to a studio, allowing multiple active sessions per agent (one per studio). Read from .pcp/identity.json. +3. Default — returns any active session for the agent. -If an active session already exists for this agent+studio, it is returned instead of creating a new one. +workspaceId is accepted as a deprecated alias for studioId. User can be identified by ONE of: userId, email, phone, or platform + platformId`, inputSchema: { @@ -1217,6 +1219,12 @@ User can be identified by ONE of: userId, email, phone, or platform + platformId .uuid() .optional() .describe('[Deprecated] Workspace ID alias for studioId.'), + threadKey: z + .string() + .optional() + .describe( + 'Thread key for session routing (e.g., "pr:32"). If an active session with this threadKey exists for the same agent, it is returned instead of creating a new one.' + ), metadata: z.record(z.unknown()).optional().describe('Session metadata'), }, }, diff --git a/packages/api/src/mcp/tools/memory-handlers.test.ts b/packages/api/src/mcp/tools/memory-handlers.test.ts index 054269c8..de9b2205 100644 --- a/packages/api/src/mcp/tools/memory-handlers.test.ts +++ b/packages/api/src/mcp/tools/memory-handlers.test.ts @@ -11,6 +11,7 @@ import { listSessionsSchema, updateSessionPhaseSchema, handleUpdateSessionPhase, + handleStartSession, } from './memory-handlers'; // ===================================================== @@ -58,6 +59,7 @@ vi.mock('../../skills/cloud-service', () => ({ function createMockDataComposer() { const mockMemoryRepo = { getActiveSession: vi.fn(), + getActiveSessionByThreadKey: vi.fn(), updateSession: vi.fn(), remember: vi.fn(), startSession: vi.fn(), @@ -1016,3 +1018,227 @@ describe('handleUpdateSessionPhase', () => { }); }); }); + +// ===================================================== +// THREAD KEY TESTS +// ===================================================== + +describe('startSessionSchema - threadKey', () => { + it('should accept threadKey as optional string', () => { + const result = startSessionSchema.safeParse({ + email: 'test@test.com', + agentId: 'lumen', + threadKey: 'pr:32', + }); + + expect(result.success).toBe(true); + if (result.success) { + expect(result.data.threadKey).toBe('pr:32'); + } + }); + + it('should accept request without threadKey', () => { + const result = startSessionSchema.safeParse({ + email: 'test@test.com', + agentId: 'lumen', + }); + + expect(result.success).toBe(true); + if (result.success) { + expect(result.data.threadKey).toBeUndefined(); + } + }); + + it('should accept threadKey with studioId together', () => { + const result = startSessionSchema.safeParse({ + email: 'test@test.com', + agentId: 'lumen', + threadKey: 'pr:32', + studioId: '550e8400-e29b-41d4-a716-446655440000', + }); + + expect(result.success).toBe(true); + if (result.success) { + expect(result.data.threadKey).toBe('pr:32'); + expect(result.data.studioId).toBe('550e8400-e29b-41d4-a716-446655440000'); + } + }); + + it('should accept various threadKey formats', () => { + const formats = ['pr:32', 'spec:cli-hooks', 'issue:45', 'branch:wren/feat/x', 'thread:perf-audit']; + for (const key of formats) { + const result = startSessionSchema.safeParse({ + email: 'test@test.com', + agentId: 'lumen', + threadKey: key, + }); + expect(result.success).toBe(true); + } + }); +}); + +describe('handleStartSession - threadKey matching', () => { + let mockDataComposer: ReturnType; + + const mockSession = { + id: 'session-existing', + userId: 'user-123', + agentId: 'lumen', + studioId: undefined, + workspaceId: undefined, + threadKey: 'pr:32', + currentPhase: 'reviewing', + startedAt: new Date('2026-02-10T10:00:00Z'), + endedAt: undefined, + summary: undefined, + metadata: {}, + }; + + const mockNewSession = { + id: 'session-new', + userId: 'user-123', + agentId: 'lumen', + studioId: undefined, + workspaceId: undefined, + threadKey: 'pr:99', + currentPhase: undefined, + startedAt: new Date('2026-02-15T10:00:00Z'), + endedAt: undefined, + summary: undefined, + metadata: {}, + }; + + beforeEach(() => { + mockDataComposer = createMockDataComposer(); + vi.clearAllMocks(); + }); + + it('should match existing session by threadKey', async () => { + mockDataComposer.repositories.memory.getActiveSessionByThreadKey.mockResolvedValue(mockSession); + + const result = await handleStartSession( + { email: 'test@test.com', agentId: 'lumen', threadKey: 'pr:32' }, + mockDataComposer as never + ); + + const parsed = JSON.parse(result.content[0].text); + expect(parsed.success).toBe(true); + expect(parsed.session.id).toBe('session-existing'); + expect(parsed.session.threadKey).toBe('pr:32'); + expect(parsed.session.isExisting).toBe(true); + + // Should have queried by threadKey + expect(mockDataComposer.repositories.memory.getActiveSessionByThreadKey).toHaveBeenCalledWith( + 'user-123', + 'lumen', + 'pr:32' + ); + // Should NOT have fallen through to studioId lookup + expect(mockDataComposer.repositories.memory.getActiveSession).not.toHaveBeenCalled(); + // Should NOT have created a new session + expect(mockDataComposer.repositories.memory.startSession).not.toHaveBeenCalled(); + }); + + it('should fall through to studioId match when threadKey has no match', async () => { + mockDataComposer.repositories.memory.getActiveSessionByThreadKey.mockResolvedValue(null); + const studioSession = { ...mockSession, threadKey: undefined, studioId: 'studio-abc' }; + mockDataComposer.repositories.memory.getActiveSession.mockResolvedValue(studioSession); + + const result = await handleStartSession( + { email: 'test@test.com', agentId: 'lumen', threadKey: 'pr:999' }, + mockDataComposer as never + ); + + const parsed = JSON.parse(result.content[0].text); + expect(parsed.success).toBe(true); + expect(parsed.session.isExisting).toBe(true); + + // Should have tried threadKey first, then fallen through + expect(mockDataComposer.repositories.memory.getActiveSessionByThreadKey).toHaveBeenCalled(); + expect(mockDataComposer.repositories.memory.getActiveSession).toHaveBeenCalled(); + }); + + it('should create new session with threadKey when no match found', async () => { + mockDataComposer.repositories.memory.getActiveSessionByThreadKey.mockResolvedValue(null); + mockDataComposer.repositories.memory.getActiveSession.mockResolvedValue(null); + mockDataComposer.repositories.memory.startSession.mockResolvedValue(mockNewSession); + + const result = await handleStartSession( + { email: 'test@test.com', agentId: 'lumen', threadKey: 'pr:99' }, + mockDataComposer as never + ); + + const parsed = JSON.parse(result.content[0].text); + expect(parsed.success).toBe(true); + expect(parsed.message).toBe('Session started successfully'); + expect(parsed.session.id).toBe('session-new'); + expect(parsed.session.threadKey).toBe('pr:99'); + + // Should have passed threadKey to startSession + expect(mockDataComposer.repositories.memory.startSession).toHaveBeenCalledWith( + expect.objectContaining({ + userId: 'user-123', + agentId: 'lumen', + threadKey: 'pr:99', + }) + ); + }); + + it('should skip threadKey lookup when agentId is not provided', async () => { + mockDataComposer.repositories.memory.getActiveSession.mockResolvedValue(null); + mockDataComposer.repositories.memory.startSession.mockResolvedValue({ + ...mockNewSession, + agentId: undefined, + threadKey: 'pr:99', + }); + + await handleStartSession( + { email: 'test@test.com', threadKey: 'pr:99' }, + mockDataComposer as never + ); + + // threadKey lookup requires agentId, so should skip it + expect(mockDataComposer.repositories.memory.getActiveSessionByThreadKey).not.toHaveBeenCalled(); + expect(mockDataComposer.repositories.memory.getActiveSession).toHaveBeenCalled(); + }); + + it('should skip threadKey lookup when threadKey is not provided', async () => { + mockDataComposer.repositories.memory.getActiveSession.mockResolvedValue(null); + mockDataComposer.repositories.memory.startSession.mockResolvedValue({ + ...mockNewSession, + threadKey: undefined, + }); + + await handleStartSession( + { email: 'test@test.com', agentId: 'lumen' }, + mockDataComposer as never + ); + + expect(mockDataComposer.repositories.memory.getActiveSessionByThreadKey).not.toHaveBeenCalled(); + }); + + it('should include threadKey in existing session response', async () => { + mockDataComposer.repositories.memory.getActiveSessionByThreadKey.mockResolvedValue(mockSession); + + const result = await handleStartSession( + { email: 'test@test.com', agentId: 'lumen', threadKey: 'pr:32' }, + mockDataComposer as never + ); + + const parsed = JSON.parse(result.content[0].text); + expect(parsed.session.threadKey).toBe('pr:32'); + }); + + it('should include null threadKey in response when not set', async () => { + const sessionNoThread = { ...mockSession, threadKey: undefined }; + mockDataComposer.repositories.memory.getActiveSession.mockResolvedValue(sessionNoThread); + + const result = await handleStartSession( + { email: 'test@test.com', agentId: 'lumen' }, + mockDataComposer as never + ); + + const parsed = JSON.parse(result.content[0].text); + expect(parsed.session.threadKey).toBeNull(); + }); +}); diff --git a/packages/api/src/mcp/tools/memory-handlers.ts b/packages/api/src/mcp/tools/memory-handlers.ts index a40018da..16df38fe 100644 --- a/packages/api/src/mcp/tools/memory-handlers.ts +++ b/packages/api/src/mcp/tools/memory-handlers.ts @@ -116,6 +116,12 @@ export const startSessionSchema = userIdentifierBaseSchema.extend({ .uuid() .optional() .describe('[Deprecated] Workspace ID alias for studioId.'), + threadKey: z + .string() + .optional() + .describe( + 'Thread key for session routing (e.g., "pr:32"). If an active session with this threadKey exists for the same agent, it is returned instead of creating a new one.' + ), metadata: z.record(z.unknown()).optional().describe('Additional session metadata'), }); @@ -536,12 +542,26 @@ export async function handleStartSession(args: unknown, dataComposer: DataCompos const { user, resolvedBy } = await resolveUserOrThrow(params, dataComposer); const studioId = resolveStudioId(params); - // Check if there's already an active session for this agent (scoped by studio if provided) - const existingSession = await dataComposer.repositories.memory.getActiveSession( - user.id, - params.agentId, - studioId - ); + // Session matching priority: + // 1. threadKey match — find active session with same agent+threadKey + // 2. studioId match — find active session scoped by agent+studio (existing behavior) + let existingSession = null; + + if (params.threadKey && params.agentId) { + existingSession = await dataComposer.repositories.memory.getActiveSessionByThreadKey( + user.id, + params.agentId, + params.threadKey + ); + } + + if (!existingSession) { + existingSession = await dataComposer.repositories.memory.getActiveSession( + user.id, + params.agentId, + studioId + ); + } if (existingSession) { return { @@ -558,6 +578,7 @@ export async function handleStartSession(args: unknown, dataComposer: DataCompos agentId: existingSession.agentId, studioId: existingSession.studioId, workspaceId: existingSession.workspaceId, + threadKey: existingSession.threadKey || null, startedAt: existingSession.startedAt.toISOString(), isExisting: true, }, @@ -575,6 +596,7 @@ export async function handleStartSession(args: unknown, dataComposer: DataCompos agentId: params.agentId, studioId, workspaceId: params.workspaceId, + threadKey: params.threadKey, metadata: params.metadata, }); @@ -583,6 +605,7 @@ export async function handleStartSession(args: unknown, dataComposer: DataCompos agentId: session.agentId, studioId: session.studioId, workspaceId: session.workspaceId, + threadKey: session.threadKey, }); return { @@ -599,6 +622,7 @@ export async function handleStartSession(args: unknown, dataComposer: DataCompos agentId: session.agentId, studioId: session.studioId, workspaceId: session.workspaceId, + threadKey: session.threadKey || null, startedAt: session.startedAt.toISOString(), }, }, @@ -1504,6 +1528,7 @@ export async function handleBootstrap(args: unknown, dataComposer: DataComposer) agentId: s.agentId, studioId: s.studioId || null, workspaceId: s.workspaceId || null, + threadKey: s.threadKey || null, currentPhase: s.currentPhase || null, startedAt: s.startedAt.toISOString(), })), From f997428e956e27636f838bbbc3b786359522f636 Mon Sep 17 00:00:00 2001 From: conoremclaughlin Date: Sun, 15 Feb 2026 23:58:54 -0800 Subject: [PATCH 2/2] fix: add migration file and scope threadKey lookup by studioId MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Addresses Lumen's review on PR #37: 1. Add committed migration file for thread_key columns — the migration was applied to prod via MCP but wasn't checked into the repo, so a fresh DB would fail at runtime. 2. Scope getActiveSessionByThreadKey by studioId — prevents cross-studio collisions where the same threadKey (e.g., "pr:32") exists in parallel workspaces. The lookup now matches the full tuple: (user + agent + studio + threadKey). Co-Authored-By: Claude Opus 4.6 --- .../data/repositories/memory-repository.ts | 20 ++++++++++++----- .../api/src/mcp/tools/memory-handlers.test.ts | 22 +++++++++++++++++-- packages/api/src/mcp/tools/memory-handlers.ts | 3 ++- .../20260216000000_add_thread_key.sql | 10 +++++++++ 4 files changed, 47 insertions(+), 8 deletions(-) create mode 100644 supabase/migrations/20260216000000_add_thread_key.sql diff --git a/packages/api/src/data/repositories/memory-repository.ts b/packages/api/src/data/repositories/memory-repository.ts index 1717fa23..61dd3dd8 100644 --- a/packages/api/src/data/repositories/memory-repository.ts +++ b/packages/api/src/data/repositories/memory-repository.ts @@ -349,15 +349,16 @@ export class MemoryRepository { } /** - * Get active session by threadKey for a user+agent. + * Get active session by threadKey for a user+agent, optionally scoped by studio. * Returns the most recent active session with a matching thread_key, or null. */ async getActiveSessionByThreadKey( userId: string, agentId: string, - threadKey: string + threadKey: string, + studioId?: string | null ): Promise { - const { data, error } = await this.supabase + let query = this.supabase .from('sessions') .select('*') .eq('user_id', userId) @@ -365,8 +366,17 @@ export class MemoryRepository { .eq('thread_key', threadKey) .is('ended_at', null) .order('started_at', { ascending: false }) - .limit(1) - .single(); + .limit(1); + + if (studioId !== undefined) { + if (studioId === null) { + query = query.is('studio_id', null); + } else { + query = query.eq('studio_id', studioId); + } + } + + const { data, error } = await query.single(); if (error) { if (error.code === 'PGRST116') return null; diff --git a/packages/api/src/mcp/tools/memory-handlers.test.ts b/packages/api/src/mcp/tools/memory-handlers.test.ts index de9b2205..8709b729 100644 --- a/packages/api/src/mcp/tools/memory-handlers.test.ts +++ b/packages/api/src/mcp/tools/memory-handlers.test.ts @@ -1127,11 +1127,12 @@ describe('handleStartSession - threadKey matching', () => { expect(parsed.session.threadKey).toBe('pr:32'); expect(parsed.session.isExisting).toBe(true); - // Should have queried by threadKey + // Should have queried by threadKey, scoped by studioId (undefined here) expect(mockDataComposer.repositories.memory.getActiveSessionByThreadKey).toHaveBeenCalledWith( 'user-123', 'lumen', - 'pr:32' + 'pr:32', + undefined ); // Should NOT have fallen through to studioId lookup expect(mockDataComposer.repositories.memory.getActiveSession).not.toHaveBeenCalled(); @@ -1184,6 +1185,23 @@ describe('handleStartSession - threadKey matching', () => { ); }); + it('should scope threadKey lookup by studioId when provided', async () => { + const studioId = '550e8400-e29b-41d4-a716-446655440000'; + mockDataComposer.repositories.memory.getActiveSessionByThreadKey.mockResolvedValue(mockSession); + + await handleStartSession( + { email: 'test@test.com', agentId: 'lumen', threadKey: 'pr:32', studioId }, + mockDataComposer as never + ); + + expect(mockDataComposer.repositories.memory.getActiveSessionByThreadKey).toHaveBeenCalledWith( + 'user-123', + 'lumen', + 'pr:32', + studioId + ); + }); + it('should skip threadKey lookup when agentId is not provided', async () => { mockDataComposer.repositories.memory.getActiveSession.mockResolvedValue(null); mockDataComposer.repositories.memory.startSession.mockResolvedValue({ diff --git a/packages/api/src/mcp/tools/memory-handlers.ts b/packages/api/src/mcp/tools/memory-handlers.ts index 16df38fe..3a186f63 100644 --- a/packages/api/src/mcp/tools/memory-handlers.ts +++ b/packages/api/src/mcp/tools/memory-handlers.ts @@ -551,7 +551,8 @@ export async function handleStartSession(args: unknown, dataComposer: DataCompos existingSession = await dataComposer.repositories.memory.getActiveSessionByThreadKey( user.id, params.agentId, - params.threadKey + params.threadKey, + studioId ); } diff --git a/supabase/migrations/20260216000000_add_thread_key.sql b/supabase/migrations/20260216000000_add_thread_key.sql new file mode 100644 index 00000000..5c19fe11 --- /dev/null +++ b/supabase/migrations/20260216000000_add_thread_key.sql @@ -0,0 +1,10 @@ +ALTER TABLE public.sessions ADD COLUMN IF NOT EXISTS thread_key text; +ALTER TABLE public.agent_inbox ADD COLUMN IF NOT EXISTS thread_key text; + +CREATE INDEX IF NOT EXISTS idx_sessions_thread_key_active + ON public.sessions (user_id, agent_id, thread_key) + WHERE ended_at IS NULL AND thread_key IS NOT NULL; + +CREATE INDEX IF NOT EXISTS idx_agent_inbox_thread_key + ON public.agent_inbox (recipient_user_id, recipient_agent_id, thread_key) + WHERE thread_key IS NOT NULL;